优化(todo): 添加/更新/删除/状态切换加loading+乐观更新,修复Switch类型抛错
CI/CD Deploy (Web) / 构建前端镜像并部署 (push) Successful in 19s

This commit is contained in:
张家训
2026-10-22 17:38:19 +08:00
parent 8fb8154820
commit 65c37d3b4a
+100 -34
View File
@@ -27,7 +27,10 @@
:data="dataList" :data="dataList"
:loading="tableLoading"> :loading="tableLoading">
<template slot-scope="{ row }" slot="status"> <template slot-scope="{ row }" slot="status">
<i-switch :value="row.status === 1" true-value="1" false-value="0" <!-- value 必须与 true-value/false-value 同类型(都是数字),否则组件内部会抛
'Value should be trueValue or falseValue.' 且状态不更新 -->
<i-switch :value="row.status" :true-value="1" :false-value="0"
:loading="!!rowLoading[row.id]"
@on-change="val => updateStatus(row, val)"> @on-change="val => updateStatus(row, val)">
<span slot="open">完成</span> <span slot="open">完成</span>
<span slot="close">未完成</span> <span slot="close">未完成</span>
@@ -49,7 +52,7 @@
:transfer="true" :transfer="true"
@on-ok="toDelete(index)" @on-ok="toDelete(index)"
> >
<Button type="error" size="small" icon="md-trash">删除</Button> <Button type="error" size="small" icon="md-trash" :loading="!!rowLoading[row.id]">删除</Button>
</Poptip> </Poptip>
</template> </template>
</Table> </Table>
@@ -200,6 +203,7 @@ export default {
addModal: false, // 添加 addModal: false, // 添加
editModal: false, // 编辑 editModal: false, // 编辑
submitLoading: false, submitLoading: false,
rowLoading: {}, // 行级 loading:{ 待办id: true },用于开关与删除按钮
formItem: { formItem: {
id: '', id: '',
title: '', title: '',
@@ -214,18 +218,27 @@ export default {
} }
}, },
methods: { methods: {
getDataList () { /**
* 拉取列表
* @param {Boolean} silent 静默刷新:不显示表格 loading,用于乐观更新后的后台同步
*/
getDataList (silent) {
let temp = JSON.parse(JSON.stringify(this.pageParam)) let temp = JSON.parse(JSON.stringify(this.pageParam))
let data = objectUtil.trimJoin(temp, this.search) let data = objectUtil.trimJoin(temp, this.search)
if (!silent) {
this.tableLoading = true this.tableLoading = true
}
const closeLoading = () => {
this.tableLoading = false
}
getDataList(data).then(res => { getDataList(data).then(res => {
let page = res.data.data let page = res.data.data
this.pageTotal = page.total this.pageTotal = page.total
this.dataList = page.records this.dataList = page.records
// 延迟300毫秒,给它更流畅的感觉 closeLoading()
setTimeout(() => { }).catch(() => {
this.tableLoading = false // 出错也要复位 loading,否则表格一直转圈
}, 300) closeLoading()
}) })
}, },
toAdd () { toAdd () {
@@ -267,53 +280,112 @@ export default {
let data = Object.assign({}, this.formItem) let data = Object.assign({}, this.formItem)
// DatePicker 绑定的是 Date 对象,后端按 yyyy-MM-dd 字符串接收 // DatePicker 绑定的是 Date 对象,后端按 yyyy-MM-dd 字符串接收
data.dueDate = this.formatDate(this.formItem.dueDate) data.dueDate = this.formatDate(this.formItem.dueDate)
if (this.addModal) { // 添加 const isAdd = this.addModal
add(data).then(res => { // 按钮转圈:公网穿透链路有延迟,没有反馈会像卡住
this.$Message.success('添加成功!') this.submitLoading = true
this.refresh() const req = isAdd ? add(data) : update(data)
this.getDataList() req.then(res => {
}) // 编辑走本地合并,改完立刻看到结果,不等服务端回包
} else { // 编辑 if (!isAdd) {
update(data).then(res => { this.mergeRow(data)
this.$Message.success('编辑成功!')
this.refresh()
this.getDataList()
})
} }
this.cancel() 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 () { cancel () {
// Modal cancel // Modal cancel:只关弹窗并复位表单,不清空搜索条件与分页
this.modal = false this.modal = false
this.refresh() this.resetForm()
},
/** 复位弹窗表单 **/
resetForm () {
this.formItem = {
id: '',
title: '',
status: 0,
priority: 2,
dueDate: '',
remark: ''
}
this.submitLoading = false
}, },
toDelete (index) { toDelete (index) {
let temp = this.dataList[index] let temp = this.dataList[index]
let data = { let data = {
id: temp.id id: temp.id
} }
// 删除按钮转圈,给即时反馈
this.$set(this.rowLoading, temp.id, true)
remove(data).then(res => { 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.$Message.success('删除成功!')
this.getDataList(true)
}).catch(() => {
this.$set(this.rowLoading, temp.id, false)
this.$Message.error('删除失败,请重试')
}) })
}, },
updateStatus (row, value) { updateStatus (row, value) {
let status = value === true || value === 1 || value === '1' ? 1 : 0 let status = value === true || value === 1 || value === '1' ? 1 : 0
if (row.status === status) {
return
}
let data = { let data = {
id: row.id, id: row.id,
status: status status: status
} }
const oldStatus = row.status
// 乐观更新:先改本地,开关立刻响应,不等网络
row.status = status
this.$set(this.rowLoading, row.id, true)
updateStatus(data).then(res => { updateStatus(data).then(res => {
this.$set(this.rowLoading, row.id, false)
this.$Message.success(status === 1 ? '已完成!' : '已标记为未完成!') this.$Message.success(status === 1 ? '已完成!' : '已标记为未完成!')
this.getDataList() // 当前按状态筛选且该行已不匹配条件时,静默刷新让它消失
if (this.search.status !== '' && Number(this.search.status) !== status) {
this.getDataList(true)
}
}).catch(() => { }).catch(() => {
this.getDataList() // 失败回滚到原状态
this.$set(this.rowLoading, row.id, false)
row.status = oldStatus
this.$Message.error('状态更新失败,请重试')
}) })
}, },
refresh () { refresh () {
// 重置查询条件:回到第一页并重新拉取(原来只清变量不刷列表,点了没变化)
this.pageParam = { this.pageParam = {
pageNum: 1, pageNum: 1,
pageSize: 10 pageSize: 10
@@ -323,14 +395,8 @@ export default {
status: '', status: '',
priority: '' priority: ''
} }
this.formItem = { this.resetForm()
id: '', this.getDataList()
title: '',
status: 0,
priority: 2,
dueDate: '',
remark: ''
}
}, },
changePage (index) { changePage (index) {
// 需要显示开始数据的index,(因为数据是从0开始的,页码是从1开始的,需要-1) // 需要显示开始数据的index,(因为数据是从0开始的,页码是从1开始的,需要-1)