3.16-3.19
摘要
设计todo list页面接口
过程
定接口
要完成以下功能:
1.获取日历定位日期所在月份所有日子的已完成待办数量、未完成代办数量
2.获取某一天的所有待办,并显示其完成状态
3.用户对某一天增加一个待办
4.用户删除一个待办
5.用户完成一个待办
6.用户取消完成一个待办
待办分为两种,一种是从作业中直接导出的,这种待办不需要添加自动呈现,并且无法删除,无法勾选,只有提交了作业后系统自动勾选。另一种是自己添加的待办,可以删除、勾选等。
页面设计:
中间是一个日历,刚进入页面的时候日历显示今天日期所在月,右侧是一个待办列表,列出当日待办,呈现出完成或未完成状态,并显示当日完成度。可以通过点击其他日期看到其他日期的待办。如果点击的是其他月份的日期,则日历翻到所选日期当月。
此外,当前日期前日期的待办在日历上显示完成度,待办列表项目不允许删除,不允许勾选或取消勾选(所以如果立下的flag没有完成,就会被钉在历史的耻辱柱上),今后日期在日历上显示未完成待办的数量,自己添加的待办可以勾选、取消勾选、删除等。
element-ui提供了日历组件,但标准的日历组件无法实现按后端给的数据显示未完成待办的数量或完成度。element-ui倒是有扩展的日历组件可以实现点击时出现√,再次点击取消√。我沿用了这一思路,魔改了日历组件,最终实现了上述要求。虽然最后代码不是很多,但这是我尝试了无数方法之后终于成功的,值得放在这里。
<template>
<div>
<el-row style="margin-left: 15px">
<el-col :span="15">
<!-- <el-calendar v-model="value" style="width: 300px;margin-left: 20px" :class="diff(value)">-->
<!-- </el-calendar>-->
<el-calendar v-model="value" id="calendar" style="min-width: 400px">
<!-- 这里使用的是 2.5 slot 语法,对于新项目请使用 2.6 slot 语法-->
<template
slot="dateCell"
slot-scope="{date, data}"
>
<!--自定义内容-->
<div>
<div class="calendar-day">{{ data.day.split('-').slice(2).join('-') }}</div>
<div v-for="item in calendarData" :key="item.ddl_day">
<div v-if="data.day.split('-').slice(1)[0]==cur_date.getMonth()+1">
<div v-if="(item.percentage)!=-1&&(item.ddl_day)==data.day.split('-').slice(2).join('-')">
<!-- <el-tooltip class="item" effect="dark" :content="item.things" placement="right">-->
<el-tag v-if="ifShow(data)&&(item.ct)>=0" class="ml-2"
type="success">{{ (item.percentage).toFixed(2) }}%
</el-tag>
<!-- <el-tag type="danger" effect="dark" class="tips">{{ item.count }}</el-tag>-->
<!-- </el-tooltip>-->
<!-- <el-tooltip class="item" effect="dark" :content="item.things" placement="right">-->
<el-tag v-if="!ifShow(data)&&(item.ct)>0" type="danger" effect="dark" class="tips">{{
item.ct
}}
</el-tag>
<!-- </el-tooltip>-->
</div>
<div v-else></div>
</div>
<div v-else></div>
</div>
</div>
</template>
</el-calendar>
</el-col>
<el-col :span="8" :offset="1">
<el-card style="text-align: left; margin-left: 0;max-width: 370px" shadow="hover">
<template #header>
<b>{{ (value.getYear() + 1900) + '-' + (value.getMonth() + 1) + '-' + value.getDate() }}</b>
<el-progress :text-inside="true"
:stroke-width="15"
:percentage="parseFloat((calendarData[value.getDate()-1].percentage).toFixed(2))"
:color="customColors"
v-if="calendarData[value.getDate()-1].percentage!=-1"
style="width: 150px;display: inline;padding-left: 20px"/>
</template>
<el-form label-width="0">
<el-form-item v-for="item in items" :key="item.id" style="padding-left: 20px;margin-bottom: 10px">
<el-checkbox :value="turn2bool(item.isFinished)" @change="tick($event,item)"
:disabled="A_less_B(value,today) || item.source==1"
style="line-height: 20px">
{{ item.title }}
</el-checkbox>
<el-popconfirm
confirm-button-text="删除"
cancel-button-text="取消"
icon-color="red"
title="确认删除此待办吗?"
@confirm="deleteOneTodo(item.no)"
>
<template #reference>
<el-button type="text" class="el-icon-close" id="delete"
:disabled="A_less_B(value,today) || item.source==1"
style="padding-top: 0;padding-bottom: 0;float: right;color: red"></el-button>
</template>
</el-popconfirm>
</el-form-item>
<el-form-item style="padding-left: 20px">
<div class="new_item">
<i class="el-icon-circle-plus" style="color: #a0cfff "></i>
<el-input v-model="new_todo"
class="line_input"
style="width: 70%;padding-left: 5px;line-height: 20px"
v-on:keyup.enter.native="addOneTodo"
/>
</div>
</el-form-item>
</el-form>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: "student_todolist",
data() {
return {
user_id: localStorage.getItem("user_id"),
user_name: localStorage.getItem("username"),
user_role: 'student',
new_todo: '',
// calendarData: [
// {months: ['03', '04'], days: ['01'], count: 3, percentage: 55.8, things: '😀'},
// {months: ['03', '04'], days: ['02'], count: 2, percentage: 24.8, things: '哈哈'},
// {months: ['03'], days: ['11'], count: 1, percentage: 33.4, things: '看星星'},
// {months: ['03'], days: ['12'], count: 5, percentage: 27.1, things: '看月亮'}
// ],
calendarData: [
// {ct: 3, percentage: 22.4, ddl_day: 1},
// {ct: 4, percentage: 23.4, ddl_day: 2},
// {ct: 5, percentage: 24.4, ddl_day: 3},
// {ct: 6, percentage: 25.4, ddl_day: 4},
],
finish_percentage: [3, 14, 25, 36, 47, 58, 69, 70, 81, 92, 3, 14, 25, 36, 47, 58, 69, 70, 81, 92, 3, 14, 25, 36, 47, 58, 69, 70, 81, 92],
customColors: [
{color: '#f56c6c', percentage: 20},
{color: '#e6a23c', percentage: 40},
{color: '#6f7ad3', percentage: 60},
{color: '#1989fa', percentage: 80},
{color: '#5cb87a', percentage: 100},
],
value: new Date(),
checked: false,
// last_date: new Date(),
items: [
// {
// id: 0,
// todo: '写完项目实训',
// ok: false
// },
// {
// id: 1,
// todo: '刷完leetcode',
// ok: false
// },
// {
// id: 2,
// todo: '读完高网',
// ok: false
// }
],
cur_item_id: '',//点击一个item的时候会把这个item的id放进来
checking_item_id: '',
unchecking_item_id: '',
del_id: [],
cur_date: '',
today: '',
queryForm: {
homeworkId: "",
homeworkTitle: ""
},
entityForm: {},
tableData: [],
/*pageSize: homeworkApi.pageSize,*/
pageCount: 1,
pageIndex: 1,
editing: false
}
},
created() {
this.cur_date = new Date()
this.today = new Date()
// 下拉当月每天未完成的事情的个数和第一件事的名称(后面加个"等")
// 下拉todo列表,注意来自系统的已完成的也要拉下来,然后计算进度百分比,并且数出来
this.getMonthlyDue(this.today)
this.getOneDayTodo(this.today)
},
watch: {
'value': {
handler(newValue, oldValue) {
console.log("change date")
this.cur_date = newValue
console.log("cur_date=" + newValue)
if (newValue.getYear() !== oldValue.getYear() || newValue.getMonth() !== oldValue.getMonth()) {
//换月份了,需要重新加载日历到新日期
this.getMonthlyDue(newValue)
}
if (newValue !== oldValue) {
//换日期了,需要重新加载todo
this.getOneDayTodo(newValue)
console.log("value=" + this.value)
console.log("today=" + this.today)
console.log("value<today:" + (this.value < this.today))
}
},
// 深度监听 监听对象,数组的变化
deep: true
}
},
methods: {
A_less_B(d1, d2) {
var d1y = d1.getYear();
var d1m = d1.getMonth();
var d1d = d1.getDate();
var d2y = d2.getYear();
var d2m = d2.getMonth();
var d2d = d2.getDate();
if (d1y < d2y) return true;
if (d1m < d2m) return true;
if (d1d < d2d) return true;
else return false;
},
turn2bool(int_val) {
return int_val == 1;
},
ifShow(data) {
let today_year = (this.today.getYear() + 1900)
let today_month = (this.today.getMonth() + 1)
if (today_month < 10) today_month = '0' + today_month
let today_day = this.today.getDate()
if (today_day < 10) today_day = '0' + today_day
// console.log(data.day)
// console.log((today_year+'-'+today_month+'-'+today_day))
return data.day < (today_year + '-' + today_month + '-' + today_day)
},
//所有涉及刷新日历的操作,都要把日历定位到刷新前的位置
getMonthlyDue(tar_day) {
console.log("tar_day=" + tar_day)
//mon中包括year,month
//当日之前的显示完成的百分比,当日及之后的显示未完成的数目
let cur_year = (tar_day.getYear() + 1900)
let cur_month = (tar_day.getMonth() + 1)
console.log("cur_year:" + cur_year + ", cur_month:" + cur_month)
this.axios({
url: '/student/todo/getMonthlyDue',
method: 'get',
params: {
id: this.user_id,
year: cur_year,
month: cur_month
},
headers: {
'token':localStorage.getItem("token")
},
}).then(
(res) => {
console.log(res)
this.calendarData = res.data
},
function (res) {
console.log(res)
console.log('啊呀,出错啦')
}
)
},
getOneDayTodo(tar_day) {
//当日日期显示为today,其他显示日期;当日之前不允许修改,用灰色,无法添加新的;当日及之后可以修改
let cur_day = tar_day.getDate()
let cur_year = (tar_day.getYear() + 1900)
let cur_month = (tar_day.getMonth() + 1)
this.axios({
url: '/student/todo/getOneDayTodo',
method: 'get',
params: {
id: this.user_id,
day: cur_day,
month: cur_month,
year: cur_year,
},
headers: {
'token':localStorage.getItem("token")
},
}).then(
(res) => {
console.log(res)
this.items = res.data
this.cur_date = tar_day
},
function (res) {
console.log(res)
console.log('啊呀,出错啦')
}
)
},
addOneTodo() {//增完了刷新日历和todo
console.log("addOneTodo:" + this.new_todo)
console.log("cur_date: " + this.cur_date)
let cur_day = this.cur_date.getDate()
let cur_year = (this.cur_date.getYear() + 1900)
let cur_month = (this.cur_date.getMonth() + 1)
this.axios({
url: '/student/todo/addOneTodo',
method: 'post',
transformRequest: [function (data) { //在请求之前对data传参进行格式转换
data = JSON.stringify(data)
return data
}],
data: {
id: this.user_id,
day: cur_day,
month: cur_month,
year: cur_year,
content: this.new_todo
},
headers: {
'Content-Type': 'application/json;charset=utf-8',
'token':localStorage.getItem("token")
}
}).then(
(res) => {
// 在 then的内部不能使用Vue的实例化的this, 因为在内部 this 没有被绑定。
console.log(res)
//成功后清空new_todo
this.new_todo = ""
this.getOneDayTodo(this.value)
this.getMonthlyDue(this.value)
},
function (res) {
alert("操作未成功")
console.log(res)
console.log('啊呀,出错啦')
}
)
},
deleteOneTodo(del_id) {//系统导出的不可删除,删完了刷新todo和日历
// let cur_day = this.cur_date.getDate()
// let cur_year = (this.cur_date.getYear() + 1900)
// let cur_month = (this.cur_date.getMonth() + 1)
console.log("I'm going to delete "+del_id)
this.axios({
url: '/student/todo/deleteOneTodo',
method: 'delete',
transformRequest: [function (data) { //在请求之前对data传参进行格式转换
data = JSON.stringify(data)
return data
}],
data: {
id: this.user_id,
// year: cur_year,
// month: cur_month,
// day: cur_day,
del_todo_id: del_id
},
headers: {
'Content-Type': 'application/json;charset=utf-8',
'token':localStorage.getItem("token")
}
}).then(
(res) => {
// 在 then的内部不能使用Vue的实例化的this, 因为在内部 this 没有被绑定。
console.log(res)
//成功后刷新
this.getOneDayTodo(this.value)
this.getMonthlyDue(this.value)
},
function (res) {
alert("操作未成功")
console.log(res)
console.log('啊呀,出错啦')
}
)
},
tick(val, item) {
console.log(val)
console.log(item)
this.axios({
url: '/student/todo/tickOneTodo',
method: 'post',
transformRequest: [function (data) { //在请求之前对data传参进行格式转换
data = JSON.stringify(data)
return data
}],
data: {
id: this.user_id,
todo_id: item.no,
isFinished: val
},
headers: {
'Content-Type': 'application/json;charset=utf-8',
'token':localStorage.getItem("token")
}
}).then(
(res) => {
// 在 then的内部不能使用Vue的实例化的this, 因为在内部 this 没有被绑定。
console.log(res)
item.isFinished = val
this.getMonthlyDue(this.value)
},
function (res) {
alert("操作未成功")
console.log(res)
console.log('啊呀,出错啦')
item.isFinished = !val;
}
)
},
created() {
this.query();
}
}
}
</script>
<style>
.el-form-item {
padding-left: 20px;
margin-bottom: 10px;
}
.new_item:hover {
color: #409EFF;
}
.main {
padding-left: 10px;
}
.is-selected {
color: #1989fa;
}
.el-progress {
width: 50px;
}
.line_input .el-input__inner {
border-radius: 0;
border-top-color: #ffffff;
border-left-color: #ffffff;
border-right-color: #ffffff;
height: 30px;
}
.line_input .el-input__inner:focus {
border-radius: 0;
border-top-color: #ffffff;
border-left-color: #ffffff;
border-right-color: #ffffff;
border-bottom-color: #585858;
}
.line_input .el-input__inner:hover {
border-radius: 0;
border-top-color: #ffffff;
border-left-color: #ffffff;
border-right-color: #ffffff;
border-bottom-color: #585858;
}
.el-checkbox__label {
font-size: 16px;
}
.el-progress-bar {
width: 50%;
}
.ml-2 {
font-size: 10px;
height: 20px;
line-height: 20px;
text-align: center;
margin: auto;
margin-top: 5px;
padding-right: unset;
padding-left: unset;
width: 45px;
}
.tips {
color: #F8A535;
font-size: 10px;
margin-top: 5px;
border-radius: 10px;
padding-left: 5px;
padding-right: 5px;
height: 20px;
width: 20px;
line-height: 20px;
margin-left: 20px;
float: right;
}
#calendar .el-button-group > .el-button:not(:first-child):not(:last-child):after {
content: '当月';
}
.el-calendar-table .el-calendar-day {
height: 60px;
}
</style>
最终效果: