327 lines
8.5 KiB
Vue
327 lines
8.5 KiB
Vue
|
|
<template>
|
|||
|
|
<div>
|
|||
|
|
<el-timeline v-if="localActivities && localActivities.length > 0">
|
|||
|
|
<el-timeline-item
|
|||
|
|
v-for="(activity, index) in localActivities"
|
|||
|
|
:key="activity.id || index"
|
|||
|
|
:timestamp="activity.planPayDate"
|
|||
|
|
placement="top"
|
|||
|
|
>
|
|||
|
|
<el-card class="editable-card">
|
|||
|
|
<!-- 编辑状态 -->
|
|||
|
|
<div v-if="activity.isEditing" class="edit-mode">
|
|||
|
|
<div class="form-item">
|
|||
|
|
<el-input
|
|||
|
|
v-model="activity.detailName"
|
|||
|
|
placeholder="进度名称"
|
|||
|
|
size="small"
|
|||
|
|
></el-input>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="time-pickers">
|
|||
|
|
<el-date-picker
|
|||
|
|
v-model="activity.actualStartDate"
|
|||
|
|
type="date"
|
|||
|
|
:default-value="
|
|||
|
|
activity.actualStartDate
|
|||
|
|
? activity.actualStartDate
|
|||
|
|
: activity.planPayDate
|
|||
|
|
"
|
|||
|
|
placeholder="实际开始时间"
|
|||
|
|
value-format="yyyy-MM-dd"
|
|||
|
|
size="small"
|
|||
|
|
></el-date-picker>
|
|||
|
|
|
|||
|
|
<el-date-picker
|
|||
|
|
v-model="activity.actualEndDate"
|
|||
|
|
type="date"
|
|||
|
|
:default-value="
|
|||
|
|
activity.actualEndDate
|
|||
|
|
? activity.actualEndDate
|
|||
|
|
: activity.planPayDate
|
|||
|
|
"
|
|||
|
|
placeholder="实际结束时间"
|
|||
|
|
value-format="yyyy-MM-dd"
|
|||
|
|
size="small"
|
|||
|
|
:disabled-date="
|
|||
|
|
(endDate) =>
|
|||
|
|
activity.actualStartDate &&
|
|||
|
|
new Date(endDate) < new Date(activity.actualStartDate)
|
|||
|
|
"
|
|||
|
|
></el-date-picker>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- <div class="form-item">
|
|||
|
|
<el-select>
|
|||
|
|
|
|||
|
|
</el-select>
|
|||
|
|
</div> -->
|
|||
|
|
|
|||
|
|
<div class="actions">
|
|||
|
|
<el-button type="danger" size="mini" @click="cancelEdit(index)"
|
|||
|
|
>取消</el-button
|
|||
|
|
>
|
|||
|
|
<el-button type="success" size="mini" @click="confirmEdit(index)"
|
|||
|
|
>确认</el-button
|
|||
|
|
>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 展示状态 -->
|
|||
|
|
<div v-else class="view-mode">
|
|||
|
|
<div class="header">
|
|||
|
|
<span style="gap: 8px; display: flex">
|
|||
|
|
<span class="title">{{
|
|||
|
|
activity.detailName || "未命名进度"
|
|||
|
|
}}</span>
|
|||
|
|
<el-tag
|
|||
|
|
v-if="activity.detailStatus == 1"
|
|||
|
|
type="success"
|
|||
|
|
size="mini"
|
|||
|
|
effect="dark"
|
|||
|
|
>
|
|||
|
|
已完成
|
|||
|
|
</el-tag>
|
|||
|
|
</span>
|
|||
|
|
|
|||
|
|
<span style="gap: 8px; display: flex">
|
|||
|
|
<el-button
|
|||
|
|
type="primary"
|
|||
|
|
icon="el-icon-edit"
|
|||
|
|
size="mini"
|
|||
|
|
@click.stop="toggleEdit(index)"
|
|||
|
|
></el-button>
|
|||
|
|
<el-button
|
|||
|
|
v-if="activity.detailStatus != 1"
|
|||
|
|
type="success"
|
|||
|
|
icon="el-icon-check"
|
|||
|
|
size="mini"
|
|||
|
|
@click.stop="markAsComplete(index)"
|
|||
|
|
></el-button>
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="time-info">
|
|||
|
|
<!-- 计划开始时间 -->
|
|||
|
|
<div v-if="activity.planStartDate" class="time-item">
|
|||
|
|
<i class="el-icon-circle-check"></i>
|
|||
|
|
计划开始:{{ formatDate(activity.planStartDate) }}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 剩余时间 -->
|
|||
|
|
<div
|
|||
|
|
v-if="
|
|||
|
|
activity.remainTime !== undefined &&
|
|||
|
|
activity.detailStatus != 1
|
|||
|
|
"
|
|||
|
|
class="time-item"
|
|||
|
|
>
|
|||
|
|
<i
|
|||
|
|
class="el-icon-time"
|
|||
|
|
:style="{ color: remainTimeColor(activity) }"
|
|||
|
|
></i>
|
|||
|
|
<span :style="{ color: remainTimeColor(activity) }">
|
|||
|
|
剩余时间:{{ activity.remainTime }}天
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
<div v-if="activity.actualStartDate" class="time-item">
|
|||
|
|
<i class="el-icon-time"></i>
|
|||
|
|
{{
|
|||
|
|
formatDate(
|
|||
|
|
activity.actualStartDate
|
|||
|
|
? activity.actualStartDate
|
|||
|
|
: activity.planStartDate
|
|||
|
|
)
|
|||
|
|
}}
|
|||
|
|
</div>
|
|||
|
|
<div v-if="activity.actualEndDate" class="time-item">
|
|||
|
|
<i class="el-icon-circle-check"></i>
|
|||
|
|
{{
|
|||
|
|
formatDate(
|
|||
|
|
activity.actualEndDate
|
|||
|
|
? activity.actualEndDate
|
|||
|
|
: activity.planEndDate
|
|||
|
|
)
|
|||
|
|
}}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</el-card>
|
|||
|
|
</el-timeline-item>
|
|||
|
|
</el-timeline>
|
|||
|
|
<el-empty v-else description="暂无进度"></el-empty>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
import { formatDate } from "@/utils";
|
|||
|
|
|
|||
|
|
export default {
|
|||
|
|
name: "EditableTimeline",
|
|||
|
|
props: {
|
|||
|
|
activities: {
|
|||
|
|
type: Array,
|
|||
|
|
required: true,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
data() {
|
|||
|
|
return {
|
|||
|
|
localActivities: this.activities.map((a) => ({
|
|||
|
|
...a,
|
|||
|
|
isEditing: false,
|
|||
|
|
})),
|
|||
|
|
};
|
|||
|
|
},
|
|||
|
|
methods: {
|
|||
|
|
// 修改toggleEdit方法保存原始数据
|
|||
|
|
toggleEdit(index) {
|
|||
|
|
this.localActivities = this.localActivities.map((a, i) => {
|
|||
|
|
if (i === index) {
|
|||
|
|
// 进入编辑模式时保存快照
|
|||
|
|
const newState = { ...a, isEditing: !a.isEditing };
|
|||
|
|
if (!a.isEditing) {
|
|||
|
|
newState._originalData = JSON.parse(JSON.stringify(a));
|
|||
|
|
}
|
|||
|
|
return newState;
|
|||
|
|
}
|
|||
|
|
return a;
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 新增取消编辑方法
|
|||
|
|
cancelEdit(index) {
|
|||
|
|
const original = this.localActivities[index]._originalData;
|
|||
|
|
if (original) {
|
|||
|
|
// 恢复原始数据
|
|||
|
|
this.localActivities.splice(index, 1, {
|
|||
|
|
...original,
|
|||
|
|
isEditing: false,
|
|||
|
|
});
|
|||
|
|
this.$forceUpdate(); // 强制更新视图
|
|||
|
|
this.$message.info("已取消编辑");
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 修改确认保存方法清理临时数据
|
|||
|
|
async confirmEdit(index) {
|
|||
|
|
try {
|
|||
|
|
await this.$confirm("确定要保存修改吗?", "操作确认", {
|
|||
|
|
confirmButtonText: "确定",
|
|||
|
|
cancelButtonText: "取消",
|
|||
|
|
type: "warning",
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const activity = this.localActivities[index];
|
|||
|
|
// 清理临时数据
|
|||
|
|
if (activity._originalData) {
|
|||
|
|
delete activity._originalData;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.toggleEdit(index);
|
|||
|
|
this.$emit("change", {
|
|||
|
|
list: this.localActivities,
|
|||
|
|
activity: activity,
|
|||
|
|
});
|
|||
|
|
} catch (error) {
|
|||
|
|
// 取消操作时自动捕获错误
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 剩余时间颜色判断
|
|||
|
|
remainTimeColor(activity) {
|
|||
|
|
if (activity.detailStatus == 1) return "#67C23A"; // 已完成绿色
|
|||
|
|
return activity.remainTime < 3 ? "#F56C6C" : "#909399"; // 小于3天红色
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
async markAsComplete(index) {
|
|||
|
|
try {
|
|||
|
|
await this.$confirm("确定要完成该进度吗?", "操作确认", {
|
|||
|
|
confirmButtonText: "确定",
|
|||
|
|
cancelButtonText: "取消",
|
|||
|
|
type: "warning",
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const activity = {
|
|||
|
|
...this.localActivities[index],
|
|||
|
|
detailStatus: 1,
|
|||
|
|
actualEndDate: formatDate(new Date()),
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
this.$emit("change", {
|
|||
|
|
list: this.localActivities.splice(index, 1, activity),
|
|||
|
|
activity,
|
|||
|
|
});
|
|||
|
|
} catch {}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
formatDate(dateStr) {
|
|||
|
|
return dateStr;
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
watch: {
|
|||
|
|
activities(newVal) {
|
|||
|
|
this.localActivities = newVal.map((a) => ({
|
|||
|
|
...a,
|
|||
|
|
isEditing: false,
|
|||
|
|
}));
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped lang="scss">
|
|||
|
|
.editable-card {
|
|||
|
|
margin: 10px 0;
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.header {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
margin-bottom: 12px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.title {
|
|||
|
|
font-weight: 500;
|
|||
|
|
font-size: 15px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.time-info {
|
|||
|
|
padding: 8px 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.time-item {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
margin: 6px 0;
|
|||
|
|
color: #666;
|
|||
|
|
|
|||
|
|
i {
|
|||
|
|
margin-right: 8px;
|
|||
|
|
font-size: 14px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.edit-mode {
|
|||
|
|
.form-item {
|
|||
|
|
margin-bottom: 12px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.time-pickers {
|
|||
|
|
display: grid;
|
|||
|
|
grid-gap: 10px;
|
|||
|
|
margin-bottom: 12px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.actions {
|
|||
|
|
text-align: right;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 增加按钮间距 */
|
|||
|
|
.actions .el-button {
|
|||
|
|
margin-left: 8px;
|
|||
|
|
}
|
|||
|
|
</style>
|