zx-infinite-scroll 1.0.7 → 1.0.10
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/index.es.js +187 -192
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
package/dist/index.es.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { defineComponent as Y, openBlock as f, createElementBlock as
|
|
2
|
-
const
|
|
1
|
+
import { defineComponent as Y, openBlock as f, createElementBlock as v, normalizeClass as X, createElementVNode as I, toDisplayString as $, computed as se, normalizeStyle as P, renderSlot as K, createTextVNode as xe, createCommentVNode as D, ref as d, watch as j, onMounted as ce, nextTick as V, onBeforeUnmount as me, withDirectives as ge, vShow as ye, createVNode as Te, createBlock as U, withCtx as ze } from "vue";
|
|
2
|
+
const we = { class: "zx-loading-content" }, Ee = { class: "zx-loading-text" }, Z = /* @__PURE__ */ Y({
|
|
3
3
|
name: "RequestLoading",
|
|
4
4
|
__name: "request-loading",
|
|
5
5
|
props: {
|
|
@@ -7,24 +7,19 @@ const Ee = { class: "loading-content" }, Re = { class: "loading-text" }, Ce = /*
|
|
|
7
7
|
fullscreen: { type: Boolean, default: !1 }
|
|
8
8
|
},
|
|
9
9
|
setup(t) {
|
|
10
|
-
return (
|
|
11
|
-
class: X(["loading-container", { fullscreen: t.fullscreen }])
|
|
10
|
+
return (z, E) => (f(), v("div", {
|
|
11
|
+
class: X(["zx-loading-container", { fullscreen: t.fullscreen }])
|
|
12
12
|
}, [
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
I("div", we, [
|
|
14
|
+
E[0] || (E[0] = I("span", { class: "zx-loading-spinner" }, null, -1)),
|
|
15
|
+
I("span", Ee, $(t.text), 1)
|
|
16
16
|
])
|
|
17
17
|
], 2));
|
|
18
18
|
}
|
|
19
|
-
}),
|
|
20
|
-
const g = t.__vccOpts || t;
|
|
21
|
-
for (const [l, x] of m)
|
|
22
|
-
g[l] = x;
|
|
23
|
-
return g;
|
|
24
|
-
}, Z = /* @__PURE__ */ G(Ce, [["__scopeId", "data-v-d89150ba"]]), ke = ["src"], Le = {
|
|
19
|
+
}), Re = ["src"], Ce = {
|
|
25
20
|
key: 0,
|
|
26
|
-
class: "empty-text"
|
|
27
|
-
},
|
|
21
|
+
class: "zx-empty-text"
|
|
22
|
+
}, ke = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='80' viewBox='0 0 96 80' fill='none'%3E%3Cpath d='M8 16C8 12.6863 10.6863 10 14 10H38L44 16H82C85.3137 16 88 18.6863 88 22V66C88 69.3137 85.3137 72 82 72H14C10.6863 72 8 69.3137 8 66V16Z' fill='%23E8E8E8'/%3E%3Cpath d='M8 20C8 18.3431 9.34315 17 11 17H85C86.6569 17 88 18.3431 88 20V66C88 69.3137 85.3137 72 82 72H14C10.6863 72 8 69.3137 8 66V20Z' fill='%23F5F5F5'/%3E%3Cpath d='M38 42C38 40.8954 38.8954 40 40 40H56C57.1046 40 58 40.8954 58 42V44C58 45.1046 57.1046 46 56 46H40C38.8954 46 38 45.1046 38 44V42Z' fill='%23D9D9D9'/%3E%3C/svg%3E", fe = /* @__PURE__ */ Y({
|
|
28
23
|
name: "EmptyComponent",
|
|
29
24
|
__name: "empty-component",
|
|
30
25
|
props: {
|
|
@@ -34,38 +29,38 @@ const Ee = { class: "loading-content" }, Re = { class: "loading-text" }, Ce = /*
|
|
|
34
29
|
emptyImage: { default: "" }
|
|
35
30
|
},
|
|
36
31
|
setup(t) {
|
|
37
|
-
const
|
|
38
|
-
paddingTop: typeof
|
|
39
|
-
})), l = se(() =>
|
|
40
|
-
return (
|
|
41
|
-
class: "empty-wrap",
|
|
42
|
-
style: P(
|
|
32
|
+
const z = t, E = se(() => ({
|
|
33
|
+
paddingTop: typeof z.paddingTop == "number" ? `${z.paddingTop}px` : z.paddingTop
|
|
34
|
+
})), l = se(() => z.emptyImage || ke);
|
|
35
|
+
return (w, i) => (f(), v("div", {
|
|
36
|
+
class: "zx-empty-wrap",
|
|
37
|
+
style: P(E.value)
|
|
43
38
|
}, [
|
|
44
|
-
|
|
45
|
-
class: "empty-image",
|
|
39
|
+
I("img", {
|
|
40
|
+
class: "zx-empty-image",
|
|
46
41
|
src: l.value,
|
|
47
42
|
alt: ""
|
|
48
|
-
}, null, 8,
|
|
49
|
-
t.showText ? (f(),
|
|
50
|
-
K(
|
|
51
|
-
|
|
52
|
-
]
|
|
43
|
+
}, null, 8, Re),
|
|
44
|
+
t.showText ? (f(), v("p", Ce, [
|
|
45
|
+
K(w.$slots, "default", {}, () => [
|
|
46
|
+
xe($(t.text), 1)
|
|
47
|
+
])
|
|
53
48
|
])) : D("", !0)
|
|
54
49
|
], 4));
|
|
55
50
|
}
|
|
56
|
-
}),
|
|
51
|
+
}), Le = { class: "zx-pull-refresh-content" }, be = { key: 0 }, He = {
|
|
57
52
|
key: 1,
|
|
58
|
-
class: "pull-text"
|
|
59
|
-
},
|
|
53
|
+
class: "zx-pull-text"
|
|
54
|
+
}, Se = {
|
|
60
55
|
key: 2,
|
|
61
|
-
class: "pull-text"
|
|
62
|
-
},
|
|
56
|
+
class: "zx-pull-text"
|
|
57
|
+
}, Be = {
|
|
63
58
|
key: 0,
|
|
64
|
-
class: "content-loading-mask"
|
|
65
|
-
},
|
|
59
|
+
class: "zx-content-loading-mask"
|
|
60
|
+
}, Me = {
|
|
66
61
|
key: 1,
|
|
67
|
-
class: "finished-text"
|
|
68
|
-
},
|
|
62
|
+
class: "zx-finished-text"
|
|
63
|
+
}, re = 8, de = /* @__PURE__ */ Y({
|
|
69
64
|
name: "InfiniteScroll",
|
|
70
65
|
__name: "infinite-scroll",
|
|
71
66
|
props: {
|
|
@@ -86,189 +81,189 @@ const Ee = { class: "loading-content" }, Re = { class: "loading-text" }, Ce = /*
|
|
|
86
81
|
contentLoading: { type: Boolean, default: !1 }
|
|
87
82
|
},
|
|
88
83
|
emits: ["refresh", "load", "retry"],
|
|
89
|
-
setup(t, { expose:
|
|
90
|
-
const l = t,
|
|
91
|
-
let
|
|
92
|
-
function B(e,
|
|
93
|
-
!l.pullRefresh ||
|
|
84
|
+
setup(t, { expose: z, emit: E }) {
|
|
85
|
+
const l = t, w = E, i = d(null), o = d(null), R = d(null), a = d("idle"), h = d(0), x = d(!1);
|
|
86
|
+
let y = 0, C = 0, m = 0, g = "none", p = !1, b = !1, H = !1, k = null, r = null, n = null, u = null, c = -1;
|
|
87
|
+
function B(e, s) {
|
|
88
|
+
!l.pullRefresh || p || (y = e, C = s, m = 0, g = "none", a.value = "idle");
|
|
94
89
|
}
|
|
95
|
-
function
|
|
96
|
-
if (!l.pullRefresh ||
|
|
97
|
-
const M = e -
|
|
98
|
-
if (
|
|
99
|
-
const
|
|
100
|
-
if (
|
|
101
|
-
|
|
90
|
+
function G(e, s, T) {
|
|
91
|
+
if (!l.pullRefresh || p || C === 0) return !1;
|
|
92
|
+
const M = e - y, L = s - C;
|
|
93
|
+
if (g === "none") {
|
|
94
|
+
const S = Math.abs(M), O = Math.abs(L);
|
|
95
|
+
if (S > re || O > re)
|
|
96
|
+
g = O > S * 1.5 ? "vertical" : "horizontal";
|
|
102
97
|
else
|
|
103
98
|
return !1;
|
|
104
99
|
}
|
|
105
|
-
if (
|
|
100
|
+
if (g === "horizontal") return !1;
|
|
106
101
|
const q = pe() <= 1;
|
|
107
102
|
if (!q && L < 0) {
|
|
108
|
-
const
|
|
109
|
-
return !!(
|
|
103
|
+
const S = i.value;
|
|
104
|
+
return !!(S && S.scrollHeight - S.scrollTop - S.clientHeight <= 1 && T);
|
|
110
105
|
}
|
|
111
|
-
return q ? (
|
|
112
|
-
|
|
106
|
+
return q ? (m = Math.max(0, Math.min(L, 150)), x.value = !0, r === null && (r = requestAnimationFrame(() => {
|
|
107
|
+
r = null, h.value = m * 0.5, m === 0 ? a.value = "idle" : a.value = m > l.pullThreshold ? "loosing" : "pulling";
|
|
113
108
|
})), L > 0) : !1;
|
|
114
109
|
}
|
|
115
|
-
function
|
|
116
|
-
!l.pullRefresh ||
|
|
110
|
+
function J() {
|
|
111
|
+
!l.pullRefresh || p || (r !== null && (cancelAnimationFrame(r), r = null), x.value = !1, m > l.pullThreshold ? (p = !0, a.value = "refreshing", h.value = 50, w("refresh")) : (h.value = 0, a.value = "idle"), ie());
|
|
117
112
|
}
|
|
118
|
-
function
|
|
119
|
-
!l.pullRefresh ||
|
|
113
|
+
function he() {
|
|
114
|
+
!l.pullRefresh || p || (r !== null && (cancelAnimationFrame(r), r = null), x.value = !1, h.value = 0, a.value = "idle", ie());
|
|
120
115
|
}
|
|
121
|
-
function
|
|
116
|
+
function Q(e) {
|
|
122
117
|
B(e.touches[0].clientX, e.touches[0].clientY);
|
|
123
118
|
}
|
|
124
|
-
function
|
|
125
|
-
const
|
|
126
|
-
|
|
119
|
+
function W(e) {
|
|
120
|
+
const s = e.touches[0];
|
|
121
|
+
G(s.clientX, s.clientY, e.cancelable) && e.cancelable && e.preventDefault();
|
|
122
|
+
}
|
|
123
|
+
function _() {
|
|
124
|
+
J();
|
|
127
125
|
}
|
|
128
126
|
function ee() {
|
|
129
|
-
|
|
127
|
+
he();
|
|
130
128
|
}
|
|
131
|
-
function te() {
|
|
132
|
-
|
|
129
|
+
function te(e) {
|
|
130
|
+
!l.pullRefresh || p || e.button !== 0 || (H = !0, B(e.clientX, e.clientY));
|
|
133
131
|
}
|
|
134
132
|
function ne(e) {
|
|
135
|
-
|
|
133
|
+
H && G(e.clientX, e.clientY, !0) && e.preventDefault();
|
|
136
134
|
}
|
|
137
|
-
function le(
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
function ae() {
|
|
141
|
-
S && (S = !1, Q());
|
|
135
|
+
function le() {
|
|
136
|
+
H && (H = !1, J());
|
|
142
137
|
}
|
|
143
138
|
function ie() {
|
|
144
|
-
|
|
139
|
+
y = 0, C = 0, m = 0, g = "none";
|
|
145
140
|
}
|
|
146
141
|
function A() {
|
|
147
|
-
l.loading || l.finished || l.error ||
|
|
142
|
+
l.loading || l.finished || l.error || b || k || (k = window.setTimeout(() => {
|
|
148
143
|
var q;
|
|
149
144
|
k = null;
|
|
150
|
-
const e =
|
|
145
|
+
const e = i.value;
|
|
151
146
|
if (!e) return;
|
|
152
|
-
const { scrollTop:
|
|
153
|
-
if (L <=
|
|
147
|
+
const { scrollTop: s, clientHeight: T, scrollHeight: M } = e, L = ((q = o.value) == null ? void 0 : q.clientHeight) || 0;
|
|
148
|
+
if (L <= T) {
|
|
154
149
|
if (c === L) return;
|
|
155
150
|
c = L, oe(!0);
|
|
156
151
|
return;
|
|
157
152
|
}
|
|
158
|
-
M -
|
|
153
|
+
M - s - T <= l.loadThreshold && oe(!1);
|
|
159
154
|
}, 100));
|
|
160
155
|
}
|
|
161
156
|
function oe(e = !1) {
|
|
162
|
-
|
|
163
|
-
var
|
|
164
|
-
const
|
|
165
|
-
|
|
157
|
+
b = !0, w("load"), V(() => {
|
|
158
|
+
var T;
|
|
159
|
+
const s = i.value;
|
|
160
|
+
s && (e ? s.scrollTop = s.scrollHeight : (T = R.value) == null || T.scrollIntoView({ block: "end", behavior: "smooth" }));
|
|
166
161
|
});
|
|
167
162
|
}
|
|
168
163
|
function pe() {
|
|
169
164
|
var e;
|
|
170
|
-
return ((e =
|
|
165
|
+
return ((e = i.value) == null ? void 0 : e.scrollTop) ?? 0;
|
|
171
166
|
}
|
|
172
167
|
function N() {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}, 250), c = -1,
|
|
168
|
+
p = !1, h.value = 0, n !== null && clearTimeout(n), n = window.setTimeout(() => {
|
|
169
|
+
a.value = "idle", n = null;
|
|
170
|
+
}, 250), c = -1, V(() => {
|
|
176
171
|
F();
|
|
177
172
|
});
|
|
178
173
|
}
|
|
179
|
-
function
|
|
180
|
-
|
|
181
|
-
var
|
|
182
|
-
const e = ((
|
|
174
|
+
function ae() {
|
|
175
|
+
b = !1, V(() => {
|
|
176
|
+
var s;
|
|
177
|
+
const e = ((s = o.value) == null ? void 0 : s.clientHeight) || 0;
|
|
183
178
|
c !== -1 && e === c || (c = -1, F());
|
|
184
179
|
});
|
|
185
180
|
}
|
|
186
181
|
function F() {
|
|
187
|
-
l.finished || l.loading || l.error ||
|
|
188
|
-
var
|
|
189
|
-
const e = ((
|
|
190
|
-
e <=
|
|
182
|
+
l.finished || l.loading || l.error || V(() => {
|
|
183
|
+
var T, M;
|
|
184
|
+
const e = ((T = o.value) == null ? void 0 : T.clientHeight) || 0, s = ((M = i.value) == null ? void 0 : M.clientHeight) || 0;
|
|
185
|
+
e <= s && !b && A();
|
|
191
186
|
});
|
|
192
187
|
}
|
|
193
|
-
function
|
|
194
|
-
|
|
188
|
+
function ve() {
|
|
189
|
+
w("retry");
|
|
195
190
|
}
|
|
196
191
|
return j(
|
|
197
192
|
() => l.loading,
|
|
198
193
|
(e) => {
|
|
199
|
-
e || (
|
|
194
|
+
e || (b = !1);
|
|
200
195
|
}
|
|
201
196
|
), j(
|
|
202
197
|
() => l.finished,
|
|
203
198
|
(e) => {
|
|
204
199
|
e && (c = -1);
|
|
205
200
|
}
|
|
206
|
-
),
|
|
207
|
-
const e =
|
|
208
|
-
e && (l.pullRefresh && (e.addEventListener("touchstart",
|
|
201
|
+
), ce(() => {
|
|
202
|
+
const e = i.value;
|
|
203
|
+
e && (l.pullRefresh && (e.addEventListener("touchstart", Q, { passive: !0 }), e.addEventListener("touchmove", W, { passive: !1 }), e.addEventListener("touchend", _, { passive: !0 }), e.addEventListener("touchcancel", ee, { passive: !0 }), e.addEventListener("mousedown", te), window.addEventListener("mousemove", ne), window.addEventListener("mouseup", le)), e.addEventListener("scroll", A, { passive: !0 }), typeof ResizeObserver < "u" && (u = new ResizeObserver(() => {
|
|
209
204
|
F();
|
|
210
|
-
}), u.observe(e),
|
|
205
|
+
}), u.observe(e), o.value && u.observe(o.value)), V(() => {
|
|
211
206
|
F();
|
|
212
207
|
}));
|
|
213
|
-
}),
|
|
214
|
-
const e =
|
|
215
|
-
e && (e.removeEventListener("touchstart",
|
|
216
|
-
}),
|
|
208
|
+
}), me(() => {
|
|
209
|
+
const e = i.value;
|
|
210
|
+
e && (e.removeEventListener("touchstart", Q), e.removeEventListener("touchmove", W), e.removeEventListener("touchend", _), e.removeEventListener("touchcancel", ee), e.removeEventListener("mousedown", te), e.removeEventListener("scroll", A)), window.removeEventListener("mousemove", ne), window.removeEventListener("mouseup", le), u && (u.disconnect(), u = null), r !== null && (cancelAnimationFrame(r), r = null), k !== null && (clearTimeout(k), k = null), n !== null && (clearTimeout(n), n = null);
|
|
211
|
+
}), z({
|
|
217
212
|
refresh: N,
|
|
218
213
|
finishRefresh: N,
|
|
219
214
|
finishPullDown: N,
|
|
220
|
-
finishLoad:
|
|
221
|
-
finishPullUp:
|
|
215
|
+
finishLoad: ae,
|
|
216
|
+
finishPullUp: ae,
|
|
222
217
|
checkContentHeight: F
|
|
223
|
-
}), (e,
|
|
218
|
+
}), (e, s) => (f(), v("div", {
|
|
224
219
|
ref_key: "containerRef",
|
|
225
|
-
ref:
|
|
226
|
-
class: "infinite-scroll"
|
|
220
|
+
ref: i,
|
|
221
|
+
class: "zx-infinite-scroll"
|
|
227
222
|
}, [
|
|
228
|
-
t.pullRefresh ? (f(),
|
|
223
|
+
t.pullRefresh ? (f(), v("div", {
|
|
229
224
|
key: 0,
|
|
230
|
-
class: X(["pull-refresh-indicator", { "no-transition":
|
|
225
|
+
class: X(["zx-pull-refresh-indicator", { "no-transition": x.value }]),
|
|
231
226
|
style: P({ transform: `translateY(${h.value}px)` })
|
|
232
227
|
}, [
|
|
233
|
-
|
|
234
|
-
|
|
228
|
+
ge(I("div", Le, [
|
|
229
|
+
a.value === "refreshing" ? (f(), v("span", be)) : a.value === "loosing" ? (f(), v("span", He, $(t.loosingText), 1)) : (f(), v("span", Se, $(t.pullingText), 1))
|
|
235
230
|
], 512), [
|
|
236
|
-
[
|
|
231
|
+
[ye, a.value !== "idle"]
|
|
237
232
|
])
|
|
238
233
|
], 6)) : D("", !0),
|
|
239
|
-
|
|
234
|
+
I("div", {
|
|
240
235
|
ref_key: "contentRef",
|
|
241
|
-
ref:
|
|
242
|
-
class: X(["infinite-scroll-content", { "no-transition":
|
|
236
|
+
ref: o,
|
|
237
|
+
class: X(["zx-infinite-scroll-content", { "no-transition": x.value }]),
|
|
243
238
|
style: P(t.pullRefresh ? { transform: `translateY(${h.value}px)` } : {})
|
|
244
239
|
}, [
|
|
245
|
-
t.contentLoading ? (f(),
|
|
246
|
-
|
|
240
|
+
t.contentLoading ? (f(), v("div", Be, [
|
|
241
|
+
Te(Z, { text: t.refreshingText }, null, 8, ["text"])
|
|
247
242
|
])) : D("", !0),
|
|
248
|
-
t.isEmpty && !t.loading ? (f(), U(
|
|
243
|
+
t.isEmpty && !t.loading ? (f(), U(fe, {
|
|
249
244
|
key: 1,
|
|
250
245
|
text: t.emptyText
|
|
251
246
|
}, null, 8, ["text"])) : D("", !0),
|
|
252
|
-
K(e.$slots, "default"
|
|
253
|
-
t.isEmpty ? D("", !0) : (f(),
|
|
247
|
+
K(e.$slots, "default"),
|
|
248
|
+
t.isEmpty ? D("", !0) : (f(), v("div", {
|
|
254
249
|
key: 2,
|
|
255
250
|
ref_key: "indicatorRef",
|
|
256
251
|
ref: R,
|
|
257
|
-
class: "load-more-indicator"
|
|
252
|
+
class: "zx-load-more-indicator"
|
|
258
253
|
}, [
|
|
259
|
-
t.loading &&
|
|
254
|
+
t.loading && a.value !== "refreshing" ? (f(), U(Z, {
|
|
260
255
|
key: 0,
|
|
261
256
|
text: t.loadingText
|
|
262
|
-
}, null, 8, ["text"])) : t.finished ? (f(),
|
|
257
|
+
}, null, 8, ["text"])) : t.finished ? (f(), v("span", Me, $(t.finishedText), 1)) : t.error ? (f(), v("span", {
|
|
263
258
|
key: 2,
|
|
264
|
-
class: "error-text",
|
|
265
|
-
onClick:
|
|
266
|
-
},
|
|
259
|
+
class: "zx-error-text",
|
|
260
|
+
onClick: ve
|
|
261
|
+
}, $(t.errorText), 1)) : D("", !0)
|
|
267
262
|
], 512))
|
|
268
263
|
], 6)
|
|
269
264
|
], 512));
|
|
270
265
|
}
|
|
271
|
-
}),
|
|
266
|
+
}), De = /* @__PURE__ */ Y({
|
|
272
267
|
name: "InfiniteScrollV2",
|
|
273
268
|
__name: "infinite-scroll-v2",
|
|
274
269
|
props: {
|
|
@@ -289,98 +284,98 @@ const Ee = { class: "loading-content" }, Re = { class: "loading-text" }, Ce = /*
|
|
|
289
284
|
errorText: { default: "加载失败,点击重试" }
|
|
290
285
|
},
|
|
291
286
|
emits: ["success", "error", "update:list"],
|
|
292
|
-
setup(t, { expose:
|
|
293
|
-
const l = t,
|
|
294
|
-
let
|
|
295
|
-
async function
|
|
296
|
-
n && (C.value = 1,
|
|
297
|
-
const u =
|
|
287
|
+
setup(t, { expose: z, emit: E }) {
|
|
288
|
+
const l = t, w = E, i = d(null), o = d([]), R = d(!1), a = d(!1), h = d(!1), x = d(!1), y = d(0), C = d(1), m = d(!1);
|
|
289
|
+
let g = 0;
|
|
290
|
+
async function p(n = !1) {
|
|
291
|
+
n && (C.value = 1, a.value = !1, h.value = !1), R.value = !0, x.value = !1, g += 1;
|
|
292
|
+
const u = g;
|
|
298
293
|
try {
|
|
299
294
|
const c = await l.fetchFn({
|
|
300
295
|
page: C.value,
|
|
301
296
|
pageSize: l.pageSize,
|
|
302
297
|
...l.params
|
|
303
298
|
});
|
|
304
|
-
if (u !==
|
|
299
|
+
if (u !== g) return;
|
|
305
300
|
const B = (c == null ? void 0 : c.records) || [];
|
|
306
|
-
n ? (
|
|
301
|
+
n ? (o.value = B, y.value = 0) : o.value = [...o.value, ...B], y.value = Number((c == null ? void 0 : c.total) || 0), a.value = o.value.length >= y.value, h.value = o.value.length === 0, w("success", { records: B, total: y.value, reset: n }), w("update:list", o.value);
|
|
307
302
|
} catch (c) {
|
|
308
|
-
if (u !==
|
|
309
|
-
|
|
303
|
+
if (u !== g) return;
|
|
304
|
+
x.value = !0, w("error", c);
|
|
310
305
|
} finally {
|
|
311
|
-
u ===
|
|
306
|
+
u === g && (R.value = !1);
|
|
312
307
|
}
|
|
313
308
|
}
|
|
314
|
-
async function
|
|
309
|
+
async function b() {
|
|
315
310
|
var n;
|
|
316
|
-
|
|
311
|
+
m.value = !0, (n = i.value) == null || n.finishRefresh(), await new Promise((u) => {
|
|
317
312
|
setTimeout(() => u(), 250);
|
|
318
|
-
}), await
|
|
313
|
+
}), await p(!0), m.value = !1;
|
|
319
314
|
}
|
|
320
|
-
async function
|
|
315
|
+
async function H() {
|
|
321
316
|
var n, u;
|
|
322
|
-
if (
|
|
323
|
-
(n =
|
|
317
|
+
if (a.value) {
|
|
318
|
+
(n = i.value) == null || n.finishLoad();
|
|
324
319
|
return;
|
|
325
320
|
}
|
|
326
|
-
|
|
321
|
+
o.value.length > 0 && (C.value += 1), await p(!1), (u = i.value) == null || u.finishLoad();
|
|
327
322
|
}
|
|
328
323
|
function k() {
|
|
329
|
-
|
|
324
|
+
x.value = !1, o.value.length === 0 ? p(!0) : H();
|
|
330
325
|
}
|
|
331
|
-
async function
|
|
332
|
-
await
|
|
326
|
+
async function r() {
|
|
327
|
+
await p(!0);
|
|
333
328
|
}
|
|
334
329
|
return j(
|
|
335
330
|
() => l.params,
|
|
336
331
|
() => {
|
|
337
|
-
l.autoReload &&
|
|
332
|
+
l.autoReload && r();
|
|
338
333
|
},
|
|
339
334
|
{ deep: !0 }
|
|
340
335
|
), j(
|
|
341
|
-
|
|
336
|
+
o,
|
|
342
337
|
async () => {
|
|
343
338
|
var n;
|
|
344
|
-
await
|
|
339
|
+
await V(), (n = i.value) == null || n.checkContentHeight();
|
|
345
340
|
},
|
|
346
341
|
{ deep: !0 }
|
|
347
|
-
),
|
|
348
|
-
l.immediate &&
|
|
349
|
-
}),
|
|
342
|
+
), ce(() => {
|
|
343
|
+
l.immediate && p(!0);
|
|
344
|
+
}), z({
|
|
350
345
|
/** 重新加载第一页 */
|
|
351
|
-
reload:
|
|
346
|
+
reload: r,
|
|
352
347
|
/** 兼容别名 */
|
|
353
|
-
refresh:
|
|
348
|
+
refresh: r,
|
|
354
349
|
/** 当前列表数据 */
|
|
355
|
-
list:
|
|
350
|
+
list: o,
|
|
356
351
|
/** 加载中状态 */
|
|
357
352
|
loading: R,
|
|
358
353
|
/** 是否已加载完 */
|
|
359
|
-
finished:
|
|
354
|
+
finished: a,
|
|
360
355
|
/** 是否空数据 */
|
|
361
356
|
isEmpty: h,
|
|
362
357
|
/** 总条数 */
|
|
363
|
-
total:
|
|
358
|
+
total: y,
|
|
364
359
|
/** 透传 v1 方法 */
|
|
365
360
|
finishLoad: () => {
|
|
366
361
|
var n;
|
|
367
|
-
return (n =
|
|
362
|
+
return (n = i.value) == null ? void 0 : n.finishLoad();
|
|
368
363
|
},
|
|
369
364
|
finishRefresh: () => {
|
|
370
365
|
var n;
|
|
371
|
-
return (n =
|
|
366
|
+
return (n = i.value) == null ? void 0 : n.finishRefresh();
|
|
372
367
|
},
|
|
373
368
|
checkContentHeight: () => {
|
|
374
369
|
var n;
|
|
375
|
-
return (n =
|
|
370
|
+
return (n = i.value) == null ? void 0 : n.checkContentHeight();
|
|
376
371
|
}
|
|
377
|
-
}), (n, u) => (f(), U(
|
|
372
|
+
}), (n, u) => (f(), U(de, {
|
|
378
373
|
ref_key: "scrollRef",
|
|
379
|
-
ref:
|
|
374
|
+
ref: i,
|
|
380
375
|
loading: R.value,
|
|
381
|
-
finished:
|
|
376
|
+
finished: a.value,
|
|
382
377
|
"is-empty": h.value,
|
|
383
|
-
error:
|
|
378
|
+
error: x.value,
|
|
384
379
|
"pull-refresh": t.pullRefresh,
|
|
385
380
|
"pull-threshold": t.pullThreshold,
|
|
386
381
|
"load-threshold": t.loadThreshold,
|
|
@@ -391,46 +386,46 @@ const Ee = { class: "loading-content" }, Re = { class: "loading-text" }, Ce = /*
|
|
|
391
386
|
"loading-text": t.loadingText,
|
|
392
387
|
"finished-text": t.finishedText,
|
|
393
388
|
"error-text": t.errorText,
|
|
394
|
-
"content-loading":
|
|
395
|
-
onLoad:
|
|
396
|
-
onRefresh:
|
|
389
|
+
"content-loading": m.value,
|
|
390
|
+
onLoad: H,
|
|
391
|
+
onRefresh: b,
|
|
397
392
|
onRetry: k
|
|
398
393
|
}, {
|
|
399
|
-
default:
|
|
394
|
+
default: ze(() => [
|
|
400
395
|
K(n.$slots, "default", {
|
|
401
|
-
list:
|
|
396
|
+
list: o.value,
|
|
402
397
|
loading: R.value,
|
|
403
|
-
finished:
|
|
398
|
+
finished: a.value,
|
|
404
399
|
isEmpty: h.value,
|
|
405
|
-
error:
|
|
406
|
-
total:
|
|
407
|
-
reload:
|
|
400
|
+
error: x.value,
|
|
401
|
+
total: y.value,
|
|
402
|
+
reload: r
|
|
408
403
|
})
|
|
409
404
|
]),
|
|
410
405
|
_: 3
|
|
411
406
|
}, 8, ["loading", "finished", "is-empty", "error", "pull-refresh", "pull-threshold", "load-threshold", "empty-text", "pulling-text", "loosing-text", "refreshing-text", "loading-text", "finished-text", "error-text", "content-loading"]));
|
|
412
407
|
}
|
|
413
408
|
});
|
|
414
|
-
let
|
|
415
|
-
const
|
|
416
|
-
function
|
|
417
|
-
if (
|
|
409
|
+
let ue = !1;
|
|
410
|
+
const Ve = ".zx-loading-content{display:flex;align-items:center;justify-content:center;gap:8px;padding:6px 12px;border-radius:16px;color:#1989fa;font-size:14px}.zx-loading-spinner{width:18px;height:18px;border-radius:50%;border:2.5px solid #1989fa;border-top-color:transparent;animation:zx-spin .8s linear infinite}@keyframes zx-spin{to{transform:rotate(360deg)}}.zx-loading-container.fullscreen{position:fixed;top:0;left:0;width:100%;height:100%;display:flex;align-items:center;justify-content:center;background-color:#ffffff80}.zx-empty-wrap{display:flex;flex-direction:column;align-items:center}.zx-empty-image{width:96px;height:80px}.zx-empty-text{margin:12px 0 0;color:#8c8f97;font-size:12px;line-height:22px}.zx-infinite-scroll{height:100%;position:relative;min-height:100%;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;touch-action:pan-y;-webkit-overflow-scrolling:touch}.zx-pull-refresh-indicator{position:absolute;top:-50px;left:0;right:0;height:50px;transition:transform .25s}.zx-pull-refresh-indicator.no-transition{transition:none}.zx-pull-refresh-content{display:flex;align-items:center;justify-content:center;height:100%;font-size:14px;color:#969799}.zx-pull-text{color:#1989fa;font-size:14px}.zx-infinite-scroll-content{position:relative;transition:transform .25s;min-height:100%}.zx-infinite-scroll-content.no-transition{transition:none}.zx-content-loading-mask{position:absolute;top:0;left:0;right:0;bottom:0;background-color:#fffc;display:flex;align-items:center;justify-content:center;z-index:10;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.zx-load-more-indicator{display:flex;align-items:center;justify-content:center;min-height:50px;padding:16px 0;font-size:14px;color:#8c8f97}.zx-finished-text{color:#8c8f97;font-size:14px}.zx-error-text{color:#ee0a24;font-size:14px;cursor:pointer}.zx-error-text:active{opacity:.7}";
|
|
411
|
+
function $e() {
|
|
412
|
+
if (ue || typeof document > "u") return;
|
|
418
413
|
const t = document.createElement("style");
|
|
419
|
-
t.setAttribute("data-source", "zx-infinite-scroll"), t.textContent =
|
|
414
|
+
t.setAttribute("data-source", "zx-infinite-scroll"), t.textContent = Ve, document.head.appendChild(t), ue = !0;
|
|
420
415
|
}
|
|
421
|
-
|
|
422
|
-
function
|
|
423
|
-
t.component("InfiniteScroll",
|
|
416
|
+
$e();
|
|
417
|
+
function Ie(t) {
|
|
418
|
+
t.component("InfiniteScroll", de), t.component("InfiniteScrollV2", De), t.component("RequestLoading", Z), t.component("EmptyComponent", fe);
|
|
424
419
|
}
|
|
425
|
-
const
|
|
426
|
-
install:
|
|
420
|
+
const je = {
|
|
421
|
+
install: Ie
|
|
427
422
|
};
|
|
428
423
|
export {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
424
|
+
fe as EmptyComponent,
|
|
425
|
+
de as InfiniteScroll,
|
|
426
|
+
De as InfiniteScrollV2,
|
|
432
427
|
Z as RequestLoading,
|
|
433
|
-
|
|
434
|
-
|
|
428
|
+
je as default,
|
|
429
|
+
Ie as install
|
|
435
430
|
};
|
|
436
431
|
//# sourceMappingURL=index.es.js.map
|
package/dist/index.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.es.js","sources":["../src/components/request-loading.vue","../src/components/empty-component.vue","../src/components/infinite-scroll.vue","../src/components/infinite-scroll-v2.vue","../src/inject-style.ts","../src/index.ts"],"sourcesContent":["<script setup lang=\"ts\">\ndefineOptions({ name: 'RequestLoading' });\n\nwithDefaults(defineProps<{ text?: string; fullscreen?: boolean }>(), {\n text: '加载中...',\n fullscreen: false\n});\n</script>\n\n<template>\n <div class=\"loading-container\" :class=\"{ fullscreen: fullscreen }\">\n <div class=\"loading-content\">\n <span class=\"loading-spinner\"></span>\n <span class=\"loading-text\">{{ text }}</span>\n </div>\n </div>\n</template>\n\n<style scoped lang=\"scss\">\n.loading-content {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 6px 12px;\n border-radius: 16px;\n color: var(--zx-primary-color, #1e5efd);\n font-size: 14px;\n}\n\n.loading-spinner {\n width: 18px;\n height: 18px;\n border-radius: 50%;\n border: 2.5px solid var(--zx-primary-color, #1e5efd);\n border-top-color: transparent;\n animation: spin 0.8s linear infinite;\n}\n\n@keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n.loading-container.fullscreen {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: rgba(255, 255, 255, 0.5);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue';\n\ndefineOptions({ name: 'EmptyComponent' });\n\ninterface Props {\n text?: string;\n showText?: boolean;\n /** 顶部间距,可传数字(px)或带单位字符串,默认 168px */\n paddingTop?: number | string;\n /** 自定义图片 URL */\n emptyImage?: string;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n text: '暂无数据',\n showText: true,\n paddingTop: 168,\n emptyImage: ''\n});\n\nconst wrapStyle = computed(() => ({\n paddingTop: typeof props.paddingTop === 'number' ? `${props.paddingTop}px` : props.paddingTop\n}));\n\n// 默认空状态 SVG(简洁的空文件夹图标)\nconst defaultEmptySvg = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='80' viewBox='0 0 96 80' fill='none'%3E%3Cpath d='M8 16C8 12.6863 10.6863 10 14 10H38L44 16H82C85.3137 16 88 18.6863 88 22V66C88 69.3137 85.3137 72 82 72H14C10.6863 72 8 69.3137 8 66V16Z' fill='%23E8E8E8'/%3E%3Cpath d='M8 20C8 18.3431 9.34315 17 11 17H85C86.6569 17 88 18.3431 88 20V66C88 69.3137 85.3137 72 82 72H14C10.6863 72 8 69.3137 8 66V20Z' fill='%23F5F5F5'/%3E%3Cpath d='M38 42C38 40.8954 38.8954 40 40 40H56C57.1046 40 58 40.8954 58 42V44C58 45.1046 57.1046 46 56 46H40C38.8954 46 38 45.1046 38 44V42Z' fill='%23D9D9D9'/%3E%3C/svg%3E`;\n\nconst imageSrc = computed(() => props.emptyImage || defaultEmptySvg);\n</script>\n\n<template>\n <div class=\"empty-wrap\" :style=\"wrapStyle\">\n <img class=\"empty-image\" :src=\"imageSrc\" alt=\"\" />\n <p v-if=\"showText\" class=\"empty-text\">\n <slot>{{ text }}</slot>\n </p>\n </div>\n</template>\n\n<style scoped lang=\"scss\">\n.empty-wrap {\n display: flex;\n flex-direction: column;\n align-items: center;\n}\n\n.empty-image {\n width: 96px;\n height: 80px;\n}\n\n.empty-text {\n margin: 12px 0 0;\n color: #8c8f97;\n font-size: 12px;\n line-height: 22px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';\nimport RequestLoading from './request-loading.vue';\nimport EmptyComponent from './empty-component.vue';\n\ndefineOptions({\n name: 'InfiniteScroll'\n});\n\ninterface Props {\n /** 是否处于加载中状态 */\n loading?: boolean;\n /** 是否已加载完所有数据 */\n finished?: boolean;\n /** 是否显示空状态 */\n isEmpty?: boolean;\n /** 空状态文案 */\n emptyText?: string;\n /** 是否启用下拉刷新 */\n pullRefresh?: boolean;\n /** 下拉刷新触发距离(px) */\n pullThreshold?: number;\n /** 上拉加载触发距离(距离底部 px) */\n loadThreshold?: number;\n /** 下拉中的文案 */\n pullingText?: string;\n /** 释放刷新的文案 */\n loosingText?: string;\n /** 刷新中的文案 */\n refreshingText?: string;\n /** 加载中的文案 */\n loadingText?: string;\n /** 加载完成的文案 */\n finishedText?: string;\n /** 加载失败的文案 */\n errorText?: string;\n /** 是否显示错误状态 */\n error?: boolean;\n /** 内容区域是否显示加载状态 */\n contentLoading?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n loading: false,\n finished: false,\n isEmpty: false,\n emptyText: '暂无数据',\n pullRefresh: false,\n pullThreshold: 70,\n loadThreshold: 50,\n pullingText: '下拉刷新',\n loosingText: '释放即可刷新',\n refreshingText: '加载中...',\n loadingText: '加载中...',\n finishedText: '没有更多了~',\n errorText: '加载失败,点击重试',\n error: false,\n contentLoading: false\n});\n\nconst emit = defineEmits<{\n /** 触发下拉刷新 */\n refresh: [];\n /** 触发上拉加载 */\n load: [];\n /** 点击错误重试 */\n retry: [];\n}>();\n\nconst containerRef = ref<HTMLElement | null>(null);\nconst contentRef = ref<HTMLElement | null>(null);\nconst indicatorRef = ref<HTMLElement | null>(null);\n\n// 下拉刷新状态\nconst pullStatus = ref<'idle' | 'pulling' | 'loosing' | 'refreshing'>('idle');\nconst pullDistance = ref(0);\n// 拖动期间禁用 transition,避免 transform 变化时被 0.25s 补间拖慢\nconst isDragging = ref(false);\n\n// 触摸相关\nlet startX = 0;\nlet startY = 0;\nlet lastDistance = 0;\n// 手势方向锁:避免横向滑动被误判为下拉\nlet gestureLock: 'none' | 'vertical' | 'horizontal' = 'none';\nconst SLOPE_LOCK_DISTANCE = 8;\n\nlet isRefreshing = false;\nlet isLoading = false;\n// 鼠标拖拽下拉(PC 端)\nlet isMouseDown = false;\n\n// 节流相关\nlet scrollTimer: number | null = null;\nlet touchMoveRaf: number | null = null;\nlet idleTimer: number | null = null;\n// 监听容器尺寸变化\nlet resizeObserver: ResizeObserver | null = null;\n// 防止空页死循环:snapshot 触发 load 时的 contentHeight,下次完成后没增长就不再 auto-load\nlet lastTriggerContentHeight = -1;\n\n// 下拉刷新核心:按下(与指针类型无关,touch/mouse 共用)\nfunction pullStart(clientX: number, clientY: number) {\n if (!props.pullRefresh || isRefreshing) return;\n startX = clientX;\n startY = clientY;\n lastDistance = 0;\n gestureLock = 'none';\n pullStatus.value = 'idle';\n}\n\n// 下拉刷新核心:移动(rAF 节流,始终使用最新位置)\n// 返回 true 表示消费了本次移动(下拉进行中),调用方据此决定是否 preventDefault\nfunction pullMove(clientX: number, clientY: number, cancelable: boolean): boolean {\n if (!props.pullRefresh || isRefreshing || startY === 0) return false;\n\n const dx = clientX - startX;\n const dy = clientY - startY;\n\n // 方向判定:先到达 8px 锁定的方向才认为有意图,垂直要明显大于水平(1.5x)\n if (gestureLock === 'none') {\n const ax = Math.abs(dx);\n const ay = Math.abs(dy);\n if (ax > SLOPE_LOCK_DISTANCE || ay > SLOPE_LOCK_DISTANCE) {\n gestureLock = ay > ax * 1.5 ? 'vertical' : 'horizontal';\n } else {\n return false;\n }\n }\n if (gestureLock === 'horizontal') return false;\n\n // 子像素容差:≤1 视为已到顶,避免 X5 内核 scrollTop 偶发非整数判断失败\n const atTop = getScrollTop() <= 1;\n\n // iOS 15- 兜底:在 atBottom 且向上拖时阻止整页 rubber-band\n // (iOS 16+ overscroll-behavior 已处理,此处兜底兼容)\n if (!atTop && dy < 0) {\n const c = containerRef.value;\n if (c) {\n const atBottom = c.scrollHeight - c.scrollTop - c.clientHeight <= 1;\n if (atBottom && cancelable) {\n return true;\n }\n }\n return false;\n }\n if (!atTop) return false;\n\n // 反向拖动也要更新(让指示器跟着手指回退),夹紧到 [0, 150]\n const clamped = Math.max(0, Math.min(dy, 150));\n\n lastDistance = clamped;\n isDragging.value = true;\n\n if (touchMoveRaf === null) {\n touchMoveRaf = requestAnimationFrame(() => {\n touchMoveRaf = null;\n pullDistance.value = lastDistance * 0.5;\n if (lastDistance === 0) {\n pullStatus.value = 'idle';\n } else {\n pullStatus.value = lastDistance > props.pullThreshold ? 'loosing' : 'pulling';\n }\n });\n }\n\n // 仅在向下拖时消费事件(避免误吞用户向上滚动的意图)\n return dy > 0;\n}\n\n// 下拉刷新核心:松开\nfunction pullEnd() {\n if (!props.pullRefresh || isRefreshing) return;\n\n // 取消待执行的 rAF,避免松手后还有一帧异步更新把 UI 拉回 loosing 卡住\n if (touchMoveRaf !== null) {\n cancelAnimationFrame(touchMoveRaf);\n touchMoveRaf = null;\n }\n\n isDragging.value = false;\n\n // 用最新的 lastDistance 同步判定,不依赖可能尚未生效的 pullStatus\n if (lastDistance > props.pullThreshold) {\n isRefreshing = true;\n pullStatus.value = 'refreshing';\n pullDistance.value = 50;\n emit('refresh');\n } else {\n pullDistance.value = 0;\n pullStatus.value = 'idle';\n }\n\n resetTouch();\n}\n\n// 下拉刷新核心:取消(回到 idle)\nfunction pullCancel() {\n if (!props.pullRefresh || isRefreshing) return;\n\n if (touchMoveRaf !== null) {\n cancelAnimationFrame(touchMoveRaf);\n touchMoveRaf = null;\n }\n\n isDragging.value = false;\n pullDistance.value = 0;\n pullStatus.value = 'idle';\n resetTouch();\n}\n\n// 下拉刷新 - touchstart\nfunction handleTouchStart(e: TouchEvent) {\n pullStart(e.touches[0].clientX, e.touches[0].clientY);\n}\n\n// 下拉刷新 - touchmove\nfunction handleTouchMove(e: TouchEvent) {\n const touch = e.touches[0];\n if (pullMove(touch.clientX, touch.clientY, e.cancelable) && e.cancelable) {\n e.preventDefault();\n }\n}\n\n// 下拉刷新 - touchend\nfunction handleTouchEnd() {\n pullEnd();\n}\n\n// 下拉刷新 - touchcancel(微信 X5 内核会在系统手势介入时派发 cancel 而非 end)\nfunction handleTouchCancel() {\n pullCancel();\n}\n\n// 下拉刷新 - mousedown(PC 端鼠标拖拽)\nfunction handleMouseDown(e: MouseEvent) {\n // 仅左键触发\n if (!props.pullRefresh || isRefreshing || e.button !== 0) return;\n isMouseDown = true;\n pullStart(e.clientX, e.clientY);\n}\n\n// 下拉刷新 - mousemove(挂在 window 上,允许拖出容器)\nfunction handleMouseMove(e: MouseEvent) {\n if (!isMouseDown) return;\n if (pullMove(e.clientX, e.clientY, true)) {\n // 阻止拖拽时选中文本\n e.preventDefault();\n }\n}\n\n// 下拉刷新 - mouseup\nfunction handleMouseUp() {\n if (!isMouseDown) return;\n isMouseDown = false;\n pullEnd();\n}\n\nfunction resetTouch() {\n startX = 0;\n startY = 0;\n lastDistance = 0;\n gestureLock = 'none';\n}\n\n// 上拉加载 - scroll(节流处理)\nfunction handleScroll() {\n if (props.loading || props.finished || props.error || isLoading) return;\n if (scrollTimer) return;\n\n scrollTimer = window.setTimeout(() => {\n scrollTimer = null;\n const container = containerRef.value;\n if (!container) return;\n\n const { scrollTop, clientHeight, scrollHeight } = container;\n const contentHeight = contentRef.value?.clientHeight || 0;\n\n // 情况1:内容不足一屏,自动触发加载\n if (contentHeight <= clientHeight) {\n // 死循环防护:上一次 auto-load 后内容没增长,不再继续触发\n if (lastTriggerContentHeight === contentHeight) return;\n lastTriggerContentHeight = contentHeight;\n // 不足一屏时主动滚到底,让 loading 指示器进入视口\n triggerLoad(true);\n return;\n }\n\n // 情况2:滚动到距离底部阈值范围内\n if (scrollHeight - scrollTop - clientHeight <= props.loadThreshold) {\n // 用户已经手动滚到底,不再强制 scrollTop(避免打断惯性滚动)\n triggerLoad(false);\n }\n }, 100);\n}\n\nfunction triggerLoad(scrollToBottom = false) {\n isLoading = true;\n emit('load');\n nextTick(() => {\n const c = containerRef.value;\n if (!c) return;\n if (scrollToBottom) {\n // 内容不足一屏:直接滚到底(没有\"惯性\"可以打断)\n c.scrollTop = c.scrollHeight;\n } else {\n // 阈值触发:用 scrollIntoView 平滑进入视口,避免突兀打断;\n // 旧 WebKit 不识别 options 会降级为即时滚动,效果等价\n indicatorRef.value?.scrollIntoView({ block: 'end', behavior: 'smooth' });\n }\n });\n}\n\n// 获取滚动距离\nfunction getScrollTop(): number {\n return containerRef.value?.scrollTop ?? 0;\n}\n\n// 完成下拉刷新\nfunction finishRefresh() {\n isRefreshing = false;\n pullDistance.value = 0;\n // 让 transform snap-back 完成后再切 idle,避免 spinner 在动画中途突然消失\n if (idleTimer !== null) clearTimeout(idleTimer);\n idleTimer = window.setTimeout(() => {\n pullStatus.value = 'idle';\n idleTimer = null;\n }, 250);\n // 重置死循环计数\n lastTriggerContentHeight = -1;\n nextTick(() => {\n checkContentHeight();\n });\n}\n\n// 完成上拉加载\nfunction finishLoad() {\n isLoading = false;\n nextTick(() => {\n const contentHeight = contentRef.value?.clientHeight || 0;\n // 内容没增长就不再触发 auto-load,避免空页死循环\n if (lastTriggerContentHeight !== -1 && contentHeight === lastTriggerContentHeight) return;\n lastTriggerContentHeight = -1;\n checkContentHeight();\n });\n}\n\n// 检查内容高度,不足一屏时自动触发加载\nfunction checkContentHeight() {\n if (props.finished || props.loading || props.error) return;\n\n nextTick(() => {\n const contentHeight = contentRef.value?.clientHeight || 0;\n const clientHeight = containerRef.value?.clientHeight || 0;\n\n if (contentHeight <= clientHeight && !isLoading) {\n handleScroll();\n }\n });\n}\n\n// 点击错误重试 — 由父组件决定具体行为,避免 retry + load 双触发\nfunction handleRetry() {\n emit('retry');\n}\n\n// loading 完成时同步重置 isLoading\nwatch(\n () => props.loading,\n newVal => {\n if (!newVal) {\n isLoading = false;\n }\n }\n);\n\n// finished 翻为 true 时重置死循环计数\nwatch(\n () => props.finished,\n newVal => {\n if (newVal) {\n lastTriggerContentHeight = -1;\n }\n }\n);\n\nonMounted(() => {\n const container = containerRef.value;\n if (!container) return;\n\n if (props.pullRefresh) {\n container.addEventListener('touchstart', handleTouchStart, { passive: true });\n container.addEventListener('touchmove', handleTouchMove, { passive: false });\n container.addEventListener('touchend', handleTouchEnd, { passive: true });\n container.addEventListener('touchcancel', handleTouchCancel, { passive: true });\n // 鼠标拖拽(PC):move/up 挂在 window 上,允许拖出容器边界\n container.addEventListener('mousedown', handleMouseDown);\n window.addEventListener('mousemove', handleMouseMove);\n window.addEventListener('mouseup', handleMouseUp);\n }\n\n container.addEventListener('scroll', handleScroll, { passive: true });\n\n // 监听容器/内容尺寸变化(旋转、WebView resize、软键盘弹出收起等)\n if (typeof ResizeObserver !== 'undefined') {\n resizeObserver = new ResizeObserver(() => {\n checkContentHeight();\n });\n resizeObserver.observe(container);\n if (contentRef.value) resizeObserver.observe(contentRef.value);\n }\n\n // 初始检查内容高度\n nextTick(() => {\n checkContentHeight();\n });\n});\n\nonBeforeUnmount(() => {\n const container = containerRef.value;\n if (container) {\n container.removeEventListener('touchstart', handleTouchStart);\n container.removeEventListener('touchmove', handleTouchMove);\n container.removeEventListener('touchend', handleTouchEnd);\n container.removeEventListener('touchcancel', handleTouchCancel);\n container.removeEventListener('mousedown', handleMouseDown);\n container.removeEventListener('scroll', handleScroll);\n }\n window.removeEventListener('mousemove', handleMouseMove);\n window.removeEventListener('mouseup', handleMouseUp);\n if (resizeObserver) {\n resizeObserver.disconnect();\n resizeObserver = null;\n }\n if (touchMoveRaf !== null) {\n cancelAnimationFrame(touchMoveRaf);\n touchMoveRaf = null;\n }\n if (scrollTimer !== null) {\n clearTimeout(scrollTimer);\n scrollTimer = null;\n }\n if (idleTimer !== null) {\n clearTimeout(idleTimer);\n idleTimer = null;\n }\n});\n\ndefineExpose({\n refresh: finishRefresh,\n finishRefresh,\n finishPullDown: finishRefresh,\n finishLoad,\n finishPullUp: finishLoad,\n checkContentHeight\n});\n</script>\n\n<template>\n <div ref=\"containerRef\" class=\"infinite-scroll\">\n <!-- 下拉刷新指示器 -->\n <div\n v-if=\"pullRefresh\"\n class=\"pull-refresh-indicator\"\n :class=\"{ 'no-transition': isDragging }\"\n :style=\"{ transform: `translateY(${pullDistance}px)` }\"\n >\n <div v-show=\"pullStatus !== 'idle'\" class=\"pull-refresh-content\">\n <!-- <RequestLoading v-if=\"pullStatus === 'refreshing'\" text=\"\" /> -->\n <span v-if=\"pullStatus === 'refreshing'\"></span>\n <span v-else-if=\"pullStatus === 'loosing'\" class=\"pull-text\">{{ loosingText }}</span>\n <span v-else class=\"pull-text\">{{ pullingText }}</span>\n </div>\n </div>\n\n <!-- 内容区域 -->\n <div\n ref=\"contentRef\"\n class=\"infinite-scroll-content\"\n :class=\"{ 'no-transition': isDragging }\"\n :style=\"pullRefresh ? { transform: `translateY(${pullDistance}px)` } : {}\"\n >\n <!-- 内容区域加载遮罩 -->\n <div v-if=\"contentLoading\" class=\"content-loading-mask\">\n <RequestLoading :text=\"refreshingText\" />\n </div>\n\n <!-- 空状态 -->\n <EmptyComponent v-if=\"isEmpty && !loading\" :text=\"emptyText\" />\n\n <!-- 默认插槽 -->\n <slot></slot>\n\n <!-- 上拉加载指示器 -->\n <div v-if=\"!isEmpty\" ref=\"indicatorRef\" class=\"load-more-indicator\">\n <RequestLoading v-if=\"loading && pullStatus !== 'refreshing'\" :text=\"loadingText\" />\n <span v-else-if=\"finished\" class=\"finished-text\">{{ finishedText }}</span>\n <span v-else-if=\"error\" class=\"error-text\" @click=\"handleRetry\">{{ errorText }}</span>\n </div>\n </div>\n </div>\n</template>\n\n<style scoped lang=\"scss\">\n.infinite-scroll {\n height: 100%;\n position: relative;\n min-height: 100%;\n overflow-y: auto;\n overflow-x: hidden;\n // 阻止滚动到边界时把滚动链传递给祖先(修复 iOS 16+ / 微信 X5 下拉变成整页拖拽)\n overscroll-behavior: contain;\n // 显式声明只接受纵向手势,避免被外层 touch-action 影响\n touch-action: pan-y;\n // iOS 下让原生回弹有惯性\n -webkit-overflow-scrolling: touch;\n}\n\n.pull-refresh-indicator {\n position: absolute;\n top: -50px;\n left: 0;\n right: 0;\n height: 50px;\n transition: transform 0.25s;\n\n // 拖动期间禁用过渡,让 transform 1:1 跟随手指\n &.no-transition {\n transition: none;\n }\n}\n\n.pull-refresh-content {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n font-size: 14px;\n color: #969799;\n}\n\n.pull-text {\n color: var(--zx-primary-color, #1989fa);\n font-size: 14px;\n}\n\n.infinite-scroll-content {\n position: relative;\n transition: transform 0.25s;\n min-height: 100%;\n\n &.no-transition {\n transition: none;\n }\n}\n\n.content-loading-mask {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(255, 255, 255, 0.8);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n backdrop-filter: blur(2px);\n}\n\n.load-more-indicator {\n display: flex;\n align-items: center;\n justify-content: center;\n // 预留高度避免 loading 渲染后被推到视口外造成遮挡\n min-height: 50px;\n padding: 16px 0;\n font-size: 14px;\n color: #8c8f97;\n}\n\n.finished-text {\n color: #8c8f97;\n font-size: 14px;\n}\n\n.error-text {\n color: #ee0a24;\n font-size: 14px;\n cursor: pointer;\n\n &:active {\n opacity: 0.7;\n }\n}\n</style>\n","<script setup lang=\"ts\" generic=\"T\">\nimport { type Ref, nextTick, onMounted, ref, watch } from 'vue';\nimport InfiniteScroll from './infinite-scroll.vue';\n\ndefineOptions({\n name: 'InfiniteScrollV2'\n});\n\nconst props = withDefaults(\n defineProps<{\n /** 数据请求函数,需返回 { records, total },失败时抛错 */\n fetchFn: (params: {\n page: number;\n pageSize: number;\n [key: string]: any;\n }) => Promise<{\n records: T[];\n total: number;\n }>;\n /** 业务查询参数(如筛选条件),变化时自动 reset 重新加载 */\n params?: Record<string, any>;\n /** 每页条数 */\n pageSize?: number;\n /** params 变化时是否自动重新加载 */\n autoReload?: boolean;\n /** 是否在挂载时自动加载首页 */\n immediate?: boolean;\n /** 是否启用下拉刷新 */\n pullRefresh?: boolean;\n /** 下拉刷新触发距离(px) */\n pullThreshold?: number;\n /** 上拉加载触发距离(距离底部 px) */\n loadThreshold?: number;\n /** 空状态文案 */\n emptyText?: string;\n /** 下拉中的文案 */\n pullingText?: string;\n /** 释放刷新的文案 */\n loosingText?: string;\n /** 刷新中的文案 */\n refreshingText?: string;\n /** 加载中的文案 */\n loadingText?: string;\n /** 加载完成的文案 */\n finishedText?: string;\n /** 加载失败的文案 */\n errorText?: string;\n }>(),\n {\n params: () => ({}),\n pageSize: 10,\n autoReload: true,\n immediate: true,\n pullRefresh: false,\n pullThreshold: 70,\n loadThreshold: 50,\n emptyText: '暂无数据',\n pullingText: '下拉刷新',\n loosingText: '释放即可刷新',\n refreshingText: '加载中...',\n loadingText: '加载中...',\n finishedText: '没有更多了~',\n errorText: '加载失败,点击重试'\n});\n\nconst emit = defineEmits<{\n /** 加载成功,每次成功(含分页)都会触发 */\n success: [payload: { records: T[]; total: number; reset: boolean }];\n /** 加载失败 */\n error: [err: unknown];\n /** 列表内容变化(v-model:list 支持) */\n 'update:list': [list: T[]];\n}>();\n\nconst scrollRef = ref<InstanceType<typeof InfiniteScroll> | null>(null);\n\nconst list = ref<T[]>([]) as Ref<T[]>;\nconst loading = ref(false);\nconst finished = ref(false);\nconst isEmpty = ref(false);\nconst loadError = ref(false);\nconst total = ref(0);\nconst page = ref(1);\nconst isRefreshing = ref(false);\n\nlet requestSeq = 0;\n\nasync function load(reset = false) {\n if (reset) {\n page.value = 1;\n finished.value = false;\n isEmpty.value = false;\n // 不立即清空 list,保留旧数据直到新数据到达\n }\n loading.value = true;\n loadError.value = false;\n\n requestSeq += 1;\n const seq = requestSeq;\n\n try {\n const result = await props.fetchFn({\n page: page.value,\n pageSize: props.pageSize,\n ...props.params\n });\n\n if (seq !== requestSeq) return;\n\n const records = result?.records || [];\n if (reset) {\n // reset 时替换整个列表\n list.value = records;\n total.value = 0;\n } else {\n list.value = [...list.value, ...records];\n }\n total.value = Number(result?.total || 0);\n finished.value = list.value.length >= total.value;\n isEmpty.value = list.value.length === 0;\n\n emit('success', { records, total: total.value, reset });\n emit('update:list', list.value);\n } catch (err) {\n if (seq !== requestSeq) return;\n loadError.value = true;\n emit('error', err);\n } finally {\n if (seq === requestSeq) {\n loading.value = false;\n }\n }\n}\n\nasync function handleRefresh() {\n isRefreshing.value = true;\n // 先让下拉刷新指示器收回\n scrollRef.value?.finishRefresh();\n // 等待 250ms 让指示器完全收回(对应 infinite-scroll.vue 的 transition 时长)\n await new Promise<void>(resolve => {\n setTimeout(() => resolve(), 250);\n });\n // 指示器收回后再开始请求新数据\n await load(true);\n isRefreshing.value = false;\n}\n\nasync function handleLoad() {\n if (finished.value) {\n scrollRef.value?.finishLoad();\n return;\n }\n // 列表为空时是首次加载(page 已经是 1),其它情况 page++\n if (list.value.length > 0) {\n page.value += 1;\n }\n await load(false);\n scrollRef.value?.finishLoad();\n}\n\nfunction handleRetry() {\n loadError.value = false;\n if (list.value.length === 0) {\n load(true);\n } else {\n handleLoad();\n }\n}\n\nasync function reload() {\n await load(true);\n}\n\nwatch(\n () => props.params,\n () => {\n if (props.autoReload) {\n reload();\n }\n },\n { deep: true }\n);\n\nwatch(\n list,\n async () => {\n await nextTick();\n scrollRef.value?.checkContentHeight();\n },\n { deep: true }\n);\n\nonMounted(() => {\n if (props.immediate) {\n load(true);\n }\n});\n\ndefineExpose({\n /** 重新加载第一页 */\n reload,\n /** 兼容别名 */\n refresh: reload,\n /** 当前列表数据 */\n list,\n /** 加载中状态 */\n loading,\n /** 是否已加载完 */\n finished,\n /** 是否空数据 */\n isEmpty,\n /** 总条数 */\n total,\n /** 透传 v1 方法 */\n finishLoad: () => scrollRef.value?.finishLoad(),\n finishRefresh: () => scrollRef.value?.finishRefresh(),\n checkContentHeight: () => scrollRef.value?.checkContentHeight()\n});\n</script>\n\n<template>\n <InfiniteScroll\n ref=\"scrollRef\"\n :loading=\"loading\"\n :finished=\"finished\"\n :is-empty=\"isEmpty\"\n :error=\"loadError\"\n :pull-refresh=\"pullRefresh\"\n :pull-threshold=\"pullThreshold\"\n :load-threshold=\"loadThreshold\"\n :empty-text=\"emptyText\"\n :pulling-text=\"pullingText\"\n :loosing-text=\"loosingText\"\n :refreshing-text=\"refreshingText\"\n :loading-text=\"loadingText\"\n :finished-text=\"finishedText\"\n :error-text=\"errorText\"\n :content-loading=\"isRefreshing\"\n @load=\"handleLoad\"\n @refresh=\"handleRefresh\"\n @retry=\"handleRetry\"\n >\n <slot\n :list=\"list\"\n :loading=\"loading\"\n :finished=\"finished\"\n :is-empty=\"isEmpty\"\n :error=\"loadError\"\n :total=\"total\"\n :reload=\"reload\"\n />\n </InfiniteScroll>\n</template>\n","// 样式自动注入\nlet styleInjected = false;\n\nconst cssContent = `.loading-content[data-v-d89150ba]{display:flex;align-items:center;justify-content:center;gap:8px;padding:6px 12px;border-radius:16px;color:var(--zx-primary-color, #1e5efd);font-size:14px}.loading-spinner[data-v-d89150ba]{width:18px;height:18px;border-radius:50%;border:2.5px solid var(--zx-primary-color, #1e5efd);border-top-color:transparent;animation:spin-d89150ba .8s linear infinite}@keyframes spin-d89150ba{to{transform:rotate(360deg)}}.loading-container.fullscreen[data-v-d89150ba]{position:fixed;top:0;left:0;width:100%;height:100%;display:flex;align-items:center;justify-content:center;background-color:#ffffff80}.empty-wrap[data-v-3c452901]{display:flex;flex-direction:column;align-items:center}.empty-image[data-v-3c452901]{width:96px;height:80px}.empty-text[data-v-3c452901]{margin:12px 0 0;color:#8c8f97;font-size:12px;line-height:22px}.infinite-scroll[data-v-bf5ca1d9]{height:100%;position:relative;min-height:100%;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;touch-action:pan-y;-webkit-overflow-scrolling:touch}.pull-refresh-indicator[data-v-bf5ca1d9]{position:absolute;top:-50px;left:0;right:0;height:50px;transition:transform .25s}.pull-refresh-indicator.no-transition[data-v-bf5ca1d9]{transition:none}.pull-refresh-content[data-v-bf5ca1d9]{display:flex;align-items:center;justify-content:center;height:100%;font-size:14px;color:#969799}.pull-text[data-v-bf5ca1d9]{color:var(--zx-primary-color, #1989fa);font-size:14px}.infinite-scroll-content[data-v-bf5ca1d9]{position:relative;transition:transform .25s;min-height:100%}.infinite-scroll-content.no-transition[data-v-bf5ca1d9]{transition:none}.content-loading-mask[data-v-bf5ca1d9]{position:absolute;top:0;left:0;right:0;bottom:0;background-color:#fffc;display:flex;align-items:center;justify-content:center;z-index:10;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.load-more-indicator[data-v-bf5ca1d9]{display:flex;align-items:center;justify-content:center;min-height:50px;padding:16px 0;font-size:14px;color:#8c8f97}.finished-text[data-v-bf5ca1d9]{color:#8c8f97;font-size:14px}.error-text[data-v-bf5ca1d9]{color:#ee0a24;font-size:14px;cursor:pointer}.error-text[data-v-bf5ca1d9]:active{opacity:.7}`;\n\nexport function injectStyle() {\n if (styleInjected || typeof document === 'undefined') return;\n\n const style = document.createElement('style');\n style.setAttribute('data-source', 'zx-infinite-scroll');\n style.textContent = cssContent;\n document.head.appendChild(style);\n\n styleInjected = true;\n}\n","import { App } from 'vue';\nimport InfiniteScroll from './components/infinite-scroll.vue';\nimport InfiniteScrollV2 from './components/infinite-scroll-v2.vue';\nimport RequestLoading from './components/request-loading.vue';\nimport EmptyComponent from './components/empty-component.vue';\nimport { injectStyle } from './inject-style';\n\n// 自动注入样式(只要导入了这个包就会执行)\ninjectStyle();\n\n// 单独导出组件\nexport { InfiniteScroll, InfiniteScrollV2, RequestLoading, EmptyComponent };\n\n// Vue plugin install 函数\nexport function install(app: App) {\n app.component('InfiniteScroll', InfiniteScroll);\n app.component('InfiniteScrollV2', InfiniteScrollV2);\n app.component('RequestLoading', RequestLoading);\n app.component('EmptyComponent', EmptyComponent);\n}\n\n// 默认导出(支持 app.use() 安装)\nexport default {\n install\n};\n"],"names":["_createElementBlock","_normalizeClass","__props","_createElementVNode","_hoisted_1","_hoisted_2","_toDisplayString","defaultEmptySvg","props","wrapStyle","computed","imageSrc","_openBlock","_renderSlot","_ctx","SLOPE_LOCK_DISTANCE","emit","__emit","containerRef","ref","contentRef","indicatorRef","pullStatus","pullDistance","isDragging","startX","startY","lastDistance","gestureLock","isRefreshing","isLoading","isMouseDown","scrollTimer","touchMoveRaf","idleTimer","resizeObserver","lastTriggerContentHeight","pullStart","clientX","clientY","pullMove","cancelable","dx","dy","ax","ay","atTop","getScrollTop","c","pullEnd","resetTouch","pullCancel","handleTouchStart","handleTouchMove","touch","handleTouchEnd","handleTouchCancel","handleMouseDown","handleMouseMove","handleMouseUp","handleScroll","container","scrollTop","clientHeight","scrollHeight","contentHeight","_a","triggerLoad","scrollToBottom","nextTick","finishRefresh","checkContentHeight","finishLoad","_b","handleRetry","watch","newVal","onMounted","onBeforeUnmount","__expose","_withDirectives","_hoisted_3","_hoisted_4","_normalizeStyle","_hoisted_5","_createVNode","RequestLoading","_createBlock","EmptyComponent","_hoisted_6","scrollRef","list","loading","finished","isEmpty","loadError","total","page","requestSeq","load","reset","seq","result","records","err","handleRefresh","resolve","handleLoad","reload","InfiniteScroll","styleInjected","cssContent","injectStyle","style","install","app","InfiniteScrollV2","index"],"mappings":";;;;;;;;;2BAUEA,EAKM,OAAA;AAAA,MALD,OAAKC,EAAA,CAAC,qBAAmB,EAAA,YAAuBC,EAAA,YAAU,CAAA;AAAA,IAAA;MAC7DC,EAGM,OAHNC,IAGM;AAAA,wBAFJD,EAAqC,QAAA,EAA/B,OAAM,kBAAA,GAAiB,MAAA,EAAA;AAAA,QAC7BA,EAA4C,QAA5CE,IAA4CC,EAAdJ,EAAA,IAAI,GAAA,CAAA;AAAA,MAAA;;;;;;;;;;;GCalCK,KAAkB;;;;;;;;;;AAZxB,UAAMC,IAAQN,GAORO,IAAYC,GAAS,OAAO;AAAA,MAChC,YAAY,OAAOF,EAAM,cAAe,WAAW,GAAGA,EAAM,UAAU,OAAOA,EAAM;AAAA,IAAA,EACnF,GAKIG,IAAWD,GAAS,MAAMF,EAAM,cAAcD,EAAe;2BAIjEP,EAKM,OAAA;AAAA,MALD,OAAM;AAAA,MAAc,SAAOS,EAAA,KAAS;AAAA,IAAA;MACvCN,EAAkD,OAAA;AAAA,QAA7C,OAAM;AAAA,QAAe,KAAKQ,EAAA;AAAA,QAAU,KAAI;AAAA,MAAA;MACpCT,EAAA,YAATU,EAAA,GAAAZ,EAEI,KAFJK,IAEI;AAAA,QADFQ,EAAuBC,yBAAvB,MAAuB;AAAA,eAAdZ,EAAA,IAAI,GAAA,CAAA;AAAA,QAAA;;;;;;;;;;;;;;;;GCkDba,KAAsB;;;;;;;;;;;;;;;;;;;;;;AA3C5B,UAAMP,IAAQN,GAkBRc,IAAOC,GASPC,IAAeC,EAAwB,IAAI,GAC3CC,IAAaD,EAAwB,IAAI,GACzCE,IAAeF,EAAwB,IAAI,GAG3CG,IAAaH,EAAmD,MAAM,GACtEI,IAAeJ,EAAI,CAAC,GAEpBK,IAAaL,EAAI,EAAK;AAG5B,QAAIM,IAAS,GACTC,IAAS,GACTC,IAAe,GAEfC,IAAkD,QAGlDC,IAAe,IACfC,IAAY,IAEZC,IAAc,IAGdC,IAA6B,MAC7BC,IAA8B,MAC9BC,IAA2B,MAE3BC,IAAwC,MAExCC,IAA2B;AAG/B,aAASC,EAAUC,GAAiBC,GAAiB;AACnD,MAAI,CAAC/B,EAAM,eAAeqB,MAC1BJ,IAASa,GACTZ,IAASa,GACTZ,IAAe,GACfC,IAAc,QACdN,EAAW,QAAQ;AAAA,IACrB;AAIA,aAASkB,EAASF,GAAiBC,GAAiBE,GAA8B;AAChF,UAAI,CAACjC,EAAM,eAAeqB,KAAgBH,MAAW,EAAG,QAAO;AAE/D,YAAMgB,IAAKJ,IAAUb,GACfkB,IAAKJ,IAAUb;AAGrB,UAAIE,MAAgB,QAAQ;AAC1B,cAAMgB,IAAK,KAAK,IAAIF,CAAE,GAChBG,IAAK,KAAK,IAAIF,CAAE;AACtB,YAAIC,IAAK7B,MAAuB8B,IAAK9B;AACnC,UAAAa,IAAciB,IAAKD,IAAK,MAAM,aAAa;AAAA;AAE3C,iBAAO;AAAA,MAEX;AACA,UAAIhB,MAAgB,aAAc,QAAO;AAGzC,YAAMkB,IAAQC,QAAkB;AAIhC,UAAI,CAACD,KAASH,IAAK,GAAG;AACpB,cAAMK,IAAI9B,EAAa;AACvB,eAAI,GAAA8B,KACeA,EAAE,eAAeA,EAAE,YAAYA,EAAE,gBAAgB,KAClDP;AAAA,MAKpB;AACA,aAAKK,KAKLnB,IAFgB,KAAK,IAAI,GAAG,KAAK,IAAIgB,GAAI,GAAG,CAAC,GAG7CnB,EAAW,QAAQ,IAEfS,MAAiB,SACnBA,IAAe,sBAAsB,MAAM;AACzC,QAAAA,IAAe,MACfV,EAAa,QAAQI,IAAe,KAChCA,MAAiB,IACnBL,EAAW,QAAQ,SAEnBA,EAAW,QAAQK,IAAenB,EAAM,gBAAgB,YAAY;AAAA,MAExE,CAAC,IAIImC,IAAK,KArBO;AAAA,IAsBrB;AAGA,aAASM,IAAU;AACjB,MAAI,CAACzC,EAAM,eAAeqB,MAGtBI,MAAiB,SACnB,qBAAqBA,CAAY,GACjCA,IAAe,OAGjBT,EAAW,QAAQ,IAGfG,IAAenB,EAAM,iBACvBqB,IAAe,IACfP,EAAW,QAAQ,cACnBC,EAAa,QAAQ,IACrBP,EAAK,SAAS,MAEdO,EAAa,QAAQ,GACrBD,EAAW,QAAQ,SAGrB4B,GAAA;AAAA,IACF;AAGA,aAASC,KAAa;AACpB,MAAI,CAAC3C,EAAM,eAAeqB,MAEtBI,MAAiB,SACnB,qBAAqBA,CAAY,GACjCA,IAAe,OAGjBT,EAAW,QAAQ,IACnBD,EAAa,QAAQ,GACrBD,EAAW,QAAQ,QACnB4B,GAAA;AAAA,IACF;AAGA,aAASE,EAAiB,GAAe;AACvC,MAAAf,EAAU,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,OAAO;AAAA,IACtD;AAGA,aAASgB,EAAgB,GAAe;AACtC,YAAMC,IAAQ,EAAE,QAAQ,CAAC;AACzB,MAAId,EAASc,EAAM,SAASA,EAAM,SAAS,EAAE,UAAU,KAAK,EAAE,cAC5D,EAAE,eAAA;AAAA,IAEN;AAGA,aAASC,KAAiB;AACxB,MAAAN,EAAA;AAAA,IACF;AAGA,aAASO,KAAoB;AAC3B,MAAAL,GAAA;AAAA,IACF;AAGA,aAASM,GAAgB,GAAe;AAEtC,MAAI,CAACjD,EAAM,eAAeqB,KAAgB,EAAE,WAAW,MACvDE,IAAc,IACdM,EAAU,EAAE,SAAS,EAAE,OAAO;AAAA,IAChC;AAGA,aAASqB,GAAgB,GAAe;AACtC,MAAK3B,KACDS,EAAS,EAAE,SAAS,EAAE,SAAS,EAAI,KAErC,EAAE,eAAA;AAAA,IAEN;AAGA,aAASmB,KAAgB;AACvB,MAAK5B,MACLA,IAAc,IACdkB,EAAA;AAAA,IACF;AAEA,aAASC,KAAa;AACpB,MAAAzB,IAAS,GACTC,IAAS,GACTC,IAAe,GACfC,IAAc;AAAA,IAChB;AAGA,aAASgC,IAAe;AACtB,MAAIpD,EAAM,WAAWA,EAAM,YAAYA,EAAM,SAASsB,KAClDE,MAEJA,IAAc,OAAO,WAAW,MAAM;;AACpC,QAAAA,IAAc;AACd,cAAM6B,IAAY3C,EAAa;AAC/B,YAAI,CAAC2C,EAAW;AAEhB,cAAM,EAAE,WAAAC,GAAW,cAAAC,GAAc,cAAAC,EAAA,IAAiBH,GAC5CI,MAAgBC,IAAA9C,EAAW,UAAX,gBAAA8C,EAAkB,iBAAgB;AAGxD,YAAID,KAAiBF,GAAc;AAEjC,cAAI3B,MAA6B6B,EAAe;AAChD,UAAA7B,IAA2B6B,GAE3BE,GAAY,EAAI;AAChB;AAAA,QACF;AAGA,QAAIH,IAAeF,IAAYC,KAAgBvD,EAAM,iBAEnD2D,GAAY,EAAK;AAAA,MAErB,GAAG,GAAG;AAAA,IACR;AAEA,aAASA,GAAYC,IAAiB,IAAO;AAC3C,MAAAtC,IAAY,IACZd,EAAK,MAAM,GACXqD,EAAS,MAAM;;AACb,cAAMrB,IAAI9B,EAAa;AACvB,QAAK8B,MACDoB,IAEFpB,EAAE,YAAYA,EAAE,gBAIhBkB,IAAA7C,EAAa,UAAb,QAAA6C,EAAoB,eAAe,EAAE,OAAO,OAAO,UAAU;MAEjE,CAAC;AAAA,IACH;AAGA,aAASnB,KAAuB;;AAC9B,eAAOmB,IAAAhD,EAAa,UAAb,gBAAAgD,EAAoB,cAAa;AAAA,IAC1C;AAGA,aAASI,IAAgB;AACvB,MAAAzC,IAAe,IACfN,EAAa,QAAQ,GAEjBW,MAAc,QAAM,aAAaA,CAAS,GAC9CA,IAAY,OAAO,WAAW,MAAM;AAClC,QAAAZ,EAAW,QAAQ,QACnBY,IAAY;AAAA,MACd,GAAG,GAAG,GAENE,IAA2B,IAC3BiC,EAAS,MAAM;AACb,QAAAE,EAAA;AAAA,MACF,CAAC;AAAA,IACH;AAGA,aAASC,KAAa;AACpB,MAAA1C,IAAY,IACZuC,EAAS,MAAM;;AACb,cAAMJ,MAAgBC,IAAA9C,EAAW,UAAX,gBAAA8C,EAAkB,iBAAgB;AAExD,QAAI9B,MAA6B,MAAM6B,MAAkB7B,MACzDA,IAA2B,IAC3BmC,EAAA;AAAA,MACF,CAAC;AAAA,IACH;AAGA,aAASA,IAAqB;AAC5B,MAAI/D,EAAM,YAAYA,EAAM,WAAWA,EAAM,SAE7C6D,EAAS,MAAM;;AACb,cAAMJ,MAAgBC,IAAA9C,EAAW,UAAX,gBAAA8C,EAAkB,iBAAgB,GAClDH,MAAeU,IAAAvD,EAAa,UAAb,gBAAAuD,EAAoB,iBAAgB;AAEzD,QAAIR,KAAiBF,KAAgB,CAACjC,KACpC8B,EAAA;AAAA,MAEJ,CAAC;AAAA,IACH;AAGA,aAASc,KAAc;AACrB,MAAA1D,EAAK,OAAO;AAAA,IACd;AAGA,WAAA2D;AAAA,MACE,MAAMnE,EAAM;AAAA,MACZ,CAAAoE,MAAU;AACR,QAAKA,MACH9C,IAAY;AAAA,MAEhB;AAAA,IAAA,GAIF6C;AAAA,MACE,MAAMnE,EAAM;AAAA,MACZ,CAAAoE,MAAU;AACR,QAAIA,MACFxC,IAA2B;AAAA,MAE/B;AAAA,IAAA,GAGFyC,GAAU,MAAM;AACd,YAAMhB,IAAY3C,EAAa;AAC/B,MAAK2C,MAEDrD,EAAM,gBACRqD,EAAU,iBAAiB,cAAcT,GAAkB,EAAE,SAAS,IAAM,GAC5ES,EAAU,iBAAiB,aAAaR,GAAiB,EAAE,SAAS,IAAO,GAC3EQ,EAAU,iBAAiB,YAAYN,IAAgB,EAAE,SAAS,IAAM,GACxEM,EAAU,iBAAiB,eAAeL,IAAmB,EAAE,SAAS,IAAM,GAE9EK,EAAU,iBAAiB,aAAaJ,EAAe,GACvD,OAAO,iBAAiB,aAAaC,EAAe,GACpD,OAAO,iBAAiB,WAAWC,EAAa,IAGlDE,EAAU,iBAAiB,UAAUD,GAAc,EAAE,SAAS,IAAM,GAGhE,OAAO,iBAAmB,QAC5BzB,IAAiB,IAAI,eAAe,MAAM;AACxC,QAAAoC,EAAA;AAAA,MACF,CAAC,GACDpC,EAAe,QAAQ0B,CAAS,GAC5BzC,EAAW,SAAOe,EAAe,QAAQf,EAAW,KAAK,IAI/DiD,EAAS,MAAM;AACb,QAAAE,EAAA;AAAA,MACF,CAAC;AAAA,IACH,CAAC,GAEDO,GAAgB,MAAM;AACpB,YAAMjB,IAAY3C,EAAa;AAC/B,MAAI2C,MACFA,EAAU,oBAAoB,cAAcT,CAAgB,GAC5DS,EAAU,oBAAoB,aAAaR,CAAe,GAC1DQ,EAAU,oBAAoB,YAAYN,EAAc,GACxDM,EAAU,oBAAoB,eAAeL,EAAiB,GAC9DK,EAAU,oBAAoB,aAAaJ,EAAe,GAC1DI,EAAU,oBAAoB,UAAUD,CAAY,IAEtD,OAAO,oBAAoB,aAAaF,EAAe,GACvD,OAAO,oBAAoB,WAAWC,EAAa,GAC/CxB,MACFA,EAAe,WAAA,GACfA,IAAiB,OAEfF,MAAiB,SACnB,qBAAqBA,CAAY,GACjCA,IAAe,OAEbD,MAAgB,SAClB,aAAaA,CAAW,GACxBA,IAAc,OAEZE,MAAc,SAChB,aAAaA,CAAS,GACtBA,IAAY;AAAA,IAEhB,CAAC,GAED6C,EAAa;AAAA,MACX,SAAST;AAAA,MACT,eAAAA;AAAA,MACA,gBAAgBA;AAAA,MAChB,YAAAE;AAAA,MACA,cAAcA;AAAA,MACd,oBAAAD;AAAA,IAAA,CACD,mBAICvE,EAyCM,OAAA;AAAA,eAzCG;AAAA,MAAJ,KAAIkB;AAAA,MAAe,OAAM;AAAA,IAAA;MAGpBhB,EAAA,oBADRF,EAYM,OAAA;AAAA;QAVJ,OAAKC,EAAA,CAAC,0BAAwB,EAAA,iBACHuB,EAAA,MAAA,CAAU,CAAA;AAAA,QACpC,oCAAkCD,EAAA,KAAY,OAAA;AAAA,MAAA;QAE/CyD,GAAA7E,EAKM,OALNC,IAKM;AAAA,UAHQkB,EAAA,UAAU,qBAAtBtB,EAAgD,QAAAK,EAAA,KAC/BiB,EAAA,UAAU,kBAA3BtB,EAAqF,QAArFiF,IAAqF3E,EAArBJ,EAAA,WAAW,GAAA,CAAA,WAC3EF,EAAuD,QAAvDkF,IAAuD5E,EAArBJ,EAAA,WAAW,GAAA,CAAA;AAAA,QAAA;eAJlCoB,EAAA,UAAU,MAAA;AAAA,QAAA;;MASzBnB,EAuBM,OAAA;AAAA,iBAtBA;AAAA,QAAJ,KAAIiB;AAAA,QACJ,OAAKnB,EAAA,CAAC,2BAAyB,EAAA,iBACJuB,EAAA,MAAA,CAAU,CAAA;AAAA,QACpC,OAAK2D,EAAEjF,EAAA,cAAW,EAAA,WAAA,cAA8BqB,EAAA,KAAY,MAAA,IAAA,CAAA,CAAA;AAAA,MAAA;QAGlDrB,EAAA,kBAAXU,EAAA,GAAAZ,EAEM,OAFNoF,IAEM;AAAA,UADJC,GAAyCC,GAAA,EAAxB,MAAMpF,EAAA,eAAA,GAAc,MAAA,GAAA,CAAA,MAAA,CAAA;AAAA,QAAA;QAIjBA,EAAA,YAAYA,EAAA,gBAAlCqF,EAA+DC,IAAA;AAAA;UAAnB,MAAMtF,EAAA;AAAA,QAAA;QAGlDW,EAAaC,EAAA,QAAA,WAAA,CAAA,GAAA,QAAA,EAAA;AAAA,QAGDZ,EAAA,4BAAZF,EAIM,OAAA;AAAA;mBAJmB;AAAA,UAAJ,KAAIqB;AAAA,UAAe,OAAM;AAAA,QAAA;UACtBnB,EAAA,WAAWoB,EAAA,UAAU,qBAA3CiE,EAAoFD,GAAA;AAAA;YAArB,MAAMpF,EAAA;AAAA,UAAA,yBACpDA,EAAA,iBAAjBF,EAA0E,QAA1EyF,IAA0EnF,EAAtBJ,EAAA,YAAY,GAAA,CAAA,KAC/CA,EAAA,cAAjBF,EAAsF,QAAA;AAAA;YAA9D,OAAM;AAAA,YAAc,SAAO0E;AAAA,UAAA,KAAgBxE,EAAA,SAAS,GAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;ACzepF,UAAMM,IAAQN,GAyDRc,IAAOC,GASPyE,IAAYvE,EAAgD,IAAI,GAEhEwE,IAAOxE,EAAS,EAAE,GAClByE,IAAUzE,EAAI,EAAK,GACnB0E,IAAW1E,EAAI,EAAK,GACpB2E,IAAU3E,EAAI,EAAK,GACnB4E,IAAY5E,EAAI,EAAK,GACrB6E,IAAQ7E,EAAI,CAAC,GACb8E,IAAO9E,EAAI,CAAC,GACZU,IAAeV,EAAI,EAAK;AAE9B,QAAI+E,IAAa;AAEjB,mBAAeC,EAAKC,IAAQ,IAAO;AACjC,MAAIA,MACFH,EAAK,QAAQ,GACbJ,EAAS,QAAQ,IACjBC,EAAQ,QAAQ,KAGlBF,EAAQ,QAAQ,IAChBG,EAAU,QAAQ,IAElBG,KAAc;AACd,YAAMG,IAAMH;AAEZ,UAAI;AACF,cAAMI,IAAS,MAAM9F,EAAM,QAAQ;AAAA,UACjC,MAAMyF,EAAK;AAAA,UACX,UAAUzF,EAAM;AAAA,UAChB,GAAGA,EAAM;AAAA,QAAA,CACV;AAED,YAAI6F,MAAQH,EAAY;AAExB,cAAMK,KAAUD,KAAA,gBAAAA,EAAQ,YAAW,CAAA;AACnC,QAAIF,KAEFT,EAAK,QAAQY,GACbP,EAAM,QAAQ,KAEdL,EAAK,QAAQ,CAAC,GAAGA,EAAK,OAAO,GAAGY,CAAO,GAEzCP,EAAM,QAAQ,QAAOM,KAAA,gBAAAA,EAAQ,UAAS,CAAC,GACvCT,EAAS,QAAQF,EAAK,MAAM,UAAUK,EAAM,OAC5CF,EAAQ,QAAQH,EAAK,MAAM,WAAW,GAEtC3E,EAAK,WAAW,EAAE,SAAAuF,GAAS,OAAOP,EAAM,OAAO,OAAAI,GAAO,GACtDpF,EAAK,eAAe2E,EAAK,KAAK;AAAA,MAChC,SAASa,GAAK;AACZ,YAAIH,MAAQH,EAAY;AACxB,QAAAH,EAAU,QAAQ,IAClB/E,EAAK,SAASwF,CAAG;AAAA,MACnB,UAAA;AACE,QAAIH,MAAQH,MACVN,EAAQ,QAAQ;AAAA,MAEpB;AAAA,IACF;AAEA,mBAAea,IAAgB;;AAC7B,MAAA5E,EAAa,QAAQ,KAErBqC,IAAAwB,EAAU,UAAV,QAAAxB,EAAiB,iBAEjB,MAAM,IAAI,QAAc,CAAAwC,MAAW;AACjC,mBAAW,MAAMA,EAAA,GAAW,GAAG;AAAA,MACjC,CAAC,GAED,MAAMP,EAAK,EAAI,GACftE,EAAa,QAAQ;AAAA,IACvB;AAEA,mBAAe8E,IAAa;;AAC1B,UAAId,EAAS,OAAO;AAClB,SAAA3B,IAAAwB,EAAU,UAAV,QAAAxB,EAAiB;AACjB;AAAA,MACF;AAEA,MAAIyB,EAAK,MAAM,SAAS,MACtBM,EAAK,SAAS,IAEhB,MAAME,EAAK,EAAK,IAChB1B,IAAAiB,EAAU,UAAV,QAAAjB,EAAiB;AAAA,IACnB;AAEA,aAASC,IAAc;AACrB,MAAAqB,EAAU,QAAQ,IACdJ,EAAK,MAAM,WAAW,IACxBQ,EAAK,EAAI,IAETQ,EAAA;AAAA,IAEJ;AAEA,mBAAeC,IAAS;AACtB,YAAMT,EAAK,EAAI;AAAA,IACjB;AAEA,WAAAxB;AAAA,MACE,MAAMnE,EAAM;AAAA,MACZ,MAAM;AACJ,QAAIA,EAAM,cACRoG,EAAA;AAAA,MAEJ;AAAA,MACA,EAAE,MAAM,GAAA;AAAA,IAAK,GAGfjC;AAAA,MACEgB;AAAA,MACA,YAAY;;AACV,cAAMtB,EAAA,IACNH,IAAAwB,EAAU,UAAV,QAAAxB,EAAiB;AAAA,MACnB;AAAA,MACA,EAAE,MAAM,GAAA;AAAA,IAAK,GAGfW,GAAU,MAAM;AACd,MAAIrE,EAAM,aACR2F,EAAK,EAAI;AAAA,IAEb,CAAC,GAEDpB,EAAa;AAAA;AAAA,MAEX,QAAA6B;AAAA;AAAA,MAEA,SAASA;AAAA;AAAA,MAET,MAAAjB;AAAA;AAAA,MAEA,SAAAC;AAAA;AAAA,MAEA,UAAAC;AAAA;AAAA,MAEA,SAAAC;AAAA;AAAA,MAEA,OAAAE;AAAA;AAAA,MAEA,YAAY,MAAA;;AAAM,gBAAA9B,IAAAwB,EAAU,UAAV,gBAAAxB,EAAiB;AAAA;AAAA,MACnC,eAAe,MAAA;;AAAM,gBAAAA,IAAAwB,EAAU,UAAV,gBAAAxB,EAAiB;AAAA;AAAA,MACtC,oBAAoB,MAAA;;AAAM,gBAAAA,IAAAwB,EAAU,UAAV,gBAAAxB,EAAiB;AAAA;AAAA,IAAmB,CAC/D,mBAICqB,EA8BiBsB,IAAA;AAAA,eA7BX;AAAA,MAAJ,KAAInB;AAAA,MACH,SAASE,EAAA;AAAA,MACT,UAAUC,EAAA;AAAA,MACV,YAAUC,EAAA;AAAA,MACV,OAAOC,EAAA;AAAA,MACP,gBAAc7F,EAAA;AAAA,MACd,kBAAgBA,EAAA;AAAA,MAChB,kBAAgBA,EAAA;AAAA,MAChB,cAAYA,EAAA;AAAA,MACZ,gBAAcA,EAAA;AAAA,MACd,gBAAcA,EAAA;AAAA,MACd,mBAAiBA,EAAA;AAAA,MACjB,gBAAcA,EAAA;AAAA,MACd,iBAAeA,EAAA;AAAA,MACf,cAAYA,EAAA;AAAA,MACZ,mBAAiB2B,EAAA;AAAA,MACjB,QAAM8E;AAAA,MACN,WAASF;AAAA,MACT,SAAO/B;AAAA,IAAA;kBAER,MAQE;AAAA,QARF7D,EAQEC,EAAA,QAAA,WAAA;AAAA,UAPC,MAAM6E,EAAA;AAAA,UACN,SAASC,EAAA;AAAA,UACT,UAAUC,EAAA;AAAA,UACV,SAAUC,EAAA;AAAA,UACV,OAAOC,EAAA;AAAA,UACP,OAAOC,EAAA;AAAA,UACP,QAAAY;AAAA,QAAA;;;;;;ACxPP,IAAIE,KAAgB;AAEpB,MAAMC,KAAa;AAEZ,SAASC,KAAc;AAC5B,MAAIF,MAAiB,OAAO,WAAa,IAAa;AAEtD,QAAMG,IAAQ,SAAS,cAAc,OAAO;AAC5C,EAAAA,EAAM,aAAa,eAAe,oBAAoB,GACtDA,EAAM,cAAcF,IACpB,SAAS,KAAK,YAAYE,CAAK,GAE/BH,KAAgB;AAClB;ACNAE,GAAA;AAMO,SAASE,GAAQC,GAAU;AAChC,EAAAA,EAAI,UAAU,kBAAkBN,EAAc,GAC9CM,EAAI,UAAU,oBAAoBC,EAAgB,GAClDD,EAAI,UAAU,kBAAkB7B,CAAc,GAC9C6B,EAAI,UAAU,kBAAkB3B,EAAc;AAChD;AAGA,MAAA6B,KAAe;AAAA,EACb,SAAAH;AACF;"}
|
|
1
|
+
{"version":3,"file":"index.es.js","sources":["../src/components/request-loading.vue","../src/components/empty-component.vue","../src/components/infinite-scroll.vue","../src/components/infinite-scroll-v2.vue","../src/inject-style.ts","../src/index.ts"],"sourcesContent":["<script setup lang=\"ts\">\ndefineOptions({ name: 'RequestLoading' });\n\nwithDefaults(defineProps<{ text?: string; fullscreen?: boolean }>(), {\n text: '加载中...',\n fullscreen: false\n});\n</script>\n\n<template>\n <div class=\"zx-loading-container\" :class=\"{ fullscreen: fullscreen }\">\n <div class=\"zx-loading-content\">\n <span class=\"zx-loading-spinner\"></span>\n <span class=\"zx-loading-text\">{{ text }}</span>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n.zx-loading-content {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 6px 12px;\n border-radius: 16px;\n color: #1989fa;\n font-size: 14px;\n}\n\n.zx-loading-spinner {\n width: 18px;\n height: 18px;\n border-radius: 50%;\n border: 2.5px solid #1989fa;\n border-top-color: transparent;\n animation: zx-spin 0.8s linear infinite;\n}\n\n@keyframes zx-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n.zx-loading-container.fullscreen {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: rgba(255, 255, 255, 0.5);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue';\n\ndefineOptions({ name: 'EmptyComponent' });\n\ninterface Props {\n text?: string;\n showText?: boolean;\n /** 顶部间距,可传数字(px)或带单位字符串,默认 168px */\n paddingTop?: number | string;\n /** 自定义图片 URL */\n emptyImage?: string;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n text: '暂无数据',\n showText: true,\n paddingTop: 168,\n emptyImage: ''\n});\n\nconst wrapStyle = computed(() => ({\n paddingTop: typeof props.paddingTop === 'number' ? `${props.paddingTop}px` : props.paddingTop\n}));\n\n// 默认空状态 SVG(简洁的空文件夹图标)\nconst defaultEmptySvg = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='80' viewBox='0 0 96 80' fill='none'%3E%3Cpath d='M8 16C8 12.6863 10.6863 10 14 10H38L44 16H82C85.3137 16 88 18.6863 88 22V66C88 69.3137 85.3137 72 82 72H14C10.6863 72 8 69.3137 8 66V16Z' fill='%23E8E8E8'/%3E%3Cpath d='M8 20C8 18.3431 9.34315 17 11 17H85C86.6569 17 88 18.3431 88 20V66C88 69.3137 85.3137 72 82 72H14C10.6863 72 8 69.3137 8 66V20Z' fill='%23F5F5F5'/%3E%3Cpath d='M38 42C38 40.8954 38.8954 40 40 40H56C57.1046 40 58 40.8954 58 42V44C58 45.1046 57.1046 46 56 46H40C38.8954 46 38 45.1046 38 44V42Z' fill='%23D9D9D9'/%3E%3C/svg%3E`;\n\nconst imageSrc = computed(() => props.emptyImage || defaultEmptySvg);\n</script>\n\n<template>\n <div class=\"zx-empty-wrap\" :style=\"wrapStyle\">\n <img class=\"zx-empty-image\" :src=\"imageSrc\" alt=\"\" />\n <p v-if=\"showText\" class=\"zx-empty-text\">\n <slot>{{ text }}</slot>\n </p>\n </div>\n</template>\n\n<style lang=\"scss\">\n.zx-empty-wrap {\n display: flex;\n flex-direction: column;\n align-items: center;\n}\n\n.zx-empty-image {\n width: 96px;\n height: 80px;\n}\n\n.zx-empty-text {\n margin: 12px 0 0;\n color: #8c8f97;\n font-size: 12px;\n line-height: 22px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';\nimport RequestLoading from './request-loading.vue';\nimport EmptyComponent from './empty-component.vue';\n\ndefineOptions({\n name: 'InfiniteScroll'\n});\n\ninterface Props {\n /** 是否处于加载中状态 */\n loading?: boolean;\n /** 是否已加载完所有数据 */\n finished?: boolean;\n /** 是否显示空状态 */\n isEmpty?: boolean;\n /** 空状态文案 */\n emptyText?: string;\n /** 是否启用下拉刷新 */\n pullRefresh?: boolean;\n /** 下拉刷新触发距离(px) */\n pullThreshold?: number;\n /** 上拉加载触发距离(距离底部 px) */\n loadThreshold?: number;\n /** 下拉中的文案 */\n pullingText?: string;\n /** 释放刷新的文案 */\n loosingText?: string;\n /** 刷新中的文案 */\n refreshingText?: string;\n /** 加载中的文案 */\n loadingText?: string;\n /** 加载完成的文案 */\n finishedText?: string;\n /** 加载失败的文案 */\n errorText?: string;\n /** 是否显示错误状态 */\n error?: boolean;\n /** 内容区域是否显示加载状态 */\n contentLoading?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n loading: false,\n finished: false,\n isEmpty: false,\n emptyText: '暂无数据',\n pullRefresh: false,\n pullThreshold: 70,\n loadThreshold: 50,\n pullingText: '下拉刷新',\n loosingText: '释放即可刷新',\n refreshingText: '加载中...',\n loadingText: '加载中...',\n finishedText: '没有更多了~',\n errorText: '加载失败,点击重试',\n error: false,\n contentLoading: false\n});\n\nconst emit = defineEmits<{\n /** 触发下拉刷新 */\n refresh: [];\n /** 触发上拉加载 */\n load: [];\n /** 点击错误重试 */\n retry: [];\n}>();\n\nconst containerRef = ref<HTMLElement | null>(null);\nconst contentRef = ref<HTMLElement | null>(null);\nconst indicatorRef = ref<HTMLElement | null>(null);\n\n// 下拉刷新状态\nconst pullStatus = ref<'idle' | 'pulling' | 'loosing' | 'refreshing'>('idle');\nconst pullDistance = ref(0);\n// 拖动期间禁用 transition,避免 transform 变化时被 0.25s 补间拖慢\nconst isDragging = ref(false);\n\n// 触摸相关\nlet startX = 0;\nlet startY = 0;\nlet lastDistance = 0;\n// 手势方向锁:避免横向滑动被误判为下拉\nlet gestureLock: 'none' | 'vertical' | 'horizontal' = 'none';\nconst SLOPE_LOCK_DISTANCE = 8;\n\nlet isRefreshing = false;\nlet isLoading = false;\n// 鼠标拖拽下拉(PC 端)\nlet isMouseDown = false;\n\n// 节流相关\nlet scrollTimer: number | null = null;\nlet touchMoveRaf: number | null = null;\nlet idleTimer: number | null = null;\n// 监听容器尺寸变化\nlet resizeObserver: ResizeObserver | null = null;\n// 防止空页死循环:snapshot 触发 load 时的 contentHeight,下次完成后没增长就不再 auto-load\nlet lastTriggerContentHeight = -1;\n\n// 下拉刷新核心:按下(与指针类型无关,touch/mouse 共用)\nfunction pullStart(clientX: number, clientY: number) {\n if (!props.pullRefresh || isRefreshing) return;\n startX = clientX;\n startY = clientY;\n lastDistance = 0;\n gestureLock = 'none';\n pullStatus.value = 'idle';\n}\n\n// 下拉刷新核心:移动(rAF 节流,始终使用最新位置)\n// 返回 true 表示消费了本次移动(下拉进行中),调用方据此决定是否 preventDefault\nfunction pullMove(clientX: number, clientY: number, cancelable: boolean): boolean {\n if (!props.pullRefresh || isRefreshing || startY === 0) return false;\n\n const dx = clientX - startX;\n const dy = clientY - startY;\n\n // 方向判定:先到达 8px 锁定的方向才认为有意图,垂直要明显大于水平(1.5x)\n if (gestureLock === 'none') {\n const ax = Math.abs(dx);\n const ay = Math.abs(dy);\n if (ax > SLOPE_LOCK_DISTANCE || ay > SLOPE_LOCK_DISTANCE) {\n gestureLock = ay > ax * 1.5 ? 'vertical' : 'horizontal';\n } else {\n return false;\n }\n }\n if (gestureLock === 'horizontal') return false;\n\n // 子像素容差:≤1 视为已到顶,避免 X5 内核 scrollTop 偶发非整数判断失败\n const atTop = getScrollTop() <= 1;\n\n // iOS 15- 兜底:在 atBottom 且向上拖时阻止整页 rubber-band\n // (iOS 16+ overscroll-behavior 已处理,此处兜底兼容)\n if (!atTop && dy < 0) {\n const c = containerRef.value;\n if (c) {\n const atBottom = c.scrollHeight - c.scrollTop - c.clientHeight <= 1;\n if (atBottom && cancelable) {\n return true;\n }\n }\n return false;\n }\n if (!atTop) return false;\n\n // 反向拖动也要更新(让指示器跟着手指回退),夹紧到 [0, 150]\n const clamped = Math.max(0, Math.min(dy, 150));\n\n lastDistance = clamped;\n isDragging.value = true;\n\n if (touchMoveRaf === null) {\n touchMoveRaf = requestAnimationFrame(() => {\n touchMoveRaf = null;\n pullDistance.value = lastDistance * 0.5;\n if (lastDistance === 0) {\n pullStatus.value = 'idle';\n } else {\n pullStatus.value = lastDistance > props.pullThreshold ? 'loosing' : 'pulling';\n }\n });\n }\n\n // 仅在向下拖时消费事件(避免误吞用户向上滚动的意图)\n return dy > 0;\n}\n\n// 下拉刷新核心:松开\nfunction pullEnd() {\n if (!props.pullRefresh || isRefreshing) return;\n\n // 取消待执行的 rAF,避免松手后还有一帧异步更新把 UI 拉回 loosing 卡住\n if (touchMoveRaf !== null) {\n cancelAnimationFrame(touchMoveRaf);\n touchMoveRaf = null;\n }\n\n isDragging.value = false;\n\n // 用最新的 lastDistance 同步判定,不依赖可能尚未生效的 pullStatus\n if (lastDistance > props.pullThreshold) {\n isRefreshing = true;\n pullStatus.value = 'refreshing';\n pullDistance.value = 50;\n emit('refresh');\n } else {\n pullDistance.value = 0;\n pullStatus.value = 'idle';\n }\n\n resetTouch();\n}\n\n// 下拉刷新核心:取消(回到 idle)\nfunction pullCancel() {\n if (!props.pullRefresh || isRefreshing) return;\n\n if (touchMoveRaf !== null) {\n cancelAnimationFrame(touchMoveRaf);\n touchMoveRaf = null;\n }\n\n isDragging.value = false;\n pullDistance.value = 0;\n pullStatus.value = 'idle';\n resetTouch();\n}\n\n// 下拉刷新 - touchstart\nfunction handleTouchStart(e: TouchEvent) {\n pullStart(e.touches[0].clientX, e.touches[0].clientY);\n}\n\n// 下拉刷新 - touchmove\nfunction handleTouchMove(e: TouchEvent) {\n const touch = e.touches[0];\n if (pullMove(touch.clientX, touch.clientY, e.cancelable) && e.cancelable) {\n e.preventDefault();\n }\n}\n\n// 下拉刷新 - touchend\nfunction handleTouchEnd() {\n pullEnd();\n}\n\n// 下拉刷新 - touchcancel(微信 X5 内核会在系统手势介入时派发 cancel 而非 end)\nfunction handleTouchCancel() {\n pullCancel();\n}\n\n// 下拉刷新 - mousedown(PC 端鼠标拖拽)\nfunction handleMouseDown(e: MouseEvent) {\n // 仅左键触发\n if (!props.pullRefresh || isRefreshing || e.button !== 0) return;\n isMouseDown = true;\n pullStart(e.clientX, e.clientY);\n}\n\n// 下拉刷新 - mousemove(挂在 window 上,允许拖出容器)\nfunction handleMouseMove(e: MouseEvent) {\n if (!isMouseDown) return;\n if (pullMove(e.clientX, e.clientY, true)) {\n // 阻止拖拽时选中文本\n e.preventDefault();\n }\n}\n\n// 下拉刷新 - mouseup\nfunction handleMouseUp() {\n if (!isMouseDown) return;\n isMouseDown = false;\n pullEnd();\n}\n\nfunction resetTouch() {\n startX = 0;\n startY = 0;\n lastDistance = 0;\n gestureLock = 'none';\n}\n\n// 上拉加载 - scroll(节流处理)\nfunction handleScroll() {\n if (props.loading || props.finished || props.error || isLoading) return;\n if (scrollTimer) return;\n\n scrollTimer = window.setTimeout(() => {\n scrollTimer = null;\n const container = containerRef.value;\n if (!container) return;\n\n const { scrollTop, clientHeight, scrollHeight } = container;\n const contentHeight = contentRef.value?.clientHeight || 0;\n\n // 情况1:内容不足一屏,自动触发加载\n if (contentHeight <= clientHeight) {\n // 死循环防护:上一次 auto-load 后内容没增长,不再继续触发\n if (lastTriggerContentHeight === contentHeight) return;\n lastTriggerContentHeight = contentHeight;\n // 不足一屏时主动滚到底,让 loading 指示器进入视口\n triggerLoad(true);\n return;\n }\n\n // 情况2:滚动到距离底部阈值范围内\n if (scrollHeight - scrollTop - clientHeight <= props.loadThreshold) {\n // 用户已经手动滚到底,不再强制 scrollTop(避免打断惯性滚动)\n triggerLoad(false);\n }\n }, 100);\n}\n\nfunction triggerLoad(scrollToBottom = false) {\n isLoading = true;\n emit('load');\n nextTick(() => {\n const c = containerRef.value;\n if (!c) return;\n if (scrollToBottom) {\n // 内容不足一屏:直接滚到底(没有\"惯性\"可以打断)\n c.scrollTop = c.scrollHeight;\n } else {\n // 阈值触发:用 scrollIntoView 平滑进入视口,避免突兀打断;\n // 旧 WebKit 不识别 options 会降级为即时滚动,效果等价\n indicatorRef.value?.scrollIntoView({ block: 'end', behavior: 'smooth' });\n }\n });\n}\n\n// 获取滚动距离\nfunction getScrollTop(): number {\n return containerRef.value?.scrollTop ?? 0;\n}\n\n// 完成下拉刷新\nfunction finishRefresh() {\n isRefreshing = false;\n pullDistance.value = 0;\n // 让 transform snap-back 完成后再切 idle,避免 spinner 在动画中途突然消失\n if (idleTimer !== null) clearTimeout(idleTimer);\n idleTimer = window.setTimeout(() => {\n pullStatus.value = 'idle';\n idleTimer = null;\n }, 250);\n // 重置死循环计数\n lastTriggerContentHeight = -1;\n nextTick(() => {\n checkContentHeight();\n });\n}\n\n// 完成上拉加载\nfunction finishLoad() {\n isLoading = false;\n nextTick(() => {\n const contentHeight = contentRef.value?.clientHeight || 0;\n // 内容没增长就不再触发 auto-load,避免空页死循环\n if (lastTriggerContentHeight !== -1 && contentHeight === lastTriggerContentHeight) return;\n lastTriggerContentHeight = -1;\n checkContentHeight();\n });\n}\n\n// 检查内容高度,不足一屏时自动触发加载\nfunction checkContentHeight() {\n if (props.finished || props.loading || props.error) return;\n\n nextTick(() => {\n const contentHeight = contentRef.value?.clientHeight || 0;\n const clientHeight = containerRef.value?.clientHeight || 0;\n\n if (contentHeight <= clientHeight && !isLoading) {\n handleScroll();\n }\n });\n}\n\n// 点击错误重试 — 由父组件决定具体行为,避免 retry + load 双触发\nfunction handleRetry() {\n emit('retry');\n}\n\n// loading 完成时同步重置 isLoading\nwatch(\n () => props.loading,\n newVal => {\n if (!newVal) {\n isLoading = false;\n }\n }\n);\n\n// finished 翻为 true 时重置死循环计数\nwatch(\n () => props.finished,\n newVal => {\n if (newVal) {\n lastTriggerContentHeight = -1;\n }\n }\n);\n\nonMounted(() => {\n const container = containerRef.value;\n if (!container) return;\n\n if (props.pullRefresh) {\n container.addEventListener('touchstart', handleTouchStart, { passive: true });\n container.addEventListener('touchmove', handleTouchMove, { passive: false });\n container.addEventListener('touchend', handleTouchEnd, { passive: true });\n container.addEventListener('touchcancel', handleTouchCancel, { passive: true });\n // 鼠标拖拽(PC):move/up 挂在 window 上,允许拖出容器边界\n container.addEventListener('mousedown', handleMouseDown);\n window.addEventListener('mousemove', handleMouseMove);\n window.addEventListener('mouseup', handleMouseUp);\n }\n\n container.addEventListener('scroll', handleScroll, { passive: true });\n\n // 监听容器/内容尺寸变化(旋转、WebView resize、软键盘弹出收起等)\n if (typeof ResizeObserver !== 'undefined') {\n resizeObserver = new ResizeObserver(() => {\n checkContentHeight();\n });\n resizeObserver.observe(container);\n if (contentRef.value) resizeObserver.observe(contentRef.value);\n }\n\n // 初始检查内容高度\n nextTick(() => {\n checkContentHeight();\n });\n});\n\nonBeforeUnmount(() => {\n const container = containerRef.value;\n if (container) {\n container.removeEventListener('touchstart', handleTouchStart);\n container.removeEventListener('touchmove', handleTouchMove);\n container.removeEventListener('touchend', handleTouchEnd);\n container.removeEventListener('touchcancel', handleTouchCancel);\n container.removeEventListener('mousedown', handleMouseDown);\n container.removeEventListener('scroll', handleScroll);\n }\n window.removeEventListener('mousemove', handleMouseMove);\n window.removeEventListener('mouseup', handleMouseUp);\n if (resizeObserver) {\n resizeObserver.disconnect();\n resizeObserver = null;\n }\n if (touchMoveRaf !== null) {\n cancelAnimationFrame(touchMoveRaf);\n touchMoveRaf = null;\n }\n if (scrollTimer !== null) {\n clearTimeout(scrollTimer);\n scrollTimer = null;\n }\n if (idleTimer !== null) {\n clearTimeout(idleTimer);\n idleTimer = null;\n }\n});\n\ndefineExpose({\n refresh: finishRefresh,\n finishRefresh,\n finishPullDown: finishRefresh,\n finishLoad,\n finishPullUp: finishLoad,\n checkContentHeight\n});\n</script>\n\n<template>\n <div ref=\"containerRef\" class=\"zx-infinite-scroll\">\n <!-- 下拉刷新指示器 -->\n <div\n v-if=\"pullRefresh\"\n class=\"zx-pull-refresh-indicator\"\n :class=\"{ 'no-transition': isDragging }\"\n :style=\"{ transform: `translateY(${pullDistance}px)` }\"\n >\n <div v-show=\"pullStatus !== 'idle'\" class=\"zx-pull-refresh-content\">\n <!-- <RequestLoading v-if=\"pullStatus === 'refreshing'\" text=\"\" /> -->\n <span v-if=\"pullStatus === 'refreshing'\"></span>\n <span v-else-if=\"pullStatus === 'loosing'\" class=\"zx-pull-text\">{{ loosingText }}</span>\n <span v-else class=\"zx-pull-text\">{{ pullingText }}</span>\n </div>\n </div>\n\n <!-- 内容区域 -->\n <div\n ref=\"contentRef\"\n class=\"zx-infinite-scroll-content\"\n :class=\"{ 'no-transition': isDragging }\"\n :style=\"pullRefresh ? { transform: `translateY(${pullDistance}px)` } : {}\"\n >\n <!-- 内容区域加载遮罩 -->\n <div v-if=\"contentLoading\" class=\"zx-content-loading-mask\">\n <RequestLoading :text=\"refreshingText\" />\n </div>\n\n <!-- 空状态 -->\n <EmptyComponent v-if=\"isEmpty && !loading\" :text=\"emptyText\" />\n\n <!-- 默认插槽 -->\n <slot></slot>\n\n <!-- 上拉加载指示器 -->\n <div v-if=\"!isEmpty\" ref=\"indicatorRef\" class=\"zx-load-more-indicator\">\n <RequestLoading v-if=\"loading && pullStatus !== 'refreshing'\" :text=\"loadingText\" />\n <span v-else-if=\"finished\" class=\"zx-finished-text\">{{ finishedText }}</span>\n <span v-else-if=\"error\" class=\"zx-error-text\" @click=\"handleRetry\">{{ errorText }}</span>\n </div>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n.zx-infinite-scroll {\n height: 100%;\n position: relative;\n min-height: 100%;\n overflow-y: auto;\n overflow-x: hidden;\n // 阻止滚动到边界时把滚动链传递给祖先(修复 iOS 16+ / 微信 X5 下拉变成整页拖拽)\n overscroll-behavior: contain;\n // 显式声明只接受纵向手势,避免被外层 touch-action 影响\n touch-action: pan-y;\n // iOS 下让原生回弹有惯性\n -webkit-overflow-scrolling: touch;\n}\n\n.zx-pull-refresh-indicator {\n position: absolute;\n top: -50px;\n left: 0;\n right: 0;\n height: 50px;\n transition: transform 0.25s;\n\n // 拖动期间禁用过渡,让 transform 1:1 跟随手指\n &.no-transition {\n transition: none;\n }\n}\n\n.zx-pull-refresh-content {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n font-size: 14px;\n color: #969799;\n}\n\n.zx-pull-text {\n color: #1989fa;\n font-size: 14px;\n}\n\n.zx-infinite-scroll-content {\n position: relative;\n transition: transform 0.25s;\n min-height: 100%;\n\n &.no-transition {\n transition: none;\n }\n}\n\n.zx-content-loading-mask {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(255, 255, 255, 0.8);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n backdrop-filter: blur(2px);\n}\n\n.zx-load-more-indicator {\n display: flex;\n align-items: center;\n justify-content: center;\n // 预留高度避免 loading 渲染后被推到视口外造成遮挡\n min-height: 50px;\n padding: 16px 0;\n font-size: 14px;\n color: #8c8f97;\n}\n\n.zx-finished-text {\n color: #8c8f97;\n font-size: 14px;\n}\n\n.zx-error-text {\n color: #ee0a24;\n font-size: 14px;\n cursor: pointer;\n\n &:active {\n opacity: 0.7;\n }\n}\n</style>\n","<script setup lang=\"ts\" generic=\"T\">\nimport { type Ref, nextTick, onMounted, ref, watch } from 'vue';\nimport InfiniteScroll from './infinite-scroll.vue';\n\ndefineOptions({\n name: 'InfiniteScrollV2'\n});\n\nconst props = withDefaults(\n defineProps<{\n /** 数据请求函数,需返回 { records, total },失败时抛错 */\n fetchFn: (params: {\n page: number;\n pageSize: number;\n [key: string]: any;\n }) => Promise<{\n records: T[];\n total: number;\n }>;\n /** 业务查询参数(如筛选条件),变化时自动 reset 重新加载 */\n params?: Record<string, any>;\n /** 每页条数 */\n pageSize?: number;\n /** params 变化时是否自动重新加载 */\n autoReload?: boolean;\n /** 是否在挂载时自动加载首页 */\n immediate?: boolean;\n /** 是否启用下拉刷新 */\n pullRefresh?: boolean;\n /** 下拉刷新触发距离(px) */\n pullThreshold?: number;\n /** 上拉加载触发距离(距离底部 px) */\n loadThreshold?: number;\n /** 空状态文案 */\n emptyText?: string;\n /** 下拉中的文案 */\n pullingText?: string;\n /** 释放刷新的文案 */\n loosingText?: string;\n /** 刷新中的文案 */\n refreshingText?: string;\n /** 加载中的文案 */\n loadingText?: string;\n /** 加载完成的文案 */\n finishedText?: string;\n /** 加载失败的文案 */\n errorText?: string;\n }>(),\n {\n params: () => ({}),\n pageSize: 10,\n autoReload: true,\n immediate: true,\n pullRefresh: false,\n pullThreshold: 70,\n loadThreshold: 50,\n emptyText: '暂无数据',\n pullingText: '下拉刷新',\n loosingText: '释放即可刷新',\n refreshingText: '加载中...',\n loadingText: '加载中...',\n finishedText: '没有更多了~',\n errorText: '加载失败,点击重试'\n});\n\nconst emit = defineEmits<{\n /** 加载成功,每次成功(含分页)都会触发 */\n success: [payload: { records: T[]; total: number; reset: boolean }];\n /** 加载失败 */\n error: [err: unknown];\n /** 列表内容变化(v-model:list 支持) */\n 'update:list': [list: T[]];\n}>();\n\nconst scrollRef = ref<InstanceType<typeof InfiniteScroll> | null>(null);\n\nconst list = ref<T[]>([]) as Ref<T[]>;\nconst loading = ref(false);\nconst finished = ref(false);\nconst isEmpty = ref(false);\nconst loadError = ref(false);\nconst total = ref(0);\nconst page = ref(1);\nconst isRefreshing = ref(false);\n\nlet requestSeq = 0;\n\nasync function load(reset = false) {\n if (reset) {\n page.value = 1;\n finished.value = false;\n isEmpty.value = false;\n // 不立即清空 list,保留旧数据直到新数据到达\n }\n loading.value = true;\n loadError.value = false;\n\n requestSeq += 1;\n const seq = requestSeq;\n\n try {\n const result = await props.fetchFn({\n page: page.value,\n pageSize: props.pageSize,\n ...props.params\n });\n\n if (seq !== requestSeq) return;\n\n const records = result?.records || [];\n if (reset) {\n // reset 时替换整个列表\n list.value = records;\n total.value = 0;\n } else {\n list.value = [...list.value, ...records];\n }\n total.value = Number(result?.total || 0);\n finished.value = list.value.length >= total.value;\n isEmpty.value = list.value.length === 0;\n\n emit('success', { records, total: total.value, reset });\n emit('update:list', list.value);\n } catch (err) {\n if (seq !== requestSeq) return;\n loadError.value = true;\n emit('error', err);\n } finally {\n if (seq === requestSeq) {\n loading.value = false;\n }\n }\n}\n\nasync function handleRefresh() {\n isRefreshing.value = true;\n // 先让下拉刷新指示器收回\n scrollRef.value?.finishRefresh();\n // 等待 250ms 让指示器完全收回(对应 infinite-scroll.vue 的 transition 时长)\n await new Promise<void>(resolve => {\n setTimeout(() => resolve(), 250);\n });\n // 指示器收回后再开始请求新数据\n await load(true);\n isRefreshing.value = false;\n}\n\nasync function handleLoad() {\n if (finished.value) {\n scrollRef.value?.finishLoad();\n return;\n }\n // 列表为空时是首次加载(page 已经是 1),其它情况 page++\n if (list.value.length > 0) {\n page.value += 1;\n }\n await load(false);\n scrollRef.value?.finishLoad();\n}\n\nfunction handleRetry() {\n loadError.value = false;\n if (list.value.length === 0) {\n load(true);\n } else {\n handleLoad();\n }\n}\n\nasync function reload() {\n await load(true);\n}\n\nwatch(\n () => props.params,\n () => {\n if (props.autoReload) {\n reload();\n }\n },\n { deep: true }\n);\n\nwatch(\n list,\n async () => {\n await nextTick();\n scrollRef.value?.checkContentHeight();\n },\n { deep: true }\n);\n\nonMounted(() => {\n if (props.immediate) {\n load(true);\n }\n});\n\ndefineExpose({\n /** 重新加载第一页 */\n reload,\n /** 兼容别名 */\n refresh: reload,\n /** 当前列表数据 */\n list,\n /** 加载中状态 */\n loading,\n /** 是否已加载完 */\n finished,\n /** 是否空数据 */\n isEmpty,\n /** 总条数 */\n total,\n /** 透传 v1 方法 */\n finishLoad: () => scrollRef.value?.finishLoad(),\n finishRefresh: () => scrollRef.value?.finishRefresh(),\n checkContentHeight: () => scrollRef.value?.checkContentHeight()\n});\n</script>\n\n<template>\n <InfiniteScroll\n ref=\"scrollRef\"\n :loading=\"loading\"\n :finished=\"finished\"\n :is-empty=\"isEmpty\"\n :error=\"loadError\"\n :pull-refresh=\"pullRefresh\"\n :pull-threshold=\"pullThreshold\"\n :load-threshold=\"loadThreshold\"\n :empty-text=\"emptyText\"\n :pulling-text=\"pullingText\"\n :loosing-text=\"loosingText\"\n :refreshing-text=\"refreshingText\"\n :loading-text=\"loadingText\"\n :finished-text=\"finishedText\"\n :error-text=\"errorText\"\n :content-loading=\"isRefreshing\"\n @load=\"handleLoad\"\n @refresh=\"handleRefresh\"\n @retry=\"handleRetry\"\n >\n <slot\n :list=\"list\"\n :loading=\"loading\"\n :finished=\"finished\"\n :is-empty=\"isEmpty\"\n :error=\"loadError\"\n :total=\"total\"\n :reload=\"reload\"\n />\n </InfiniteScroll>\n</template>\n","// 样式自动注入\nlet styleInjected = false;\n\nconst cssContent = `.zx-loading-content{display:flex;align-items:center;justify-content:center;gap:8px;padding:6px 12px;border-radius:16px;color:#1989fa;font-size:14px}.zx-loading-spinner{width:18px;height:18px;border-radius:50%;border:2.5px solid #1989fa;border-top-color:transparent;animation:zx-spin .8s linear infinite}@keyframes zx-spin{to{transform:rotate(360deg)}}.zx-loading-container.fullscreen{position:fixed;top:0;left:0;width:100%;height:100%;display:flex;align-items:center;justify-content:center;background-color:#ffffff80}.zx-empty-wrap{display:flex;flex-direction:column;align-items:center}.zx-empty-image{width:96px;height:80px}.zx-empty-text{margin:12px 0 0;color:#8c8f97;font-size:12px;line-height:22px}.zx-infinite-scroll{height:100%;position:relative;min-height:100%;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;touch-action:pan-y;-webkit-overflow-scrolling:touch}.zx-pull-refresh-indicator{position:absolute;top:-50px;left:0;right:0;height:50px;transition:transform .25s}.zx-pull-refresh-indicator.no-transition{transition:none}.zx-pull-refresh-content{display:flex;align-items:center;justify-content:center;height:100%;font-size:14px;color:#969799}.zx-pull-text{color:#1989fa;font-size:14px}.zx-infinite-scroll-content{position:relative;transition:transform .25s;min-height:100%}.zx-infinite-scroll-content.no-transition{transition:none}.zx-content-loading-mask{position:absolute;top:0;left:0;right:0;bottom:0;background-color:#fffc;display:flex;align-items:center;justify-content:center;z-index:10;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.zx-load-more-indicator{display:flex;align-items:center;justify-content:center;min-height:50px;padding:16px 0;font-size:14px;color:#8c8f97}.zx-finished-text{color:#8c8f97;font-size:14px}.zx-error-text{color:#ee0a24;font-size:14px;cursor:pointer}.zx-error-text:active{opacity:.7}`;\n\nexport function injectStyle() {\n if (styleInjected || typeof document === 'undefined') return;\n\n const style = document.createElement('style');\n style.setAttribute('data-source', 'zx-infinite-scroll');\n style.textContent = cssContent;\n document.head.appendChild(style);\n\n styleInjected = true;\n}\n","import { App } from 'vue';\nimport InfiniteScroll from './components/infinite-scroll.vue';\nimport InfiniteScrollV2 from './components/infinite-scroll-v2.vue';\nimport RequestLoading from './components/request-loading.vue';\nimport EmptyComponent from './components/empty-component.vue';\nimport { injectStyle } from './inject-style';\n\n// 自动注入样式(只要导入了这个包就会执行)\ninjectStyle();\n\n// 单独导出组件\nexport { InfiniteScroll, InfiniteScrollV2, RequestLoading, EmptyComponent };\n\n// Vue plugin install 函数\nexport function install(app: App) {\n app.component('InfiniteScroll', InfiniteScroll);\n app.component('InfiniteScrollV2', InfiniteScrollV2);\n app.component('RequestLoading', RequestLoading);\n app.component('EmptyComponent', EmptyComponent);\n}\n\n// 默认导出(支持 app.use() 安装)\nexport default {\n install\n};\n"],"names":["_createElementBlock","_normalizeClass","__props","_createElementVNode","_hoisted_1","_hoisted_2","_toDisplayString","defaultEmptySvg","props","wrapStyle","computed","imageSrc","_openBlock","_renderSlot","_ctx","SLOPE_LOCK_DISTANCE","emit","__emit","containerRef","ref","contentRef","indicatorRef","pullStatus","pullDistance","isDragging","startX","startY","lastDistance","gestureLock","isRefreshing","isLoading","isMouseDown","scrollTimer","touchMoveRaf","idleTimer","resizeObserver","lastTriggerContentHeight","pullStart","clientX","clientY","pullMove","cancelable","dx","dy","ax","ay","atTop","getScrollTop","c","pullEnd","resetTouch","pullCancel","handleTouchStart","handleTouchMove","touch","handleTouchEnd","handleTouchCancel","handleMouseDown","handleMouseMove","handleMouseUp","handleScroll","container","scrollTop","clientHeight","scrollHeight","contentHeight","_a","triggerLoad","scrollToBottom","nextTick","finishRefresh","checkContentHeight","finishLoad","_b","handleRetry","watch","newVal","onMounted","onBeforeUnmount","__expose","_withDirectives","_hoisted_3","_hoisted_4","_normalizeStyle","_hoisted_5","_createVNode","RequestLoading","_createBlock","EmptyComponent","_hoisted_6","scrollRef","list","loading","finished","isEmpty","loadError","total","page","requestSeq","load","reset","seq","result","records","err","handleRefresh","resolve","handleLoad","reload","InfiniteScroll","styleInjected","cssContent","injectStyle","style","install","app","InfiniteScrollV2","index"],"mappings":";;;;;;;;;2BAUEA,EAKM,OAAA;AAAA,MALD,OAAKC,EAAA,CAAC,wBAAsB,EAAA,YAAuBC,EAAA,YAAU,CAAA;AAAA,IAAA;MAChEC,EAGM,OAHNC,IAGM;AAAA,wBAFJD,EAAwC,QAAA,EAAlC,OAAM,qBAAA,GAAoB,MAAA,EAAA;AAAA,QAChCA,EAA+C,QAA/CE,IAA+CC,EAAdJ,EAAA,IAAI,GAAA,CAAA;AAAA,MAAA;;;;;;GCarCK,KAAkB;;;;;;;;;;AAZxB,UAAMC,IAAQN,GAORO,IAAYC,GAAS,OAAO;AAAA,MAChC,YAAY,OAAOF,EAAM,cAAe,WAAW,GAAGA,EAAM,UAAU,OAAOA,EAAM;AAAA,IAAA,EACnF,GAKIG,IAAWD,GAAS,MAAMF,EAAM,cAAcD,EAAe;2BAIjEP,EAKM,OAAA;AAAA,MALD,OAAM;AAAA,MAAiB,SAAOS,EAAA,KAAS;AAAA,IAAA;MAC1CN,EAAqD,OAAA;AAAA,QAAhD,OAAM;AAAA,QAAkB,KAAKQ,EAAA;AAAA,QAAU,KAAI;AAAA,MAAA;MACvCT,EAAA,YAATU,EAAA,GAAAZ,EAEI,KAFJK,IAEI;AAAA,QADFQ,EAAuBC,yBAAvB,MAAuB;AAAA,eAAdZ,EAAA,IAAI,GAAA,CAAA;AAAA,QAAA;;;;;;;;;;;;;;;;GCkDba,KAAsB;;;;;;;;;;;;;;;;;;;;;;AA3C5B,UAAMP,IAAQN,GAkBRc,IAAOC,GASPC,IAAeC,EAAwB,IAAI,GAC3CC,IAAaD,EAAwB,IAAI,GACzCE,IAAeF,EAAwB,IAAI,GAG3CG,IAAaH,EAAmD,MAAM,GACtEI,IAAeJ,EAAI,CAAC,GAEpBK,IAAaL,EAAI,EAAK;AAG5B,QAAIM,IAAS,GACTC,IAAS,GACTC,IAAe,GAEfC,IAAkD,QAGlDC,IAAe,IACfC,IAAY,IAEZC,IAAc,IAGdC,IAA6B,MAC7BC,IAA8B,MAC9BC,IAA2B,MAE3BC,IAAwC,MAExCC,IAA2B;AAG/B,aAASC,EAAUC,GAAiBC,GAAiB;AACnD,MAAI,CAAC/B,EAAM,eAAeqB,MAC1BJ,IAASa,GACTZ,IAASa,GACTZ,IAAe,GACfC,IAAc,QACdN,EAAW,QAAQ;AAAA,IACrB;AAIA,aAASkB,EAASF,GAAiBC,GAAiBE,GAA8B;AAChF,UAAI,CAACjC,EAAM,eAAeqB,KAAgBH,MAAW,EAAG,QAAO;AAE/D,YAAMgB,IAAKJ,IAAUb,GACfkB,IAAKJ,IAAUb;AAGrB,UAAIE,MAAgB,QAAQ;AAC1B,cAAMgB,IAAK,KAAK,IAAIF,CAAE,GAChBG,IAAK,KAAK,IAAIF,CAAE;AACtB,YAAIC,IAAK7B,MAAuB8B,IAAK9B;AACnC,UAAAa,IAAciB,IAAKD,IAAK,MAAM,aAAa;AAAA;AAE3C,iBAAO;AAAA,MAEX;AACA,UAAIhB,MAAgB,aAAc,QAAO;AAGzC,YAAMkB,IAAQC,QAAkB;AAIhC,UAAI,CAACD,KAASH,IAAK,GAAG;AACpB,cAAMK,IAAI9B,EAAa;AACvB,eAAI,GAAA8B,KACeA,EAAE,eAAeA,EAAE,YAAYA,EAAE,gBAAgB,KAClDP;AAAA,MAKpB;AACA,aAAKK,KAKLnB,IAFgB,KAAK,IAAI,GAAG,KAAK,IAAIgB,GAAI,GAAG,CAAC,GAG7CnB,EAAW,QAAQ,IAEfS,MAAiB,SACnBA,IAAe,sBAAsB,MAAM;AACzC,QAAAA,IAAe,MACfV,EAAa,QAAQI,IAAe,KAChCA,MAAiB,IACnBL,EAAW,QAAQ,SAEnBA,EAAW,QAAQK,IAAenB,EAAM,gBAAgB,YAAY;AAAA,MAExE,CAAC,IAIImC,IAAK,KArBO;AAAA,IAsBrB;AAGA,aAASM,IAAU;AACjB,MAAI,CAACzC,EAAM,eAAeqB,MAGtBI,MAAiB,SACnB,qBAAqBA,CAAY,GACjCA,IAAe,OAGjBT,EAAW,QAAQ,IAGfG,IAAenB,EAAM,iBACvBqB,IAAe,IACfP,EAAW,QAAQ,cACnBC,EAAa,QAAQ,IACrBP,EAAK,SAAS,MAEdO,EAAa,QAAQ,GACrBD,EAAW,QAAQ,SAGrB4B,GAAA;AAAA,IACF;AAGA,aAASC,KAAa;AACpB,MAAI,CAAC3C,EAAM,eAAeqB,MAEtBI,MAAiB,SACnB,qBAAqBA,CAAY,GACjCA,IAAe,OAGjBT,EAAW,QAAQ,IACnBD,EAAa,QAAQ,GACrBD,EAAW,QAAQ,QACnB4B,GAAA;AAAA,IACF;AAGA,aAASE,EAAiB,GAAe;AACvC,MAAAf,EAAU,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,OAAO;AAAA,IACtD;AAGA,aAASgB,EAAgB,GAAe;AACtC,YAAMC,IAAQ,EAAE,QAAQ,CAAC;AACzB,MAAId,EAASc,EAAM,SAASA,EAAM,SAAS,EAAE,UAAU,KAAK,EAAE,cAC5D,EAAE,eAAA;AAAA,IAEN;AAGA,aAASC,IAAiB;AACxB,MAAAN,EAAA;AAAA,IACF;AAGA,aAASO,KAAoB;AAC3B,MAAAL,GAAA;AAAA,IACF;AAGA,aAASM,GAAgB,GAAe;AAEtC,MAAI,CAACjD,EAAM,eAAeqB,KAAgB,EAAE,WAAW,MACvDE,IAAc,IACdM,EAAU,EAAE,SAAS,EAAE,OAAO;AAAA,IAChC;AAGA,aAASqB,GAAgB,GAAe;AACtC,MAAK3B,KACDS,EAAS,EAAE,SAAS,EAAE,SAAS,EAAI,KAErC,EAAE,eAAA;AAAA,IAEN;AAGA,aAASmB,KAAgB;AACvB,MAAK5B,MACLA,IAAc,IACdkB,EAAA;AAAA,IACF;AAEA,aAASC,KAAa;AACpB,MAAAzB,IAAS,GACTC,IAAS,GACTC,IAAe,GACfC,IAAc;AAAA,IAChB;AAGA,aAASgC,IAAe;AACtB,MAAIpD,EAAM,WAAWA,EAAM,YAAYA,EAAM,SAASsB,KAClDE,MAEJA,IAAc,OAAO,WAAW,MAAM;;AACpC,QAAAA,IAAc;AACd,cAAM6B,IAAY3C,EAAa;AAC/B,YAAI,CAAC2C,EAAW;AAEhB,cAAM,EAAE,WAAAC,GAAW,cAAAC,GAAc,cAAAC,EAAA,IAAiBH,GAC5CI,MAAgBC,IAAA9C,EAAW,UAAX,gBAAA8C,EAAkB,iBAAgB;AAGxD,YAAID,KAAiBF,GAAc;AAEjC,cAAI3B,MAA6B6B,EAAe;AAChD,UAAA7B,IAA2B6B,GAE3BE,GAAY,EAAI;AAChB;AAAA,QACF;AAGA,QAAIH,IAAeF,IAAYC,KAAgBvD,EAAM,iBAEnD2D,GAAY,EAAK;AAAA,MAErB,GAAG,GAAG;AAAA,IACR;AAEA,aAASA,GAAYC,IAAiB,IAAO;AAC3C,MAAAtC,IAAY,IACZd,EAAK,MAAM,GACXqD,EAAS,MAAM;;AACb,cAAMrB,IAAI9B,EAAa;AACvB,QAAK8B,MACDoB,IAEFpB,EAAE,YAAYA,EAAE,gBAIhBkB,IAAA7C,EAAa,UAAb,QAAA6C,EAAoB,eAAe,EAAE,OAAO,OAAO,UAAU;MAEjE,CAAC;AAAA,IACH;AAGA,aAASnB,KAAuB;;AAC9B,eAAOmB,IAAAhD,EAAa,UAAb,gBAAAgD,EAAoB,cAAa;AAAA,IAC1C;AAGA,aAASI,IAAgB;AACvB,MAAAzC,IAAe,IACfN,EAAa,QAAQ,GAEjBW,MAAc,QAAM,aAAaA,CAAS,GAC9CA,IAAY,OAAO,WAAW,MAAM;AAClC,QAAAZ,EAAW,QAAQ,QACnBY,IAAY;AAAA,MACd,GAAG,GAAG,GAENE,IAA2B,IAC3BiC,EAAS,MAAM;AACb,QAAAE,EAAA;AAAA,MACF,CAAC;AAAA,IACH;AAGA,aAASC,KAAa;AACpB,MAAA1C,IAAY,IACZuC,EAAS,MAAM;;AACb,cAAMJ,MAAgBC,IAAA9C,EAAW,UAAX,gBAAA8C,EAAkB,iBAAgB;AAExD,QAAI9B,MAA6B,MAAM6B,MAAkB7B,MACzDA,IAA2B,IAC3BmC,EAAA;AAAA,MACF,CAAC;AAAA,IACH;AAGA,aAASA,IAAqB;AAC5B,MAAI/D,EAAM,YAAYA,EAAM,WAAWA,EAAM,SAE7C6D,EAAS,MAAM;;AACb,cAAMJ,MAAgBC,IAAA9C,EAAW,UAAX,gBAAA8C,EAAkB,iBAAgB,GAClDH,MAAeU,IAAAvD,EAAa,UAAb,gBAAAuD,EAAoB,iBAAgB;AAEzD,QAAIR,KAAiBF,KAAgB,CAACjC,KACpC8B,EAAA;AAAA,MAEJ,CAAC;AAAA,IACH;AAGA,aAASc,KAAc;AACrB,MAAA1D,EAAK,OAAO;AAAA,IACd;AAGA,WAAA2D;AAAA,MACE,MAAMnE,EAAM;AAAA,MACZ,CAAAoE,MAAU;AACR,QAAKA,MACH9C,IAAY;AAAA,MAEhB;AAAA,IAAA,GAIF6C;AAAA,MACE,MAAMnE,EAAM;AAAA,MACZ,CAAAoE,MAAU;AACR,QAAIA,MACFxC,IAA2B;AAAA,MAE/B;AAAA,IAAA,GAGFyC,GAAU,MAAM;AACd,YAAMhB,IAAY3C,EAAa;AAC/B,MAAK2C,MAEDrD,EAAM,gBACRqD,EAAU,iBAAiB,cAAcT,GAAkB,EAAE,SAAS,IAAM,GAC5ES,EAAU,iBAAiB,aAAaR,GAAiB,EAAE,SAAS,IAAO,GAC3EQ,EAAU,iBAAiB,YAAYN,GAAgB,EAAE,SAAS,IAAM,GACxEM,EAAU,iBAAiB,eAAeL,IAAmB,EAAE,SAAS,IAAM,GAE9EK,EAAU,iBAAiB,aAAaJ,EAAe,GACvD,OAAO,iBAAiB,aAAaC,EAAe,GACpD,OAAO,iBAAiB,WAAWC,EAAa,IAGlDE,EAAU,iBAAiB,UAAUD,GAAc,EAAE,SAAS,IAAM,GAGhE,OAAO,iBAAmB,QAC5BzB,IAAiB,IAAI,eAAe,MAAM;AACxC,QAAAoC,EAAA;AAAA,MACF,CAAC,GACDpC,EAAe,QAAQ0B,CAAS,GAC5BzC,EAAW,SAAOe,EAAe,QAAQf,EAAW,KAAK,IAI/DiD,EAAS,MAAM;AACb,QAAAE,EAAA;AAAA,MACF,CAAC;AAAA,IACH,CAAC,GAEDO,GAAgB,MAAM;AACpB,YAAMjB,IAAY3C,EAAa;AAC/B,MAAI2C,MACFA,EAAU,oBAAoB,cAAcT,CAAgB,GAC5DS,EAAU,oBAAoB,aAAaR,CAAe,GAC1DQ,EAAU,oBAAoB,YAAYN,CAAc,GACxDM,EAAU,oBAAoB,eAAeL,EAAiB,GAC9DK,EAAU,oBAAoB,aAAaJ,EAAe,GAC1DI,EAAU,oBAAoB,UAAUD,CAAY,IAEtD,OAAO,oBAAoB,aAAaF,EAAe,GACvD,OAAO,oBAAoB,WAAWC,EAAa,GAC/CxB,MACFA,EAAe,WAAA,GACfA,IAAiB,OAEfF,MAAiB,SACnB,qBAAqBA,CAAY,GACjCA,IAAe,OAEbD,MAAgB,SAClB,aAAaA,CAAW,GACxBA,IAAc,OAEZE,MAAc,SAChB,aAAaA,CAAS,GACtBA,IAAY;AAAA,IAEhB,CAAC,GAED6C,EAAa;AAAA,MACX,SAAST;AAAA,MACT,eAAAA;AAAA,MACA,gBAAgBA;AAAA,MAChB,YAAAE;AAAA,MACA,cAAcA;AAAA,MACd,oBAAAD;AAAA,IAAA,CACD,mBAICvE,EAyCM,OAAA;AAAA,eAzCG;AAAA,MAAJ,KAAIkB;AAAA,MAAe,OAAM;AAAA,IAAA;MAGpBhB,EAAA,oBADRF,EAYM,OAAA;AAAA;QAVJ,OAAKC,EAAA,CAAC,6BAA2B,EAAA,iBACNuB,EAAA,MAAA,CAAU,CAAA;AAAA,QACpC,oCAAkCD,EAAA,KAAY,OAAA;AAAA,MAAA;QAE/CyD,GAAA7E,EAKM,OALNC,IAKM;AAAA,UAHQkB,EAAA,UAAU,qBAAtBtB,EAAgD,QAAAK,EAAA,KAC/BiB,EAAA,UAAU,kBAA3BtB,EAAwF,QAAxFiF,IAAwF3E,EAArBJ,EAAA,WAAW,GAAA,CAAA,WAC9EF,EAA0D,QAA1DkF,IAA0D5E,EAArBJ,EAAA,WAAW,GAAA,CAAA;AAAA,QAAA;eAJrCoB,EAAA,UAAU,MAAA;AAAA,QAAA;;MASzBnB,EAuBM,OAAA;AAAA,iBAtBA;AAAA,QAAJ,KAAIiB;AAAA,QACJ,OAAKnB,EAAA,CAAC,8BAA4B,EAAA,iBACPuB,EAAA,MAAA,CAAU,CAAA;AAAA,QACpC,OAAK2D,EAAEjF,EAAA,cAAW,EAAA,WAAA,cAA8BqB,EAAA,KAAY,MAAA,IAAA,CAAA,CAAA;AAAA,MAAA;QAGlDrB,EAAA,kBAAXU,EAAA,GAAAZ,EAEM,OAFNoF,IAEM;AAAA,UADJC,GAAyCC,GAAA,EAAxB,MAAMpF,EAAA,eAAA,GAAc,MAAA,GAAA,CAAA,MAAA,CAAA;AAAA,QAAA;QAIjBA,EAAA,YAAYA,EAAA,gBAAlCqF,EAA+DC,IAAA;AAAA;UAAnB,MAAMtF,EAAA;AAAA,QAAA;QAGlDW,EAAaC,EAAA,QAAA,SAAA;AAAA,QAGDZ,EAAA,4BAAZF,EAIM,OAAA;AAAA;mBAJmB;AAAA,UAAJ,KAAIqB;AAAA,UAAe,OAAM;AAAA,QAAA;UACtBnB,EAAA,WAAWoB,EAAA,UAAU,qBAA3CiE,EAAoFD,GAAA;AAAA;YAArB,MAAMpF,EAAA;AAAA,UAAA,yBACpDA,EAAA,iBAAjBF,EAA6E,QAA7EyF,IAA6EnF,EAAtBJ,EAAA,YAAY,GAAA,CAAA,KAClDA,EAAA,cAAjBF,EAAyF,QAAA;AAAA;YAAjE,OAAM;AAAA,YAAiB,SAAO0E;AAAA,UAAA,KAAgBxE,EAAA,SAAS,GAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;ACzevF,UAAMM,IAAQN,GAyDRc,IAAOC,GASPyE,IAAYvE,EAAgD,IAAI,GAEhEwE,IAAOxE,EAAS,EAAE,GAClByE,IAAUzE,EAAI,EAAK,GACnB0E,IAAW1E,EAAI,EAAK,GACpB2E,IAAU3E,EAAI,EAAK,GACnB4E,IAAY5E,EAAI,EAAK,GACrB6E,IAAQ7E,EAAI,CAAC,GACb8E,IAAO9E,EAAI,CAAC,GACZU,IAAeV,EAAI,EAAK;AAE9B,QAAI+E,IAAa;AAEjB,mBAAeC,EAAKC,IAAQ,IAAO;AACjC,MAAIA,MACFH,EAAK,QAAQ,GACbJ,EAAS,QAAQ,IACjBC,EAAQ,QAAQ,KAGlBF,EAAQ,QAAQ,IAChBG,EAAU,QAAQ,IAElBG,KAAc;AACd,YAAMG,IAAMH;AAEZ,UAAI;AACF,cAAMI,IAAS,MAAM9F,EAAM,QAAQ;AAAA,UACjC,MAAMyF,EAAK;AAAA,UACX,UAAUzF,EAAM;AAAA,UAChB,GAAGA,EAAM;AAAA,QAAA,CACV;AAED,YAAI6F,MAAQH,EAAY;AAExB,cAAMK,KAAUD,KAAA,gBAAAA,EAAQ,YAAW,CAAA;AACnC,QAAIF,KAEFT,EAAK,QAAQY,GACbP,EAAM,QAAQ,KAEdL,EAAK,QAAQ,CAAC,GAAGA,EAAK,OAAO,GAAGY,CAAO,GAEzCP,EAAM,QAAQ,QAAOM,KAAA,gBAAAA,EAAQ,UAAS,CAAC,GACvCT,EAAS,QAAQF,EAAK,MAAM,UAAUK,EAAM,OAC5CF,EAAQ,QAAQH,EAAK,MAAM,WAAW,GAEtC3E,EAAK,WAAW,EAAE,SAAAuF,GAAS,OAAOP,EAAM,OAAO,OAAAI,GAAO,GACtDpF,EAAK,eAAe2E,EAAK,KAAK;AAAA,MAChC,SAASa,GAAK;AACZ,YAAIH,MAAQH,EAAY;AACxB,QAAAH,EAAU,QAAQ,IAClB/E,EAAK,SAASwF,CAAG;AAAA,MACnB,UAAA;AACE,QAAIH,MAAQH,MACVN,EAAQ,QAAQ;AAAA,MAEpB;AAAA,IACF;AAEA,mBAAea,IAAgB;;AAC7B,MAAA5E,EAAa,QAAQ,KAErBqC,IAAAwB,EAAU,UAAV,QAAAxB,EAAiB,iBAEjB,MAAM,IAAI,QAAc,CAAAwC,MAAW;AACjC,mBAAW,MAAMA,EAAA,GAAW,GAAG;AAAA,MACjC,CAAC,GAED,MAAMP,EAAK,EAAI,GACftE,EAAa,QAAQ;AAAA,IACvB;AAEA,mBAAe8E,IAAa;;AAC1B,UAAId,EAAS,OAAO;AAClB,SAAA3B,IAAAwB,EAAU,UAAV,QAAAxB,EAAiB;AACjB;AAAA,MACF;AAEA,MAAIyB,EAAK,MAAM,SAAS,MACtBM,EAAK,SAAS,IAEhB,MAAME,EAAK,EAAK,IAChB1B,IAAAiB,EAAU,UAAV,QAAAjB,EAAiB;AAAA,IACnB;AAEA,aAASC,IAAc;AACrB,MAAAqB,EAAU,QAAQ,IACdJ,EAAK,MAAM,WAAW,IACxBQ,EAAK,EAAI,IAETQ,EAAA;AAAA,IAEJ;AAEA,mBAAeC,IAAS;AACtB,YAAMT,EAAK,EAAI;AAAA,IACjB;AAEA,WAAAxB;AAAA,MACE,MAAMnE,EAAM;AAAA,MACZ,MAAM;AACJ,QAAIA,EAAM,cACRoG,EAAA;AAAA,MAEJ;AAAA,MACA,EAAE,MAAM,GAAA;AAAA,IAAK,GAGfjC;AAAA,MACEgB;AAAA,MACA,YAAY;;AACV,cAAMtB,EAAA,IACNH,IAAAwB,EAAU,UAAV,QAAAxB,EAAiB;AAAA,MACnB;AAAA,MACA,EAAE,MAAM,GAAA;AAAA,IAAK,GAGfW,GAAU,MAAM;AACd,MAAIrE,EAAM,aACR2F,EAAK,EAAI;AAAA,IAEb,CAAC,GAEDpB,EAAa;AAAA;AAAA,MAEX,QAAA6B;AAAA;AAAA,MAEA,SAASA;AAAA;AAAA,MAET,MAAAjB;AAAA;AAAA,MAEA,SAAAC;AAAA;AAAA,MAEA,UAAAC;AAAA;AAAA,MAEA,SAAAC;AAAA;AAAA,MAEA,OAAAE;AAAA;AAAA,MAEA,YAAY,MAAA;;AAAM,gBAAA9B,IAAAwB,EAAU,UAAV,gBAAAxB,EAAiB;AAAA;AAAA,MACnC,eAAe,MAAA;;AAAM,gBAAAA,IAAAwB,EAAU,UAAV,gBAAAxB,EAAiB;AAAA;AAAA,MACtC,oBAAoB,MAAA;;AAAM,gBAAAA,IAAAwB,EAAU,UAAV,gBAAAxB,EAAiB;AAAA;AAAA,IAAmB,CAC/D,mBAICqB,EA8BiBsB,IAAA;AAAA,eA7BX;AAAA,MAAJ,KAAInB;AAAA,MACH,SAASE,EAAA;AAAA,MACT,UAAUC,EAAA;AAAA,MACV,YAAUC,EAAA;AAAA,MACV,OAAOC,EAAA;AAAA,MACP,gBAAc7F,EAAA;AAAA,MACd,kBAAgBA,EAAA;AAAA,MAChB,kBAAgBA,EAAA;AAAA,MAChB,cAAYA,EAAA;AAAA,MACZ,gBAAcA,EAAA;AAAA,MACd,gBAAcA,EAAA;AAAA,MACd,mBAAiBA,EAAA;AAAA,MACjB,gBAAcA,EAAA;AAAA,MACd,iBAAeA,EAAA;AAAA,MACf,cAAYA,EAAA;AAAA,MACZ,mBAAiB2B,EAAA;AAAA,MACjB,QAAM8E;AAAA,MACN,WAASF;AAAA,MACT,SAAO/B;AAAA,IAAA;kBAER,MAQE;AAAA,QARF7D,EAQEC,EAAA,QAAA,WAAA;AAAA,UAPC,MAAM6E,EAAA;AAAA,UACN,SAASC,EAAA;AAAA,UACT,UAAUC,EAAA;AAAA,UACV,SAAUC,EAAA;AAAA,UACV,OAAOC,EAAA;AAAA,UACP,OAAOC,EAAA;AAAA,UACP,QAAAY;AAAA,QAAA;;;;;;ACxPP,IAAIE,KAAgB;AAEpB,MAAMC,KAAa;AAEZ,SAASC,KAAc;AAC5B,MAAIF,MAAiB,OAAO,WAAa,IAAa;AAEtD,QAAMG,IAAQ,SAAS,cAAc,OAAO;AAC5C,EAAAA,EAAM,aAAa,eAAe,oBAAoB,GACtDA,EAAM,cAAcF,IACpB,SAAS,KAAK,YAAYE,CAAK,GAE/BH,KAAgB;AAClB;ACNAE,GAAA;AAMO,SAASE,GAAQC,GAAU;AAChC,EAAAA,EAAI,UAAU,kBAAkBN,EAAc,GAC9CM,EAAI,UAAU,oBAAoBC,EAAgB,GAClDD,EAAI,UAAU,kBAAkB7B,CAAc,GAC9C6B,EAAI,UAAU,kBAAkB3B,EAAc;AAChD;AAGA,MAAA6B,KAAe;AAAA,EACb,SAAAH;AACF;"}
|
package/dist/index.umd.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(function(p,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(p=typeof globalThis<"u"?globalThis:p||self,e(p.ZxInfiniteScroll={},p.Vue))})(this,function(p,e){"use strict";const le={class:"loading-content"},oe={class:"loading-text"},ae=e.defineComponent({name:"RequestLoading",__name:"request-loading",props:{text:{default:"加载中..."},fullscreen:{type:Boolean,default:!1}},setup(n){return(m,g)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(["loading-container",{fullscreen:n.fullscreen}])},[e.createElementVNode("div",le,[g[0]||(g[0]=e.createElementVNode("span",{class:"loading-spinner"},null,-1)),e.createElementVNode("span",oe,e.toDisplayString(n.text),1)])],2))}}),I=(n,m)=>{const g=n.__vccOpts||n;for(const[o,x]of m)g[o]=x;return g},M=I(ae,[["__scopeId","data-v-d89150ba"]]),ie=["src"],re={key:0,class:"empty-text"},se="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='80' viewBox='0 0 96 80' fill='none'%3E%3Cpath d='M8 16C8 12.6863 10.6863 10 14 10H38L44 16H82C85.3137 16 88 18.6863 88 22V66C88 69.3137 85.3137 72 82 72H14C10.6863 72 8 69.3137 8 66V16Z' fill='%23E8E8E8'/%3E%3Cpath d='M8 20C8 18.3431 9.34315 17 11 17H85C86.6569 17 88 18.3431 88 20V66C88 69.3137 85.3137 72 82 72H14C10.6863 72 8 69.3137 8 66V20Z' fill='%23F5F5F5'/%3E%3Cpath d='M38 42C38 40.8954 38.8954 40 40 40H56C57.1046 40 58 40.8954 58 42V44C58 45.1046 57.1046 46 56 46H40C38.8954 46 38 45.1046 38 44V42Z' fill='%23D9D9D9'/%3E%3C/svg%3E",N=I(e.defineComponent({name:"EmptyComponent",__name:"empty-component",props:{text:{default:"暂无数据"},showText:{type:Boolean,default:!0},paddingTop:{default:168},emptyImage:{default:""}},setup(n){const m=n,g=e.computed(()=>({paddingTop:typeof m.paddingTop=="number"?`${m.paddingTop}px`:m.paddingTop})),o=e.computed(()=>m.emptyImage||se);return(x,a)=>(e.openBlock(),e.createElementBlock("div",{class:"empty-wrap",style:e.normalizeStyle(g.value)},[e.createElementVNode("img",{class:"empty-image",src:o.value,alt:""},null,8,ie),n.showText?(e.openBlock(),e.createElementBlock("p",re,[e.renderSlot(x.$slots,"default",{},()=>[e.createTextVNode(e.toDisplayString(n.text),1)],!0)])):e.createCommentVNode("",!0)],4))}}),[["__scopeId","data-v-3c452901"]]),ce={class:"pull-refresh-content"},fe={key:0},de={key:1,class:"pull-text"},ue={key:2,class:"pull-text"},he={key:0,class:"content-loading-mask"},pe={key:1,class:"finished-text"},O=8,$=I(e.defineComponent({name:"InfiniteScroll",__name:"infinite-scroll",props:{loading:{type:Boolean,default:!1},finished:{type:Boolean,default:!1},isEmpty:{type:Boolean,default:!1},emptyText:{default:"暂无数据"},pullRefresh:{type:Boolean,default:!1},pullThreshold:{default:70},loadThreshold:{default:50},pullingText:{default:"下拉刷新"},loosingText:{default:"释放即可刷新"},refreshingText:{default:"加载中..."},loadingText:{default:"加载中..."},finishedText:{default:"没有更多了~"},errorText:{default:"加载失败,点击重试"},error:{type:Boolean,default:!1},contentLoading:{type:Boolean,default:!1}},emits:["refresh","load","retry"],setup(n,{expose:m,emit:g}){const o=n,x=g,a=e.ref(null),i=e.ref(null),b=e.ref(null),r=e.ref("idle"),u=e.ref(0),y=e.ref(!1);let k=0,w=0,v=0,T="none",h=!1,R=!1,S=!1,C=null,c=null,l=null,f=null,d=-1;function z(t,s){!o.pullRefresh||h||(k=t,w=s,v=0,T="none",r.value="idle")}function X(t,s,E){if(!o.pullRefresh||h||w===0)return!1;const H=t-k,B=s-w;if(T==="none"){const L=Math.abs(H),F=Math.abs(B);if(L>O||F>O)T=F>L*1.5?"vertical":"horizontal";else return!1}if(T==="horizontal")return!1;const D=ve()<=1;if(!D&&B<0){const L=a.value;return!!(L&&L.scrollHeight-L.scrollTop-L.clientHeight<=1&&E)}return D?(v=Math.max(0,Math.min(B,150)),y.value=!0,c===null&&(c=requestAnimationFrame(()=>{c=null,u.value=v*.5,v===0?r.value="idle":r.value=v>o.pullThreshold?"loosing":"pulling"})),B>0):!1}function Z(){!o.pullRefresh||h||(c!==null&&(cancelAnimationFrame(c),c=null),y.value=!1,v>o.pullThreshold?(h=!0,r.value="refreshing",u.value=50,x("refresh")):(u.value=0,r.value="idle"),ee())}function ye(){!o.pullRefresh||h||(c!==null&&(cancelAnimationFrame(c),c=null),y.value=!1,u.value=0,r.value="idle",ee())}function _(t){z(t.touches[0].clientX,t.touches[0].clientY)}function U(t){const s=t.touches[0];X(s.clientX,s.clientY,t.cancelable)&&t.cancelable&&t.preventDefault()}function K(){Z()}function G(){ye()}function J(t){!o.pullRefresh||h||t.button!==0||(S=!0,z(t.clientX,t.clientY))}function Q(t){S&&X(t.clientX,t.clientY,!0)&&t.preventDefault()}function W(){S&&(S=!1,Z())}function ee(){k=0,w=0,v=0,T="none"}function q(){o.loading||o.finished||o.error||R||C||(C=window.setTimeout(()=>{var D;C=null;const t=a.value;if(!t)return;const{scrollTop:s,clientHeight:E,scrollHeight:H}=t,B=((D=i.value)==null?void 0:D.clientHeight)||0;if(B<=E){if(d===B)return;d=B,te(!0);return}H-s-E<=o.loadThreshold&&te(!1)},100))}function te(t=!1){R=!0,x("load"),e.nextTick(()=>{var E;const s=a.value;s&&(t?s.scrollTop=s.scrollHeight:(E=b.value)==null||E.scrollIntoView({block:"end",behavior:"smooth"}))})}function ve(){var t;return((t=a.value)==null?void 0:t.scrollTop)??0}function j(){h=!1,u.value=0,l!==null&&clearTimeout(l),l=window.setTimeout(()=>{r.value="idle",l=null},250),d=-1,e.nextTick(()=>{V()})}function ne(){R=!1,e.nextTick(()=>{var s;const t=((s=i.value)==null?void 0:s.clientHeight)||0;d!==-1&&t===d||(d=-1,V())})}function V(){o.finished||o.loading||o.error||e.nextTick(()=>{var E,H;const t=((E=i.value)==null?void 0:E.clientHeight)||0,s=((H=a.value)==null?void 0:H.clientHeight)||0;t<=s&&!R&&q()})}function Te(){x("retry")}return e.watch(()=>o.loading,t=>{t||(R=!1)}),e.watch(()=>o.finished,t=>{t&&(d=-1)}),e.onMounted(()=>{const t=a.value;t&&(o.pullRefresh&&(t.addEventListener("touchstart",_,{passive:!0}),t.addEventListener("touchmove",U,{passive:!1}),t.addEventListener("touchend",K,{passive:!0}),t.addEventListener("touchcancel",G,{passive:!0}),t.addEventListener("mousedown",J),window.addEventListener("mousemove",Q),window.addEventListener("mouseup",W)),t.addEventListener("scroll",q,{passive:!0}),typeof ResizeObserver<"u"&&(f=new ResizeObserver(()=>{V()}),f.observe(t),i.value&&f.observe(i.value)),e.nextTick(()=>{V()}))}),e.onBeforeUnmount(()=>{const t=a.value;t&&(t.removeEventListener("touchstart",_),t.removeEventListener("touchmove",U),t.removeEventListener("touchend",K),t.removeEventListener("touchcancel",G),t.removeEventListener("mousedown",J),t.removeEventListener("scroll",q)),window.removeEventListener("mousemove",Q),window.removeEventListener("mouseup",W),f&&(f.disconnect(),f=null),c!==null&&(cancelAnimationFrame(c),c=null),C!==null&&(clearTimeout(C),C=null),l!==null&&(clearTimeout(l),l=null)}),m({refresh:j,finishRefresh:j,finishPullDown:j,finishLoad:ne,finishPullUp:ne,checkContentHeight:V}),(t,s)=>(e.openBlock(),e.createElementBlock("div",{ref_key:"containerRef",ref:a,class:"infinite-scroll"},[n.pullRefresh?(e.openBlock(),e.createElementBlock("div",{key:0,class:e.normalizeClass(["pull-refresh-indicator",{"no-transition":y.value}]),style:e.normalizeStyle({transform:`translateY(${u.value}px)`})},[e.withDirectives(e.createElementVNode("div",ce,[r.value==="refreshing"?(e.openBlock(),e.createElementBlock("span",fe)):r.value==="loosing"?(e.openBlock(),e.createElementBlock("span",de,e.toDisplayString(n.loosingText),1)):(e.openBlock(),e.createElementBlock("span",ue,e.toDisplayString(n.pullingText),1))],512),[[e.vShow,r.value!=="idle"]])],6)):e.createCommentVNode("",!0),e.createElementVNode("div",{ref_key:"contentRef",ref:i,class:e.normalizeClass(["infinite-scroll-content",{"no-transition":y.value}]),style:e.normalizeStyle(n.pullRefresh?{transform:`translateY(${u.value}px)`}:{})},[n.contentLoading?(e.openBlock(),e.createElementBlock("div",he,[e.createVNode(M,{text:n.refreshingText},null,8,["text"])])):e.createCommentVNode("",!0),n.isEmpty&&!n.loading?(e.openBlock(),e.createBlock(N,{key:1,text:n.emptyText},null,8,["text"])):e.createCommentVNode("",!0),e.renderSlot(t.$slots,"default",{},void 0,!0),n.isEmpty?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("div",{key:2,ref_key:"indicatorRef",ref:b,class:"load-more-indicator"},[n.loading&&r.value!=="refreshing"?(e.openBlock(),e.createBlock(M,{key:0,text:n.loadingText},null,8,["text"])):n.finished?(e.openBlock(),e.createElementBlock("span",pe,e.toDisplayString(n.finishedText),1)):n.error?(e.openBlock(),e.createElementBlock("span",{key:2,class:"error-text",onClick:Te},e.toDisplayString(n.errorText),1)):e.createCommentVNode("",!0)],512))],6)],512))}}),[["__scopeId","data-v-bf5ca1d9"]]),Y=e.defineComponent({name:"InfiniteScrollV2",__name:"infinite-scroll-v2",props:{fetchFn:{},params:{default:()=>({})},pageSize:{default:10},autoReload:{type:Boolean,default:!0},immediate:{type:Boolean,default:!0},pullRefresh:{type:Boolean,default:!1},pullThreshold:{default:70},loadThreshold:{default:50},emptyText:{default:"暂无数据"},pullingText:{default:"下拉刷新"},loosingText:{default:"释放即可刷新"},refreshingText:{default:"加载中..."},loadingText:{default:"加载中..."},finishedText:{default:"没有更多了~"},errorText:{default:"加载失败,点击重试"}},emits:["success","error","update:list"],setup(n,{expose:m,emit:g}){const o=n,x=g,a=e.ref(null),i=e.ref([]),b=e.ref(!1),r=e.ref(!1),u=e.ref(!1),y=e.ref(!1),k=e.ref(0),w=e.ref(1),v=e.ref(!1);let T=0;async function h(l=!1){l&&(w.value=1,r.value=!1,u.value=!1),b.value=!0,y.value=!1,T+=1;const f=T;try{const d=await o.fetchFn({page:w.value,pageSize:o.pageSize,...o.params});if(f!==T)return;const z=(d==null?void 0:d.records)||[];l?(i.value=z,k.value=0):i.value=[...i.value,...z],k.value=Number((d==null?void 0:d.total)||0),r.value=i.value.length>=k.value,u.value=i.value.length===0,x("success",{records:z,total:k.value,reset:l}),x("update:list",i.value)}catch(d){if(f!==T)return;y.value=!0,x("error",d)}finally{f===T&&(b.value=!1)}}async function R(){var l;v.value=!0,(l=a.value)==null||l.finishRefresh(),await new Promise(f=>{setTimeout(()=>f(),250)}),await h(!0),v.value=!1}async function S(){var l,f;if(r.value){(l=a.value)==null||l.finishLoad();return}i.value.length>0&&(w.value+=1),await h(!1),(f=a.value)==null||f.finishLoad()}function C(){y.value=!1,i.value.length===0?h(!0):S()}async function c(){await h(!0)}return e.watch(()=>o.params,()=>{o.autoReload&&c()},{deep:!0}),e.watch(i,async()=>{var l;await e.nextTick(),(l=a.value)==null||l.checkContentHeight()},{deep:!0}),e.onMounted(()=>{o.immediate&&h(!0)}),m({reload:c,refresh:c,list:i,loading:b,finished:r,isEmpty:u,total:k,finishLoad:()=>{var l;return(l=a.value)==null?void 0:l.finishLoad()},finishRefresh:()=>{var l;return(l=a.value)==null?void 0:l.finishRefresh()},checkContentHeight:()=>{var l;return(l=a.value)==null?void 0:l.checkContentHeight()}}),(l,f)=>(e.openBlock(),e.createBlock($,{ref_key:"scrollRef",ref:a,loading:b.value,finished:r.value,"is-empty":u.value,error:y.value,"pull-refresh":n.pullRefresh,"pull-threshold":n.pullThreshold,"load-threshold":n.loadThreshold,"empty-text":n.emptyText,"pulling-text":n.pullingText,"loosing-text":n.loosingText,"refreshing-text":n.refreshingText,"loading-text":n.loadingText,"finished-text":n.finishedText,"error-text":n.errorText,"content-loading":v.value,onLoad:S,onRefresh:R,onRetry:C},{default:e.withCtx(()=>[e.renderSlot(l.$slots,"default",{list:i.value,loading:b.value,finished:r.value,isEmpty:u.value,error:y.value,total:k.value,reload:c})]),_:3},8,["loading","finished","is-empty","error","pull-refresh","pull-threshold","load-threshold","empty-text","pulling-text","loosing-text","refreshing-text","loading-text","finished-text","error-text","content-loading"]))}});let A=!1;const me=".loading-content[data-v-d89150ba]{display:flex;align-items:center;justify-content:center;gap:8px;padding:6px 12px;border-radius:16px;color:var(--zx-primary-color, #1e5efd);font-size:14px}.loading-spinner[data-v-d89150ba]{width:18px;height:18px;border-radius:50%;border:2.5px solid var(--zx-primary-color, #1e5efd);border-top-color:transparent;animation:spin-d89150ba .8s linear infinite}@keyframes spin-d89150ba{to{transform:rotate(360deg)}}.loading-container.fullscreen[data-v-d89150ba]{position:fixed;top:0;left:0;width:100%;height:100%;display:flex;align-items:center;justify-content:center;background-color:#ffffff80}.empty-wrap[data-v-3c452901]{display:flex;flex-direction:column;align-items:center}.empty-image[data-v-3c452901]{width:96px;height:80px}.empty-text[data-v-3c452901]{margin:12px 0 0;color:#8c8f97;font-size:12px;line-height:22px}.infinite-scroll[data-v-bf5ca1d9]{height:100%;position:relative;min-height:100%;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;touch-action:pan-y;-webkit-overflow-scrolling:touch}.pull-refresh-indicator[data-v-bf5ca1d9]{position:absolute;top:-50px;left:0;right:0;height:50px;transition:transform .25s}.pull-refresh-indicator.no-transition[data-v-bf5ca1d9]{transition:none}.pull-refresh-content[data-v-bf5ca1d9]{display:flex;align-items:center;justify-content:center;height:100%;font-size:14px;color:#969799}.pull-text[data-v-bf5ca1d9]{color:var(--zx-primary-color, #1989fa);font-size:14px}.infinite-scroll-content[data-v-bf5ca1d9]{position:relative;transition:transform .25s;min-height:100%}.infinite-scroll-content.no-transition[data-v-bf5ca1d9]{transition:none}.content-loading-mask[data-v-bf5ca1d9]{position:absolute;top:0;left:0;right:0;bottom:0;background-color:#fffc;display:flex;align-items:center;justify-content:center;z-index:10;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.load-more-indicator[data-v-bf5ca1d9]{display:flex;align-items:center;justify-content:center;min-height:50px;padding:16px 0;font-size:14px;color:#8c8f97}.finished-text[data-v-bf5ca1d9]{color:#8c8f97;font-size:14px}.error-text[data-v-bf5ca1d9]{color:#ee0a24;font-size:14px;cursor:pointer}.error-text[data-v-bf5ca1d9]:active{opacity:.7}";function ge(){if(A||typeof document>"u")return;const n=document.createElement("style");n.setAttribute("data-source","zx-infinite-scroll"),n.textContent=me,document.head.appendChild(n),A=!0}ge();function P(n){n.component("InfiniteScroll",$),n.component("InfiniteScrollV2",Y),n.component("RequestLoading",M),n.component("EmptyComponent",N)}const xe={install:P};p.EmptyComponent=N,p.InfiniteScroll=$,p.InfiniteScrollV2=Y,p.RequestLoading=M,p.default=xe,p.install=P,Object.defineProperties(p,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
|
1
|
+
(function(p,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(p=typeof globalThis<"u"?globalThis:p||self,e(p.ZxInfiniteScroll={},p.Vue))})(this,function(p,e){"use strict";const ne={class:"zx-loading-content"},le={class:"zx-loading-text"},M=e.defineComponent({name:"RequestLoading",__name:"request-loading",props:{text:{default:"加载中..."},fullscreen:{type:Boolean,default:!1}},setup(n){return(k,z)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(["zx-loading-container",{fullscreen:n.fullscreen}])},[e.createElementVNode("div",ne,[z[0]||(z[0]=e.createElementVNode("span",{class:"zx-loading-spinner"},null,-1)),e.createElementVNode("span",le,e.toDisplayString(n.text),1)])],2))}}),oe=["src"],ie={key:0,class:"zx-empty-text"},ae="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='80' viewBox='0 0 96 80' fill='none'%3E%3Cpath d='M8 16C8 12.6863 10.6863 10 14 10H38L44 16H82C85.3137 16 88 18.6863 88 22V66C88 69.3137 85.3137 72 82 72H14C10.6863 72 8 69.3137 8 66V16Z' fill='%23E8E8E8'/%3E%3Cpath d='M8 20C8 18.3431 9.34315 17 11 17H85C86.6569 17 88 18.3431 88 20V66C88 69.3137 85.3137 72 82 72H14C10.6863 72 8 69.3137 8 66V20Z' fill='%23F5F5F5'/%3E%3Cpath d='M38 42C38 40.8954 38.8954 40 40 40H56C57.1046 40 58 40.8954 58 42V44C58 45.1046 57.1046 46 56 46H40C38.8954 46 38 45.1046 38 44V42Z' fill='%23D9D9D9'/%3E%3C/svg%3E",N=e.defineComponent({name:"EmptyComponent",__name:"empty-component",props:{text:{default:"暂无数据"},showText:{type:Boolean,default:!0},paddingTop:{default:168},emptyImage:{default:""}},setup(n){const k=n,z=e.computed(()=>({paddingTop:typeof k.paddingTop=="number"?`${k.paddingTop}px`:k.paddingTop})),o=e.computed(()=>k.emptyImage||ae);return(v,i)=>(e.openBlock(),e.createElementBlock("div",{class:"zx-empty-wrap",style:e.normalizeStyle(z.value)},[e.createElementVNode("img",{class:"zx-empty-image",src:o.value,alt:""},null,8,oe),n.showText?(e.openBlock(),e.createElementBlock("p",ie,[e.renderSlot(v.$slots,"default",{},()=>[e.createTextVNode(e.toDisplayString(n.text),1)])])):e.createCommentVNode("",!0)],4))}}),re={class:"zx-pull-refresh-content"},se={key:0},ce={key:1,class:"zx-pull-text"},fe={key:2,class:"zx-pull-text"},de={key:0,class:"zx-content-loading-mask"},ue={key:1,class:"zx-finished-text"},F=8,$=e.defineComponent({name:"InfiniteScroll",__name:"infinite-scroll",props:{loading:{type:Boolean,default:!1},finished:{type:Boolean,default:!1},isEmpty:{type:Boolean,default:!1},emptyText:{default:"暂无数据"},pullRefresh:{type:Boolean,default:!1},pullThreshold:{default:70},loadThreshold:{default:50},pullingText:{default:"下拉刷新"},loosingText:{default:"释放即可刷新"},refreshingText:{default:"加载中..."},loadingText:{default:"加载中..."},finishedText:{default:"没有更多了~"},errorText:{default:"加载失败,点击重试"},error:{type:Boolean,default:!1},contentLoading:{type:Boolean,default:!1}},emits:["refresh","load","retry"],setup(n,{expose:k,emit:z}){const o=n,v=z,i=e.ref(null),a=e.ref(null),E=e.ref(null),r=e.ref("idle"),u=e.ref(0),m=e.ref(!1);let y=0,w=0,x=0,g="none",h=!1,R=!1,S=!1,C=null,c=null,l=null,f=null,d=-1;function b(t,s){!o.pullRefresh||h||(y=t,w=s,x=0,g="none",r.value="idle")}function P(t,s,T){if(!o.pullRefresh||h||w===0)return!1;const H=t-y,B=s-w;if(g==="none"){const L=Math.abs(H),q=Math.abs(B);if(L>F||q>F)g=q>L*1.5?"vertical":"horizontal";else return!1}if(g==="horizontal")return!1;const D=ge()<=1;if(!D&&B<0){const L=i.value;return!!(L&&L.scrollHeight-L.scrollTop-L.clientHeight<=1&&T)}return D?(x=Math.max(0,Math.min(B,150)),m.value=!0,c===null&&(c=requestAnimationFrame(()=>{c=null,u.value=x*.5,x===0?r.value="idle":r.value=x>o.pullThreshold?"loosing":"pulling"})),B>0):!1}function X(){!o.pullRefresh||h||(c!==null&&(cancelAnimationFrame(c),c=null),m.value=!1,x>o.pullThreshold?(h=!0,r.value="refreshing",u.value=50,v("refresh")):(u.value=0,r.value="idle"),_())}function xe(){!o.pullRefresh||h||(c!==null&&(cancelAnimationFrame(c),c=null),m.value=!1,u.value=0,r.value="idle",_())}function Z(t){b(t.touches[0].clientX,t.touches[0].clientY)}function U(t){const s=t.touches[0];P(s.clientX,s.clientY,t.cancelable)&&t.cancelable&&t.preventDefault()}function K(){X()}function G(){xe()}function J(t){!o.pullRefresh||h||t.button!==0||(S=!0,b(t.clientX,t.clientY))}function Q(t){S&&P(t.clientX,t.clientY,!0)&&t.preventDefault()}function W(){S&&(S=!1,X())}function _(){y=0,w=0,x=0,g="none"}function I(){o.loading||o.finished||o.error||R||C||(C=window.setTimeout(()=>{var D;C=null;const t=i.value;if(!t)return;const{scrollTop:s,clientHeight:T,scrollHeight:H}=t,B=((D=a.value)==null?void 0:D.clientHeight)||0;if(B<=T){if(d===B)return;d=B,ee(!0);return}H-s-T<=o.loadThreshold&&ee(!1)},100))}function ee(t=!1){R=!0,v("load"),e.nextTick(()=>{var T;const s=i.value;s&&(t?s.scrollTop=s.scrollHeight:(T=E.value)==null||T.scrollIntoView({block:"end",behavior:"smooth"}))})}function ge(){var t;return((t=i.value)==null?void 0:t.scrollTop)??0}function j(){h=!1,u.value=0,l!==null&&clearTimeout(l),l=window.setTimeout(()=>{r.value="idle",l=null},250),d=-1,e.nextTick(()=>{V()})}function te(){R=!1,e.nextTick(()=>{var s;const t=((s=a.value)==null?void 0:s.clientHeight)||0;d!==-1&&t===d||(d=-1,V())})}function V(){o.finished||o.loading||o.error||e.nextTick(()=>{var T,H;const t=((T=a.value)==null?void 0:T.clientHeight)||0,s=((H=i.value)==null?void 0:H.clientHeight)||0;t<=s&&!R&&I()})}function ye(){v("retry")}return e.watch(()=>o.loading,t=>{t||(R=!1)}),e.watch(()=>o.finished,t=>{t&&(d=-1)}),e.onMounted(()=>{const t=i.value;t&&(o.pullRefresh&&(t.addEventListener("touchstart",Z,{passive:!0}),t.addEventListener("touchmove",U,{passive:!1}),t.addEventListener("touchend",K,{passive:!0}),t.addEventListener("touchcancel",G,{passive:!0}),t.addEventListener("mousedown",J),window.addEventListener("mousemove",Q),window.addEventListener("mouseup",W)),t.addEventListener("scroll",I,{passive:!0}),typeof ResizeObserver<"u"&&(f=new ResizeObserver(()=>{V()}),f.observe(t),a.value&&f.observe(a.value)),e.nextTick(()=>{V()}))}),e.onBeforeUnmount(()=>{const t=i.value;t&&(t.removeEventListener("touchstart",Z),t.removeEventListener("touchmove",U),t.removeEventListener("touchend",K),t.removeEventListener("touchcancel",G),t.removeEventListener("mousedown",J),t.removeEventListener("scroll",I)),window.removeEventListener("mousemove",Q),window.removeEventListener("mouseup",W),f&&(f.disconnect(),f=null),c!==null&&(cancelAnimationFrame(c),c=null),C!==null&&(clearTimeout(C),C=null),l!==null&&(clearTimeout(l),l=null)}),k({refresh:j,finishRefresh:j,finishPullDown:j,finishLoad:te,finishPullUp:te,checkContentHeight:V}),(t,s)=>(e.openBlock(),e.createElementBlock("div",{ref_key:"containerRef",ref:i,class:"zx-infinite-scroll"},[n.pullRefresh?(e.openBlock(),e.createElementBlock("div",{key:0,class:e.normalizeClass(["zx-pull-refresh-indicator",{"no-transition":m.value}]),style:e.normalizeStyle({transform:`translateY(${u.value}px)`})},[e.withDirectives(e.createElementVNode("div",re,[r.value==="refreshing"?(e.openBlock(),e.createElementBlock("span",se)):r.value==="loosing"?(e.openBlock(),e.createElementBlock("span",ce,e.toDisplayString(n.loosingText),1)):(e.openBlock(),e.createElementBlock("span",fe,e.toDisplayString(n.pullingText),1))],512),[[e.vShow,r.value!=="idle"]])],6)):e.createCommentVNode("",!0),e.createElementVNode("div",{ref_key:"contentRef",ref:a,class:e.normalizeClass(["zx-infinite-scroll-content",{"no-transition":m.value}]),style:e.normalizeStyle(n.pullRefresh?{transform:`translateY(${u.value}px)`}:{})},[n.contentLoading?(e.openBlock(),e.createElementBlock("div",de,[e.createVNode(M,{text:n.refreshingText},null,8,["text"])])):e.createCommentVNode("",!0),n.isEmpty&&!n.loading?(e.openBlock(),e.createBlock(N,{key:1,text:n.emptyText},null,8,["text"])):e.createCommentVNode("",!0),e.renderSlot(t.$slots,"default"),n.isEmpty?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("div",{key:2,ref_key:"indicatorRef",ref:E,class:"zx-load-more-indicator"},[n.loading&&r.value!=="refreshing"?(e.openBlock(),e.createBlock(M,{key:0,text:n.loadingText},null,8,["text"])):n.finished?(e.openBlock(),e.createElementBlock("span",ue,e.toDisplayString(n.finishedText),1)):n.error?(e.openBlock(),e.createElementBlock("span",{key:2,class:"zx-error-text",onClick:ye},e.toDisplayString(n.errorText),1)):e.createCommentVNode("",!0)],512))],6)],512))}}),Y=e.defineComponent({name:"InfiniteScrollV2",__name:"infinite-scroll-v2",props:{fetchFn:{},params:{default:()=>({})},pageSize:{default:10},autoReload:{type:Boolean,default:!0},immediate:{type:Boolean,default:!0},pullRefresh:{type:Boolean,default:!1},pullThreshold:{default:70},loadThreshold:{default:50},emptyText:{default:"暂无数据"},pullingText:{default:"下拉刷新"},loosingText:{default:"释放即可刷新"},refreshingText:{default:"加载中..."},loadingText:{default:"加载中..."},finishedText:{default:"没有更多了~"},errorText:{default:"加载失败,点击重试"}},emits:["success","error","update:list"],setup(n,{expose:k,emit:z}){const o=n,v=z,i=e.ref(null),a=e.ref([]),E=e.ref(!1),r=e.ref(!1),u=e.ref(!1),m=e.ref(!1),y=e.ref(0),w=e.ref(1),x=e.ref(!1);let g=0;async function h(l=!1){l&&(w.value=1,r.value=!1,u.value=!1),E.value=!0,m.value=!1,g+=1;const f=g;try{const d=await o.fetchFn({page:w.value,pageSize:o.pageSize,...o.params});if(f!==g)return;const b=(d==null?void 0:d.records)||[];l?(a.value=b,y.value=0):a.value=[...a.value,...b],y.value=Number((d==null?void 0:d.total)||0),r.value=a.value.length>=y.value,u.value=a.value.length===0,v("success",{records:b,total:y.value,reset:l}),v("update:list",a.value)}catch(d){if(f!==g)return;m.value=!0,v("error",d)}finally{f===g&&(E.value=!1)}}async function R(){var l;x.value=!0,(l=i.value)==null||l.finishRefresh(),await new Promise(f=>{setTimeout(()=>f(),250)}),await h(!0),x.value=!1}async function S(){var l,f;if(r.value){(l=i.value)==null||l.finishLoad();return}a.value.length>0&&(w.value+=1),await h(!1),(f=i.value)==null||f.finishLoad()}function C(){m.value=!1,a.value.length===0?h(!0):S()}async function c(){await h(!0)}return e.watch(()=>o.params,()=>{o.autoReload&&c()},{deep:!0}),e.watch(a,async()=>{var l;await e.nextTick(),(l=i.value)==null||l.checkContentHeight()},{deep:!0}),e.onMounted(()=>{o.immediate&&h(!0)}),k({reload:c,refresh:c,list:a,loading:E,finished:r,isEmpty:u,total:y,finishLoad:()=>{var l;return(l=i.value)==null?void 0:l.finishLoad()},finishRefresh:()=>{var l;return(l=i.value)==null?void 0:l.finishRefresh()},checkContentHeight:()=>{var l;return(l=i.value)==null?void 0:l.checkContentHeight()}}),(l,f)=>(e.openBlock(),e.createBlock($,{ref_key:"scrollRef",ref:i,loading:E.value,finished:r.value,"is-empty":u.value,error:m.value,"pull-refresh":n.pullRefresh,"pull-threshold":n.pullThreshold,"load-threshold":n.loadThreshold,"empty-text":n.emptyText,"pulling-text":n.pullingText,"loosing-text":n.loosingText,"refreshing-text":n.refreshingText,"loading-text":n.loadingText,"finished-text":n.finishedText,"error-text":n.errorText,"content-loading":x.value,onLoad:S,onRefresh:R,onRetry:C},{default:e.withCtx(()=>[e.renderSlot(l.$slots,"default",{list:a.value,loading:E.value,finished:r.value,isEmpty:u.value,error:m.value,total:y.value,reload:c})]),_:3},8,["loading","finished","is-empty","error","pull-refresh","pull-threshold","load-threshold","empty-text","pulling-text","loosing-text","refreshing-text","loading-text","finished-text","error-text","content-loading"]))}});let A=!1;const he=".zx-loading-content{display:flex;align-items:center;justify-content:center;gap:8px;padding:6px 12px;border-radius:16px;color:#1989fa;font-size:14px}.zx-loading-spinner{width:18px;height:18px;border-radius:50%;border:2.5px solid #1989fa;border-top-color:transparent;animation:zx-spin .8s linear infinite}@keyframes zx-spin{to{transform:rotate(360deg)}}.zx-loading-container.fullscreen{position:fixed;top:0;left:0;width:100%;height:100%;display:flex;align-items:center;justify-content:center;background-color:#ffffff80}.zx-empty-wrap{display:flex;flex-direction:column;align-items:center}.zx-empty-image{width:96px;height:80px}.zx-empty-text{margin:12px 0 0;color:#8c8f97;font-size:12px;line-height:22px}.zx-infinite-scroll{height:100%;position:relative;min-height:100%;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;touch-action:pan-y;-webkit-overflow-scrolling:touch}.zx-pull-refresh-indicator{position:absolute;top:-50px;left:0;right:0;height:50px;transition:transform .25s}.zx-pull-refresh-indicator.no-transition{transition:none}.zx-pull-refresh-content{display:flex;align-items:center;justify-content:center;height:100%;font-size:14px;color:#969799}.zx-pull-text{color:#1989fa;font-size:14px}.zx-infinite-scroll-content{position:relative;transition:transform .25s;min-height:100%}.zx-infinite-scroll-content.no-transition{transition:none}.zx-content-loading-mask{position:absolute;top:0;left:0;right:0;bottom:0;background-color:#fffc;display:flex;align-items:center;justify-content:center;z-index:10;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.zx-load-more-indicator{display:flex;align-items:center;justify-content:center;min-height:50px;padding:16px 0;font-size:14px;color:#8c8f97}.zx-finished-text{color:#8c8f97;font-size:14px}.zx-error-text{color:#ee0a24;font-size:14px;cursor:pointer}.zx-error-text:active{opacity:.7}";function pe(){if(A||typeof document>"u")return;const n=document.createElement("style");n.setAttribute("data-source","zx-infinite-scroll"),n.textContent=he,document.head.appendChild(n),A=!0}pe();function O(n){n.component("InfiniteScroll",$),n.component("InfiniteScrollV2",Y),n.component("RequestLoading",M),n.component("EmptyComponent",N)}const me={install:O};p.EmptyComponent=N,p.InfiniteScroll=$,p.InfiniteScrollV2=Y,p.RequestLoading=M,p.default=me,p.install=O,Object.defineProperties(p,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
|
2
2
|
//# sourceMappingURL=index.umd.js.map
|
package/dist/index.umd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.umd.js","sources":["../src/components/request-loading.vue","../src/components/empty-component.vue","../src/components/infinite-scroll.vue","../src/components/infinite-scroll-v2.vue","../src/inject-style.ts","../src/index.ts"],"sourcesContent":["<script setup lang=\"ts\">\ndefineOptions({ name: 'RequestLoading' });\n\nwithDefaults(defineProps<{ text?: string; fullscreen?: boolean }>(), {\n text: '加载中...',\n fullscreen: false\n});\n</script>\n\n<template>\n <div class=\"loading-container\" :class=\"{ fullscreen: fullscreen }\">\n <div class=\"loading-content\">\n <span class=\"loading-spinner\"></span>\n <span class=\"loading-text\">{{ text }}</span>\n </div>\n </div>\n</template>\n\n<style scoped lang=\"scss\">\n.loading-content {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 6px 12px;\n border-radius: 16px;\n color: var(--zx-primary-color, #1e5efd);\n font-size: 14px;\n}\n\n.loading-spinner {\n width: 18px;\n height: 18px;\n border-radius: 50%;\n border: 2.5px solid var(--zx-primary-color, #1e5efd);\n border-top-color: transparent;\n animation: spin 0.8s linear infinite;\n}\n\n@keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n.loading-container.fullscreen {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: rgba(255, 255, 255, 0.5);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue';\n\ndefineOptions({ name: 'EmptyComponent' });\n\ninterface Props {\n text?: string;\n showText?: boolean;\n /** 顶部间距,可传数字(px)或带单位字符串,默认 168px */\n paddingTop?: number | string;\n /** 自定义图片 URL */\n emptyImage?: string;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n text: '暂无数据',\n showText: true,\n paddingTop: 168,\n emptyImage: ''\n});\n\nconst wrapStyle = computed(() => ({\n paddingTop: typeof props.paddingTop === 'number' ? `${props.paddingTop}px` : props.paddingTop\n}));\n\n// 默认空状态 SVG(简洁的空文件夹图标)\nconst defaultEmptySvg = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='80' viewBox='0 0 96 80' fill='none'%3E%3Cpath d='M8 16C8 12.6863 10.6863 10 14 10H38L44 16H82C85.3137 16 88 18.6863 88 22V66C88 69.3137 85.3137 72 82 72H14C10.6863 72 8 69.3137 8 66V16Z' fill='%23E8E8E8'/%3E%3Cpath d='M8 20C8 18.3431 9.34315 17 11 17H85C86.6569 17 88 18.3431 88 20V66C88 69.3137 85.3137 72 82 72H14C10.6863 72 8 69.3137 8 66V20Z' fill='%23F5F5F5'/%3E%3Cpath d='M38 42C38 40.8954 38.8954 40 40 40H56C57.1046 40 58 40.8954 58 42V44C58 45.1046 57.1046 46 56 46H40C38.8954 46 38 45.1046 38 44V42Z' fill='%23D9D9D9'/%3E%3C/svg%3E`;\n\nconst imageSrc = computed(() => props.emptyImage || defaultEmptySvg);\n</script>\n\n<template>\n <div class=\"empty-wrap\" :style=\"wrapStyle\">\n <img class=\"empty-image\" :src=\"imageSrc\" alt=\"\" />\n <p v-if=\"showText\" class=\"empty-text\">\n <slot>{{ text }}</slot>\n </p>\n </div>\n</template>\n\n<style scoped lang=\"scss\">\n.empty-wrap {\n display: flex;\n flex-direction: column;\n align-items: center;\n}\n\n.empty-image {\n width: 96px;\n height: 80px;\n}\n\n.empty-text {\n margin: 12px 0 0;\n color: #8c8f97;\n font-size: 12px;\n line-height: 22px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';\nimport RequestLoading from './request-loading.vue';\nimport EmptyComponent from './empty-component.vue';\n\ndefineOptions({\n name: 'InfiniteScroll'\n});\n\ninterface Props {\n /** 是否处于加载中状态 */\n loading?: boolean;\n /** 是否已加载完所有数据 */\n finished?: boolean;\n /** 是否显示空状态 */\n isEmpty?: boolean;\n /** 空状态文案 */\n emptyText?: string;\n /** 是否启用下拉刷新 */\n pullRefresh?: boolean;\n /** 下拉刷新触发距离(px) */\n pullThreshold?: number;\n /** 上拉加载触发距离(距离底部 px) */\n loadThreshold?: number;\n /** 下拉中的文案 */\n pullingText?: string;\n /** 释放刷新的文案 */\n loosingText?: string;\n /** 刷新中的文案 */\n refreshingText?: string;\n /** 加载中的文案 */\n loadingText?: string;\n /** 加载完成的文案 */\n finishedText?: string;\n /** 加载失败的文案 */\n errorText?: string;\n /** 是否显示错误状态 */\n error?: boolean;\n /** 内容区域是否显示加载状态 */\n contentLoading?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n loading: false,\n finished: false,\n isEmpty: false,\n emptyText: '暂无数据',\n pullRefresh: false,\n pullThreshold: 70,\n loadThreshold: 50,\n pullingText: '下拉刷新',\n loosingText: '释放即可刷新',\n refreshingText: '加载中...',\n loadingText: '加载中...',\n finishedText: '没有更多了~',\n errorText: '加载失败,点击重试',\n error: false,\n contentLoading: false\n});\n\nconst emit = defineEmits<{\n /** 触发下拉刷新 */\n refresh: [];\n /** 触发上拉加载 */\n load: [];\n /** 点击错误重试 */\n retry: [];\n}>();\n\nconst containerRef = ref<HTMLElement | null>(null);\nconst contentRef = ref<HTMLElement | null>(null);\nconst indicatorRef = ref<HTMLElement | null>(null);\n\n// 下拉刷新状态\nconst pullStatus = ref<'idle' | 'pulling' | 'loosing' | 'refreshing'>('idle');\nconst pullDistance = ref(0);\n// 拖动期间禁用 transition,避免 transform 变化时被 0.25s 补间拖慢\nconst isDragging = ref(false);\n\n// 触摸相关\nlet startX = 0;\nlet startY = 0;\nlet lastDistance = 0;\n// 手势方向锁:避免横向滑动被误判为下拉\nlet gestureLock: 'none' | 'vertical' | 'horizontal' = 'none';\nconst SLOPE_LOCK_DISTANCE = 8;\n\nlet isRefreshing = false;\nlet isLoading = false;\n// 鼠标拖拽下拉(PC 端)\nlet isMouseDown = false;\n\n// 节流相关\nlet scrollTimer: number | null = null;\nlet touchMoveRaf: number | null = null;\nlet idleTimer: number | null = null;\n// 监听容器尺寸变化\nlet resizeObserver: ResizeObserver | null = null;\n// 防止空页死循环:snapshot 触发 load 时的 contentHeight,下次完成后没增长就不再 auto-load\nlet lastTriggerContentHeight = -1;\n\n// 下拉刷新核心:按下(与指针类型无关,touch/mouse 共用)\nfunction pullStart(clientX: number, clientY: number) {\n if (!props.pullRefresh || isRefreshing) return;\n startX = clientX;\n startY = clientY;\n lastDistance = 0;\n gestureLock = 'none';\n pullStatus.value = 'idle';\n}\n\n// 下拉刷新核心:移动(rAF 节流,始终使用最新位置)\n// 返回 true 表示消费了本次移动(下拉进行中),调用方据此决定是否 preventDefault\nfunction pullMove(clientX: number, clientY: number, cancelable: boolean): boolean {\n if (!props.pullRefresh || isRefreshing || startY === 0) return false;\n\n const dx = clientX - startX;\n const dy = clientY - startY;\n\n // 方向判定:先到达 8px 锁定的方向才认为有意图,垂直要明显大于水平(1.5x)\n if (gestureLock === 'none') {\n const ax = Math.abs(dx);\n const ay = Math.abs(dy);\n if (ax > SLOPE_LOCK_DISTANCE || ay > SLOPE_LOCK_DISTANCE) {\n gestureLock = ay > ax * 1.5 ? 'vertical' : 'horizontal';\n } else {\n return false;\n }\n }\n if (gestureLock === 'horizontal') return false;\n\n // 子像素容差:≤1 视为已到顶,避免 X5 内核 scrollTop 偶发非整数判断失败\n const atTop = getScrollTop() <= 1;\n\n // iOS 15- 兜底:在 atBottom 且向上拖时阻止整页 rubber-band\n // (iOS 16+ overscroll-behavior 已处理,此处兜底兼容)\n if (!atTop && dy < 0) {\n const c = containerRef.value;\n if (c) {\n const atBottom = c.scrollHeight - c.scrollTop - c.clientHeight <= 1;\n if (atBottom && cancelable) {\n return true;\n }\n }\n return false;\n }\n if (!atTop) return false;\n\n // 反向拖动也要更新(让指示器跟着手指回退),夹紧到 [0, 150]\n const clamped = Math.max(0, Math.min(dy, 150));\n\n lastDistance = clamped;\n isDragging.value = true;\n\n if (touchMoveRaf === null) {\n touchMoveRaf = requestAnimationFrame(() => {\n touchMoveRaf = null;\n pullDistance.value = lastDistance * 0.5;\n if (lastDistance === 0) {\n pullStatus.value = 'idle';\n } else {\n pullStatus.value = lastDistance > props.pullThreshold ? 'loosing' : 'pulling';\n }\n });\n }\n\n // 仅在向下拖时消费事件(避免误吞用户向上滚动的意图)\n return dy > 0;\n}\n\n// 下拉刷新核心:松开\nfunction pullEnd() {\n if (!props.pullRefresh || isRefreshing) return;\n\n // 取消待执行的 rAF,避免松手后还有一帧异步更新把 UI 拉回 loosing 卡住\n if (touchMoveRaf !== null) {\n cancelAnimationFrame(touchMoveRaf);\n touchMoveRaf = null;\n }\n\n isDragging.value = false;\n\n // 用最新的 lastDistance 同步判定,不依赖可能尚未生效的 pullStatus\n if (lastDistance > props.pullThreshold) {\n isRefreshing = true;\n pullStatus.value = 'refreshing';\n pullDistance.value = 50;\n emit('refresh');\n } else {\n pullDistance.value = 0;\n pullStatus.value = 'idle';\n }\n\n resetTouch();\n}\n\n// 下拉刷新核心:取消(回到 idle)\nfunction pullCancel() {\n if (!props.pullRefresh || isRefreshing) return;\n\n if (touchMoveRaf !== null) {\n cancelAnimationFrame(touchMoveRaf);\n touchMoveRaf = null;\n }\n\n isDragging.value = false;\n pullDistance.value = 0;\n pullStatus.value = 'idle';\n resetTouch();\n}\n\n// 下拉刷新 - touchstart\nfunction handleTouchStart(e: TouchEvent) {\n pullStart(e.touches[0].clientX, e.touches[0].clientY);\n}\n\n// 下拉刷新 - touchmove\nfunction handleTouchMove(e: TouchEvent) {\n const touch = e.touches[0];\n if (pullMove(touch.clientX, touch.clientY, e.cancelable) && e.cancelable) {\n e.preventDefault();\n }\n}\n\n// 下拉刷新 - touchend\nfunction handleTouchEnd() {\n pullEnd();\n}\n\n// 下拉刷新 - touchcancel(微信 X5 内核会在系统手势介入时派发 cancel 而非 end)\nfunction handleTouchCancel() {\n pullCancel();\n}\n\n// 下拉刷新 - mousedown(PC 端鼠标拖拽)\nfunction handleMouseDown(e: MouseEvent) {\n // 仅左键触发\n if (!props.pullRefresh || isRefreshing || e.button !== 0) return;\n isMouseDown = true;\n pullStart(e.clientX, e.clientY);\n}\n\n// 下拉刷新 - mousemove(挂在 window 上,允许拖出容器)\nfunction handleMouseMove(e: MouseEvent) {\n if (!isMouseDown) return;\n if (pullMove(e.clientX, e.clientY, true)) {\n // 阻止拖拽时选中文本\n e.preventDefault();\n }\n}\n\n// 下拉刷新 - mouseup\nfunction handleMouseUp() {\n if (!isMouseDown) return;\n isMouseDown = false;\n pullEnd();\n}\n\nfunction resetTouch() {\n startX = 0;\n startY = 0;\n lastDistance = 0;\n gestureLock = 'none';\n}\n\n// 上拉加载 - scroll(节流处理)\nfunction handleScroll() {\n if (props.loading || props.finished || props.error || isLoading) return;\n if (scrollTimer) return;\n\n scrollTimer = window.setTimeout(() => {\n scrollTimer = null;\n const container = containerRef.value;\n if (!container) return;\n\n const { scrollTop, clientHeight, scrollHeight } = container;\n const contentHeight = contentRef.value?.clientHeight || 0;\n\n // 情况1:内容不足一屏,自动触发加载\n if (contentHeight <= clientHeight) {\n // 死循环防护:上一次 auto-load 后内容没增长,不再继续触发\n if (lastTriggerContentHeight === contentHeight) return;\n lastTriggerContentHeight = contentHeight;\n // 不足一屏时主动滚到底,让 loading 指示器进入视口\n triggerLoad(true);\n return;\n }\n\n // 情况2:滚动到距离底部阈值范围内\n if (scrollHeight - scrollTop - clientHeight <= props.loadThreshold) {\n // 用户已经手动滚到底,不再强制 scrollTop(避免打断惯性滚动)\n triggerLoad(false);\n }\n }, 100);\n}\n\nfunction triggerLoad(scrollToBottom = false) {\n isLoading = true;\n emit('load');\n nextTick(() => {\n const c = containerRef.value;\n if (!c) return;\n if (scrollToBottom) {\n // 内容不足一屏:直接滚到底(没有\"惯性\"可以打断)\n c.scrollTop = c.scrollHeight;\n } else {\n // 阈值触发:用 scrollIntoView 平滑进入视口,避免突兀打断;\n // 旧 WebKit 不识别 options 会降级为即时滚动,效果等价\n indicatorRef.value?.scrollIntoView({ block: 'end', behavior: 'smooth' });\n }\n });\n}\n\n// 获取滚动距离\nfunction getScrollTop(): number {\n return containerRef.value?.scrollTop ?? 0;\n}\n\n// 完成下拉刷新\nfunction finishRefresh() {\n isRefreshing = false;\n pullDistance.value = 0;\n // 让 transform snap-back 完成后再切 idle,避免 spinner 在动画中途突然消失\n if (idleTimer !== null) clearTimeout(idleTimer);\n idleTimer = window.setTimeout(() => {\n pullStatus.value = 'idle';\n idleTimer = null;\n }, 250);\n // 重置死循环计数\n lastTriggerContentHeight = -1;\n nextTick(() => {\n checkContentHeight();\n });\n}\n\n// 完成上拉加载\nfunction finishLoad() {\n isLoading = false;\n nextTick(() => {\n const contentHeight = contentRef.value?.clientHeight || 0;\n // 内容没增长就不再触发 auto-load,避免空页死循环\n if (lastTriggerContentHeight !== -1 && contentHeight === lastTriggerContentHeight) return;\n lastTriggerContentHeight = -1;\n checkContentHeight();\n });\n}\n\n// 检查内容高度,不足一屏时自动触发加载\nfunction checkContentHeight() {\n if (props.finished || props.loading || props.error) return;\n\n nextTick(() => {\n const contentHeight = contentRef.value?.clientHeight || 0;\n const clientHeight = containerRef.value?.clientHeight || 0;\n\n if (contentHeight <= clientHeight && !isLoading) {\n handleScroll();\n }\n });\n}\n\n// 点击错误重试 — 由父组件决定具体行为,避免 retry + load 双触发\nfunction handleRetry() {\n emit('retry');\n}\n\n// loading 完成时同步重置 isLoading\nwatch(\n () => props.loading,\n newVal => {\n if (!newVal) {\n isLoading = false;\n }\n }\n);\n\n// finished 翻为 true 时重置死循环计数\nwatch(\n () => props.finished,\n newVal => {\n if (newVal) {\n lastTriggerContentHeight = -1;\n }\n }\n);\n\nonMounted(() => {\n const container = containerRef.value;\n if (!container) return;\n\n if (props.pullRefresh) {\n container.addEventListener('touchstart', handleTouchStart, { passive: true });\n container.addEventListener('touchmove', handleTouchMove, { passive: false });\n container.addEventListener('touchend', handleTouchEnd, { passive: true });\n container.addEventListener('touchcancel', handleTouchCancel, { passive: true });\n // 鼠标拖拽(PC):move/up 挂在 window 上,允许拖出容器边界\n container.addEventListener('mousedown', handleMouseDown);\n window.addEventListener('mousemove', handleMouseMove);\n window.addEventListener('mouseup', handleMouseUp);\n }\n\n container.addEventListener('scroll', handleScroll, { passive: true });\n\n // 监听容器/内容尺寸变化(旋转、WebView resize、软键盘弹出收起等)\n if (typeof ResizeObserver !== 'undefined') {\n resizeObserver = new ResizeObserver(() => {\n checkContentHeight();\n });\n resizeObserver.observe(container);\n if (contentRef.value) resizeObserver.observe(contentRef.value);\n }\n\n // 初始检查内容高度\n nextTick(() => {\n checkContentHeight();\n });\n});\n\nonBeforeUnmount(() => {\n const container = containerRef.value;\n if (container) {\n container.removeEventListener('touchstart', handleTouchStart);\n container.removeEventListener('touchmove', handleTouchMove);\n container.removeEventListener('touchend', handleTouchEnd);\n container.removeEventListener('touchcancel', handleTouchCancel);\n container.removeEventListener('mousedown', handleMouseDown);\n container.removeEventListener('scroll', handleScroll);\n }\n window.removeEventListener('mousemove', handleMouseMove);\n window.removeEventListener('mouseup', handleMouseUp);\n if (resizeObserver) {\n resizeObserver.disconnect();\n resizeObserver = null;\n }\n if (touchMoveRaf !== null) {\n cancelAnimationFrame(touchMoveRaf);\n touchMoveRaf = null;\n }\n if (scrollTimer !== null) {\n clearTimeout(scrollTimer);\n scrollTimer = null;\n }\n if (idleTimer !== null) {\n clearTimeout(idleTimer);\n idleTimer = null;\n }\n});\n\ndefineExpose({\n refresh: finishRefresh,\n finishRefresh,\n finishPullDown: finishRefresh,\n finishLoad,\n finishPullUp: finishLoad,\n checkContentHeight\n});\n</script>\n\n<template>\n <div ref=\"containerRef\" class=\"infinite-scroll\">\n <!-- 下拉刷新指示器 -->\n <div\n v-if=\"pullRefresh\"\n class=\"pull-refresh-indicator\"\n :class=\"{ 'no-transition': isDragging }\"\n :style=\"{ transform: `translateY(${pullDistance}px)` }\"\n >\n <div v-show=\"pullStatus !== 'idle'\" class=\"pull-refresh-content\">\n <!-- <RequestLoading v-if=\"pullStatus === 'refreshing'\" text=\"\" /> -->\n <span v-if=\"pullStatus === 'refreshing'\"></span>\n <span v-else-if=\"pullStatus === 'loosing'\" class=\"pull-text\">{{ loosingText }}</span>\n <span v-else class=\"pull-text\">{{ pullingText }}</span>\n </div>\n </div>\n\n <!-- 内容区域 -->\n <div\n ref=\"contentRef\"\n class=\"infinite-scroll-content\"\n :class=\"{ 'no-transition': isDragging }\"\n :style=\"pullRefresh ? { transform: `translateY(${pullDistance}px)` } : {}\"\n >\n <!-- 内容区域加载遮罩 -->\n <div v-if=\"contentLoading\" class=\"content-loading-mask\">\n <RequestLoading :text=\"refreshingText\" />\n </div>\n\n <!-- 空状态 -->\n <EmptyComponent v-if=\"isEmpty && !loading\" :text=\"emptyText\" />\n\n <!-- 默认插槽 -->\n <slot></slot>\n\n <!-- 上拉加载指示器 -->\n <div v-if=\"!isEmpty\" ref=\"indicatorRef\" class=\"load-more-indicator\">\n <RequestLoading v-if=\"loading && pullStatus !== 'refreshing'\" :text=\"loadingText\" />\n <span v-else-if=\"finished\" class=\"finished-text\">{{ finishedText }}</span>\n <span v-else-if=\"error\" class=\"error-text\" @click=\"handleRetry\">{{ errorText }}</span>\n </div>\n </div>\n </div>\n</template>\n\n<style scoped lang=\"scss\">\n.infinite-scroll {\n height: 100%;\n position: relative;\n min-height: 100%;\n overflow-y: auto;\n overflow-x: hidden;\n // 阻止滚动到边界时把滚动链传递给祖先(修复 iOS 16+ / 微信 X5 下拉变成整页拖拽)\n overscroll-behavior: contain;\n // 显式声明只接受纵向手势,避免被外层 touch-action 影响\n touch-action: pan-y;\n // iOS 下让原生回弹有惯性\n -webkit-overflow-scrolling: touch;\n}\n\n.pull-refresh-indicator {\n position: absolute;\n top: -50px;\n left: 0;\n right: 0;\n height: 50px;\n transition: transform 0.25s;\n\n // 拖动期间禁用过渡,让 transform 1:1 跟随手指\n &.no-transition {\n transition: none;\n }\n}\n\n.pull-refresh-content {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n font-size: 14px;\n color: #969799;\n}\n\n.pull-text {\n color: var(--zx-primary-color, #1989fa);\n font-size: 14px;\n}\n\n.infinite-scroll-content {\n position: relative;\n transition: transform 0.25s;\n min-height: 100%;\n\n &.no-transition {\n transition: none;\n }\n}\n\n.content-loading-mask {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(255, 255, 255, 0.8);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n backdrop-filter: blur(2px);\n}\n\n.load-more-indicator {\n display: flex;\n align-items: center;\n justify-content: center;\n // 预留高度避免 loading 渲染后被推到视口外造成遮挡\n min-height: 50px;\n padding: 16px 0;\n font-size: 14px;\n color: #8c8f97;\n}\n\n.finished-text {\n color: #8c8f97;\n font-size: 14px;\n}\n\n.error-text {\n color: #ee0a24;\n font-size: 14px;\n cursor: pointer;\n\n &:active {\n opacity: 0.7;\n }\n}\n</style>\n","<script setup lang=\"ts\" generic=\"T\">\nimport { type Ref, nextTick, onMounted, ref, watch } from 'vue';\nimport InfiniteScroll from './infinite-scroll.vue';\n\ndefineOptions({\n name: 'InfiniteScrollV2'\n});\n\nconst props = withDefaults(\n defineProps<{\n /** 数据请求函数,需返回 { records, total },失败时抛错 */\n fetchFn: (params: {\n page: number;\n pageSize: number;\n [key: string]: any;\n }) => Promise<{\n records: T[];\n total: number;\n }>;\n /** 业务查询参数(如筛选条件),变化时自动 reset 重新加载 */\n params?: Record<string, any>;\n /** 每页条数 */\n pageSize?: number;\n /** params 变化时是否自动重新加载 */\n autoReload?: boolean;\n /** 是否在挂载时自动加载首页 */\n immediate?: boolean;\n /** 是否启用下拉刷新 */\n pullRefresh?: boolean;\n /** 下拉刷新触发距离(px) */\n pullThreshold?: number;\n /** 上拉加载触发距离(距离底部 px) */\n loadThreshold?: number;\n /** 空状态文案 */\n emptyText?: string;\n /** 下拉中的文案 */\n pullingText?: string;\n /** 释放刷新的文案 */\n loosingText?: string;\n /** 刷新中的文案 */\n refreshingText?: string;\n /** 加载中的文案 */\n loadingText?: string;\n /** 加载完成的文案 */\n finishedText?: string;\n /** 加载失败的文案 */\n errorText?: string;\n }>(),\n {\n params: () => ({}),\n pageSize: 10,\n autoReload: true,\n immediate: true,\n pullRefresh: false,\n pullThreshold: 70,\n loadThreshold: 50,\n emptyText: '暂无数据',\n pullingText: '下拉刷新',\n loosingText: '释放即可刷新',\n refreshingText: '加载中...',\n loadingText: '加载中...',\n finishedText: '没有更多了~',\n errorText: '加载失败,点击重试'\n});\n\nconst emit = defineEmits<{\n /** 加载成功,每次成功(含分页)都会触发 */\n success: [payload: { records: T[]; total: number; reset: boolean }];\n /** 加载失败 */\n error: [err: unknown];\n /** 列表内容变化(v-model:list 支持) */\n 'update:list': [list: T[]];\n}>();\n\nconst scrollRef = ref<InstanceType<typeof InfiniteScroll> | null>(null);\n\nconst list = ref<T[]>([]) as Ref<T[]>;\nconst loading = ref(false);\nconst finished = ref(false);\nconst isEmpty = ref(false);\nconst loadError = ref(false);\nconst total = ref(0);\nconst page = ref(1);\nconst isRefreshing = ref(false);\n\nlet requestSeq = 0;\n\nasync function load(reset = false) {\n if (reset) {\n page.value = 1;\n finished.value = false;\n isEmpty.value = false;\n // 不立即清空 list,保留旧数据直到新数据到达\n }\n loading.value = true;\n loadError.value = false;\n\n requestSeq += 1;\n const seq = requestSeq;\n\n try {\n const result = await props.fetchFn({\n page: page.value,\n pageSize: props.pageSize,\n ...props.params\n });\n\n if (seq !== requestSeq) return;\n\n const records = result?.records || [];\n if (reset) {\n // reset 时替换整个列表\n list.value = records;\n total.value = 0;\n } else {\n list.value = [...list.value, ...records];\n }\n total.value = Number(result?.total || 0);\n finished.value = list.value.length >= total.value;\n isEmpty.value = list.value.length === 0;\n\n emit('success', { records, total: total.value, reset });\n emit('update:list', list.value);\n } catch (err) {\n if (seq !== requestSeq) return;\n loadError.value = true;\n emit('error', err);\n } finally {\n if (seq === requestSeq) {\n loading.value = false;\n }\n }\n}\n\nasync function handleRefresh() {\n isRefreshing.value = true;\n // 先让下拉刷新指示器收回\n scrollRef.value?.finishRefresh();\n // 等待 250ms 让指示器完全收回(对应 infinite-scroll.vue 的 transition 时长)\n await new Promise<void>(resolve => {\n setTimeout(() => resolve(), 250);\n });\n // 指示器收回后再开始请求新数据\n await load(true);\n isRefreshing.value = false;\n}\n\nasync function handleLoad() {\n if (finished.value) {\n scrollRef.value?.finishLoad();\n return;\n }\n // 列表为空时是首次加载(page 已经是 1),其它情况 page++\n if (list.value.length > 0) {\n page.value += 1;\n }\n await load(false);\n scrollRef.value?.finishLoad();\n}\n\nfunction handleRetry() {\n loadError.value = false;\n if (list.value.length === 0) {\n load(true);\n } else {\n handleLoad();\n }\n}\n\nasync function reload() {\n await load(true);\n}\n\nwatch(\n () => props.params,\n () => {\n if (props.autoReload) {\n reload();\n }\n },\n { deep: true }\n);\n\nwatch(\n list,\n async () => {\n await nextTick();\n scrollRef.value?.checkContentHeight();\n },\n { deep: true }\n);\n\nonMounted(() => {\n if (props.immediate) {\n load(true);\n }\n});\n\ndefineExpose({\n /** 重新加载第一页 */\n reload,\n /** 兼容别名 */\n refresh: reload,\n /** 当前列表数据 */\n list,\n /** 加载中状态 */\n loading,\n /** 是否已加载完 */\n finished,\n /** 是否空数据 */\n isEmpty,\n /** 总条数 */\n total,\n /** 透传 v1 方法 */\n finishLoad: () => scrollRef.value?.finishLoad(),\n finishRefresh: () => scrollRef.value?.finishRefresh(),\n checkContentHeight: () => scrollRef.value?.checkContentHeight()\n});\n</script>\n\n<template>\n <InfiniteScroll\n ref=\"scrollRef\"\n :loading=\"loading\"\n :finished=\"finished\"\n :is-empty=\"isEmpty\"\n :error=\"loadError\"\n :pull-refresh=\"pullRefresh\"\n :pull-threshold=\"pullThreshold\"\n :load-threshold=\"loadThreshold\"\n :empty-text=\"emptyText\"\n :pulling-text=\"pullingText\"\n :loosing-text=\"loosingText\"\n :refreshing-text=\"refreshingText\"\n :loading-text=\"loadingText\"\n :finished-text=\"finishedText\"\n :error-text=\"errorText\"\n :content-loading=\"isRefreshing\"\n @load=\"handleLoad\"\n @refresh=\"handleRefresh\"\n @retry=\"handleRetry\"\n >\n <slot\n :list=\"list\"\n :loading=\"loading\"\n :finished=\"finished\"\n :is-empty=\"isEmpty\"\n :error=\"loadError\"\n :total=\"total\"\n :reload=\"reload\"\n />\n </InfiniteScroll>\n</template>\n","// 样式自动注入\nlet styleInjected = false;\n\nconst cssContent = `.loading-content[data-v-d89150ba]{display:flex;align-items:center;justify-content:center;gap:8px;padding:6px 12px;border-radius:16px;color:var(--zx-primary-color, #1e5efd);font-size:14px}.loading-spinner[data-v-d89150ba]{width:18px;height:18px;border-radius:50%;border:2.5px solid var(--zx-primary-color, #1e5efd);border-top-color:transparent;animation:spin-d89150ba .8s linear infinite}@keyframes spin-d89150ba{to{transform:rotate(360deg)}}.loading-container.fullscreen[data-v-d89150ba]{position:fixed;top:0;left:0;width:100%;height:100%;display:flex;align-items:center;justify-content:center;background-color:#ffffff80}.empty-wrap[data-v-3c452901]{display:flex;flex-direction:column;align-items:center}.empty-image[data-v-3c452901]{width:96px;height:80px}.empty-text[data-v-3c452901]{margin:12px 0 0;color:#8c8f97;font-size:12px;line-height:22px}.infinite-scroll[data-v-bf5ca1d9]{height:100%;position:relative;min-height:100%;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;touch-action:pan-y;-webkit-overflow-scrolling:touch}.pull-refresh-indicator[data-v-bf5ca1d9]{position:absolute;top:-50px;left:0;right:0;height:50px;transition:transform .25s}.pull-refresh-indicator.no-transition[data-v-bf5ca1d9]{transition:none}.pull-refresh-content[data-v-bf5ca1d9]{display:flex;align-items:center;justify-content:center;height:100%;font-size:14px;color:#969799}.pull-text[data-v-bf5ca1d9]{color:var(--zx-primary-color, #1989fa);font-size:14px}.infinite-scroll-content[data-v-bf5ca1d9]{position:relative;transition:transform .25s;min-height:100%}.infinite-scroll-content.no-transition[data-v-bf5ca1d9]{transition:none}.content-loading-mask[data-v-bf5ca1d9]{position:absolute;top:0;left:0;right:0;bottom:0;background-color:#fffc;display:flex;align-items:center;justify-content:center;z-index:10;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.load-more-indicator[data-v-bf5ca1d9]{display:flex;align-items:center;justify-content:center;min-height:50px;padding:16px 0;font-size:14px;color:#8c8f97}.finished-text[data-v-bf5ca1d9]{color:#8c8f97;font-size:14px}.error-text[data-v-bf5ca1d9]{color:#ee0a24;font-size:14px;cursor:pointer}.error-text[data-v-bf5ca1d9]:active{opacity:.7}`;\n\nexport function injectStyle() {\n if (styleInjected || typeof document === 'undefined') return;\n\n const style = document.createElement('style');\n style.setAttribute('data-source', 'zx-infinite-scroll');\n style.textContent = cssContent;\n document.head.appendChild(style);\n\n styleInjected = true;\n}\n","import { App } from 'vue';\nimport InfiniteScroll from './components/infinite-scroll.vue';\nimport InfiniteScrollV2 from './components/infinite-scroll-v2.vue';\nimport RequestLoading from './components/request-loading.vue';\nimport EmptyComponent from './components/empty-component.vue';\nimport { injectStyle } from './inject-style';\n\n// 自动注入样式(只要导入了这个包就会执行)\ninjectStyle();\n\n// 单独导出组件\nexport { InfiniteScroll, InfiniteScrollV2, RequestLoading, EmptyComponent };\n\n// Vue plugin install 函数\nexport function install(app: App) {\n app.component('InfiniteScroll', InfiniteScroll);\n app.component('InfiniteScrollV2', InfiniteScrollV2);\n app.component('RequestLoading', RequestLoading);\n app.component('EmptyComponent', EmptyComponent);\n}\n\n// 默认导出(支持 app.use() 安装)\nexport default {\n install\n};\n"],"names":["_createElementBlock","_normalizeClass","__props","_createElementVNode","_hoisted_1","_hoisted_2","_toDisplayString","defaultEmptySvg","props","wrapStyle","computed","imageSrc","_openBlock","_renderSlot","_ctx","SLOPE_LOCK_DISTANCE","emit","__emit","containerRef","ref","contentRef","indicatorRef","pullStatus","pullDistance","isDragging","startX","startY","lastDistance","gestureLock","isRefreshing","isLoading","isMouseDown","scrollTimer","touchMoveRaf","idleTimer","resizeObserver","lastTriggerContentHeight","pullStart","clientX","clientY","pullMove","cancelable","dx","dy","ax","ay","atTop","getScrollTop","c","pullEnd","resetTouch","pullCancel","handleTouchStart","e","handleTouchMove","touch","handleTouchEnd","handleTouchCancel","handleMouseDown","handleMouseMove","handleMouseUp","handleScroll","container","scrollTop","clientHeight","scrollHeight","contentHeight","_a","triggerLoad","scrollToBottom","nextTick","finishRefresh","checkContentHeight","finishLoad","_b","handleRetry","watch","newVal","onMounted","onBeforeUnmount","__expose","_withDirectives","_hoisted_3","_hoisted_4","_normalizeStyle","_hoisted_5","_createVNode","RequestLoading","_createBlock","EmptyComponent","_hoisted_6","scrollRef","list","loading","finished","isEmpty","loadError","total","page","requestSeq","load","reset","seq","result","records","err","handleRefresh","resolve","handleLoad","reload","InfiniteScroll","styleInjected","cssContent","injectStyle","style","install","app","InfiniteScrollV2","index"],"mappings":"ifAUEA,EAAAA,mBAKM,MAAA,CALD,MAAKC,EAAAA,eAAA,CAAC,oBAAmB,CAAA,WAAuBC,EAAA,WAAU,CAAA,CAAA,GAC7DC,EAAAA,mBAGM,MAHNC,GAGM,aAFJD,EAAAA,mBAAqC,OAAA,CAA/B,MAAM,iBAAA,EAAiB,KAAA,EAAA,GAC7BA,EAAAA,mBAA4C,OAA5CE,GAA4CC,EAAAA,gBAAdJ,EAAA,IAAI,EAAA,CAAA,CAAA,mKCalCK,GAAkB,izBAZxB,MAAMC,EAAQN,EAORO,EAAYC,EAAAA,SAAS,KAAO,CAChC,WAAY,OAAOF,EAAM,YAAe,SAAW,GAAGA,EAAM,UAAU,KAAOA,EAAM,UAAA,EACnF,EAKIG,EAAWD,EAAAA,SAAS,IAAMF,EAAM,YAAcD,EAAe,8BAIjEP,EAAAA,mBAKM,MAAA,CALD,MAAM,aAAc,uBAAOS,EAAA,KAAS,CAAA,GACvCN,EAAAA,mBAAkD,MAAA,CAA7C,MAAM,cAAe,IAAKQ,EAAA,MAAU,IAAI,EAAA,aACpCT,EAAA,UAATU,EAAAA,UAAA,EAAAZ,EAAAA,mBAEI,IAFJK,GAEI,CADFQ,EAAAA,WAAuBC,sBAAvB,IAAuB,qCAAdZ,EAAA,IAAI,EAAA,CAAA,CAAA,gQCkDba,EAAsB,gnBA3C5B,MAAMP,EAAQN,EAkBRc,EAAOC,EASPC,EAAeC,EAAAA,IAAwB,IAAI,EAC3CC,EAAaD,EAAAA,IAAwB,IAAI,EACzCE,EAAeF,EAAAA,IAAwB,IAAI,EAG3CG,EAAaH,EAAAA,IAAmD,MAAM,EACtEI,EAAeJ,EAAAA,IAAI,CAAC,EAEpBK,EAAaL,EAAAA,IAAI,EAAK,EAG5B,IAAIM,EAAS,EACTC,EAAS,EACTC,EAAe,EAEfC,EAAkD,OAGlDC,EAAe,GACfC,EAAY,GAEZC,EAAc,GAGdC,EAA6B,KAC7BC,EAA8B,KAC9BC,EAA2B,KAE3BC,EAAwC,KAExCC,EAA2B,GAG/B,SAASC,EAAUC,EAAiBC,EAAiB,CAC/C,CAAC/B,EAAM,aAAeqB,IAC1BJ,EAASa,EACTZ,EAASa,EACTZ,EAAe,EACfC,EAAc,OACdN,EAAW,MAAQ,OACrB,CAIA,SAASkB,EAASF,EAAiBC,EAAiBE,EAA8B,CAChF,GAAI,CAACjC,EAAM,aAAeqB,GAAgBH,IAAW,EAAG,MAAO,GAE/D,MAAMgB,EAAKJ,EAAUb,EACfkB,EAAKJ,EAAUb,EAGrB,GAAIE,IAAgB,OAAQ,CAC1B,MAAMgB,EAAK,KAAK,IAAIF,CAAE,EAChBG,EAAK,KAAK,IAAIF,CAAE,EACtB,GAAIC,EAAK7B,GAAuB8B,EAAK9B,EACnCa,EAAciB,EAAKD,EAAK,IAAM,WAAa,iBAE3C,OAAO,EAEX,CACA,GAAIhB,IAAgB,aAAc,MAAO,GAGzC,MAAMkB,EAAQC,MAAkB,EAIhC,GAAI,CAACD,GAASH,EAAK,EAAG,CACpB,MAAMK,EAAI9B,EAAa,MACvB,MAAI,GAAA8B,GACeA,EAAE,aAAeA,EAAE,UAAYA,EAAE,cAAgB,GAClDP,EAKpB,CACA,OAAKK,GAKLnB,EAFgB,KAAK,IAAI,EAAG,KAAK,IAAIgB,EAAI,GAAG,CAAC,EAG7CnB,EAAW,MAAQ,GAEfS,IAAiB,OACnBA,EAAe,sBAAsB,IAAM,CACzCA,EAAe,KACfV,EAAa,MAAQI,EAAe,GAChCA,IAAiB,EACnBL,EAAW,MAAQ,OAEnBA,EAAW,MAAQK,EAAenB,EAAM,cAAgB,UAAY,SAExE,CAAC,GAIImC,EAAK,GArBO,EAsBrB,CAGA,SAASM,GAAU,CACb,CAACzC,EAAM,aAAeqB,IAGtBI,IAAiB,OACnB,qBAAqBA,CAAY,EACjCA,EAAe,MAGjBT,EAAW,MAAQ,GAGfG,EAAenB,EAAM,eACvBqB,EAAe,GACfP,EAAW,MAAQ,aACnBC,EAAa,MAAQ,GACrBP,EAAK,SAAS,IAEdO,EAAa,MAAQ,EACrBD,EAAW,MAAQ,QAGrB4B,GAAA,EACF,CAGA,SAASC,IAAa,CAChB,CAAC3C,EAAM,aAAeqB,IAEtBI,IAAiB,OACnB,qBAAqBA,CAAY,EACjCA,EAAe,MAGjBT,EAAW,MAAQ,GACnBD,EAAa,MAAQ,EACrBD,EAAW,MAAQ,OACnB4B,GAAA,EACF,CAGA,SAASE,EAAiBC,EAAe,CACvChB,EAAUgB,EAAE,QAAQ,CAAC,EAAE,QAASA,EAAE,QAAQ,CAAC,EAAE,OAAO,CACtD,CAGA,SAASC,EAAgBD,EAAe,CACtC,MAAME,EAAQF,EAAE,QAAQ,CAAC,EACrBb,EAASe,EAAM,QAASA,EAAM,QAASF,EAAE,UAAU,GAAKA,EAAE,YAC5DA,EAAE,eAAA,CAEN,CAGA,SAASG,GAAiB,CACxBP,EAAA,CACF,CAGA,SAASQ,GAAoB,CAC3BN,GAAA,CACF,CAGA,SAASO,EAAgBL,EAAe,CAElC,CAAC7C,EAAM,aAAeqB,GAAgBwB,EAAE,SAAW,IACvDtB,EAAc,GACdM,EAAUgB,EAAE,QAASA,EAAE,OAAO,EAChC,CAGA,SAASM,EAAgBN,EAAe,CACjCtB,GACDS,EAASa,EAAE,QAASA,EAAE,QAAS,EAAI,GAErCA,EAAE,eAAA,CAEN,CAGA,SAASO,GAAgB,CAClB7B,IACLA,EAAc,GACdkB,EAAA,EACF,CAEA,SAASC,IAAa,CACpBzB,EAAS,EACTC,EAAS,EACTC,EAAe,EACfC,EAAc,MAChB,CAGA,SAASiC,GAAe,CAClBrD,EAAM,SAAWA,EAAM,UAAYA,EAAM,OAASsB,GAClDE,IAEJA,EAAc,OAAO,WAAW,IAAM,OACpCA,EAAc,KACd,MAAM8B,EAAY5C,EAAa,MAC/B,GAAI,CAAC4C,EAAW,OAEhB,KAAM,CAAE,UAAAC,EAAW,aAAAC,EAAc,aAAAC,CAAA,EAAiBH,EAC5CI,IAAgBC,EAAA/C,EAAW,QAAX,YAAA+C,EAAkB,eAAgB,EAGxD,GAAID,GAAiBF,EAAc,CAEjC,GAAI5B,IAA6B8B,EAAe,OAChD9B,EAA2B8B,EAE3BE,GAAY,EAAI,EAChB,MACF,CAGIH,EAAeF,EAAYC,GAAgBxD,EAAM,eAEnD4D,GAAY,EAAK,CAErB,EAAG,GAAG,EACR,CAEA,SAASA,GAAYC,EAAiB,GAAO,CAC3CvC,EAAY,GACZd,EAAK,MAAM,EACXsD,EAAAA,SAAS,IAAM,OACb,MAAMtB,EAAI9B,EAAa,MAClB8B,IACDqB,EAEFrB,EAAE,UAAYA,EAAE,cAIhBmB,EAAA9C,EAAa,QAAb,MAAA8C,EAAoB,eAAe,CAAE,MAAO,MAAO,SAAU,WAEjE,CAAC,CACH,CAGA,SAASpB,IAAuB,OAC9B,QAAOoB,EAAAjD,EAAa,QAAb,YAAAiD,EAAoB,YAAa,CAC1C,CAGA,SAASI,GAAgB,CACvB1C,EAAe,GACfN,EAAa,MAAQ,EAEjBW,IAAc,MAAM,aAAaA,CAAS,EAC9CA,EAAY,OAAO,WAAW,IAAM,CAClCZ,EAAW,MAAQ,OACnBY,EAAY,IACd,EAAG,GAAG,EAENE,EAA2B,GAC3BkC,EAAAA,SAAS,IAAM,CACbE,EAAA,CACF,CAAC,CACH,CAGA,SAASC,IAAa,CACpB3C,EAAY,GACZwC,EAAAA,SAAS,IAAM,OACb,MAAMJ,IAAgBC,EAAA/C,EAAW,QAAX,YAAA+C,EAAkB,eAAgB,EAEpD/B,IAA6B,IAAM8B,IAAkB9B,IACzDA,EAA2B,GAC3BoC,EAAA,EACF,CAAC,CACH,CAGA,SAASA,GAAqB,CACxBhE,EAAM,UAAYA,EAAM,SAAWA,EAAM,OAE7C8D,EAAAA,SAAS,IAAM,SACb,MAAMJ,IAAgBC,EAAA/C,EAAW,QAAX,YAAA+C,EAAkB,eAAgB,EAClDH,IAAeU,EAAAxD,EAAa,QAAb,YAAAwD,EAAoB,eAAgB,EAErDR,GAAiBF,GAAgB,CAAClC,GACpC+B,EAAA,CAEJ,CAAC,CACH,CAGA,SAASc,IAAc,CACrB3D,EAAK,OAAO,CACd,CAGA4D,OAAAA,EAAAA,MACE,IAAMpE,EAAM,QACZqE,GAAU,CACHA,IACH/C,EAAY,GAEhB,CAAA,EAIF8C,EAAAA,MACE,IAAMpE,EAAM,SACZqE,GAAU,CACJA,IACFzC,EAA2B,GAE/B,CAAA,EAGF0C,EAAAA,UAAU,IAAM,CACd,MAAMhB,EAAY5C,EAAa,MAC1B4C,IAEDtD,EAAM,cACRsD,EAAU,iBAAiB,aAAcV,EAAkB,CAAE,QAAS,GAAM,EAC5EU,EAAU,iBAAiB,YAAaR,EAAiB,CAAE,QAAS,GAAO,EAC3EQ,EAAU,iBAAiB,WAAYN,EAAgB,CAAE,QAAS,GAAM,EACxEM,EAAU,iBAAiB,cAAeL,EAAmB,CAAE,QAAS,GAAM,EAE9EK,EAAU,iBAAiB,YAAaJ,CAAe,EACvD,OAAO,iBAAiB,YAAaC,CAAe,EACpD,OAAO,iBAAiB,UAAWC,CAAa,GAGlDE,EAAU,iBAAiB,SAAUD,EAAc,CAAE,QAAS,GAAM,EAGhE,OAAO,eAAmB,MAC5B1B,EAAiB,IAAI,eAAe,IAAM,CACxCqC,EAAA,CACF,CAAC,EACDrC,EAAe,QAAQ2B,CAAS,EAC5B1C,EAAW,OAAOe,EAAe,QAAQf,EAAW,KAAK,GAI/DkD,EAAAA,SAAS,IAAM,CACbE,EAAA,CACF,CAAC,EACH,CAAC,EAEDO,EAAAA,gBAAgB,IAAM,CACpB,MAAMjB,EAAY5C,EAAa,MAC3B4C,IACFA,EAAU,oBAAoB,aAAcV,CAAgB,EAC5DU,EAAU,oBAAoB,YAAaR,CAAe,EAC1DQ,EAAU,oBAAoB,WAAYN,CAAc,EACxDM,EAAU,oBAAoB,cAAeL,CAAiB,EAC9DK,EAAU,oBAAoB,YAAaJ,CAAe,EAC1DI,EAAU,oBAAoB,SAAUD,CAAY,GAEtD,OAAO,oBAAoB,YAAaF,CAAe,EACvD,OAAO,oBAAoB,UAAWC,CAAa,EAC/CzB,IACFA,EAAe,WAAA,EACfA,EAAiB,MAEfF,IAAiB,OACnB,qBAAqBA,CAAY,EACjCA,EAAe,MAEbD,IAAgB,OAClB,aAAaA,CAAW,EACxBA,EAAc,MAEZE,IAAc,OAChB,aAAaA,CAAS,EACtBA,EAAY,KAEhB,CAAC,EAED8C,EAAa,CACX,QAAST,EACT,cAAAA,EACA,eAAgBA,EAChB,WAAAE,GACA,aAAcA,GACd,mBAAAD,CAAA,CACD,wBAICxE,EAAAA,mBAyCM,MAAA,SAzCG,eAAJ,IAAIkB,EAAe,MAAM,iBAAA,GAGpBhB,EAAA,2BADRF,EAAAA,mBAYM,MAAA,OAVJ,MAAKC,EAAAA,eAAA,CAAC,yBAAwB,CAAA,gBACHuB,EAAA,KAAA,CAAU,CAAA,EACpC,gDAAkCD,EAAA,KAAY,MAAA,CAAA,GAE/C0D,iBAAA9E,EAAAA,mBAKM,MALNC,GAKM,CAHQkB,EAAA,QAAU,4BAAtBtB,EAAAA,mBAAgD,OAAAK,EAAA,GAC/BiB,EAAA,QAAU,yBAA3BtB,EAAAA,mBAAqF,OAArFkF,GAAqF5E,EAAAA,gBAArBJ,EAAA,WAAW,EAAA,CAAA,kBAC3EF,EAAAA,mBAAuD,OAAvDmF,GAAuD7E,EAAAA,gBAArBJ,EAAA,WAAW,EAAA,CAAA,EAAA,iBAJlCoB,EAAA,QAAU,MAAA,CAAA,oCASzBnB,EAAAA,mBAuBM,MAAA,SAtBA,aAAJ,IAAIiB,EACJ,MAAKnB,EAAAA,eAAA,CAAC,0BAAyB,CAAA,gBACJuB,EAAA,KAAA,CAAU,CAAA,EACpC,MAAK4D,EAAAA,eAAElF,EAAA,YAAW,CAAA,UAAA,cAA8BqB,EAAA,KAAY,KAAA,EAAA,CAAA,CAAA,CAAA,GAGlDrB,EAAA,gBAAXU,EAAAA,UAAA,EAAAZ,EAAAA,mBAEM,MAFNqF,GAEM,CADJC,cAAyCC,EAAA,CAAxB,KAAMrF,EAAA,cAAA,EAAc,KAAA,EAAA,CAAA,MAAA,CAAA,CAAA,gCAIjBA,EAAA,UAAYA,EAAA,uBAAlCsF,EAAAA,YAA+DC,EAAA,OAAnB,KAAMvF,EAAA,SAAA,gDAGlDW,EAAAA,WAAaC,EAAA,OAAA,UAAA,CAAA,EAAA,OAAA,EAAA,EAGDZ,EAAA,mDAAZF,EAAAA,mBAIM,MAAA,eAJmB,eAAJ,IAAIqB,EAAe,MAAM,qBAAA,GACtBnB,EAAA,SAAWoB,EAAA,QAAU,4BAA3CkE,EAAAA,YAAoFD,EAAA,OAArB,KAAMrF,EAAA,WAAA,oBACpDA,EAAA,wBAAjBF,EAAAA,mBAA0E,OAA1E0F,GAA0EpF,EAAAA,gBAAtBJ,EAAA,YAAY,EAAA,CAAA,GAC/CA,EAAA,qBAAjBF,EAAAA,mBAAsF,OAAA,OAA9D,MAAM,aAAc,QAAO2E,EAAA,oBAAgBzE,EAAA,SAAS,EAAA,CAAA,mqBCzepF,MAAMM,EAAQN,EAyDRc,EAAOC,EASP0E,EAAYxE,EAAAA,IAAgD,IAAI,EAEhEyE,EAAOzE,EAAAA,IAAS,EAAE,EAClB0E,EAAU1E,EAAAA,IAAI,EAAK,EACnB2E,EAAW3E,EAAAA,IAAI,EAAK,EACpB4E,EAAU5E,EAAAA,IAAI,EAAK,EACnB6E,EAAY7E,EAAAA,IAAI,EAAK,EACrB8E,EAAQ9E,EAAAA,IAAI,CAAC,EACb+E,EAAO/E,EAAAA,IAAI,CAAC,EACZU,EAAeV,EAAAA,IAAI,EAAK,EAE9B,IAAIgF,EAAa,EAEjB,eAAeC,EAAKC,EAAQ,GAAO,CAC7BA,IACFH,EAAK,MAAQ,EACbJ,EAAS,MAAQ,GACjBC,EAAQ,MAAQ,IAGlBF,EAAQ,MAAQ,GAChBG,EAAU,MAAQ,GAElBG,GAAc,EACd,MAAMG,EAAMH,EAEZ,GAAI,CACF,MAAMI,EAAS,MAAM/F,EAAM,QAAQ,CACjC,KAAM0F,EAAK,MACX,SAAU1F,EAAM,SAChB,GAAGA,EAAM,MAAA,CACV,EAED,GAAI8F,IAAQH,EAAY,OAExB,MAAMK,GAAUD,GAAA,YAAAA,EAAQ,UAAW,CAAA,EAC/BF,GAEFT,EAAK,MAAQY,EACbP,EAAM,MAAQ,GAEdL,EAAK,MAAQ,CAAC,GAAGA,EAAK,MAAO,GAAGY,CAAO,EAEzCP,EAAM,MAAQ,QAAOM,GAAA,YAAAA,EAAQ,QAAS,CAAC,EACvCT,EAAS,MAAQF,EAAK,MAAM,QAAUK,EAAM,MAC5CF,EAAQ,MAAQH,EAAK,MAAM,SAAW,EAEtC5E,EAAK,UAAW,CAAE,QAAAwF,EAAS,MAAOP,EAAM,MAAO,MAAAI,EAAO,EACtDrF,EAAK,cAAe4E,EAAK,KAAK,CAChC,OAASa,EAAK,CACZ,GAAIH,IAAQH,EAAY,OACxBH,EAAU,MAAQ,GAClBhF,EAAK,QAASyF,CAAG,CACnB,QAAA,CACMH,IAAQH,IACVN,EAAQ,MAAQ,GAEpB,CACF,CAEA,eAAea,GAAgB,OAC7B7E,EAAa,MAAQ,IAErBsC,EAAAwB,EAAU,QAAV,MAAAxB,EAAiB,gBAEjB,MAAM,IAAI,QAAcwC,GAAW,CACjC,WAAW,IAAMA,EAAA,EAAW,GAAG,CACjC,CAAC,EAED,MAAMP,EAAK,EAAI,EACfvE,EAAa,MAAQ,EACvB,CAEA,eAAe+E,GAAa,SAC1B,GAAId,EAAS,MAAO,EAClB3B,EAAAwB,EAAU,QAAV,MAAAxB,EAAiB,aACjB,MACF,CAEIyB,EAAK,MAAM,OAAS,IACtBM,EAAK,OAAS,GAEhB,MAAME,EAAK,EAAK,GAChB1B,EAAAiB,EAAU,QAAV,MAAAjB,EAAiB,YACnB,CAEA,SAASC,GAAc,CACrBqB,EAAU,MAAQ,GACdJ,EAAK,MAAM,SAAW,EACxBQ,EAAK,EAAI,EAETQ,EAAA,CAEJ,CAEA,eAAeC,GAAS,CACtB,MAAMT,EAAK,EAAI,CACjB,CAEAxB,OAAAA,EAAAA,MACE,IAAMpE,EAAM,OACZ,IAAM,CACAA,EAAM,YACRqG,EAAA,CAEJ,EACA,CAAE,KAAM,EAAA,CAAK,EAGfjC,EAAAA,MACEgB,EACA,SAAY,OACV,MAAMtB,WAAA,GACNH,EAAAwB,EAAU,QAAV,MAAAxB,EAAiB,oBACnB,EACA,CAAE,KAAM,EAAA,CAAK,EAGfW,EAAAA,UAAU,IAAM,CACVtE,EAAM,WACR4F,EAAK,EAAI,CAEb,CAAC,EAEDpB,EAAa,CAEX,OAAA6B,EAEA,QAASA,EAET,KAAAjB,EAEA,QAAAC,EAEA,SAAAC,EAEA,QAAAC,EAEA,MAAAE,EAEA,WAAY,IAAA,OAAM,OAAA9B,EAAAwB,EAAU,QAAV,YAAAxB,EAAiB,cACnC,cAAe,IAAA,OAAM,OAAAA,EAAAwB,EAAU,QAAV,YAAAxB,EAAiB,iBACtC,mBAAoB,IAAA,OAAM,OAAAA,EAAAwB,EAAU,QAAV,YAAAxB,EAAiB,qBAAmB,CAC/D,wBAICqB,EAAAA,YA8BiBsB,EAAA,SA7BX,YAAJ,IAAInB,EACH,QAASE,EAAA,MACT,SAAUC,EAAA,MACV,WAAUC,EAAA,MACV,MAAOC,EAAA,MACP,eAAc9F,EAAA,YACd,iBAAgBA,EAAA,cAChB,iBAAgBA,EAAA,cAChB,aAAYA,EAAA,UACZ,eAAcA,EAAA,YACd,eAAcA,EAAA,YACd,kBAAiBA,EAAA,eACjB,eAAcA,EAAA,YACd,gBAAeA,EAAA,aACf,aAAYA,EAAA,UACZ,kBAAiB2B,EAAA,MACjB,OAAM+E,EACN,UAASF,EACT,QAAO/B,CAAA,qBAER,IAQE,CARF9D,aAQEC,EAAA,OAAA,UAAA,CAPC,KAAM8E,EAAA,MACN,QAASC,EAAA,MACT,SAAUC,EAAA,MACV,QAAUC,EAAA,MACV,MAAOC,EAAA,MACP,MAAOC,EAAA,MACP,OAAAY,CAAA,uOCxPP,IAAIE,EAAgB,GAEpB,MAAMC,GAAa,upEAEZ,SAASC,IAAc,CAC5B,GAAIF,GAAiB,OAAO,SAAa,IAAa,OAEtD,MAAMG,EAAQ,SAAS,cAAc,OAAO,EAC5CA,EAAM,aAAa,cAAe,oBAAoB,EACtDA,EAAM,YAAcF,GACpB,SAAS,KAAK,YAAYE,CAAK,EAE/BH,EAAgB,EAClB,CCNAE,GAAA,EAMO,SAASE,EAAQC,EAAU,CAChCA,EAAI,UAAU,iBAAkBN,CAAc,EAC9CM,EAAI,UAAU,mBAAoBC,CAAgB,EAClDD,EAAI,UAAU,iBAAkB7B,CAAc,EAC9C6B,EAAI,UAAU,iBAAkB3B,CAAc,CAChD,CAGA,MAAA6B,GAAe,CACb,QAAAH,CACF"}
|
|
1
|
+
{"version":3,"file":"index.umd.js","sources":["../src/components/request-loading.vue","../src/components/empty-component.vue","../src/components/infinite-scroll.vue","../src/components/infinite-scroll-v2.vue","../src/inject-style.ts","../src/index.ts"],"sourcesContent":["<script setup lang=\"ts\">\ndefineOptions({ name: 'RequestLoading' });\n\nwithDefaults(defineProps<{ text?: string; fullscreen?: boolean }>(), {\n text: '加载中...',\n fullscreen: false\n});\n</script>\n\n<template>\n <div class=\"zx-loading-container\" :class=\"{ fullscreen: fullscreen }\">\n <div class=\"zx-loading-content\">\n <span class=\"zx-loading-spinner\"></span>\n <span class=\"zx-loading-text\">{{ text }}</span>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n.zx-loading-content {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 6px 12px;\n border-radius: 16px;\n color: #1989fa;\n font-size: 14px;\n}\n\n.zx-loading-spinner {\n width: 18px;\n height: 18px;\n border-radius: 50%;\n border: 2.5px solid #1989fa;\n border-top-color: transparent;\n animation: zx-spin 0.8s linear infinite;\n}\n\n@keyframes zx-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n.zx-loading-container.fullscreen {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: rgba(255, 255, 255, 0.5);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue';\n\ndefineOptions({ name: 'EmptyComponent' });\n\ninterface Props {\n text?: string;\n showText?: boolean;\n /** 顶部间距,可传数字(px)或带单位字符串,默认 168px */\n paddingTop?: number | string;\n /** 自定义图片 URL */\n emptyImage?: string;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n text: '暂无数据',\n showText: true,\n paddingTop: 168,\n emptyImage: ''\n});\n\nconst wrapStyle = computed(() => ({\n paddingTop: typeof props.paddingTop === 'number' ? `${props.paddingTop}px` : props.paddingTop\n}));\n\n// 默认空状态 SVG(简洁的空文件夹图标)\nconst defaultEmptySvg = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='80' viewBox='0 0 96 80' fill='none'%3E%3Cpath d='M8 16C8 12.6863 10.6863 10 14 10H38L44 16H82C85.3137 16 88 18.6863 88 22V66C88 69.3137 85.3137 72 82 72H14C10.6863 72 8 69.3137 8 66V16Z' fill='%23E8E8E8'/%3E%3Cpath d='M8 20C8 18.3431 9.34315 17 11 17H85C86.6569 17 88 18.3431 88 20V66C88 69.3137 85.3137 72 82 72H14C10.6863 72 8 69.3137 8 66V20Z' fill='%23F5F5F5'/%3E%3Cpath d='M38 42C38 40.8954 38.8954 40 40 40H56C57.1046 40 58 40.8954 58 42V44C58 45.1046 57.1046 46 56 46H40C38.8954 46 38 45.1046 38 44V42Z' fill='%23D9D9D9'/%3E%3C/svg%3E`;\n\nconst imageSrc = computed(() => props.emptyImage || defaultEmptySvg);\n</script>\n\n<template>\n <div class=\"zx-empty-wrap\" :style=\"wrapStyle\">\n <img class=\"zx-empty-image\" :src=\"imageSrc\" alt=\"\" />\n <p v-if=\"showText\" class=\"zx-empty-text\">\n <slot>{{ text }}</slot>\n </p>\n </div>\n</template>\n\n<style lang=\"scss\">\n.zx-empty-wrap {\n display: flex;\n flex-direction: column;\n align-items: center;\n}\n\n.zx-empty-image {\n width: 96px;\n height: 80px;\n}\n\n.zx-empty-text {\n margin: 12px 0 0;\n color: #8c8f97;\n font-size: 12px;\n line-height: 22px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';\nimport RequestLoading from './request-loading.vue';\nimport EmptyComponent from './empty-component.vue';\n\ndefineOptions({\n name: 'InfiniteScroll'\n});\n\ninterface Props {\n /** 是否处于加载中状态 */\n loading?: boolean;\n /** 是否已加载完所有数据 */\n finished?: boolean;\n /** 是否显示空状态 */\n isEmpty?: boolean;\n /** 空状态文案 */\n emptyText?: string;\n /** 是否启用下拉刷新 */\n pullRefresh?: boolean;\n /** 下拉刷新触发距离(px) */\n pullThreshold?: number;\n /** 上拉加载触发距离(距离底部 px) */\n loadThreshold?: number;\n /** 下拉中的文案 */\n pullingText?: string;\n /** 释放刷新的文案 */\n loosingText?: string;\n /** 刷新中的文案 */\n refreshingText?: string;\n /** 加载中的文案 */\n loadingText?: string;\n /** 加载完成的文案 */\n finishedText?: string;\n /** 加载失败的文案 */\n errorText?: string;\n /** 是否显示错误状态 */\n error?: boolean;\n /** 内容区域是否显示加载状态 */\n contentLoading?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n loading: false,\n finished: false,\n isEmpty: false,\n emptyText: '暂无数据',\n pullRefresh: false,\n pullThreshold: 70,\n loadThreshold: 50,\n pullingText: '下拉刷新',\n loosingText: '释放即可刷新',\n refreshingText: '加载中...',\n loadingText: '加载中...',\n finishedText: '没有更多了~',\n errorText: '加载失败,点击重试',\n error: false,\n contentLoading: false\n});\n\nconst emit = defineEmits<{\n /** 触发下拉刷新 */\n refresh: [];\n /** 触发上拉加载 */\n load: [];\n /** 点击错误重试 */\n retry: [];\n}>();\n\nconst containerRef = ref<HTMLElement | null>(null);\nconst contentRef = ref<HTMLElement | null>(null);\nconst indicatorRef = ref<HTMLElement | null>(null);\n\n// 下拉刷新状态\nconst pullStatus = ref<'idle' | 'pulling' | 'loosing' | 'refreshing'>('idle');\nconst pullDistance = ref(0);\n// 拖动期间禁用 transition,避免 transform 变化时被 0.25s 补间拖慢\nconst isDragging = ref(false);\n\n// 触摸相关\nlet startX = 0;\nlet startY = 0;\nlet lastDistance = 0;\n// 手势方向锁:避免横向滑动被误判为下拉\nlet gestureLock: 'none' | 'vertical' | 'horizontal' = 'none';\nconst SLOPE_LOCK_DISTANCE = 8;\n\nlet isRefreshing = false;\nlet isLoading = false;\n// 鼠标拖拽下拉(PC 端)\nlet isMouseDown = false;\n\n// 节流相关\nlet scrollTimer: number | null = null;\nlet touchMoveRaf: number | null = null;\nlet idleTimer: number | null = null;\n// 监听容器尺寸变化\nlet resizeObserver: ResizeObserver | null = null;\n// 防止空页死循环:snapshot 触发 load 时的 contentHeight,下次完成后没增长就不再 auto-load\nlet lastTriggerContentHeight = -1;\n\n// 下拉刷新核心:按下(与指针类型无关,touch/mouse 共用)\nfunction pullStart(clientX: number, clientY: number) {\n if (!props.pullRefresh || isRefreshing) return;\n startX = clientX;\n startY = clientY;\n lastDistance = 0;\n gestureLock = 'none';\n pullStatus.value = 'idle';\n}\n\n// 下拉刷新核心:移动(rAF 节流,始终使用最新位置)\n// 返回 true 表示消费了本次移动(下拉进行中),调用方据此决定是否 preventDefault\nfunction pullMove(clientX: number, clientY: number, cancelable: boolean): boolean {\n if (!props.pullRefresh || isRefreshing || startY === 0) return false;\n\n const dx = clientX - startX;\n const dy = clientY - startY;\n\n // 方向判定:先到达 8px 锁定的方向才认为有意图,垂直要明显大于水平(1.5x)\n if (gestureLock === 'none') {\n const ax = Math.abs(dx);\n const ay = Math.abs(dy);\n if (ax > SLOPE_LOCK_DISTANCE || ay > SLOPE_LOCK_DISTANCE) {\n gestureLock = ay > ax * 1.5 ? 'vertical' : 'horizontal';\n } else {\n return false;\n }\n }\n if (gestureLock === 'horizontal') return false;\n\n // 子像素容差:≤1 视为已到顶,避免 X5 内核 scrollTop 偶发非整数判断失败\n const atTop = getScrollTop() <= 1;\n\n // iOS 15- 兜底:在 atBottom 且向上拖时阻止整页 rubber-band\n // (iOS 16+ overscroll-behavior 已处理,此处兜底兼容)\n if (!atTop && dy < 0) {\n const c = containerRef.value;\n if (c) {\n const atBottom = c.scrollHeight - c.scrollTop - c.clientHeight <= 1;\n if (atBottom && cancelable) {\n return true;\n }\n }\n return false;\n }\n if (!atTop) return false;\n\n // 反向拖动也要更新(让指示器跟着手指回退),夹紧到 [0, 150]\n const clamped = Math.max(0, Math.min(dy, 150));\n\n lastDistance = clamped;\n isDragging.value = true;\n\n if (touchMoveRaf === null) {\n touchMoveRaf = requestAnimationFrame(() => {\n touchMoveRaf = null;\n pullDistance.value = lastDistance * 0.5;\n if (lastDistance === 0) {\n pullStatus.value = 'idle';\n } else {\n pullStatus.value = lastDistance > props.pullThreshold ? 'loosing' : 'pulling';\n }\n });\n }\n\n // 仅在向下拖时消费事件(避免误吞用户向上滚动的意图)\n return dy > 0;\n}\n\n// 下拉刷新核心:松开\nfunction pullEnd() {\n if (!props.pullRefresh || isRefreshing) return;\n\n // 取消待执行的 rAF,避免松手后还有一帧异步更新把 UI 拉回 loosing 卡住\n if (touchMoveRaf !== null) {\n cancelAnimationFrame(touchMoveRaf);\n touchMoveRaf = null;\n }\n\n isDragging.value = false;\n\n // 用最新的 lastDistance 同步判定,不依赖可能尚未生效的 pullStatus\n if (lastDistance > props.pullThreshold) {\n isRefreshing = true;\n pullStatus.value = 'refreshing';\n pullDistance.value = 50;\n emit('refresh');\n } else {\n pullDistance.value = 0;\n pullStatus.value = 'idle';\n }\n\n resetTouch();\n}\n\n// 下拉刷新核心:取消(回到 idle)\nfunction pullCancel() {\n if (!props.pullRefresh || isRefreshing) return;\n\n if (touchMoveRaf !== null) {\n cancelAnimationFrame(touchMoveRaf);\n touchMoveRaf = null;\n }\n\n isDragging.value = false;\n pullDistance.value = 0;\n pullStatus.value = 'idle';\n resetTouch();\n}\n\n// 下拉刷新 - touchstart\nfunction handleTouchStart(e: TouchEvent) {\n pullStart(e.touches[0].clientX, e.touches[0].clientY);\n}\n\n// 下拉刷新 - touchmove\nfunction handleTouchMove(e: TouchEvent) {\n const touch = e.touches[0];\n if (pullMove(touch.clientX, touch.clientY, e.cancelable) && e.cancelable) {\n e.preventDefault();\n }\n}\n\n// 下拉刷新 - touchend\nfunction handleTouchEnd() {\n pullEnd();\n}\n\n// 下拉刷新 - touchcancel(微信 X5 内核会在系统手势介入时派发 cancel 而非 end)\nfunction handleTouchCancel() {\n pullCancel();\n}\n\n// 下拉刷新 - mousedown(PC 端鼠标拖拽)\nfunction handleMouseDown(e: MouseEvent) {\n // 仅左键触发\n if (!props.pullRefresh || isRefreshing || e.button !== 0) return;\n isMouseDown = true;\n pullStart(e.clientX, e.clientY);\n}\n\n// 下拉刷新 - mousemove(挂在 window 上,允许拖出容器)\nfunction handleMouseMove(e: MouseEvent) {\n if (!isMouseDown) return;\n if (pullMove(e.clientX, e.clientY, true)) {\n // 阻止拖拽时选中文本\n e.preventDefault();\n }\n}\n\n// 下拉刷新 - mouseup\nfunction handleMouseUp() {\n if (!isMouseDown) return;\n isMouseDown = false;\n pullEnd();\n}\n\nfunction resetTouch() {\n startX = 0;\n startY = 0;\n lastDistance = 0;\n gestureLock = 'none';\n}\n\n// 上拉加载 - scroll(节流处理)\nfunction handleScroll() {\n if (props.loading || props.finished || props.error || isLoading) return;\n if (scrollTimer) return;\n\n scrollTimer = window.setTimeout(() => {\n scrollTimer = null;\n const container = containerRef.value;\n if (!container) return;\n\n const { scrollTop, clientHeight, scrollHeight } = container;\n const contentHeight = contentRef.value?.clientHeight || 0;\n\n // 情况1:内容不足一屏,自动触发加载\n if (contentHeight <= clientHeight) {\n // 死循环防护:上一次 auto-load 后内容没增长,不再继续触发\n if (lastTriggerContentHeight === contentHeight) return;\n lastTriggerContentHeight = contentHeight;\n // 不足一屏时主动滚到底,让 loading 指示器进入视口\n triggerLoad(true);\n return;\n }\n\n // 情况2:滚动到距离底部阈值范围内\n if (scrollHeight - scrollTop - clientHeight <= props.loadThreshold) {\n // 用户已经手动滚到底,不再强制 scrollTop(避免打断惯性滚动)\n triggerLoad(false);\n }\n }, 100);\n}\n\nfunction triggerLoad(scrollToBottom = false) {\n isLoading = true;\n emit('load');\n nextTick(() => {\n const c = containerRef.value;\n if (!c) return;\n if (scrollToBottom) {\n // 内容不足一屏:直接滚到底(没有\"惯性\"可以打断)\n c.scrollTop = c.scrollHeight;\n } else {\n // 阈值触发:用 scrollIntoView 平滑进入视口,避免突兀打断;\n // 旧 WebKit 不识别 options 会降级为即时滚动,效果等价\n indicatorRef.value?.scrollIntoView({ block: 'end', behavior: 'smooth' });\n }\n });\n}\n\n// 获取滚动距离\nfunction getScrollTop(): number {\n return containerRef.value?.scrollTop ?? 0;\n}\n\n// 完成下拉刷新\nfunction finishRefresh() {\n isRefreshing = false;\n pullDistance.value = 0;\n // 让 transform snap-back 完成后再切 idle,避免 spinner 在动画中途突然消失\n if (idleTimer !== null) clearTimeout(idleTimer);\n idleTimer = window.setTimeout(() => {\n pullStatus.value = 'idle';\n idleTimer = null;\n }, 250);\n // 重置死循环计数\n lastTriggerContentHeight = -1;\n nextTick(() => {\n checkContentHeight();\n });\n}\n\n// 完成上拉加载\nfunction finishLoad() {\n isLoading = false;\n nextTick(() => {\n const contentHeight = contentRef.value?.clientHeight || 0;\n // 内容没增长就不再触发 auto-load,避免空页死循环\n if (lastTriggerContentHeight !== -1 && contentHeight === lastTriggerContentHeight) return;\n lastTriggerContentHeight = -1;\n checkContentHeight();\n });\n}\n\n// 检查内容高度,不足一屏时自动触发加载\nfunction checkContentHeight() {\n if (props.finished || props.loading || props.error) return;\n\n nextTick(() => {\n const contentHeight = contentRef.value?.clientHeight || 0;\n const clientHeight = containerRef.value?.clientHeight || 0;\n\n if (contentHeight <= clientHeight && !isLoading) {\n handleScroll();\n }\n });\n}\n\n// 点击错误重试 — 由父组件决定具体行为,避免 retry + load 双触发\nfunction handleRetry() {\n emit('retry');\n}\n\n// loading 完成时同步重置 isLoading\nwatch(\n () => props.loading,\n newVal => {\n if (!newVal) {\n isLoading = false;\n }\n }\n);\n\n// finished 翻为 true 时重置死循环计数\nwatch(\n () => props.finished,\n newVal => {\n if (newVal) {\n lastTriggerContentHeight = -1;\n }\n }\n);\n\nonMounted(() => {\n const container = containerRef.value;\n if (!container) return;\n\n if (props.pullRefresh) {\n container.addEventListener('touchstart', handleTouchStart, { passive: true });\n container.addEventListener('touchmove', handleTouchMove, { passive: false });\n container.addEventListener('touchend', handleTouchEnd, { passive: true });\n container.addEventListener('touchcancel', handleTouchCancel, { passive: true });\n // 鼠标拖拽(PC):move/up 挂在 window 上,允许拖出容器边界\n container.addEventListener('mousedown', handleMouseDown);\n window.addEventListener('mousemove', handleMouseMove);\n window.addEventListener('mouseup', handleMouseUp);\n }\n\n container.addEventListener('scroll', handleScroll, { passive: true });\n\n // 监听容器/内容尺寸变化(旋转、WebView resize、软键盘弹出收起等)\n if (typeof ResizeObserver !== 'undefined') {\n resizeObserver = new ResizeObserver(() => {\n checkContentHeight();\n });\n resizeObserver.observe(container);\n if (contentRef.value) resizeObserver.observe(contentRef.value);\n }\n\n // 初始检查内容高度\n nextTick(() => {\n checkContentHeight();\n });\n});\n\nonBeforeUnmount(() => {\n const container = containerRef.value;\n if (container) {\n container.removeEventListener('touchstart', handleTouchStart);\n container.removeEventListener('touchmove', handleTouchMove);\n container.removeEventListener('touchend', handleTouchEnd);\n container.removeEventListener('touchcancel', handleTouchCancel);\n container.removeEventListener('mousedown', handleMouseDown);\n container.removeEventListener('scroll', handleScroll);\n }\n window.removeEventListener('mousemove', handleMouseMove);\n window.removeEventListener('mouseup', handleMouseUp);\n if (resizeObserver) {\n resizeObserver.disconnect();\n resizeObserver = null;\n }\n if (touchMoveRaf !== null) {\n cancelAnimationFrame(touchMoveRaf);\n touchMoveRaf = null;\n }\n if (scrollTimer !== null) {\n clearTimeout(scrollTimer);\n scrollTimer = null;\n }\n if (idleTimer !== null) {\n clearTimeout(idleTimer);\n idleTimer = null;\n }\n});\n\ndefineExpose({\n refresh: finishRefresh,\n finishRefresh,\n finishPullDown: finishRefresh,\n finishLoad,\n finishPullUp: finishLoad,\n checkContentHeight\n});\n</script>\n\n<template>\n <div ref=\"containerRef\" class=\"zx-infinite-scroll\">\n <!-- 下拉刷新指示器 -->\n <div\n v-if=\"pullRefresh\"\n class=\"zx-pull-refresh-indicator\"\n :class=\"{ 'no-transition': isDragging }\"\n :style=\"{ transform: `translateY(${pullDistance}px)` }\"\n >\n <div v-show=\"pullStatus !== 'idle'\" class=\"zx-pull-refresh-content\">\n <!-- <RequestLoading v-if=\"pullStatus === 'refreshing'\" text=\"\" /> -->\n <span v-if=\"pullStatus === 'refreshing'\"></span>\n <span v-else-if=\"pullStatus === 'loosing'\" class=\"zx-pull-text\">{{ loosingText }}</span>\n <span v-else class=\"zx-pull-text\">{{ pullingText }}</span>\n </div>\n </div>\n\n <!-- 内容区域 -->\n <div\n ref=\"contentRef\"\n class=\"zx-infinite-scroll-content\"\n :class=\"{ 'no-transition': isDragging }\"\n :style=\"pullRefresh ? { transform: `translateY(${pullDistance}px)` } : {}\"\n >\n <!-- 内容区域加载遮罩 -->\n <div v-if=\"contentLoading\" class=\"zx-content-loading-mask\">\n <RequestLoading :text=\"refreshingText\" />\n </div>\n\n <!-- 空状态 -->\n <EmptyComponent v-if=\"isEmpty && !loading\" :text=\"emptyText\" />\n\n <!-- 默认插槽 -->\n <slot></slot>\n\n <!-- 上拉加载指示器 -->\n <div v-if=\"!isEmpty\" ref=\"indicatorRef\" class=\"zx-load-more-indicator\">\n <RequestLoading v-if=\"loading && pullStatus !== 'refreshing'\" :text=\"loadingText\" />\n <span v-else-if=\"finished\" class=\"zx-finished-text\">{{ finishedText }}</span>\n <span v-else-if=\"error\" class=\"zx-error-text\" @click=\"handleRetry\">{{ errorText }}</span>\n </div>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n.zx-infinite-scroll {\n height: 100%;\n position: relative;\n min-height: 100%;\n overflow-y: auto;\n overflow-x: hidden;\n // 阻止滚动到边界时把滚动链传递给祖先(修复 iOS 16+ / 微信 X5 下拉变成整页拖拽)\n overscroll-behavior: contain;\n // 显式声明只接受纵向手势,避免被外层 touch-action 影响\n touch-action: pan-y;\n // iOS 下让原生回弹有惯性\n -webkit-overflow-scrolling: touch;\n}\n\n.zx-pull-refresh-indicator {\n position: absolute;\n top: -50px;\n left: 0;\n right: 0;\n height: 50px;\n transition: transform 0.25s;\n\n // 拖动期间禁用过渡,让 transform 1:1 跟随手指\n &.no-transition {\n transition: none;\n }\n}\n\n.zx-pull-refresh-content {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n font-size: 14px;\n color: #969799;\n}\n\n.zx-pull-text {\n color: #1989fa;\n font-size: 14px;\n}\n\n.zx-infinite-scroll-content {\n position: relative;\n transition: transform 0.25s;\n min-height: 100%;\n\n &.no-transition {\n transition: none;\n }\n}\n\n.zx-content-loading-mask {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(255, 255, 255, 0.8);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n backdrop-filter: blur(2px);\n}\n\n.zx-load-more-indicator {\n display: flex;\n align-items: center;\n justify-content: center;\n // 预留高度避免 loading 渲染后被推到视口外造成遮挡\n min-height: 50px;\n padding: 16px 0;\n font-size: 14px;\n color: #8c8f97;\n}\n\n.zx-finished-text {\n color: #8c8f97;\n font-size: 14px;\n}\n\n.zx-error-text {\n color: #ee0a24;\n font-size: 14px;\n cursor: pointer;\n\n &:active {\n opacity: 0.7;\n }\n}\n</style>\n","<script setup lang=\"ts\" generic=\"T\">\nimport { type Ref, nextTick, onMounted, ref, watch } from 'vue';\nimport InfiniteScroll from './infinite-scroll.vue';\n\ndefineOptions({\n name: 'InfiniteScrollV2'\n});\n\nconst props = withDefaults(\n defineProps<{\n /** 数据请求函数,需返回 { records, total },失败时抛错 */\n fetchFn: (params: {\n page: number;\n pageSize: number;\n [key: string]: any;\n }) => Promise<{\n records: T[];\n total: number;\n }>;\n /** 业务查询参数(如筛选条件),变化时自动 reset 重新加载 */\n params?: Record<string, any>;\n /** 每页条数 */\n pageSize?: number;\n /** params 变化时是否自动重新加载 */\n autoReload?: boolean;\n /** 是否在挂载时自动加载首页 */\n immediate?: boolean;\n /** 是否启用下拉刷新 */\n pullRefresh?: boolean;\n /** 下拉刷新触发距离(px) */\n pullThreshold?: number;\n /** 上拉加载触发距离(距离底部 px) */\n loadThreshold?: number;\n /** 空状态文案 */\n emptyText?: string;\n /** 下拉中的文案 */\n pullingText?: string;\n /** 释放刷新的文案 */\n loosingText?: string;\n /** 刷新中的文案 */\n refreshingText?: string;\n /** 加载中的文案 */\n loadingText?: string;\n /** 加载完成的文案 */\n finishedText?: string;\n /** 加载失败的文案 */\n errorText?: string;\n }>(),\n {\n params: () => ({}),\n pageSize: 10,\n autoReload: true,\n immediate: true,\n pullRefresh: false,\n pullThreshold: 70,\n loadThreshold: 50,\n emptyText: '暂无数据',\n pullingText: '下拉刷新',\n loosingText: '释放即可刷新',\n refreshingText: '加载中...',\n loadingText: '加载中...',\n finishedText: '没有更多了~',\n errorText: '加载失败,点击重试'\n});\n\nconst emit = defineEmits<{\n /** 加载成功,每次成功(含分页)都会触发 */\n success: [payload: { records: T[]; total: number; reset: boolean }];\n /** 加载失败 */\n error: [err: unknown];\n /** 列表内容变化(v-model:list 支持) */\n 'update:list': [list: T[]];\n}>();\n\nconst scrollRef = ref<InstanceType<typeof InfiniteScroll> | null>(null);\n\nconst list = ref<T[]>([]) as Ref<T[]>;\nconst loading = ref(false);\nconst finished = ref(false);\nconst isEmpty = ref(false);\nconst loadError = ref(false);\nconst total = ref(0);\nconst page = ref(1);\nconst isRefreshing = ref(false);\n\nlet requestSeq = 0;\n\nasync function load(reset = false) {\n if (reset) {\n page.value = 1;\n finished.value = false;\n isEmpty.value = false;\n // 不立即清空 list,保留旧数据直到新数据到达\n }\n loading.value = true;\n loadError.value = false;\n\n requestSeq += 1;\n const seq = requestSeq;\n\n try {\n const result = await props.fetchFn({\n page: page.value,\n pageSize: props.pageSize,\n ...props.params\n });\n\n if (seq !== requestSeq) return;\n\n const records = result?.records || [];\n if (reset) {\n // reset 时替换整个列表\n list.value = records;\n total.value = 0;\n } else {\n list.value = [...list.value, ...records];\n }\n total.value = Number(result?.total || 0);\n finished.value = list.value.length >= total.value;\n isEmpty.value = list.value.length === 0;\n\n emit('success', { records, total: total.value, reset });\n emit('update:list', list.value);\n } catch (err) {\n if (seq !== requestSeq) return;\n loadError.value = true;\n emit('error', err);\n } finally {\n if (seq === requestSeq) {\n loading.value = false;\n }\n }\n}\n\nasync function handleRefresh() {\n isRefreshing.value = true;\n // 先让下拉刷新指示器收回\n scrollRef.value?.finishRefresh();\n // 等待 250ms 让指示器完全收回(对应 infinite-scroll.vue 的 transition 时长)\n await new Promise<void>(resolve => {\n setTimeout(() => resolve(), 250);\n });\n // 指示器收回后再开始请求新数据\n await load(true);\n isRefreshing.value = false;\n}\n\nasync function handleLoad() {\n if (finished.value) {\n scrollRef.value?.finishLoad();\n return;\n }\n // 列表为空时是首次加载(page 已经是 1),其它情况 page++\n if (list.value.length > 0) {\n page.value += 1;\n }\n await load(false);\n scrollRef.value?.finishLoad();\n}\n\nfunction handleRetry() {\n loadError.value = false;\n if (list.value.length === 0) {\n load(true);\n } else {\n handleLoad();\n }\n}\n\nasync function reload() {\n await load(true);\n}\n\nwatch(\n () => props.params,\n () => {\n if (props.autoReload) {\n reload();\n }\n },\n { deep: true }\n);\n\nwatch(\n list,\n async () => {\n await nextTick();\n scrollRef.value?.checkContentHeight();\n },\n { deep: true }\n);\n\nonMounted(() => {\n if (props.immediate) {\n load(true);\n }\n});\n\ndefineExpose({\n /** 重新加载第一页 */\n reload,\n /** 兼容别名 */\n refresh: reload,\n /** 当前列表数据 */\n list,\n /** 加载中状态 */\n loading,\n /** 是否已加载完 */\n finished,\n /** 是否空数据 */\n isEmpty,\n /** 总条数 */\n total,\n /** 透传 v1 方法 */\n finishLoad: () => scrollRef.value?.finishLoad(),\n finishRefresh: () => scrollRef.value?.finishRefresh(),\n checkContentHeight: () => scrollRef.value?.checkContentHeight()\n});\n</script>\n\n<template>\n <InfiniteScroll\n ref=\"scrollRef\"\n :loading=\"loading\"\n :finished=\"finished\"\n :is-empty=\"isEmpty\"\n :error=\"loadError\"\n :pull-refresh=\"pullRefresh\"\n :pull-threshold=\"pullThreshold\"\n :load-threshold=\"loadThreshold\"\n :empty-text=\"emptyText\"\n :pulling-text=\"pullingText\"\n :loosing-text=\"loosingText\"\n :refreshing-text=\"refreshingText\"\n :loading-text=\"loadingText\"\n :finished-text=\"finishedText\"\n :error-text=\"errorText\"\n :content-loading=\"isRefreshing\"\n @load=\"handleLoad\"\n @refresh=\"handleRefresh\"\n @retry=\"handleRetry\"\n >\n <slot\n :list=\"list\"\n :loading=\"loading\"\n :finished=\"finished\"\n :is-empty=\"isEmpty\"\n :error=\"loadError\"\n :total=\"total\"\n :reload=\"reload\"\n />\n </InfiniteScroll>\n</template>\n","// 样式自动注入\nlet styleInjected = false;\n\nconst cssContent = `.zx-loading-content{display:flex;align-items:center;justify-content:center;gap:8px;padding:6px 12px;border-radius:16px;color:#1989fa;font-size:14px}.zx-loading-spinner{width:18px;height:18px;border-radius:50%;border:2.5px solid #1989fa;border-top-color:transparent;animation:zx-spin .8s linear infinite}@keyframes zx-spin{to{transform:rotate(360deg)}}.zx-loading-container.fullscreen{position:fixed;top:0;left:0;width:100%;height:100%;display:flex;align-items:center;justify-content:center;background-color:#ffffff80}.zx-empty-wrap{display:flex;flex-direction:column;align-items:center}.zx-empty-image{width:96px;height:80px}.zx-empty-text{margin:12px 0 0;color:#8c8f97;font-size:12px;line-height:22px}.zx-infinite-scroll{height:100%;position:relative;min-height:100%;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;touch-action:pan-y;-webkit-overflow-scrolling:touch}.zx-pull-refresh-indicator{position:absolute;top:-50px;left:0;right:0;height:50px;transition:transform .25s}.zx-pull-refresh-indicator.no-transition{transition:none}.zx-pull-refresh-content{display:flex;align-items:center;justify-content:center;height:100%;font-size:14px;color:#969799}.zx-pull-text{color:#1989fa;font-size:14px}.zx-infinite-scroll-content{position:relative;transition:transform .25s;min-height:100%}.zx-infinite-scroll-content.no-transition{transition:none}.zx-content-loading-mask{position:absolute;top:0;left:0;right:0;bottom:0;background-color:#fffc;display:flex;align-items:center;justify-content:center;z-index:10;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.zx-load-more-indicator{display:flex;align-items:center;justify-content:center;min-height:50px;padding:16px 0;font-size:14px;color:#8c8f97}.zx-finished-text{color:#8c8f97;font-size:14px}.zx-error-text{color:#ee0a24;font-size:14px;cursor:pointer}.zx-error-text:active{opacity:.7}`;\n\nexport function injectStyle() {\n if (styleInjected || typeof document === 'undefined') return;\n\n const style = document.createElement('style');\n style.setAttribute('data-source', 'zx-infinite-scroll');\n style.textContent = cssContent;\n document.head.appendChild(style);\n\n styleInjected = true;\n}\n","import { App } from 'vue';\nimport InfiniteScroll from './components/infinite-scroll.vue';\nimport InfiniteScrollV2 from './components/infinite-scroll-v2.vue';\nimport RequestLoading from './components/request-loading.vue';\nimport EmptyComponent from './components/empty-component.vue';\nimport { injectStyle } from './inject-style';\n\n// 自动注入样式(只要导入了这个包就会执行)\ninjectStyle();\n\n// 单独导出组件\nexport { InfiniteScroll, InfiniteScrollV2, RequestLoading, EmptyComponent };\n\n// Vue plugin install 函数\nexport function install(app: App) {\n app.component('InfiniteScroll', InfiniteScroll);\n app.component('InfiniteScrollV2', InfiniteScrollV2);\n app.component('RequestLoading', RequestLoading);\n app.component('EmptyComponent', EmptyComponent);\n}\n\n// 默认导出(支持 app.use() 安装)\nexport default {\n install\n};\n"],"names":["_createElementBlock","_normalizeClass","__props","_createElementVNode","_hoisted_1","_hoisted_2","_toDisplayString","defaultEmptySvg","props","wrapStyle","computed","imageSrc","_openBlock","_renderSlot","_ctx","SLOPE_LOCK_DISTANCE","emit","__emit","containerRef","ref","contentRef","indicatorRef","pullStatus","pullDistance","isDragging","startX","startY","lastDistance","gestureLock","isRefreshing","isLoading","isMouseDown","scrollTimer","touchMoveRaf","idleTimer","resizeObserver","lastTriggerContentHeight","pullStart","clientX","clientY","pullMove","cancelable","dx","dy","ax","ay","atTop","getScrollTop","c","pullEnd","resetTouch","pullCancel","handleTouchStart","e","handleTouchMove","touch","handleTouchEnd","handleTouchCancel","handleMouseDown","handleMouseMove","handleMouseUp","handleScroll","container","scrollTop","clientHeight","scrollHeight","contentHeight","_a","triggerLoad","scrollToBottom","nextTick","finishRefresh","checkContentHeight","finishLoad","_b","handleRetry","watch","newVal","onMounted","onBeforeUnmount","__expose","_withDirectives","_hoisted_3","_hoisted_4","_normalizeStyle","_hoisted_5","_createVNode","RequestLoading","_createBlock","EmptyComponent","_hoisted_6","scrollRef","list","loading","finished","isEmpty","loadError","total","page","requestSeq","load","reset","seq","result","records","err","handleRefresh","resolve","handleLoad","reload","InfiniteScroll","styleInjected","cssContent","injectStyle","style","install","app","InfiniteScrollV2","index"],"mappings":"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"}
|
package/dist/style.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.loading-content
|
|
1
|
+
.zx-loading-content{display:flex;align-items:center;justify-content:center;gap:8px;padding:6px 12px;border-radius:16px;color:#1989fa;font-size:14px}.zx-loading-spinner{width:18px;height:18px;border-radius:50%;border:2.5px solid #1989fa;border-top-color:transparent;animation:zx-spin .8s linear infinite}@keyframes zx-spin{to{transform:rotate(360deg)}}.zx-loading-container.fullscreen{position:fixed;top:0;left:0;width:100%;height:100%;display:flex;align-items:center;justify-content:center;background-color:#ffffff80}.zx-empty-wrap{display:flex;flex-direction:column;align-items:center}.zx-empty-image{width:96px;height:80px}.zx-empty-text{margin:12px 0 0;color:#8c8f97;font-size:12px;line-height:22px}.zx-infinite-scroll{height:100%;position:relative;min-height:100%;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;touch-action:pan-y;-webkit-overflow-scrolling:touch}.zx-pull-refresh-indicator{position:absolute;top:-50px;left:0;right:0;height:50px;transition:transform .25s}.zx-pull-refresh-indicator.no-transition{transition:none}.zx-pull-refresh-content{display:flex;align-items:center;justify-content:center;height:100%;font-size:14px;color:#969799}.zx-pull-text{color:#1989fa;font-size:14px}.zx-infinite-scroll-content{position:relative;transition:transform .25s;min-height:100%}.zx-infinite-scroll-content.no-transition{transition:none}.zx-content-loading-mask{position:absolute;top:0;left:0;right:0;bottom:0;background-color:#fffc;display:flex;align-items:center;justify-content:center;z-index:10;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.zx-load-more-indicator{display:flex;align-items:center;justify-content:center;min-height:50px;padding:16px 0;font-size:14px;color:#8c8f97}.zx-finished-text{color:#8c8f97;font-size:14px}.zx-error-text{color:#ee0a24;font-size:14px;cursor:pointer}.zx-error-text:active{opacity:.7}
|