Sun 394c6ce20c 适配多语言
Squashed commit of the following:

commit 632f86c0228c68391c01865c7576f3aa0408c102
Author: Sun <95302870@qq.com>
Date:   Sun Jan 7 14:47:55 2024 +0800

    退出的时候清除appstore

commit b9d805e49a3c6b2ad38bc8d527cb12cc8709012e
Author: Sun <95302870@qq.com>
Date:   Sun Jan 7 13:55:20 2024 +0800

    系统状态监控适配国际化

commit daece99723ec96d210241d2ca4e5a85dc5ae69bd
Author: Sun <95302870@qq.com>
Date:   Sun Jan 7 13:09:46 2024 +0800

    适配添加项目页面的国际化配置还有时钟的星期*

commit 8ea2b2fe951f6266415c96a197cb8d00faef4058
Author: Sun <95302870@qq.com>
Date:   Sun Jan 7 12:01:55 2024 +0800

    完成适配所有apps国际化

commit 21ef54e0d4afb10f560c8cb7aff666374afe0f87
Author: Sun <95302870@qq.com>
Date:   Sat Jan 6 21:36:07 2024 +0800

    增加读取默认浏览器语言

commit 6f710bbebe63ab2800193f27c71e5c0034f11978
Author: Sun <95302870@qq.com>
Date:   Sat Jan 6 21:09:58 2024 +0800

    登录页面增加语言选择选项

commit cb7c4a89a160ed3ef91ad566ec98e75325e7601f
Author: Sun <95302870@qq.com>
Date:   Sat Jan 6 20:37:16 2024 +0800

    首次尝试增加英文语言,并在我的信息设置

commit fb996e17cd11611d30c0e12feee00ddf7b225e32
Author: Sun <95302870@qq.com>
Date:   Sat Jan 6 18:22:40 2024 +0800

    完成基础设置页面的语言国际化适配
2024-01-07 14:53:53 +08:00

