| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286 |
- <template>
- <PlayfulCard elevation="1">
- <view class="container">
- <!-- 任务列表 -->
- <view class="task-list" v-if="tasks.length > 0">
- <view
- v-for="task in tasks"
- :key="task.id"
- class="task-item"
- :class="{ completed: task.status === 'completed' }"
- >
- <view class="task-left">
- <view class="task-title">{{ task.title }}</view>
- <view class="task-meta">
- <text class="points">+{{ task.points }}分</text>
- <text class="deadline">截止 {{ formatTime(task.deadline) }}</text>
- </view>
- </view>
- <view class="task-right">
- <button
- v-if="task.status === 'pending'"
- class="btn-complete"
- @click="completeTask(task.id)"
- >
- 完成
- </button>
- <view v-else class="completed-badge">已完成</view>
- </view>
- </view>
- </view>
-
- <view class="empty" v-else>
- <text>暂无今日任务</text>
- </view>
- <!-- 家长模式:添加任务按钮 -->
- <button
- v-if="isParent"
- class="btn-primary add-btn"
- @click="showAddModal = true"
- >
- + 添加任务
- </button>
- <!-- 添加任务弹窗 -->
- <view class="modal-mask" v-if="showAddModal" @click="showAddModal = false">
- <view class="modal" @click.stop>
- <view class="modal-title">添加任务</view>
- <view class="form-item">
- <input v-model="newTask.title" placeholder="任务名称" />
- </view>
- <view class="form-item">
- <input v-model="newTask.points" type="number" placeholder="积分(1-10)" />
- </view>
- <view class="form-item">
- <picker mode="time" @change="onTimeChange">
- <view class="picker">
- {{ newTask.deadlineTime || '选择截止时间' }}
- </view>
- </picker>
- </view>
- <view class="form-item">
- <picker mode="selector" :range="categories" @change="onCategoryChange">
- <view class="picker">
- {{ newTask.category || '选择分类' }}
- </view>
- </picker>
- </view>
- <view class="modal-btns">
- <button @click="showAddModal = false">取消</button>
- <button class="btn-primary" @click="createTask">确定</button>
- </view>
- </view>
- </view>
- </view>
- </PlayfulCard>
- </template>
- <script>
- import { getTodayTasks, completeTask, createTask, getChildren } from '../../utils/api.js'
- import PlayfulCard from '@/components/PlayfulCard.vue'
- export default {
- components: { PlayfulCard },
- data() {
- return {
- tasks: [],
- isParent: false,
- currentChildId: '',
- showAddModal: false,
- newTask: {
- title: '',
- points: 2,
- deadlineTime: '',
- category: ''
- },
- categories: ['学习类', '生活类', '运动类', '其他']
- }
- },
- onShow() {
- this.loadTasks()
- this.isParent = this.$store.state.role === 'parent'
- },
- methods: {
- async loadTasks() {
- try {
- const childrenRes = await getChildren()
- const children = childrenRes.data
- if (children && children.length > 0) {
- this.currentChildId = children[0].id
- const res = await getTodayTasks(this.currentChildId)
- this.tasks = res.data || []
- }
- } catch (e) {
- console.error('加载任务失败', e)
- }
- },
- async completeTask(taskId) {
- try {
- const res = await completeTask(taskId, this.currentChildId, '')
- uni.showToast({
- title: `获得 ${res.data.pointsEarned} 积分`,
- icon: 'success'
- })
- this.loadTasks()
- } catch (e) {
- console.error('完成任务失败', e)
- }
- },
- onTimeChange(e) {
- const now = new Date()
- const deadline = new Date(now.getFullYear(), now.getMonth(), now.getDate(), e.detail.value)
- this.newTask.deadlineTime = e.detail.value
- },
- onCategoryChange(e) {
- this.newTask.category = this.categories[e.detail.value]
- },
- async createTask() {
- if (!this.newTask.title) {
- uni.showToast({ title: '请输入任务名称', icon: 'none' })
- return
- }
-
- const now = new Date()
- const deadline = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1)
- const [hours, minutes] = (this.newTask.deadlineTime || '20:00').split(':')
- deadline.setHours(parseInt(hours), parseInt(minutes), 0, 0)
- try {
- await createTask({
- title: this.newTask.title,
- points: parseInt(this.newTask.points),
- deadline: deadline.toISOString(),
- category: this.newTask.category,
- childId: this.currentChildId
- })
- uni.showToast({ title: '创建成功', icon: 'success' })
- this.showAddModal = false
- this.loadTasks()
- } catch (e) {
- console.error('创建任务失败', e)
- }
- },
- formatTime(dateStr) {
- if (!dateStr) return ''
- const date = new Date(dateStr)
- return `${date.getHours()}:${String(date.getMinutes()).padStart(2, '0')}`
- }
- }
- }
- </script>
- <style scoped>
- .container {
- padding: 30rpx;
- padding-bottom: 120rpx;
- }
- .task-item {
- background: #fff;
- border-radius: 16rpx;
- padding: 30rpx;
- margin-bottom: 20rpx;
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
- .task-item.completed {
- opacity: 0.6;
- }
- .task-title {
- font-size: 30rpx;
- color: #333;
- margin-bottom: 10rpx;
- }
- .task-meta {
- display: flex;
- gap: 20rpx;
- }
- .points {
- color: #FF6B6B;
- font-size: 24rpx;
- }
- .deadline {
- color: #999;
- font-size: 24rpx;
- }
- .btn-complete {
- background: #FF6B6B;
- color: #fff;
- font-size: 26rpx;
- padding: 10rpx 30rpx;
- border-radius: 30rpx;
- }
- .completed-badge {
- color: #52C41A;
- font-size: 26rpx;
- }
- .add-btn {
- position: fixed;
- bottom: 30rpx;
- left: 30rpx;
- right: 30rpx;
- }
- .empty {
- text-align: center;
- color: #999;
- padding: 100rpx;
- }
- .modal-mask {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(0, 0, 0, 0.5);
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .modal {
- background: #fff;
- border-radius: 20rpx;
- padding: 40rpx;
- width: 80%;
- }
- .modal-title {
- font-size: 32rpx;
- font-weight: bold;
- margin-bottom: 30rpx;
- text-align: center;
- }
- .form-item {
- margin-bottom: 20rpx;
- }
- .form-item input, .picker {
- border: 1rpx solid #ddd;
- border-radius: 10rpx;
- padding: 20rpx;
- font-size: 28rpx;
- }
- .modal-btns {
- display: flex;
- gap: 20rpx;
- margin-top: 30rpx;
- }
- .modal-btns button {
- flex: 1;
- }
- </style>
|