usePagination.js
6.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/**
* 分页逻辑组合式函数
* @description 提供分页的原始分组、过滤后分组、当前页索引、是否启用分页、可见字段等
* @param {import('vue').Ref<Array>} formDataRef 表单字段数据的 ref
* @returns {{pages_raw: import('vue').Ref<Array>, filtered_pages: import('vue').ComputedRef<Array>, visible_keys: import('vue').ComputedRef<Array>, visible_form_data: import('vue').ComputedRef<Array>, current_page_index: import('vue').Ref<number>, enable_pagination: import('vue').Ref<boolean>, buildPages: Function, page_nav: import('vue').ComputedRef<Object>}}
*/
import { ref, computed, watch, nextTick } from 'vue'
import { showToast } from 'vant'
export function usePagination(formDataRef, options = {}) {
const { myFormRef, validOther, afterSwitch } = options
// 分页原始分组
const pages_raw = ref([])
// 是否启用分页
const enable_pagination = ref(false)
// 当前页索引
const current_page_index = ref(0)
// 过滤后的分页:剔除被隐藏(disabled)的字段,但保留空页占位,确保分页总数与 paginator 一致
const filtered_pages = computed(() => {
if (!pages_raw.value.length) {
const keys = formDataRef.value
.filter(i => !i.component_props?.disabled && i.component_props?.tag !== 'paginator')
.map(i => i.key)
return [keys]
}
const result = pages_raw.value
.map(keys => keys.filter(k => {
const f = formDataRef.value.find(i => i.key === k)
return f && !f.component_props?.disabled
}))
// 不再删除空页,以保证总页数符合分页符定义
return result.length ? result : [[]]
})
// 当前页可见的 key 列表
const visible_keys = computed(() => filtered_pages.value[current_page_index.value] || [])
// 当前页可见的字段数据
const visible_form_data = computed(() => {
const set = new Set(visible_keys.value)
return formDataRef.value.filter(i => set.has(i.key) && !i.component_props?.disabled)
})
/**
* 构建分页分组
* @description 优先按 paginator 分组;若仅一组则按固定大小分片
*/
const buildPages = () => {
const result = []
let cur = []
// 连续的 paginator 需要忽略,避免出现空分页
let last_was_paginator = false
formDataRef.value.forEach(item => {
const tag = item.component_props?.tag
// 分隔符 paginator 作为分页分组边界
if (tag === 'paginator') {
// 若前一个也是 paginator,则忽略本次(不产生空页)
if (last_was_paginator) return
if (cur.length) result.push(cur)
cur = []
last_was_paginator = true
return
}
cur.push(item.key)
last_was_paginator = false
})
if (cur.length) result.push(cur)
if (result.length <= 1) {
const size = 8
const keys = formDataRef.value.filter(i => i.component_props?.tag !== 'paginator').map(i => i.key)
const chunked = []
for (let i = 0; i < keys.length; i += size) {
chunked.push(keys.slice(i, i + size))
}
pages_raw.value = chunked.length ? chunked : [keys]
} else {
pages_raw.value = result
}
}
// 监听过滤后的分页变化,纠正当前页索引并设置启用状态
watch(
() => filtered_pages.value,
(newPages) => {
const last = newPages.length - 1
if (current_page_index.value > last) {
current_page_index.value = last >= 0 ? last : 0
}
enable_pagination.value = newPages.length > 1
},
{ flush: 'post' }
)
/**
* 分页导航配置
* @description 仅从 tag 为 paginator 的字段中读取导航文案与可返回状态
* @returns {{prev_text: string, next_text: string, prev_disabled: boolean}}
*/
const page_nav = computed(() => {
// 默认文案与状态
let prev_text = '上一页'
let next_text = '下一页'
let prev_disabled = false
// 仅查找 tag=paginator 的字段并提取其 component_props
const paginator_item = formDataRef.value.find(i => i?.component_props?.tag === 'paginator')
if (paginator_item && paginator_item.component_props) {
const props = paginator_item.component_props
if (props.back_title) prev_text = props.back_title
if (props.next_title) next_text = props.next_title
// is_back 表示是否允许返回
if (typeof props.is_back === 'boolean') prev_disabled = !props.is_back
}
return { prev_text, next_text, prev_disabled }
})
/**
* 校验当前页
* @returns {Promise<boolean>} 是否通过校验
*/
const validateCurrentPage = async () => {
try {
await myFormRef?.value?.validate()
} catch (e) {
const err = Array.isArray(e?.errors) ? e.errors[0] : null
const name = err?.name || ''
let error_label = ''
formDataRef.value.forEach(item => { if (item.key === name) { error_label = item.component_props?.label || name } })
const msg = err?.message || '验证失败'
showToast((error_label || '表单') + ': ' + msg)
return false
}
const other = typeof validOther === 'function' ? validOther() : { status: true }
if (!other.status) {
showToast('验证失败')
return false
}
return true
}
/**
* 上一页
* @description 切换到上一页并执行页面切换后的回调
*/
const handlePrev = async () => {
if (current_page_index.value === 0) return
current_page_index.value -= 1
if (typeof afterSwitch === 'function') await afterSwitch()
}
/**
* 下一页
* @description 验证当前页,通过后切换到下一页并执行页面切换后的回调
*/
const handleNext = async () => {
const ok = await validateCurrentPage()
if (!ok) return
if (current_page_index.value < filtered_pages.value.length - 1) {
current_page_index.value += 1
if (typeof afterSwitch === 'function') await afterSwitch()
await nextTick()
window.scrollTo({ top: 0 })
}
}
/**
* 最后一页提交
* @description 触发表单提交
*/
const handleSubmit = () => {
myFormRef?.value?.submit()
}
return {
pages_raw,
filtered_pages,
visible_keys,
visible_form_data,
current_page_index,
enable_pagination,
buildPages,
page_nav,
validateCurrentPage,
handlePrev,
handleNext,
handleSubmit,
}
}