mirror of
https://github.com/xfgryujk/blivechat.git
synced 2026-08-22 19:33:27 +08:00
前端添加自定义模板配置
This commit is contained in:
@@ -9,3 +9,7 @@ export async function uploadEmoticon(file) {
|
|||||||
body.set('file', file)
|
body.set('file', file)
|
||||||
return (await axios.post('/api/emoticon', body)).data
|
return (await axios.post('/api/emoticon', body)).data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getTemplates() {
|
||||||
|
return (await axios.get('/api/templates')).data
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,6 +65,13 @@ export default {
|
|||||||
addEmoticon: 'Add emote',
|
addEmoticon: 'Add emote',
|
||||||
emoticonFileTooLarge: 'File size is too large. Max size is 1MB',
|
emoticonFileTooLarge: 'File size is too large. Max size is 1MB',
|
||||||
|
|
||||||
|
template: 'Custom HTML Templates',
|
||||||
|
templateDefaultTitle: 'Default',
|
||||||
|
templateDefaultDescription: 'A YouTube-style template that can be styled with custom CSS. Choose this if you are unfamiliar with custom HTML templates, otherwise your custom CSS will not take effect',
|
||||||
|
templateCustomUrlTitle: 'Enter template URL',
|
||||||
|
templateCustomUrlDescription: 'For templates not listed, you can manually enter the URL provided by the author here',
|
||||||
|
author: 'Author: ',
|
||||||
|
|
||||||
urlTooLong: 'The room URL is too long, and will be truncated by Livehime (but not by OBS)',
|
urlTooLong: 'The room URL is too long, and will be truncated by Livehime (but not by OBS)',
|
||||||
roomUrlUpdated: 'The room URL is updated. Remember to copy it again',
|
roomUrlUpdated: 'The room URL is updated. Remember to copy it again',
|
||||||
roomUrl: 'Room URL',
|
roomUrl: 'Room URL',
|
||||||
|
|||||||
@@ -65,6 +65,13 @@ export default {
|
|||||||
addEmoticon: 'スタンプを追加',
|
addEmoticon: 'スタンプを追加',
|
||||||
emoticonFileTooLarge: 'ファイルサイズが大きすぎます。最大サイズは1MBです',
|
emoticonFileTooLarge: 'ファイルサイズが大きすぎます。最大サイズは1MBです',
|
||||||
|
|
||||||
|
template: 'カスタムHTMLテンプレート',
|
||||||
|
templateDefaultTitle: 'デフォルト',
|
||||||
|
templateDefaultDescription: 'YouTube風のテンプレートで、カスタムCSSでスタイルを変更できます。カスタムHTMLテンプレートの機能に慣れていない場合はこれを選択してください。そうでない場合、カスタムCSSが適用されない',
|
||||||
|
templateCustomUrlTitle: 'テンプレートURLを入力',
|
||||||
|
templateCustomUrlDescription: 'リストにないテンプレートは、作者が提供するURLをここで手動で入力できます',
|
||||||
|
author: '作者:',
|
||||||
|
|
||||||
urlTooLong: 'ルームのURLが長すぎて、直播姬によって切り詰められます(ただし、OBSでは切り詰められません)',
|
urlTooLong: 'ルームのURLが長すぎて、直播姬によって切り詰められます(ただし、OBSでは切り詰められません)',
|
||||||
roomUrlUpdated: 'ルームのURLが更新されました。再度コピーすることをお忘れなく',
|
roomUrlUpdated: 'ルームのURLが更新されました。再度コピーすることをお忘れなく',
|
||||||
roomUrl: 'ルームのURL',
|
roomUrl: 'ルームのURL',
|
||||||
|
|||||||
@@ -65,6 +65,13 @@ export default {
|
|||||||
addEmoticon: '添加表情',
|
addEmoticon: '添加表情',
|
||||||
emoticonFileTooLarge: '文件尺寸太大,最大1MB',
|
emoticonFileTooLarge: '文件尺寸太大,最大1MB',
|
||||||
|
|
||||||
|
template: '自定义HTML模板',
|
||||||
|
templateDefaultTitle: '默认',
|
||||||
|
templateDefaultDescription: '仿YouTube风格的模板,可以用自定义CSS修改样式。如果你不了解自定义HTML模板的功能,就选择这个,否则自定义CSS不会生效',
|
||||||
|
templateCustomUrlTitle: '输入模板URL',
|
||||||
|
templateCustomUrlDescription: '没有列出的模板,也可以在这里手动输入作者提供的URL',
|
||||||
|
author: '作者:',
|
||||||
|
|
||||||
urlTooLong: '房间URL太长了,会被直播姬截断(OBS不会)',
|
urlTooLong: '房间URL太长了,会被直播姬截断(OBS不会)',
|
||||||
roomUrlUpdated: '房间URL已更新,记得重新复制',
|
roomUrlUpdated: '房间URL已更新,记得重新复制',
|
||||||
roomUrl: '房间URL',
|
roomUrl: '房间URL',
|
||||||
|
|||||||
177
frontend/src/views/Home/TemplateSelect.vue
Normal file
177
frontend/src/views/Home/TemplateSelect.vue
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-radio-group :value="selectedIndex" @input="onSelect">
|
||||||
|
<el-radio :label="INDEX_DEFAULT" border>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="title-line">
|
||||||
|
<h3 class="name">{{ $t('home.templateDefaultTitle') }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="description">{{ $t('home.templateDefaultDescription') }}</div>
|
||||||
|
</div>
|
||||||
|
</el-radio>
|
||||||
|
|
||||||
|
<el-radio :label="INDEX_CUSTOM" border>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="title-line">
|
||||||
|
<h3 class="name">{{ $t('home.templateCustomUrlTitle') }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="description">{{ $t('home.templateCustomUrlDescription') }}</div>
|
||||||
|
<div>
|
||||||
|
<el-input v-model="customUrl" placeholder="https://example.com/path/to/you/template/index.html"></el-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-radio>
|
||||||
|
|
||||||
|
<el-radio v-for="(template, index) in templates" :key="template.id" :label="index" border>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="title-line">
|
||||||
|
<h3 class="name">{{ template.name }}</h3>
|
||||||
|
<span class="version">{{ template.version }}</span>
|
||||||
|
<span class="author push-inline-end">{{ $t('home.author') }}{{ template.author }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="template.description !== ''" class="description">{{ template.description }}</div>
|
||||||
|
<div>URL: {{ template.url }}</div>
|
||||||
|
</div>
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as mainApi from '@/api/main'
|
||||||
|
|
||||||
|
const INDEX_DEFAULT = 'default'
|
||||||
|
const INDEX_CUSTOM = 'custom'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TemplateSelect',
|
||||||
|
props: {
|
||||||
|
value: String
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
INDEX_DEFAULT,
|
||||||
|
INDEX_CUSTOM,
|
||||||
|
|
||||||
|
templates: [],
|
||||||
|
customUrl: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selectedIndex() {
|
||||||
|
if (this.value == '') {
|
||||||
|
return INDEX_DEFAULT
|
||||||
|
}
|
||||||
|
let index = this.templates.findIndex(template => template.url === this.value)
|
||||||
|
if (index !== -1) {
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
return INDEX_CUSTOM
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value: {
|
||||||
|
immediate: true,
|
||||||
|
handler(val) {
|
||||||
|
if (this.selectedIndex === INDEX_CUSTOM) {
|
||||||
|
this.customUrl = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
customUrl(val) {
|
||||||
|
if (this.selectedIndex === INDEX_CUSTOM) {
|
||||||
|
this.$emit('input', val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.updateTemplates()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async updateTemplates() {
|
||||||
|
try {
|
||||||
|
this.templates = (await mainApi.getTemplates()).templates
|
||||||
|
} catch (e) {
|
||||||
|
this.$message.error(`Failed to fetch templates: ${e}`)
|
||||||
|
throw e
|
||||||
|
} finally {
|
||||||
|
if (this.selectedIndex !== INDEX_CUSTOM) {
|
||||||
|
this.customUrl = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSelect(selectedIndex) {
|
||||||
|
let url
|
||||||
|
if (selectedIndex === INDEX_DEFAULT) {
|
||||||
|
url = ''
|
||||||
|
} else if (selectedIndex === INDEX_CUSTOM) {
|
||||||
|
url = this.customUrl
|
||||||
|
} else {
|
||||||
|
url = this.templates[selectedIndex].url
|
||||||
|
}
|
||||||
|
this.$emit('input', url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.el-radio-group {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-radio {
|
||||||
|
display: block;
|
||||||
|
height: unset;
|
||||||
|
margin: 0 0 16px 0 !important;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-radio >>> .el-radio__input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-radio >>> .el-radio__label {
|
||||||
|
padding-left: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.push-inline-end {
|
||||||
|
margin-inline-start: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
max-height: 200px;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-line {
|
||||||
|
margin-block-end: 1em;
|
||||||
|
flex: none;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
align-items: baseline;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
margin-block: 0 0;
|
||||||
|
margin-inline-end: 8px;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version {
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author {
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
margin-block-end: 1em;
|
||||||
|
flex: auto;
|
||||||
|
overflow-y: auto;
|
||||||
|
text-wrap: wrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -217,6 +217,10 @@
|
|||||||
<el-button type="primary" icon="el-icon-plus" @click="addEmoticon">{{$t('home.addEmoticon')}}</el-button>
|
<el-button type="primary" icon="el-icon-plus" @click="addEmoticon">{{$t('home.addEmoticon')}}</el-button>
|
||||||
</p>
|
</p>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane :label="$t('home.template')" lazy>
|
||||||
|
<template-select v-model="form.templateUrl"></template-select>
|
||||||
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</el-form>
|
</el-form>
|
||||||
</p>
|
</p>
|
||||||
@@ -249,12 +253,14 @@
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import download from 'downloadjs'
|
import download from 'downloadjs'
|
||||||
|
|
||||||
|
import TemplateSelect from './TemplateSelect'
|
||||||
import { mergeConfig } from '@/utils'
|
import { mergeConfig } from '@/utils'
|
||||||
import * as mainApi from '@/api/main'
|
import * as mainApi from '@/api/main'
|
||||||
import * as chatConfig from '@/api/chatConfig'
|
import * as chatConfig from '@/api/chatConfig'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Home',
|
name: 'Home',
|
||||||
|
components: { TemplateSelect },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
serverConfig: {
|
serverConfig: {
|
||||||
Reference in New Issue
Block a user