Skip to content

Commit 145cdf3

Browse files
authored
Koala - New charge limit dialog (openWB#3199)
* add charge limit dialog * add toggle icon * switch case to update charge Limit based on charge mode * Limit dialog - only modes instant, pv and eco clickable * enable energy limit toggle for modes instant, pv and eco * hide energy limit toggle when not required * Add tooltip - limit slider * only show tool tip for relevant charge modes * Rename charge limits dialog - add slider attributes * Add slider props * Add dropdown button group for mobile * Replace the SoC figure with info text if no SoC-Modul configured
1 parent a9f2ade commit 145cdf3

File tree

4 files changed

+284
-28
lines changed

4 files changed

+284
-28
lines changed

packages/modules/web_themes/koala/source/src/components/ChargePointCard.vue

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
<q-card-section>
6060
<ChargePointModeButtons :charge-point-id="props.chargePointId" />
6161
</q-card-section>
62+
6263
<q-card-section class="row q-mt-sm">
6364
<div class="col">
6465
<div class="text-subtitle2">Leistung</div>
@@ -77,8 +78,7 @@
7778
</q-card-section>
7879
<q-card-section>
7980
<SliderDouble
80-
v-if="showSocTargetSlider"
81-
class="q-mt-sm"
81+
:class="['q-mt-sm', limitEditable && 'cursor-pointer']"
8282
:model-value="target"
8383
:readonly="true"
8484
:charge-mode="chargeMode"
@@ -88,7 +88,16 @@
8888
:vehicle-soc-type="vehicleSocType"
8989
:on-edit-soc="openSocDialog"
9090
:on-refresh-soc="refreshSoc"
91-
/>
91+
@click="openLimitDialog"
92+
>
93+
<template #tooltip>
94+
<q-tooltip
95+
v-if="chargeMode !== 'scheduled_charging' && chargeMode !== 'stop'"
96+
>
97+
Begrenzung einstellen
98+
</q-tooltip>
99+
</template>
100+
</SliderDouble>
92101
</q-card-section>
93102
<q-card-actions v-if="$slots['card-actions']" align="right">
94103
<slot name="card-actions"></slot>
@@ -98,6 +107,11 @@
98107
:chargePointId="props.chargePointId"
99108
v-model="settingsVisible"
100109
/>
110+
<!-- ////////////////////// modal charge limit settings dialog //////////////////// -->
111+
<ChargePointChargeLimits
112+
:chargePointId="props.chargePointId"
113+
v-model="chargeLimitsVisible"
114+
/>
101115
<!-- ////////////////////// modal soc dialog //////////////////// -->
102116
<ManualSocDialog
103117
:vehicleId="vehicleId"
@@ -117,6 +131,7 @@ import ChargePointModeButtons from './ChargePointModeButtons.vue';
117131
import ChargePointMessage from './ChargePointMessage.vue';
118132
import ChargePointVehicleSelect from './ChargePointVehicleSelect.vue';
119133
import ChargePointSettings from './ChargePointSettings.vue';
134+
import ChargePointChargeLimits from './ChargePointChargeLimits.vue';
120135
import ManualSocDialog from './ManualSocDialog.vue';
121136
import ChargePointTimeCharging from './ChargePointTimeCharging.vue';
122137
import ChargePointPowerData from './ChargePointPowerData.vue';
@@ -161,6 +176,16 @@ const limitMode = computed(() => {
161176
});
162177
163178
const settingsVisible = ref<boolean>(false);
179+
const chargeLimitsVisible = ref<boolean>(false);
180+
181+
const limitEditable = computed(() => {
182+
return !['scheduled_charging', 'stop'].includes(chargeMode.value);
183+
});
184+
185+
const openLimitDialog = () => {
186+
if (!limitEditable.value) return;
187+
chargeLimitsVisible.value = true;
188+
};
164189
165190
const socInputVisible = ref<boolean>(false);
166191
const openSocDialog = () => {
@@ -272,18 +297,6 @@ const target = computed(() => {
272297
}
273298
});
274299
275-
const showSocTargetSlider = computed(() => {
276-
if (target.value && target.value > 999) {
277-
// we have a energy based target
278-
return true;
279-
}
280-
if (vehicleSocType.value) {
281-
// we have a soc module defined
282-
return true;
283-
}
284-
return false;
285-
});
286-
287300
const vehicleTarget = computed(() => {
288301
return mqttStore.vehicleChargeTarget(props.chargePointId).value;
289302
});
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
<template>
2+
<q-dialog
3+
v-model="visible"
4+
:backdrop-filter="isSmallScreen ? '' : 'blur(4px)'"
5+
>
6+
<q-card class="card-width">
7+
<q-card-section>
8+
<div class="row nowrap q-mb-sm">
9+
<div class="text-h6">Begrenzung</div>
10+
<q-space />
11+
<q-btn icon="close" flat round dense v-close-popup />
12+
</div>
13+
<q-separator class="q-mt-sm q-mb-sm" />
14+
<div v-if="isSmallScreen" class="row q-pt-md full-width">
15+
<q-btn-dropdown
16+
class="col"
17+
transition-show="scale"
18+
transition-hide="scale"
19+
transition-duration="500"
20+
color="primary"
21+
:label="currentLimitModeLabel"
22+
size="md"
23+
dropdown-icon="none"
24+
cover
25+
push
26+
>
27+
<q-list>
28+
<template v-for="(mode, index) in limitModes" :key="mode.value">
29+
<q-item
30+
clickable
31+
v-close-popup
32+
@click="limitMode.value = mode.value"
33+
:active="limitMode.value === mode.value"
34+
:disable="mode.value === 'soc' && !vehicleSocType"
35+
active-class="bg-primary text-white"
36+
>
37+
<q-item-section class="text-center text-weight-bold">
38+
<q-item-label>{{
39+
mode.label.toLocaleUpperCase()
40+
}}</q-item-label>
41+
</q-item-section>
42+
</q-item>
43+
<q-separator v-if="index < limitModes.length - 1" />
44+
</template>
45+
</q-list>
46+
</q-btn-dropdown>
47+
</div>
48+
<div
49+
v-else
50+
class="row items-center justify-center q-ma-none q-pa-none no-wrap"
51+
>
52+
<q-btn-group class="col">
53+
<q-btn
54+
v-for="mode in limitModes"
55+
:key="mode.value"
56+
:color="limitMode.value === mode.value ? 'primary' : 'grey'"
57+
:label="mode.label"
58+
:disable="mode.value === 'soc' && !vehicleSocType"
59+
:outline="mode.value === 'soc' && !vehicleSocType"
60+
size="sm"
61+
class="col"
62+
@click="limitMode.value = mode.value"
63+
/>
64+
</q-btn-group>
65+
</div>
66+
<SliderStandard
67+
v-if="limitMode.value === 'soc'"
68+
title="SoC-Limit für das Fahrzeug"
69+
:min="5"
70+
:max="100"
71+
:step="5"
72+
color="light-green-14"
73+
:track-size="'1em'"
74+
thumb-size="2.3em"
75+
thumb-color="light-green-14"
76+
unit="%"
77+
v-model="limitSoC.value"
78+
class="q-mt-md"
79+
/>
80+
<SliderStandard
81+
v-if="limitMode.value === 'amount'"
82+
title="Energie-Limit"
83+
:min="1"
84+
:max="50"
85+
color="green-7"
86+
:track-size="'1em'"
87+
thumb-size="2.3em"
88+
thumb-color="green-7"
89+
unit="kWh"
90+
v-model="limitEnergy.value"
91+
class="q-mt-md"
92+
/>
93+
</q-card-section>
94+
</q-card>
95+
</q-dialog>
96+
</template>
97+
98+
<script setup lang="ts">
99+
import { Screen, QDialog } from 'quasar';
100+
import { useMqttStore } from 'src/stores/mqtt-store';
101+
import SliderStandard from './SliderStandard.vue';
102+
import { computed, ref, watch } from 'vue';
103+
104+
const props = defineProps<{
105+
chargePointId: number;
106+
modelValue: boolean;
107+
}>();
108+
109+
const emit = defineEmits<{
110+
'update:model-value': [value: boolean];
111+
}>();
112+
113+
const mqttStore = useMqttStore();
114+
115+
const isSmallScreen = computed(() => Screen.lt.sm);
116+
const tempValue = ref<boolean>(props.modelValue);
117+
118+
const limitModes = [
119+
{ value: 'none', label: 'keine' },
120+
{ value: 'soc', label: 'EV-SoC' },
121+
{ value: 'amount', label: 'Energie' },
122+
];
123+
124+
const vehicleSocType = computed(() =>
125+
mqttStore.chargePointConnectedVehicleSocType(props.chargePointId),
126+
)?.value;
127+
128+
const chargeMode = computed(
129+
() =>
130+
mqttStore.chargePointConnectedVehicleChargeMode(props.chargePointId)?.value,
131+
);
132+
133+
const activeChargeLimitConfig = computed(() => {
134+
switch (chargeMode.value) {
135+
case 'instant_charging':
136+
return {
137+
mode: mqttStore.chargePointConnectedVehicleInstantChargeLimit(
138+
props.chargePointId,
139+
),
140+
soc: mqttStore.chargePointConnectedVehicleInstantChargeLimitSoC(
141+
props.chargePointId,
142+
),
143+
energy: mqttStore.chargePointConnectedVehicleInstantChargeLimitEnergy(
144+
props.chargePointId,
145+
),
146+
};
147+
case 'pv_charging':
148+
return {
149+
mode: mqttStore.chargePointConnectedVehiclePvChargeLimit(
150+
props.chargePointId,
151+
),
152+
soc: mqttStore.chargePointConnectedVehiclePvChargeLimitSoC(
153+
props.chargePointId,
154+
),
155+
energy: mqttStore.chargePointConnectedVehiclePvChargeLimitEnergy(
156+
props.chargePointId,
157+
),
158+
};
159+
case 'eco_charging':
160+
return {
161+
mode: mqttStore.chargePointConnectedVehicleEcoChargeLimit(
162+
props.chargePointId,
163+
),
164+
soc: mqttStore.chargePointConnectedVehicleEcoChargeLimitSoC(
165+
props.chargePointId,
166+
),
167+
energy: mqttStore.chargePointConnectedVehicleEcoChargeLimitEnergy(
168+
props.chargePointId,
169+
),
170+
};
171+
default:
172+
return {
173+
mode: ref('none'),
174+
soc: ref(0),
175+
energy: ref(0),
176+
};
177+
}
178+
});
179+
180+
const limitMode = computed(() => activeChargeLimitConfig.value.mode);
181+
const limitSoC = computed(() => activeChargeLimitConfig.value.soc);
182+
const limitEnergy = computed(() => activeChargeLimitConfig.value.energy);
183+
184+
const visible = computed({
185+
get: () => tempValue.value,
186+
set: (value) => {
187+
tempValue.value = value;
188+
emit('update:model-value', value);
189+
},
190+
});
191+
192+
const currentLimitModeLabel = computed(
193+
() => limitModes.find((mode) => mode.value === limitMode.value.value)?.label,
194+
);
195+
196+
watch(
197+
() => props.modelValue,
198+
(value) => {
199+
tempValue.value = value;
200+
},
201+
);
202+
</script>
203+
204+
<style scoped>
205+
.card-width {
206+
width: 25em;
207+
}
208+
.q-btn-group .q-btn {
209+
min-width: 100px !important;
210+
}
211+
212+
body.mobile .q-btn-group .q-btn {
213+
padding: 4px 8px;
214+
font-size: 12px !important;
215+
min-height: 30px;
216+
}
217+
218+
:deep(.q-btn-dropdown__arrow-container) {
219+
width: 0;
220+
padding: 0;
221+
margin: 0;
222+
}
223+
</style>

packages/modules/web_themes/koala/source/src/components/SliderDouble.vue

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<template>
2-
<div class="double-slider-container">
2+
<div>
33
<div class="slider-container">
44
<q-slider
5-
:model-value="currentValue"
5+
:model-value="props.currentValue"
66
:min="0"
77
:max="maxValue"
88
:markers="props.limitMode == 'amount' ? 10000 : 10"
@@ -16,6 +16,7 @@
1616
@touchmove.stop
1717
@touchend.stop
1818
/>
19+
<slot name="tooltip" />
1920
<q-slider
2021
v-if="props.limitMode == 'soc'"
2122
v-model="target"
@@ -37,25 +38,27 @@
3738
<div>{{ props.limitMode == 'amount' ? 'Geladen' : 'Ladestand' }}</div>
3839
<div>
3940
{{
40-
props.limitMode == 'amount'
41-
? formatEnergy(currentValue)
42-
: currentValue + '%'
41+
props.limitMode === 'amount'
42+
? formatEnergy(props.currentValue)
43+
: !props.vehicleSocType
44+
? 'Kein SoC-Modul konfiguriert'
45+
: props.currentValue + '%'
4346
}}
4447
<q-icon
45-
v-if="vehicleSocType === 'manual' && limitMode !== 'amount'"
48+
v-if="props.vehicleSocType === 'manual' && props.limitMode !== 'amount'"
4649
name="edit"
4750
size="xs"
4851
class="q-ml-xs cursor-pointer"
49-
@click="onEditSoc"
52+
@click.stop="onEditSoc"
5053
>
5154
<q-tooltip>Ladestand eingeben</q-tooltip>
5255
</q-icon>
5356
<q-icon
54-
v-else-if="vehicleSocType !== undefined && limitMode !== 'amount'"
57+
v-else-if="props.vehicleSocType && props.vehicleSocType !== 'manual' && props.limitMode !== 'amount'"
5558
name="refresh"
5659
size="xs"
5760
class="q-ml-xs cursor-pointer"
58-
@click="onRefreshSoc"
61+
@click.stop="onRefreshSoc"
5962
>
6063
<q-tooltip>Ladestand aktualisieren</q-tooltip>
6164
</q-icon>
@@ -116,7 +119,7 @@ const props = defineProps({
116119
vehicleSocType: {
117120
type: String,
118121
required: false,
119-
default: undefined,
122+
default: null,
120123
},
121124
onEditSoc: {
122125
type: Function,

0 commit comments

Comments
 (0)