<template> <q-dialog persistent v-model="show_dialog" @before-hide="resetData" @keydown=" (evt) => { if (!loading && evt.keyCode == 27) { show_dialog = false; } } " > <q-card class="overflow-hidden" style="overflow-y: scroll; max-width: 50vw"> <q-form @submit="onSubmit"> <q-card-section class="q-ma-none q-pa-sm"> <div class="row"> <div class="col-auto text-h6"> {{ type == 1 ? $t("add plan") : type == 2 ? $t("edit plan") : $t("add plan") }} </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: 70vh; width: 50vw" class="scroll"> <q-list> <q-item> <q-item-label>{{ $t("parent group") }}:</q-item-label> </q-item> <q-item class="q-pa-none q-ma-none"> <q-item-section style="padding-right: 10px"> <q-tree ref="tree" class="scroll" :class="loading ? 'disable_tree' : ''" v-model:selected="selected" :nodes="tree_nodes" default-expand-all node-key="uuid" labelKey="name" filter="group filter" :filter-method="treeNodesFilter" > <template v-slot:default-header="prop"> <q-item class="full-width" :class=" prop.tree.selected == prop.key ? 'item-selected-bg' : '' " > <q-item-section avatar> <q-icon :name="'img:source_icon/group.png'" color="orange" size="28px" class="q-mr-sm" /> </q-item-section> <q-item-section> <div class="text-weight-bold text-primary"> {{ prop.node.name }} </div> </q-item-section> </q-item> </template> </q-tree> </q-item-section> </q-item> <q-item> <q-item-section> <q-input autofocus :loading="loading" :disable="loading" filled v-model="name" :label="$t('plan name')" :hint="$t('please input plan name')" lazy-rules :rules="[ (val) => (val && val.length > 0) || $t('Please type something'), ]" @keydown=" (evt) => { if (evt.keyCode == 13) { $refs?.accept?.click(); } } " > <template v-if="name" v-slot:append> <q-icon name="cancel" @click.stop="name = null" class="cursor-pointer" /> </template> </q-input> </q-item-section> </q-item> <q-item> <q-item-section> <q-table style="height: 50vh" :rows="datas" :columns="columns" virtual-scroll :title="$t('plan data') + ':'" hide-bottom color="primary" row-key="name" :loading="loading" :rows-per-page-options="[0]" :virtual-scroll-sticky-size-start="48" @row-contextmenu="onContextMenu" > <template v-slot:loading> <q-inner-loading showing color="primary" /> </template> <template v-slot:body-cell="props"> <q-td :props="props"> <div v-if="props.col.name == 'index'"> {{ props.pageIndex + 1 }} </div> <div v-else-if="props.col.name == 'uuid'" class="fit"> <q-popup-edit :ref="'popup_edit' + props.pageIndex" v-model="props.row.uuid" > <q-tree ref="tree" :nodes="$store.state.mode_tree" node-key="uuid" labelKey="name" default-expand-all > <template v-slot:default-header="prop"> <q-item class="full-width" :clickable="!prop.node.is_group" @click=" (evt) => { props.row.uuid = prop.node.item_data.uuid; $refs[ 'popup_edit' + props.pageIndex ]?.hide(); } " :style="{ border: props.value == prop?.node?.item_data?.uuid ? '1px solid #aacceec2' : 'none', }" > <q-item-section avatar> <q-icon :name=" prop.node.is_group ? 'img:source_icon/group.png' : 'img:new_icon/mode_icon.png' " color="orange" size="28px" class="q-mr-sm" /> </q-item-section> <q-item-section> <div class="text-weight-bold text-primary"> <span v-if=" prop.node.item_data && !prop.node.is_group " >({{ prop.node.item_data?.number }}) </span> {{ prop.node.name }} </div> </q-item-section> </q-item> </template> </q-tree> </q-popup-edit> {{ modes.find( (element) => element && element.uuid == props.value )?.name ?? "" }} </div> <div v-else-if="props.col.name == 'delay'"> {{ props.value }}{{ $t("s") }} <q-popup-edit v-model="props.row.delay" :validate=" (val) => !isNaN(parseInt(val)) && parseInt(val) > 9 " auto-save v-slot="scope" > <q-input type="number" v-model="scope.value" :min="min_delay" dense autofocus :rules="[ (val) => (val != null && val != undefined && val.toString().length > 0) || $t('Please type something'), (val) => parseInt(val) >= 10 || $t('number must be greater than or equal to ') + '10', ]" > <template v-slot:append> <span>{{ $t("s") }}</span> </template> </q-input> </q-popup-edit> </div> <div v-else> {{ $t(props.value) }} </div> </q-td> </template> </q-table> </q-item-section> </q-item> <q-separator /> <q-item> <q-item-section avatar class="width_5_1">{{ $t("timing cycle") + ":" }}</q-item-section> <q-item-section> <q-select v-model="timing_cycle" :loading="loading" :options="[$t('enable'), $t('disable')]" /> </q-item-section> </q-item> <q-item v-if="timing_cycle != $t('disable')"> <q-item-section avatar class="width_5_1">{{ $t("time") + ":" }}</q-item-section> <q-item-section> <q-input v-if="false" v-model="time_" :loading="loading" type="time" /> <q-input v-else filled v-model="time_" :loading="loading" mask="fulltime" :rules="['fulltime']" > <template v-slot:append> <q-icon name="access_time" class="cursor-pointer"> <q-popup-proxy cover transition-show="scale" transition-hide="scale" > <q-time v-model="time_" with-seconds format24h now-btn> <div class="row items-center justify-end"> <q-btn v-close-popup :label="$t('close')" :disable="loading" color="primary" flat /> </div> </q-time> </q-popup-proxy> </q-icon> </template> </q-input> </q-item-section> </q-item> <q-item v-if="timing_cycle != $t('disable')"> <q-item-section avatar class="width_5_1">{{ $t("loop") + ":" }}</q-item-section> <q-item-section> <q-checkbox v-model="day1" :label="$t('monday')" /> </q-item-section> <q-item-section> <q-checkbox v-model="day2" :label="$t('tuesday')" /> </q-item-section> <q-item-section> <q-checkbox v-model="day3" :label="$t('wednesday')" /> </q-item-section> <q-item-section> <q-checkbox v-model="day4" :label="$t('thursday')" /> </q-item-section> </q-item> <q-item v-if="timing_cycle != $t('disable')"> <q-item-section avatar class="width_5_1" /> <q-item-section> <q-checkbox v-model="day5" :label="$t('friday')" /> </q-item-section> <q-item-section> <q-checkbox v-model="day6" :label="$t('saturday')" /> </q-item-section> <q-item-section> <q-checkbox v-model="day7" :label="$t('sunday')" /> </q-item-section> <q-item-section> </q-item-section> </q-item> </q-list> </q-card-section> <q-separator /> <q-card-actions align="left"> <q-btn :loading="loading" flat no-caps :label="$t('add row')" color="primary" @click="addRow(false)" /> <q-space /> <q-btn :loading="loading" flat :label="$t('Cancel')" no-caps color="primary" v-close-popup /> <q-btn ref="accept" flat :label="$t('Accept')" no-caps :loading="loading" type="submit" color="primary" /> </q-card-actions> </q-form> </q-card> </q-dialog> <div> <q-menu :target="target_dom" v-model="show_context_menu"> <q-list> <q-item clickable v-close-popup @click="(evt) => deleteRow()"> <q-item-section avatar ><q-icon name="delete" color="red" /></q-item-section> <q-item-section>{{ $t("delete row") }}</q-item-section> </q-item> <q-item v-if="false" clickable v-close-popup @click="(evt) => addRow(true)" > <q-item-section avatar><q-icon name="add" /></q-item-section> <q-item-section>{{ $t("add row") }}</q-item-section> </q-item> </q-list> </q-menu> </div> </template> <style scoped> .disable_tree { background: #9e9e9e; cursor: wait; pointer-events: none; } .width_5_1 { width: 18%; } </style> <script lang="ts"> import { defineComponent, ref, Ref, watch, computed } from "vue"; import { useStore } from "src/store"; import GlobalData from "src/common/GlobalData"; import { useQuasar, date as $date } from "quasar"; import { useI18n } from "vue-i18n"; import { StringKeyValueEntity } from "src/entities/StringKeyValueEntity"; import { PlanEntity } from "src/entities/PlanEntity"; import { EWeekDays } from "src/entities/EWeekDays"; import { TableAddRowHelper } from "src/common/TableAddRowHelper"; const __MIN_DELAY = 10; const __DEFAULT_DELAY = 60; class TableRow { uuid = ""; delay = __MIN_DELAY; } const table_add_row_helper = new TableAddRowHelper(); export default defineComponent({ name: "ComponentPlanDialog", setup() { let $store = useStore(); let $q = useQuasar(); let $t = useI18n(); const min_delay = ref(__MIN_DELAY); let show_dialog = ref(false); let type = ref(1); let name = ref(null); let uuid = ref(""); const selected: any = ref(null); let loading = ref(false); let datas: Ref<TableRow[]> = ref([]); let modes = ref(GlobalData.getInstance().modes); const columns = [ { align: "left", name: "index", required: true, label: $t.t("No."), sortable: false, }, { align: "left", name: "uuid", required: true, label: $t.t("mode"), field: "uuid", sortable: false, }, { align: "left", name: "delay", label: $t.t("delay"), field: "delay", sortable: false, }, ]; let show_context_menu = ref(false); let target_dom: any = ref(document.body.children[0]); let current_index = 0; let timing_cycle = ref($t.t("disable")); let time_ = ref("09:00:00"); let week_days = ref(EWeekDays.EWeekAll); let day1 = ref(true); let day2 = ref(true); let day3 = ref(true); let day4 = ref(true); let day5 = ref(true); let day6 = ref(true); let day7 = ref(true); const tree_nodes = computed({ get: () => $store.state.plan_tree, set: (val) => {}, }); watch( () => selected.value, (newValue, oldValue) => { if (newValue == null) { selected.value = ""; } } ); const requestAddPlan = async () => { let entity = new PlanEntity(); const __datas: StringKeyValueEntity[] = []; for (const item of datas.value) { if (item) { __datas.push({ key: "operator_call_mode", value: item.uuid, }); __datas.push({ key: "param_delay", value: item.delay < __MIN_DELAY ? __MIN_DELAY.toString() : item.delay.toString(), }); } } entity.group_uuid = selected.value; entity.uuid = uuid.value; entity.name = name.value ?? ""; entity.datas = __datas; entity.time_ = time_.value; entity.timing_cycle = timing_cycle.value == $t.t("enable"); entity.week_days = EWeekDays.EWeekNone; if (day1.value) entity.week_days |= EWeekDays.EWeekMonday; if (day2.value) entity.week_days |= EWeekDays.EWeekTuesday; if (day3.value) entity.week_days |= EWeekDays.EWeekWednesday; if (day4.value) entity.week_days |= EWeekDays.EWeekThursday; if (day5.value) entity.week_days |= EWeekDays.EWeekFriday; if (day6.value) entity.week_days |= EWeekDays.EWeekSaturday; if (day7.value) entity.week_days |= EWeekDays.EWeekSunday; let response = await GlobalData.getInstance() .getCurrentClient() ?.addPlan(entity); if (response) { $q.notify({ color: response.success ? "positive" : "negative", icon: response.success ? "done" : "warning", message: $t.t("add plan") + (response.success ? $t.t("success") : $t.t("fail")) + "!", position: "top", timeout: 1500, }); } }; const requestEditPlan = async () => { let entity = new PlanEntity(); const __datas: StringKeyValueEntity[] = []; for (const item of datas.value) { if (item) { __datas.push({ key: "operator_call_mode", value: item.uuid, }); __datas.push({ key: "param_delay", value: item.delay < __MIN_DELAY ? __MIN_DELAY.toString() : item.delay.toString(), }); } } entity.group_uuid = selected.value; entity.uuid = uuid.value; entity.name = name.value ?? ""; entity.datas = __datas; entity.time_ = time_.value; entity.timing_cycle = timing_cycle.value == $t.t("enable"); entity.week_days = EWeekDays.EWeekNone; if (day1.value) entity.week_days |= EWeekDays.EWeekMonday; if (day2.value) entity.week_days |= EWeekDays.EWeekTuesday; if (day3.value) entity.week_days |= EWeekDays.EWeekWednesday; if (day4.value) entity.week_days |= EWeekDays.EWeekThursday; if (day5.value) entity.week_days |= EWeekDays.EWeekFriday; if (day6.value) entity.week_days |= EWeekDays.EWeekSaturday; if (day7.value) entity.week_days |= EWeekDays.EWeekSunday; let response = await GlobalData.getInstance() .getCurrentClient() ?.editPlan(entity); if (response) { $q.notify({ color: response.success ? "positive" : "negative", icon: response.success ? "done" : "warning", message: $t.t("edit plan") + (response.success ? $t.t("success") : $t.t("fail")) + "!", position: "top", timeout: 1500, }); } }; return { show_dialog, min_delay, type, name, uuid, selected, loading, datas, columns, show_context_menu, target_dom, tree_nodes, modes, week_days, time_, timing_cycle, day1, day2, day3, day4, day5, day6, day7, loga(a: any) { console.log(a); }, showDialog(options: any) { let __datas: StringKeyValueEntity[] = []; if (options) { type.value = options.type ?? 1; time_.value = "09:00:00"; timing_cycle.value = $t.t("disable"); week_days.value = EWeekDays.EWeekAll; if (type.value == 2) { name.value = options.data?.name ?? $t.t("new plan 1"); selected.value = options.data?.item_data?.group_uuid ?? null; uuid.value = options.data?.item_data?.uuid ?? null; week_days.value = options.data?.item_data?.week_days ?? EWeekDays.EWeekAll; time_.value = options.data?.item_data?.time_ ?? "09:00:00"; timing_cycle.value = options.data?.item_data?.timing_cycle ? $t.t("enable") : $t.t("disable"); __datas = JSON.parse( JSON.stringify(options.data?.item_data?.datas ?? []) ); } else { name.value = options.data?.name ?? $t.t("new plan 1"); selected.value = options.data?.uuid ?? null; uuid.value = options.data?.uuid ?? null; __datas = options.data?.item_data?.datas ?? []; } modes.value = GlobalData.getInstance().modes; datas.value = []; let auto_removed = false; for ( let i = 0; i < (__datas.length % 2 ? __datas.length - 1 : __datas.length); i += 2 ) { const uuid_item = __datas[i]; const delay_item = __datas[i + 1]; if (uuid_item && uuid_item.key.startsWith("operator_")) { const item_value = GlobalData.getInstance().modes.find( (element) => element && element.uuid == uuid_item.value ); const delay = parseInt(delay_item.value); if (item_value) { datas.value.push({ uuid: uuid_item.value, delay: isNaN(delay) ? __MIN_DELAY : delay, }); } } else { auto_removed = true; } } if (auto_removed) { $q.notify({ type: "warning", message: $t.t("auto delete unknow mode success"), position: "top", timeout: 1500, }); } // format time { let _times = time_.value.split(":"); if (Array.isArray(_times)) { while (_times.length < 3) { _times.push("00"); } _times = _times.slice(0, 3); time_.value = _times.join(":"); } else { time_.value = "09:00:00"; } } } for (const item of datas.value) { if (item.delay < __MIN_DELAY) { item.delay = __MIN_DELAY; } } day1.value = (week_days.value & EWeekDays.EWeekMonday) == 0 ? false : true; day2.value = (week_days.value & EWeekDays.EWeekTuesday) == 0 ? false : true; day3.value = (week_days.value & EWeekDays.EWeekWednesday) == 0 ? false : true; day4.value = (week_days.value & EWeekDays.EWeekThursday) == 0 ? false : true; day5.value = (week_days.value & EWeekDays.EWeekFriday) == 0 ? false : true; day6.value = (week_days.value & EWeekDays.EWeekSaturday) == 0 ? false : true; day7.value = (week_days.value & EWeekDays.EWeekSunday) == 0 ? false : true; show_dialog.value = true; }, resetData() { loading.value = false; (selected.value = null), (name.value = null); type.value = 1; }, treeNodesFilter(node: any, filter: any) { return node.is_group; }, addRow(insert_flag: boolean = false) { if (GlobalData.getInstance().modes.length <= 0) { $q.notify({ color: "positive", icon: "warning", message: $t.t("mode is empty! please add mode first") + "!", position: "top", timeout: 1500, }); return; } const item = { uuid: GlobalData.getInstance().modes[0].uuid, delay: __DEFAULT_DELAY, }; if (insert_flag) { datas.value.splice(current_index + 1, 0, item); } else { datas.value.push(item); table_add_row_helper.showProup({ refs: (<any>this).$refs, ref_name: "popup_edit" + (datas.value.length - 1), delay: 5, count: 0, max_count: 5000, }); } }, onContextMenu( evt: PointerEvent, row: StringKeyValueEntity, index: number ) { evt.preventDefault(); evt.stopPropagation(); target_dom.value = evt.srcElement; if (row) { current_index = index; show_context_menu.value = true; } }, deleteRow() { if (datas.value.length > current_index) { datas.value.splice(current_index, 1); } }, async onSubmit() { loading.value = true; try { await (type.value == 2 ? requestEditPlan() : requestAddPlan()); show_dialog.value = false; } catch {} loading.value = false; }, }; }, }); </script>