第一次上传

This commit is contained in:
xxk
2026-06-11 09:53:11 +08:00
commit e257f2009e
89 changed files with 4336 additions and 0 deletions
+179
View File
@@ -0,0 +1,179 @@
import { reactive } from 'vue'
import { createDefaultData } from './constants'
const STORAGE_KEY = 'bill-helper-miniapp-v1'
function deepClone(value) {
return JSON.parse(JSON.stringify(value))
}
function normalizeData(raw) {
const fallback = createDefaultData()
const source = raw || {}
return {
categories: {
expense: Array.isArray(source.categories?.expense) && source.categories.expense.length
? source.categories.expense
: fallback.categories.expense,
income: Array.isArray(source.categories?.income) && source.categories.income.length
? source.categories.income
: fallback.categories.income
},
accounts: Array.isArray(source.accounts) && source.accounts.length ? source.accounts : fallback.accounts,
bills: Array.isArray(source.bills) ? source.bills : fallback.bills,
budgets: {
total: Number(source.budgets?.total) || fallback.budgets.total,
categoryBudgets: source.budgets?.categoryBudgets || fallback.budgets.categoryBudgets
},
settings: {
theme: source.settings?.theme || fallback.settings.theme,
profile: {
authorized: Boolean(source.settings?.profile?.authorized),
nickname: source.settings?.profile?.nickname || fallback.settings.profile.nickname,
avatarUrl: source.settings?.profile?.avatarUrl || ''
},
lastBackupAt: source.settings?.lastBackupAt || ''
}
}
}
function loadData() {
try {
const raw = uni.getStorageSync(STORAGE_KEY)
if (!raw) {
const seeded = createDefaultData()
uni.setStorageSync(STORAGE_KEY, seeded)
return seeded
}
return normalizeData(raw)
} catch (error) {
return createDefaultData()
}
}
const state = reactive(normalizeData(loadData()))
function patchState(nextState) {
state.categories.expense.splice(0, state.categories.expense.length, ...nextState.categories.expense)
state.categories.income.splice(0, state.categories.income.length, ...nextState.categories.income)
state.accounts.splice(0, state.accounts.length, ...nextState.accounts)
state.bills.splice(0, state.bills.length, ...nextState.bills)
state.budgets.total = Number(nextState.budgets.total) || 0
state.budgets.categoryBudgets = { ...nextState.budgets.categoryBudgets }
state.settings.theme = nextState.settings.theme
state.settings.profile = { ...nextState.settings.profile }
state.settings.lastBackupAt = nextState.settings.lastBackupAt || ''
}
function persist() {
uni.setStorageSync(STORAGE_KEY, deepClone(state))
}
function buildBillPayload(payload) {
return {
id: payload.id || `bill-${Date.now()}`,
type: payload.type || 'expense',
amount: Number(payload.amount) || 0,
categoryId: payload.categoryId || '',
accountId: payload.accountId || '',
note: payload.note || '',
date: payload.date,
createdAt: payload.createdAt || Date.now()
}
}
export function useAppStore() {
function saveBill(payload) {
const nextBill = buildBillPayload(payload)
const index = state.bills.findIndex((item) => item.id === nextBill.id)
if (index === -1) {
state.bills.unshift(nextBill)
} else {
state.bills.splice(index, 1, nextBill)
}
persist()
}
function deleteBill(id) {
state.bills.splice(
0,
state.bills.length,
...state.bills.filter((item) => item.id !== id)
)
persist()
}
function deleteBills(ids) {
const idSet = new Set(ids)
state.bills.splice(
0,
state.bills.length,
...state.bills.filter((item) => !idSet.has(item.id))
)
persist()
}
function setBudgetTotal(value) {
state.budgets.total = Number(value) || 0
persist()
}
function setCategoryBudget(categoryId, value) {
state.budgets.categoryBudgets = {
...state.budgets.categoryBudgets,
[categoryId]: Number(value) || 0
}
persist()
}
function setTheme(theme) {
state.settings.theme = theme
persist()
}
function setProfile(profile) {
state.settings.profile = {
...state.settings.profile,
...profile
}
persist()
}
function markBackup(timeLabel) {
state.settings.lastBackupAt = timeLabel
persist()
}
function exportBackup() {
return JSON.stringify(deepClone(state), null, 2)
}
function importBackup(payload) {
const parsed = normalizeData(JSON.parse(payload))
patchState(parsed)
persist()
}
function resetAll() {
patchState(createDefaultData())
persist()
}
return {
state,
saveBill,
deleteBill,
deleteBills,
setBudgetTotal,
setCategoryBudget,
setTheme,
setProfile,
markBackup,
exportBackup,
importBackup,
resetAll
}
}