release: Elysia ToDo v1.0.0

鍏ㄦ爤涓汉淇℃伅绠$悊搴旂敤锛岄泦鎴愬緟鍔炰换鍔°€佷範鎯墦鍗°€佺邯蹇垫棩鎻愰啋銆佽祫浜ф€昏鍔熻兘銆

Made-with: Cursor
This commit is contained in:
祀梦
2026-03-14 22:21:26 +08:00
commit 2979197b1c
104 changed files with 21737 additions and 0 deletions

View File

@@ -0,0 +1,208 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { accountApi } from '@/api/accounts'
import type {
FinancialAccount, AccountFormData, BalanceUpdateData,
AccountHistoryRecord, DebtInstallment, DebtInstallmentFormData
} from '@/api/types'
export const useAccountStore = defineStore('account', () => {
const accounts = ref<FinancialAccount[]>([])
const installments = ref<DebtInstallment[]>([])
const loading = ref(false)
const savingsAccounts = computed(() =>
accounts.value.filter(a => a.account_type === 'savings' && a.is_active)
)
const debtAccounts = computed(() =>
accounts.value.filter(a => a.account_type === 'debt' && a.is_active)
)
const totalSavings = computed(() =>
savingsAccounts.value.reduce((sum, a) => sum + a.balance, 0)
)
const totalDebt = computed(() =>
debtAccounts.value.reduce((sum, a) => sum + a.balance, 0)
)
const netAssets = computed(() => totalSavings.value - totalDebt.value)
const activeInstallments = computed(() =>
installments.value.filter(i => !i.is_completed && i.days_until_payment !== null)
)
const upcomingPayments = computed(() =>
activeInstallments.value
.filter(i => i.days_until_payment! >= 0)
.sort((a, b) => a.days_until_payment! - b.days_until_payment!)
)
// ============ 账户操作 ============
async function fetchAccounts() {
loading.value = true
try {
accounts.value = await accountApi.getAccounts()
} catch (error) {
console.error('获取账户列表失败:', error)
} finally {
loading.value = false
}
}
async function createAccount(data: AccountFormData): Promise<FinancialAccount | null> {
try {
const account = await accountApi.createAccount(data)
accounts.value.push(account)
return account
} catch (error) {
console.error('创建账户失败:', error)
return null
}
}
async function updateAccount(id: number, data: Partial<AccountFormData>): Promise<FinancialAccount | null> {
try {
const updated = await accountApi.updateAccount(id, data)
const index = accounts.value.findIndex(a => a.id === id)
if (index !== -1) {
accounts.value[index] = updated
}
return updated
} catch (error) {
console.error('更新账户失败:', error)
return null
}
}
async function deleteAccount(id: number): Promise<boolean> {
try {
await accountApi.deleteAccount(id)
accounts.value = accounts.value.filter(a => a.id !== id)
return true
} catch (error) {
console.error('删除账户失败:', error)
return false
}
}
async function updateBalance(id: number, data: BalanceUpdateData): Promise<FinancialAccount | null> {
try {
const updated = await accountApi.updateBalance(id, data)
const index = accounts.value.findIndex(a => a.id === id)
if (index !== -1) {
accounts.value[index] = updated
}
return updated
} catch (error) {
console.error('更新余额失败:', error)
return null
}
}
async function fetchHistory(id: number, page = 1, pageSize = 20): Promise<AccountHistoryResponse> {
try {
return await accountApi.getHistory(id, { page, page_size: pageSize })
} catch (error) {
console.error('获取变更历史失败:', error)
return { total: 0, page: 1, page_size: pageSize, records: [] }
}
}
// ============ 分期计划操作 ============
async function fetchInstallments() {
try {
installments.value = await accountApi.getInstallments()
} catch (error) {
console.error('获取分期计划失败:', error)
}
}
async function createInstallment(data: DebtInstallmentFormData): Promise<DebtInstallment | null> {
try {
const inst = await accountApi.createInstallment(data)
installments.value.push(inst)
installments.value.sort((a, b) => {
const aActive = !a.is_completed && a.days_until_payment !== null ? 0 : 1
const bActive = !b.is_completed && b.days_until_payment !== null ? 0 : 1
if (aActive !== bActive) return aActive - bActive
return (a.days_until_payment ?? 9999) - (b.days_until_payment ?? 9999)
})
return inst
} catch (error) {
console.error('创建分期计划失败:', error)
return null
}
}
async function updateInstallment(id: number, data: Partial<DebtInstallmentFormData>): Promise<DebtInstallment | null> {
try {
const updated = await accountApi.updateInstallment(id, data)
const index = installments.value.findIndex(i => i.id === id)
if (index !== -1) {
installments.value[index] = updated
}
return updated
} catch (error) {
console.error('更新分期计划失败:', error)
return null
}
}
async function deleteInstallment(id: number): Promise<boolean> {
try {
await accountApi.deleteInstallment(id)
installments.value = installments.value.filter(i => i.id !== id)
return true
} catch (error) {
console.error('删除分期计划失败:', error)
return false
}
}
async function payInstallment(id: number): Promise<DebtInstallment | null> {
try {
const updated = await accountApi.payInstallment(id)
const index = installments.value.findIndex(i => i.id === id)
if (index !== -1) {
installments.value[index] = updated
}
return updated
} catch (error) {
console.error('标记还款失败:', error)
return null
}
}
async function init() {
await Promise.all([fetchAccounts(), fetchInstallments()])
}
return {
accounts,
installments,
loading,
savingsAccounts,
debtAccounts,
totalSavings,
totalDebt,
netAssets,
activeInstallments,
upcomingPayments,
fetchAccounts,
createAccount,
updateAccount,
deleteAccount,
updateBalance,
fetchHistory,
fetchInstallments,
createInstallment,
updateInstallment,
deleteInstallment,
payInstallment,
init,
}
})

