usePagination.js
6.33 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
/**
* 分页逻辑组合式函数
* @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)的字段,以及空页
const filtered_pages = computed(() => {
if (!pages_raw.value.length) {
const keys = formDataRef.value.filter(i => !i.component_props?.disabled).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
}))
.filter(keys => keys.length > 0)
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 = []
formDataRef.value.forEach(item => {
const tag = item.component_props?.tag
// 分隔符 paginator 作为分页分组边界
if (tag === 'paginator') {
if (cur.length) result.push(cur)
cur = []
return
}
cur.push(item.key)
})
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' }
)
// 分页导航文案与禁用状态(按当前页字段的 component_props 提供的 mock 值)
const page_nav = computed(() => {
const idx = current_page_index.value
const keys = filtered_pages.value[idx] || []
let prev_text = '上一页'
let next_text = '下一页'
let prev_disabled = false
for (let k of keys) {
const item = formDataRef.value.find(i => i.key === k)
if (item && item.component_props) {
if (item.component_props.back_title) prev_text = item.component_props.back_title
if (item.component_props.next_title) next_text = item.component_props.next_title
if (typeof item.component_props.is_back === 'boolean') prev_disabled = item.component_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,
}
}