| <script setup lang="ts"> |
|
|
| import router from "@/router.ts"; |
| import { useSettingsStore } from "@/stores/config.ts"; |
| import { onMounted, ref, reactive, computed, h } from "vue"; |
| import { Modal } from 'ant-design-vue'; |
| import { SettingTwoTone } from "@ant-design/icons-vue"; |
| import axios from "axios"; |
|
|
| const base_url = axios.defaults.baseURL |
|
|
| const settingsStore = useSettingsStore() |
|
|
| import setting from "@/assets/setting.png" |
|
|
|
|
| onMounted(async () => { |
| await fetchASRLanguages(); |
| await fetchTTSRoles(); |
| }); |
|
|
| const chatAction = async () => { |
| const state = await startAudioChat(); |
| if (!state) { |
| console.error('Failed to start audio chat system service'); |
|
|
| Modal.error({ |
| title: 'Error', |
| content: 'Failed to start audio chat system service', |
| }); |
| return; |
| } |
| router.replace('/home') |
| } |
| const chatLoading = ref<boolean>(false); |
|
|
| const startAudioChat = async () => { |
| try { |
| chatLoading.value = true; |
| const response = await fetch(`${base_url}/system/start`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| }, |
| body: JSON.stringify({ |
| enable_echo_cancellation: echoCancel.value |
| }) |
| }); |
| if (!response.ok) { |
| throw new Error(`HTTP error! status: ${response.status}`); |
| } |
| const data = await response.json(); |
| console.log('ASR Instance started successfully:', data); |
| return true; |
| } catch (error) { |
| console.error('Error starting ASR instance:', error); |
| return false; |
| } finally { |
| chatLoading.value = false; |
| } |
| } |
|
|
|
|
| const modelOpen = ref<boolean>(false); |
| const modalLoading = ref<boolean>(false); |
|
|
| const handleCancel = () => { |
| modelOpen.value = false; |
| }; |
|
|
| const handleSubmit = async () => { |
| console.log('Selected Language:', language.value); |
| console.log('Selected Role:', role.value); |
| console.log('Echo Cancel:', echoCancel.value); |
| settingsStore.$state.language = language.value; |
| settingsStore.$state.role = role.value || ''; |
| settingsStore.$state.echoCancel = echoCancel.value; |
|
|
| await pushConfig(settingsStore.$state.role); |
| }; |
|
|
| const pushConfig = async (model_id: string) => { |
| try { |
| modalLoading.value = true; |
| const response = await fetch(`${base_url}/tts/models/load`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| }, |
| body: JSON.stringify({ |
| "model_id": model_id, |
| }) |
| }); |
| if (!response.ok) { |
| throw new Error(`HTTP error! status: ${response.status}`); |
| } |
| const data = await response.json(); |
| console.log('Config pushed successfully:', data); |
|
|
| const response2 = await fetch(`${base_url}/asr/instance/create`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| }, |
| body: JSON.stringify({ |
| "language": language.value, |
| }) |
| }); |
| if (!response2.ok) { |
| throw new Error(`HTTP error! status: ${response2.status}`); |
| } |
| const data2 = await response2.json(); |
| console.log('ASR Language set successfully:', data2); |
|
|
| } catch (err) { |
| console.error('Error pushing config:', err); |
| Modal.error({ |
| title: 'Error', |
| content: "Error config: " + JSON.stringify(err), |
| }); |
| } finally { |
| modalLoading.value = false; |
| modelOpen.value = false; |
| } |
|
|
| console.log('Selected Language:', language.value); |
| console.log('Selected Role:', role.value); |
| } |
|
|
|
|
| const language = ref<string>(settingsStore.$state.language || 'zh'); |
| const languages = reactive([]); |
| const languageOptions = { |
| 'zh': 'Chinese', |
| 'en': 'English', |
| 'auto': 'Auto', |
| }; |
| const role = ref<string>(settingsStore.$state.role || ''); |
| const roles = reactive([]) |
| const echoCancel = ref<boolean>(settingsStore.$state.echoCancel ?? true); |
|
|
| const radioStyle = reactive({ |
| display: 'flex', |
| height: '36px', |
| lineHeight: '36px', |
| fontSize: '15px', |
| }); |
|
|
| const filteredRoles = computed(() => { |
| const is_chinese = language.value == 'zh'; |
| return roles.filter(ro => ro['is_chinese_voice'] == is_chinese); |
| }); |
|
|
|
|
| const fetchTTSRoles = async () => { |
| try { |
| const response = await fetch(`${base_url}/tts/models`); |
| const data = await response.json() |
| if (data && data.models) { |
| |
| roles.splice(0, data.length, ...data.models) |
| console.log('Fetched TTS Roles:', roles); |
|
|
| if (data.current_model_id) { |
| role.value = data.current_model_id; |
| } |
| } |
| } catch (error) { |
| console.error('Error fetching TTS roles:', error); |
| } |
| }; |
|
|
| const fetchASRLanguages = async () => { |
| try { |
| const response = await fetch(`${base_url}/asr/languages`); |
| const data = await response.json(); |
| if (data && data.languages) { |
| |
| languages.splice(0, languages.length, ...data.languages); |
| console.log('Fetched ASR Languages:', data.languages); |
|
|
| if (data.current_asr_language) { |
| language.value = data.current_asr_language; |
| } |
| } |
| } catch (error) { |
| console.error('Error fetching ASR languages:', error); |
| } |
| }; |
|
|
| const toggleSider = () => { |
| settingsStore.$patch({ sider_open: !settingsStore.$state.sider_open }); |
| console.log('sider open: ', modelOpen.value); |
| }; |
|
|
| </script> |
|
|
| <template> |
| <div class="welcome-wrapper"> |
| <div class="content"> |
| <div class="inner-content"> |
| <div class="text-box"> |
| <div class="title"> |
| 欢迎使用 |
| </div> |
| <div class="sub-title"> |
| 点击下方按钮开始对话 |
| </div> |
| </div> |
| <div class="btn-box"> |
| <a-button @click="chatAction" block :loading="chatLoading" type="primary" size="large"> |
| <span>开始对话</span> |
| </a-button> |
| </div> |
| </div> |
| </div> |
| |
| <div class="actions"> |
| |
| |
| <a-button type="text" @click="modelOpen = true" |
| style="width:44px; height: 44px; margin-right:24px;margin-bottom: 24px;"> |
| <template #icon> |
| <img :src="setting" width="28" height="28" alt="settings" /> |
| </template> |
| </a-button> |
| </div> |
| |
| <a-modal v-model:open="modelOpen" :title="null" :mask-closable="false" centered> |
| <template #footer> |
| <a-button key="back" @click="handleCancel">Cancel</a-button> |
| <a-button key="submit" type="primary" :loading="modalLoading" @click="handleSubmit">Submit</a-button> |
| </template> |
| <div class="languages"> |
| <div class="echo-cancel-item"> |
| <div style="display: flex; justify-content: space-between; align-items: center;"> |
| <p style="margin: 0;">Enable Echo Cancellation:</p> |
| <a-switch v-model:checked="echoCancel" /> |
| </div> |
| </div> |
| </div> |
| <div class="languages"> |
| <div class="language-item"> |
| <p>Select Language:</p> |
| <a-select v-model:value="language" style="width: 100%;"> |
| <a-select-option v-for="lan in languages" :value="lan" :key="lan"> |
| {{ languageOptions[lan] }} |
| </a-select-option> |
| </a-select> |
| </div> |
| </div> |
| <div class="languages"> |
| <div class="role-item"> |
| <p>Select voice Role:</p> |
| <a-radio-group size="large" v-model:value="role"> |
| <a-radio v-for="r in filteredRoles" :style="radioStyle" :value="r['id']" :key="r['id']"> |
| {{ r['character_name'] }} |
| </a-radio> |
| </a-radio-group> |
| </div> |
| </div> |
| </a-modal> |
| </div> |
| </template> |
|
|
| <style lang="scss" scoped> |
| |
| .languages { |
| margin-top: 40px; |
| margin-bottom: 8px; |
| |
| p { |
| font-size: 16px; |
| font-weight: 500; |
| margin-bottom: 8px; |
| } |
| } |
| |
| .welcome-wrapper { |
| width: 100%; |
| height: 100%; |
| background-image: url('@/assets/bg.png'); |
| background-repeat: no-repeat; |
| background-attachment: fixed; |
| background-size: cover; |
| background-position: center; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| justify-content: space-between; |
| color: #fff; |
| |
| .content { |
| width: 100%; |
| height: 80vh; |
| display: flex; |
| flex-direction: column; |
| justify-content: space-around; |
| margin-top: 64px; |
| |
| .inner-content { |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| justify-content: center; |
| text-align: center; |
| padding: 20px; |
| |
| .text-box { |
| color: #000; |
| margin-bottom: 36px; |
| |
| .title { |
| font-size: 24px; |
| font-weight: 600; |
| margin-bottom: 24px; |
| } |
| |
| .sub-title { |
| font-size: 15px; |
| margin-top: 10px; |
| } |
| } |
| .btn-box { |
| width: 224px; |
| height: 80px; |
| } |
| } |
| } |
| |
| .actions { |
| width: 100%;; |
| height: 64px; |
| |
| display: flex; |
| justify-content: flex-end; |
| } |
| } |
| </style> |
|
|