View File

@@ -0,0 +1,180 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { anniversaryApi } from '@/api/anniversaries'
import type { Anniversary, AnniversaryFormData, AnniversaryCategory, AnniversaryCategoryFormData } from '@/api/types'
export const useAnniversaryStore = defineStore('anniversary', () => {
const anniversaries = ref<Anniversary[]>([])
const categories = ref<AnniversaryCategory[]>([])
const loading = ref(false)
const activeCategoryId = ref<number | null>(null)
const filteredAnniversaries = computed(() => {
if (activeCategoryId.value === null) {
return anniversaries.value
}
return anniversaries.value.filter(a => a.category_id === activeCategoryId.value)
})
const upcomingAnniversaries = computed(() =>
filteredAnniversaries.value.filter(a => a.days_until !== null && a.days_until! >= 0)
)
const pastAnniversaries = computed(() =>
filteredAnniversaries.value.filter(a => a.days_until === null || a.days_until! < 0)
)
const todayAnniversaries = computed(() =>
filteredAnniversaries.value.filter(a => a.days_until === 0)
)
const remindAnniversaries = computed(() =>
filteredAnniversaries.value.filter(a =>
a.days_until !== null && a.days_until! >= 0 && a.days_until! <= a.remind_days_before
)
)
// ============ 纪念日操作 ============
async function fetchAnniversaries() {
loading.value = true
try {
const params = activeCategoryId.value !== null
? { category_id: activeCategoryId.value }
: undefined
anniversaries.value = await anniversaryApi.getAnniversaries(params)
} catch (error) {
console.error('获取纪念日列表失败:', error)
} finally {
loading.value = false
}
}
async function createAnniversary(data: AnniversaryFormData): Promise<Anniversary | null> {
try {
const newAnniversary = await anniversaryApi.createAnniversary(data)
anniversaries.value.unshift(newAnniversary)
reSort()
return newAnniversary
} catch (error) {
console.error('创建纪念日失败:', error)
return null
}
}
async function updateAnniversary(id: number, data: Partial<AnniversaryFormData>): Promise<Anniversary | null> {
try {
const updated = await anniversaryApi.updateAnniversary(id, data)
const index = anniversaries.value.findIndex(a => a.id === id)
if (index !== -1) {
anniversaries.value[index] = updated
}
reSort()
return updated
} catch (error) {
console.error('更新纪念日失败:', error)
return null
}
}
async function deleteAnniversary(id: number): Promise<boolean> {
try {
await anniversaryApi.deleteAnniversary(id)
anniversaries.value = anniversaries.value.filter(a => a.id !== id)
return true
} catch (error) {
console.error('删除纪念日失败:', error)
return false
}
}
// ============ 分类操作 ============
async function fetchCategories() {
try {
categories.value = await anniversaryApi.getCategories()
} catch (error) {
console.error('获取纪念日分类失败:', error)
}
}
async function createCategory(data: AnniversaryCategoryFormData): Promise<AnniversaryCategory | null> {
try {
const newCat = await anniversaryApi.createCategory(data)
categories.value.push(newCat)
categories.value.sort((a, b) => a.sort_order - b.sort_order)
return newCat
} catch (error) {
console.error('创建纪念日分类失败:', error)
return null
}
}
async function updateCategory(id: number, data: Partial<AnniversaryCategoryFormData>): Promise<AnniversaryCategory | null> {
try {
const updated = await anniversaryApi.updateCategory(id, data)
const index = categories.value.findIndex(c => c.id === id)
if (index !== -1) {
categories.value[index] = updated
}
categories.value.sort((a, b) => a.sort_order - b.sort_order)
return updated
} catch (error) {
console.error('更新纪念日分类失败:', error)
return null
}
}
async function deleteCategory(id: number): Promise<boolean> {
try {
await anniversaryApi.deleteCategory(id)
categories.value = categories.value.filter(c => c.id !== id)
if (activeCategoryId.value === id) {
activeCategoryId.value = null
}
return true
} catch (error) {
console.error('删除纪念日分类失败:', error)
return false
}
}
function setFilter(categoryId: number | null) {
activeCategoryId.value = categoryId
}
function reSort() {
anniversaries.value.sort((a, b) => {
const aUpcoming = a.days_until !== null && a.days_until >= 0 ? 0 : 1
const bUpcoming = b.days_until !== null && b.days_until >= 0 ? 0 : 1
if (aUpcoming !== bUpcoming) return aUpcoming - bUpcoming
return (a.days_until ?? 9999) - (b.days_until ?? 9999)
})
}
async function init() {
await Promise.all([fetchAnniversaries(), fetchCategories()])
}
return {
anniversaries,
categories,
loading,
activeCategoryId,
filteredAnniversaries,
upcomingAnniversaries,
pastAnniversaries,
todayAnniversaries,
remindAnniversaries,
fetchAnniversaries,
createAnniversary,
updateAnniversary,
deleteAnniversary,
fetchCategories,
createCategory,
updateCategory,
deleteCategory,
setFilter,
init,
}
})

