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:
parent
a689406b28
commit
45cbfbf002
|
@ -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>
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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}`
|
||||||
|
}
|
Loading…
Reference in New Issue