| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192 |
- /**
- * 列表页状态保持工具(筛选条件 / 排序 / 页码)
- *
- * 解决的问题:列表页 → 编辑/详情页 → 返回,查询条件和页码丢失。
- * 两个层次互相配合:
- * 1. URL query 承载列表状态:进入编辑页时把状态参数带过去,返回时原样带回,
- * 天然支持刷新、分享、深链(Layout.vue 的 keep-alive 兜不住时也能还原);
- * 2. Layout.vue 的 <keep-alive :include="cachedPages">:直接保住组件实例,
- * 表格勾选、滚动位置、弹窗可见性等 DOM 态一起保留,返回零延迟。
- *
- * 页面接入约定(以 ProductManage 为例,见 ListStateMixin):
- * - data 中必须有 filters / pagination 两个字段(filter 键名即 URL 参数名)
- * - 混入 SortMixin 时有 sortState 字段,排序状态也会自动同步
- */
- // 列表页默认分页大小(与各页 el-pagination 默认 page-size 保持一致)
- export const DEFAULT_PAGE_SIZE = 20
- // 允许的每页条数(写进 URL 前校验,避免非法值)
- export const ALLOWED_PAGE_SIZES = [5, 10, 15, 20, 50, 100]
- /**
- * 把列表状态序列化为 URL query 对象(空值 / 默认值省略,保持 URL 简洁)
- * @param {object} filters 筛选对象,如 { keyword: 'xx', status: 'on_shelf' }
- * @param {object} pagination 分页对象,如 { page: 3, size: 20 }
- * @param {Array} sortState SortMixin 的排序状态,如 [{ column: 'name', order: 'asc' }]
- * @returns {object} 可传给 this.$router.replace / push 的 query
- */
- export function buildListQuery(filters, pagination, sortState) {
- const query = {}
- if (filters) {
- Object.keys(filters).forEach(k => {
- const v = filters[k]
- if (v !== undefined && v !== null && v !== '') query[k] = String(v)
- })
- }
- if (pagination) {
- if (pagination.page && pagination.page > 1) query.page = String(pagination.page)
- if (pagination.size && pagination.size !== DEFAULT_PAGE_SIZE) query.size = String(pagination.size)
- }
- if (sortState && sortState.length > 0) {
- query.sort = sortState.map(s => s.column + ':' + s.order).join(',')
- }
- return query
- }
- /**
- * 从 URL query 还原列表状态(缺失字段回退到传入的默认值)
- * @param {object} query this.$route.query
- * @param {object} defaults { filters: {...}, size: 20 },filters 每键的兜底值取自页面初始值
- * @returns {{ filters: object, page: number, size: number, sort: Array }}
- */
- export function parseListQuery(query, defaults) {
- const d = defaults || {}
- const baseFilters = d.filters || {}
- const filters = {}
- Object.keys(baseFilters).forEach(k => {
- const v = query[k]
- filters[k] = (v !== undefined && v !== null && v !== '') ? v : baseFilters[k]
- })
- const sort = []
- if (query.sort) {
- query.sort.split(',').forEach(pair => {
- const i = pair.indexOf(':')
- if (i > 0) {
- const column = pair.slice(0, i)
- const order = pair.slice(i + 1)
- if (order === 'asc' || order === 'desc') sort.push({ column, order })
- }
- })
- }
- const page = parseInt(query.page, 10)
- const size = parseInt(query.size, 10)
- return {
- filters,
- page: page > 0 ? page : 1,
- size: ALLOWED_PAGE_SIZES.indexOf(size) > -1 ? size : (d.size || DEFAULT_PAGE_SIZE),
- sort
- }
- }
- /**
- * 判断两个 query 对象是否等价(键集合与键值都一致),
- * 用于避免筛选无变化时产生无意义的 router.replace 导航。
- */
- export function sameQuery(a, b) {
- const keysA = Object.keys(a)
- const keysB = Object.keys(b)
- if (keysA.length !== keysB.length) return false
- return keysA.every(k => String(a[k] || '') === String(b[k] || ''))
- }
|