Files
what-to-eat/apps/web/app/pages/tabs/my/index.vue
T

219 lines
6.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts" setup>
import dayjs from 'dayjs'
import { customRecipes } from '~/composables/store/customRecipe'
import { links } from '~/constants'
definePageMeta({
alias: ['/my'],
})
const {
account,
configurationMessage,
errorMessage,
isAuthenticated,
signIn,
signOut,
status,
} = useYunlefunAuth()
const authBusy = computed(() => status.value === 'checking' || status.value === 'signing-in')
const {
errorMessage: syncErrorMessage,
lastSyncedAt,
status: syncStatus,
syncNow,
} = useUserDataSync()
const syncText = computed(() => {
switch (syncStatus.value) {
case 'syncing':
return '正在同步…'
case 'synced':
return `已同步 ${dayjs(lastSyncedAt.value).format('HH:mm')}`
case 'error':
return `${syncErrorMessage.value},点击重试`
default:
return '等待同步'
}
})
function handleSyncClick(): void {
if (syncStatus.value === 'error')
syncNow()
}
async function handleSignIn(): Promise<void> {
await signIn()
}
async function handleSignOut(): Promise<void> {
await signOut()
}
</script>
<template>
<ion-page>
<ion-header :translucent="true">
<ion-toolbar>
<ion-title>我的</ion-title>
</ion-toolbar>
</ion-header>
<ion-content :fullscreen="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">
我的
</ion-title>
</ion-toolbar>
</ion-header>
<ion-list :inset="true">
<ion-item v-if="isAuthenticated && account" class="my-account" lines="full">
<ion-avatar v-if="account.avatarUrl" slot="start">
<img :alt="account.displayName" :src="account.avatarUrl">
</ion-avatar>
<ion-icon v-else slot="start" class="my-account-placeholder" :icon="ioniconsPersonCircle" />
<ion-label>
<h2>{{ account.displayName }}</h2>
<p>云乐坊账号</p>
</ion-label>
</ion-item>
<ion-item
v-if="isAuthenticated"
:button="syncStatus === 'error'"
:detail="false"
@click="handleSyncClick"
>
<span v-if="syncStatus === 'syncing'" slot="start" class="my-sync-spinner">
<ion-spinner name="crescent" />
</span>
<CookListIcon
v-else
slot="start"
:icon="syncStatus === 'error' ? ioniconsCloudOfflineOutline : ioniconsCloudDoneOutline"
:color="syncStatus === 'error' ? '#ff3b30' : '#34c759'"
/>
<ion-label>
<h2>收藏和历史云同步</h2>
<p>{{ syncText }}</p>
</ion-label>
</ion-item>
<ion-item
v-if="!isAuthenticated"
:button="true"
:disabled="authBusy || Boolean(configurationMessage)"
@click="handleSignIn"
>
<span v-if="authBusy" slot="start" class="my-sync-spinner">
<ion-spinner name="crescent" />
</span>
<CookListIcon v-else slot="start" :icon="ioniconsLogInOutline" color="#007aff" />
<ion-label>
<h2>{{ authBusy ? '正在检查登录状态' : '使用云乐坊账号登录' }}</h2>
<p>安全授权,无需在 Cook 输入密码</p>
</ion-label>
</ion-item>
<ion-item v-if="configurationMessage || errorMessage" lines="none">
<ion-icon slot="start" color="danger" :icon="ioniconsAlertCircleOutline" />
<ion-label class="ion-text-wrap" color="danger">
{{ configurationMessage || errorMessage }}
</ion-label>
</ion-item>
</ion-list>
<ion-list :inset="true">
<ion-item router-link="/recipes/history">
<CookListIcon slot="start" :icon="ioniconsTimeOutline" color="#5856d6" />
<ion-label>历史记录</ion-label>
</ion-item>
<ion-item router-link="/recipes/favorites">
<CookListIcon slot="start" :icon="ioniconsStarOutline" color="#ff9500" />
<ion-label>我的收藏</ion-label>
</ion-item>
<ion-item router-link="/recipes/mine">
<CookListIcon slot="start" :icon="ioniconsBookOutline" color="#a2845e" />
<ion-label>我的菜谱</ion-label>
<ion-note v-if="customRecipes.length" slot="end">
{{ customRecipes.length }}
</ion-note>
</ion-item>
</ion-list>
<ion-list :inset="true">
<ion-item :href="links.githubIssue" target="_blank" :detail-icon="ioniconsOpenOutline">
<CookListIcon slot="start" :icon="ioniconsChatbubbleEllipsesOutline" color="#34c759" />
<ion-label>问题反馈</ion-label>
</ion-item>
<ion-item :href="links.githubDiscussions" target="_blank" :detail-icon="ioniconsOpenOutline">
<CookListIcon slot="start" :icon="ioniconsChatbubblesOutline" color="#30b0c7" />
<ion-label>参与讨论</ion-label>
</ion-item>
<ion-item :href="links.contribute" target="_blank" :detail-icon="ioniconsOpenOutline">
<CookListIcon slot="start" :icon="ioniconsMailOutline" color="#ff2d55" />
<ion-label>菜谱投稿</ion-label>
</ion-item>
</ion-list>
<ion-list :inset="true">
<ion-item router-link="/settings">
<CookListIcon slot="start" :icon="ioniconsSettingsOutline" color="#8e8e93" />
<ion-label>设置</ion-label>
</ion-item>
</ion-list>
<ion-list :inset="true">
<ion-item router-link="/changelog">
<CookListIcon slot="start" :icon="ioniconsDocumentTextOutline" color="#af52de" />
<ion-label>更新日志</ion-label>
</ion-item>
<ion-item router-link="/help">
<CookListIcon slot="start" :icon="ioniconsHelpCircleOutline" color="#007aff" />
<ion-label>帮助</ion-label>
</ion-item>
<ion-item router-link="/about">
<CookListIcon slot="start" :icon="ioniconsInformationCircleOutline" color="#5ac8fa" />
<ion-label>关于</ion-label>
</ion-item>
</ion-list>
<ion-list v-if="isAuthenticated" :inset="true">
<ion-item :button="true" :detail="false" :disabled="authBusy" @click="handleSignOut">
<ion-label class="ion-text-center" color="danger">
退出登录
</ion-label>
</ion-item>
</ion-list>
</ion-content>
</ion-page>
</template>
<style scoped>
.my-account {
--padding-top: 6px;
--padding-bottom: 6px;
}
.my-account ion-avatar {
width: 56px;
height: 56px;
margin-inline-end: 14px;
}
.my-account-placeholder {
font-size: 56px;
margin-inline-end: 14px;
color: var(--ion-color-medium);
}
.my-account h2 {
font-size: 20px;
font-weight: 600;
}
.my-sync-spinner {
display: inline-flex;
justify-content: center;
width: 29px;
margin-inline-end: 14px;
}
</style>