From c58d757bbdf6e7e1329307a2b5416a893d233fe9 Mon Sep 17 00:00:00 2001
From: tyro880 <你的GitHub绑定邮箱>
Date: Wed, 28 Jan 2026 20:16:22 +0800
Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E7=94=A8?=
=?UTF-8?q?=E6=88=B7=E5=88=97=E8=A1=A8=E9=AB=98=E7=BA=A7=E6=90=9C=E7=B4=A2?=
=?UTF-8?q?=E8=A1=A8=E5=8D=95=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/router/index.js | 2 +
src/router/modules/user.js | 24 ++
src/views/user/index.vue | 542 +++++++++++++++++++++++++++++++++++++
3 files changed, 568 insertions(+)
create mode 100644 src/router/modules/user.js
create mode 100644 src/views/user/index.vue
diff --git a/src/router/index.js b/src/router/index.js
index 2be959d2..ba53fafb 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -11,6 +11,7 @@ import componentsRouter from './modules/components'
import chartsRouter from './modules/charts'
import tableRouter from './modules/table'
import nestedRouter from './modules/nested'
+import userRouter from './modules/user'
/**
* Note: sub-menu only appear when route children.length >= 1
@@ -189,6 +190,7 @@ export const asyncRoutes = [
chartsRouter,
nestedRouter,
tableRouter,
+ userRouter,
{
path: '/example',
diff --git a/src/router/modules/user.js b/src/router/modules/user.js
new file mode 100644
index 00000000..b1f54604
--- /dev/null
+++ b/src/router/modules/user.js
@@ -0,0 +1,24 @@
+/** 用户管理路由模块 **/
+
+import Layout from '@/layout'
+
+const userRouter = {
+ path: '/user',
+ component: Layout,
+ redirect: '/user/list',
+ name: 'User',
+ meta: {
+ title: '用户管理',
+ icon: 'people'
+ },
+ children: [
+ {
+ path: 'list',
+ component: () => import('@/views/user/index'),
+ name: 'UserList',
+ meta: { title: '用户列表', icon: 'peoples' }
+ }
+ ]
+}
+
+export default userRouter
diff --git a/src/views/user/index.vue b/src/views/user/index.vue
new file mode 100644
index 00000000..44582e66
--- /dev/null
+++ b/src/views/user/index.vue
@@ -0,0 +1,542 @@
+
+
+
+
+
+ {{ advancedSearchVisible ? '收起高级搜索' : '展开高级搜索' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 启用
+ 禁用
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+
+
+
+
+
+ 导出选中
+
+
+ 导出全部
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ getRoleLabel(row.role) }}
+
+
+
+
+
+ {{ row.createTime | parseTime('{y}-{m}-{d} {h}:{i}') }}
+
+
+
+
+
+ {{ row.status === 'enabled' ? '启用' : '禁用' }}
+
+
+
+
+
+
+ 编辑
+
+
+ {{ row.status === 'enabled' ? '禁用' : '启用' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
From 64e2ddf10cdcd105b6ed82d8ac8f8e6825a98952 Mon Sep 17 00:00:00 2001
From: tyro880 <你的GitHub绑定邮箱>
Date: Sat, 18 Apr 2026 10:06:38 +0800
Subject: [PATCH 2/4] =?UTF-8?q?feat(=E8=A1=A8=E6=A0=BC):=20=E6=B7=BB?=
=?UTF-8?q?=E5=8A=A0=E6=89=B9=E9=87=8F=E9=80=89=E6=8B=A9=E5=92=8C=E6=93=8D?=
=?UTF-8?q?=E4=BD=9C=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 增加表格多选功能
- 添加批量删除和导出按钮
- 实现批量删除和导出逻辑
- 添加相关状态管理和提示信息
---
src/views/table/complex-table.vue | 91 ++++++++++++++++++++++++++++++-
1 file changed, 90 insertions(+), 1 deletion(-)
diff --git a/src/views/table/complex-table.vue b/src/views/table/complex-table.vue
index 295c5fc4..4845b730 100644
--- a/src/views/table/complex-table.vue
+++ b/src/views/table/complex-table.vue
@@ -25,6 +25,28 @@
+
+
+ 批量删除 ({{ selectedList.length }})
+
+
+ 批量导出 ({{ selectedList.length }})
+
+
+
+
{{ row.id }}
@@ -223,7 +247,9 @@ export default {
timestamp: [{ type: 'date', required: true, message: 'timestamp is required', trigger: 'change' }],
title: [{ required: true, message: 'title is required', trigger: 'blur' }]
},
- downloadLoading: false
+ downloadLoading: false,
+ selectedList: [],
+ batchExportLoading: false
}
},
created() {
@@ -373,6 +399,69 @@ export default {
getSortClass: function(key) {
const sort = this.listQuery.sort
return sort === `+${key}` ? 'ascending' : 'descending'
+ },
+ handleSelectionChange(selection) {
+ this.selectedList = selection
+ },
+ handleBatchDelete() {
+ if (this.selectedList.length === 0) {
+ this.$message({
+ message: '请先选择要删除的记录',
+ type: 'warning'
+ })
+ return
+ }
+
+ this.$confirm(`确定要删除选中的 ${this.selectedList.length} 条记录吗?`, '提示', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning'
+ }).then(() => {
+ // 批量删除操作
+ const ids = this.selectedList.map(item => item.id)
+ const newList = this.list.filter(item => !ids.includes(item.id))
+ this.list = newList
+ this.selectedList = []
+ this.$notify({
+ title: 'Success',
+ message: `成功删除 ${ids.length} 条记录`,
+ type: 'success',
+ duration: 2000
+ })
+ }).catch(() => {
+ // 用户取消删除
+ })
+ },
+ handleBatchExport() {
+ if (this.selectedList.length === 0) {
+ this.$message({
+ message: '请先选择要导出的记录',
+ type: 'warning'
+ })
+ return
+ }
+
+ this.batchExportLoading = true
+ import('@/vendor/Export2Excel').then(excel => {
+ const tHeader = ['timestamp', 'title', 'type', 'importance', 'status']
+ const filterVal = ['timestamp', 'title', 'type', 'importance', 'status']
+ const data = this.formatJsonForSelected(filterVal)
+ excel.export_json_to_excel({
+ header: tHeader,
+ data,
+ filename: 'selected-table-list'
+ })
+ this.batchExportLoading = false
+ })
+ },
+ formatJsonForSelected(filterVal) {
+ return this.selectedList.map(v => filterVal.map(j => {
+ if (j === 'timestamp') {
+ return parseTime(v[j])
+ } else {
+ return v[j]
+ }
+ }))
}
}
}
From 40e466b1464e505a44d5f01b29a785794dcb8363 Mon Sep 17 00:00:00 2001
From: tyro880 <你的GitHub绑定邮箱>
Date: Sat, 18 Apr 2026 12:45:29 +0800
Subject: [PATCH 3/4] =?UTF-8?q?fix(=E8=A1=A8=E6=A0=BC):=20=E4=BF=AE?=
=?UTF-8?q?=E5=A4=8D=E5=88=A0=E9=99=A4=E5=92=8C=E5=AF=BC=E5=87=BA=E5=8A=9F?=
=?UTF-8?q?=E8=83=BD=E5=B9=B6=E4=BC=98=E5=8C=96=E6=80=BB=E6=95=B0=E8=AE=A1?=
=?UTF-8?q?=E7=AE=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 修复删除操作后总数未更新的问题
- 合并导出功能代码并添加错误处理
- 为导出文件添加时间戳后缀
- 移除重复的formatJsonForSelected方法
---
src/views/table/complex-table.vue | 88 +++++++++++++++++++++----------
1 file changed, 60 insertions(+), 28 deletions(-)
diff --git a/src/views/table/complex-table.vue b/src/views/table/complex-table.vue
index 4845b730..de037172 100644
--- a/src/views/table/complex-table.vue
+++ b/src/views/table/complex-table.vue
@@ -366,6 +366,7 @@ export default {
duration: 2000
})
this.list.splice(index, 1)
+ this.total = Math.max(0, this.total - 1)
},
handleFetchPv(pv) {
fetchPv(pv).then(response => {
@@ -376,19 +377,39 @@ export default {
handleDownload() {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
- const tHeader = ['timestamp', 'title', 'type', 'importance', 'status']
- const filterVal = ['timestamp', 'title', 'type', 'importance', 'status']
- const data = this.formatJson(filterVal)
- excel.export_json_to_excel({
- header: tHeader,
- data,
- filename: 'table-list'
- })
+ try {
+ const tHeader = ['timestamp', 'title', 'type', 'importance', 'status']
+ const filterVal = ['timestamp', 'title', 'type', 'importance', 'status']
+ const data = this.formatJson(filterVal)
+ const timestamp = parseTime(new Date(), '{y}{m}{d}-{h}{i}{s}')
+ excel.export_json_to_excel({
+ header: tHeader,
+ data,
+ filename: `article-list-${timestamp}`
+ })
+ this.$message({
+ message: '导出成功',
+ type: 'success'
+ })
+ } catch (error) {
+ this.$message({
+ message: '导出失败',
+ type: 'error'
+ })
+ } finally {
+ this.downloadLoading = false
+ }
+ }).catch(() => {
this.downloadLoading = false
+ this.$message({
+ message: '导出失败',
+ type: 'error'
+ })
})
},
- formatJson(filterVal) {
- return this.list.map(v => filterVal.map(j => {
+ formatJson(filterVal, dataSource) {
+ const data = dataSource || this.list
+ return data.map(v => filterVal.map(j => {
if (j === 'timestamp') {
return parseTime(v[j])
} else {
@@ -421,6 +442,7 @@ export default {
const ids = this.selectedList.map(item => item.id)
const newList = this.list.filter(item => !ids.includes(item.id))
this.list = newList
+ this.total = Math.max(0, this.total - ids.length)
this.selectedList = []
this.$notify({
title: 'Success',
@@ -443,25 +465,35 @@ export default {
this.batchExportLoading = true
import('@/vendor/Export2Excel').then(excel => {
- const tHeader = ['timestamp', 'title', 'type', 'importance', 'status']
- const filterVal = ['timestamp', 'title', 'type', 'importance', 'status']
- const data = this.formatJsonForSelected(filterVal)
- excel.export_json_to_excel({
- header: tHeader,
- data,
- filename: 'selected-table-list'
- })
- this.batchExportLoading = false
- })
- },
- formatJsonForSelected(filterVal) {
- return this.selectedList.map(v => filterVal.map(j => {
- if (j === 'timestamp') {
- return parseTime(v[j])
- } else {
- return v[j]
+ try {
+ const tHeader = ['timestamp', 'title', 'type', 'importance', 'status']
+ const filterVal = ['timestamp', 'title', 'type', 'importance', 'status']
+ const data = this.formatJson(filterVal, this.selectedList)
+ const timestamp = parseTime(new Date(), '{y}{m}{d}-{h}{i}{s}')
+ excel.export_json_to_excel({
+ header: tHeader,
+ data,
+ filename: `article-list-${timestamp}`
+ })
+ this.$message({
+ message: '导出成功',
+ type: 'success'
+ })
+ } catch (error) {
+ this.$message({
+ message: '导出失败',
+ type: 'error'
+ })
+ } finally {
+ this.batchExportLoading = false
}
- }))
+ }).catch(() => {
+ this.batchExportLoading = false
+ this.$message({
+ message: '导出失败',
+ type: 'error'
+ })
+ })
}
}
}
From 970ec1710e592c4ccba9a6f74a94b9281ef0f3c5 Mon Sep 17 00:00:00 2001
From: tyro880 <你的GitHub绑定邮箱>
Date: Sat, 18 Apr 2026 18:18:26 +0800
Subject: [PATCH 4/4] =?UTF-8?q?fix(complex-table):=20=E4=BF=AE=E5=A4=8D?=
=?UTF-8?q?=E8=A1=A8=E6=A0=BC=E5=A4=9A=E9=80=89=E5=8A=9F=E8=83=BD=E5=B9=B6?=
=?UTF-8?q?=E4=BC=98=E5=8C=96=E9=80=89=E6=8B=A9=E7=8A=B6=E6=80=81=E7=AE=A1?=
=?UTF-8?q?=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
添加表格引用和row-key属性以支持多选功能
使用selectedMap对象维护跨页选择状态
实现clearSelection方法清除选择状态
修改handleSelectionChange处理跨页选择逻辑
更新删除操作同步维护选择状态
---
src/views/table/complex-table.vue | 40 ++++++++++++++++++++++++++++---
1 file changed, 37 insertions(+), 3 deletions(-)
diff --git a/src/views/table/complex-table.vue b/src/views/table/complex-table.vue
index de037172..7a687529 100644
--- a/src/views/table/complex-table.vue
+++ b/src/views/table/complex-table.vue
@@ -48,6 +48,7 @@
-
+
{{ row.id }}
@@ -249,6 +251,7 @@ export default {
},
downloadLoading: false,
selectedList: [],
+ selectedMap: {},
batchExportLoading: false
}
},
@@ -269,9 +272,17 @@ export default {
})
},
handleFilter() {
+ this.clearSelection()
this.listQuery.page = 1
this.getList()
},
+ clearSelection() {
+ if (this.$refs.multipleTable) {
+ this.$refs.multipleTable.clearSelection()
+ }
+ this.selectedMap = {}
+ this.selectedList = []
+ },
handleModifyStatus(row, status) {
this.$message({
message: '操作Success',
@@ -367,6 +378,8 @@ export default {
})
this.list.splice(index, 1)
this.total = Math.max(0, this.total - 1)
+ delete this.selectedMap[row.id]
+ this.selectedList = Object.values(this.selectedMap)
},
handleFetchPv(pv) {
fetchPv(pv).then(response => {
@@ -422,7 +435,19 @@ export default {
return sort === `+${key}` ? 'ascending' : 'descending'
},
handleSelectionChange(selection) {
- this.selectedList = selection
+ if (!this.list || this.list.length === 0) return
+
+ const currentPageIds = this.list.map(item => item.id)
+
+ for (const id of currentPageIds) {
+ delete this.selectedMap[id]
+ }
+
+ for (const row of selection) {
+ this.selectedMap[row.id] = row
+ }
+
+ this.selectedList = Object.values(this.selectedMap)
},
handleBatchDelete() {
if (this.selectedList.length === 0) {
@@ -443,7 +468,16 @@ export default {
const newList = this.list.filter(item => !ids.includes(item.id))
this.list = newList
this.total = Math.max(0, this.total - ids.length)
- this.selectedList = []
+
+ for (const id of ids) {
+ delete this.selectedMap[id]
+ }
+ this.selectedList = Object.values(this.selectedMap)
+
+ if (this.$refs.multipleTable) {
+ this.$refs.multipleTable.clearSelection()
+ }
+
this.$notify({
title: 'Success',
message: `成功删除 ${ids.length} 条记录`,