update dbmData and Subscription so it is reactive
new save load scenes
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "lightcontrol",
|
"name": "lightcontrol",
|
||||||
"version": "0.0.4",
|
"version": "0.0.14",
|
||||||
"description": "A Quasar Project",
|
"description": "A Tecamino App",
|
||||||
"productName": "Light Control",
|
"productName": "Light Control",
|
||||||
"author": "A. Zuercher",
|
"author": "A. Zuercher",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
no-transition
|
no-transition
|
||||||
:default-expand-all="false"
|
:default-expand-all="false"
|
||||||
v-model:expanded="expanded"
|
v-model:expanded="expanded"
|
||||||
|
@update:expanded="onExpandedChange"
|
||||||
@lazy-load="onLazyLoad"
|
@lazy-load="onLazyLoad"
|
||||||
>
|
>
|
||||||
<template v-slot:[`default-header`]="props">
|
<template v-slot:[`default-header`]="props">
|
||||||
@@ -52,7 +53,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { watch, onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import DataTable from './dataTable.vue';
|
import DataTable from './dataTable.vue';
|
||||||
import type { TreeNode } from 'src/composables/dbm/dbmTree';
|
import type { TreeNode } from 'src/composables/dbm/dbmTree';
|
||||||
import {
|
import {
|
||||||
@@ -60,7 +61,6 @@ import {
|
|||||||
buildTree,
|
buildTree,
|
||||||
getSubscriptionsByUuid,
|
getSubscriptionsByUuid,
|
||||||
addChildrentoTree,
|
addChildrentoTree,
|
||||||
removeSubtreeByParentKey,
|
|
||||||
getAllSubscriptions,
|
getAllSubscriptions,
|
||||||
} from 'src/composables/dbm/dbmTree';
|
} from 'src/composables/dbm/dbmTree';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
@@ -72,12 +72,14 @@ import { subscribe, unsubscribe, setValues } from 'src/services/websocket';
|
|||||||
import { onBeforeRouteLeave } from 'vue-router';
|
import { onBeforeRouteLeave } from 'vue-router';
|
||||||
import { api } from 'boot/axios';
|
import { api } from 'boot/axios';
|
||||||
import type { Subs } from 'src/models/Subscribe';
|
import type { Subs } from 'src/models/Subscribe';
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const expanded = ref<string[]>([]);
|
const expanded = ref<string[]>([]);
|
||||||
const selectedNode = ref<TreeNode | null>(null);
|
const selectedNode = ref<TreeNode | null>(null);
|
||||||
const ZERO_UUID = '00000000-0000-0000-0000-000000000000';
|
const ZERO_UUID = '00000000-0000-0000-0000-000000000000';
|
||||||
const Subscriptions = ref<Subs>([]);
|
const Subscriptions = reactive<Subs>([]);
|
||||||
|
let lastExpanded: string[] = [];
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const payload = {
|
const payload = {
|
||||||
@@ -92,7 +94,7 @@ onMounted(() => {
|
|||||||
.post('/json_data', payload)
|
.post('/json_data', payload)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.data.get) {
|
if (res.data.get) {
|
||||||
dbmData.value = buildTree(res.data.get);
|
dbmData.splice(0, dbmData.length, ...buildTree(res.data.get));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
@@ -121,7 +123,6 @@ function onLazyLoad({
|
|||||||
fail: () => void;
|
fail: () => void;
|
||||||
}): void {
|
}): void {
|
||||||
//first unsubsrice nodes
|
//first unsubsrice nodes
|
||||||
|
|
||||||
unsubscribe([
|
unsubscribe([
|
||||||
{
|
{
|
||||||
path: '.*',
|
path: '.*',
|
||||||
@@ -129,7 +130,7 @@ function onLazyLoad({
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
Subscriptions.value = [];
|
Subscriptions.length = 0;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
NotifyResponse($q, err, 'error');
|
NotifyResponse($q, err, 'error');
|
||||||
@@ -152,9 +153,13 @@ function onLazyLoad({
|
|||||||
resp.subscribe.filter((sub) => sub.uuid !== ZERO_UUID).map((sub) => sub.uuid),
|
resp.subscribe.filter((sub) => sub.uuid !== ZERO_UUID).map((sub) => sub.uuid),
|
||||||
);
|
);
|
||||||
|
|
||||||
Subscriptions.value = getAllSubscriptions().filter((sub) => toRemove.has(sub.uuid));
|
Subscriptions.splice(
|
||||||
|
0,
|
||||||
|
Subscriptions.length,
|
||||||
|
...getAllSubscriptions().filter((sub) => toRemove.has(sub.uuid)),
|
||||||
|
);
|
||||||
|
|
||||||
done(dbmData.value);
|
done(dbmData);
|
||||||
} else {
|
} else {
|
||||||
done([]); // no children returned
|
done([]); // no children returned
|
||||||
}
|
}
|
||||||
@@ -167,11 +172,12 @@ function onLazyLoad({
|
|||||||
|
|
||||||
function onValueEdit(newValue: undefined, node: TreeNode) {
|
function onValueEdit(newValue: undefined, node: TreeNode) {
|
||||||
console.log(node.value, node.value === undefined);
|
console.log(node.value, node.value === undefined);
|
||||||
|
if (!node.key) return;
|
||||||
const sub = getSubscriptionsByUuid(node.key);
|
const sub = getSubscriptionsByUuid(node.key);
|
||||||
if (sub) {
|
if (sub) {
|
||||||
setValues([
|
setValues([
|
||||||
{
|
{
|
||||||
path: sub.path ?? '',
|
path: sub.value?.path ?? '',
|
||||||
value: newValue,
|
value: newValue,
|
||||||
},
|
},
|
||||||
]).catch((err) => {
|
]).catch((err) => {
|
||||||
@@ -180,37 +186,72 @@ function onValueEdit(newValue: undefined, node: TreeNode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
function onExpandedChange(newExpanded: readonly string[]) {
|
||||||
expanded,
|
const collapsed = lastExpanded.filter((k) => !newExpanded.includes(k));
|
||||||
(newVal, oldVal) => {
|
const newlyExpanded = newExpanded.filter((k) => !lastExpanded.includes(k));
|
||||||
const collapsedKeys = oldVal.filter((key) => !newVal.includes(key));
|
|
||||||
collapsedKeys.forEach((key: string) => {
|
if (collapsed.length) {
|
||||||
// WebSocket unsubscribe
|
collapsed.forEach((key: string) => {
|
||||||
unsubscribe([
|
subscribe([
|
||||||
{
|
{
|
||||||
uuid: key,
|
uuid: key,
|
||||||
path: '.*',
|
path: '',
|
||||||
depth: 0,
|
depth: 2,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
// Remove children of this node from the tree
|
if (resp?.subscribe) {
|
||||||
removeSubtreeByParentKey(key);
|
// Optional: update your internal store too
|
||||||
if (resp?.unsubscribe) {
|
addChildrentoTree(resp?.subscribe);
|
||||||
|
|
||||||
const toRemove = new Set(
|
const toRemove = new Set(
|
||||||
resp.unsubscribe.filter((sub) => sub.uuid !== ZERO_UUID).map((sub) => sub.uuid),
|
resp.subscribe.filter((sub) => sub.uuid !== ZERO_UUID).map((sub) => sub.uuid),
|
||||||
);
|
);
|
||||||
|
|
||||||
Subscriptions.value = Subscriptions.value.filter((sub) => !toRemove.has(sub.uuid));
|
Subscriptions.splice(
|
||||||
|
0,
|
||||||
|
Subscriptions.length,
|
||||||
|
...getAllSubscriptions().filter((sub) => toRemove.has(sub.uuid)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
NotifyResponse($q, err, 'error');
|
NotifyResponse($q, err, 'error');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
} else if (newlyExpanded.length) {
|
||||||
|
newlyExpanded.forEach((key: string) => {
|
||||||
|
subscribe([
|
||||||
|
{
|
||||||
|
uuid: key,
|
||||||
|
path: '',
|
||||||
|
depth: 2,
|
||||||
},
|
},
|
||||||
{ deep: false },
|
])
|
||||||
);
|
.then((resp) => {
|
||||||
|
if (resp?.subscribe) {
|
||||||
|
// Optional: update your internal store too
|
||||||
|
addChildrentoTree(resp?.subscribe);
|
||||||
|
|
||||||
|
const toRemove = new Set(
|
||||||
|
resp.subscribe.filter((sub) => sub.uuid !== ZERO_UUID).map((sub) => sub.uuid),
|
||||||
|
);
|
||||||
|
|
||||||
|
Subscriptions.splice(
|
||||||
|
0,
|
||||||
|
Subscriptions.length,
|
||||||
|
...getAllSubscriptions().filter((sub) => toRemove.has(sub.uuid)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
NotifyResponse($q, err, 'error');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
lastExpanded = [...newExpanded];
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="q-pa-md">
|
<div class="q-pa-md">
|
||||||
<q-table
|
<q-table
|
||||||
v-if="props.rows.length > 0"
|
v-if="tableRows.length > 0"
|
||||||
style="height: 600px"
|
style="height: 600px"
|
||||||
flat
|
flat
|
||||||
bordered
|
bordered
|
||||||
:title="props.rows[0]?.path"
|
:title="props.rows[0]?.path"
|
||||||
:rows="props.rows ?? []"
|
:rows="rows"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
row-key="path"
|
row-key="path"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
@@ -17,13 +17,16 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { QTableProps } from 'quasar';
|
import type { QTableProps } from 'quasar';
|
||||||
import type { Subscribe } from 'src/models/Subscribe';
|
import type { Subs } from 'src/models/Subscribe';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
// we generate lots of rows here
|
// we generate lots of rows here
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
rows: Subscribe[];
|
rows: Subs;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const tableRows = computed(() => [...props.rows]);
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ name: 'path', label: 'Path', field: 'path', align: 'left' },
|
{ name: 'path', label: 'Path', field: 'path', align: 'left' },
|
||||||
{
|
{
|
||||||
|
89
src/components/dialog/okDialog.vue
Normal file
89
src/components/dialog/okDialog.vue
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<template>
|
||||||
|
<q-dialog v-model="internalShowDialog">
|
||||||
|
<q-card :style="'width:' + props.width">
|
||||||
|
<q-card-section
|
||||||
|
v-if="props.dialogLabel"
|
||||||
|
class="text-h6 text-center"
|
||||||
|
:class="'text-' + props.labelColor"
|
||||||
|
>{{ props.dialogLabel }}</q-card-section
|
||||||
|
>
|
||||||
|
<q-card-section v-if="props.text" class="text-center" style="white-space: pre-line">{{
|
||||||
|
props.text
|
||||||
|
}}</q-card-section>
|
||||||
|
<q-card-actions align="right" class="text-primary">
|
||||||
|
<q-btn v-if="props.buttonCancelLabel" flat :label="props.buttonCancelLabel" v-close-popup>
|
||||||
|
</q-btn>
|
||||||
|
<q-btn
|
||||||
|
v-if="props.buttonOkLabel"
|
||||||
|
flat
|
||||||
|
:label="props.buttonOkLabel"
|
||||||
|
v-close-popup
|
||||||
|
@click="closeDialog"
|
||||||
|
>
|
||||||
|
</q-btn>
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
showDialog: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
buttonOkLabel: {
|
||||||
|
type: String,
|
||||||
|
default: 'OK',
|
||||||
|
},
|
||||||
|
labelColor: {
|
||||||
|
type: String,
|
||||||
|
default: 'primary',
|
||||||
|
},
|
||||||
|
dialogLabel: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
buttonCancelLabel: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: String,
|
||||||
|
default: '300px',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:showDialog', 'confirmed', 'cancel']);
|
||||||
|
const internalShowDialog = ref(props.showDialog);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.showDialog,
|
||||||
|
(newValue) => {
|
||||||
|
console.log('watch showDialog', newValue);
|
||||||
|
internalShowDialog.value = newValue;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
watch(internalShowDialog, (newValue) => {
|
||||||
|
console.log('watch internalShowDialog', newValue);
|
||||||
|
emit('update:showDialog', newValue);
|
||||||
|
if (!newValue) {
|
||||||
|
console.log('emit cancel');
|
||||||
|
emit('cancel');
|
||||||
|
} else {
|
||||||
|
console.log('emit confirmed');
|
||||||
|
emit('confirmed');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function closeDialog() {
|
||||||
|
internalShowDialog.value = false;
|
||||||
|
emit('update:showDialog', false);
|
||||||
|
}
|
||||||
|
</script>
|
@@ -18,7 +18,7 @@
|
|||||||
reverse
|
reverse
|
||||||
v-model="light.Brightness"
|
v-model="light.Brightness"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="100"
|
:max="255"
|
||||||
:step="1"
|
:step="1"
|
||||||
color="black"
|
color="black"
|
||||||
style="opacity: 0.5"
|
style="opacity: 0.5"
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
reverse
|
reverse
|
||||||
v-model="light.Red"
|
v-model="light.Red"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="100"
|
:max="255"
|
||||||
:step="1"
|
:step="1"
|
||||||
label
|
label
|
||||||
color="red"
|
color="red"
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
reverse
|
reverse
|
||||||
v-model="light.Green"
|
v-model="light.Green"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="100"
|
:max="255"
|
||||||
:step="1"
|
:step="1"
|
||||||
label
|
label
|
||||||
color="green"
|
color="green"
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
reverse
|
reverse
|
||||||
v-model="light.Blue"
|
v-model="light.Blue"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="100"
|
:max="255"
|
||||||
:step="1"
|
:step="1"
|
||||||
label
|
label
|
||||||
color="blue"
|
color="blue"
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
reverse
|
reverse
|
||||||
v-model="light.White"
|
v-model="light.White"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="100"
|
:max="255"
|
||||||
:step="1"
|
:step="1"
|
||||||
label
|
label
|
||||||
color="grey"
|
color="grey"
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
reverse
|
reverse
|
||||||
v-model="light.Amber"
|
v-model="light.Amber"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="100"
|
:max="255"
|
||||||
:step="1"
|
:step="1"
|
||||||
label
|
label
|
||||||
color="amber"
|
color="amber"
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
reverse
|
reverse
|
||||||
v-model="light.Purple"
|
v-model="light.Purple"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="100"
|
:max="255"
|
||||||
:step="1"
|
:step="1"
|
||||||
label
|
label
|
||||||
color="purple"
|
color="purple"
|
||||||
|
@@ -3,22 +3,35 @@
|
|||||||
<div class="row q-ma-xs">
|
<div class="row q-ma-xs">
|
||||||
<div class="column items-center q-mr-md" :style="{ height: containerSize + 'px' }">
|
<div class="column items-center q-mr-md" :style="{ height: containerSize + 'px' }">
|
||||||
<div class="column justify-between items-center" :style="{ height: containerSize + 'px' }">
|
<div class="column justify-between items-center" :style="{ height: containerSize + 'px' }">
|
||||||
<q-item-label class="text-black text-bold q-mb-none">Tilt</q-item-label>
|
<q-item-label
|
||||||
|
@click="toggleTilt = !toggleTilt"
|
||||||
|
:class="[
|
||||||
|
'cursor-pointer',
|
||||||
|
'text-bold',
|
||||||
|
'clickable-text-effect',
|
||||||
|
'q-mb-none',
|
||||||
|
`text-black`,
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
{{ toggleTilt ? 'Tilt Fine' : 'Tilt' }}</q-item-label
|
||||||
|
>
|
||||||
<q-btn
|
<q-btn
|
||||||
:size="buttonSize"
|
:size="buttonSize"
|
||||||
round
|
round
|
||||||
color="positive"
|
color="positive"
|
||||||
icon="add_circle_outline"
|
icon="add_circle_outline"
|
||||||
class="q-mb-md"
|
class="q-mb-md"
|
||||||
@click="reverseTilt ? substractTiltOne : addTiltOne"
|
@click="reverseTilt ? substractTilt : addTilt"
|
||||||
v-touch-repeat:300:300:300:300:50.mouse="reverseTilt ? substractTiltOne : addTiltOne"
|
v-touch-repeat:0:300:300:300:300:50:50:50:20.mouse="
|
||||||
|
reverseTilt ? substractTilt : addTilt
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
<q-slider
|
<q-slider
|
||||||
vertical
|
vertical
|
||||||
:reverse="!props.reverseTilt"
|
:reverse="!props.reverseTilt"
|
||||||
v-model="tilt"
|
v-model="tilt"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="100"
|
:max="255"
|
||||||
:step="1"
|
:step="1"
|
||||||
label
|
label
|
||||||
class="col"
|
class="col"
|
||||||
@@ -31,9 +44,9 @@
|
|||||||
round
|
round
|
||||||
color="negative"
|
color="negative"
|
||||||
icon="remove_circle_outline"
|
icon="remove_circle_outline"
|
||||||
@click="reverseTilt ? addTiltOne : substractTiltOne"
|
@click="reverseTilt ? addTilt : substractTilt"
|
||||||
v-touch-repeat:300:300:300:300:50:50:50:50:20.mouse="
|
v-touch-repeat:0:300:300:300:300:50:50:50:20.mouse="
|
||||||
reverseTilt ? addTiltOne : substractTiltOne
|
reverseTilt ? addTilt : substractTilt
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,7 +62,11 @@
|
|||||||
>
|
>
|
||||||
<div class="marker" :style="markerStyle" :class="{ crosshair: dragging }"></div>
|
<div class="marker" :style="markerStyle" :class="{ crosshair: dragging }"></div>
|
||||||
</div>
|
</div>
|
||||||
<q-item-label class="q-ma-sm text-black text-bold">Pan</q-item-label>
|
<q-item-label
|
||||||
|
@click="togglePan = !togglePan"
|
||||||
|
:class="['cursor-pointer', 'text-bold', 'clickable-text-effect', 'q-mt-lg', `text-black`]"
|
||||||
|
>{{ togglePan ? 'Pan Fine' : 'Pan' }}</q-item-label
|
||||||
|
>
|
||||||
|
|
||||||
<div class="q-gutter-sm row items-center full-width">
|
<div class="q-gutter-sm row items-center full-width">
|
||||||
<q-btn
|
<q-btn
|
||||||
@@ -58,9 +75,9 @@
|
|||||||
round
|
round
|
||||||
color="negative"
|
color="negative"
|
||||||
icon="remove_circle_outline"
|
icon="remove_circle_outline"
|
||||||
@click="reversePan ? addPanOne : substractPanOne"
|
@click="reversePan ? addPan : substractPan"
|
||||||
v-touch-repeat:300:300:300:300:50:50:50:50:20.mouse="
|
v-touch-repeat:0:300:300:300:300:50:50:50:50:20.mouse="
|
||||||
reversePan ? addPanOne : substractPanOne
|
reversePan ? addPan : substractPan
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<q-slider
|
<q-slider
|
||||||
@@ -68,7 +85,7 @@
|
|||||||
:reverse="props.reversePan"
|
:reverse="props.reversePan"
|
||||||
v-model="pan"
|
v-model="pan"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="100"
|
:max="255"
|
||||||
:step="1"
|
:step="1"
|
||||||
label
|
label
|
||||||
color="black"
|
color="black"
|
||||||
@@ -80,8 +97,8 @@
|
|||||||
round
|
round
|
||||||
color="positive"
|
color="positive"
|
||||||
icon="add_circle_outline"
|
icon="add_circle_outline"
|
||||||
@click="reversePan ? substractPanOne : addPanOne"
|
@click="reversePan ? substractPan : addPan"
|
||||||
v-touch-repeat:300:300:300:300:50.mouse="reversePan ? substractPanOne : addPanOne"
|
v-touch-repeat:0:300:300:300:300:50:50:50:20.mouse="reversePan ? substractPan : addPan"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -90,18 +107,53 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { addOne, substractOne } from 'src/utils/number-helpers';
|
||||||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||||
|
import { updateValue } from 'src/composables/dbm/dbmTree';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
reversePan: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
reverseTilt: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
panPath: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
panPath2: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
tiltPath: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
tiltPath2: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const $q = useQuasar();
|
||||||
|
|
||||||
|
const togglePan = ref(false);
|
||||||
|
const toggleTilt = ref(false);
|
||||||
const pad = ref<HTMLElement | null>(null);
|
const pad = ref<HTMLElement | null>(null);
|
||||||
const dragging = ref(false);
|
const dragging = ref(false);
|
||||||
const containerSize = ref(0);
|
const containerSize = ref(0);
|
||||||
|
|
||||||
const pan = defineModel<number>('pan', { default: 0 });
|
const pan = updateValue(props.panPath, $q, togglePan, props.panPath2);
|
||||||
const tilt = defineModel<number>('tilt', { default: 0 });
|
const tilt = updateValue(props.tiltPath, $q, toggleTilt, props.tiltPath2);
|
||||||
const props = defineProps<{
|
|
||||||
reversePan: boolean;
|
const scaleFactor = computed(() => containerSize.value / 255);
|
||||||
reverseTilt: boolean;
|
// 200px → 2, 400px → 4, etc.
|
||||||
}>();
|
|
||||||
const buttonSize = computed(() => (containerSize.value <= 200 ? 'sm' : 'md'));
|
const buttonSize = computed(() => (containerSize.value <= 200 ? 'sm' : 'md'));
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -120,16 +172,12 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const scaleFactor = computed(() => containerSize.value / 100);
|
|
||||||
// 200px → 2, 400px → 4, etc.
|
|
||||||
|
|
||||||
const markerStyle = computed(() => {
|
const markerStyle = computed(() => {
|
||||||
const scale = scaleFactor.value;
|
const scale = scaleFactor.value;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
position: 'absolute' as const,
|
position: 'absolute' as const,
|
||||||
top: `${scale * (props.reverseTilt ? tilt.value : 100 - tilt.value)}px`,
|
top: `${scale * (props.reverseTilt ? tilt.value : 255 - tilt.value)}px`,
|
||||||
left: `${scale * (props.reversePan ? 100 - pan.value : pan.value)}px`,
|
left: `${scale * (props.reversePan ? 255 - pan.value : pan.value)}px`,
|
||||||
width: '12px',
|
width: '12px',
|
||||||
height: '12px',
|
height: '12px',
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
@@ -191,35 +239,27 @@ function updatePosition(e: MouseEvent | Touch) {
|
|||||||
const newY = Math.min(Math.max(0, e.clientY - rect.top), rect.height);
|
const newY = Math.min(Math.max(0, e.clientY - rect.top), rect.height);
|
||||||
|
|
||||||
pan.value = props.reversePan
|
pan.value = props.reversePan
|
||||||
? Math.round((1 - newX / rect.width) * 100)
|
? Math.round((1 - newX / rect.width) * 255)
|
||||||
: Math.round((newX / rect.width) * 100);
|
: Math.round((newX / rect.width) * 255);
|
||||||
tilt.value = props.reverseTilt
|
tilt.value = props.reverseTilt
|
||||||
? Math.round((newY / rect.height) * 100)
|
? Math.round((newY / rect.height) * 255)
|
||||||
: Math.round(100 - (newY / rect.height) * 100);
|
: Math.round(255 - (newY / rect.height) * 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addTiltOne() {
|
function addTilt() {
|
||||||
if (tilt.value <= 255) {
|
addOne(tilt, 255);
|
||||||
tilt.value++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function substractTiltOne() {
|
function substractTilt() {
|
||||||
if (tilt.value >= 0) {
|
substractOne(tilt, 0);
|
||||||
tilt.value--;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function addPanOne() {
|
function addPan() {
|
||||||
if (pan.value <= 255) {
|
addOne(pan, 255);
|
||||||
pan.value++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function substractPanOne() {
|
function substractPan() {
|
||||||
if (pan.value >= 0) {
|
substractOne(pan, 0);
|
||||||
pan.value--;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@@ -12,46 +12,46 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<LightSlider
|
<LightSlider
|
||||||
title="Dimmer"
|
mainTitle="Dimmer"
|
||||||
:dbm-path="'LightBar:Brightness'"
|
dbm-path="LightBar:Brightness"
|
||||||
:opacity="0.5"
|
:opacity="0.5"
|
||||||
class="q-ma-sm"
|
class="q-ma-sm"
|
||||||
></LightSlider>
|
></LightSlider>
|
||||||
<LightSlider
|
<LightSlider
|
||||||
title="Strobe"
|
mainTitle="Strobe"
|
||||||
:dbm-path="'LightBar:Strobe'"
|
dbm-path="LightBar:Strobe"
|
||||||
:opacity="0.5"
|
:opacity="0.5"
|
||||||
class="q-ma-sm"
|
class="q-ma-sm"
|
||||||
></LightSlider>
|
></LightSlider>
|
||||||
<LightSlider
|
<LightSlider
|
||||||
title="Program"
|
mainTitle="Program"
|
||||||
:dbm-path="'LightBar:Program'"
|
dbm-path="LightBar:Program"
|
||||||
:opacity="0.5"
|
:opacity="0.5"
|
||||||
class="q-ma-sm"
|
class="q-ma-sm"
|
||||||
></LightSlider>
|
></LightSlider>
|
||||||
<LightSlider
|
<LightSlider
|
||||||
title="Program Speed"
|
mainTitle="Program Speed"
|
||||||
:dbm-path="'LightBar:Program:Speed'"
|
dbm-path="LightBar:Program:Speed"
|
||||||
:opacity="0.8"
|
:opacity="0.8"
|
||||||
class="q-ma-sm"
|
class="q-ma-sm"
|
||||||
></LightSlider>
|
></LightSlider>
|
||||||
<LightSlider
|
<LightSlider
|
||||||
title="Red"
|
mainTitle="Red"
|
||||||
:dbm-path="'LightBar:Red'"
|
dbm-path="LightBar:Red"
|
||||||
color="red"
|
color="red"
|
||||||
:opacity="0.8"
|
:opacity="0.8"
|
||||||
class="q-ma-sm"
|
class="q-ma-sm"
|
||||||
></LightSlider>
|
></LightSlider>
|
||||||
<LightSlider
|
<LightSlider
|
||||||
title="Green"
|
mainTitle="Green"
|
||||||
:dbm-path="'LightBar:Green'"
|
dbm-path="LightBar:Green"
|
||||||
:opacity="0.8"
|
:opacity="0.8"
|
||||||
color="green"
|
color="green"
|
||||||
class="q-ma-sm"
|
class="q-ma-sm"
|
||||||
></LightSlider>
|
></LightSlider>
|
||||||
<LightSlider
|
<LightSlider
|
||||||
title="Blue"
|
mainTitle="Blue"
|
||||||
:dbm-path="'LightBar:Blue'"
|
dbm-path="LightBar:Blue"
|
||||||
:opacity="0.8"
|
:opacity="0.8"
|
||||||
color="blue"
|
color="blue"
|
||||||
class="q-ma-sm"
|
class="q-ma-sm"
|
||||||
@@ -67,12 +67,12 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
|
import LightSlider from './LightSlider.vue';
|
||||||
import { ref, onMounted, onUnmounted } from 'vue';
|
import { ref, onMounted, onUnmounted } from 'vue';
|
||||||
import { subscribe, unsubscribe } from 'src/services/websocket';
|
import { subscribe, unsubscribe } from 'src/services/websocket';
|
||||||
import SettingDialog from 'src/components/lights/SettingDomeLight.vue';
|
import SettingDialog from 'src/components/lights/SettingDomeLight.vue';
|
||||||
import { NotifyResponse } from 'src/composables/notify';
|
import { NotifyResponse } from 'src/composables/notify';
|
||||||
import { updateValue, buildTree, dbmData } from 'src/composables/dbm/dbmTree';
|
import { updateValue, buildTree, dbmData } from 'src/composables/dbm/dbmTree';
|
||||||
import LightSlider from './LightSlider.vue';
|
|
||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const settings = ref(false);
|
const settings = ref(false);
|
||||||
@@ -87,7 +87,7 @@ onMounted(() => {
|
|||||||
])
|
])
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response?.subscribe) {
|
if (response?.subscribe) {
|
||||||
dbmData.value = buildTree(response.subscribe ?? []);
|
dbmData.splice(0, dbmData.length, ...buildTree(response.subscribe));
|
||||||
} else {
|
} else {
|
||||||
NotifyResponse($q, response);
|
NotifyResponse($q, response);
|
||||||
}
|
}
|
||||||
@@ -111,7 +111,7 @@ onUnmounted(() => {
|
|||||||
function changeState() {
|
function changeState() {
|
||||||
if (brightness.value === 0) {
|
if (brightness.value === 0) {
|
||||||
if (state.value === 0) {
|
if (state.value === 0) {
|
||||||
brightness.value = 100;
|
brightness.value = 255;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
brightness.value = state.value;
|
brightness.value = state.value;
|
||||||
|
@@ -1,14 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="'column items-center ' + props.class">
|
<div :class="'column items-center ' + props.class">
|
||||||
<q-item-label :class="['text-bold', `text-${textColor}`]"><p v-html="title"></p></q-item-label>
|
<q-item-label v-if="!toggleHighLow" :class="['text-bold', `text-${textColor}`]"
|
||||||
|
><p v-html="mainTitle"></p>
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label
|
||||||
|
v-else
|
||||||
|
@click="toggle = !toggle"
|
||||||
|
:class="['cursor-pointer', 'text-bold', 'clickable-text-effect', `text-${textColor}`]"
|
||||||
|
><p v-html="toggle ? secondTitle : mainTitle"></p>
|
||||||
|
</q-item-label>
|
||||||
<q-btn
|
<q-btn
|
||||||
size="sm"
|
size="sm"
|
||||||
class="q-mb-sm"
|
class="q-mb-md"
|
||||||
round
|
round
|
||||||
color="positive"
|
color="positive"
|
||||||
icon="add_circle_outline"
|
icon="add_circle_outline"
|
||||||
@click="addOne"
|
@click="reverse ? add : substract"
|
||||||
v-touch-repeat:300:300:300:300:50:50:50:50:20.mouse="addOne"
|
v-touch-repeat:0:300:300:300:300:50:50:50:50:20.mouse="reverse ? add : substract"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<q-slider
|
<q-slider
|
||||||
@@ -29,20 +37,30 @@
|
|||||||
round
|
round
|
||||||
color="negative"
|
color="negative"
|
||||||
icon="remove_circle_outline"
|
icon="remove_circle_outline"
|
||||||
@click="substractOne"
|
@click="reverse ? substract : add"
|
||||||
v-touch-repeat:300:300:300:300:50:50:50:50:20.mouse="substractOne"
|
v-touch-repeat:0:300:300:300:300:50:50:50:50:20.mouse="reverse ? substract : add"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import { updateValue } from 'src/composables/dbm/dbmTree';
|
import { updateValue } from 'src/composables/dbm/dbmTree';
|
||||||
|
import { addOne, substractOne } from 'src/utils/number-helpers';
|
||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
title: {
|
toggleHighLow: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
mainTitle: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
secondTitle: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
@@ -77,7 +95,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
max: {
|
max: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 100,
|
default: 255,
|
||||||
},
|
},
|
||||||
vertical: {
|
vertical: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -105,17 +123,35 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const localValue = updateValue(props.dbmPath, $q, props.dbmPath2, props.dbmPath3, props.dbmValue3);
|
const toggle = ref(false);
|
||||||
|
const localValue = updateValue(
|
||||||
|
props.dbmPath,
|
||||||
|
$q,
|
||||||
|
toggle,
|
||||||
|
props.dbmPath2,
|
||||||
|
props.dbmPath3,
|
||||||
|
props.dbmValue3,
|
||||||
|
);
|
||||||
|
|
||||||
function addOne() {
|
function add() {
|
||||||
if (localValue.value <= 255) {
|
addOne(localValue, 255);
|
||||||
localValue.value++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function substractOne() {
|
function substract() {
|
||||||
if (localValue.value >= 0) {
|
substractOne(localValue, 0);
|
||||||
localValue.value--;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.clickable-text-effect {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #040303; /* Static text color */
|
||||||
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.6);
|
||||||
|
/* Add hover effect here if you want it part of this class */
|
||||||
|
transition: text-shadow 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clickable-text-effect:hover {
|
||||||
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@@ -21,49 +21,59 @@ select
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<LightSlider
|
<LightSlider
|
||||||
title="Dimmer"
|
mainTitle="Dimmer"
|
||||||
:dbm-path="'MovingHead:Brightness'"
|
second-title="Dimmer Fine"
|
||||||
:dbm-path2="'MovingHead:BrightnessFine'"
|
:toggle-high-low="true"
|
||||||
:dbm-path3="'MovingHead:Strobe'"
|
dbm-path="MovingHead:Brightness"
|
||||||
|
dbm-path2="MovingHead:BrightnessFine"
|
||||||
|
dbm-path3="MovingHead:Strobe"
|
||||||
:dbm-value3="255"
|
:dbm-value3="255"
|
||||||
:opacity="0.5"
|
:opacity="0.5"
|
||||||
class="q-ma-md"
|
class="q-ma-md"
|
||||||
/>
|
/>
|
||||||
<LightSlider
|
<LightSlider
|
||||||
title="Red"
|
mainTitle="Red"
|
||||||
:dbm-path="'MovingHead:Red'"
|
second-title="Red Fine"
|
||||||
:dbm-path2="'MovingHead:RedFine'"
|
:toggle-high-low="true"
|
||||||
|
dbm-path="MovingHead:Red"
|
||||||
|
dbm-path2="MovingHead:RedFine"
|
||||||
:opacity="0.8"
|
:opacity="0.8"
|
||||||
color="red"
|
color="red"
|
||||||
class="q-ma-md"
|
class="q-ma-md"
|
||||||
/>
|
/>
|
||||||
<LightSlider
|
<LightSlider
|
||||||
title="Green"
|
mainTitle="Green"
|
||||||
:dbm-path="'MovingHead:Green'"
|
second-title="Green Fine"
|
||||||
:dbm-path2="'MovingHead:GreenFine'"
|
:toggle-high-low="true"
|
||||||
|
dbm-path="MovingHead:Green"
|
||||||
|
dbm-path2="MovingHead:GreenFine"
|
||||||
:opacity="0.8"
|
:opacity="0.8"
|
||||||
color="green"
|
color="green"
|
||||||
class="q-ma-md"
|
class="q-ma-md"
|
||||||
/>
|
/>
|
||||||
<LightSlider
|
<LightSlider
|
||||||
title="Blue"
|
mainTitle="Blue"
|
||||||
:dbm-path="'MovingHead:Blue'"
|
second-title="Blue Fine"
|
||||||
:dbm-path2="'MovingHead:BlueFine'"
|
:toggle-high-low="true"
|
||||||
|
dbm-path="MovingHead:Blue"
|
||||||
|
dbm-path2="MovingHead:BlueFine"
|
||||||
:opacity="0.8"
|
:opacity="0.8"
|
||||||
color="blue"
|
color="blue"
|
||||||
class="q-ma-md"
|
class="q-ma-md"
|
||||||
/>
|
/>
|
||||||
<LightSlider
|
<LightSlider
|
||||||
title="White"
|
mainTitle="White"
|
||||||
:dbm-path="'MovingHead:White'"
|
second-title="White Fine"
|
||||||
:dbm-path2="'MovingHead:WhiteFine'"
|
:toggle-high-low="true"
|
||||||
|
dbm-path="MovingHead:White"
|
||||||
|
dbm-path2="MovingHead:WhiteFine"
|
||||||
:opacity="0.3"
|
:opacity="0.3"
|
||||||
color="grey"
|
color="grey"
|
||||||
class="q-ma-md"
|
class="q-ma-md"
|
||||||
/>
|
/>
|
||||||
<LightSlider
|
<LightSlider
|
||||||
title="Zoom"
|
mainTitle="Zoom"
|
||||||
:dbm-path="'MovingHead:Zoom'"
|
dbm-path="MovingHead:Zoom"
|
||||||
:opacity="1"
|
:opacity="1"
|
||||||
color="black"
|
color="black"
|
||||||
class="q-ma-md"
|
class="q-ma-md"
|
||||||
@@ -71,9 +81,11 @@ select
|
|||||||
<div>
|
<div>
|
||||||
<DragPad
|
<DragPad
|
||||||
class="q-ma-md"
|
class="q-ma-md"
|
||||||
v-model:pan="pan"
|
pan-path="MovingHead:Pan"
|
||||||
|
pan-path2="MovingHead:PanFine"
|
||||||
v-model:reverse-pan="settings.reversePan"
|
v-model:reverse-pan="settings.reversePan"
|
||||||
v-model:tilt="tilt"
|
tilt-path="MovingHead:Tilt"
|
||||||
|
tilt-path2="MovingHead:TiltFine"
|
||||||
v-model:reverse-tilt="settings.reverseTilt"
|
v-model:reverse-tilt="settings.reverseTilt"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -102,8 +114,6 @@ import type { Settings } from 'src/models/MovingHead';
|
|||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const brightness = updateBrightnessValue('MovingHead:Brightness');
|
const brightness = updateBrightnessValue('MovingHead:Brightness');
|
||||||
const pan = updateValue('MovingHead:Pan', true);
|
|
||||||
const tilt = updateValue('MovingHead:Tilt', true);
|
|
||||||
const state = updateValue('MovingHead:State');
|
const state = updateValue('MovingHead:State');
|
||||||
const settings = ref<Settings>({
|
const settings = ref<Settings>({
|
||||||
show: false,
|
show: false,
|
||||||
@@ -125,7 +135,7 @@ onMounted(() => {
|
|||||||
.then((response) => {
|
.then((response) => {
|
||||||
console.log(response);
|
console.log(response);
|
||||||
if (response?.subscribe) {
|
if (response?.subscribe) {
|
||||||
dbmData.value = buildTree(response.subscribe ?? []);
|
dbmData.splice(0, dbmData.length, ...buildTree(response.subscribe));
|
||||||
} else {
|
} else {
|
||||||
NotifyResponse($q, response);
|
NotifyResponse($q, response);
|
||||||
}
|
}
|
||||||
@@ -149,7 +159,7 @@ onUnmounted(() => {
|
|||||||
function changeState() {
|
function changeState() {
|
||||||
if (brightness.value === 0) {
|
if (brightness.value === 0) {
|
||||||
if (state.value === 0) {
|
if (state.value === 0) {
|
||||||
brightness.value = 100;
|
brightness.value = 255;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
brightness.value = state.value;
|
brightness.value = state.value;
|
||||||
@@ -164,14 +174,13 @@ function updateValue(path: string, isDouble = false) {
|
|||||||
get() {
|
get() {
|
||||||
const sub = getSubscriptionsByPath(path);
|
const sub = getSubscriptionsByPath(path);
|
||||||
const value = sub ? Number(sub.value ?? 0) : 0;
|
const value = sub ? Number(sub.value ?? 0) : 0;
|
||||||
return isDouble ? Math.round((100 / 255) * value) : Math.round((100 / 255) * value);
|
return isDouble ? value : value;
|
||||||
},
|
},
|
||||||
set(val) {
|
set(val) {
|
||||||
const baseValue = Math.round((255 / 100) * val);
|
const setPaths = [{ path, value: val }];
|
||||||
const setPaths = [{ path, value: baseValue }];
|
|
||||||
|
|
||||||
if (isDouble) {
|
if (isDouble) {
|
||||||
setPaths.push({ path: `${path}Fine`, value: baseValue });
|
setPaths.push({ path: `${path}Fine`, value: val });
|
||||||
}
|
}
|
||||||
|
|
||||||
setValues(setPaths)
|
setValues(setPaths)
|
||||||
@@ -186,12 +195,11 @@ function updateBrightnessValue(path: string) {
|
|||||||
get() {
|
get() {
|
||||||
const sub = getSubscriptionsByPath(path);
|
const sub = getSubscriptionsByPath(path);
|
||||||
const value = sub ? Number(sub.value ?? 0) : 0;
|
const value = sub ? Number(sub.value ?? 0) : 0;
|
||||||
return Math.round((100 / 255) * value);
|
return value;
|
||||||
},
|
},
|
||||||
set(val) {
|
set(val) {
|
||||||
const baseValue = Math.round((255 / 100) * val);
|
const setPaths = [{ path, value: val }];
|
||||||
const setPaths = [{ path, value: baseValue }];
|
setPaths.push({ path: `${path}Fine`, value: val });
|
||||||
setPaths.push({ path: `${path}Fine`, value: baseValue });
|
|
||||||
setPaths.push({ path: `MovingHead:Strobe`, value: 255 });
|
setPaths.push({ path: `MovingHead:Strobe`, value: 255 });
|
||||||
|
|
||||||
setValues(setPaths)
|
setValues(setPaths)
|
||||||
|
292
src/components/scenes/ScenesTab.vue
Normal file
292
src/components/scenes/ScenesTab.vue
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
<template>
|
||||||
|
<!-- new edit scene dialog-->
|
||||||
|
<q-dialog v-model="showDialog" persistent>
|
||||||
|
<q-card style="min-width: 350px">
|
||||||
|
<q-card-section>
|
||||||
|
<div class="text-primary text-h6">{{ dialogLabel }}</div>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-card-section class="q-pt-none">
|
||||||
|
<q-input
|
||||||
|
class="q-mb-md"
|
||||||
|
dense
|
||||||
|
v-model="newScene.name"
|
||||||
|
placeholder="Name"
|
||||||
|
autofocus
|
||||||
|
:rules="[(val) => !!val || 'Field is required']"
|
||||||
|
@keyup.enter="saveScene"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
dense
|
||||||
|
v-model="newScene.description"
|
||||||
|
placeholder="Description"
|
||||||
|
autofocus
|
||||||
|
@keyup.enter="saveScene"
|
||||||
|
/>
|
||||||
|
<div class="q-py-md">
|
||||||
|
<div class="q-gutter-sm">
|
||||||
|
<q-checkbox v-model="newScene.movingHead" label="Moving Head" />
|
||||||
|
<q-checkbox v-model="newScene.lightBar" label="Light Bar" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-card-actions align="right" class="text-primary">
|
||||||
|
<q-btn flat label="Cancel" v-close-popup />
|
||||||
|
<q-btn flat :label="dialogLabel" @click="saveScene" />
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
<Dialog
|
||||||
|
dialogLabel="Duplicate Scene"
|
||||||
|
:text="`Scene '${newScene.name}' exists already`"
|
||||||
|
:show-dialog="existsAlready"
|
||||||
|
v-on:update:show-dialog="existsAlready = $event"
|
||||||
|
/>
|
||||||
|
<q-list
|
||||||
|
bordered
|
||||||
|
v-if="items.length > 0"
|
||||||
|
class="q-mx-auto"
|
||||||
|
style="max-width: 100%; max-width: 500px"
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
rounded
|
||||||
|
color="primary"
|
||||||
|
:class="['q-ma-md', 'text-bold', 'text-white']"
|
||||||
|
@click="openAddDialog"
|
||||||
|
>Add New Scene</q-btn
|
||||||
|
>
|
||||||
|
<q-item class="row">
|
||||||
|
<q-item-section :class="['text-black', 'text-bold', 'col-5']">Name</q-item-section>
|
||||||
|
<q-item-section :class="['text-black', 'text-left', 'text-bold', 'text-left']"
|
||||||
|
>Description</q-item-section
|
||||||
|
>
|
||||||
|
</q-item>
|
||||||
|
<q-item
|
||||||
|
v-for="(item, index) in items"
|
||||||
|
:key="item.name"
|
||||||
|
bordered
|
||||||
|
style="border: 0.1px solid lightgray; border-radius: 5px; margin-bottom: 1px"
|
||||||
|
>
|
||||||
|
<q-item-section
|
||||||
|
@click="openLoadDialog(item.name)"
|
||||||
|
:class="['text-black', 'text-left', 'cursor-pointer']"
|
||||||
|
style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis"
|
||||||
|
>{{ item.name }}</q-item-section
|
||||||
|
>
|
||||||
|
<q-item-section
|
||||||
|
@click="openLoadDialog(item.name)"
|
||||||
|
:class="['text-black', 'text-left', 'cursor-pointer']"
|
||||||
|
left
|
||||||
|
style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis"
|
||||||
|
>{{ item.description }}</q-item-section
|
||||||
|
>
|
||||||
|
<q-item-section top side>
|
||||||
|
<div class="text-grey-8 q-gutter-xs">
|
||||||
|
<q-btn size="12px" flat dense round icon="delete" @click="removeScene(item.name)" />
|
||||||
|
<q-btn
|
||||||
|
size="12px"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
round
|
||||||
|
icon="more_vert"
|
||||||
|
@click="openEditDialog(item, index)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
<!-- Fallback if list is empty -->
|
||||||
|
<div v-else class="q-pa-md text-grey text-center">
|
||||||
|
<div>No scenes available</div>
|
||||||
|
<q-btn
|
||||||
|
rounded
|
||||||
|
color="primary"
|
||||||
|
:class="['q-ma-md', 'text-bold', 'text-white']"
|
||||||
|
@click="openAddDialog"
|
||||||
|
>Add First Scene</q-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { NotifyDialog } from 'src/composables/notify';
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import type { Scene } from 'src/models/Scene';
|
||||||
|
import type { Set } from 'src/models/Set';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { api } from 'boot/axios';
|
||||||
|
import { NotifyResponse } from 'src/composables/notify';
|
||||||
|
import Dialog from 'src/components/dialog/okDialog.vue';
|
||||||
|
import { setValues } from 'src/services/websocket';
|
||||||
|
const $q = useQuasar();
|
||||||
|
const showDialog = ref(false);
|
||||||
|
const isEdit = ref(false);
|
||||||
|
const isLoad = ref(false);
|
||||||
|
const existsAlready = ref(false);
|
||||||
|
const editIndex = ref(-1);
|
||||||
|
const dialogLabel = ref('');
|
||||||
|
const newScene = ref<Scene>({
|
||||||
|
name: '',
|
||||||
|
movingHead: false,
|
||||||
|
lightBar: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const items = ref<Scene[]>([]);
|
||||||
|
|
||||||
|
const quasarApi = axios.create({
|
||||||
|
baseURL: `http://localhost:9500`,
|
||||||
|
timeout: 10000,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
quasarApi
|
||||||
|
.get('/api/loadScenes')
|
||||||
|
.then((resp) => {
|
||||||
|
if (resp.data) {
|
||||||
|
items.value = resp.data as Scene[];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => NotifyResponse($q, err.response.data.error, 'error'));
|
||||||
|
});
|
||||||
|
|
||||||
|
function removeScene(name: string) {
|
||||||
|
NotifyDialog($q, 'Delete', 'Do you want to delete scene: ' + name, 'YES', 'NO')
|
||||||
|
.then((res) => {
|
||||||
|
if (res) {
|
||||||
|
items.value = items.value.filter((s) => s.name !== name);
|
||||||
|
quasarApi
|
||||||
|
.delete('/api/deleteScene', {
|
||||||
|
data: { name },
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.data) {
|
||||||
|
NotifyResponse($q, res.data, 'warning');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
NotifyResponse($q, err.response.data.error, 'error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => NotifyResponse($q, err.resp, 'warning'));
|
||||||
|
}
|
||||||
|
function openAddDialog() {
|
||||||
|
isEdit.value = false;
|
||||||
|
dialogLabel.value = 'Add Scene';
|
||||||
|
|
||||||
|
newScene.value = {
|
||||||
|
name: '',
|
||||||
|
movingHead: true,
|
||||||
|
lightBar: true,
|
||||||
|
};
|
||||||
|
showDialog.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openEditDialog(scene: Scene, index: number) {
|
||||||
|
isEdit.value = true;
|
||||||
|
dialogLabel.value = 'Update Scene';
|
||||||
|
console.log(76, scene);
|
||||||
|
newScene.value = { ...scene };
|
||||||
|
editIndex.value = index;
|
||||||
|
showDialog.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveScene = async () => {
|
||||||
|
if (!newScene.value.name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const exists = items.value.some(
|
||||||
|
(item, index) => item.name === newScene.value.name && index !== editIndex.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (exists) {
|
||||||
|
if (isLoad.value) {
|
||||||
|
console.log(44, 'load');
|
||||||
|
const setPaths = <Set[]>[];
|
||||||
|
|
||||||
|
newScene.value.values?.forEach((element) => {
|
||||||
|
setPaths.push({ path: element.path, value: element.value });
|
||||||
|
});
|
||||||
|
|
||||||
|
setValues(setPaths)
|
||||||
|
.then((response) => NotifyResponse($q, response))
|
||||||
|
.catch((err) => console.error(`Failed to load scene ${newScene.value.name}`, err));
|
||||||
|
isLoad.value = false;
|
||||||
|
showDialog.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
existsAlready.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEdit.value && editIndex.value !== -1) {
|
||||||
|
items.value[editIndex.value] = { ...newScene.value };
|
||||||
|
} else {
|
||||||
|
items.value.push(newScene.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort alphabetically by scene name
|
||||||
|
items.value.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
|
const sendValues = [];
|
||||||
|
if (newScene.value.movingHead) {
|
||||||
|
sendValues.push({
|
||||||
|
path: 'MovingHead',
|
||||||
|
query: { depth: 0 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newScene.value.lightBar) {
|
||||||
|
sendValues.push({
|
||||||
|
path: 'LightBar',
|
||||||
|
query: { depth: 0 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log(33, sendValues);
|
||||||
|
if (sendValues.length > 0) {
|
||||||
|
try {
|
||||||
|
const res = await api.post('/json_data', { get: sendValues });
|
||||||
|
newScene.value.values = res.data.get;
|
||||||
|
} catch (err) {
|
||||||
|
NotifyResponse($q, err as Error, 'error');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newScene.value.values = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
quasarApi
|
||||||
|
.post('/api/saveScene', JSON.stringify(newScene.value))
|
||||||
|
.then((res) => {
|
||||||
|
if (res.data) {
|
||||||
|
NotifyResponse($q, res.data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
NotifyResponse($q, err.response.data.error, 'error');
|
||||||
|
});
|
||||||
|
|
||||||
|
showDialog.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
function openLoadDialog(name: string) {
|
||||||
|
isLoad.value = true;
|
||||||
|
quasarApi
|
||||||
|
.post('/api/loadScene', { name })
|
||||||
|
.then((res) => {
|
||||||
|
if (res.data) {
|
||||||
|
const scene = res.data as Scene;
|
||||||
|
newScene.value = scene;
|
||||||
|
newScene.value.name = name;
|
||||||
|
showDialog.value = true;
|
||||||
|
isEdit.value = true;
|
||||||
|
dialogLabel.value = 'Load Scene';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => NotifyResponse($q, err.response.data.error, 'error'));
|
||||||
|
}
|
||||||
|
</script>
|
@@ -1,12 +1,13 @@
|
|||||||
import type { Subs } from 'src/models/Subscribe';
|
import type { Subs, Subscribe } from 'src/models/Subscribe';
|
||||||
import { ref, nextTick, computed } from 'vue';
|
import type { Ref } from 'vue';
|
||||||
|
import { nextTick, computed, reactive, ref } from 'vue';
|
||||||
import { setValues } from 'src/services/websocket';
|
import { setValues } from 'src/services/websocket';
|
||||||
import { NotifyResponse } from 'src/composables/notify';
|
import { NotifyResponse } from 'src/composables/notify';
|
||||||
import type { QVueGlobals } from 'quasar';
|
import type { QVueGlobals } from 'quasar';
|
||||||
|
|
||||||
const Subscriptions = ref<Subs>([]);
|
const Subscriptions = reactive<Record<string, Subscribe>>({});
|
||||||
|
|
||||||
export const dbmData = ref<TreeNode[]>([]);
|
export const dbmData = reactive<TreeNode[]>([]);
|
||||||
|
|
||||||
export interface TreeNode {
|
export interface TreeNode {
|
||||||
path: string | undefined;
|
path: string | undefined;
|
||||||
@@ -28,9 +29,10 @@ export function buildTree(subs: Subs): TreeNode[] {
|
|||||||
|
|
||||||
const root: TreeMap = {};
|
const root: TreeMap = {};
|
||||||
|
|
||||||
Subscriptions.value = subs;
|
|
||||||
|
|
||||||
for (const item of subs) {
|
for (const item of subs) {
|
||||||
|
if (item.path) {
|
||||||
|
Subscriptions[item.path] = item;
|
||||||
|
}
|
||||||
const pathParts = item.path?.split(':') ?? [];
|
const pathParts = item.path?.split(':') ?? [];
|
||||||
let current = root;
|
let current = root;
|
||||||
|
|
||||||
@@ -54,13 +56,15 @@ export function buildTree(subs: Subs): TreeNode[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function convert(map: TreeMap): TreeNode[] {
|
function convert(map: TreeMap): TreeNode[] {
|
||||||
return Object.entries(map).map(([path, node]) => ({
|
return reactive(
|
||||||
|
Object.entries(map).map(([path, node]) => ({
|
||||||
path,
|
path,
|
||||||
key: node.uuid ?? path, // `key` is used by QTree
|
key: node.uuid ?? path, // `key` is used by QTree
|
||||||
value: node.value,
|
value: node.value,
|
||||||
lazy: node.lazy,
|
lazy: node.lazy,
|
||||||
children: convert(node.__children),
|
children: convert(node.__children),
|
||||||
}));
|
})),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@@ -74,24 +78,32 @@ export function buildTree(subs: Subs): TreeNode[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getTreeElementByPath(path: string) {
|
export function getTreeElementByPath(path: string) {
|
||||||
return dbmData.value.find((s) => s.path === path);
|
const sub = dbmData.find((s) => s.path === path);
|
||||||
|
return ref(sub);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSubscriptionsByUuid(uid: string | undefined) {
|
export function getSubscriptionsByUuid(uid: string) {
|
||||||
return Subscriptions.value.find((s) => s.uuid === uid);
|
const sub = Object.values(Subscriptions).find((sub) => sub.uuid === uid);
|
||||||
|
return ref(sub);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addChildrentoTree(subs: Subs) {
|
export function addChildrentoTree(subs: Subs) {
|
||||||
const ZERO_UUID = '00000000-0000-0000-0000-000000000000';
|
const ZERO_UUID = '00000000-0000-0000-0000-000000000000';
|
||||||
const existingIds = new Set(Subscriptions.value.map((sub) => sub.uuid));
|
const existingIds = new Set(Object.values(Subscriptions).map((sub) => sub.uuid));
|
||||||
const newSubs = subs
|
const newSubs = subs
|
||||||
.filter((sub) => sub.uuid !== ZERO_UUID) // Skip UUIDs with all zeroes
|
.filter((sub) => sub.uuid !== ZERO_UUID) // Skip UUIDs with all zeroes
|
||||||
.filter((sub) => !existingIds.has(sub.uuid));
|
.filter((sub) => !existingIds.has(sub.uuid));
|
||||||
|
|
||||||
Subscriptions.value.push(...newSubs);
|
for (const sub of newSubs) {
|
||||||
|
if (sub.path !== undefined) {
|
||||||
|
Subscriptions[sub.path] = sub;
|
||||||
|
} else {
|
||||||
|
console.warn('Skipping sub with undefined path', sub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void nextTick(() => {
|
void nextTick(() => {
|
||||||
dbmData.value = buildTree(Subscriptions.value);
|
dbmData.splice(0, dbmData.length, ...buildTree(Object.values(Subscriptions)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,39 +123,44 @@ export function removeSubtreeByParentKey(parentKey: string) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeChildrenAndMarkLazy(dbmData.value, parentKey);
|
removeChildrenAndMarkLazy(dbmData, parentKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSubscriptionsByPath(path: string | undefined) {
|
export function getSubscriptionsByPath(path: string) {
|
||||||
return Subscriptions.value.find((s) => s.path === path);
|
return ref(Subscriptions[path]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAllSubscriptions() {
|
export function getAllSubscriptions() {
|
||||||
return Subscriptions.value;
|
return Object.values(Subscriptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateValue(
|
export function updateValue(
|
||||||
path1: string,
|
path1: string,
|
||||||
$q: QVueGlobals,
|
$q: QVueGlobals,
|
||||||
|
toggle?: Ref<boolean>,
|
||||||
path2?: string,
|
path2?: string,
|
||||||
path3?: string,
|
path3?: string,
|
||||||
value3?: number,
|
value3?: number,
|
||||||
) {
|
) {
|
||||||
return computed({
|
return computed({
|
||||||
get() {
|
get() {
|
||||||
const sub = getSubscriptionsByPath(path1);
|
const sub = getSubscriptionsByPath(toggle?.value && path2 ? path2 : path1);
|
||||||
const value = sub ? Number(sub.value ?? 0) : 0;
|
const value = sub?.value ? Number(sub.value.value ?? 0) : 0;
|
||||||
return Math.round((100 / 255) * value);
|
return value;
|
||||||
},
|
},
|
||||||
set(val) {
|
set(val) {
|
||||||
const baseValue = Math.round((255 / 100) * val);
|
const baseValue = val;
|
||||||
const setPaths = [{ path: path1, value: baseValue }];
|
const setPaths = [];
|
||||||
if (path2) {
|
if (toggle?.value && path2) {
|
||||||
setPaths.push({ path: path2, value: baseValue });
|
setPaths.push({ path: path2, value: baseValue });
|
||||||
|
} else {
|
||||||
|
setPaths.push({ path: path1, value: baseValue });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path3) {
|
if (path3) {
|
||||||
setPaths.push({ path: path3, value: value3 ? value3 : baseValue });
|
setPaths.push({ path: path3, value: value3 ? value3 : baseValue });
|
||||||
}
|
}
|
||||||
|
|
||||||
setValues(setPaths)
|
setValues(setPaths)
|
||||||
.then((response) => NotifyResponse($q, response))
|
.then((response) => NotifyResponse($q, response))
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
@@ -16,7 +16,7 @@ export function NotifyResponse(
|
|||||||
icon = 'warning';
|
icon = 'warning';
|
||||||
break;
|
break;
|
||||||
case 'error':
|
case 'error':
|
||||||
color = 'orange';
|
color = 'red';
|
||||||
icon = 'error';
|
icon = 'error';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -26,8 +26,9 @@ export function NotifyResponse(
|
|||||||
if (message === '') {
|
if (message === '') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
color = typeof response === 'string' ? response : response?.error ? 'red' : color;
|
|
||||||
icon = typeof response === 'string' ? response : response?.error ? 'error' : icon;
|
color = typeof response === 'string' ? color : response?.error ? 'red' : color;
|
||||||
|
icon = typeof response === 'string' ? icon : response?.error ? 'error' : icon;
|
||||||
$q?.notify({
|
$q?.notify({
|
||||||
message: message,
|
message: message,
|
||||||
color: color,
|
color: color,
|
||||||
@@ -37,3 +38,30 @@ export function NotifyResponse(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function NotifyDialog(
|
||||||
|
$q: QVueGlobals,
|
||||||
|
title: string,
|
||||||
|
text: string,
|
||||||
|
okText?: string,
|
||||||
|
cancelText?: string,
|
||||||
|
) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
$q.dialog({
|
||||||
|
title: title,
|
||||||
|
message: text,
|
||||||
|
persistent: true,
|
||||||
|
ok: okText ?? 'OK',
|
||||||
|
cancel: cancelText ?? 'CANCEL',
|
||||||
|
})
|
||||||
|
.onOk(() => {
|
||||||
|
resolve(true);
|
||||||
|
})
|
||||||
|
.onCancel(() => {
|
||||||
|
resolve(false);
|
||||||
|
})
|
||||||
|
.onDismiss(() => {
|
||||||
|
resolve(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
9
src/models/Scene.ts
Normal file
9
src/models/Scene.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import type { Value } from './Value';
|
||||||
|
|
||||||
|
export interface Scene {
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
movingHead: boolean;
|
||||||
|
lightBar: boolean;
|
||||||
|
values?: Value[];
|
||||||
|
}
|
@@ -2,7 +2,7 @@ export type Set = {
|
|||||||
uuid?: string | undefined;
|
uuid?: string | undefined;
|
||||||
path: string;
|
path: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
value: number | boolean | undefined;
|
value: string | number | boolean | undefined;
|
||||||
create?: boolean;
|
create?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
|
// API type (from backend)
|
||||||
export type Subscribe = {
|
export type Subscribe = {
|
||||||
uuid?: string | undefined;
|
uuid?: string;
|
||||||
path?: string | undefined;
|
path?: string;
|
||||||
depth?: number;
|
depth?: number;
|
||||||
value?: string | number | boolean | undefined;
|
value?: string | number | boolean;
|
||||||
hasChild?: boolean;
|
hasChild?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
7
src/models/Value.ts
Normal file
7
src/models/Value.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import type { UUID } from 'crypto';
|
||||||
|
|
||||||
|
export interface Value {
|
||||||
|
uuid?: UUID;
|
||||||
|
path: string;
|
||||||
|
value: number | string | undefined;
|
||||||
|
}
|
@@ -10,6 +10,7 @@ import { NotifyResponse } from 'src/composables/notify';
|
|||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
|
|
||||||
function saveDBM() {
|
function saveDBM() {
|
||||||
api
|
api
|
||||||
.get('saveData')
|
.get('saveData')
|
||||||
|
@@ -5,8 +5,9 @@ const routes: RouteRecordRaw[] = [
|
|||||||
path: '/',
|
path: '/',
|
||||||
component: () => import('layouts/MainLayout.vue'),
|
component: () => import('layouts/MainLayout.vue'),
|
||||||
children: [
|
children: [
|
||||||
{ path: '', component: () => import('pages/IndexPage.vue') },
|
{ path: '', component: () => import('pages/MainPage.vue') },
|
||||||
{ path: '/data', component: () => import('pages/DataPage.vue') },
|
{ path: '/data', component: () => import('pages/DataPage.vue') },
|
||||||
|
{ path: '/scenes', component: () => import('components/scenes/ScenesTab.vue') },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user