View File

@@ -0,0 +1,65 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { categoryApi, type CategoryResponse } from '@/api/categories'
import type { CategoryFormData } from '@/api/types'
export const useCategoryStore = defineStore('category', () => {
const categories = ref<CategoryResponse[]>([])
const loading = ref(false)
async function fetchCategories() {
loading.value = true
try {
categories.value = await categoryApi.getCategories()
} catch (error) {
console.error('获取分类列表失败:', error)
} finally {
loading.value = false
}
}
async function createCategory(data: CategoryFormData) {
try {
const newCategory = await categoryApi.createCategory(data)
categories.value.push(newCategory)
return newCategory
} catch (error) {
console.error('创建分类失败:', error)
return null
}
}
async function updateCategory(id: number, data: CategoryFormData) {
try {
const updatedCategory = await categoryApi.updateCategory(id, data)
const index = categories.value.findIndex((c: CategoryResponse) => c.id === id)
if (index !== -1) {
categories.value[index] = updatedCategory
}
return updatedCategory
} catch (error) {
console.error('更新分类失败:', error)
return null
}
}
async function deleteCategory(id: number) {
try {
await categoryApi.deleteCategory(id)
categories.value = categories.value.filter((c: CategoryResponse) => c.id !== id)
return true
} catch (error) {
console.error('删除分类失败:', error)
return false
}
}
return {
categories,
loading,
fetchCategories,
createCategory,
updateCategory,
deleteCategory
}
})

