分頁組件+Table表格+編程式導航

描述:

之前實現(xiàn)了自定義分頁組件,但是由于現(xiàn)實業(yè)務需求(需要將過濾項表單、分頁的offset和limit傳遞到路由中,即a?xxx=xxx&xxx=xxx)。因此對分頁組件進行了重新的設計。

分頁組件的重新設計

上一篇:Quasar的自定義分頁組件

之前的分頁組件主要是監(jiān)聽每頁顯示數(shù)量limit和偏移量offset的變化來觸發(fā)事件,進行回調(diào),在回調(diào)處發(fā)起網(wǎng)絡請求。

由于上述需求,在新的分頁組件中主要監(jiān)聽了頁面改變的監(jiān)聽事件,并通過頁面改變監(jiān)聽,觸發(fā)編程式導航,從而改變頁面路由,通過對頁面路由的更新來發(fā)起網(wǎng)絡請求,從而實現(xiàn)目標。

<!--
  by nickctang
  自定義分頁組件
  傳入?yún)?shù): pagination
  pagination包含偏移量、每頁顯示數(shù)量、和總數(shù)
  在進行分頁切換是,觸發(fā)編程式導航,導航到對應位置,并進行服務器請求,請求完畢后,將pagination回傳回來
 -->
<template>
  <div class="row q-my-md fs-14" >
    <span class="self-center col-auto q-pb-sm q-mt-md q-mr-md">共{{paginationModel.count}}條,第{{ page }} /{{max}}頁</span>
    <q-select
      class="col-auto self-center q-mr-md q-mt-md"
      style="width: 90px"
      v-model="paginationModel.limit"
      :options="perPageNumOptions"
    />
    <q-pagination class="col-auto q-mt-md" v-model="page"  :max="max" size="sm" boundary-links boundary-numbers :max-pages="10" direction-links ellipses @input="onPageChange"/>
  </div>
</template>

<script>

export default {
  name: 'CPagination',
  data () {
    return {
      perPageNumOptions: [
        {
          label: '每頁12條',
          value: 12
        },
        {
          label: '每頁24條',
          value: 24
        },
        {
          label: '每頁48條',
          value: 48
        },
        {
          label: '每頁96條',
          value: 96
        }
      ]
    }
  },
  computed: {
    page: {
      get: function () {
        if (this.paginationModel.count === 0) {
          return 0
        }
        // 偏移量/每頁顯示數(shù)量 向下取整 + 1
        return Math.floor(this.paginationModel.offset / this.paginationModel.limit) + 1
      },
      set: function (val) {
        // 頁碼改變動態(tài)的更新偏移量
        this.paginationModel.offset = (val - 1) * this.paginationModel.limit
      }
    },
    max: function () {
      // 最大頁數(shù),總數(shù)/每頁顯示數(shù)量,向上取整
      return Math.ceil(this.paginationModel.count / this.paginationModel.limit)
    },
    paginationModel: {// pagination的雙向綁定,可令外界的pagination來改變page
      get: function () {
        return this.pagination
      },
      set: function (val) {
        this.$emit('paginationChange', val)
      }
    }
  },
  model: {
    prop: 'pagination',
    event: 'paginationChange'
  },
  methods: {
    onPageChange: function (val) {
      console.log('pagechange ' + val)
      const query = JSON.parse(JSON.stringify(this.$route.query)) // 深度復制
      query.limit = this.paginationModel.limit
      query.offset = this.paginationModel.offset
      this.$router.push(
        {
          path: this.$route.path,
          query: query
        })
    }
  },
  props: {
    pagination: {
      offset: 0,
      limit: 12,
      count: 0
    }
  }
}
</script>

在父組件使用該分頁組件時,只需要傳入pagination即可,pagination是一個JSON對象,包含offset、limit、count。

在頁面進行網(wǎng)絡請求得到結(jié)果后,只需要改變pagination的內(nèi)容,即可動態(tài)的更新分頁組件。

分頁組件 + table + 編程式導航的結(jié)合使用

描述:
在父組件使用Table表格和分頁組件的過程中,一般還會存在過濾項表單,來對數(shù)據(jù)進行過濾請求,這些過濾項和分頁的offset、limit都需要當作路由的query參數(shù)。

實際上,該query參數(shù)模樣,相當于axios進行get請求時的params。因此將表單對應組件的v-model和請求所需的param定義成相同的名稱。

