From e8f9bfb5c6486829804fc63b7fabd5e81fc6fbf9 Mon Sep 17 00:00:00 2001
From: RuoYi <yzz_ivy@163.com>
Date: Mon, 3 Jun 2024 13:31:33 +0800
Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=A1=A8=E6=A0=BC=E7=A4=BA?=
 =?UTF-8?q?=E4=BE=8B=EF=BC=88=E4=BF=9D=E5=AD=98=E7=8A=B6=E6=80=81=EF=BC=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../demo/controller/DemoTableController.java  |   9 +
 .../cookie/bootstrap-table-cookie.js          | 606 ++++++++++++++++++
 .../templates/demo/table/cookie.html          |  78 +++
 .../resources/templates/index-topnav.html     |   1 +
 .../src/main/resources/templates/index.html   |   1 +
 5 files changed, 695 insertions(+)
 create mode 100644 ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/cookie/bootstrap-table-cookie.js
 create mode 100644 ruoyi-admin/src/main/resources/templates/demo/table/cookie.html

diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoTableController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoTableController.java
index 6497228d..bcf81092 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoTableController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoTableController.java
@@ -226,6 +226,15 @@ public class DemoTableController extends BaseController
         return prefix + "/remember";
     }
 
+    /**
+     * 表格保存状态
+     */
+    @GetMapping("/cookie")
+    public String cookie()
+    {
+        return prefix + "/cookie";
+    }
+
     /**
      * 跳转至指定页
      */
diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/cookie/bootstrap-table-cookie.js b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/cookie/bootstrap-table-cookie.js
new file mode 100644
index 00000000..2d0c852a
--- /dev/null
+++ b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/cookie/bootstrap-table-cookie.js
@@ -0,0 +1,606 @@
+/**
+ * @author: Dennis Hernández
+ * @update zhixin wen <wenzhixin2010@gmail.com>
+ */
+var Utils = $.fn.bootstrapTable.utils
+var UtilsCookie = {
+  cookieIds: {
+    sortOrder: 'bs.table.sortOrder',
+    sortName: 'bs.table.sortName',
+    sortPriority: 'bs.table.sortPriority',
+    pageNumber: 'bs.table.pageNumber',
+    pageList: 'bs.table.pageList',
+    hiddenColumns: 'bs.table.hiddenColumns',
+    cardView: 'bs.table.cardView',
+    customView: 'bs.table.customView',
+    searchText: 'bs.table.searchText',
+    reorderColumns: 'bs.table.reorderColumns',
+    filterControl: 'bs.table.filterControl',
+    filterBy: 'bs.table.filterBy'
+  },
+  getCurrentHeader (that) {
+    return that.options.height ? that.$tableHeader : that.$header
+  },
+  getCurrentSearchControls (that) {
+    return that.options.height ? 'table select, table input' : 'select, input'
+  },
+  isCookieSupportedByBrowser () {
+    return navigator.cookieEnabled
+  },
+  isCookieEnabled (that, cookieName) {
+    return that.options.cookiesEnabled.includes(cookieName)
+  },
+  setCookie (that, cookieName, cookieValue) {
+    if (
+      !that.options.cookie ||
+      !UtilsCookie.isCookieEnabled(that, cookieName)
+    ) {
+      return
+    }
+
+    return that._storage.setItem(`${that.options.cookieIdTable}.${cookieName}`, cookieValue)
+  },
+  getCookie (that, cookieName) {
+    if (
+      !cookieName ||
+      !UtilsCookie.isCookieEnabled(that, cookieName)
+    ) {
+      return null
+    }
+
+    return that._storage.getItem(`${that.options.cookieIdTable}.${cookieName}`)
+  },
+  deleteCookie (that, cookieName) {
+    return that._storage.removeItem(`${that.options.cookieIdTable}.${cookieName}`)
+  },
+  calculateExpiration (cookieExpire) {
+    const time = cookieExpire.replace(/[0-9]*/, '') // s,mi,h,d,m,y
+
+    cookieExpire = cookieExpire.replace(/[A-Za-z]{1,2}/, '') // number
+
+    switch (time.toLowerCase()) {
+      case 's':
+        cookieExpire = +cookieExpire
+        break
+      case 'mi':
+        cookieExpire *= 60
+        break
+      case 'h':
+        cookieExpire = cookieExpire * 60 * 60
+        break
+      case 'd':
+        cookieExpire = cookieExpire * 24 * 60 * 60
+        break
+      case 'm':
+        cookieExpire = cookieExpire * 30 * 24 * 60 * 60
+        break
+      case 'y':
+        cookieExpire = cookieExpire * 365 * 24 * 60 * 60
+        break
+      default:
+        cookieExpire = undefined
+        break
+    }
+    if (!cookieExpire) {
+      return ''
+    }
+    const d = new Date()
+
+    d.setTime(d.getTime() + cookieExpire * 1000)
+    return d.toGMTString()
+  },
+  initCookieFilters (that) {
+    setTimeout(() => {
+      const parsedCookieFilters = JSON.parse(
+        UtilsCookie.getCookie(that, UtilsCookie.cookieIds.filterControl))
+
+      if (!that._filterControlValuesLoaded && parsedCookieFilters) {
+        const cachedFilters = {}
+        const header = UtilsCookie.getCurrentHeader(that)
+        const searchControls = UtilsCookie.getCurrentSearchControls(that)
+
+        const applyCookieFilters = (element, filteredCookies) => {
+          filteredCookies.forEach(cookie => {
+            const value = element.value.toString()
+            const text = cookie.text
+
+            if (
+              text === '' ||
+              element.type === 'radio' &&
+              value !== text
+            ) {
+              return
+            }
+
+            if (
+              element.tagName === 'INPUT' &&
+              element.type === 'radio' &&
+              value === text
+            ) {
+              element.checked = true
+              cachedFilters[cookie.field] = text
+            } else if (element.tagName === 'INPUT') {
+              element.value = text
+              cachedFilters[cookie.field] = text
+            } else if (
+              element.tagName === 'SELECT' &&
+              that.options.filterControlContainer
+            ) {
+              element.value = text
+              cachedFilters[cookie.field] = text
+            } else if (text !== '' && element.tagName === 'SELECT') {
+              cachedFilters[cookie.field] = text
+              for (const currentElement of element) {
+                if (currentElement.value === text) {
+                  currentElement.selected = true
+                  return
+                }
+              }
+              const option = document.createElement('option')
+
+              option.value = text
+              option.text = text
+              element.add(option, element[1])
+              element.selectedIndex = 1
+            }
+          })
+        }
+
+        let filterContainer = header
+
+        if (that.options.filterControlContainer) {
+          filterContainer = $(`${that.options.filterControlContainer}`)
+        }
+
+        filterContainer.find(searchControls).each(function () {
+          const field = $(this).closest('[data-field]').data('field')
+          const filteredCookies = parsedCookieFilters.filter(cookie => cookie.field === field)
+
+          applyCookieFilters(this, filteredCookies)
+        })
+
+        that.initColumnSearch(cachedFilters)
+        that._filterControlValuesLoaded = true
+        that.initServer()
+      }
+    }, 250)
+  }
+}
+
+Object.assign($.fn.bootstrapTable.defaults, {
+  cookie: false,
+  cookieExpire: '2h',
+  cookiePath: null,
+  cookieDomain: null,
+  cookieSecure: null,
+  cookieSameSite: 'Lax',
+  cookieIdTable: '',
+  cookiesEnabled: [
+    'bs.table.sortOrder', 'bs.table.sortName', 'bs.table.sortPriority',
+    'bs.table.pageNumber', 'bs.table.pageList',
+    'bs.table.hiddenColumns', 'bs.table.searchText',
+    'bs.table.filterControl', 'bs.table.filterBy',
+    'bs.table.reorderColumns', 'bs.table.cardView', 'bs.table.customView'
+  ],
+  cookieStorage: 'cookieStorage', // localStorage, sessionStorage, customStorage
+  cookieCustomStorageGet: null,
+  cookieCustomStorageSet: null,
+  cookieCustomStorageDelete: null,
+  // internal variable
+  _filterControls: [],
+  _filterControlValuesLoaded: false,
+  _storage: {
+    setItem: undefined,
+    getItem: undefined,
+    removeItem: undefined
+  }
+})
+
+$.fn.bootstrapTable.methods.push('getCookies')
+$.fn.bootstrapTable.methods.push('deleteCookie')
+
+Object.assign($.fn.bootstrapTable.utils, {
+  setCookie: UtilsCookie.setCookie,
+  getCookie: UtilsCookie.getCookie
+})
+
+$.BootstrapTable = class extends $.BootstrapTable {
+  init () {
+    if (this.options.cookie) {
+      if (
+        this.options.cookieStorage === 'cookieStorage' &&
+        !UtilsCookie.isCookieSupportedByBrowser()
+      ) {
+        throw new Error('Cookies are not enabled in this browser.')
+      }
+
+      this.configureStorage()
+
+      // FilterBy logic
+      const filterByCookieValue = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.filterBy)
+
+      if (typeof filterByCookieValue === 'boolean' && !filterByCookieValue) {
+        throw new Error('The cookie value of filterBy must be a json!')
+      }
+
+      let filterByCookie = {}
+
+      try {
+        filterByCookie = JSON.parse(filterByCookieValue)
+      } catch (e) {
+        throw new Error('Could not parse the json of the filterBy cookie!')
+      }
+      this.filterColumns = filterByCookie ? filterByCookie : {}
+
+      // FilterControl logic
+      this._filterControls = []
+      this._filterControlValuesLoaded = false
+
+      this.options.cookiesEnabled = typeof this.options.cookiesEnabled === 'string' ?
+        this.options.cookiesEnabled.replace('[', '').replace(']', '')
+          .replace(/'/g, '').replace(/ /g, '').split(',') :
+        this.options.cookiesEnabled
+
+      if (this.options.filterControl) {
+        const that = this
+
+        this.$el.on('column-search.bs.table', (e, field, text) => {
+          let isNewField = true
+
+          for (let i = 0; i < that._filterControls.length; i++) {
+            if (that._filterControls[i].field === field) {
+              that._filterControls[i].text = text
+              isNewField = false
+              break
+            }
+          }
+          if (isNewField) {
+            that._filterControls.push({
+              field,
+              text
+            })
+          }
+
+          UtilsCookie.setCookie(that, UtilsCookie.cookieIds.filterControl, JSON.stringify(that._filterControls))
+        }).on('created-controls.bs.table', UtilsCookie.initCookieFilters(that))
+      }
+    }
+    super.init()
+  }
+
+  initServer (...args) {
+    if (
+      this.options.cookie &&
+      this.options.filterControl &&
+      !this._filterControlValuesLoaded
+    ) {
+      const cookie = JSON.parse(UtilsCookie.getCookie(this, UtilsCookie.cookieIds.filterControl))
+
+      if (cookie) {
+        return
+      }
+    }
+    super.initServer(...args)
+  }
+
+  initTable (...args) {
+    super.initTable(...args)
+    this.initCookie()
+  }
+
+  onSort (...args) {
+    super.onSort(...args)
+
+    if (!this.options.cookie) {
+      return
+    }
+
+    if (this.options.sortName === undefined || this.options.sortOrder === undefined) {
+      UtilsCookie.deleteCookie(this, UtilsCookie.cookieIds.sortName)
+      UtilsCookie.deleteCookie(this, UtilsCookie.cookieIds.sortOrder)
+    } else {
+      this.options.sortPriority = null
+      UtilsCookie.deleteCookie(this, UtilsCookie.cookieIds.sortPriority)
+
+      UtilsCookie.setCookie(this, UtilsCookie.cookieIds.sortOrder, this.options.sortOrder)
+      UtilsCookie.setCookie(this, UtilsCookie.cookieIds.sortName, this.options.sortName)
+    }
+  }
+
+  onMultipleSort (...args) {
+    super.onMultipleSort(...args)
+
+    if (!this.options.cookie) {
+      return
+    }
+
+    if (this.options.sortPriority === undefined) {
+      UtilsCookie.deleteCookie(this, UtilsCookie.cookieIds.sortPriority)
+    } else {
+      this.options.sortName = undefined
+      this.options.sortOrder = undefined
+      UtilsCookie.deleteCookie(this, UtilsCookie.cookieIds.sortName)
+      UtilsCookie.deleteCookie(this, UtilsCookie.cookieIds.sortOrder)
+
+      UtilsCookie.setCookie(this, UtilsCookie.cookieIds.sortPriority, JSON.stringify(this.options.sortPriority))
+    }
+  }
+
+  onPageNumber (...args) {
+    super.onPageNumber(...args)
+    if (!this.options.cookie) {
+      return
+    }
+    UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageNumber, this.options.pageNumber)
+  }
+
+  onPageListChange (...args) {
+    super.onPageListChange(...args)
+    if (!this.options.cookie) {
+      return
+    }
+    UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageList,
+      this.options.pageSize === this.options.formatAllRows() ? 'all' : this.options.pageSize)
+    UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageNumber, this.options.pageNumber)
+  }
+
+  onPagePre (...args) {
+    super.onPagePre(...args)
+    if (!this.options.cookie) {
+      return
+    }
+    UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageNumber, this.options.pageNumber)
+  }
+
+  onPageNext (...args) {
+    super.onPageNext(...args)
+    if (!this.options.cookie) {
+      return
+    }
+    UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageNumber, this.options.pageNumber)
+  }
+
+  _toggleColumn (...args) {
+    super._toggleColumn(...args)
+    if (!this.options.cookie) {
+      return
+    }
+    UtilsCookie.setCookie(this, UtilsCookie.cookieIds.hiddenColumns, JSON.stringify(this.getHiddenColumns().map(column => column.field)))
+  }
+
+  _toggleAllColumns (...args) {
+    super._toggleAllColumns(...args)
+    if (!this.options.cookie) {
+      return
+    }
+    UtilsCookie.setCookie(this, UtilsCookie.cookieIds.hiddenColumns, JSON.stringify(this.getHiddenColumns().map(column => column.field)))
+  }
+
+  toggleView () {
+    super.toggleView()
+    UtilsCookie.setCookie(this, UtilsCookie.cookieIds.cardView, this.options.cardView)
+  }
+
+  toggleCustomView () {
+    super.toggleCustomView()
+    UtilsCookie.setCookie(this, UtilsCookie.cookieIds.customView, this.customViewDefaultView)
+  }
+
+  selectPage (page) {
+    super.selectPage(page)
+    if (!this.options.cookie) {
+      return
+    }
+    UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageNumber, page)
+  }
+
+  onSearch (event) {
+    super.onSearch(event, arguments.length > 1 ? arguments[1] : true)
+    if (!this.options.cookie) {
+      return
+    }
+    if (this.options.search) {
+      UtilsCookie.setCookie(this, UtilsCookie.cookieIds.searchText, this.searchText)
+    }
+    UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageNumber, this.options.pageNumber)
+  }
+
+  initHeader (...args) {
+    if (this.options.reorderableColumns && this.options.cookie) {
+      this.columnsSortOrder = JSON.parse(UtilsCookie.getCookie(this, UtilsCookie.cookieIds.reorderColumns))
+    }
+    super.initHeader(...args)
+  }
+
+  persistReorderColumnsState (that) {
+    UtilsCookie.setCookie(that, UtilsCookie.cookieIds.reorderColumns, JSON.stringify(that.columnsSortOrder))
+  }
+
+  filterBy (...args) {
+    super.filterBy(...args)
+    if (!this.options.cookie) {
+      return
+    }
+    UtilsCookie.setCookie(this, UtilsCookie.cookieIds.filterBy, JSON.stringify(this.filterColumns))
+  }
+
+  initCookie () {
+    if (!this.options.cookie) {
+      return
+    }
+
+    if (this.options.cookieIdTable === '' || this.options.cookieExpire === '') {
+      console.error('Configuration error. Please review the cookieIdTable and the cookieExpire property. If the properties are correct, then this browser does not support cookies.')
+      this.options.cookie = false // Make sure that the cookie extension is disabled
+      return
+    }
+
+    const sortOrderCookie = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.sortOrder)
+    const sortOrderNameCookie = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.sortName)
+    let sortPriorityCookie = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.sortPriority)
+    const pageNumberCookie = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.pageNumber)
+    const pageListCookie = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.pageList)
+    const searchTextCookie = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.searchText)
+    const cardViewCookie = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.cardView)
+    const customViewCookie = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.customView)
+    const hiddenColumnsCookieValue = UtilsCookie.getCookie(this, UtilsCookie.cookieIds.hiddenColumns)
+
+    let hiddenColumnsCookie = {}
+
+    try {
+      hiddenColumnsCookie = JSON.parse(hiddenColumnsCookieValue)
+    } catch (e) {
+      throw new Error('Could not parse the json of the hidden columns cookie!', hiddenColumnsCookieValue)
+    }
+
+    try {
+      sortPriorityCookie = JSON.parse(sortPriorityCookie)
+    } catch (e) {
+      throw new Error('Could not parse the json of the sortPriority cookie!', sortPriorityCookie)
+    }
+
+    if (!sortPriorityCookie) {
+      // sortOrder
+      this.options.sortOrder = sortOrderCookie ? sortOrderCookie : this.options.sortOrder
+      // sortName
+      this.options.sortName = sortOrderNameCookie ? sortOrderNameCookie : this.options.sortName
+    } else {
+      this.options.sortOrder = undefined
+      this.options.sortName = undefined
+    }
+
+    // sortPriority
+    this.options.sortPriority = sortPriorityCookie ? sortPriorityCookie : this.options.sortPriority
+
+    if (this.options.sortOrder || this.options.sortName) {
+      // sortPriority
+      this.options.sortPriority = null
+    }
+
+    // pageNumber
+    this.options.pageNumber = pageNumberCookie ? +pageNumberCookie : this.options.pageNumber
+    // pageSize
+    this.options.pageSize = pageListCookie ? pageListCookie === 'all' ?
+      this.options.formatAllRows() : +pageListCookie : this.options.pageSize
+    // searchText
+    if (UtilsCookie.isCookieEnabled(this, UtilsCookie.cookieIds.searchText) && this.options.searchText === '') {
+      this.options.searchText = searchTextCookie ? searchTextCookie : ''
+    }
+    // cardView
+    if (cardViewCookie !== null) {
+      this.options.cardView = cardViewCookie === 'true' ? cardViewCookie : false
+    }
+    this.customViewDefaultView = customViewCookie === 'true'
+
+    if (hiddenColumnsCookie) {
+      for (const column of this.columns) {
+        if (!column.switchable) {
+          continue
+        }
+
+        column.visible = this.isSelectionColumn(column) ||
+          !hiddenColumnsCookie.includes(column.field)
+      }
+    }
+  }
+
+  getCookies () {
+    const bootstrapTable = this
+    const cookies = {}
+
+    for (const [key, value] of Object.entries(UtilsCookie.cookieIds)) {
+      cookies[key] = UtilsCookie.getCookie(bootstrapTable, value)
+      if (key === 'columns' || key === 'hiddenColumns' || key === 'sortPriority') {
+        cookies[key] = JSON.parse(cookies[key])
+      }
+    }
+    return cookies
+  }
+
+  deleteCookie (cookieName) {
+    if (!cookieName) {
+      return
+    }
+
+    UtilsCookie.deleteCookie(this, UtilsCookie.cookieIds[cookieName])
+  }
+
+  configureStorage () {
+    const that = this
+
+    this._storage = {}
+    switch (this.options.cookieStorage) {
+      case 'cookieStorage':
+        this._storage.setItem = function (cookieName, cookieValue) {
+          document.cookie = [
+            cookieName, '=', encodeURIComponent(cookieValue),
+            `; expires=${UtilsCookie.calculateExpiration(that.options.cookieExpire)}`,
+            that.options.cookiePath ? `; path=${that.options.cookiePath}` : '',
+            that.options.cookieDomain ? `; domain=${that.options.cookieDomain}` : '',
+            that.options.cookieSecure ? '; secure' : '',
+            `;SameSite=${that.options.cookieSameSite}`
+          ].join('')
+        }
+        this._storage.getItem = function (cookieName) {
+          const value = `; ${document.cookie}`
+          const parts = value.split(`; ${cookieName}=`)
+
+          return parts.length === 2 ? decodeURIComponent(parts.pop().split(';').shift()) : null
+        }
+        this._storage.removeItem = function (cookieName) {
+          document.cookie = [
+            encodeURIComponent(cookieName), '=',
+            '; expires=Thu, 01 Jan 1970 00:00:00 GMT',
+            that.options.cookiePath ? `; path=${that.options.cookiePath}` : '',
+            that.options.cookieDomain ? `; domain=${that.options.cookieDomain}` : '',
+            `;SameSite=${that.options.cookieSameSite}`
+          ].join('')
+        }
+        break
+      case 'localStorage':
+        this._storage.setItem = function (cookieName, cookieValue) {
+          localStorage.setItem(cookieName, cookieValue)
+        }
+        this._storage.getItem = function (cookieName) {
+          return localStorage.getItem(cookieName)
+        }
+        this._storage.removeItem = function (cookieName) {
+          localStorage.removeItem(cookieName)
+        }
+        break
+      case 'sessionStorage':
+        this._storage.setItem = function (cookieName, cookieValue) {
+          sessionStorage.setItem(cookieName, cookieValue)
+        }
+        this._storage.getItem = function (cookieName) {
+          return sessionStorage.getItem(cookieName)
+        }
+        this._storage.removeItem = function (cookieName) {
+          sessionStorage.removeItem(cookieName)
+        }
+        break
+      case 'customStorage':
+        if (
+          !this.options.cookieCustomStorageSet ||
+          !this.options.cookieCustomStorageGet ||
+          !this.options.cookieCustomStorageDelete
+        ) {
+          throw new Error('The following options must be set while using the customStorage: cookieCustomStorageSet, cookieCustomStorageGet and cookieCustomStorageDelete')
+        }
+
+        this._storage.setItem = function (cookieName, cookieValue) {
+          Utils.calculateObjectValue(that.options, that.options.cookieCustomStorageSet, [cookieName, cookieValue], '')
+        }
+        this._storage.getItem = function (cookieName) {
+          return Utils.calculateObjectValue(that.options, that.options.cookieCustomStorageGet, [cookieName], '')
+        }
+        this._storage.removeItem = function (cookieName) {
+          Utils.calculateObjectValue(that.options, that.options.cookieCustomStorageDelete, [cookieName], '')
+        }
+
+        break
+      default:
+        throw new Error('Storage method not supported.')
+    }
+  }
+}
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/resources/templates/demo/table/cookie.html b/ruoyi-admin/src/main/resources/templates/demo/table/cookie.html
new file mode 100644
index 00000000..3ff29835
--- /dev/null
+++ b/ruoyi-admin/src/main/resources/templates/demo/table/cookie.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
+<head>
+	<th:block th:include="include :: header('表格保存状态')" />
+</head>
+<body class="gray-bg">
+     <div class="container-div">
+		<div class="row">
+			<div class="col-sm-12 select-table table-striped">
+				<table id="bootstrap-table" data-cookie="true" data-cookie-id-table="userTableId"></table>
+			</div>
+		</div>
+	</div>
+    <div th:include="include :: footer"></div>
+    <th:block th:include="include :: bootstrap-table-cookie-js" />
+    <script th:inline="javascript">
+        var prefix = ctx + "demo/table";
+        var datas = [[${@dict.getType('sys_normal_disable')}]];
+
+        $(function() {
+            var options = {
+                url: prefix + "/list",
+                search: true,
+                showSearch: false,
+                showRefresh: false,
+		        showToggle: false,
+                columns: [{
+                	field: 'state',
+		            checkbox: true
+		        },
+				{
+					field : 'userId', 
+					title : '用户ID'
+				},
+				{
+					field : 'userCode', 
+					title : '用户编号'
+				},
+				{
+					field : 'userName', 
+					title : '用户姓名'
+				},
+				{
+					field : 'userPhone', 
+					title : '用户手机'
+				},
+				{
+					field : 'userEmail', 
+					title : '用户邮箱'
+				},
+				{
+				    field : 'userBalance',
+				    title : '用户余额'
+				},
+				{
+                    field: 'status',
+                    title: '用户状态',
+                    align: 'center',
+                    formatter: function(value, row, index) {
+                    	return $.table.selectDictLabel(datas, value);
+                    }
+                },
+		        {
+		            title: '操作',
+		            align: 'center',
+		            formatter: function(value, row, index) {
+		            	var actions = [];
+		            	actions.push('<a class="btn btn-success btn-xs" href="javascript:;"><i class="fa fa-edit"></i>编辑</a> ');
+                        actions.push('<a class="btn btn-danger btn-xs" href="javascript:;"><i class="fa fa-remove"></i>删除</a>');
+						return actions.join('');
+		            }
+		        }]
+            };
+            $.table.init(options);
+        });
+    </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/resources/templates/index-topnav.html b/ruoyi-admin/src/main/resources/templates/index-topnav.html
index 6c5712db..3dd19475 100644
--- a/ruoyi-admin/src/main/resources/templates/index-topnav.html
+++ b/ruoyi-admin/src/main/resources/templates/index-topnav.html
@@ -128,6 +128,7 @@
 					<li><a class="menuItem" th:href="@{/demo/table/textSearch}">全文索引</a></li>
 					<li><a class="menuItem" th:href="@{/demo/table/exportSelected}">导出选择列</a></li>
 					<li><a class="menuItem" th:href="@{/demo/table/remember}">翻页记住选择</a></li>
