<template> <q-dialog persistent v-model="show_dialog" @before-hide="resetData" @keydown=" (evt) => { if (!loading && evt.keyCode == 27) { show_dialog = false; } } " > <q-card class="overflow-hidden" style="overflow-y: scroll; max-width: 45vw"> <q-form ref="signal_source_form" @submit="onSubmit"> <q-card-section class="q-ma-none q-pa-sm"> <div class="row"> <div class="col-auto text-h6"> {{ type == 1 ? $t("add signal source") : type == 2 ? $t("edit signal source") : $t("add signal source") }} </div> <q-space /> <div> <q-btn :loading="loading" flat round icon="close" color="red" :disable="loading" v-close-popup > <q-tooltip> {{ $t("close") }} </q-tooltip> </q-btn> </div> </div> </q-card-section> <q-separator /> <q-card-section style="max-height: 50vh; width: 45vw" class="scroll"> <q-list> <q-item v-if="/*type != 2*/ true"> <q-item-label>{{ $t("parent group") }}:</q-item-label> </q-item> <q-item v-if="/*type != 2*/ true" class="q-pa-none q-ma-none"> <q-item-section style="padding-right: 10px"> <q-tree ref="tree" class="scroll" :class="loading ? 'disable_tree' : ''" v-model:selected="selected" :nodes="tree_nodes" default-expand-all node-key="uuid" labelKey="name" filter="group filter" :filter-method="treeNodesFilter" > <template v-slot:default-header="prop"> <q-item class="full-width" :class=" prop.tree.selected == prop.key ? 'item-selected-bg' : '' " > <q-item-section avatar> <q-icon :name="'img:source_icon/group.png'" color="orange" size="28px" class="q-mr-sm" /> </q-item-section> <q-item-section> <div class="text-weight-bold text-primary"> {{ prop.node.name }} </div> </q-item-section> </q-item> </template> </q-tree> </q-item-section> </q-item> <q-item> <q-item-section> <q-input :loading="loading" :disable="loading" filled autofocus v-model="item_data.name" :label="$t('signal source name')" :hint="$t('please input signal source name')" lazy-rules :rules="[ (val) => (val && val.length > 0) || $t('Please type something'), ]" > <template v-if="item_data.name" v-slot:append> <q-icon name="cancel" @click.stop="item_data.name = null" class="cursor-pointer" /> </template> </q-input> </q-item-section> </q-item> <q-item> <q-item-section> <q-select :loading="loading" :disable="loading" filled option-value="value" option-label="label" emit-value map-options v-model="item_data.window_type" :options="signal_source_options" :label="$t('signal source type')" :hint="$t('please select signal source type')" @update:model-value="onSelected" > </q-select> </q-item-section> </q-item> <q-item v-if=" $store.state.isSpecialVideo() && (item_data.window_type == 'EwindowType::Multimedia' || item_data.window_type == 'EwindowType::Image') " > <q-item-section> <q-select :loading="loading" :disable="loading" filled option-value="value" option-label="label" emit-value map-options v-model="item_data.rotation" :options="[0, 90, 180, 270]" :label="$t('multimedia rotation')" :hint="$t('please select multimedia rotation')" @update:model-value="onSelected" > </q-select> </q-item-section> </q-item> <q-item> <q-item-section> <q-input :loading="loading" :disable="loading" filled @dblclick=" media_url_label.startsWith($t('file path')) && item_data.window_type == 'EwindowType::Image' ? doSelectImageFile() : item_data.window_type == 'EwindowType::Clock' ? showClockDialog() : item_data.window_type == 'EwindowType::Weather' ? showWeatherDialog() : item_data.window_type == 'EwindowType::Timer' ? showTimerDialog() : showVideoPlayListDialog() " v-model="item_data.media_url" :readonly=" media_url_label.startsWith($t('file path')) || media_url_label.startsWith($t('clock')) || media_url_label.startsWith($t('weather')) || media_url_label.startsWith($t('timer')) " :label="media_url_label" :hint=" media_url_label.startsWith($t('file path')) ? $t('dbclick select file') : media_url_label.startsWith($t('clock')) ? $t('dbclick config ') + $t('clock') : media_url_label.startsWith($t('weather')) ? $t('dbclick config ') + $t('weather') : media_url_label.startsWith($t('timer')) ? $t('dbclick config ') + $t('timer') : $t('please input ') + media_url_label.substr(0, media_url_label.length - 1) " lazy-rules :rules="[ (val) => (val && val.length > 0) || $t('Please type something') + ',' + (media_url_label.startsWith($t('file path')) ? $t('dbclick select file') : media_url_label.startsWith($t('clock')) ? $t('dbclick config ') + $t('clock') : media_url_label.startsWith($t('weather')) ? $t('dbclick config ') + $t('weather') : media_url_label.startsWith($t('timer')) ? $t('dbclick config ') + $t('timer') : $t('please input ') + media_url_label.substr( 0, media_url_label.length - 1 )), ]" > <template v-if="item_data.media_url" v-slot:append> <q-icon name="cancel" @click.stop="item_data.media_url = null" class="cursor-pointer" /> </template> </q-input> </q-item-section> </q-item> <q-item v-if="item_data && item_data.window_type == 'EwindowType::Rtsp'" > <q-item-section> <q-input :loading="loading" :disable="loading" filled v-model="item_data.user_name" :label="$t('user name')" :hint="$t('please input user name')" lazy-rules > <template v-if="item_data.user_name" v-slot:append> <q-icon name="cancel" @click.stop="item_data.user_name = null" class="cursor-pointer" /> </template> </q-input> </q-item-section> </q-item> <q-item v-if="item_data && item_data.window_type == 'EwindowType::Rtsp'" > <q-item-section> <q-input :loading="loading" :disable="loading" filled v-model="item_data.password" :label="$t('password')" :hint="$t('please input password')" lazy-rules > <template v-if="item_data.password" v-slot:append> <q-icon name="cancel" @click.stop="item_data.password = null" class="cursor-pointer" /> </template> </q-input> </q-item-section> </q-item> </q-list> </q-card-section> <q-separator /> <q-card-actions align="right"> <q-btn :loading="loading" flat :label="$t('Cancel')" no-caps color="primary" v-close-popup /> <q-btn ref="accept" flat :label="$t('Accept')" :loading="loading" no-caps color="primary" type="submit" /> </q-card-actions> </q-form> </q-card> <playlist-dialog ref="playlist_dialog" /> <file-manage-dialog ref="file_manage_dialog" /> <clock-signal-source-dialog ref="clock_dialog" /> <weather-signal-source-dialog ref="weather_dialog" /> <timer-signal-source-dialog ref="timer_dialog" /> </q-dialog> </template> <style scoped> .disable_tree { background: #9e9e9e; cursor: wait; pointer-events: none; } </style> <script lang="ts"> import { defineComponent, ref, watch, reactive, onUnmounted, computed, nextTick, Ref, } from "vue"; import { useStore } from "src/store"; import GlobalData from "src/common/GlobalData"; import { useQuasar } from "quasar"; import { useI18n } from "vue-i18n"; import { SignalSourceEntity } from "src/entities/SignalSourceEntity"; import FileManageDialog from "src/components/FileManageDialog.vue"; import PlaylistDialog from "src/components/PlaylistDialog.vue"; import ClockSignalSourceDialog from "src/components/ClockSignalSourceDialog.vue"; import WeatherSignalSourceDialog from "src/components/WeatherSignalSourceDialog.vue"; import TimerSignalSourceDialog from "src/components/TimerSignalSourceDialog.vue"; import FileEntity from "src/entities/FileEntity"; import FileSuffixHelper from "src/common/FileSuffixHelper"; import { Protocol } from "src/entities/WSProtocol"; import { EDeviceAttribute } from "src/entities/EDeviceAttribute"; export default defineComponent({ name: "ComponentSignalSourceDialog", components: { FileManageDialog, PlaylistDialog, ClockSignalSourceDialog, WeatherSignalSourceDialog, TimerSignalSourceDialog, }, setup() { let $store = useStore(); let $q = useQuasar(); let $t = useI18n(); let show_dialog = ref(false); let type = ref(1); let media_url_label = ref("URL"); let item_data: SignalSourceEntity = reactive(new SignalSourceEntity()); item_data.window_type = "EwindowType::Multimedia"; const selected: any = ref(null); let loading = ref(false); let playlist_dialog: any = ref(null); let clock_dialog: any = ref(null); let weather_dialog: any = ref(null); let timer_dialog: any = ref(null); let file_manage_dialog: any = ref(null); const signal_source_form: any = ref(null); let suppored_window_types = new Set<string>([ "EwindowType::Multimedia", "EwindowType::Web", "EwindowType::Image", "EwindowType::Rtsp", "EwindowType::Clock", "EwindowType::Weather", "EwindowType::Timer", ]); const signal_source_options: Ref<any> = ref([]); const build_signal_source_options = [ { label: $t.t("multimedia file"), value: "EwindowType::Multimedia", }, { label: $t.t("image"), value: "EwindowType::Image", }, { label: $t.t("rtsp"), value: "EwindowType::Rtsp", }, { label: $t.t("Web"), value: "EwindowType::Web", }, { label: $t.t("clock"), value: "EwindowType::Clock", }, { label: $t.t("timer"), value: "EwindowType::Timer", }, ]; const refresh_signal_source_options = () => { signal_source_options.value = []; if ($store.state.isLedPlayer()) { for (const item of build_signal_source_options) { signal_source_options.value.push(item); } if (($store.state.device_attribute & EDeviceAttribute.CustomISV) == 0) { signal_source_options.value.push({ label: $t.t("weather"), value: "EwindowType::Weather", }); } else { const index = signal_source_options.value.findIndex( (element: any) => element && element.value == "EwindowType::Weather" ); if (index != -1) { signal_source_options.value.splice(index, 1); } } } else if ($store.state.isSpecialVideo()) { signal_source_options.value = []; signal_source_options.value = build_signal_source_options.slice(0, 2); } }; refresh_signal_source_options(); watch( () => $store.state.device_attribute, (value) => { refresh_signal_source_options(); } ); const tree_nodes = computed({ get: () => $store.state.signal_source_tree, set: (val) => {}, }); watch( () => selected.value, (newValue, oldValue) => { if (newValue == null) { selected.value = ""; } } ); watch( () => item_data.window_type, (newValue, oldValue) => { if (!suppored_window_types.has(newValue)) { item_data.window_type = "EwindowType::Multimedia"; } } ); const setMediaUrlLabel = (value: string) => { switch (value) { case "EwindowType::Web": media_url_label.value = $t.t("http url") + ":"; break; case "EwindowType::Rtsp": media_url_label.value = $t.t("RTSP url") + ":"; break; case "EwindowType::Clock": media_url_label.value = $t.t("clock setting") + ":"; break; case "EwindowType::Weather": media_url_label.value = $t.t("weather setting") + ":"; break; case "EwindowType::Timer": media_url_label.value = $t.t("timer setting") + ":"; break; default: media_url_label.value = $t.t("file path") + ":"; break; } }; const requestAddSignalSource = async () => { item_data.group_uuid = selected.value; let response = await GlobalData.getInstance() .getCurrentClient() ?.addSignalSource(item_data); if (response) { $q.notify({ color: response.success ? "positive" : "negative", icon: response.success ? "done" : "warning", message: $t.t("add signal source") + (response.success ? $t.t("success") : $t.t("fail")) + "!", position: "top", timeout: 1500, }); } }; const requestEditSignalSource = async () => { item_data.group_uuid = selected.value; let response = await GlobalData.getInstance() .getCurrentClient() ?.editSignalSource(item_data); if (response) { $q.notify({ color: response.success ? "positive" : "negative", icon: response.success ? "done" : "warning", message: $t.t("edit signal source") + (response.success ? $t.t("success") : $t.t("fail")) + "!", position: "top", timeout: 1500, }); } }; const doSelectFile = async ( no_filter = true, video_filter = false, image_filter = false ) => { if (!media_url_label.value.startsWith($t.t("file path"))) { return; } const obj = await file_manage_dialog.value.showDialogAsync( "select", no_filter, video_filter, image_filter ); if (obj) { interface __I { path: string; file: FileEntity; } let { path, file }: __I = obj; if (path && file) { item_data.media_url = path + "/" + file.name; if ( !item_data.name || item_data.name.trim() == "" || item_data.name.trim() == $t.t("new signal source") ) { nextTick(() => { item_data.name = file.name; }); } } } }; const showPlaylistDialog = async ( no_filter = true, video_filter = false ) => { if (item_data.window_type != "EwindowType::Multimedia") { return; } const result = await playlist_dialog.value.showDialogAsync( item_data.media_url, no_filter, video_filter, false ); if (Array.isArray(result)) { if (result.length) { if ( !item_data.name || item_data.name.trim() == "" || item_data.name.trim() == $t.t("new signal source") ) { try { const temp_item = decodeURI(result[0].toString()); let index = temp_item.lastIndexOf("\\"); if (index == -1) { index = temp_item.lastIndexOf("/"); } if (index + 1 == temp_item.length) { item_data.name = temp_item.substr(index); } else if (index + 1 < temp_item.length) { item_data.name = temp_item.substr(index + 1); } } catch (e) { console.log(e); } } item_data.media_url = decodeURI(JSON.stringify(result)); } } }; return { show_dialog, type, signal_source_options, media_url_label, item_data, selected, loading, tree_nodes, playlist_dialog, clock_dialog, weather_dialog, timer_dialog, file_manage_dialog, signal_source_form, showDialog(options: any) { if (options) { type.value = options.type ?? 1; if (options.data && options.data.item_data) { SignalSourceEntity.copy( item_data, JSON.parse(JSON.stringify(options.data.item_data)) ); } if (type.value == 2) { selected.value = item_data.group_uuid; } else { selected.value = options.parent_node ?? ""; item_data.name = $t.t("new signal source"); } } if (item_data) { setMediaUrlLabel(item_data.window_type); } show_dialog.value = true; }, resetData() { loading.value = false; selected.value = null; SignalSourceEntity.copy(item_data); type.value = 1; }, treeNodesFilter(node: any, filter: any) { return node.is_group && !node.item_data?.system_default; }, onSelected(value: any) { if (signal_source_form.value) { signal_source_form.value.resetValidation(); } setMediaUrlLabel(value as string); }, async onSubmit() { loading.value = true; try { await (type.value == 2 ? requestEditSignalSource() : requestAddSignalSource()); show_dialog.value = false; } catch {} loading.value = false; }, async showClockDialog() { if (item_data.window_type != "EwindowType::Clock") { return; } const result = await clock_dialog.value.showDialogAsync( item_data.media_url ); if (result) { item_data.media_url = decodeURI(result); } }, async showWeatherDialog() { if (item_data.window_type != "EwindowType::Weather") { return; } const result = await weather_dialog.value.showDialogAsync( item_data.media_url ); if (result) { item_data.media_url = decodeURI(result); } }, async showTimerDialog() { if (item_data.window_type != "EwindowType::Timer") { return; } const result = await timer_dialog.value.showDialogAsync( item_data.media_url ); if (result) { item_data.media_url = decodeURI(result); } }, async showVideoPlayListDialog() { return showPlaylistDialog(false, true); }, async showPlaylistDialog(no_filter = true, video_filter = false) { return showPlaylistDialog(no_filter, video_filter); }, async doSelectImageFile() { return doSelectFile(false, false, true); }, async doSelectFile( no_filter = true, video_filter = false, image_filter = false ) { return doSelectFile(no_filter, video_filter, image_filter); }, }; }, }); </script>