feat(todo): 新增待办清单前端页面
CI/CD Deploy (Web) / 构建前端镜像并部署 (push) Failing after 10m27s

- src/api/todo/todo.js 接口层(列表/新增/更新/改状态/删除)
- src/view/todo/todo-list.vue 列表+表单+Modal,风格对齐 sys/delta 页面
This commit is contained in:
zhangjiaxun
2026-10-16 23:27:37 +08:00
parent 15cae333a3
commit 2d6cac2d06
2 changed files with 440 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
import axios from '@/libs/api.request'
import { getToken } from '@/libs/util'
/** 分页查询当前用户的待办列表 **/
export const getDataList = (data) => {
return axios.request({
url: 'todo/findPageList',
headers: {
'Authorization': getToken()
},
data: data,
method: 'post'
})
}
/** 新增待办 **/
export const add = (data) => {
return axios.request({
url: 'todo/add',
headers: {
'Authorization': getToken()
},
data: data,
method: 'post'
})
}
/** 更新待办 **/
export const update = (data) => {
return axios.request({
url: 'todo/update',
headers: {
'Authorization': getToken()
},
data: data,
method: 'post'
})
}
/** 勾选/取消完成 **/
export const updateStatus = (data) => {
return axios.request({
url: 'todo/updateStatus',
headers: {
'Authorization': getToken()
},
data: data,
method: 'post'
})
}
/** 删除待办 **/
export const remove = (data) => {
return axios.request({
url: 'todo/remove',
headers: {
'Authorization': getToken()
},
data: data,
method: 'post'
})
}
+378
View File
@@ -0,0 +1,378 @@
<template>
<div>
<!-- 查询区域 start -->
<div class="option">
<Input v-model="search.title" class="short-input" clearable placeholder="标题"/>
<Select v-model="search.status" class="short-input" clearable placeholder="状态">
<Option :value="0">未完成</Option>
<Option :value="1">已完成</Option>
</Select>
<Select v-model="search.priority" class="short-input" clearable placeholder="优先级">
<Option :value="1">低</Option>
<Option :value="2">中</Option>
<Option :value="3">高</Option>
</Select>
<Button class="free" type="primary" icon="ios-search" @click="getDataList()">查询</Button>
<Button class="free" type="primary" icon="md-refresh" @click="refresh()">重置</Button>
</div>
<!-- 查询区域 end -->
<!-- 列表区域 start -->
<div class="table-button-list">
<Button class="free" type="info" icon="md-add" @click="toAdd">添加</Button>
</div>
<Table :height="fixedHeader ? autoHeight : ''"
border ref="table"
:columns="listColumns"
:data="dataList"
:loading="tableLoading">
<template slot-scope="{ row }" slot="status">
<i-switch :value="row.status === 1" true-value="1" false-value="0"
@on-change="val => updateStatus(row, val)">
<span slot="open">完成</span>
<span slot="close">未完成</span>
</i-switch>
</template>
<template slot-scope="{ row }" slot="priority">
<Tag :color="priorityColor(row.priority)">{{ priorityName(row.priority) }}</Tag>
</template>
<template slot-scope="{ row, index }" slot="action">
<Button type="primary"
size="small"
style="margin-right: 5px"
icon="ios-create"
@click="toEdit(index)">编辑
</Button>
<Poptip
confirm
title="您确定要删除吗?"
:transfer="true"
@on-ok="toDelete(index)"
>
<Button type="error" size="small" icon="md-trash">删除</Button>
</Poptip>
</template>
</Table>
<div class="pagination">
<Page show-elevator show-sizer show-total
:total="pageTotal" :page-size="pageParam.pageSize"
:current="pageParam.pageNum"
@on-page-size-change="changePageSize" @on-change="changePage"></Page>
</div>
<!-- 列表区域 end -->
<!-- Modal区域 start -->
<Modal v-model="modal" :title="modalTitle" :styles="{top: '20px'}" width="700" :mask-closable="false"
@on-cancel="cancel()">
<Form :rules="ruleCustom" :model="formItem" ref="formItem" :label-width="110" label-position="right">
<Row :gutter="32">
<Col span="24">
<FormItem label="标题" prop="title">
<Input type="text" class="form-input" v-model="formItem.title" placeholder="请输入待办标题"/>
</FormItem>
</Col>
<Col span="12">
<FormItem label="优先级" prop="priority">
<RadioGroup v-model="formItem.priority">
<Radio :label="1"><span>低</span></Radio>
<Radio :label="2"><span>中</span></Radio>
<Radio :label="3"><span>高</span></Radio>
</RadioGroup>
</FormItem>
</Col>
<Col span="12">
<FormItem label="状态" prop="status">
<RadioGroup v-model="formItem.status">
<Radio :label="0"><span>未完成</span></Radio>
<Radio :label="1"><span>已完成</span></Radio>
</RadioGroup>
</FormItem>
</Col>
<Col span="12">
<FormItem label="截止日期" prop="dueDate">
<DatePicker type="date"
class="form-input"
format="yyyy-MM-dd"
v-model="formItem.dueDate"
placeholder="请选择截止日期">
</DatePicker>
</FormItem>
</Col>
<Col span="24">
<FormItem label="备注" prop="remark">
<Input type="textarea" :rows="3" maxlength="200" show-word-limit class="form-input"
v-model="formItem.remark" placeholder="选填:补充说明"/>
</FormItem>
</Col>
</Row>
</Form>
<div slot="footer">
<Button type="text" size="large" @click="cancel">取消</Button>
<Button type="primary" size="large"
:loading="submitLoading"
@click="submit('formItem')">提交
</Button>
</div>
</Modal>
<!-- Modal区域 end -->
</div>
</template>
<script>
import { getDataList, add, update, updateStatus, remove } from '@/api/todo/todo'
import { objectUtil } from '@/common/js/util'
export default {
name: 'todo-list',
data () {
return {
fixedHeader: true, // 高度自适应
autoHeight: `${window.innerHeight} ` * 0.55, // 获取当前可视高度-定义Table高度
search: {
title: '',
status: '',
priority: ''
},
listColumns: [
{
title: '#',
type: 'index',
width: 60,
align: 'center',
indexMethod: (row) => {
return (row._index + 1) + ((this.pageParam.pageNum - 1) * this.pageParam.pageSize)
}
},
{
title: '标题',
key: 'title',
minWidth: 200
},
{
title: '状态',
slot: 'status',
width: 110,
align: 'center'
},
{
title: '优先级',
slot: 'priority',
width: 100,
align: 'center'
},
{
title: '截止日期',
key: 'dueDate',
width: 120,
align: 'center'
},
{
title: '备注',
key: 'remark',
minWidth: 180,
ellipsis: true,
tooltip: true
},
{
title: '创建时间',
key: 'createDate',
width: 160,
align: 'center'
},
{
title: '操作区',
slot: 'action',
width: 160,
fixed: 'right',
align: 'center'
}
],
dataList: [],
tableLoading: false,
pageTotal: 0, // 总页数
pageParam: { // 调用时候的分页参数
pageNum: 1,
pageSize: 10
},
modal: false,
modalTitle: '',
addModal: false, // 添加
editModal: false, // 编辑
submitLoading: false,
formItem: {
id: '',
title: '',
status: 0,
priority: 2,
dueDate: '',
remark: ''
},
ruleCustom: {
title: [{ required: true, message: '请输入标题', trigger: 'blur' }]
}
}
},
methods: {
getDataList () {
let temp = JSON.parse(JSON.stringify(this.pageParam))
let data = objectUtil.trimJoin(temp, this.search)
this.tableLoading = true
getDataList(data).then(res => {
let page = res.data.data
this.pageTotal = page.total
this.dataList = page.records
// 延迟300毫秒,给它更流畅的感觉
setTimeout(() => {
this.tableLoading = false
}, 300)
})
},
toAdd () {
this.modalTitle = '添加'
this.modal = true
this.addModal = true
this.editModal = false
this.$nextTick(() => {
if (this.$refs.formItem) {
this.$refs.formItem.resetFields()
}
})
},
toEdit (index) {
this.modalTitle = '编辑'
let temp = this.dataList[index]
this.addModal = false
this.editModal = true
this.modal = true
this.$nextTick(() => {
// 清除上次校验状态,避免编辑回显报红
if (this.$refs.formItem) {
this.$refs.formItem.resetFields()
}
this.formItem.id = temp.id
this.formItem.title = temp.title
this.formItem.status = temp.status
this.formItem.priority = temp.priority
this.formItem.dueDate = temp.dueDate ? new Date(String(temp.dueDate).replace(/-/g, '/')) : ''
this.formItem.remark = temp.remark || ''
})
},
submit (formItem) {
// modal 提交
this.$refs[formItem].validate((valid) => {
if (valid) {
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()
}
})
},
cancel () {
// Modal cancel
this.modal = false
this.refresh()
},
toDelete (index) {
let temp = this.dataList[index]
let data = {
id: temp.id
}
remove(data).then(res => {
this.refresh()
this.getDataList()
this.$Message.success('删除成功!')
})
},
updateStatus (row, value) {
let status = value === true || value === 1 || value === '1' ? 1 : 0
let data = {
id: row.id,
status: status
}
updateStatus(data).then(res => {
this.$Message.success(status === 1 ? '已完成!' : '已标记为未完成!')
this.getDataList()
}).catch(() => {
this.getDataList()
})
},
refresh () {
this.pageParam = {
pageNum: 1,
pageSize: 10
}
this.search = {
title: '',
status: '',
priority: ''
}
this.formItem = {
id: '',
title: '',
status: 0,
priority: 2,
dueDate: '',
remark: ''
}
},
changePage (index) {
// 需要显示开始数据的index,(因为数据是从0开始的,页码是从1开始的,需要-1)
this.pageParam.pageNum = index
this.getDataList()
},
changePageSize (page) {
// 切换分页条数
this.pageParam.pageNum = 1
this.pageParam.pageSize = page
this.getDataList()
},
/** 日期格式化为 yyyy-MM-dd,空值返回空串 **/
formatDate (date) {
if (!date) {
return ''
}
let d = date instanceof Date ? date : new Date(String(date).replace(/-/g, '/'))
if (isNaN(d.getTime())) {
return ''
}
let month = d.getMonth() + 1
let day = d.getDate()
return `${d.getFullYear()}-${month < 10 ? '0' + month : month}-${day < 10 ? '0' + day : day}`
},
priorityName (priority) {
let map = { 1: '低', 2: '中', 3: '高' }
return map[priority] || '中'
},
priorityColor (priority) {
let map = { 1: 'default', 2: 'blue', 3: 'red' }
return map[priority] || 'blue'
}
},
created () {
this.getDataList()
}
}
</script>
<style lang="less" scoped>
.form-input {
width: 100%
}
</style>