添加窗口显示。宫格行啊是

This commit is contained in:
fangxiang 2021-08-05 17:26:27 +08:00
parent ff030f8b6b
commit be4f57530e
9 changed files with 262 additions and 162 deletions

View File

@ -6,7 +6,7 @@
// Configuration for your app // Configuration for your app
// https://v2.quasar.dev/quasar-cli/quasar-conf-js // https://v2.quasar.dev/quasar-cli/quasar-conf-js
const { configure } = require('quasar/wrappers'); const { configure } = require("quasar/wrappers");
module.exports = configure(function (ctx) { module.exports = configure(function (ctx) {
return { return {
@ -19,15 +19,10 @@ module.exports = configure(function (ctx) {
// app boot file (/src/boot) // app boot file (/src/boot)
// --> boot files are part of "main.js" // --> boot files are part of "main.js"
// https://v2.quasar.dev/quasar-cli/boot-files // https://v2.quasar.dev/quasar-cli/boot-files
boot: [ boot: ["i18n", "axios"],
'i18n',
'axios',
],
// https://v2.quasar.dev/quasar-cli/quasar-conf-js#Property%3A-css // https://v2.quasar.dev/quasar-cli/quasar-conf-js#Property%3A-css
css: [ css: ["app.scss"],
'app.scss'
],
// https://github.com/quasarframework/quasar/tree/dev/extras // https://github.com/quasarframework/quasar/tree/dev/extras
extras: [ extras: [
@ -39,13 +34,13 @@ module.exports = configure(function (ctx) {
// 'line-awesome', // 'line-awesome',
// 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both! // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
'roboto-font', // optional, you are not bound to it "roboto-font", // optional, you are not bound to it
'material-icons', // optional, you are not bound to it "material-icons", // optional, you are not bound to it
], ],
// Full list of options: https://v2.quasar.dev/quasar-cli/quasar-conf-js#Property%3A-build // Full list of options: https://v2.quasar.dev/quasar-cli/quasar-conf-js#Property%3A-build
build: { build: {
vueRouterMode: 'hash', // available values: 'hash', 'history' vueRouterMode: "hash", // available values: 'hash', 'history'
// transpile: false, // transpile: false,
@ -65,7 +60,7 @@ module.exports = configure(function (ctx) {
// https://v2.quasar.dev/quasar-cli/handling-webpack // https://v2.quasar.dev/quasar-cli/handling-webpack
// "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain // "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain
chainWebpack (/* chain */) { chainWebpack(/* chain */) {
// //
}, },
}, },
@ -74,7 +69,7 @@ module.exports = configure(function (ctx) {
devServer: { devServer: {
https: false, https: false,
port: 8080, port: 8080,
open: true // opens browser window automatically open: true, // opens browser window automatically
}, },
// https://v2.quasar.dev/quasar-cli/quasar-conf-js#Property%3A-framework // https://v2.quasar.dev/quasar-cli/quasar-conf-js#Property%3A-framework
@ -92,7 +87,7 @@ module.exports = configure(function (ctx) {
// directives: [], // directives: [],
// Quasar plugins // Quasar plugins
plugins: [] plugins: ["Notify", "Dialog", "Loading", "AppFullscreen"],
}, },
// animations: 'all', // --- includes all animations // animations: 'all', // --- includes all animations
@ -112,24 +107,24 @@ module.exports = configure(function (ctx) {
maxAge: 1000 * 60 * 60 * 24 * 30, maxAge: 1000 * 60 * 60 * 24 * 30,
// Tell browser when a file from the server should expire from cache (in ms) // Tell browser when a file from the server should expire from cache (in ms)
chainWebpackWebserver (/* chain */) { chainWebpackWebserver(/* chain */) {
// //
}, },
middlewares: [ middlewares: [
ctx.prod ? 'compression' : '', ctx.prod ? "compression" : "",
'render' // keep this as last one "render", // keep this as last one
] ],
}, },
// https://v2.quasar.dev/quasar-cli/developing-pwa/configuring-pwa // https://v2.quasar.dev/quasar-cli/developing-pwa/configuring-pwa
pwa: { pwa: {
workboxPluginMode: 'GenerateSW', // 'GenerateSW' or 'InjectManifest' workboxPluginMode: "GenerateSW", // 'GenerateSW' or 'InjectManifest'
workboxOptions: {}, // only for GenerateSW workboxOptions: {}, // only for GenerateSW
// for the custom service worker ONLY (/src-pwa/custom-service-worker.[js|ts]) // for the custom service worker ONLY (/src-pwa/custom-service-worker.[js|ts])
// if using workbox in InjectManifest mode // if using workbox in InjectManifest mode
chainWebpackCustomSW (/* chain */) { chainWebpackCustomSW(/* chain */) {
// //
}, },
@ -137,38 +132,38 @@ module.exports = configure(function (ctx) {
name: `Quasar App`, name: `Quasar App`,
short_name: `Quasar App`, short_name: `Quasar App`,
description: `A Quasar Framework app`, description: `A Quasar Framework app`,
display: 'standalone', display: "standalone",
orientation: 'portrait', orientation: "portrait",
background_color: '#ffffff', background_color: "#ffffff",
theme_color: '#027be3', theme_color: "#027be3",
icons: [ icons: [
{ {
src: 'icons/icon-128x128.png', src: "icons/icon-128x128.png",
sizes: '128x128', sizes: "128x128",
type: 'image/png' type: "image/png",
}, },
{ {
src: 'icons/icon-192x192.png', src: "icons/icon-192x192.png",
sizes: '192x192', sizes: "192x192",
type: 'image/png' type: "image/png",
}, },
{ {
src: 'icons/icon-256x256.png', src: "icons/icon-256x256.png",
sizes: '256x256', sizes: "256x256",
type: 'image/png' type: "image/png",
}, },
{ {
src: 'icons/icon-384x384.png', src: "icons/icon-384x384.png",
sizes: '384x384', sizes: "384x384",
type: 'image/png' type: "image/png",
}, },
{ {
src: 'icons/icon-512x512.png', src: "icons/icon-512x512.png",
sizes: '512x512', sizes: "512x512",
type: 'image/png' type: "image/png",
} },
] ],
} },
}, },
// Full list of options: https://v2.quasar.dev/quasar-cli/developing-cordova-apps/configuring-cordova // Full list of options: https://v2.quasar.dev/quasar-cli/developing-cordova-apps/configuring-cordova
@ -178,22 +173,20 @@ module.exports = configure(function (ctx) {
// Full list of options: https://v2.quasar.dev/quasar-cli/developing-capacitor-apps/configuring-capacitor // Full list of options: https://v2.quasar.dev/quasar-cli/developing-capacitor-apps/configuring-capacitor
capacitor: { capacitor: {
hideSplashscreen: true hideSplashscreen: true,
}, },
// Full list of options: https://v2.quasar.dev/quasar-cli/developing-electron-apps/configuring-electron // Full list of options: https://v2.quasar.dev/quasar-cli/developing-electron-apps/configuring-electron
electron: { electron: {
bundler: 'packager', // 'packager' or 'builder' bundler: "packager", // 'packager' or 'builder'
packager: { packager: {
// https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
// OS X / Mac App Store // OS X / Mac App Store
// appBundleId: '', // appBundleId: '',
// appCategoryType: '', // appCategoryType: '',
// osxSign: '', // osxSign: '',
// protocol: 'myapp://path', // protocol: 'myapp://path',
// Windows only // Windows only
// win32metadata: { ... } // win32metadata: { ... }
}, },
@ -201,20 +194,20 @@ module.exports = configure(function (ctx) {
builder: { builder: {
// https://www.electron.build/configuration/configuration // https://www.electron.build/configuration/configuration
appId: 'media_player_web' appId: "media_player_web",
}, },
// "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain // "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain
chainWebpack (/* chain */) { chainWebpack(/* chain */) {
// do something with the Electron main process Webpack cfg // do something with the Electron main process Webpack cfg
// extendWebpackMain also available besides this chainWebpackMain // extendWebpackMain also available besides this chainWebpackMain
}, },
// "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain // "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain
chainWebpackPreload (/* chain */) { chainWebpackPreload(/* chain */) {
// do something with the Electron main process Webpack cfg // do something with the Electron main process Webpack cfg
// extendWebpackPreload also available besides this chainWebpackPreload // extendWebpackPreload also available besides this chainWebpackPreload
}, },
} },
} };
}); });

View File

@ -3,15 +3,19 @@
<router-view /> <router-view />
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from "vue";
import { useI18n } from 'vue-i18n'; import { useI18n } from "vue-i18n";
import EventBus from "src/common/EventBus";
export default defineComponent({ export default defineComponent({
name: 'App', name: "App",
setup() { setup() {
const $t = useI18n(); const $t = useI18n();
document.title = $t.t('title'); document.title = $t.t("title");
window.onresize = (evt: any) =>
EventBus.getInstance().emit("windowResize", evt);
return {}; return {};
}, },
}); });

View File

@ -144,11 +144,10 @@ export default class ClientConnection {
} }
} }
public async doRpc(reuqest: any | Protocol.PacketEntity) { public async doRpc<_RequestType extends Protocol.PacketEntity, _ResponseType>(
RequestType: new () => _RequestType
): Promise<_ResponseType | null> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!reuqest) {
reject();
}
const rpc_id = ++this._rpc_id_counter; const rpc_id = ++this._rpc_id_counter;
if (this.rpc_map.has(rpc_id)) { if (this.rpc_map.has(rpc_id)) {
const f = this.rpc_map.get(rpc_id); const f = this.rpc_map.get(rpc_id);
@ -157,8 +156,9 @@ export default class ClientConnection {
this.rpc_map.delete(rpc_id); this.rpc_map.delete(rpc_id);
} }
} }
reuqest.rpc_id = rpc_id; let request: _RequestType = new RequestType();
this.ws?.send(JSON.stringify(reuqest)); request.rpc_id = rpc_id;
this.ws?.send(JSON.stringify(request));
this.rpc_map.set( this.rpc_map.set(
rpc_id, rpc_id,
(is_fail: boolean, packet: Protocol.Commands, data: string) => { (is_fail: boolean, packet: Protocol.Commands, data: string) => {
@ -166,9 +166,7 @@ export default class ClientConnection {
reject(); reject();
} else { } else {
try { try {
const response = JSON.parse( const response = JSON.parse(data) as _ResponseType;
data
) as Protocol.GetApplicationConfigResponseEntity;
if (response) { if (response) {
resolve(response); resolve(response);
} else { } else {
@ -186,49 +184,33 @@ export default class ClientConnection {
} }
public async getSignalSources() { public async getSignalSources() {
return new Promise((resolve, reject) => {
const rpc_id = ++this._rpc_id_counter;
if (this.rpc_map.has(rpc_id)) {
const f = this.rpc_map.get(rpc_id);
if (f && typeof f == "function") {
f(true, new Protocol.Commands(), "");
this.rpc_map.delete(rpc_id);
}
}
this.ws?.send(
JSON.stringify(new Protocol.GetSignalSourcesRequest(rpc_id))
);
this.rpc_map.set(
rpc_id,
(is_fail: boolean, packet: Protocol.Commands, data: string) => {
if (is_fail) {
reject();
} else {
try { try {
const response = JSON.parse( return await this.doRpc<
data Protocol.GetSignalSourcesRequest,
) as Protocol.GetSignalSourcesResponse; Protocol.GetSignalSourcesResponse
if (response) { >(Protocol.GetSignalSourcesRequest);
resolve(response); } catch (e) {
} else { console.error(e);
reject();
console.log("reject");
} }
} catch {
reject();
console.log("reject");
}
}
}
);
});
} }
public async getApplicationSettins() { public async getApplicationSettins() {
try { try {
return (await this.doRpc( return await this.doRpc<
new Protocol.GetApplicationConfigRequestEntity() Protocol.GetApplicationConfigRequestEntity,
)) as Protocol.GetApplicationConfigResponseEntity; Protocol.GetApplicationConfigResponseEntity
>(Protocol.GetApplicationConfigRequestEntity);
} catch (e) {
console.error(e);
}
}
public async getWindows() {
try {
return await this.doRpc<
Protocol.GetWindowsRequestEntity,
Protocol.GetWindowsResponseEntity
>(Protocol.GetWindowsRequestEntity);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }

12
src/common/EventBus.ts Normal file
View File

@ -0,0 +1,12 @@
import EventEmitter from "events";
export default class EventBus extends EventEmitter {
private static _instance: EventBus | null = null;
public static getInstance() {
if (!EventBus._instance) {
EventBus._instance = new EventBus();
}
return EventBus._instance;
}
}

View File

@ -1,5 +1,6 @@
import { SignalSourceEntity } from "./SignalSourceEntity"; import { SignalSourceEntity } from "./SignalSourceEntity";
import ApplicationConfigEntity from "./ApplicationConfigEntity"; import ApplicationConfigEntity from "./ApplicationConfigEntity";
import MultimediaWindowEntity from "./MultimediaWindowEntity";
export namespace Protocol { export namespace Protocol {
export class Commands { export class Commands {
@ -139,4 +140,23 @@ export namespace Protocol {
this.command = Commands.kRpcGetApplicationConfig; this.command = Commands.kRpcGetApplicationConfig;
} }
} }
export class GetWindowsRequestEntity extends PacketEntity {
timestamp: number = new Date().getMilliseconds();
constructor(rpcid?: number) {
super();
this.rpc_id = rpcid ?? 0;
this.command = Commands.kRpcGetWindows;
}
}
export class GetWindowsResponseEntity extends PacketEntity {
windows: MultimediaWindowEntity[] = [];
constructor() {
super();
this.command = Commands.kRpcGetWindows;
}
}
} }

View File

@ -2,21 +2,25 @@
// so you can safely delete all default props below // so you can safely delete all default props below
export default { export default {
title: 'MediaPlayerWebAPP', title: "MediaPlayerWebAPP",
failed: '失败', failed: "失败",
success: '成功', success: "成功",
login: '登录', login: "登录",
'user name': '用户名', "user name": "用户名",
password: '密码', password: "密码",
'please input user name': '请输入用户', "please input user name": "请输入用户",
'please input password': '请输入密码', "please input password": "请输入密码",
'login fail!': '登陆失败', "login fail!": "登陆失败",
'Please type something': '请输入内容', "Please type something": "请输入内容",
'grid setting': '宫格设置', "grid setting": "宫格设置",
'background image': '底图设置', "background image": "底图设置",
'data import': '数据导入', "data import": "数据导入",
'data export': '数据导出', "data export": "数据导出",
root: '根节点', root: "根节点",
"close all windwos": "关闭所有窗口",
"close this window": "关闭当前窗口",
"close other windows": "关闭其它窗口",
}; };

View File

@ -7,6 +7,7 @@
icon="img:icons/favicon-32x32.png" icon="img:icons/favicon-32x32.png"
label="电视机拼接盒" label="电视机拼接盒"
class="q-mr-sm" class="q-mr-sm"
@click="test"
/> />
<q-separator vertical inset /> <q-separator vertical inset />
<q-btn <q-btn
@ -60,15 +61,22 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from "vue";
import { useStore } from "src/store";
export default defineComponent({ export default defineComponent({
name: 'PageTopToolBar', name: "PageTopToolBar",
components: {}, components: {},
setup() { setup() {
return {}; let $store = useStore();
return {
test() {
$store.commit("setWallCol", 2);
},
};
}, },
}); });
</script> </script>

View File

@ -18,6 +18,24 @@
</q-item> </q-item>
</q-list> </q-list>
</q-popup-proxy> </q-popup-proxy>
<div id="windows" style="position: absolute">
<div
:ref="'window_' + index"
v-for="(item, index) in windows"
:key="index"
class="window"
:style="{
top: item.y / wall_height_scaler + 'px',
left: item.x / wall_width_scaler + 'px',
width: item.width / wall_width_scaler + 'px',
height: item.height / wall_height_scaler + 'px',
}"
>
{{ wall_height_scaler }}
{{ wall_width_scaler }}
</div>
</div>
<div ref="wall_grids">
<div <div
v-for="row in wall_rows" v-for="row in wall_rows"
:key="row" :key="row"
@ -35,6 +53,7 @@
width: item_witdh + 'px', width: item_witdh + 'px',
height: item_height + 'px', height: item_height + 'px',
}" }"
@resize="(evt) => loga(evt)"
> >
<q-popup-proxy context-menu no-parent-event> <q-popup-proxy context-menu no-parent-event>
<q-popup-proxy context-menu /> <q-popup-proxy context-menu />
@ -49,7 +68,9 @@
<q-item-section avatar> <q-item-section avatar>
<q-icon name="close" color="red" /> <q-icon name="close" color="red" />
</q-item-section> </q-item-section>
<q-item-section> {{ $t("close other windwos") }} </q-item-section> <q-item-section>
{{ $t("close other windwos") }}
</q-item-section>
</q-item> </q-item>
<q-item clickable v-close-popup> <q-item clickable v-close-popup>
<q-item-section avatar> <q-item-section avatar>
@ -62,6 +83,7 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</template> </template>
<style scoped> <style scoped>
@ -72,16 +94,22 @@
.wall_item { .wall_item {
border: 1px solid black; border: 1px solid black;
} }
.window {
border: 1px solid rebeccapurple;
position: absolute;
}
</style> </style>
<script lang="ts"> <script lang="ts">
import GlobalData from "src/common/GlobalData"; import GlobalData from "src/common/GlobalData";
import { defineComponent, ref, Ref, computed } from "vue"; import { defineComponent, ref, Ref, computed, watch, onMounted } from "vue";
import { Common } from "src/common/Common"; import { Common } from "src/common/Common";
import { Protocol } from "src/entities/WSProtocol"; import { Protocol } from "src/entities/WSProtocol";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useStore } from "src/store"; import { useStore } from "src/store";
import EventBus from "src/common/EventBus";
interface _OptionsType { interface _OptionsType {
$t: any; $t: any;
@ -131,6 +159,15 @@ const _getApplicationConfig = async (options: _OptionsType) => {
} }
}; };
const _getWindows = async (options: _OptionsType) => {
const global_data = GlobalData.getInstance();
let windows = (await global_data.getCurrentClient()?.getWindows())?.windows;
let $store = options.$store;
if (windows && $store) {
$store.commit("setWindows", windows);
}
};
const _initialize = async (options: _OptionsType) => { const _initialize = async (options: _OptionsType) => {
const global_data = GlobalData.getInstance(); const global_data = GlobalData.getInstance();
let client = global_data.getCurrentClient(); let client = global_data.getCurrentClient();
@ -139,7 +176,9 @@ const _initialize = async (options: _OptionsType) => {
await Common.waitFor(100); await Common.waitFor(100);
} }
_initSignalSourceTree(options); _initSignalSourceTree(options).then(() => {
_getWindows(options);
});
_getApplicationConfig(options); _getApplicationConfig(options);
} }
}; };
@ -157,6 +196,11 @@ export default defineComponent({
$store, $store,
}); });
const windows = computed({
get: () => $store.state.windows,
set: (val) => $store.commit("setWindows", val),
});
const wall_rows = computed({ const wall_rows = computed({
get: () => $store.state.wall_row, get: () => $store.state.wall_row,
set: (val) => $store.commit("setWallRow", val), set: (val) => $store.commit("setWallRow", val),
@ -195,13 +239,40 @@ export default defineComponent({
set: (val) => $store.commit("setWallCol", val), set: (val) => $store.commit("setWallCol", val),
}); });
const wall_width_scaler = ref(0);
const wall_height_scaler = ref(0);
const calcWallVWScaler = () => {
if (wall.value && wall.value.parentElement) {
wall_height_scaler.value =
$store.state.device_screen_height /
(item_height.value * wall_rows.value);
wall_width_scaler.value =
$store.state.device_screen_width /
wall.value.parentElement.offsetWidth;
wall_height_scaler.value += 0.1;
wall_width_scaler.value += 0.05;
}
};
EventBus.getInstance().on("windowResize", () => {
calcWallVWScaler();
});
onMounted(() => {
calcWallVWScaler();
});
return { return {
windows,
wall, wall,
wall_rows, wall_rows,
wall_cols, wall_cols,
item_witdh, item_witdh,
item_height, item_height,
wall_width_scaler,
wall_height_scaler,
loga(a: any) { loga(a: any) {
console.log(a); console.log(a);
}, },

View File

@ -62,6 +62,12 @@ export default store(function (/* { ssrContext } */) {
}, },
mutations: { mutations: {
setWindows(state: StateInterface, playload?: any) {
let windows = playload as MultimediaWindowEntity[];
if (windows) {
state.windows = windows;
}
},
clearWindows(state: StateInterface, playload?: any) { clearWindows(state: StateInterface, playload?: any) {
state.windows = []; state.windows = [];
}, },