add buttons

This commit is contained in:
2026-08-20 23:39:56 +07:00
parent 4174ad304a
commit 606d515ade
6 changed files with 234 additions and 6 deletions
+38
View File
@@ -6,6 +6,7 @@ import { NAlert, NCard, NFlex, NInput, NModal, NSpace, NTag, NText, NUpload, NUp
import { ref } from 'vue'
import { useRoute } from 'vue-router'
import { getArchiveClient } from '@/api/archive_client'
import { getAuthClient } from '@/api/auth_client'
import type { Place, Scenario } from '@/api/generated/crabs/evening_detective_server'
import HeaderMenu from '@/components/HeaderMenu.vue'
@@ -15,12 +16,14 @@ import { useAuthStore } from '@/stores/auth'
const authStore = useAuthStore()
const client = getAuthClient()
const archiveClient = getArchiveClient()
const route = useRoute()
const scenarioId = route.params.id
const showSettingsModal = ref(false)
const statusScenarioName = ref('')
const deletedScenarioName = ref('')
const isDownloadingArchive = ref(false)
const message = useMessage()
@@ -105,6 +108,27 @@ async function draftScenario(id: number) {
showSettingsModal.value = false
}
async function downloadArchive() {
isDownloadingArchive.value = true
try {
const { blob, filename } = await archiveClient.downloadScenarioArchive(scenario.value.id!)
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = filename
document.body.appendChild(link)
link.click()
link.remove()
// Отзываем URL после того как браузер начал скачивание
setTimeout(() => URL.revokeObjectURL(url), 1000)
message.success('Архив сценария скачан')
} catch (err) {
message.error(err instanceof Error ? err.message : 'Не удалось скачать архив')
} finally {
isDownloadingArchive.value = false
}
}
async function uploadFile(file: File | null, filename: string): Promise<string> {
if (!file) {
return ''
@@ -370,6 +394,15 @@ function getRandomComplimentForNastya() {
<n-button @click="updateScenario()" ghost> Сохранить изменения </n-button>
<hr class="settings-hr" />
<p class="settings-header">Архив сценария</p>
<p class="settings-hint">
Скачайте сценарий ZIP-архивом (scenario.json + изображения) и импортируйте его в другом аккаунте.
</p>
<n-button :loading="isDownloadingArchive" :disabled="!scenario.id" ghost @click="downloadArchive()">
Скачать архив (.zip)
</n-button>
<hr class="settings-hr" />
<p>Создать тестовую игру</p>
<n-button @click="addGame(scenario.name || '', scenario.id!)" ghost>
@@ -443,6 +476,11 @@ function getRandomComplimentForNastya() {
margin-top: 20px;
}
.settings-hint {
color: #999;
margin-bottom: 10px;
}
.settings-hr {
margin: 20px 0;
}
+88 -1
View File
@@ -1,14 +1,18 @@
<script setup lang="ts">
import { Archive } from '@vicons/carbon'
import { Icon } from '@vicons/utils'
import { useMessage } from 'naive-ui'
import { NButton, NCard, NFlex,NInput, NModal, NTag } from 'naive-ui'
import { NButton, NCard, NFlex, NInput, NModal, NTag, NText, NUpload, NUploadDragger, type UploadFileInfo } from 'naive-ui'
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { getArchiveClient } from '@/api/archive_client'
import { getAuthClient } from '@/api/auth_client'
import type { Scenario } from '@/api/generated/crabs/evening_detective_server'
import HeaderMenu from '@/components/HeaderMenu.vue'
const client = getAuthClient()
const archiveClient = getArchiveClient()
const scenarios = ref<Scenario[]>([])
@@ -19,6 +23,10 @@ const message = useMessage()
const showAddScenarioModal = ref(false)
const newScenarioName = ref('')
const showImportModal = ref(false)
const importFileList = ref<UploadFileInfo[]>([])
const isImporting = ref(false)
async function getScenarios() {
scenarios.value = []
const res = await client.GetMyScenarios({})
@@ -38,6 +46,33 @@ async function toScenarioEditor(id: number) {
router.push('/scenarios/' + id + '/editor')
}
function resetImport() {
importFileList.value = []
showImportModal.value = false
}
async function importScenario() {
const file = importFileList.value[0]?.file
if (!file) {
return
}
isImporting.value = true
try {
const res = await archiveClient.uploadScenarioArchive(file)
if (res.error) {
message.error(res.error)
return
}
message.success('Сценарий импортирован из архива')
resetImport()
await toScenarioEditor(res.id!)
} catch (err) {
message.error(err instanceof Error ? err.message : 'Не удалось импортировать архив')
} finally {
isImporting.value = false
}
}
getScenarios()
</script>
@@ -51,6 +86,17 @@ getScenarios()
</div>
</div>
<div class="scenario-block" @click="showImportModal = true">
<div class="scenario-image-block scenario-image-plus import-icon">
<Icon>
<Archive />
</Icon>
</div>
<div class="scenario-content-block">
<p class="scenario-title">Импортировать из архива</p>
</div>
</div>
<div class="scenario-block" v-for="scenario in scenarios" @click="toScenarioEditor(scenario.id!)"
v-bind:key="scenario.id">
<div class="scenario-image-block scenario-image" :style="{ backgroundImage: `url(${scenario.image})` }"></div>
@@ -86,6 +132,37 @@ getScenarios()
</n-card>
</n-modal>
<!-- Окно импорта сценария из архива -->
<n-modal v-model:show="showImportModal">
<n-card style="width: 600px" title="Импорт сценария из архива" :bordered="false" size="huge" role="dialog"
aria-modal="true">
<template #header-extra>
<n-button @click="resetImport()"> x </n-button>
</template>
<p class="import-hint">
Выберите ZIP-архив сценария (файл scenario.json + папка images). Архив можно получить
через «Скачать архив» в настройках сценария.
</p>
<n-upload :max="1" v-model:file-list="importFileList"
accept=".zip,application/zip,application/x-zip-compressed,application/octet-stream">
<n-upload-dragger>
<n-text style="font-size: 16px">
Щелкните или перетащите ZIP-архив в эту область
</n-text>
</n-upload-dragger>
</n-upload>
<template #footer>
<n-flex justify="end">
<n-button @click="resetImport()">Отмена</n-button>
<n-button type="primary" :loading="isImporting" :disabled="importFileList.length == 0"
@click="importScenario()">
Импортировать
</n-button>
</n-flex>
</template>
</n-card>
</n-modal>
<HeaderMenu active="scenarios" />
</template>
@@ -126,6 +203,16 @@ getScenarios()
background-color: #111;
}
.import-icon svg {
height: 100px;
width: 100px;
}
.import-hint {
color: #aaa;
margin-bottom: 16px;
}
.scenario-image {
background-size: cover;
background-position: center;