'integer', 'start_date' => 'date', 'due_date' => 'date', 'completed_at' => 'date', 'sort_order' => 'integer', 'imported_at' => 'datetime', ]; public function attachments(): HasMany { return $this->hasMany(SystemizationTodoAttachment::class, 'systemization_todo_id') ->orderBy('id'); } public function isCompleted(): bool { return $this->status === self::STATUS_COMPLETED; } public function statusLabel(): string { return $this->isCompleted() ? self::STATUS_COMPLETED : self::STATUS_INCOMPLETE; } public function displayProgress(): string { $progress = trim((string) $this->progress); if ($progress === '') { return ''; } $normalized = (string) preg_replace('/[\s。.]+$/u', '', $progress); return $this->isCompleted() && $normalized === self::STATUS_COMPLETED ? '' : $progress; } public function reminder(): array { if ($this->isCompleted()) { return ['label' => '—', 'tone' => 'muted']; } if ($this->due_date === null) { return ['label' => '时间待定', 'tone' => 'muted']; } $today = today()->startOfDay(); $dueDate = $this->due_date->copy()->startOfDay(); if ($dueDate->lt($today)) { return [ 'label' => '逾期 ' . $dueDate->diffInDays($today) . ' 天', 'tone' => 'danger', ]; } if ($dueDate->equalTo($today)) { return ['label' => '今天到期', 'tone' => 'warning']; } return [ 'label' => '剩余 ' . $today->diffInDays($dueDate) . ' 天', 'tone' => 'info', ]; } }