361 lines
11 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 setup lang="ts">
import { onMounted, ref } from 'vue'
import type { UploadFileInfo } from 'naive-ui'
import { NAlert, NButton, NCheckbox, NCheckboxGroup, NDivider, NInput, NSpace, NUpload, useMessage } from 'naive-ui'
import { RoundCardModal, SvgIcon } from '@/components/common'
import type { IconGroup, ImportJsonResult } from '@/utils/jsonImportExport'
import { ConfigVersionLowError, FormatError, exportJson, importJsonString } from '@/utils/jsonImportExport'
import { get as getAbout } from '@/api/system/about'
import { edit as addGroup, getList as getGroupList } from '@/api/panel/itemIconGroup'
import { addMultiple as addMultipleIcons, getListByGroupId } from '@/api/panel/itemIcon'
import { t } from '@/locales'
interface ItemGroup extends Panel.ItemIconGroup {
items?: Panel.ItemInfo[]
}
const ms = useMessage()
const jsonData = ref<string | null>(null)
const importWarning = ref<string[]>([])
const importRoundModalShow = ref(false)
const exportRoundModalShow = ref(false)
const loading = ref(false)
const uploadLoading = ref(false)
const version = ref('') // 当前软件版本
const debug = ref(false)
const importObj = ref<ImportJsonResult | null> (null)
const importItems = ref<string[]>(['icons']) // 当前软件版本支持导入导出的项目
const checkedItems = ref<string[]>(['icons']) // 当前准备导入的项目
// 导入图标
async function importIcons(): Promise<string | null> {
const groups = importObj.value?.geticons()
const batchSize = 50
if (!groups)
return null
try {
for (let i = 0; i < groups.length; i++) {
const element = groups[i]
// 创建组得到组id
const createGroupResponse = await addGroup<Panel.ItemIconGroup>({
title: element.title,
sort: element.sort,
})
if (createGroupResponse.code === 0) {
const groupId = createGroupResponse.data?.id
if (groupId) {
let addIcons: Panel.ItemInfo[] = []
// 批量添加子项
for (let iconI = 0; iconI < element.children.length; iconI++) {
const iconElement = element.children[iconI]
addIcons.push({
title: iconElement.title,
sort: iconElement.sort,
icon: iconElement.icon,
url: iconElement.url,
lanUrl: iconElement.lanUrl,
description: iconElement.description,
openMethod: iconElement.openMethod,
itemIconGroupId: groupId,
})
// 每 batchSize 个添加一次
if (addIcons.length === batchSize || iconI === element.children.length - 1) {
const response = await addMultipleIcons(addIcons)
if (response.code !== 0)
return response.msg
addIcons = []
}
}
}
}
else {
return createGroupResponse.msg
}
}
return null
}
catch (error) {
if (error instanceof Error)
return `${t('common.failed')}: ${error.message}`
else
return t('common.unknownError')
}
}
// 导出图标
async function exportIcons(): Promise<IconGroup[]> {
const iconGroups: IconGroup[] = []
// 获取组数据
const { code, data } = await getGroupList<Common.ListResponse<ItemGroup[]>>()
if (code === 0) {
// 使用 Promise.all 等待所有异步操作完成
await Promise.all(data.list.map(async (element) => {
const group: IconGroup = {
title: element.title as string,
sort: element.sort as 0,
children: [],
}
const res = await getListByGroupId<Common.ListResponse<Panel.ItemInfo[]>>(element.id)
if (res.code === 0) {
for (const iconElement of res.data.list) {
group.children.push({
icon: iconElement.icon,
sort: iconElement.sort || 99999,
title: iconElement.title,
url: iconElement.url,
lanUrl: iconElement.lanUrl || '',
description: iconElement.description || '',
openMethod: iconElement.openMethod || 1,
})
}
}
iconGroups.push(group)
}))
return iconGroups
}
else {
return []
}
}
onMounted(() => {
interface Version {
versionName: string
versionCode: number
}
getAbout<Version>().then((res) => {
if (res.code === 0)
version.value = res.data.versionName
})
})
function handleFileChange(options: { file: UploadFileInfo; fileList: Array<UploadFileInfo> }) {
uploadLoading.value = true
console.log(options.file.file)
if (options.file.file) {
const reader = new FileReader()
reader.onload = () => {
if (reader.result) {
jsonData.value = reader.result as string
importCheck()
}
else {
ms.error(`${t('common.failed')}: ${t('common.repeatLater')}`)
}
uploadLoading.value = false
}
reader.readAsText(options.file.file)
}
}
// 验证导入文件
function importCheck() {
importWarning.value = []
if (jsonData.value) {
try {
importObj.value = importJsonString(jsonData.value)
if (importObj.value) {
if (!importObj.value.isPassCheckMd5())
importWarning.value.push(t('apps.exportImport.fileModified'))
if (!importObj.value.isPassCheckConfigVersionOld())
importWarning.value.push(t('apps.exportImport.warnConfigFileLow'))
if (!importObj.value.isPassCheckConfigVersionNew())
importWarning.value.push(t('apps.exportImport.softwareVersionLow'))
// 暂时不做此处可以判断当前的配置文件是否存在的导入项目不存在隐藏importItems里面的值操作变量importItems
// 通过了验证,打开弹窗
importRoundModalShow.value = !importRoundModalShow.value
// console.log(importObj.value.geticons())
}
}
catch (error) {
if (error instanceof ConfigVersionLowError) {
ms.error(t('apps.exportImport.errorConfigFileLow'))
console.error('The configuration file version is too low to be compatible')
}
else if (error instanceof FormatError) {
ms.error(t('apps.exportImport.errorConfigFileFormat'))
console.error('The format is incorrect and cannot be imported')
}
}
}
else {
ms.error(t('apps.exportImport.errorConfigFileFormat'))
}
}
// 开始导出
async function handleStartExport() {
loading.value = true
// console.log('要导出的项目', checkedItems.value)
// 获取软件版本号
const exportResult = exportJson(version.value)
if (checkedItems.value.includes('icons')) {
console.log('export icons ...')
const iconGroups = await exportIcons()
exportResult.addIconsData(iconGroups)
console.log('export icons finish', iconGroups)
}
// console.log('导出结果')
jsonData.value = exportResult.string()
exportResult.exportFile()
loading.value = false
exportRoundModalShow.value = false
// ms.success(t('common.success'))
}
// 开始导入
async function handleStartImport() {
loading.value = true
if (checkedItems.value.includes('icons')) {
console.log('export icons ...')
const errMsg = await importIcons()
if (errMsg !== null)
ms.success(`${t('common.failed')}:${errMsg}`)
}
loading.value = false
importRoundModalShow.value = false
ms.success(`${t('common.success')}, ${t('common.refreshPage')}`)
}
</script>
<template>
<div class="pt-2">
<NAlert type="info" :bordered="false">
<p>{{ $t('apps.exportImport.tip') }}</p>
</NAlert>
<div class="flex justify-center m-[50px]">
<div class="m-[10px]">
<NUpload
accept=".sun-panel.json,.sunpanel.json"
directory-dnd
:default-upload="false"
:show-file-list="false"
@change="handleFileChange"
>
<NButton type="info" size="large" :loading="uploadLoading">
<template #icon>
<SvgIcon icon="fa6:solid-file-import" />
</template>
{{ $t('apps.exportImport.import') }}
</NButton>
</NUpload>
</div>
<div class="m-[10px]">
<NButton type="info" size="large" @click="exportRoundModalShow = !exportRoundModalShow">
<template #icon>
<SvgIcon icon="fa6:solid-file-export" />
</template>
{{ $t('apps.exportImport.export') }}
</NButton>
</div>
</div>
<div class="flex justify-center">
<a href="https://hslr-s.github.io/sun-panel-tool-page/#/" target="_blank">浏览器书签转换工具</a>
</div>
<!-- 调试模式 -->
<div v-if="debug">
<NButton @click="importCheck">
检查导入
</NButton>
<!-- <NButton @click="exportJsonS">
导出JSON
</NButton> -->
<NButton @click="jsonData = ''">
清空导入数据
</NButton>
<NInput
v-model:value="jsonData"
type="textarea"
placeholder="基本的 Textarea"
/>
<div v-if="jsonData">
<h2>JSON 数据</h2>
<pre>{{ jsonData }}</pre>
</div>
</div>
<RoundCardModal v-model:show="importRoundModalShow" style="max-width: 400px;" :title=" $t('apps.exportImport.import')">
<div v-if="importWarning.length > 0">
<NAlert :title="$t('common.warning')" type="warning">
<div v-for="(text, index) in importWarning " :key="index">
{{ text }}
</div>
</NAlert>
</div>
<NDivider title-placement="left">
{{ $t('apps.exportImport.selectImportData') }}
</NDivider>
<NSpace justify="center" style="margin-top: 20px;">
<NCheckboxGroup v-model:value="checkedItems">
<NCheckbox v-if="importItems.includes('icons')" value="icons" :label="$t('apps.exportImport.moduleIcon')" />
<NCheckbox v-if="importItems.includes('style')" value="style" :label="$t('apps.exportImport.moduleStyle')" />
</NCheckboxGroup>
</NSpace>
<NSpace justify="center">
<div class="mt-[50px]">
<NButton type="success" :disabled="checkedItems.length === 0" :loading="loading" @click="handleStartImport">
{{ $t('common.continue') }}
</NButton>
</div>
</NSpace>
</RoundCardModal>
<RoundCardModal v-model:show="exportRoundModalShow" style="max-width: 400px;" :title=" $t('apps.exportImport.export')">
<NDivider title-placement="left">
{{ $t('apps.exportImport.selectExportData') }}
</NDivider>
<NSpace justify="center" style="margin-top: 20px;">
<NCheckboxGroup v-model:value="checkedItems">
<NCheckbox v-if="importItems.includes('icons')" value="icons" :label="$t('apps.exportImport.moduleIcon')" />
<NCheckbox v-if="importItems.includes('style')" value="style" :label="$t('apps.exportImport.moduleStyle')" />
</NCheckboxGroup>
</NSpace>
<NSpace justify="center">
<div class="mt-[50px]">
<NButton type="success" :disabled="checkedItems.length === 0" :loading="loading" @click="handleStartExport">
{{ $t('common.continue') }}
</NButton>
</div>
</NSpace>
</RoundCardModal>
</div>
</template>