View File

@@ -0,0 +1,227 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { habitApi, habitGroupApi, type HabitResponse, type HabitGroupResponse, type HabitStatsResponse } from '@/api/habits'
import type { HabitFormData, HabitGroupFormData } from '@/api/types'
export const useHabitStore = defineStore('habit', () => {
const habits = ref<HabitResponse[]>([])
const groups = ref<HabitGroupResponse[]>([])
const statsMap = ref<Record<number, HabitStatsResponse>>({})
const loading = ref(false)
// 按分组组织习惯
const groupedHabits = computed(() => {
const map = new Map<number, { group: HabitGroupResponse | null; habits: HabitResponse[] }>()
// 先添加有分组的
for (const group of groups.value) {
map.set(group.id, { group, habits: [] })
}
for (const habit of habits.value) {
if (habit.group_id && map.has(habit.group_id)) {
map.get(habit.group_id)!.habits.push(habit)
} else {
// 未分组
if (!map.has(0)) {
map.set(0, { group: null, habits: [] })
}
map.get(0)!.habits.push(habit)
}
}
return Array.from(map.values())
})
// 今日统计
const todaySummary = computed(() => {
const todayHabits = habits.value.filter(h => !h.is_archived)
const total = todayHabits.length
let completed = 0
let maxStreak = 0
for (const habit of todayHabits) {
const stats = statsMap.value[habit.id]
if (stats?.today_completed) completed++
if (stats && stats.current_streak > maxStreak) maxStreak = stats.current_streak
}
return { total, completed, maxStreak }
})
async function fetchHabits(includeArchived = false) {
loading.value = true
try {
habits.value = await habitApi.getHabits({ include_archived: includeArchived })
} catch (error) {
console.error('获取习惯列表失败:', error)
} finally {
loading.value = false
}
}
async function fetchGroups() {
try {
groups.value = await habitGroupApi.getGroups()
} catch (error) {
console.error('获取习惯分组失败:', error)
}
}
async function fetchStats(habitId: number) {
try {
statsMap.value[habitId] = await habitApi.getStats(habitId)
} catch (error) {
console.error('获取习惯统计失败:', error)
}
}
async function fetchAllStats() {
for (const habit of habits.value) {
await fetchStats(habit.id)
}
}
async function createHabit(data: HabitFormData) {
try {
const newHabit = await habitApi.createHabit(data)
habits.value.unshift(newHabit)
await fetchStats(newHabit.id)
return newHabit
} catch (error) {
console.error('创建习惯失败:', error)
return null
}
}
async function updateHabit(id: number, data: Partial<HabitFormData>) {
try {
const updated = await habitApi.updateHabit(id, data)
const index = habits.value.findIndex(h => h.id === id)
if (index !== -1) habits.value[index] = updated
return updated
} catch (error) {
console.error('更新习惯失败:', error)
return null
}
}
async function deleteHabit(id: number) {
try {
await habitApi.deleteHabit(id)
habits.value = habits.value.filter(h => h.id !== id)
delete statsMap.value[id]
return true
} catch (error) {
console.error('删除习惯失败:', error)
return false
}
}
async function toggleArchive(id: number) {
try {
const updated = await habitApi.toggleArchive(id)
const index = habits.value.findIndex(h => h.id === id)
if (index !== -1) habits.value[index] = updated
return updated
} catch (error) {
console.error('切换归档状态失败:', error)
return null
}
}
async function checkin(habitId: number, count?: number) {
try {
const result = await habitApi.checkin(habitId, count)
await fetchStats(habitId)
return result
} catch (error) {
console.error('打卡失败:', error)
return null
}
}
async function cancelCheckin(habitId: number, count: number = 1) {
try {
await habitApi.cancelCheckin(habitId, count)
await fetchStats(habitId)
return true
} catch (error) {
console.error('取消打卡失败:', error)
return false
}
}
async function createGroup(data: HabitGroupFormData) {
try {
const newGroup = await habitGroupApi.createGroup(data)
groups.value.push(newGroup)
return newGroup
} catch (error) {
console.error('创建分组失败:', error)
return null
}
}
async function updateGroup(id: number, data: Partial<HabitGroupFormData>) {
try {
const updated = await habitGroupApi.updateGroup(id, data)
const index = groups.value.findIndex(g => g.id === id)
if (index !== -1) groups.value[index] = updated
// 同步更新 habits 中的 group 引用
for (const habit of habits.value) {
if (habit.group_id === id) habit.group = updated
}
return updated
} catch (error) {
console.error('更新分组失败:', error)
return null
}
}
async function deleteGroup(id: number) {
try {
await habitGroupApi.deleteGroup(id)
groups.value = groups.value.filter(g => g.id !== id)
// 清空关联习惯的 group_id
for (const habit of habits.value) {
if (habit.group_id === id) {
habit.group_id = undefined
habit.group = undefined
}
}
return true
} catch (error) {
console.error('删除分组失败:', error)
return false
}
}
async function init() {
await Promise.all([fetchGroups(), fetchHabits()])
await fetchAllStats()
}
return {
habits,
groups,
statsMap,
loading,
groupedHabits,
todaySummary,
fetchHabits,
fetchGroups,
fetchStats,
fetchAllStats,
createHabit,
updateHabit,
deleteHabit,
toggleArchive,
checkin,
cancelCheckin,
createGroup,
updateGroup,
deleteGroup,
init
}
})