期間遇到的問題:
基本上還是多次請求的問題,在編程式導航過程中,如果沒有對應的更新分頁組件,由于count的變化,容易讓分頁組件響應編程式導航,而且由于沒有更新offset、limit,從而導致編程式導航成功響應,進行了二次網(wǎng)絡請求。

最終的解決:
對原來的分頁組件進行的更新。在每次獲取網(wǎng)絡請求結(jié)束后,動態(tài)的改變pagination中的count、limit、offset,從而基本觸發(fā)了編程式導航,但是由于路由的query沒有改變,而不會響應beforeRouteUpdate,也就不會再次響應網(wǎng)絡請求。

實現(xiàn)過程

  • 首先,定義table。
// html
<q-table
    no-data-label="沒有更多數(shù)據(jù)"
    :data="tableData"
    :columns="columns"
    :pagination.sync="paginationControl"
    :loading="tableLoading"
    name="id"
    color="secondary" >
    ...
    //vue
    tableLoading: false, // 表格加載狀態(tài)
    paginationControl: { rowsPerPage: 0 }, // 顯示全部
    tableData: [],
    columns: [{
        name: 'severity',
        required: true,
        align: 'left',
        label: '問題級別',
        field: 'severity'
    },...
    ],
  • 其次,覆蓋Table默認的分頁,采用新的分頁
// html
<template slot="bottom" slot-scope="props">
    <div class="col"></div>
    <c-pagination class="no-margin q-pb-sm" :pagination="pagination" />
</template>
// vue
// table+分頁組件所需
pagination: {
    offset: 0,
    limit: 12,
    count: 0
}
  • 此外,如果對過濾項進行點擊過濾,是進行的編程式路由導航操作,將過濾項格式化成為規(guī)定的路由query格式(為了保證和get請求params一致)。
// vue methods中
filterSearch: function () {
    this.$router.push({
        path: this.$route.path,
        query: filterToQueryFormat(this.filterForm) // 將過濾項格式化匹配到路由
    })
}
// filterToQueryFormat,引用的utils。包含了
/**
 * 篩選項格式化到url
 * @param {{篩選項表單}} filterForm
 * @return {{返回url的query}} query
 */
export function filterToQueryFormat (filterForm) {
      console.log('filterToQuery')
      let query = {}
      for (var key in filterForm) {
        if (key.endsWith('__in')) {
          query[key] = filterForm[key].toString()
        } else {
          query[key] = filterForm[key] || filterForm[key] === '' ? filterForm[key] : null
        }
      }
      query.offset = 0
      console.log(filterForm)
      console.log(query)
      console.log('filterToQuery end')
      return query
}
/**
 * url格式化匹配篩選項
 * @param {{url的query}} query
 * @param {{篩選項表單}}} filterFrom 由于是對象的引用,所以在此對filterForm進行改變即改變了外層的filterForm
 * @return {{返回url的query}} query
 */
export function queryTofilterFormat (query, filterForm) {
      console.log('queryToFilter')
      let filter = {}
      for (var key in filterForm) {
        if (key.endsWith('__in')) {
          filter[key] = query[key] ? query[key].split(',') : []
        } else {
          filter[key] = query[key] ? query[key] : null
        }
      }
      filter.limit = query.limit
      filter.offset = query.offset
      console.log(query)
      console.log(filter)
      console.log('queryToFilter end')
      return filter
}
  • 接下來,將過濾項表單定義為filterFrom,表單項所對應的具體組件v-model根據(jù)網(wǎng)絡請求param一一對應。
// html
<q-input :clearable="filterForm.file__icontains!=null" v-model="filterForm.file__icontains" float-label="所屬文件" />
q-btn color="secondary" size="sm" icon="search" label="篩選" @click="filterSearch" />
// vue
filterForm: { // 過濾項表單
    state__in: [],
    resolution__in: [],
    severity__in: [],
    is_tapdbug: null,
    file__icontains: null,
    author: null,
    ci_time__lte: null,
    ci_time__gte: null
}
  • 然后,每次進入頁面,在mounted中進行第一次請求。對頁面路由更新進行監(jiān)聽,從而發(fā)起請求。beforeRouteUpdate是只有頁面路由的params或query進行改變才會觸發(fā),原頁面復用,不進行頁面刷新。
// vue
mounted () {
    // 加載數(shù)據(jù)
    this.tableRequest(this.$route.query)
 },
 beforeRouteUpdate (to, from, next) {
    // 路由query更新觸發(fā)
    this.tableRequest(to.query)
    next()
  },
  • 最后,對數(shù)據(jù)請求的操作。
///
    /**
    * 表格數(shù)據(jù)請求
    * @param {{路由query參數(shù)}} query
    */
