diff --git a/src/view/todo/todo-list.vue b/src/view/todo/todo-list.vue index 284bc56..c556eff 100644 --- a/src/view/todo/todo-list.vue +++ b/src/view/todo/todo-list.vue @@ -27,7 +27,10 @@ :data="dataList" :loading="tableLoading"> @@ -200,6 +203,7 @@ export default { addModal: false, // 添加 editModal: false, // 编辑 submitLoading: false, + rowLoading: {}, // 行级 loading:{ 待办id: true },用于开关与删除按钮 formItem: { id: '', title: '', @@ -214,18 +218,27 @@ export default { } }, methods: { - getDataList () { + /** + * 拉取列表 + * @param {Boolean} silent 静默刷新:不显示表格 loading,用于乐观更新后的后台同步 + */ + getDataList (silent) { let temp = JSON.parse(JSON.stringify(this.pageParam)) let data = objectUtil.trimJoin(temp, this.search) - this.tableLoading = true + if (!silent) { + this.tableLoading = true + } + const closeLoading = () => { + this.tableLoading = false + } getDataList(data).then(res => { let page = res.data.data this.pageTotal = page.total this.dataList = page.records - // 延迟300毫秒,给它更流畅的感觉 - setTimeout(() => { - this.tableLoading = false - }, 300) + closeLoading() + }).catch(() => { + // 出错也要复位 loading,否则表格一直转圈 + closeLoading() }) }, toAdd () { @@ -267,53 +280,112 @@ export default { let data = Object.assign({}, this.formItem) // DatePicker 绑定的是 Date 对象,后端按 yyyy-MM-dd 字符串接收 data.dueDate = this.formatDate(this.formItem.dueDate) - if (this.addModal) { // 添加 - add(data).then(res => { - this.$Message.success('添加成功!') - this.refresh() - this.getDataList() - }) - } else { // 编辑 - update(data).then(res => { - this.$Message.success('编辑成功!') - this.refresh() - this.getDataList() - }) - } - this.cancel() + const isAdd = this.addModal + // 按钮转圈:公网穿透链路有延迟,没有反馈会像卡住 + this.submitLoading = true + const req = isAdd ? add(data) : update(data) + req.then(res => { + // 编辑走本地合并,改完立刻看到结果,不等服务端回包 + if (!isAdd) { + this.mergeRow(data) + } + this.$Message.success(isAdd ? '添加成功!' : '编辑成功!') + // 请求成功后再关弹窗,避免"点了没反应" + this.modal = false + this.resetForm() + this.submitLoading = false + // 新增的行 id 由服务端生成,静默拉一次同步(不显示表格 loading) + if (isAdd) { + this.getDataList(true) + } + }).catch(() => { + this.submitLoading = false + this.$Message.error(isAdd ? '添加失败,请重试' : '编辑失败,请重试') + }) } }) }, + /** 把提交的字段合并进当前列表对应行,实现即时更新 **/ + mergeRow (data) { + let row = this.dataList.find(item => String(item.id) === String(data.id)) + if (!row) { + return + } + row.title = data.title + row.status = data.status + row.priority = data.priority + row.dueDate = data.dueDate + row.remark = data.remark + }, cancel () { - // Modal cancel + // Modal cancel:只关弹窗并复位表单,不清空搜索条件与分页 this.modal = false - this.refresh() + this.resetForm() + }, + /** 复位弹窗表单 **/ + resetForm () { + this.formItem = { + id: '', + title: '', + status: 0, + priority: 2, + dueDate: '', + remark: '' + } + this.submitLoading = false }, toDelete (index) { let temp = this.dataList[index] let data = { id: temp.id } + // 删除按钮转圈,给即时反馈 + this.$set(this.rowLoading, temp.id, true) remove(data).then(res => { - this.refresh() - this.getDataList() + // 先本地移除,画面立即响应,再静默同步分页数据 + this.dataList.splice(index, 1) + this.pageTotal = Math.max(0, this.pageTotal - 1) + // 本页删空了就退回上一页,避免停在空页 + if (this.dataList.length === 0 && this.pageParam.pageNum > 1) { + this.pageParam.pageNum -= 1 + } + this.$set(this.rowLoading, temp.id, false) this.$Message.success('删除成功!') + this.getDataList(true) + }).catch(() => { + this.$set(this.rowLoading, temp.id, false) + this.$Message.error('删除失败,请重试') }) }, updateStatus (row, value) { let status = value === true || value === 1 || value === '1' ? 1 : 0 + if (row.status === status) { + return + } let data = { id: row.id, status: status } + const oldStatus = row.status + // 乐观更新:先改本地,开关立刻响应,不等网络 + row.status = status + this.$set(this.rowLoading, row.id, true) updateStatus(data).then(res => { + this.$set(this.rowLoading, row.id, false) this.$Message.success(status === 1 ? '已完成!' : '已标记为未完成!') - this.getDataList() + // 当前按状态筛选且该行已不匹配条件时,静默刷新让它消失 + if (this.search.status !== '' && Number(this.search.status) !== status) { + this.getDataList(true) + } }).catch(() => { - this.getDataList() + // 失败回滚到原状态 + this.$set(this.rowLoading, row.id, false) + row.status = oldStatus + this.$Message.error('状态更新失败,请重试') }) }, refresh () { + // 重置查询条件:回到第一页并重新拉取(原来只清变量不刷列表,点了没变化) this.pageParam = { pageNum: 1, pageSize: 10 @@ -323,14 +395,8 @@ export default { status: '', priority: '' } - this.formItem = { - id: '', - title: '', - status: 0, - priority: 2, - dueDate: '', - remark: '' - } + this.resetForm() + this.getDataList() }, changePage (index) { // 需要显示开始数据的index,(因为数据是从0开始的,页码是从1开始的,需要-1)