View File

@@ -0,0 +1,50 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { tagApi, type TagResponse } from '@/api/tags'
import type { TagFormData } from '@/api/types'
export const useTagStore = defineStore('tag', () => {
const tags = ref<TagResponse[]>([])
const loading = ref(false)
async function fetchTags() {
loading.value = true
try {
tags.value = await tagApi.getTags()
} catch (error) {
console.error('获取标签列表失败:', error)
} finally {
loading.value = false
}
}
async function createTag(data: TagFormData) {
try {
const newTag = await tagApi.createTag(data)
tags.value.push(newTag)
return newTag
} catch (error) {
console.error('创建标签失败:', error)
return null
}
}
async function deleteTag(id: number) {
try {
await tagApi.deleteTag(id)
tags.value = tags.value.filter((t: TagResponse) => t.id !== id)
return true
} catch (error) {
console.error('删除标签失败:', error)
return false
}
}
return {
tags,
loading,
fetchTags,
createTag,
deleteTag
}
})

View File

@@ -0,0 +1,180 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { taskApi, type TaskResponse } from '@/api/tasks'
import type { TaskFormData, TaskFilters } from '@/api/types'
import { matchWithPinyin } from '@/utils/pinyin'
import { formatDate } from '@/utils/date'
export const useTaskStore = defineStore('task', () => {
const allTasks = ref<TaskResponse[]>([])
const loading = ref(false)
const filters = ref<TaskFilters>({
status: 'all',
sort_by: 'priority',
sort_order: 'desc'
})
// 所有任务
const tasks = computed(() => {
let result = [...allTasks.value]
// 搜索过滤(支持拼音)
if (filters.value.search?.trim()) {
const keyword = filters.value.search.trim()
result = result.filter(t =>
matchWithPinyin(t.title, keyword) ||
(t.description && matchWithPinyin(t.description, keyword)) ||
t.tags?.some(tag => matchWithPinyin(tag.name, keyword))
)
}
// 状态筛选
if (filters.value.status === 'active') {
result = result.filter(t => !t.is_completed)
} else if (filters.value.status === 'completed') {
result = result.filter(t => t.is_completed)
}
// 分类筛选
if (filters.value.category_id) {
result = result.filter(t => t.category_id === filters.value.category_id)
}
// 排序
if (filters.value.sort_by) {
result.sort((a, b) => {
let comparison = 0
if (filters.value.sort_by === 'priority') {
const priorityOrder: Record<string, number> = { q1: 4, q2: 3, q3: 2, q4: 1 }
const aOrder = priorityOrder[a.priority] || 0
const bOrder = priorityOrder[b.priority] || 0
comparison = aOrder - bOrder
} else if (filters.value.sort_by === 'due_date') {
if (!a.due_date && !b.due_date) comparison = 0
else if (!a.due_date) comparison = 1
else if (!b.due_date) comparison = -1
else comparison = new Date(a.due_date).getTime() - new Date(b.due_date).getTime()
} else {
comparison = new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
}
return filters.value.sort_order === 'asc' ? -comparison : comparison
})
}
return result
})
// 计算进行中和已完成的任务数量(基于所有任务)
const activeTasks = computed(() => allTasks.value.filter(t => !t.is_completed))
const completedTasks = computed(() => allTasks.value.filter(t => t.is_completed))
const totalTasks = computed(() => allTasks.value)
// 按日期分组的任务(用于月视图)
const tasksByDate = computed(() => {
const grouped = new Map<string, TaskResponse[]>()
const today = new Date()
const todayStr = formatDate(today)
allTasks.value.forEach(task => {
let dateKey: string
if (task.due_date) {
dateKey = formatDate(new Date(task.due_date))
} else {
// 无截止日期的任务显示在当天
dateKey = todayStr
}
if (!grouped.has(dateKey)) {
grouped.set(dateKey, [])
}
grouped.get(dateKey)!.push(task)
})
return grouped
})
async function fetchTasks() {
loading.value = true
try {
// 一次获取所有任务,在前端进行筛选和排序
allTasks.value = await taskApi.getTasks({ status: 'all' })
} catch (error) {
console.error('获取任务列表失败:', error)
} finally {
loading.value = false
}
}
async function createTask(data: TaskFormData) {
try {
const newTask = await taskApi.createTask(data)
allTasks.value.unshift(newTask)
return newTask
} catch (error) {
console.error('创建任务失败:', error)
return null
}
}
async function updateTask(id: number, data: TaskFormData) {
try {
const updatedTask = await taskApi.updateTask(id, data)
const index = allTasks.value.findIndex((t: TaskResponse) => t.id === id)
if (index !== -1) {
allTasks.value[index] = updatedTask
}
return updatedTask
} catch (error) {
console.error('更新任务失败:', error)
return null
}
}
async function deleteTask(id: number) {
try {
await taskApi.deleteTask(id)
allTasks.value = allTasks.value.filter((t: TaskResponse) => t.id !== id)
return true
} catch (error) {
console.error('删除任务失败:', error)
return false
}
}
async function toggleTask(id: number) {
try {
const updatedTask = await taskApi.toggleTask(id)
const index = allTasks.value.findIndex((t: TaskResponse) => t.id === id)
if (index !== -1) {
allTasks.value[index] = updatedTask
}
return updatedTask
} catch (error) {
console.error('切换任务状态失败:', error)
return null
}
}
function setFilters(newFilters: TaskFilters) {
filters.value = { ...filters.value, ...newFilters }
// 筛选现在在前端完成,不需要重新请求
}
return {
tasks,
loading,
filters,
activeTasks,
completedTasks,
totalTasks: allTasks,
tasksByDate,
fetchTasks,
createTask,
updateTask,
deleteTask,
toggleTask,
setFilters
}
})