tableRequest: function (query) {
    this.tableLoading = true // 表格加載動畫
    // 判斷路由是否有l(wèi)imit和offset,如果沒有,默認12、0
    query.limit = query.limit ? parseInt(query.limit) : 12
    query.offset = query.offset ? parseInt(query.offset) : 0
    issues(this.$route.params.project_id, query).then(response => {
    // 請求成功后,改變分頁組件顯示
    this.pagination = {
    limit: query.limit,
    offset: query.offset,
    count: response.count
    }
    // 將路由的query匹配到過濾項表單中
    this.filterForm = queryTofilterFormat(query, this.filterForm)
    // 返回數(shù)據(jù)
    this.tableData = response.results
    // 關閉table加載狀態(tài)
    this.tableLoading = false
    }).catch(error => {
    this.tableLoading = false
    console.log(error)
    })
}

整個代碼

// 分頁組件
<!--
  by nickctang
  自定義分頁組件
  傳入?yún)?shù): pagination
  pagination包含偏移量、每頁顯示數(shù)量、和總數(shù)
  在進行分頁切換是,觸發(fā)編程式導航,導航到對應位置,并進行服務器請求,請求完畢后,將pagination回傳回來
 -->
<template>
  <div class="row q-my-md fs-14" >
    <span class="self-center col-auto q-pb-sm q-mt-md q-mr-md">共{{paginationModel.count}}條,第{{ page }} /{{max}}頁</span>
    <q-select
      class="col-auto self-center q-mr-md q-mt-md"
      style="width: 90px"
      v-model="paginationModel.limit"
      :options="perPageNumOptions"
    />
    <q-pagination class="col-auto q-mt-md" v-model="page"  :max="max" size="sm" boundary-links boundary-numbers :max-pages="10" direction-links ellipses @input="onPageChange"/>
  </div>
</template>

<script>

