chore: 导入清理前的完整工程状态(含 Capacitor 原生壳,作为回滚备份点)

This commit is contained in:
toor
2026-10-21 11:50:10 +08:00
committed by trae-solo-agent
commit 8ded87a3a8
389 changed files with 52242 additions and 0 deletions
+147
View File
@@ -0,0 +1,147 @@
<script lang="ts" setup>
import dayjs from 'dayjs'
import { exportLocalUserData, importLocalUserData } from '~/composables/store/userData'
import { promptText, showAlert } from '~/utils/dialog'
import { yunleHaptic } from '~/utils/yunleHost'
const { isDark, toggleDark } = useDarkMode()
const app = useAppStore()
definePageMeta({
layout: 'child',
})
const importInput = ref<HTMLInputElement>()
function exportText(): string {
return JSON.stringify(exportLocalUserData())
}
function exportToFile(): void {
const blob = new Blob([exportText()], { type: 'application/json' })
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `cook-data-${dayjs().format('YYYYMMDD-HHmm')}.json`
link.click()
setTimeout(() => URL.revokeObjectURL(url), 0)
}
async function copyExport(): Promise<void> {
try {
await navigator.clipboard.writeText(exportText())
void yunleHaptic('success')
await showAlert('已复制', '数据已复制到剪贴板,可以在新设备上用「粘贴导入」恢复。')
}
catch {
await showAlert('复制失败', '当前环境不支持写入剪贴板,请改用「导出为文件」。')
}
}
async function applyImport(text: string): Promise<void> {
const result = importLocalUserData(text)
void yunleHaptic(result ? 'success' : 'heavy')
if (result)
await showAlert('导入完成', `已合并到本机:现有 ${result.favorites} 个收藏、${result.histories} 条历史记录。`)
else
await showAlert('导入失败', '无法识别这份数据,请确认它来自食用手册的导出。')
}
async function importFromFile(event: Event): Promise<void> {
const input = event.target as HTMLInputElement
const file = input.files?.[0]
input.value = ''
if (file)
await applyImport(await file.text())
}
function onToggleChange() {
void yunleHaptic('light')
}
async function importFromPaste(): Promise<void> {
const value = await promptText({ header: '粘贴导入', message: '粘贴之前复制的数据', okText: '导入' })
if (value?.trim())
await applyImport(value)
}
</script>
<template>
<ion-page>
<ion-header :translucent="true">
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button default-href="/my" />
</ion-buttons>
<ion-title>设置</ion-title>
</ion-toolbar>
</ion-header>
<ion-content :fullscreen="true">
<h2 class="cook-group-header">
外观
</h2>
<ion-list :inset="true">
<ion-item>
<ion-toggle :checked="isDark" justify="space-between" @ion-change="toggleDark(); onToggleChange()">
暗色模式
</ion-toggle>
</ion-item>
</ion-list>
<h2 class="cook-group-header">
数据
</h2>
<ion-list :inset="true">
<ion-item>
<ion-toggle v-model:checked="app.settings.keepLocalData" justify="space-between" @ion-change="onToggleChange">
离开网页后保留选中数据
</ion-toggle>
</ion-item>
</ion-list>
<p class="cook-group-footer">
关闭后,选中的食材、厨具和调料只在这次打开时有效。
</p>
<h2 class="cook-group-header">
收藏和历史
</h2>
<ion-list :inset="true">
<ion-item :button="true" :detail="false" @click="exportToFile">
<CookListIcon slot="start" :icon="ioniconsDownloadOutline" color="#007aff" />
<ion-label>导出为文件</ion-label>
</ion-item>
<ion-item :button="true" :detail="false" @click="copyExport">
<CookListIcon slot="start" :icon="ioniconsCopyOutline" color="#5856d6" />
<ion-label>复制到剪贴板</ion-label>
</ion-item>
<ion-item :button="true" :detail="false" @click="importInput?.click()">
<CookListIcon slot="start" :icon="ioniconsFolderOpenOutline" color="#ff9500" />
<ion-label>从文件导入</ion-label>
</ion-item>
<ion-item :button="true" :detail="false" @click="importFromPaste">
<CookListIcon slot="start" :icon="ioniconsClipboardOutline" color="#34c759" />
<ion-label>粘贴导入</ion-label>
</ion-item>
</ion-list>
<p class="cook-group-footer">
导入会和本机已有数据合并,不会覆盖。登录云乐坊账号后,收藏和历史会自动同步。
</p>
<input
ref="importInput"
hidden
type="file"
accept="application/json,.json"
@change="importFromFile"
>
</ion-content>
</ion-page>
</template>
<style>
/* (Optional) This is added to prevent the flashing that happens when toggling between palettes */
ion-item {
--transition: none;
}
</style>