优霸定制VideoWall界面

This commit is contained in:
fangxiang 2022-11-07 19:35:28 +08:00
parent d32de186d1
commit 64a86cc23e
28 changed files with 2055 additions and 221 deletions

View File

@ -13,6 +13,8 @@ import { ExternalControlTableEntity } from "src/entities/ExternalControlTableEnt
import { SerialPortConfigEntity } from "src/entities/SerialPortConfigEntity";
import TimingTaskEntity from "src/entities/TimingTaskEntity";
import JointActionEquipmentTableEntity from "src/entities/JointActionEquipmentTableEntity";
import { CustomProtocol } from "src/entities/WSProtocolCustom";
import ClientConnectionCustom from "./ClientConnectionCustom";
class _RpcInfo {
send_timestamp: number;
@ -50,6 +52,8 @@ export default class ClientConnection {
_rpc_id_counter = 0;
rpc_map = new Map<number, _RpcInfo>();
custom_connection: ClientConnectionCustom = new ClientConnectionCustom(this);
public login_callback:
| ((this: ClientConnection, logined: boolean) => void)
| null = null;
@ -83,6 +87,10 @@ export default class ClientConnection {
return this._is_login;
}
get customConnection() {
return this.custom_connection ?? new ClientConnectionCustom(this);
}
checkRpcTimeout() {
const current_datetime = new Date().getTime();
this.rpc_map.forEach((v, k, m) => {
@ -142,7 +150,10 @@ export default class ClientConnection {
this.rpc_map.get(packet.rpc_id)?.reject();
this.rpc_map.delete(packet.rpc_id);
}
if (Protocol.Commands.AllCommands.has(packet.command)) {
if (
Protocol.Commands.AllCommands.has(packet.command) ||
CustomProtocol.Commands.AllCommands.has(packet.command)
) {
if (
packet.flag == Protocol.PacketEntity.FLAG_RESPONSE ||
packet.flag == Protocol.PacketEntity.FLAG_NOTIFY

View File

@ -0,0 +1,53 @@
import OutputBoardItemConfigEntity from "src/entities/OutputBoardItemConfigEntity";
import { CustomProtocol } from "src/entities/WSProtocolCustom";
import ClientConnection from "./ClientConnection";
export default class ClientConnectionCustom {
connection_: ClientConnection | null = null;
constructor(connection: ClientConnection) {
this.connection_ = connection;
}
get connection() {
return this.connection_;
}
public async getVideoWallConfig() {
if (this.connection) {
try {
return await this.connection.doRpc<CustomProtocol.ISVGetVideoWallConfigResponseEntity>(
new CustomProtocol.ISVGetVideoWallConfigRequestEntity()
);
} catch (e) {
console.error(e);
return;
}
}
throw "connection is nullptr!";
}
public async setVideoWallConfig(
outputs: OutputBoardItemConfigEntity[],
wall_rows: number,
wall_cols: number,
device_rotation: number
) {
if (this.connection) {
try {
return await this.connection.doRpc<CustomProtocol.ISVSetVideoWallConfigResponseEntity>(
new CustomProtocol.ISVSetVideoWallConfigRequestEntity(
outputs,
wall_rows,
wall_cols,
device_rotation
)
);
} catch (e) {
console.error(e);
return;
}
}
throw "connection is nullptr!";
}
}

View File

@ -36,5 +36,6 @@ export namespace EventNamesDefine {
export const RefreshJointActionEquipmentList =
"onRefreshJointActionEquipmentList";
export const CheckDebug = "CheckDebug";
export const CheckDebug2 = "CheckDebug2";
export const NotifyDownloadAndroid = "NotifyDownloadAndroid";
}

View File

@ -4,6 +4,7 @@ import { Common } from "./Common";
import EventBus, { EventNamesDefine } from "./EventBus";
import OptionsType from "./OptionsType";
import { Cookies } from "quasar";
import { EDeviceAttribute } from "src/entities/EDeviceAttribute";
export default class Initializer {
private options: OptionsType;
@ -194,7 +195,7 @@ export default class Initializer {
.then((response) => {
if (response && typeof response.attribute != "undefined") {
$store.commit("setDeviceAttribute", response.attribute);
if (response.attribute & Protocol.EDeviceAttribute.CustomISV) {
if (response.attribute & EDeviceAttribute.CustomISV) {
let language = Cookies.get("language");
if (!language) {
language = "zh-CN";

View File

@ -175,6 +175,7 @@ import { useQuasar, date as $date } from "quasar";
import { useI18n } from "vue-i18n";
import GlobalData from "src/common/GlobalData";
import { Protocol } from "src/entities/WSProtocol";
import { EDeviceAttribute } from "src/entities/EDeviceAttribute";
export default defineComponent({
name: "ComponentAdvancedDebugDialog",
@ -187,10 +188,16 @@ export default defineComponent({
let show_dialog = ref(false);
let loading = ref(false);
const function_center_control = ref(false);
const function_output_board = ref(false);
const function_mirroring_output = ref(false);
const function_custom_ISV = ref(false);
const function_custom_ISV = ref($store.state.custom_defines.is_custom_isv);
const function_center_control = ref(
$store.state.custom_defines.function_center_control
);
const function_output_board = ref(
$store.state.custom_defines.function_output_board
);
const function_mirroring_output = ref(
$store.state.custom_defines.function_mirroring_output
);
const target_language = ref("zh-CN");
return {
@ -205,33 +212,13 @@ export default defineComponent({
showDialog() {
show_dialog.value = true;
setTimeout(async () => {
try {
const response = await GlobalData.getInstance()
.getCurrentClient()
?.getDeviceAttribute();
if (response) {
nextTick(() => {
function_center_control.value =
(response.attribute &
Protocol.EDeviceAttribute.CenterControl) !=
0;
function_output_board.value =
(response.attribute &
Protocol.EDeviceAttribute.OutputBoard) !=
0;
function_mirroring_output.value =
($store.state.device_attribute &
Protocol.EDeviceAttribute.MirroringOutput) !=
0;
function_custom_ISV.value =
(response.attribute & Protocol.EDeviceAttribute.CustomISV) !=
0;
});
}
} catch {}
}, 0);
function_custom_ISV.value = $store.state.custom_defines.is_custom_isv;
function_center_control.value =
$store.state.custom_defines.function_center_control;
function_output_board.value =
$store.state.custom_defines.function_output_board;
function_mirroring_output.value =
$store.state.custom_defines.function_mirroring_output;
},
resetData() {
loading.value = false;
@ -318,19 +305,19 @@ export default defineComponent({
});
},
setDeviceAttribute() {
let attribute = Protocol.EDeviceAttribute.None;
let attribute = EDeviceAttribute.None;
if (function_center_control.value) {
attribute |= Protocol.EDeviceAttribute.CenterControl;
attribute |= EDeviceAttribute.CenterControl;
}
if (function_output_board.value) {
attribute |= Protocol.EDeviceAttribute.OutputBoard;
attribute |= EDeviceAttribute.OutputBoard;
}
if (function_mirroring_output.value) {
attribute |= Protocol.EDeviceAttribute.MirroringOutput;
attribute |= EDeviceAttribute.MirroringOutput;
}
if (function_custom_ISV.value) {
attribute |= Protocol.EDeviceAttribute.CustomISV;
attribute |= EDeviceAttribute.CustomISV;
}
GlobalData.getInstance()

View File

@ -550,7 +550,6 @@ export default defineComponent({
let success = false;
let request_current_type = current_type.value;
console.log(request_current_type);
if (
request_current_type != "SERIAL_PORT" &&
request_current_type != "UDP" &&

View File

@ -108,6 +108,7 @@
:loading="loading"
flat
:label="$t('Close')"
:disable="loading"
no-caps
color="primary"
v-close-popup

View File

@ -90,6 +90,7 @@
:loading="loading"
flat
:label="$t('Close')"
:disable="loading"
no-caps
color="primary"
v-close-popup

View File

@ -297,6 +297,7 @@
<q-btn
v-close-popup
:label="$t('close')"
:disable="loading"
color="primary"
flat
/>

View File

@ -388,6 +388,7 @@ import { useI18n } from "vue-i18n";
import GlobalData from "src/common/GlobalData";
import QrcodeVue from "qrcode.vue";
import { Protocol } from "src/entities/WSProtocol";
import { EDeviceAttribute } from "src/entities/EDeviceAttribute";
export default defineComponent({
name: "ComponentRegisterDialog",
@ -464,22 +465,18 @@ export default defineComponent({
show_dialog.value = false;
}
function_center_control.value =
($store.state.device_attribute &
Protocol.EDeviceAttribute.CenterControl) !=
0;
function_output_board.value =
($store.state.device_attribute &
Protocol.EDeviceAttribute.OutputBoard) !=
0;
function_mirroring_output.value =
($store.state.device_attribute &
Protocol.EDeviceAttribute.MirroringOutput) !=
0;
function_custom_ISV.value =
($store.state.device_attribute & Protocol.EDeviceAttribute.CustomISV) !=
0;
const function_custom_ISV = ref(
$store.state.custom_defines.is_custom_isv
);
const function_center_control = ref(
$store.state.custom_defines.function_center_control
);
const function_output_board = ref(
$store.state.custom_defines.function_output_board
);
const function_mirroring_output = ref(
$store.state.custom_defines.function_mirroring_output
);
};
return {
@ -615,19 +612,19 @@ export default defineComponent({
}
if (success) {
let attribute = Protocol.EDeviceAttribute.None;
let attribute = EDeviceAttribute.None;
if (function_center_control.value) {
attribute |= Protocol.EDeviceAttribute.CenterControl;
attribute |= EDeviceAttribute.CenterControl;
}
if (function_output_board.value) {
attribute |= Protocol.EDeviceAttribute.OutputBoard;
attribute |= EDeviceAttribute.OutputBoard;
}
if (function_mirroring_output.value) {
attribute |= Protocol.EDeviceAttribute.MirroringOutput;
attribute |= EDeviceAttribute.MirroringOutput;
}
if (function_custom_ISV.value) {
attribute |= Protocol.EDeviceAttribute.CustomISV;
attribute |= EDeviceAttribute.CustomISV;
}
GlobalData.getInstance()

View File

@ -341,6 +341,7 @@ 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",
@ -417,11 +418,7 @@ export default defineComponent({
for (const item of build_signal_source_options) {
signal_source_options.value.push(item);
}
if (
($store.state.device_attribute &
Protocol.EDeviceAttribute.CustomISV) ==
0
) {
if (($store.state.device_attribute & EDeviceAttribute.CustomISV) == 0) {
signal_source_options.value.push({
label: $t.t("weather"),
value: "EwindowType::Weather",

View File

@ -57,12 +57,16 @@
:disable="loading"
/>
<q-tab
v-if="$store.state.isLedPlayer() || $store.state.advanced_debug"
v-if="
$store.state.advanced_debug ||
(!$store.state.custom_defines.is_custom_isv &&
$store.state.isLedPlayer())
"
name="graphics"
no-caps
icon="img:new_icon/graphics_setting.png"
:label="
function_output_board_attribute
$store.state.custom_defines.function_output_board
? $t('graphics setting')
: $t('output setting')
"
@ -84,7 +88,9 @@
/>
<q-tab
v-if="
function_output_board_attribute || $store.state.advanced_debug
$store.state.advanced_debug ||
(!$store.state.custom_defines.is_custom_isv &&
$store.state.custom_defines.function_output_board)
"
name="output_board"
no-caps
@ -364,7 +370,11 @@
</q-btn>
</q-item-section>
</q-item>
<q-item v-if="function_mirroring_output">
<q-item
v-if="
$store.state.custom_defines.function_mirroring_output
"
>
<q-item-section avatar class="width_5_2_2"
>{{ $t("mirroring output") }}:</q-item-section
>
@ -377,7 +387,9 @@
/>
</q-item-section>
</q-item>
<q-item v-if="!function_output_board_attribute">
<q-item
v-if="!$store.state.custom_defines.function_output_board"
>
<q-item-section avatar class="width_5_2_2"
>{{ $t("output type") }}:</q-item-section
>
@ -421,7 +433,7 @@
</q-item>
<q-item
v-if="
!function_output_board_attribute &&
!$store.state.custom_defines.function_output_board &&
device_resolution_type.toUpperCase() == 'EDID'
"
>
@ -439,7 +451,7 @@
</q-item>
<q-item
v-if="
!function_output_board_attribute &&
!$store.state.custom_defines.function_output_board &&
device_resolution_type.toUpperCase() == 'CVT'
"
>
@ -457,7 +469,7 @@
</q-item>
<q-item
v-if="
!function_output_board_attribute &&
!$store.state.custom_defines.function_output_board &&
device_resolution_type.toUpperCase() == 'CUSTOM'
"
>
@ -791,7 +803,8 @@
name="output_board"
class="_panel"
v-if="
function_output_board_attribute || $store.state.advanced_debug
$store.state.custom_defines.function_output_board ||
$store.state.advanced_debug
"
>
<q-card class="fit">
@ -809,8 +822,8 @@
:loading="loading"
:disable="loading"
type="number"
max="11"
min="0"
max="5"
min="1"
:rules="[
(val) =>
(val != null &&
@ -818,9 +831,9 @@
val.toString().length > 0) ||
$t('Please type something'),
(val) =>
(parseInt(val) >= 0 && parseInt(val) <= 11) ||
(parseInt(val) >= 1 && parseInt(val) <= 5) ||
$t('number must between') +
'0 ~ 11' +
'1 ~ 5' +
$t('CN_BERWEEN_SUFFIX'),
]"
lazy-rules
@ -836,8 +849,8 @@
v-model="output_board_wall_col"
:loading="loading"
:disable="loading"
max="11"
min="0"
max="5"
min="1"
type="number"
:rules="[
(val) =>
@ -846,9 +859,9 @@
val.toString().length > 0) ||
$t('Please type something'),
(val) =>
(parseInt(val) >= 0 && parseInt(val) <= 11) ||
(parseInt(val) >= 1 && parseInt(val) <= 5) ||
$t('number must between') +
'0 ~ 11' +
'1 ~ 5' +
$t('CN_BERWEEN_SUFFIX'),
]"
lazy-rules
@ -1114,6 +1127,8 @@ import { EWeekDays } from "src/entities/EWeekDays";
import SystenSettingAdvancedNetworkDialog from "src/components/SystenSettingAdvancedNetworkDialog.vue";
import { AdvancedIpAddressEntity } from "src/entities/AdvancedIpAddressEntity";
import { EDeviceAttribute } from "src/entities/EDeviceAttribute";
const _time_zones = [
"UTC-12(Central Pacific)",
"UTC-11(Central Pacific)",
@ -1157,12 +1172,6 @@ export default defineComponent({
const timing_task_dialog: Ref<any> = ref(null);
const system_setting_advanced_network_dialog: Ref<any> = ref(null);
const function_mirroring_output = ref(
($store.state.device_attribute &
Protocol.EDeviceAttribute.MirroringOutput) !=
0
);
let tab = ref("network");
let auto_ip = ref($t.t("enable"));
@ -1181,30 +1190,6 @@ export default defineComponent({
const mirroring_output = ref(false);
const function_output_board_attribute = ref(
($store.state.device_attribute & Protocol.EDeviceAttribute.OutputBoard) !=
0
);
if ($store.state.isSpecialVideo()) {
function_output_board_attribute.value = false;
}
watch(
() => $store.state.device_attribute,
(value) => {
if ($store.state.isSpecialVideo()) {
function_output_board_attribute.value = false;
} else {
function_output_board_attribute.value =
(value & Protocol.EDeviceAttribute.OutputBoard) != 0;
}
function_mirroring_output.value =
($store.state.device_attribute &
Protocol.EDeviceAttribute.MirroringOutput) !=
0;
}
);
let device_resolution = ref("");
let device_resolution_options: Ref<string[]> = ref([]);
let device_rotate = ref(0);
@ -1486,7 +1471,7 @@ export default defineComponent({
request.target_resolution_timing = device_resolution_timing.value;
request.rotate = parseInt(device_rotate.value.toString());
request.hdmi_output_count = mirroring_output.value ? 2 : 1;
if (!function_mirroring_output.value) {
if (!$store.state.custom_defines.function_mirroring_output) {
request.hdmi_output_count = 1;
}
@ -1731,8 +1716,6 @@ export default defineComponent({
timing_task_dialog,
system_setting_advanced_network_dialog,
timing_tasks,
function_mirroring_output,
function_output_board_attribute,
loading,
tab,
auto_ip,

View File

@ -108,6 +108,7 @@
<q-btn
v-close-popup
:label="$t('close')"
:disable="loading"
color="primary"
flat
/>

View File

@ -205,6 +205,7 @@
flat
:label="$t('Close')"
no-caps
:disable="loading"
color="primary"
v-close-popup
/>

View File

@ -0,0 +1,438 @@
<template>
<q-dialog
persistent
v-model="show_dialog"
@before-hide="resetData"
@keydown="
(evt) => {
if (!loading && evt.keyCode == 27) {
show_dialog = false;
}
}
"
>
<q-popup-proxy context-menu></q-popup-proxy>
<q-card class="overflow-hidden" style="overflow-y: scroll; max-width: 60vw">
<q-form @submit="onSubmit">
<q-card-section class="q-ma-none q-pa-sm">
<div class="row">
<div class="col-auto text-h6">
{{ $t("video wall") }}
</div>
<q-space />
<div>
<q-btn
:loading="loading"
flat
round
icon="close"
:disable="loading"
color="red"
v-close-popup
>
<q-tooltip>
{{ $t("close") }}
</q-tooltip>
</q-btn>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-section style="max-height: 68vh; width: 55vw" class="scroll">
<q-list>
<q-item>
<q-item-section avatar class="head_1">
{{ $t("screen mode") }}
</q-item-section>
<q-item-section>
<q-select
v-model="screen_mode"
:options="[
{
label: $t('landspace mode'),
value: 0,
},
{
label: $t('portrait mode'),
value: 1,
},
]"
emit-value
map-options
:loading="loading"
:disable="loading"
>
</q-select>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar class="head_1">
{{ $t("splice mode") }}
</q-item-section>
<q-item-section>
<q-select
v-model="splice_mode"
:options="screen_mode == 0 ? landspace_modes : portrait_modes"
emit-value
map-options
:rules="[
(val) =>
val != 'Other' ||
$t('splice mode') +
$t(' ') +
$t('can\'t be') +
$t(' ') +
'Other',
]"
:loading="loading"
:disable="loading"
>
</q-select>
</q-item-section>
</q-item>
</q-list>
</q-card-section>
<q-separator />
<q-card-section style="width: 55vw">
<div
style="max-height: 50vh"
:style="{ height: screen_mode == 0 ? '20vh' : '40vh' }"
class="row items-center justify-center"
>
<div
ref="preview_grid"
style="height: 100%"
:style="{ width: screen_mode == 0 ? '90%' : '40%' }"
class="preview_grid"
>
<div
v-for="row in preview_rows"
:key="'row' + (row - 1)"
class="row"
>
<div
v-for="col in preview_cols"
:style="{ height: previce_grid_height + 'px' }"
class="preview_grid_item col row items-center justify-center"
:key="(row - 1) * preview_cols + col"
>
<span>
{{ (row - 1) * preview_cols + col }}
</span>
<span
v-if="
(preview_rotation == 1 || preview_rotation == 180) &&
row == 1
"
>
(180°)
</span>
</div>
</div>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions>
<q-space />
<q-btn
:loading="loading"
flat
no-caps
:label="$t('close')"
color="primary"
v-close-popup
:disable="loading"
/>
<q-btn
ref="accept"
flat
no-caps
:label="$t('accept')"
:loading="loading"
type="submit"
color="primary"
/>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>
<style scoped>
.head_1 {
width: 25%;
}
.head_2 {
width: 12%;
}
.preview_grid {
border: 1px solid gray;
}
.preview_grid_item {
border: 1px solid gray;
}
</style>
<script lang="ts">
import { defineComponent, ref, Ref, watch, computed, onMounted } from "vue";
import { useStore } from "src/store";
import { useQuasar, date as DateHelper } from "quasar";
import { useI18n } from "vue-i18n";
import GlobalData from "src/common/GlobalData";
import OutputBoardItemConfigEntity from "src/entities/OutputBoardItemConfigEntity";
const elementResizeDetectorMaker = require("element-resize-detector");
export default defineComponent({
name: "ComponentCustomISVVideoWallDialog",
components: {},
setup() {
let $store = useStore();
let $q = useQuasar();
let $t = useI18n();
let show_dialog = ref(false);
let loading = ref(false);
const landspace_modes = [
"2x2",
"2x2 (180°)",
"1x2",
"1x3",
"1x4",
"2x1",
"2x1 (180°)",
"3x1",
"4x1",
"Other",
];
const portrait_modes = [
"2x2",
"1x2",
"1x3",
"1x4",
"2x1",
"3x1",
"4x1",
"Other",
];
const screen_mode = ref(0);
const splice_mode = ref("2x2");
const preview_grid: Ref<HTMLElement | null> = ref(null);
const preview_rows = ref(3);
const preview_cols = ref(3);
const preview_rotation = ref(0);
const previce_grid_height = ref(100);
let cache_outputs: OutputBoardItemConfigEntity[] = [];
class parseSpliceModeResult {
rotation = 0;
rows = 2;
cols = 2;
}
const parseSpliceMode = (splice_mode: string) => {
splice_mode = splice_mode.toLowerCase();
const result = new parseSpliceModeResult();
const space_index = splice_mode.indexOf(" ");
if (space_index != -1) {
const rotation = splice_mode
.substring(space_index + 1)
.replace(/\(|\)|°/g, "")
.trim();
const temp = parseInt(rotation);
if (!isNaN(temp)) {
result.rotation = temp;
}
splice_mode = splice_mode.substring(0, space_index).trim();
}
const x_index = splice_mode.indexOf("x");
if (x_index != -1) {
const rows = splice_mode.substring(0, x_index).trim();
const cols = splice_mode.substring(x_index + 1).trim();
let temp = parseInt(rows);
if (!isNaN(temp)) {
result.rows = temp;
}
temp = parseInt(cols);
if (!isNaN(temp)) {
result.cols = temp;
}
}
return result;
};
watch(
() => splice_mode.value,
(newV: string) => {
const result = parseSpliceMode(newV);
preview_cols.value = result.cols;
preview_rows.value = result.rows;
preview_rotation.value = result.rotation;
if (preview_grid.value) {
previce_grid_height.value =
preview_grid.value.clientHeight / preview_rows.value;
}
}
);
watch(
() => preview_grid.value,
(newV: any) => {
if (newV) {
elementResizeDetectorMaker().listenTo(
newV,
(element: HTMLElement) => {
if (element) {
previce_grid_height.value =
element.clientHeight / preview_rows.value;
}
}
);
}
}
);
return {
show_dialog,
loading,
screen_mode,
splice_mode,
landspace_modes,
portrait_modes,
preview_grid,
preview_rows,
preview_cols,
preview_rotation,
previce_grid_height,
async showDialog() {
{
const temp = parseInt(
(
GlobalData.getInstance().applicationConfig?.device_rotate ?? 0
).toString()
);
if (isNaN(temp)) {
screen_mode.value = 0;
} else {
screen_mode.value = temp == 90 ? 1 : 0;
}
}
{
const video_wall_config = await GlobalData.getInstance()
.getCurrentClient()
?.customConnection.getVideoWallConfig();
if (video_wall_config) {
splice_mode.value =
video_wall_config.wall_rows.toString() +
"x" +
video_wall_config.wall_cols.toString();
if (
Array.isArray(video_wall_config.outputs) &&
video_wall_config?.outputs.length > 0
) {
let index = video_wall_config.outputs[0].index;
let pos = 0;
for (let i = 0; i < video_wall_config.outputs.length; ++i) {
if (video_wall_config.outputs[i].index < index) {
index = video_wall_config.outputs[i].index;
pos = i;
}
}
if (
video_wall_config.outputs[pos].rotation == 1 ||
video_wall_config.outputs[pos].rotation == 180
) {
splice_mode.value += " (180°)";
console.log(splice_mode.value);
}
// preview_rotation.value = video_wall_config.outputs[pos].rotation;
cache_outputs = video_wall_config.outputs;
}
}
const temp_array =
screen_mode.value == 0 ? landspace_modes : portrait_modes;
const temp = temp_array.find((e) => e && e == splice_mode.value);
if (!temp) {
splice_mode.value = "Other";
}
}
show_dialog.value = true;
},
resetData() {
loading.value = false;
},
async onSubmit() {
loading.value = true;
let success = false;
try {
const result = parseSpliceMode(splice_mode.value);
const output = <OutputBoardItemConfigEntity[]>[];
if (result.rotation != 0) {
for (let i = 0; i < result.cols * result.rows; ++i) {
let item = cache_outputs.find((e) => e && e.index == i);
if (!item) {
item = new OutputBoardItemConfigEntity();
item.index = i;
item.output_index = i;
item.rotation = 0;
}
item.rotation = result.rotation == 180 ? 1 : 0;
output.push(item);
}
}
try {
const response = await GlobalData.getInstance()
.getCurrentClient()
?.customConnection.setVideoWallConfig(
output,
result.rows,
result.cols,
screen_mode.value == 0 ? 0 : 90
);
if (response) {
success = response.success;
}
} catch (e) {
console.error(e);
}
show_dialog.value = false;
} catch {}
$q.notify({
color: success ? "positive" : "negative",
icon: success ? "done" : "warning",
message:
$t.t("apply") + (success ? $t.t("success") : $t.t("fail")) + "!",
position: "top",
timeout: 1500,
});
loading.value = false;
},
};
},
});
</script>

View File

@ -0,0 +1,38 @@
export enum EDeviceAttribute {
None_UnKNow = 0x0000,
None = 0x00000001,
OutputBoard = 0x00000002,
CenterControl = 0x00000004,
MirroringOutput = 0x00000008,
CustomISV = 0x00000010,
Reserve5 = 0x00000020,
Reserve6 = 0x00000040,
Reserve7 = 0x00000080,
Reserve8 = 0x00000100,
Reserve9 = 0x00000200,
Reserve10 = 0x00000400,
Reserve11 = 0x00000800,
Reserve12 = 0x00001000,
Reserve13 = 0x00002000,
Reserve14 = 0x00004000,
Reserve15 = 0x00008000,
Reserve16 = 0x00010000,
Reserve17 = 0x00020000,
Reserve18 = 0x00040000,
Reserve19 = 0x00080000,
Reserve20 = 0x00100000,
Reserve21 = 0x00200000,
Reserve22 = 0x00400000,
Reserve23 = 0x00800000,
Reserve24 = 0x01000000,
Reserve25 = 0x02000000,
Reserve26 = 0x04000000,
Reserve27 = 0x08000000,
Reserve28 = 0x10000000,
Reserve29 = 0x20000000,
Reserve30 = 0x40000000,
Reserve31 = 0x80000000,
All = 0xffffffff,
}

View File

@ -0,0 +1,5 @@
export default class OutputBoardItemConfigEntity {
index = 0;
output_index = 0;
rotation = 0;
}

View File

@ -2660,45 +2660,6 @@ export namespace Protocol {
full_screen: boolean;
}
export enum EDeviceAttribute {
None_UnKNow = 0x0000,
None = 0x00000001,
OutputBoard = 0x00000002,
CenterControl = 0x00000004,
MirroringOutput = 0x00000008,
CustomISV = 0x00000010,
Reserve5 = 0x00000020,
Reserve6 = 0x00000040,
Reserve7 = 0x00000080,
Reserve8 = 0x00000100,
Reserve9 = 0x00000200,
Reserve10 = 0x00000400,
Reserve11 = 0x00000800,
Reserve12 = 0x00001000,
Reserve13 = 0x00002000,
Reserve14 = 0x00004000,
Reserve15 = 0x00008000,
Reserve16 = 0x00010000,
Reserve17 = 0x00020000,
Reserve18 = 0x00040000,
Reserve19 = 0x00080000,
Reserve20 = 0x00100000,
Reserve21 = 0x00200000,
Reserve22 = 0x00400000,
Reserve23 = 0x00800000,
Reserve24 = 0x01000000,
Reserve25 = 0x02000000,
Reserve26 = 0x04000000,
Reserve27 = 0x08000000,
Reserve28 = 0x10000000,
Reserve29 = 0x20000000,
Reserve30 = 0x40000000,
Reserve31 = 0x80000000,
All = 0xffffffff,
}
export class GetDeviceAttributeRequestEntity extends PacketEntity {
constructor(rpc_id = 0) {
super();

View File

@ -0,0 +1,85 @@
import { Protocol } from "src/entities/WSProtocol";
import OutputBoardItemConfigEntity from "./OutputBoardItemConfigEntity";
export namespace CustomProtocol {
export class Commands {
public static get PROTOCOL_PREFIX() {
return "_";
}
public static get kUnKnowCommand() {
return Commands.PROTOCOL_PREFIX + "UnKnowCommand";
}
public static get kISVGetVideoWallConfig() {
return Commands.PROTOCOL_PREFIX + "ISVGetVideoWallConfig";
}
public static get kISVSetVideoWallConfig() {
return Commands.PROTOCOL_PREFIX + "ISVSetVideoWallConfig";
}
static _all_commands = new Set([
Commands.kUnKnowCommand,
Commands.kISVGetVideoWallConfig,
Commands.kISVSetVideoWallConfig,
]);
public static get AllCommands() {
return this._all_commands;
}
}
export class ISVGetVideoWallConfigRequestEntity extends Protocol.PacketEntity {
timestamp = Date.now();
constructor(rpcid?: number) {
super();
this.rpc_id = rpcid ?? 0;
this.command = Commands.kISVGetVideoWallConfig;
}
}
export class ISVGetVideoWallConfigResponseEntity extends Protocol.PacketEntity {
outputs: OutputBoardItemConfigEntity[] = [];
wall_rows = 2;
wall_cols = 2;
device_rotation = 0;
constructor() {
super();
super.flag = Protocol.PacketEntity.FLAG_RESPONSE;
this.command = Commands.kISVGetVideoWallConfig;
}
}
export class ISVSetVideoWallConfigRequestEntity extends Protocol.PacketEntity {
outputs: OutputBoardItemConfigEntity[] = [];
wall_rows = 2;
wall_cols = 2;
device_rotation = 0;
constructor(
outputs: OutputBoardItemConfigEntity[],
wall_rows: number,
wall_cols: number,
device_rotation: number,
rpcid?: number
) {
super();
this.rpc_id = rpcid ?? 0;
this.command = Commands.kISVSetVideoWallConfig;
this.outputs = Array.isArray(outputs) ? outputs : [];
this.wall_cols = wall_cols;
this.wall_rows = wall_rows;
this.device_rotation = device_rotation;
}
}
export class ISVSetVideoWallConfigResponseEntity extends Protocol.PacketEntity {
success = false;
constructor() {
super();
super.flag = Protocol.PacketEntity.FLAG_RESPONSE;
this.command = Commands.kISVSetVideoWallConfig;
}
}
}

View File

@ -383,4 +383,10 @@ export default {
tcp: "TCP",
udp: "UDP",
"serial port": "Serial Port",
"screen mode": "Screen Mode",
"splice mode": "Splice Mode",
"landspace mode": "Landspace Mode",
"portrait mode": "Portrait Mode",
"can't be": "Can't Be",
"video wall": "Video Wall",
};

View File

@ -654,4 +654,10 @@ export default {
tcp: "TCP",
udp: "UDP",
"serial port": "串口",
"screen mode": "屏幕方向",
"splice mode": "拼接模式",
"landspace mode": "横屏",
"portrait mode": "竖屏",
"can't be": "不能为",
"video wall": "拼接墙",
};

View File

@ -7,7 +7,8 @@
class="header text-black"
@click="(evt) => evt.stopPropagation()"
>
<top-tool-bar />
<top-tool-bar v-if="!$store.state.custom_defines.is_custom_isv" />
<i-s-v-top-toolbar v-else />
</q-header>
<!-- page -->
@ -103,6 +104,7 @@ import { defineComponent, ref } from "vue";
import LeftToolBar from "src/pages/LeftToolBar.vue";
import RightToolBar from "src/pages/RightToolBar.vue";
import TopToolBar from "src/pages/TopToolBar.vue";
import ISVTopToolbar from "src/pages/custom/ISVTopToolBar.vue";
import FooterPortrait from "src/pages/FooterPortrait.vue";
import EventBus, { EventNamesDefine } from "src/common/EventBus";
import { useStore } from "src/store";
@ -111,7 +113,13 @@ import FileSuffixHelper from "src/common/FileSuffixHelper";
export default defineComponent({
name: "MainLayout",
components: { LeftToolBar, RightToolBar, TopToolBar, FooterPortrait },
components: {
LeftToolBar,
RightToolBar,
TopToolBar,
ISVTopToolbar,
FooterPortrait,
},
setup() {
const $store = useStore();

View File

@ -26,7 +26,8 @@
class="header"
@touchstart="(evt) => evt.stopPropagation()"
>
<top-tool-bar />
<top-tool-bar v-if="!$store.state.custom_defines.is_custom_isv" />
<i-s-v-top-toolbar v-else />
</q-header>
<!-- page -->
@ -87,8 +88,9 @@ body {
<script lang="ts">
import { defineComponent, ref } from "vue";
import TopToolBar from "./TopToolbar.vue";
import BottomBar from "./BottomBar.vue";
import TopToolBar from "src/pad/TopToolbar.vue";
import ISVTopToolbar from "src/pad/custom/ISVTopToolbar.vue";
import BottomBar from "src/pad/BottomBar.vue";
import EventBus, { EventNamesDefine } from "src/common/EventBus";
import { useI18n } from "vue-i18n";
@ -102,7 +104,7 @@ import { EProductNames } from "src/entities/ProductNames";
export default defineComponent({
name: "PadMainLayout",
components: { TopToolBar, BottomBar },
components: { TopToolBar, ISVTopToolbar, BottomBar },
setup() {
const $q = useQuasar();

View File

@ -75,7 +75,7 @@
/>
<q-btn
v-if="function_center_control"
v-if="$store.state.custom_defines.function_center_control"
:disable="!$store.state.power_state"
class="col-auto"
stretch
@ -192,12 +192,7 @@
{{ $t("background image setting") }}
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="switchLanguage"
v-if="!is_custon_isv"
>
<q-item clickable v-close-popup @click="switchLanguage">
<q-item-section avatar>
<q-icon name="img:pad/toolbar/cn_en.png" />
</q-item-section>
@ -257,6 +252,7 @@ import SystemSettingDialog from "src/components/SystemSettingDialog.vue";
import GlobalData from "src/common/GlobalData";
import { Protocol } from "src/entities/WSProtocol";
import { component } from "v-viewer";
import { EDeviceAttribute } from "src/entities/EDeviceAttribute";
export default defineComponent({
name: "PadTopToolbarPage",
@ -280,26 +276,6 @@ export default defineComponent({
() => $store.state.current_running_plan.trim() != ""
);
const function_center_control = ref(
($store.state.device_attribute &
Protocol.EDeviceAttribute.CenterControl) !=
0
);
const is_custon_isv = ref(
($store.state.device_attribute & Protocol.EDeviceAttribute.CustomISV) != 0
);
watch(
() => $store.state.device_attribute,
(value) => {
function_center_control.value =
(value & Protocol.EDeviceAttribute.CenterControl) != 0;
is_custon_isv.value =
(value & Protocol.EDeviceAttribute.CustomISV) != 0;
}
);
const power_flag = ref(false);
watch(
() => power_flag.value,
@ -314,8 +290,6 @@ export default defineComponent({
return {
plan_running,
function_center_control,
is_custon_isv,
window_rect_edit_dialog,
power_flag,

View File

@ -0,0 +1,477 @@
<template>
<q-toolbar
class="shadow-2 text-accent bg-secondary q-pa-none"
style="flex-wrap: wrap"
>
<q-btn
class="col-auto"
:disable="!$store.state.power_state"
stretch
stack
flat
no-caps
icon="img:pad/toolbar/close_icon.png"
:label="$t('close')"
@click="closeCurrentWindow"
/>
<q-btn
class="col-auto"
:disable="!$store.state.power_state"
stretch
stack
flat
no-caps
icon="img:pad/toolbar/clean_windows_icon.png"
:label="$t('clean screen')"
@click="closeAllWindows"
/>
<q-btn
class="col-auto"
:disable="!$store.state.power_state"
stretch
stack
flat
no-caps
icon="img:pad/toolbar/full_screen_icon.png"
:label="$t('full screen window')"
@click="fullscreenWindow"
/>
<q-btn
class="col-auto"
:disable="!$store.state.power_state"
stretch
stack
flat
no-caps
icon="img:pad/toolbar/exit_full_screen_icon.png"
:label="$t('restore window size')"
@click="exitFullscreenWindow"
/>
<q-btn
class="col-auto"
:disable="!$store.state.power_state"
stretch
stack
flat
no-caps
icon="img:pad/toolbar/top_window_icon.png"
:label="$t('win top')"
@click="topWindow"
/>
<q-btn
class="col-auto"
:disable="!$store.state.power_state"
stretch
stack
flat
no-caps
icon="img:pad/toolbar/lower_window_icon.png"
:label="$t('win lower')"
@click="lowerWindow"
/>
<q-btn
v-if="function_center_control"
:disable="!$store.state.power_state"
class="col-auto"
stretch
stack
flat
no-caps
icon="img:pad/toolbar/center_control_icon.png"
:label="$t('center control')"
@click="$refs.center_control_dialog.showDialog()"
/>
<q-btn
class="col-auto"
stretch
stack
flat
no-caps
:disable="power_flag"
icon="img:pad/toolbar/power_on_icon.png"
:label="$t('power on')"
@click="powerOn"
/>
<q-btn
class="col-auto"
stretch
stack
flat
no-caps
:disable="power_flag"
icon="img:pad/toolbar/power_off_icon.png"
:label="$t('power off')"
@click="powerOff"
/>
<q-btn
stretch
:disable="!$store.state.power_state"
flat
stack
no-caps
icon="img:pad/toolbar/stop_plan.png"
:label="$t('stop plan')"
class="col-auto"
v-if="plan_running"
@click="stopPlan"
/>
<q-btn
stretch
:disable="!$store.state.power_state"
flat
stack
no-caps
icon="img:pad/toolbar/subtitle.png"
:label="$t('subtitle')"
class="col-auto"
@click="$refs.subtitle_dialog.showDialog()"
/>
<q-btn
stretch
:disable="!$store.state.power_state"
flat
stack
no-caps
icon="img:pad/toolbar/edit_window_rect.png"
:label="$t('toolbar edit window rect')"
class="col-auto"
@click="editRect"
/>
<q-btn
class="col-auto"
:disable="!$store.state.power_state"
stretch
stack
flat
no-caps
icon="img:pad/toolbar/other_setting.png"
:label="$t('other setting')"
>
<q-popup-proxy>
<q-list class="shadow-2 text-primary">
<q-item
clickable
v-close-popup
@click="$refs.system_setting_dialog.showDialog()"
>
<q-item-section avatar>
<q-icon name="img:pad/toolbar/system_setting.png" />
</q-item-section>
<q-item-section>
{{ $t("system setting") }}
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="$refs.file_manage_dialog.showDialogAsync()"
>
<q-item-section avatar>
<q-icon name="img:pad/toolbar/file_upload_icon.png" />
</q-item-section>
<q-item-section>
{{ $t("file manage") }}
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="$refs.background_image_dialog.showDialog()"
>
<q-item-section avatar>
<q-icon name="img:pad/toolbar/wallpaper_icon.png" />
</q-item-section>
<q-item-section>
{{ $t("background image setting") }}
</q-item-section>
</q-item>
<q-item
v-if="$store.state.advanced_debug"
clickable
v-close-popup
@click="$refs.advanced_debug_dialog.showDialog()"
>
<q-item-section avatar>
<q-icon name="bug_report" color="accent" />
</q-item-section>
<q-item-section> AdvancedDebug </q-item-section>
</q-item>
</q-list>
</q-popup-proxy>
</q-btn>
<q-space />
<q-btn
class="col-1"
stretch
stack
flat
no-caps
icon="img:pad/toolbar/logout_icon.png"
:label="$t('logout')"
@click="logout"
/>
</q-toolbar>
<background-image-dialog ref="background_image_dialog" />
<file-manage-dialog ref="file_manage_dialog" />
<center-control-dialog ref="center_control_dialog" />
<subtitle-dialog ref="subtitle_dialog" />
<advanced-debug-dialog ref="advanced_debug_dialog" />
<window-rect-edit-dialog ref="window_rect_edit_dialog" />
<system-setting-dialog ref="system_setting_dialog" />
</template>
<script lang="ts">
import { defineComponent, Ref, ref, computed, watch } from "vue";
import { Cookies, SessionStorage, useQuasar } from "quasar";
import { useI18n } from "vue-i18n";
import { useStore } from "src/store";
import BackgroundImageDialog from "src/components/BackgroundImageDialog.vue";
import FileManageDialog from "src/components/FileManageDialog.vue";
import SubtitleDialog from "src/components/SubtitleDialog.vue";
import CenterControlDialog from "src/components/CenterControlDialog.vue";
import AdvancedDebugDialog from "src/components/AdvancedDebugDialog.vue";
import WindowRectEditDialog from "src/components/WindowRectEditDialog.vue";
import SystemSettingDialog from "src/components/SystemSettingDialog.vue";
import GlobalData from "src/common/GlobalData";
import { EDeviceAttribute } from "src/entities/EDeviceAttribute";
export default defineComponent({
name: "PadCustomISVTopToolbarPage",
components: {
BackgroundImageDialog,
FileManageDialog,
CenterControlDialog,
SubtitleDialog,
AdvancedDebugDialog,
WindowRectEditDialog,
SystemSettingDialog,
},
setup() {
const $store = useStore();
const $q = useQuasar();
const $t = useI18n();
const window_rect_edit_dialog: Ref<any> = ref(null);
const plan_running = computed(
() => $store.state.current_running_plan.trim() != ""
);
const function_center_control = ref(
($store.state.device_attribute & EDeviceAttribute.CenterControl) != 0
);
watch(
() => $store.state.device_attribute,
(value) => {
function_center_control.value =
(value & EDeviceAttribute.CenterControl) != 0;
}
);
const power_flag = ref(false);
watch(
() => power_flag.value,
(newV) => {
if (newV) {
setTimeout(() => {
power_flag.value = false;
}, 1100 * 5);
}
}
);
return {
plan_running,
function_center_control,
window_rect_edit_dialog,
power_flag,
toogleFullScreen() {
if (!$q.fullscreen.isCapable) {
if ($q.platform.is.ipad) {
$q.notify({
type: "warning",
message:
$t.t("on the iPad, only Safari supports full screen") + "!",
position: "top",
timeout: 1500,
});
} else {
$q.notify({
type: "warning",
message:
$t.t("the current browser does not support full screen") + "!",
position: "top",
timeout: 1500,
});
}
return;
}
if ($q.fullscreen.isActive) {
$q.fullscreen.exit();
} else {
$q.fullscreen.request().catch((e: any) => {
console.log(e);
});
}
},
stopPlan() {
GlobalData.getInstance().getCurrentClient()?.stopCurrentRunningPlan();
},
topWindow() {
const window = $store.state.windows.find(
(element) => element && element.uuid == $store.state.selected_window
);
if (window) {
GlobalData.getInstance()
.getCurrentClient()
?.focusIn(window.window_id);
}
},
lowerWindow() {
const window = $store.state.windows.find(
(element) => element && element.uuid == $store.state.selected_window
);
if (window) {
GlobalData.getInstance()
.getCurrentClient()
?.lowerWindow(window.window_id);
}
},
fullscreenWindow() {
const window = $store.state.windows.find(
(element) => element && element.uuid == $store.state.selected_window
);
if (window) {
GlobalData.getInstance()
.getCurrentClient()
?.windowFullScreen(window.window_id, true);
}
},
exitFullscreenWindow() {
const window = $store.state.windows.find(
(element) => element && element.uuid == $store.state.selected_window
);
if (window) {
GlobalData.getInstance()
.getCurrentClient()
?.windowFullScreen(window.window_id, false);
}
},
closeCurrentWindow() {
const window = $store.state.windows.find(
(element) => element && element.uuid == $store.state.selected_window
);
if (window) {
GlobalData.getInstance()
.getCurrentClient()
?.closeWindow(window.window_id);
}
},
closeAllWindows() {
GlobalData.getInstance().getCurrentClient()?.closeWindow(-1);
},
switchLanguage() {
let language = Cookies.get("language");
if (!language) {
language = "zh-CN";
}
if (language != "zh-CN" && language != "en-US") {
language = "zh-CN";
} else {
language = language == "zh-CN" ? "en-US" : "zh-CN";
}
Cookies.set("language", language, {
expires: 365,
});
window.location.reload();
},
logout() {
const w = window as any;
if (w.controlLogout && typeof w.controlLogout == "function") {
w.controlLogout();
} else {
Cookies.remove("auto_login");
SessionStorage.clear();
try {
$q.fullscreen.exit();
} catch {}
window.location.reload();
}
},
async powerOff() {
let success = false;
try {
const response = await GlobalData.getInstance()
.getCurrentClient()
?.deviceStandByMode();
if (response) {
success = response?.success ?? false;
}
} catch {}
if (success) {
power_flag.value = true;
}
$q.notify({
color: success ? "positive" : "negative",
icon: success ? "done" : "warning",
message:
$t.t("send power off command") +
(success ? $t.t("success") : $t.t("fail")) +
"!",
position: "top",
timeout: 1500,
});
},
async powerOn() {
let success = false;
try {
const response = await GlobalData.getInstance()
.getCurrentClient()
?.wakeUpDevice();
if (response) {
success = response?.success ?? false;
}
} catch {}
if (success) {
power_flag.value = true;
}
$q.notify({
color: success ? "positive" : "negative",
icon: success ? "done" : "warning",
message:
$t.t("send power on command") +
(success ? $t.t("success") : $t.t("fail")) +
"!",
position: "top",
timeout: 1500,
});
},
async editRect() {
if (window_rect_edit_dialog.value) {
try {
const window = $store.state.windows.find(
(element) =>
element && element.uuid == $store.state.selected_window
);
if (window) {
window_rect_edit_dialog.value.showDialog(window.window_id);
}
} catch {}
}
},
};
},
});
</script>

View File

@ -266,7 +266,7 @@
/>
<q-btn
v-if="function_center_control"
v-if="$store.state.custom_defines.function_center_control"
:disable="!$store.state.power_state"
no-caps
stretch
@ -386,12 +386,7 @@
{{ $t("upgrade") }}
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="switchLanguage"
v-if="!is_custon_isv"
>
<q-item clickable v-close-popup @click="switchLanguage">
<q-item-section avatar>
<!-- <q-icon name="info_outline" /> -->
<q-icon name="translate" />
@ -404,7 +399,6 @@
clickable
v-close-popup
@click="$refs.register_dialog.showDialog()"
v-if="$store.state.factory_mode || !is_custon_isv"
>
<q-item-section avatar>
<!-- <q-icon name="devices" /> -->
@ -524,6 +518,7 @@ import { SessionStorage, Cookies, openURL, useQuasar } from "quasar";
import { useI18n } from "vue-i18n";
import { ConnectTableEntity } from "src/entities/ConnectTableEntity";
import ClientConnection from "src/common/ClientConnection";
import { EDeviceAttribute } from "src/entities/EDeviceAttribute";
export default defineComponent({
name: "PageTopToolBar",
@ -555,16 +550,6 @@ export default defineComponent({
const edge_blending_dialog: Ref<any> = ref(null);
const register_dialog: Ref<any> = ref(null);
const function_center_control = ref(
($store.state.device_attribute &
Protocol.EDeviceAttribute.CenterControl) !=
0
);
const is_custon_isv = ref(
($store.state.device_attribute & Protocol.EDeviceAttribute.CustomISV) != 0
);
const window_rect_edit_dialog: Ref<any> = ref(null);
const power_flag = ref(false);
@ -642,16 +627,6 @@ export default defineComponent({
}
);
watch(
() => $store.state.device_attribute,
(value) => {
function_center_control.value =
(value & Protocol.EDeviceAttribute.CenterControl) != 0;
is_custon_isv.value =
(value & Protocol.EDeviceAttribute.CustomISV) != 0;
}
);
const show_device_list = ref(true);
onMounted(() => {
show_device_list.value =
@ -663,8 +638,6 @@ export default defineComponent({
plan_running,
edge_blending_dialog,
register_dialog,
function_center_control,
is_custon_isv,
window_rect_edit_dialog,
show_device_list,
power_flag,

View File

@ -0,0 +1,803 @@
<template>
<div>
<q-toolbar style="background-color: #3e9acd" class="shadow-2 text-white">
<q-btn-dropdown
v-if="show_device_list"
stretch
no-caps
flat
icon="devices"
:label="$store.state.device_ip_address"
class="q-mr-sm"
>
<q-list>
<q-item clickable v-close-popup @click="logout">
<q-item-section avatar> <q-icon name="logout" /> </q-item-section>
<q-item-section>
{{ $t("logout") }}
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
<q-separator vertical inset />
<q-btn
stretch
no-caps
:disable="!$store.state.power_state"
flat
stack
:icon="/*settings*/ 'img:new_icon/system_setting.png'"
:label="$t('system setting')"
class="q-mr-sm"
@click="$refs.system_setting_dialog.showDialog()"
/>
<q-btn
stretch
no-caps
:disable="!$store.state.power_state"
flat
stack
:icon="/*description*/ 'img:new_icon/file_manager.png'"
:label="$t('file manage')"
class="q-mr-sm"
@click="$refs.file_manage_dialog.showDialogAsync()"
v-if="false"
/>
<q-btn
stretch
no-caps
:disable="!$store.state.power_state"
flat
stack
:icon="/*settings*/ 'img:new_icon/system_setting.png'"
:label="$t('video wall')"
class="q-mr-sm"
@click="$refs.isv_video_wall_dialog.showDialog()"
/>
<q-btn
stretch
no-caps
:disable="!$store.state.power_state"
flat
stack
:icon="/*settings*/ 'img:new_icon/system_setting.png'"
:label="$t('splice on')"
class="q-mr-sm"
@click="$refs.system_setting_dialog.showDialog()"
/>
<q-btn
stretch
no-caps
:disable="!$store.state.power_state"
flat
stack
:icon="/*grid_on*/ 'img:new_icon/grid_setting.png'"
:label="$t('grid setting')"
class="q-mr-sm"
@click="
($store.state.isSpecialVideo()
? $refs.special_video_grid_setting_dialog
: $refs.grid_setting_dialog
)?.showDialog()
"
v-if="false"
/>
<q-btn
v-if="$store.state.isLedPlayer()"
stretch
no-caps
:disable="!$store.state.power_state"
flat
stack
:icon="/*art_track*/ 'img:new_icon/subtitle.png'"
:label="$t('subtitle')"
class="q-mr-sm"
@click="$refs.subtitle_dialog.showDialog()"
/>
<q-btn
stretch
no-caps
flat
stack
:disable="plan_running || !$store.state.power_state"
:icon="/*vertical_align_top*/ 'img:new_icon/top_window.png'"
:label="$t('win top')"
class="q-mr-sm"
@click="topWindow"
/>
<q-btn
stretch
no-caps
flat
stack
:disable="plan_running || !$store.state.power_state"
:icon="/*vertical_align_bottom*/ 'img:new_icon/lower_window.png'"
:label="$t('win lower')"
class="q-mr-sm"
@click="lowerWindow"
/>
<q-btn
stretch
no-caps
flat
stack
:disable="plan_running || !$store.state.power_state"
:icon="/*vertical_align_bottom*/ 'img:new_icon/edit_window_rect.png'"
:label="$t('toolbar edit window rect')"
class="q-mr-sm"
@click="editRect"
/>
<q-btn
stretch
no-caps
flat
stack
:disable="plan_running || !$store.state.power_state"
:icon="/*close*/ 'img:new_icon/close_window.png'"
:label="$t('close')"
class="q-mr-sm"
@click="closeCurrentWindow"
/>
<q-btn
stretch
flat
no-caps
stack
:disable="plan_running || !$store.state.power_state"
:icon="/*clear_all*/ 'img:new_icon/clean_windows.png'"
:label="$t('clean screen')"
class="q-mr-sm"
@click="closeAllWindows"
/>
<q-btn
stretch
no-caps
:disable="!$store.state.power_state"
flat
stack
icon="stop"
:label="$t('stop plan')"
class="q-mr-sm"
v-if="plan_running"
@click="stopPlan"
/>
<q-btn
stretch
no-caps
flat
stack
:disable="power_flag"
icon="img:new_icon/power_off.png"
:label="$t('power off')"
class="q-mr-sm"
@click="powerOff"
/>
<q-btn
stretch
no-caps
flat
stack
:disable="power_flag"
icon="img:new_icon/power_on.png"
:label="$t('power on')"
class="q-mr-sm"
@click="powerOn"
/>
<q-btn
v-if="$store.state.custom_defines.function_center_control"
:disable="!$store.state.power_state"
no-caps
stretch
flat
stack
icon="img:new_icon/center_control.png"
:label="$t('center control')"
class="q-mr-sm"
@click="$refs.center_control_dialog.showDialog()"
/>
<q-space />
<q-btn-dropdown
:disable="!$store.state.power_state"
no-caps
stretch
flat
:icon="/*build*/ 'img:new_icon/other_setting.png'"
:label="$t('other setting')"
class="q-mr-sm"
>
<q-list style="background-color: #3e9acd" class="shadow-2 text-white">
<q-item
clickable
:disable="!$store.state.power_state"
v-close-popup
@click="
($store.state.isSpecialVideo()
? $refs.special_video_grid_setting_dialog
: $refs.grid_setting_dialog
)?.showDialog()
"
>
<q-item-section avatar>
<!-- <q-icon name="image" /> -->
<q-icon name="img:new_icon/grid_setting.png" />
</q-item-section>
<q-item-section>
{{ $t("grid setting") }}
</q-item-section>
</q-item>
<q-item
clickable
:disable="!$store.state.power_state"
v-close-popup
@click="$refs.file_manage_dialog.showDialogAsync()"
>
<q-item-section avatar>
<!-- <q-icon name="image" /> -->
<q-icon name="img:new_icon/file_manager.png" />
</q-item-section>
<q-item-section>
{{ $t("file manage") }}
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="$refs.background_image_dialog.showDialog()"
>
<q-item-section avatar>
<!-- <q-icon name="image" /> -->
<q-icon name="img:new_icon/background_image.png" />
</q-item-section>
<q-item-section>
{{ $t("background image setting") }}
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="$refs.recovery_database_dialog.showDialog()"
>
<q-item-section avatar>
<!-- <q-icon name="backup" /> -->
<q-icon name="img:new_icon/database_backup.png" />
</q-item-section>
<q-item-section>
{{ $t("database import") }}
</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="backupDB">
<q-item-section avatar>
<!-- <q-icon name="cloud_download" /> -->
<q-icon name="img:new_icon/database_recovery.png" />
</q-item-section>
<q-item-section>
{{ $t("database export") }}
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="$refs.upgrade_dialog.showDialog()"
>
<q-item-section avatar>
<!-- <q-icon name="system_update" /> -->
<q-icon name="img:new_icon/upgrade.png" />
</q-item-section>
<q-item-section>
{{ $t("upgrade") }}
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="$refs.about_dialog.showDialog()"
>
<q-item-section avatar>
<!-- <q-icon name="info_outline" /> -->
<q-icon name="img:new_icon/about.png" />
</q-item-section>
<q-item-section>
{{ $t("about") }}
</q-item-section>
</q-item>
<q-item
v-if="$store.state.advanced_debug"
clickable
v-close-popup
@click="$refs.advanced_debug_dialog.showDialog()"
>
<q-item-section avatar>
<q-icon name="bug_report" />
</q-item-section>
<q-item-section> AdvancedDebug </q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
<q-separator vertical inset />
<q-item>
<q-item-section avatar style="margin-right: 0px; padding-right: 0px">
<!-- <q-icon class="text-white rotate" name="img:svgs/fan.svg" /> -->
<q-icon class="text-white rotate" name="img:new_icon/fan.png" />
</q-item-section>
<q-item-section style="margin-left: -25px">
{{ $store.state.fan_temp.toFixed(1) }}
</q-item-section>
</q-item>
</q-toolbar>
</div>
<grid-setting-dialog
v-if="$store.state.isLedPlayer()"
ref="grid_setting_dialog"
/>
<special-video-grid-setting-dialog
v-if="$store.state.isSpecialVideo()"
ref="special_video_grid_setting_dialog"
/>
<background-image-dialog ref="background_image_dialog" />
<subtitle-dialog ref="subtitle_dialog" />
<recovery-database-dialog ref="recovery_database_dialog" />
<upgrade-dialog ref="upgrade_dialog" />
<file-manage-dialog ref="file_manage_dialog" />
<system-setting-dialog ref="system_setting_dialog" />
<about-dialog ref="about_dialog" />
<edge-blending-dialog ref="edge_blending_dialog" />
<register-dialog ref="register_dialog" />
<center-control-dialog ref="center_control_dialog" />
<advanced-debug-dialog ref="advanced_debug_dialog" />
<window-rect-edit-dialog ref="window_rect_edit_dialog" />
<i-s-v-video-wall-dialog ref="isv_video_wall_dialog" />
</template>
<style scoped>
@keyframes rotate {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
.rotate {
animation: rotate 1.5s linear infinite;
}
</style>
<script lang="ts">
import { defineComponent, ref, Ref, computed, watch, onMounted } from "vue";
import { useStore } from "src/store";
import GridSettingDialog from "src/components/GridSettingDialog.vue";
import SpecialVideoGridSettingDialog from "src/components/SpecialVideoGridSettingDialog.vue";
import BackgroundImageDialog from "src/components/BackgroundImageDialog.vue";
import RecoveryDatabaseDialog from "src/components/RecoveryDatabaseDialog.vue";
import UpgradeDialog from "src/components/UpgradeDialog.vue";
import FileManageDialog from "src/components/FileManageDialog.vue";
import SubtitleDialog from "src/components/SubtitleDialog.vue";
import SystemSettingDialog from "src/components/SystemSettingDialog.vue";
import AboutDialog from "src/components/AboutDialog.vue";
import EdgeBlendingDialog from "src/components/EdgeBlendingDialog.vue";
import RegisterDialog from "src/components/RegisterDialog.vue";
import CenterControlDialog from "src/components/CenterControlDialog.vue";
import AdvancedDebugDialog from "src/components/AdvancedDebugDialog.vue";
import WindowRectEditDialog from "src/components/WindowRectEditDialog.vue";
import ISVVideoWallDialog from "src/components/custom/ISVVideoWallDialog.vue";
import EventBus, { EventNamesDefine } from "src/common/EventBus";
import { Protocol } from "src/entities/WSProtocol";
import GlobalData from "src/common/GlobalData";
import { api } from "boot/axios";
import { HttpProtocol } from "src/entities/HttpProtocol";
import { SessionStorage, Cookies, openURL, useQuasar } from "quasar";
import { useI18n } from "vue-i18n";
import { ConnectTableEntity } from "src/entities/ConnectTableEntity";
import ClientConnection from "src/common/ClientConnection";
export default defineComponent({
name: "PageCustomISVTopToolBar",
components: {
GridSettingDialog,
SpecialVideoGridSettingDialog,
BackgroundImageDialog,
RecoveryDatabaseDialog,
UpgradeDialog,
FileManageDialog,
SubtitleDialog,
SystemSettingDialog,
AboutDialog,
EdgeBlendingDialog,
RegisterDialog,
CenterControlDialog,
AdvancedDebugDialog,
WindowRectEditDialog,
ISVVideoWallDialog,
},
setup() {
let $store = useStore();
let $q = useQuasar();
let $t = useI18n();
let show_advanced_menu = ref(true);
const edge_blending_dialog: Ref<any> = ref(null);
const register_dialog: Ref<any> = ref(null);
const window_rect_edit_dialog: Ref<any> = ref(null);
const power_flag = ref(false);
watch(
() => power_flag.value,
(newV) => {
if (newV) {
setTimeout(() => {
power_flag.value = false;
}, 1100 * 5);
}
}
);
const plan_running = computed(
() => $store.state.current_running_plan.trim() != ""
);
const checkRegistered = () => {
if (
GlobalData.getInstance().getCurrentClient()?.is_connected &&
GlobalData.getInstance().getCurrentClient()?.is_login
) {
let register: any =
GlobalData.getInstance().applicationConfig?.registered;
try {
register = JSON.parse(register);
} catch {
register = false;
}
if (
!register &&
(!register_dialog.value || !register_dialog.value.isShow())
) {
$q.notify({
color: "negative",
icon: "report_problem",
message:
"<span class='text-h4'>" +
$t.t("not registered") +
"!!!" +
"</span>",
position: "center",
actions: [
{
label: $t.t("register"),
color: "blue",
handler: () => {
if (register_dialog.value) {
register_dialog.value.showDialog();
}
},
},
],
html: true,
timeout: 2500,
});
}
}
};
setInterval(checkRegistered, 5000);
EventBus.getInstance().on(
EventNamesDefine.CurrentConnectConnected,
async () => {
try {
const client = GlobalData.getInstance().getCurrentClient();
if (client) {
const response = await client.getConnectList();
if (response) {
$store.commit("setConnects", response.connects);
}
}
} catch {}
}
);
const show_device_list = ref(true);
onMounted(() => {
show_device_list.value =
typeof (<any>window).user_search?.hide_device_list == "undefined";
});
return {
show_advanced_menu,
plan_running,
edge_blending_dialog,
register_dialog,
window_rect_edit_dialog,
show_device_list,
power_flag,
async backupDB() {
let client = GlobalData.getInstance().getCurrentClient();
if (client) {
let url = new URL(client.url);
url.port =
GlobalData.getInstance().applicationConfig?.httpserver_port ??
HttpProtocol.DefaultHttpPort.toString();
url.pathname = HttpProtocol.RequestPathUpdateDBBackupFile;
url.protocol = "http:";
console.log(url.toString());
let response = await api.get(url.toString());
if (response.status == 200 && response && response.data) {
url.pathname =
HttpProtocol.RequestPathDBBackup + "/" + response.data;
openURL(url.toString());
} else {
$q.notify({
type: "warning",
message: $t.t("data export ") + $t.t("fail") + "!",
position: "top",
timeout: 1500,
});
}
}
},
handleHold() {
show_advanced_menu.value = true;
},
stopPlan() {
GlobalData.getInstance().getCurrentClient()?.stopCurrentRunningPlan();
},
topWindow() {
const window = $store.state.windows.find(
(element) => element && element.uuid == $store.state.selected_window
);
if (window) {
GlobalData.getInstance()
.getCurrentClient()
?.focusIn(window.window_id);
}
},
lowerWindow() {
const window = $store.state.windows.find(
(element) => element && element.uuid == $store.state.selected_window
);
if (window) {
GlobalData.getInstance()
.getCurrentClient()
?.lowerWindow(window.window_id);
}
},
closeCurrentWindow() {
const window = $store.state.windows.find(
(element) => element && element.uuid == $store.state.selected_window
);
if (window) {
GlobalData.getInstance()
.getCurrentClient()
?.closeWindow(window.window_id);
}
},
closeAllWindows() {
// for (const window of $store.state.windows) {
// if (window) {
// GlobalData.getInstance()
// .getCurrentClient()
// ?.closeWindow(window.window_id);
// }
// }
GlobalData.getInstance().getCurrentClient()?.closeWindow(-1);
},
switchLanguage() {
let language = Cookies.get("language");
if (!language) {
language = "zh-CN";
}
if (language != "zh-CN" && language != "en-US") {
language = "zh-CN";
} else {
language = language == "zh-CN" ? "en-US" : "zh-CN";
}
Cookies.set("language", language, {
expires: 365,
});
window.location.reload();
},
logout() {
const w = window as any;
if (w.controlLogout && typeof w.controlLogout == "function") {
w.controlLogout();
} else {
Cookies.remove("auto_login");
SessionStorage.clear();
try {
$q.fullscreen.exit();
} catch {}
window.location.reload();
}
},
powerOff() {
$q.dialog({
title: $t.t("power off"),
message: $t.t("are you sure power off device") + "?",
ok: {
label: $t.t("ok"),
noCaps: true,
flat: true,
},
cancel: {
label: $t.t("cancel"),
noCaps: true,
flat: true,
},
persistent: true,
}).onOk(async () => {
let success = false;
try {
const response = await GlobalData.getInstance()
.getCurrentClient()
?.deviceStandByMode();
if (response) {
success = response?.success ?? false;
}
} catch {}
if (success) {
power_flag.value = true;
}
$q.notify({
color: success ? "positive" : "negative",
icon: success ? "done" : "warning",
message:
$t.t("send power off command") +
(success ? $t.t("success") : $t.t("fail")) +
"!",
position: "top",
timeout: 1500,
});
});
},
async powerOn() {
let success = false;
try {
const response = await GlobalData.getInstance()
.getCurrentClient()
?.wakeUpDevice();
if (response) {
success = response?.success ?? false;
}
} catch {}
if (success) {
power_flag.value = true;
}
$q.notify({
color: success ? "positive" : "negative",
icon: success ? "done" : "warning",
message:
$t.t("send power on command") +
(success ? $t.t("success") : $t.t("fail")) +
"!",
position: "top",
timeout: 1500,
});
},
async changeHost(item: ConnectTableEntity) {
if (item) {
const url =
"ws://" +
item.host +
":" +
GlobalData.kDefaultWebsocektPort.toString() +
GlobalData.kWebsocketResource;
const web_socket = new ClientConnection(
url,
item.user,
item.password
);
$q.loading.hide();
$q.loading.show({
message: $t.t("connection") + "...",
});
const wait_for = (ms: number) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true);
}, ms);
});
};
let login_success = false;
for (let i = 0; i < 20; ++i) {
await wait_for(500);
if (web_socket._is_login) {
login_success = true;
break;
}
}
$q.loading.hide();
{
$q.notify({
color: login_success ? "positive" : "negative",
icon: login_success ? "done" : "warning",
message:
$t.t("login") +
(login_success
? $t.t("success")
: $t.t("fail") +
$t.t("please check ipaddress or username or password")) +
"!",
position: "top",
timeout: 1500,
});
if (login_success) {
GlobalData.getInstance().addClient(item.host, web_socket);
GlobalData.getInstance().setCurrentClientName(item.host);
$store.commit("setDeviceIpAddress", item.host);
EventBus.getInstance().emit(
EventNamesDefine.CurrentClientChanged
);
} else {
web_socket.destory();
GlobalData.getInstance().removeClient(item.host);
}
}
}
},
async editRect() {
if (window_rect_edit_dialog.value) {
try {
const window = $store.state.windows.find(
(element) =>
element && element.uuid == $store.state.selected_window
);
if (window) {
window_rect_edit_dialog.value.showDialog(window.window_id);
}
} catch {}
}
},
fullscreenWindow() {
const window = $store.state.windows.find(
(element) => element && element.uuid == $store.state.selected_window
);
if (window) {
GlobalData.getInstance()
.getCurrentClient()
?.windowFullScreen(window.window_id, true);
}
},
exitFullscreenWindow() {
const window = $store.state.windows.find(
(element) => element && element.uuid == $store.state.selected_window
);
if (window) {
GlobalData.getInstance()
.getCurrentClient()
?.windowFullScreen(window.window_id, false);
}
},
};
},
});
</script>

View File

@ -1,8 +1,8 @@
import { ConnectTableEntity } from "./../entities/ConnectTableEntity";
import { ConnectTableEntity } from "src/entities/ConnectTableEntity";
import {
PollingEntity,
PollingTreeItemEntity,
} from "./../entities/PollingEntity";
} from "src/entities/PollingEntity";
import { store } from "quasar/wrappers";
import { ModeEntity, ModeTreeItemEntity } from "src/entities/ModeEntity";
import { WindowOpenNotifyEntity } from "src/entities/MultimediaWindowEntity";
@ -18,6 +18,8 @@ import {
useStore as vuexUseStore,
} from "vuex";
import { EDeviceAttribute } from "src/entities/EDeviceAttribute";
// import example from './module-example'
// import { ExampleStateInterface } from './module-example/state';
@ -30,6 +32,13 @@ import {
* with the Store instance.
*/
export class CustomDefines {
is_custom_isv = false;
function_output_board = false;
function_center_control = false;
function_mirroring_output = false;
}
export interface StateInterface {
// Define your own store structure, using submodules if needed
// example: ExampleStateInterface;
@ -68,6 +77,8 @@ export interface StateInterface {
power_state: boolean;
product_name: string;
custom_defines: CustomDefines;
isLedPlayer: () => boolean;
isSpecialVideo: () => boolean;
}
@ -329,6 +340,8 @@ export default store(function (/* { ssrContext } */) {
power_state: false,
product_name: EProductNames.LED_PLAYER,
custom_defines: new CustomDefines(),
isLedPlayer: () => {
return (
Store.state.product_name == EProductNames.LED_PLAYER ||
@ -365,6 +378,17 @@ export default store(function (/* { ssrContext } */) {
const num = parseInt(playload);
if (!isNaN(num) && num >= 0) {
state.device_attribute = num;
// parse custom attributes
state.custom_defines.is_custom_isv =
(state.device_attribute & EDeviceAttribute.CustomISV) != 0;
state.custom_defines.function_output_board =
(state.device_attribute & EDeviceAttribute.OutputBoard) != 0;
state.custom_defines.function_center_control =
(state.device_attribute & EDeviceAttribute.CenterControl) != 0;
state.custom_defines.function_mirroring_output =
(state.device_attribute & EDeviceAttribute.MirroringOutput) != 0;
}
},
setAvancedDebug(state: StateInterface, playload?: any) {