export default {
  name: 'CPagination',
  data () {
    return {
      perPageNumOptions: [
        {
          label: '每頁12條',
          value: 12
        },
        {
          label: '每頁24條',
          value: 24
        },
        {
          label: '每頁48條',
          value: 48
        },
        {
          label: '每頁96條',
          value: 96
        }
      ]
    }
  },
  computed: {
    page: {
      get: function () {
        if (this.paginationModel.count === 0) {
          return 0
        }
        // 偏移量/每頁顯示數(shù)量 向下取整 + 1
        return Math.floor(this.paginationModel.offset / this.paginationModel.limit) + 1
      },
      set: function (val) {
        // 頁碼改變動態(tài)的更新偏移量
        this.paginationModel.offset = (val - 1) * this.paginationModel.limit
      }
    },
    max: function () {
      // 最大頁數(shù),總數(shù)/每頁顯示數(shù)量,向上取整
      return Math.ceil(this.paginationModel.count / this.paginationModel.limit)
    },
    paginationModel: {// pagination的雙向綁定,可令外界的pagination來改變page
      get: function () {
        return this.pagination
      },
      set: function (val) {
        this.$emit('paginationChange', val)
      }
    }
  },
  model: {
    prop: 'pagination',
    event: 'paginationChange'
  },
  methods: {
    onPageChange: function (val) {
      console.log('pagechange ' + val)
      const query = JSON.parse(JSON.stringify(this.$route.query)) // 深度復制
      query.limit = this.paginationModel.limit
      query.offset = this.paginationModel.offset
      this.$router.push(
        {
          path: this.$route.path,
          query: query
        })
    }
  },
  props: {
    pagination: {
      offset: 0,
      limit: 12,
      count: 0
    }
  }
}
</script>
// issue_list
<template>
  <q-page>
    <proj-base-scroll content_class="p-max-w">
      <p class="q-title q-mb-md">
        <span class="q-mr-md">問題列表</span>
      <q-table
        no-data-label="沒有更多數(shù)據(jù)"
        :data="tableData"
        :columns="columns"
        selection="multiple"
        :selected.sync="selectedSecond"
        :pagination.sync="paginationControl"
        :loading="tableLoading"
        name="id"
        color="secondary" >
        <q-tr slot="body" slot-scope="props" class="textPrimary" :props="props">
          <q-td auto-width>
            <q-checkbox color="secondary" v-model="props.selected" />
          </q-td>
          <q-td key="severity" :props="props">
            <q-chip dense square :color="CODELINT_STATUS.SEVERITY_CHOICES[props.row.severity].color">
              {{ CODELINT_STATUS.SEVERITY_CHOICES[props.row.severity].label }}
            </q-chip>
          </q-td>
          <q-td key="file" :props="props">
            <q-btn flat color="primary" size="sm" no-caps class="no-padding">
              {{ props.row.file.slice(props.row.file.lastIndexOf('/')) }}
            </q-btn>
            <q-tooltip anchor="top middle" self="bottom middle" :offset="[10, 10]">
                {{ props.row.file }}
            </q-tooltip>
          </q-td>
          <q-td key="revision" :props="props">
            <p class="q-mb-sm">
              {{ props.row.revision.substring(0, 8) }}
              <q-tooltip anchor="top middle" self="bottom middle" :offset="[10, 10]">
                {{ props.row.revision }}
              </q-tooltip>
            </p>
            <p class="no-margin textTertiary">{{ formatDatetime(props.row.ci_time) }}</p>
          </q-td>
          <q-td key="ruleinfo" :props="props">
            <p class="q-mb-sm">{{ props.row.checkrule__display_name }}</p>
            <p class="no-margin textTertiary">{{ props.row.msg }}</p>
          </q-td>
          <q-td key="state" :props="props">
            <q-chip dense square :color="CODELINT_STATUS.STATE_CHOICES[props.row.state].color">
              {{ CODELINT_STATUS.STATE_CHOICES[props.row.state].label }}
            </q-chip>
          </q-td>
          <q-td key="author" :props="props">{{ props.row.author }}</q-td>
          <q-td key="is_tapdbug" :props="props">{{ props.row.is_tapdbug?'已提單':'未提單' }}</q-td>
        </q-tr>
        <template slot="top" slot-scope="props">
          <div class="row col q-mb-md fs-14 gutter-sm">
            <div class="col-md-2 col-xs-4">
              <q-select
                multiple
                clearable
                v-model="filterForm.state__in"
                :options="CODELINT_STATUS.STATE_OPTIONS"
                float-label="狀態(tài)"
              />
            </div>
            <div class="col-md-2 col-xs-4">
              <q-select
                multiple
                clearable
                v-model="filterForm.resolution__in"
                :options="CODELINT_STATUS.RESOLUTION_OPTIONS"
                float-label="解決方法"
              />
            </div>
            <div class="col-md-2 col-xs-4">
              <q-select
                multiple
                clearable
                v-model="filterForm.severity__in"
                :options="CODELINT_STATUS.SEVERITY_OPTIONS"
                float-label="問題級別"
              />
            </div>
            <div class="col-md-2 col-xs-4">
              <q-input :clearable="filterForm.file__icontains!=null" v-model="filterForm.file__icontains" float-label="所屬文件" />
            </div>
            <div class="col-md-2 col-xs-4">
              <q-input :clearable="filterForm.author!=null" v-model="filterForm.author" float-label="責任人" />
            </div>
            <div class="col-md-2 col-xs-4">
              <q-select
                class="col-md-2 col-xs-4"
                clearable
                v-model="filterForm.is_tapdbug"
                :options="tapdOptions"
                float-label="xx情況"
              />
            </div>
            <div class="col-md-2 col-xs-4">
              <q-datetime float-label="引入時間>="  v-model="filterForm.ci_time__gte" type="datetime" clearable />
            </div>
            <div class="col-md-2 col-xs-4">
              <q-datetime float-label="引入時間<="  v-model="filterForm.ci_time__lte" type="datetime" clearable />
            </div>
            <div class="col self-center text-right">
              <q-btn color="secondary" :disabled="selectedSecond.length==0" size="sm" label="xxx" class="q-mr-md" />
              <q-btn color="secondary" size="sm" icon="search" label="篩選" @click="filterSearch" />
            </div>
          </div>
        </template>
        <template slot="bottom" slot-scope="props">
          <div class="col"></div>
          <c-pagination class="no-margin q-pb-sm" :pagination="pagination" />
        </template>
      </q-table>
    </proj-base-scroll>
  </q-page>
</template>

<style lang="stylus">
</style>