View File

@@ -0,0 +1,72 @@
import { defineStore } from 'pinia'
import { ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import type { Task, Category } from '@/api/types'
export const useUIStore = defineStore('ui', () => {
const router = useRouter()
const taskDialogVisible = ref(false)
const editingTask = ref<Task | null>(null)
const categoryDialogVisible = ref(false)
const editingCategory = ref<Category | null>(null)
const sidebarCollapsed = ref(false)
const globalLoading = ref(false)
const currentView = ref<'list' | 'calendar' | 'quadrant' | 'profile' | 'settings' | 'habits' | 'anniversaries' | 'assets'>('list')
const calendarMode = ref<'week' | 'monthly'>('monthly')
function openTaskDialog(task?: Task) {
editingTask.value = task || null
taskDialogVisible.value = true
}
function closeTaskDialog() {
taskDialogVisible.value = false
editingTask.value = null
}
function openCategoryDialog(category?: Category) {
editingCategory.value = category || null
categoryDialogVisible.value = true
}
function closeCategoryDialog() {
categoryDialogVisible.value = false
editingCategory.value = null
}
function toggleSidebar() {
sidebarCollapsed.value = !sidebarCollapsed.value
}
function setLoading(loading: boolean) {
globalLoading.value = loading
}
function setCurrentView(view: 'list' | 'calendar' | 'quadrant' | 'profile' | 'settings' | 'habits' | 'anniversaries' | 'assets') {
currentView.value = view
}
function setCalendarMode(mode: 'week' | 'monthly') {
calendarMode.value = mode
}
return {
taskDialogVisible,
editingTask,
categoryDialogVisible,
editingCategory,
sidebarCollapsed,
globalLoading,
currentView,
calendarMode,
openTaskDialog,
closeTaskDialog,
openCategoryDialog,
closeCategoryDialog,
toggleSidebar,
setLoading,
setCurrentView,
setCalendarMode
}
})

View File

@@ -0,0 +1,74 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import type { UserSettings, UserSettingsUpdate } from '@/api/types'
import { getUserSettings, updateUserSettings as apiUpdateSettings } from '@/api/userSettings'
export const useUserSettingsStore = defineStore('userSettings', () => {
const settings = ref<UserSettings | null>(null)
const loading = ref(false)
async function fetchSettings() {
loading.value = true
try {
settings.value = await getUserSettings()
} finally {
loading.value = false
}
}
async function updateSettings(data: UserSettingsUpdate) {
loading.value = true
try {
settings.value = await apiUpdateSettings(data)
} finally {
loading.value = false
}
}
const nickname = ref('爱莉希雅')
const avatar = ref('')
const signature = ref('')
const birthday = ref('')
const email = ref('')
const siteName = ref('爱莉希雅待办')
const defaultView = ref('list')
const defaultSortBy = ref('priority')
const defaultSortOrder = ref('desc')
function syncFromSettings(s: UserSettings) {
nickname.value = s.nickname || ''
avatar.value = s.avatar || ''
signature.value = s.signature || ''
birthday.value = s.birthday || ''
email.value = s.email || ''
siteName.value = s.site_name || '爱莉希雅待办'
defaultView.value = s.default_view || 'list'
defaultSortBy.value = s.default_sort_by || 'priority'
defaultSortOrder.value = s.default_sort_order || 'desc'
}
async function fetchAndSync() {
await fetchSettings()
if (settings.value) {
syncFromSettings(settings.value)
}
}
return {
settings,
loading,
fetchSettings,
updateSettings,
fetchAndSync,
nickname,
avatar,
signature,
birthday,
email,
siteName,
defaultView,
defaultSortBy,
defaultSortOrder,
syncFromSettings
}
})