generated from VLADIMIR/template_frontend
up
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
<script setup lang="ts">
|
||||
import { NButton, NInput, NSpace, NText, NUpload, NUploadDragger } from 'naive-ui'
|
||||
import { computed, type PropType } from 'vue'
|
||||
|
||||
import { getURL } from '@/api/generated/crabs/evening_detective_server/client'
|
||||
import type { EditableIntroduction } from '@/types/editor'
|
||||
|
||||
import MessagePaper from './MessagePaper.vue'
|
||||
|
||||
const props = defineProps({
|
||||
introduction: {
|
||||
type: Object as PropType<EditableIntroduction>,
|
||||
required: true,
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:mode': [value: string]
|
||||
}>()
|
||||
|
||||
const updateMode = (mode: string) => {
|
||||
emit('update:mode', mode)
|
||||
}
|
||||
|
||||
const hasText = computed(() => Boolean(props.introduction?.text))
|
||||
const hasAudio = computed(() => Boolean(props.introduction?.audio))
|
||||
|
||||
// Бэкенд отдаёт аудио полным URL (домен + имя файла); на случай голого
|
||||
// имени файла строим URL до /api/files самостоятельно.
|
||||
const audioUrl = computed(() => {
|
||||
const value = props.introduction?.audio
|
||||
if (!value) return undefined
|
||||
if (/^https?:\/\//i.test(value)) return value
|
||||
return `${getURL()}/api/files/${encodeURIComponent(value)}`
|
||||
})
|
||||
|
||||
const removeAudio = () => {
|
||||
if (props.introduction) {
|
||||
props.introduction!.audio = ''
|
||||
props.introduction!.fileList = undefined
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Просмотр вступления командой (первый лист дела) -->
|
||||
<div v-if="mode == 'show'">
|
||||
<MessagePaper>
|
||||
<div class="intro-title font">Вступление</div>
|
||||
<hr class="intro-hr" />
|
||||
<div class="intro-text font">{{ introduction.text }}</div>
|
||||
<div v-if="hasAudio" class="intro-audio-block">
|
||||
<audio :src="audioUrl" controls preload="none" class="intro-audio"></audio>
|
||||
</div>
|
||||
</MessagePaper>
|
||||
</div>
|
||||
|
||||
<!-- Просмотр вступления в редакторе: клик — в режим редактирования -->
|
||||
<div v-if="mode == 'show-editor'" class="place-block-hover show-editor-block" @click="updateMode('edit')">
|
||||
<div class="message-header">
|
||||
Вступление
|
||||
</div>
|
||||
<hr class="hr" />
|
||||
<div class="message-content">
|
||||
<div v-if="hasAudio" class="intro-audio-block">
|
||||
<audio :src="audioUrl" controls preload="none" class="intro-audio"></audio>
|
||||
</div>
|
||||
<span v-if="hasText">{{ introduction.text }}</span>
|
||||
<span v-else class="intro-empty">Введение не задано — нажмите, чтобы добавить</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Редактирование вступления автором -->
|
||||
<div v-if="mode == 'edit'" class="place-block">
|
||||
<n-space vertical>
|
||||
<p class="settings-header">Текст вступления</p>
|
||||
<n-input v-model:value="props.introduction!.text" type="textarea"
|
||||
:autosize="{ minRows: 4, maxRows: 12 }"
|
||||
placeholder="Дорогие сыщики! В этом деле вам предстоит..." />
|
||||
|
||||
<p class="settings-header">Аудио вступления</p>
|
||||
<div class="intro-edit-audio">
|
||||
<n-space vertical>
|
||||
<audio v-if="hasAudio" :src="audioUrl" controls preload="none" class="intro-audio"></audio>
|
||||
<n-text v-else depth="3">Аудио не задано</n-text>
|
||||
<n-button @click="removeAudio()" :disabled="!hasAudio">Убрать аудио</n-button>
|
||||
</n-space>
|
||||
<n-upload directory-dnd :max="1" v-model:file-list="props.introduction!.fileList">
|
||||
<n-upload-dragger>
|
||||
<n-text style="font-size: 16px">
|
||||
Щелкните или перетащите аудиофайл в эту область для загрузки
|
||||
</n-text>
|
||||
</n-upload-dragger>
|
||||
</n-upload>
|
||||
</div>
|
||||
|
||||
<div class="slot-block">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</n-space>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Бумажный лист вступления (просмотр командой и в каталоге) */
|
||||
.font {
|
||||
font-family: 'font_old_typer';
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.intro-title {
|
||||
text-align: center;
|
||||
font-size: 26px;
|
||||
letter-spacing: 3px;
|
||||
color: var(--second-color);
|
||||
}
|
||||
|
||||
.intro-hr {
|
||||
margin: 10px 0;
|
||||
border: dashed 1px;
|
||||
border-color: #222;
|
||||
}
|
||||
|
||||
.intro-text {
|
||||
font-weight: 500;
|
||||
white-space: pre-wrap;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Панели в стиле блоков точек (см. PlaceBlock.vue) */
|
||||
.place-block-hover,
|
||||
.place-block {
|
||||
margin: 10px 0;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #222;
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
.place-block-hover:hover {
|
||||
color: #63e2b7;
|
||||
cursor: pointer;
|
||||
border: 1px solid #63e2b7;
|
||||
}
|
||||
|
||||
.show-editor-block {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hr {
|
||||
margin: 10px 0;
|
||||
border: dashed 1px;
|
||||
border-color: #eee;
|
||||
}
|
||||
|
||||
.message-header {
|
||||
font-size: 20px;
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
font-weight: 500;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.intro-empty {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.settings-header {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.intro-audio-block {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.intro-audio {
|
||||
width: 100%;
|
||||
max-width: 460px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.intro-edit-audio {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
gap: 20px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.slot-block {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user