Spaces:
Runtime error
Runtime error
<script lang="ts" setup> | |
import { computed, ref } from 'vue' | |
import { NButton, NInput, NPopconfirm, NSelect, useMessage } from 'naive-ui' | |
import type { Language, Theme } from '@/store/modules/app/helper' | |
import { SvgIcon } from '@/components/common' | |
import { useAppStore, useUserStore } from '@/store' | |
import type { UserInfo } from '@/store/modules/user/helper' | |
import { getCurrentDate } from '@/utils/functions' | |
import { useBasicLayout } from '@/hooks/useBasicLayout' | |
import { t } from '@/locales' | |
const appStore = useAppStore() | |
const userStore = useUserStore() | |
const { isMobile } = useBasicLayout() | |
const ms = useMessage() | |
const theme = computed(() => appStore.theme) | |
const userInfo = computed(() => userStore.userInfo) | |
const avatar = ref(userInfo.value.avatar ?? '') | |
const name = ref(userInfo.value.name ?? '') | |
const description = ref(userInfo.value.description ?? '') | |
const language = computed({ | |
get() { | |
return appStore.language | |
}, | |
set(value: Language) { | |
appStore.setLanguage(value) | |
}, | |
}) | |
const themeOptions: { label: string; key: Theme; icon: string }[] = [ | |
{ | |
label: 'Auto', | |
key: 'auto', | |
icon: 'ri:contrast-line', | |
}, | |
{ | |
label: 'Light', | |
key: 'light', | |
icon: 'ri:sun-foggy-line', | |
}, | |
{ | |
label: 'Dark', | |
key: 'dark', | |
icon: 'ri:moon-foggy-line', | |
}, | |
] | |
const languageOptions: { label: string; key: Language; value: Language }[] = [ | |
{ label: '简体中文', key: 'zh-CN', value: 'zh-CN' }, | |
{ label: '繁體中文', key: 'zh-TW', value: 'zh-TW' }, | |
{ label: 'English', key: 'en-US', value: 'en-US' }, | |
{ label: '한국어', key: 'ko-KR', value: 'ko-KR' }, | |
] | |
function updateUserInfo(options: Partial<UserInfo>) { | |
userStore.updateUserInfo(options) | |
ms.success(t('common.success')) | |
} | |
function handleReset() { | |
userStore.resetUserInfo() | |
ms.success(t('common.success')) | |
window.location.reload() | |
} | |
function exportData(): void { | |
const date = getCurrentDate() | |
const data: string = localStorage.getItem('chatStorage') || '{}' | |
const jsonString: string = JSON.stringify(JSON.parse(data), null, 2) | |
const blob: Blob = new Blob([jsonString], { type: 'application/json' }) | |
const url: string = URL.createObjectURL(blob) | |
const link: HTMLAnchorElement = document.createElement('a') | |
link.href = url | |
link.download = `chat-store_${date}.json` | |
document.body.appendChild(link) | |
link.click() | |
document.body.removeChild(link) | |
} | |
function importData(event: Event): void { | |
const target = event.target as HTMLInputElement | |
if (!target || !target.files) | |
return | |
const file: File = target.files[0] | |
if (!file) | |
return | |
const reader: FileReader = new FileReader() | |
reader.onload = () => { | |
try { | |
const data = JSON.parse(reader.result as string) | |
localStorage.setItem('chatStorage', JSON.stringify(data)) | |
ms.success(t('common.success')) | |
location.reload() | |
} | |
catch (error) { | |
ms.error(t('common.invalidFileFormat')) | |
} | |
} | |
reader.readAsText(file) | |
} | |
function clearData(): void { | |
localStorage.removeItem('chatStorage') | |
location.reload() | |
} | |
function handleImportButtonClick(): void { | |
const fileInput = document.getElementById('fileInput') as HTMLElement | |
if (fileInput) | |
fileInput.click() | |
} | |
</script> | |
<template> | |
<div class="p-4 space-y-5 min-h-[200px]"> | |
<div class="space-y-6"> | |
<div class="flex items-center space-x-4"> | |
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.avatarLink') }}</span> | |
<div class="flex-1"> | |
<NInput v-model:value="avatar" placeholder="" /> | |
</div> | |
<NButton size="tiny" text type="primary" @click="updateUserInfo({ avatar })"> | |
{{ $t('common.save') }} | |
</NButton> | |
</div> | |
<div class="flex items-center space-x-4"> | |
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.name') }}</span> | |
<div class="w-[200px]"> | |
<NInput v-model:value="name" placeholder="" /> | |
</div> | |
<NButton size="tiny" text type="primary" @click="updateUserInfo({ name })"> | |
{{ $t('common.save') }} | |
</NButton> | |
</div> | |
<div class="flex items-center space-x-4"> | |
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.description') }}</span> | |
<div class="flex-1"> | |
<NInput v-model:value="description" placeholder="" /> | |
</div> | |
<NButton size="tiny" text type="primary" @click="updateUserInfo({ description })"> | |
{{ $t('common.save') }} | |
</NButton> | |
</div> | |
<div | |
class="flex items-center space-x-4" | |
:class="isMobile && 'items-start'" | |
> | |
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.chatHistory') }}</span> | |
<div class="flex flex-wrap items-center gap-4"> | |
<NButton size="small" @click="exportData"> | |
<template #icon> | |
<SvgIcon icon="ri:download-2-fill" /> | |
</template> | |
{{ $t('common.export') }} | |
</NButton> | |
<input id="fileInput" type="file" style="display:none" @change="importData"> | |
<NButton size="small" @click="handleImportButtonClick"> | |
<template #icon> | |
<SvgIcon icon="ri:upload-2-fill" /> | |
</template> | |
{{ $t('common.import') }} | |
</NButton> | |
<NPopconfirm placement="bottom" @positive-click="clearData"> | |
<template #trigger> | |
<NButton size="small"> | |
<template #icon> | |
<SvgIcon icon="ri:close-circle-line" /> | |
</template> | |
{{ $t('common.clear') }} | |
</NButton> | |
</template> | |
{{ $t('chat.clearHistoryConfirm') }} | |
</NPopconfirm> | |
</div> | |
</div> | |
<div class="flex items-center space-x-4"> | |
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.theme') }}</span> | |
<div class="flex flex-wrap items-center gap-4"> | |
<template v-for="item of themeOptions" :key="item.key"> | |
<NButton | |
size="small" | |
:type="item.key === theme ? 'primary' : undefined" | |
@click="appStore.setTheme(item.key)" | |
> | |
<template #icon> | |
<SvgIcon :icon="item.icon" /> | |
</template> | |
</NButton> | |
</template> | |
</div> | |
</div> | |
<div class="flex items-center space-x-4"> | |
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.language') }}</span> | |
<div class="flex flex-wrap items-center gap-4"> | |
<NSelect | |
style="width: 140px" | |
:value="language" | |
:options="languageOptions" | |
@update-value="value => appStore.setLanguage(value)" | |
/> | |
</div> | |
</div> | |
<div class="flex items-center space-x-4"> | |
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.resetUserInfo') }}</span> | |
<NButton size="small" @click="handleReset"> | |
{{ $t('common.reset') }} | |
</NButton> | |
</div> | |
</div> | |
</div> | |
</template> | |