generated from VLADIMIR/template_frontend
add
This commit is contained in:
@@ -19,7 +19,7 @@ import { useRoute } 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 type { Conclusion, Scenario } from '@/api/generated/crabs/evening_detective_server'
|
||||
import HeaderMenu from '@/components/HeaderMenu.vue'
|
||||
import IntroBlock from '@/components/IntroBlock.vue'
|
||||
import PlaceBlock from '@/components/PlaceBlock.vue'
|
||||
@@ -32,6 +32,7 @@ import type {
|
||||
EditablePlace,
|
||||
EditableQuestion,
|
||||
} from '@/types/editor'
|
||||
import { applicationFileURL } from '@/utils/application'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const client = getAuthClient()
|
||||
@@ -64,6 +65,54 @@ const savingBackground = ref(false)
|
||||
|
||||
const intro = ref<EditableIntroduction>({ text: '', audio: '' })
|
||||
const introMode = ref('show-editor')
|
||||
const conclusion = ref<Conclusion>({ text: '', video: '' })
|
||||
const conclusionFiles = ref<UploadFileInfo[]>([])
|
||||
const savingConclusion = ref(false)
|
||||
const conclusionVideoURL = computed(() => applicationFileURL(conclusion.value.video))
|
||||
|
||||
function resetConclusion() {
|
||||
conclusion.value = { text: '', video: '', ...scenario.value.story?.conclusion }
|
||||
conclusionFiles.value = []
|
||||
}
|
||||
|
||||
function removeConclusionVideo() {
|
||||
conclusion.value.video = ''
|
||||
conclusionFiles.value = []
|
||||
}
|
||||
|
||||
async function updateConclusion(remove = false) {
|
||||
savingConclusion.value = true
|
||||
try {
|
||||
let video = toRawFilename(conclusion.value.video)
|
||||
if (!remove && conclusionFiles.value.length) {
|
||||
const file = conclusionFiles.value[0].file
|
||||
if (!file) return
|
||||
const uploaded = await uploadFile(
|
||||
file,
|
||||
'scenarios_' + scenario.value.id + '_conclusion_' + file.name,
|
||||
)
|
||||
if (!uploaded.filename) return
|
||||
if (uploaded.fileType !== 'video') {
|
||||
message.error('Закрывашка должна содержать видео MP4 или WebM')
|
||||
return
|
||||
}
|
||||
video = uploaded.filename
|
||||
}
|
||||
const saved = remove ? { text: '', video: '' } : { text: conclusion.value.text, video }
|
||||
const res = await client.UpdateScenarioConclusion({ id: scenario.value.id, conclusion: saved })
|
||||
if (res.error) {
|
||||
message.error(res.error!)
|
||||
return
|
||||
}
|
||||
scenario.value.story!.conclusion = saved
|
||||
resetConclusion()
|
||||
message.success(remove ? 'Закрывашка удалена' : 'Закрывашка сохранена')
|
||||
} catch {
|
||||
message.error('Не удалось сохранить закрывашку')
|
||||
} finally {
|
||||
savingConclusion.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Точки сценария со служебными полями редактора (oldCode, mode, fileList)
|
||||
const editorPlaces = computed<EditablePlace[]>(() => {
|
||||
@@ -82,6 +131,7 @@ async function getScenario(id: number) {
|
||||
return
|
||||
}
|
||||
scenario.value = res.scenario!
|
||||
resetConclusion()
|
||||
scenario.value.story?.places?.forEach((item) => {
|
||||
const editable = item as EditablePlace
|
||||
editable.oldCode = editable.code
|
||||
@@ -195,7 +245,7 @@ async function uploadFile(file: File | null, filename: string): Promise<UploadFi
|
||||
return { filename: '', fileType: '' }
|
||||
}
|
||||
|
||||
// file_type возвращает сервер по содержимому файла (image | pdf | audio) —
|
||||
// file_type возвращает сервер по содержимому файла (image | pdf | audio | video) —
|
||||
// редактор сохраняет его на улике.
|
||||
return {
|
||||
filename: resUploadFile.filename || '',
|
||||
@@ -621,6 +671,58 @@ function getRandomComplimentForNastya() {
|
||||
</n-flex>
|
||||
</IntroBlock>
|
||||
|
||||
<n-card title="Закрывашка сценария">
|
||||
<n-space vertical>
|
||||
<n-text depth="3">Разгадка дела для автора. Игрокам не показывается.</n-text>
|
||||
<n-input
|
||||
v-model:value="conclusion.text"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 4, maxRows: 12 }"
|
||||
placeholder="Текст закрывашки"
|
||||
aria-label="Текст закрывашки"
|
||||
:disabled="savingConclusion"
|
||||
/>
|
||||
<video
|
||||
v-if="conclusionVideoURL"
|
||||
:src="conclusionVideoURL"
|
||||
controls
|
||||
preload="none"
|
||||
class="conclusion-video"
|
||||
aria-label="Видео закрывашки"
|
||||
></video>
|
||||
<n-button
|
||||
v-if="conclusion.video"
|
||||
:disabled="savingConclusion"
|
||||
@click="removeConclusionVideo()"
|
||||
>Убрать видео</n-button
|
||||
>
|
||||
<n-upload
|
||||
v-model:file-list="conclusionFiles"
|
||||
accept=".mp4,.webm"
|
||||
:max="1"
|
||||
:default-upload="false"
|
||||
:disabled="savingConclusion"
|
||||
>
|
||||
<n-button :disabled="savingConclusion">Выбрать видео MP4/WebM</n-button>
|
||||
</n-upload>
|
||||
<n-flex justify="end">
|
||||
<n-button
|
||||
v-if="scenario.story?.conclusion?.text || scenario.story?.conclusion?.video"
|
||||
:disabled="savingConclusion"
|
||||
@click="updateConclusion(true)"
|
||||
>Удалить закрывашку</n-button
|
||||
>
|
||||
<n-button :disabled="savingConclusion" @click="resetConclusion()">Отмена</n-button>
|
||||
<n-button
|
||||
:loading="savingConclusion"
|
||||
:disabled="savingConclusion || !scenario.story"
|
||||
@click="updateConclusion()"
|
||||
>Сохранить закрывашку</n-button
|
||||
>
|
||||
</n-flex>
|
||||
</n-space>
|
||||
</n-card>
|
||||
|
||||
<div class="questions-header">Вопросы дела</div>
|
||||
|
||||
<QuestionBlock
|
||||
@@ -817,6 +919,10 @@ function getRandomComplimentForNastya() {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.conclusion-video {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.background-preview {
|
||||
max-height: 200px;
|
||||
max-width: 100%;
|
||||
|
||||
Reference in New Issue
Block a user