刻Element UI分頁(yè)組件:Qt桌面應(yīng)用的組件化實(shí)踐)
簡(jiǎn)介一份參考Element UI分頁(yè)交互風(fēng)格實(shí)現(xiàn)的Qt分頁(yè)組件源碼面向熟悉C/Qt、需要在桌面應(yīng)用中集成翻頁(yè)控件的開(kāi)發(fā)者適合數(shù)據(jù)表格、列表瀏覽及后臺(tái)管理端的桌面端適配。資源共2個(gè)文件分別對(duì)應(yīng)組件實(shí)現(xiàn)與頭文件壓縮包僅3KB代碼精簡(jiǎn)無(wú)冗余依賴。組件封裝了PageNavigator類包含當(dāng)前頁(yè)、總頁(yè)數(shù)、每頁(yè)條目數(shù)等核心屬性提供上一頁(yè)/下一頁(yè)、跳轉(zhuǎn)至指定頁(yè)、首尾頁(yè)快捷操作等槽函數(shù)并通過(guò)pageChanged信號(hào)與外部界面解耦同時(shí)預(yù)留QSS樣式入口可輕松復(fù)現(xiàn)Element UI的簡(jiǎn)潔觀感。已有1080人學(xué)習(xí)下載。借助這份小體積樣例可快速掌握自定義QWidget封裝、信號(hào)槽通信、QSpinBox與QComboBox聯(lián)動(dòng)等Qt實(shí)用技巧稍作擴(kuò)展即可應(yīng)用到真實(shí)項(xiàng)目。1. 這個(gè)Qt分頁(yè)組件能干什么把Element UI的分頁(yè)體驗(yàn)搬進(jìn)桌面應(yīng)用如果你的 Qt 桌面應(yīng)用需要一套分頁(yè)組件又正好看著 Element UI 的 el-pagination 流口水那這份資源就是答案。它用 QML JavaScript開(kāi)發(fā)語(yǔ)言層面是 Qt Quick 的聲明式語(yǔ)法復(fù)刻了 Element UI 分頁(yè)組件的主要能力layout 任意排列、帶省略號(hào)的頁(yè)碼、快速跳轉(zhuǎn)、每頁(yè)條數(shù)下拉前端組件的屬性、事件、樣式都可用類似方式調(diào)整。適合兩類人一類是 Qt 桌面開(kāi)發(fā)想在項(xiàng)目里引入組件化思路另一類是熟悉前端 Vue/Element UI、需要把既有原型平移到 QML 場(chǎng)景的開(kāi)發(fā)者。讀完這篇筆記你能拿到可以直接編譯運(yùn)行的分頁(yè)組件源碼并知道怎么改造成自己業(yè)務(wù)里的黑匣子。2. 技術(shù)棧與組件設(shè)計(jì)用 QML JavaScript 復(fù)刻 el-pagination 的 API 層2.1 為什么選 QML 而不是 Widgets選 QMLQt Quick而不是傳統(tǒng) Widgets核心原因是組件化表達(dá)能力和 JavaScript 生態(tài)。Widgets 里做分頁(yè)控件通常是在 QWidget 里手動(dòng) layout、連接 clicked 信號(hào)每個(gè)按鈕都是獨(dú)立對(duì)象狀態(tài)管理依賴 C 撐著。而 QML 的聲明式 UI 寫法本身就接近 Vue 模板一個(gè) Item 包含屬性、信號(hào)、嵌套子組件界面狀態(tài)通過(guò)屬性綁定自動(dòng)刷新。前端背景的人看這套組件會(huì)非常親切改樣式也基本不用碰 C。另一個(gè)原因是 JavaScript 邏輯跑在 QML 內(nèi)部不需要為分頁(yè)這種輕邏輯跨 QObject 通信。頁(yè)碼窗口裁剪、layout 渲染順序、跳頁(yè)輸入校驗(yàn)這些在 QML 里用幾十行 JS 就能寫完。雖然也可以把分頁(yè)算法寫在 C 里再暴露給 QML但從組件復(fù)用角度看純 QML JS 的實(shí)現(xiàn)更貼近“下載一個(gè) .qml 文件就能用”的前端組件習(xí)慣如果你所在的是大規(guī)模 C 工程后續(xù)也可以把算法層整體遷到 C接口保持不變。這個(gè)項(xiàng)目把重心放在“組件庫(kù)化、可復(fù)用”這個(gè)目標(biāo)上。再說(shuō)說(shuō)為什么要參考 Element UI 而不是自己拍腦袋設(shè)計(jì)。el-pagination 在 Vue 生態(tài)里被大量項(xiàng)目驗(yàn)證過(guò)它的兩個(gè)設(shè)計(jì)很值錢layout 字符串驅(qū)動(dòng)布局以及 pager 省略號(hào)算法。這兩種能力恰好都能在 QML 里自然表達(dá)——layout 對(duì)應(yīng) Repeater 按順序加載子組件省略號(hào)對(duì)應(yīng)數(shù)組裁剪。把別人驗(yàn)證過(guò)的交互邏輯平移過(guò)來(lái)能少踩很多不知道從哪里冒出來(lái)的交互坑。2.2 對(duì)齊 el-pagination 的屬性與事件組件對(duì)外 API 盡量照搬 Element UI 的命名讓熟悉前端的同事接手時(shí)不用重新學(xué)一遍。屬性和原型的對(duì)應(yīng)關(guān)系如下Element UI 屬性本項(xiàng)目 QML 屬性類型說(shuō)明current-pagecurrentPageint當(dāng)前頁(yè)從 1 開(kāi)始page-sizepageSizeint每頁(yè)條數(shù)totaltotalint數(shù)據(jù)總量page-countpageCountint只讀由 total 與 pageSize 計(jì)算pager-countpagerCountint頁(yè)碼按鈕數(shù)量默認(rèn) 7layoutlayoutArray控制項(xiàng)顯示順序如 total、prev、pagerpage-sizespageSizesArray每頁(yè)條數(shù)下拉選項(xiàng)事件上current-change 對(duì)應(yīng)change(currentPage)size-change 對(duì)應(yīng)sizeChange(newPageSize, currentPage)。設(shè)計(jì)時(shí)我把“狀態(tài)變化”和“用戶操作事件”分開(kāi)currentPage 屬性變化只負(fù)責(zé)刷新組件內(nèi)部外觀change 信號(hào)只在用戶真正點(diǎn)擊、跳轉(zhuǎn)時(shí)發(fā)射一次。這個(gè)區(qū)分在后面第 5 章的信號(hào)重復(fù)觸發(fā)坑里會(huì)重點(diǎn)展開(kāi)也是整個(gè)組件不容易踩坑的關(guān)鍵。2.3 目錄結(jié)構(gòu)pagination-qt/ └── Pagination.qml # 組件主體屬性、信號(hào)、算法、渲染 └── pager-utils.js # 純函數(shù)頁(yè)碼序列生成與邊界裁剪 └── demo/ └── main.qml # 演示頁(yè)模擬列表 分頁(yè)聯(lián)動(dòng) └── main.cpp # Qt Quick 入口 └── pagination.pro # qmake 工程文件把 pager-utils.js 獨(dú)立出來(lái)是為了讓頁(yè)碼算法可以在 Node 里單獨(dú)驗(yàn)證第 6 章會(huì)用斷言向量鎖住它防止后續(xù)改樣式把算法改壞。Pagination.qml 只負(fù)責(zé)組件邊界和布局內(nèi)部調(diào)用 utils 的函數(shù)demo 是一個(gè)模擬 86 條數(shù)據(jù)的列表切頁(yè)時(shí)重新填充 ListModel方便直接觀察分頁(yè)效果。main.cpp 是標(biāo)準(zhǔn) Qt Quick 啟動(dòng)入口不做額外邏輯保證跨平臺(tái)編譯簡(jiǎn)單。3. 核心模塊拆解分頁(yè)請(qǐng)求從點(diǎn)擊到刷新的完整鏈路3.1 對(duì)外接口屬性、信號(hào)與跳轉(zhuǎn)函數(shù)組件最外層是 Item直接暴露與 el-pagination 對(duì)齊的屬性并定義兩個(gè)信號(hào)。所有頁(yè)碼跳轉(zhuǎn)都收斂到j(luò)ump()函數(shù)不在按鈕 onClicked 里直接改 currentPage這是整個(gè)組件行為可控的起點(diǎn)。import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 Item { id: root property int currentPage: 1 property int pageSize: 10 property int total: 0 property int pagerCount: 7 property var pageSizes: [10, 20, 50, 100] property var layout: [total, prev, pager, next, jumper] readonly property int pageCount: total 0 ? Math.ceil(total / pageSize) : 1 signal change(int page) signal sizeChange(int newPageSize, int page) function jump(page) { var target Math.max(1, Math.min(page, root.pageCount)) if (root.currentPage target) { return } root.currentPage target root.change(root.currentPage) } property var pagerItems: [] function updatePagerItems() { root.pagerItems buildPager(root.pageCount, root.currentPage, root.pagerCount) } onCurrentPageChanged: updatePagerItems() onPageCountChanged: updatePagerItems() Component.onCompleted: updatePagerItems() }jump 先做 clamp 到[1, pageCount]再判斷目標(biāo)頁(yè)沒(méi)變時(shí)直接 return避免無(wú)意義的信號(hào)。change 信號(hào)只在 jump 里發(fā)射onCurrentPageChanged只負(fù)責(zé)重建 pagerItems。readonly pageCount 用total 0判斷total 為 0 時(shí)兜底為 1防止出現(xiàn) 0 頁(yè)或 NaN 頁(yè)。這里故意讓外部拿到 total 后只需賦值不用關(guān)心 pageCount 怎么算。3.2 頁(yè)碼序列算法省略號(hào)是怎么算出來(lái)的這是分頁(yè)組件最有技術(shù)含量的部分。el-pagination 在頁(yè)碼總數(shù)超過(guò) pager-count 時(shí)會(huì)保留第 1 頁(yè)和最后一頁(yè)把當(dāng)前頁(yè)附近的頁(yè)碼作為窗口顯示窗口兩邊用省略號(hào)占位。下面這個(gè)函數(shù)實(shí)現(xiàn)了同樣的策略并且不依賴任何 QML 對(duì)象方便單獨(dú)測(cè)試。// pager-utils.js function buildPager(pageCount, currentPage, pagerCount) { var count Math.max(5, pagerCount) if (!pageCount || pageCount 1) { return [{ type: page, label: 1 }] } if (pageCount count) { var all [] for (var i 1; i pageCount; i) { all.push({ type: page, label: i }) } return all } var half Math.floor(count / 2) var left currentPage - half var right currentPage half if (left 1) left 1 if (right pageCount) right pageCount var items [{ type: page, label: 1 }] var leftMore left 2 var rightMore right pageCount - 1 if (leftMore) { items.push({ type: more, label: ... }) } for (var j left; j right; j) { if (j 2 j pageCount - 1) { items.push({ type: page, label: j }) } } if (rightMore) { items.push({ type: more, label: ... }) } items.push({ type: page, label: pageCount }) return items } if (typeof module ! undefined) { module.exports { buildPager } }邏輯說(shuō)明pageCount 小于等于 pagerCount 時(shí)干脆全部渲染不做省略號(hào)否則保留第 1 頁(yè)和最后 1 頁(yè)把以當(dāng)前頁(yè)為中心的窗口頁(yè)渲染在中間窗口不足時(shí)貼住邊界。leftMore 和 rightMore 分別決定左右是否出現(xiàn)省略號(hào)。參數(shù) pageCount、currentPage、pagerCount 都由組件屬性傳入函數(shù)本身沒(méi)有 QML 依賴所以能在 Node 里直接跑斷言。這個(gè)窗口算法在極端貼近邊界時(shí)按鈕數(shù)量可能會(huì)少于 pagerCount常見(jiàn)前端分頁(yè)庫(kù)也有類似表現(xiàn)對(duì)業(yè)務(wù)沒(méi)什么影響后續(xù)要嚴(yán)格固定按鈕數(shù)量基于這個(gè)函數(shù)再裁剪 width 即可。3.3 layout 渲染像寫 Vue 模板一樣編排控制項(xiàng)layout 值是字符串?dāng)?shù)組順序決定控件從左到右的排列。實(shí)現(xiàn)方式是外層 Row 套一個(gè) RepeaterRepeater 的 delegate 是一個(gè) Loader根據(jù) switch 匹配結(jié)果加載對(duì)應(yīng)的組件。Row { id: layoutRow spacing: 12 layoutDirection: Qt.LeftToRight Repeater { model: root.layout delegate: Loader { property string controlType: String(modelData).trim() width: item ? item.implicitWidth : 0 height: item ? item.implicitHeight : 0 sourceComponent: { switch (controlType) { case total: return totalComponent case prev: return prevComponent case pager: return pagerComponent case next: return nextComponent case jumper: return jumperComponent case sizes: return sizesComponent default: return undefined } } } } }把String(modelData).trim()放在 switch 之前是為了處理從配置文件或外部 JSON 傳入帶空格、換行的字符串。如果不做這一步“ pager ”和“pager”會(huì)匹配不上控件直接不渲染這種問(wèn)題在視覺(jué)上很難排查。Loader 的 width 和 height 取實(shí)際子對(duì)象的 implicitWidth / implicitHeight確保 Row 能計(jì)算出正確布局寬度。頁(yè)碼列表本身也是一個(gè) Row 套 Repeater數(shù)據(jù)源是 pagerItemsComponent { id: pagerComponent Row { spacing: 4 Repeater { model: root.pagerItems delegate: Button { text: model.label checkable: model.type page checked: model.type page model.label root.currentPage enabled: model.type page onClicked: { if (model.type page) { root.jump(model.label) } } } } } }這里的參數(shù)說(shuō)明checkable 只對(duì)數(shù)字頁(yè)碼生效省略號(hào)項(xiàng)是 disabled 狀態(tài)視覺(jué)上是置灰的避免用戶點(diǎn)“...”后沒(méi)反應(yīng)以為是按鈕壞了。最終點(diǎn)擊都走 root.jump所以 currentPage 的越界保護(hù)、信號(hào)發(fā)射都集中在 3.1 節(jié)里delegate 不需要知道跳轉(zhuǎn)細(xì)節(jié)。Button 用的是 QtQuick.Controls 2自帶 hover 和 focus 外觀省去手寫鼠標(biāo)事件。3.4 事件處理prev、next、jumper 與 sizes上一頁(yè)和下一頁(yè)按鈕只是把 currentPage 加減 1 再走 jump唯一要注意的是在邊界時(shí)按鈕應(yīng)該禁用而不是點(diǎn)了后沒(méi)反應(yīng)。跳頁(yè)輸入框用 TextField IntValidator按下回車時(shí)解析輸入并調(diào)用 jump。Component { id: jumperComponent Row { spacing: 4 Text { text: 跳至 } TextField { width: 48 validator: IntValidator { bottom: 1; top: root.pageCount } onEditingFinished: { var value parseInt(text, 10) if (value 1 value root.pageCount) { root.jump(value) } text root.currentPage } } Text { text: 頁(yè) } } }編輯結(jié)束后做兩件事合法數(shù)字走 jump然后無(wú)論是否合法都把輸入框重置成當(dāng)前頁(yè)這樣用戶輸錯(cuò)可以看到輸入被糾正而不是留下一個(gè)錯(cuò)誤數(shù)字等下一次提交。每頁(yè)條數(shù)選擇用 ComboBoxComponent { id: sizesComponent ComboBox { model: root.pageSizes onActivated: { var newPageSize parseInt(currentText, 10) root.pageSize newPageSize root.sizeChange(newPageSize, root.currentPage) } } }換 pageSize 后 currentPage 是保持還是重置為 1不同業(yè)務(wù)有不同習(xí)慣。這個(gè)組件保持當(dāng)前頁(yè)把決定權(quán)交給外部監(jiān)聽(tīng) sizeChange 的代碼。如果后端分頁(yè)接口不支持從非第 1 頁(yè)開(kāi)始外部直接pager.currentPage 1再請(qǐng)求即可。4. 運(yùn)行與驗(yàn)證從命令行到 Qt Creator 的啟動(dòng)實(shí)錄4.1 用 qmlscene 快速啟動(dòng)最快驗(yàn)證組件的方式是 qmlscene不需要編譯。進(jìn)入項(xiàng)目目錄后執(zhí)行cd pagination-qt qmlscene -I . demo/main.qml參數(shù)-I .把當(dāng)前目錄加入 QML import 路徑這樣 demo/main.qml 里可以通過(guò)相對(duì)路徑找到 Pagination.qml。能彈出窗口并且看到“共 86 條”的 total 文本就說(shuō)明組件加載成功。如果提示module QtQuick version 2.15 is not installed說(shuō)明本機(jī) Qt 版本低于 5.15把兩個(gè) import 里的版本號(hào)改成 2.12 或 2.6例如import QtQuick 2.6。大部分老版本 Qt 5 都帶這些模塊不需要額外安裝。4.2 用 qmake 工程跑Qt Creator 的完整路徑qmlscene 適合快速預(yù)覽真正在業(yè)務(wù)工程里還是要放到 Qt Creator 里編譯調(diào)試。qmake 工程文件很簡(jiǎn)潔QT quick qml CONFIG c11 SOURCES main.cpp RESOURCES qml.qrcmain.cpp 就是標(biāo)準(zhǔn)的 QGuiApplication QQmlApplicationEngine加載qrc:/demo/main.qml。這里有個(gè)容易翻車的地方qml.qrc 必須把 Pagination.qml、pager-utils.js、demo/main.qml 全部登記進(jìn)去Qt Creator 部署時(shí)只認(rèn) qrc 里列出的文件漏掉 pager-utils.js 會(huì)導(dǎo)致組件運(yùn)行時(shí)報(bào)“無(wú)法加載腳本”但界面還能彈出來(lái)特別難發(fā)現(xiàn)。如果只做本地調(diào)試也可以不建 qrc直接把 qml 文件放工程目錄用相對(duì)路徑加載省掉資源編譯環(huán)節(jié)。4.3 驗(yàn)證矩陣啟動(dòng)成功后按下面這份驗(yàn)證矩陣快速過(guò)一遍功能確保改動(dòng)沒(méi)有破壞核心行為操作預(yù)期結(jié)果關(guān)注點(diǎn)啟動(dòng) demo列表顯示第 1 頁(yè) 10 條total 顯示 86初始 currentPage 為 1點(diǎn)擊第 3 頁(yè)頁(yè)碼 3 高亮列表內(nèi)容切換pagerItems 是否正確重建jumper 輸入 5 回車跳到第 5 頁(yè)頁(yè)碼 5 高亮輸入校驗(yàn)和 jump clamp每頁(yè)條數(shù)改為 20sizeChange 觸發(fā)列表刷新pageCount 變?yōu)?5ComboBox 的 currentText 解析total 改為 0分頁(yè)器只剩 1 個(gè)頁(yè)碼prev/next 置灰pageCount 兜底邏輯生效驗(yàn)證時(shí)把 Qt Creator 的調(diào)試輸出打開(kāi)因?yàn)闆](méi)有在組件里埋日志的話信號(hào)發(fā)沒(méi)發(fā)出去只能靠界面表現(xiàn)猜。我在組件里臨時(shí)加過(guò)console.log(change, page)等所有驗(yàn)證通過(guò)后再刪掉。5. 常見(jiàn)坑與排查QML 分頁(yè)組件的五個(gè)翻車現(xiàn)場(chǎng)5.1 total 為 0 時(shí)所有按鈕全部失效現(xiàn)象接口返回空列表total 0分頁(yè)器一片灰連頁(yè)碼都沒(méi)了。原因pageCount Math.ceil(0 / pageSize)算出 0buildPager 里 for 循環(huán)執(zhí)行 0 次pagerItems 是空數(shù)組。解決在 pageCount 計(jì)算時(shí)做兜底total 0 ? Math.ceil(total / pageSize) : 1讓空數(shù)據(jù)也顯示一個(gè)不可跳轉(zhuǎn)的頁(yè)碼視覺(jué)上更完整。buildPager 里也加了pageCount 1的直接返回雙保險(xiǎn)。5.2 currentPage 越界后被反復(fù)拉回現(xiàn)象外部把 currentPage 賦值為 100界面先跳到 100又立刻被拉回最大值像在抽搐。原因有些封裝會(huì)在onCurrentPageChanged里做越界修正賦值一次后觸發(fā)回調(diào)回調(diào)又賦值形成二次變更。解決所有越界修正集中在 jump 和屬性綁定里外部不要監(jiān)聽(tīng) currentPage 變化去改它如果必須監(jiān)聽(tīng)用標(biāo)志位擋住重入property bool syncing: false onCurrentPageChanged: { if (syncing) return syncing true if (currentPage pageCount) currentPage pageCount syncing false }5.3 省略號(hào)按鈕渲染成可點(diǎn)擊狀態(tài)現(xiàn)象鼠標(biāo)移到“...”上有 hover 效果點(diǎn)擊卻沒(méi)反應(yīng)看起來(lái)像組件壞了。原因delegate 里把所有 item 都當(dāng)成 Button省略號(hào)也拿到 enabled true。解決pager 的 delegate 里給“...”設(shè)置enabled: false同時(shí)checkable: false讓它從視覺(jué)到交互都明確“不可點(diǎn)”。如果業(yè)務(wù)要求點(diǎn)擊省略號(hào)跳半頁(yè)那是另一個(gè)功能在第 6 章會(huì)給改造方案。5.4 layout 字符串匹配翻車空格和大小寫現(xiàn)象layout 配置成[total, pager , next]pager 沒(méi)渲染其他控件正常。原因switch 是嚴(yán)格字符串比較 pager 不等于pager。解決在 Loader 里先String(modelData).trim()再 switch大小寫問(wèn)題也在同一層統(tǒng)一toLowerCase()。從 JSON 或配置文件讀 layout 時(shí)常帶隱藏?fù)Q行trim 能一并處理。這個(gè)坑很難發(fā)現(xiàn)因?yàn)椴季稚僖粋€(gè)控件并不會(huì)報(bào)錯(cuò)。5.5 點(diǎn)擊頁(yè)碼導(dǎo)致列表刷兩次現(xiàn)象點(diǎn)一下第 3 頁(yè)網(wǎng)絡(luò)請(qǐng)求發(fā)了兩次列表閃兩下。原因jump 里改了 currentPage 后發(fā)射 change而onCurrentPageChanged里又發(fā)射了一次 change。信號(hào)職責(zé)沒(méi)有分開(kāi)。解決change 只在 jump 里發(fā)射一次onCurrentPageChanged只負(fù)責(zé)更新 pagerItems 和按鈕高亮不做事件上報(bào)。這個(gè)約定寫進(jìn)代碼注釋后續(xù)接手的人再往 onCurrentPageChanged 里塞邏輯時(shí)能看到提示。6. 二次開(kāi)發(fā)把分頁(yè)組件塞進(jìn)你的業(yè)務(wù)工程6.1 讓省略號(hào)具備前后半頁(yè)快速跳躍默認(rèn)省略號(hào)是置灰的Element UI 里點(diǎn)擊“...”會(huì)前后跳半屏。改造方法很簡(jiǎn)單給省略號(hào)項(xiàng)設(shè)置 enabled true但點(diǎn)擊時(shí)不走 jump 而是跳pagerCount - 2頁(yè)onClicked: { if (model.type more) { forwardJump() } else if (model.type page) { root.jump(model.label) } } function forwardJump() { root.jump(root.currentPage root.pagerCount - 2) }左右兩個(gè)省略號(hào)可以共用一個(gè)函數(shù)根據(jù)當(dāng)前相對(duì)于中間頁(yè)的位置判斷跳前還是跳后或者直接在數(shù)據(jù)模型里給 more 項(xiàng)加一個(gè)step字段左邊為負(fù)、右邊為正渲染層無(wú)腦累加。6.2 用純函數(shù) 斷言向量鎖住頁(yè)碼算法pager-utils.js 抽出獨(dú)立函數(shù)后我在業(yè)務(wù)里每次改版都會(huì)先跑一遍斷言向量用 Node 直接執(zhí)行node tests/pager.test.js測(cè)試文件里不依賴 Qt只需要常規(guī)斷言const assert require(assert) const { buildPager } require(../pager-utils.js) var labels buildPager(10, 5, 7).map(function (x) { return x.label }) assert.ok(labels.indexOf(1) 0, must contain first page) assert.ok(labels.indexOf(10) 0, must contain last page) assert.ok(labels.indexOf(0) -1, must not contain page 0) labels.forEach(function (label) { assert.ok(label ... || (label 1 label 10), invalid page: label) }) console.log(pager ok)這套向量不鎖具體樣式只鎖合法性包含首尾、沒(méi)有 0 頁(yè)、所有數(shù)字都在有效范圍內(nèi)。從那以后我每次新增分頁(yè)功能都強(qiáng)制先跑一遍斷言再往界面上綁定屬性順序反了就容易把排期燒在玄學(xué)上。6.3 對(duì)接真實(shí)后端接口把 demo 里的假數(shù)據(jù)換成網(wǎng)絡(luò)請(qǐng)求只需要在外部監(jiān)聽(tīng)組件的兩個(gè)信號(hào)Pagination { id: pager total: fakeApi.totalCount onChange: function (page) { fakeApi.fetchPage(page, pager.pageSize).then(function (rows) { listModel.clear() rows.forEach(function (row) { listModel.append(row) }) }) } onSizeChange: function (newPageSize, page) { pager.currentPage 1 fakeApi.fetchPage(1, newPageSize).then(function (rows) { listModel.clear() rows.forEach(function (row) { listModel.append(row) }) }) } }換 pageSize 時(shí)先重置到第 1 頁(yè)是大多數(shù)后端分頁(yè)接口的硬性要求否則當(dāng)前頁(yè)大于新 pageCount 時(shí)請(qǐng)求會(huì)拿不到數(shù)據(jù)。Pagination.qml 本身不關(guān)心數(shù)據(jù)來(lái)源你傳 total它負(fù)責(zé)算頁(yè)碼你監(jiān)聽(tīng) change它把用戶意圖交出來(lái)。組件和業(yè)務(wù)邏輯之間就這一層契約后續(xù)把整個(gè)分頁(yè)器換成 C 實(shí)現(xiàn)外部代碼也不用動(dòng)。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取