2026-04-16 09:40:25 +08:00
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { ref } from 'vue'
|
2026-04-16 15:19:05 +08:00
|
|
|
import { NModal, NUpload, NButton, useMessage } from 'naive-ui'
|
|
|
|
|
import type { UploadFileInfo } from 'naive-ui'
|
2026-04-16 09:40:25 +08:00
|
|
|
import { useProfilesStore } from '@/stores/hermes/profiles'
|
|
|
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
|
|
close: []
|
|
|
|
|
saved: []
|
|
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
const { t } = useI18n()
|
|
|
|
|
const profilesStore = useProfilesStore()
|
|
|
|
|
const message = useMessage()
|
|
|
|
|
|
|
|
|
|
const showModal = ref(true)
|
|
|
|
|
const loading = ref(false)
|
2026-04-16 15:19:05 +08:00
|
|
|
const fileList = ref<UploadFileInfo[]>([])
|
|
|
|
|
|
|
|
|
|
const ACCEPT_TYPES = [
|
|
|
|
|
'.tar.gz',
|
|
|
|
|
'.tgz',
|
|
|
|
|
'.gz',
|
|
|
|
|
'.zip',
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
function beforeUpload({ file }: { file: UploadFileInfo }) {
|
|
|
|
|
const name = file.name?.toLowerCase() || ''
|
|
|
|
|
const valid = ACCEPT_TYPES.some(ext => name.endsWith(ext))
|
|
|
|
|
if (!valid) {
|
|
|
|
|
message.warning(t('profiles.importInvalidFile'))
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
2026-04-16 09:40:25 +08:00
|
|
|
|
|
|
|
|
async function handleSave() {
|
2026-04-16 15:19:05 +08:00
|
|
|
if (!fileList.value.length) {
|
|
|
|
|
message.warning(t('profiles.importSelectFile'))
|
2026-04-16 09:40:25 +08:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
try {
|
2026-04-16 15:19:05 +08:00
|
|
|
const file = fileList.value[0].file
|
|
|
|
|
if (!file) {
|
|
|
|
|
message.error(t('profiles.importFailed'))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const ok = await profilesStore.importProfile(file)
|
2026-04-16 09:40:25 +08:00
|
|
|
if (ok) {
|
|
|
|
|
message.success(t('profiles.importSuccess'))
|
|
|
|
|
emit('saved')
|
|
|
|
|
} else {
|
|
|
|
|
message.error(t('profiles.importFailed'))
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
loading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleClose() {
|
|
|
|
|
showModal.value = false
|
|
|
|
|
setTimeout(() => emit('close'), 200)
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<NModal
|
|
|
|
|
v-model:show="showModal"
|
|
|
|
|
preset="card"
|
|
|
|
|
:title="t('profiles.import')"
|
|
|
|
|
:style="{ width: 'min(420px, calc(100vw - 32px))' }"
|
|
|
|
|
:mask-closable="!loading"
|
|
|
|
|
@after-leave="emit('close')"
|
|
|
|
|
>
|
2026-04-16 15:19:05 +08:00
|
|
|
<NUpload
|
|
|
|
|
v-model:file-list="fileList"
|
|
|
|
|
:max="1"
|
|
|
|
|
:accept="ACCEPT_TYPES.join(',')"
|
|
|
|
|
:disabled="loading"
|
|
|
|
|
@before-upload="beforeUpload"
|
|
|
|
|
>
|
|
|
|
|
<NButton>{{ t('profiles.importSelectFile') }}</NButton>
|
|
|
|
|
</NUpload>
|
2026-04-16 09:40:25 +08:00
|
|
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
<div class="modal-footer">
|
|
|
|
|
<NButton @click="handleClose">{{ t('common.cancel') }}</NButton>
|
2026-04-16 15:19:05 +08:00
|
|
|
<NButton type="primary" :loading="loading" :disabled="!fileList.length" @click="handleSave">
|
2026-04-16 09:40:25 +08:00
|
|
|
{{ t('common.confirm') }}
|
|
|
|
|
</NButton>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</NModal>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
.modal-footer {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|