<script>
import projBaseScroll from 'src/components/codeproj/projBaseScroll'
import { CODELINT_STATUS } from '@/libs/status'
import { filterToQueryFormat, queryTofilterFormat } from '@/libs/utils'
import { mapState } from 'vuex'
import { date } from 'quasar'
import { issues } from '@/service/xxx'
export default {
  components: {
    projBaseScroll
  },
  data () {
    return {
      filterForm: { // 過濾項表單
        state__in: [],
        resolution__in: [],
        severity__in: [],
        is_tapdbug: null,
        file__icontains: null,
        author: null,
        ci_time__lte: null,
        ci_time__gte: null
      },
      // table+分頁組件所需
      pagination: {
        offset: 0,
        limit: 12,
        count: 0
      },
      tableLoading: false, // 表格加載狀態(tài)
      paginationControl: { rowsPerPage: 0 }, // 顯示全部
      tableData: [],
      columns: [
       ....
      ],
      CODELINT_STATUS: CODELINT_STATUS,
      tapdOptions: [
        {
          label: '是',
          value: true
        }, {
          label: '否',
          value: false
        }
      ],
      selectedSecond: []
    }
  },
  mounted () {
    // 加載數(shù)據(jù)
    this.tableRequest(this.$route.query)
  },
  computed: mapState({
    project: state => state.codeproj.project
  }),
  beforeRouteUpdate (to, from, next) {
    // 路由query更新觸發(fā)
    this.tableRequest(to.query)
    next()
  },
  methods: {
    filterSearch: function () {
      this.$router.push({
        path: this.$route.path,
        query: filterToQueryFormat(this.filterForm) // 將過濾項匹配到路由
      })
    },
    /**
     * 表格數(shù)據(jù)請求
     * @param {{路由query參數(shù)}} query
     */
    tableRequest: function (query) {
      this.tableLoading = true // 表格加載動畫
      // 判斷路由是否有l(wèi)imit和offset,如果沒有,默認12、0
      query.limit = query.limit ? parseInt(query.limit) : 12
      query.offset = query.offset ? parseInt(query.offset) : 0
      issues(this.$route.params.project_id, query).then(response => {
        // 請求成功后,改變分頁組件顯示
        this.pagination = {
          limit: query.limit,
          offset: query.offset,
          count: response.count
        }
        // 將路由的query匹配到過濾項表單中
        this.filterForm = queryTofilterFormat(query, this.filterForm)
        // 返回數(shù)據(jù)
        this.tableData = response.results
        // 關閉table加載狀態(tài)
        this.tableLoading = false
      }).catch(error => {
        this.tableLoading = false
        console.log(error)
      })
    },
    /**
     * 格式化時間
     */
    formatDatetime: function (datetime) {
      return date.formatDate(datetime, 'YYYY-MM-DD HH:mm')
    }
  }
}
</script>

// utils
/**
 * 篩選項格式化到url
 * @param {{篩選項表單}} filterForm
 * @return {{返回url的query}} query
 */
export function filterToQueryFormat (filterForm) {
  console.log('filterToQuery')
  let query = {}
  for (var key in filterForm) {
    if (key.endsWith('__in')) {
      query[key] = filterForm[key].toString()
    } else {
      query[key] = filterForm[key] || filterForm[key] === '' ? filterForm[key] : null
    }
  }
  query.offset = 0
  console.log(filterForm)
  console.log(query)
  console.log('filterToQuery end')
  return query
}

/**
 * url格式化匹配篩選項
 * @param {{url的query}} query
 * @param {{篩選項表單}}} filterFrom 由于是對象的引用,所以在此對filterForm進行改變即改變了外層的filterForm
 * @return {{返回url的query}} query
 */
export function queryTofilterFormat (query, filterForm) {
  console.log('queryToFilter')
  let filter = {}
  for (var key in filterForm) {
    if (key.endsWith('__in')) {
      filter[key] = query[key] ? query[key].split(',') : []
    } else {
      filter[key] = query[key] ? query[key] : null
    }
  }
  filter.limit = query.limit
  filter.offset = query.offset
  console.log(query)
  console.log(filter)
  console.log('queryToFilter end')
  return filter
}

最后

代碼感覺還是不夠精簡,雖然實現(xiàn)了一定的流程化,但是感覺還有其它改進過程,望指正。。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,568評論 19 139
  • 分頁 REST framework 包含對可定制分頁樣式的支持。這使你可以將較大的結(jié)果集分成單獨的數(shù)據(jù)頁面。分頁 ...
    入間閱讀 1,387評論 0 2
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,058評論 25 709
  • 用于 @interface 或 @protocol將類或協(xié)議的名字在編譯時指定成另一個名字:
    OneByte閱讀 579評論 0 1
  • 這是一部講述一個慈愛父親與其孤獨癥兒子間感人故事的電影,看完電影后刻意翻閱了孤獨癥的定義。 孤獨癥:又稱自閉癥,也...
    麥田守望者lzu閱讀 512評論 0 3

友情鏈接更多精彩內(nèi)容