+					<li><a class="menuItem" th:href="@{/demo/table/cookie}">表格保存状态</a></li>
 					<li><a class="menuItem" th:href="@{/demo/table/pageGo}">跳转至指定页</a></li>
 					<li><a class="menuItem" th:href="@{/demo/table/params}">自定义查询参数</a></li>
 					<li><a class="menuItem" th:href="@{/demo/table/multi}">初始多表格</a></li>
diff --git a/ruoyi-admin/src/main/resources/templates/index.html b/ruoyi-admin/src/main/resources/templates/index.html
index 3cf7731d..26f764ee 100644
--- a/ruoyi-admin/src/main/resources/templates/index.html
+++ b/ruoyi-admin/src/main/resources/templates/index.html
@@ -108,6 +108,7 @@
 								<li><a class="menuItem" th:href="@{/demo/table/textSearch}">全文索引</a></li>
 								<li><a class="menuItem" th:href="@{/demo/table/exportSelected}">导出选择列</a></li>
 								<li><a class="menuItem" th:href="@{/demo/table/remember}">翻页记住选择</a></li>
+								<li><a class="menuItem" th:href="@{/demo/table/cookie}">表格保存状态</a></li>
 								<li><a class="menuItem" th:href="@{/demo/table/pageGo}">跳转至指定页</a></li>
 								<li><a class="menuItem" th:href="@{/demo/table/params}">自定义查询参数</a></li>
 								<li><a class="menuItem" th:href="@{/demo/table/multi}">初始多表格</a></li>