feat: 增加聊天记录导入导出清空的功能 (#369)

* feat: 增加聊天记录导入导出功能

* feat: 添加日期和图标

* perf: 移除触发事件改为 reload

---------

Co-authored-by: Nine9 <germmc99@gmail.com>
Co-authored-by: ChenZhaoYu <790348264@qq.com>
This commit is contained in:
Nine9 2023-03-07 18:26:55 +08:00 committed by GitHub
parent a689406b28
commit 45cbfbf002
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 113 additions and 34 deletions

View File

@ -1,18 +1,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { NButton, NInput, useMessage } from 'naive-ui' import { NButton, NInput, NPopconfirm, useMessage } from 'naive-ui'
import type { Language, Theme } from '@/store/modules/app/helper' import type { Language, Theme } from '@/store/modules/app/helper'
import { SvgIcon } from '@/components/common' import { SvgIcon } from '@/components/common'
import { useAppStore, useUserStore } from '@/store' import { useAppStore, useUserStore } from '@/store'
import type { UserInfo } from '@/store/modules/user/helper' import type { UserInfo } from '@/store/modules/user/helper'
import { getCurrentDate } from '@/utils/functions'
import { t } from '@/locales' import { t } from '@/locales'
interface Emit {
(event: 'update'): void
}
const emit = defineEmits<Emit>()
const appStore = useAppStore() const appStore = useAppStore()
const userStore = useUserStore() const userStore = useUserStore()
@ -68,7 +63,56 @@ function updateUserInfo(options: Partial<UserInfo>) {
function handleReset() { function handleReset() {
userStore.resetUserInfo() userStore.resetUserInfo()
ms.success(t('common.success')) ms.success(t('common.success'))
emit('update') 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> </script>
@ -102,25 +146,49 @@ function handleReset() {
{{ $t('common.save') }} {{ $t('common.save') }}
</NButton> </NButton>
</div> </div>
<div class="flex items-center space-x-4"> <div class="flex items-center space-x-4">
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.resetUserInfo') }}</span> <span class="flex-shrink-0 w-[100px]">{{ $t('setting.chatHistory') }}</span>
<NButton text type="primary" @click="handleReset">
{{ $t('common.reset') }} <NButton @click="exportData">
<template #icon>
<SvgIcon icon="ri:download-2-fill" />
</template>
{{ $t('common.export') }}
</NButton> </NButton>
<input id="fileInput" type="file" style="display:none" @change="importData">
<NButton @click="handleImportButtonClick">
<template #icon>
<SvgIcon icon="ri:upload-2-fill" />
</template>
{{ $t('common.import') }}
</NButton>
<NPopconfirm placement="bottom" @positive-click="clearData">
<template #trigger>
<NButton>
<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"> <div class="flex items-center space-x-4">
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.theme') }}</span> <span class="flex-shrink-0 w-[100px]">{{ $t('setting.theme') }}</span>
<div class="flex items-center space-x-4"> <div class="flex items-center space-x-4">
<template v-for="item of themeOptions" :key="item.key"> <template v-for="item of themeOptions" :key="item.key">
<a <NButton
class="flex items-center justify-center h-8 px-4 border rounded-md cursor-pointer dark:border-neutral-700" :type="item.key === theme ? 'primary' : undefined"
:class="item.key === theme && ['bg-[#4ca85e]', 'border-[#4ca85e]', 'text-white']"
@click="appStore.setTheme(item.key)" @click="appStore.setTheme(item.key)"
> >
<span class="text-xl"> <template #icon>
<SvgIcon :icon="item.icon" /> <SvgIcon :icon="item.icon" />
</span> </template>
</a> </NButton>
</template> </template>
</div> </div>
</div> </div>
@ -128,18 +196,21 @@ function handleReset() {
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.language') }}</span> <span class="flex-shrink-0 w-[100px]">{{ $t('setting.language') }}</span>
<div class="flex items-center space-x-4"> <div class="flex items-center space-x-4">
<template v-for="item of languageOptions" :key="item.key"> <template v-for="item of languageOptions" :key="item.key">
<a <NButton
class="flex items-center justify-center h-8 px-4 border rounded-md cursor-pointer dark:border-neutral-700" :type="item.key === language ? 'primary' : undefined"
:class="item.key === language && ['bg-[#4ca85e]', 'border-[#4ca85e]', 'text-white']"
@click="appStore.setLanguage(item.key)" @click="appStore.setLanguage(item.key)"
> >
<span class="text-sm"> {{ item.label }}
{{ item.label }} </NButton>
</span>
</a>
</template> </template>
</div> </div>
</div> </div>
<div class="flex items-center space-x-4">
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.resetUserInfo') }}</span>
<NButton @click="handleReset">
{{ $t('common.reset') }}
</NButton>
</div>
</div> </div>
</div> </div>
</template> </template>

View File

@ -19,8 +19,6 @@ interface Emit {
const active = ref('General') const active = ref('General')
const reload = ref(false)
const show = computed({ const show = computed({
get() { get() {
return props.visible return props.visible
@ -29,13 +27,6 @@ const show = computed({
emit('update:visible', visible) emit('update:visible', visible)
}, },
}) })
function handleReload() {
reload.value = true
setTimeout(() => {
reload.value = false
}, 0)
}
</script> </script>
<template> <template>
@ -48,7 +39,7 @@ function handleReload() {
<span class="ml-2">{{ $t('setting.general') }}</span> <span class="ml-2">{{ $t('setting.general') }}</span>
</template> </template>
<div class="min-h-[100px]"> <div class="min-h-[100px]">
<General v-if="!reload" @update="handleReload" /> <General />
</div> </div>
</NTabPane> </NTabPane>
<NTabPane name="Config" tab="Config"> <NTabPane name="Config" tab="Config">

View File

@ -3,6 +3,9 @@ export default {
delete: 'Delete', delete: 'Delete',
save: 'Save', save: 'Save',
reset: 'Reset', reset: 'Reset',
export: 'Export',
import: 'Import',
clear: 'Clear',
yes: 'Yes', yes: 'Yes',
no: 'No', no: 'No',
noData: 'No Data', noData: 'No Data',
@ -21,6 +24,7 @@ export default {
deleteMessage: 'Delete Message', deleteMessage: 'Delete Message',
deleteMessageConfirm: 'Are you sure to delete this message?', deleteMessageConfirm: 'Are you sure to delete this message?',
deleteHistoryConfirm: 'Are you sure to clear this history?', deleteHistoryConfirm: 'Are you sure to clear this history?',
clearHistoryConfirm: 'Are you sure to clear chat history?',
}, },
setting: { setting: {
setting: 'Setting', setting: 'Setting',
@ -30,6 +34,7 @@ export default {
name: 'Name', name: 'Name',
description: 'Description', description: 'Description',
resetUserInfo: 'Reset UserInfo', resetUserInfo: 'Reset UserInfo',
chatHistory: 'ChatHistory',
theme: 'Theme', theme: 'Theme',
language: 'Language', language: 'Language',
api: 'API', api: 'API',

View File

@ -3,6 +3,9 @@ export default {
delete: '删除', delete: '删除',
save: '保存', save: '保存',
reset: '重置', reset: '重置',
export: '导出',
import: '导入',
clear: '清空',
yes: '是', yes: '是',
no: '否', no: '否',
noData: '暂无数据', noData: '暂无数据',
@ -21,6 +24,7 @@ export default {
deleteMessage: '删除消息', deleteMessage: '删除消息',
deleteMessageConfirm: '是否删除此消息?', deleteMessageConfirm: '是否删除此消息?',
deleteHistoryConfirm: '确定删除此记录?', deleteHistoryConfirm: '确定删除此记录?',
clearHistoryConfirm: '确定清空聊天记录?',
}, },
setting: { setting: {
setting: '设置', setting: '设置',
@ -30,6 +34,7 @@ export default {
name: '名称', name: '名称',
description: '描述', description: '描述',
resetUserInfo: '重置用户信息', resetUserInfo: '重置用户信息',
chatHistory: '聊天记录',
theme: '主题', theme: '主题',
language: '语言', language: '语言',
api: 'API', api: 'API',

View File

@ -0,0 +1,7 @@
export function getCurrentDate() {
const date = new Date()
const day = date.getDate()
const month = date.getMonth() + 1
const year = date.getFullYear()
return `${year}-${month}-${day}`
}