refactor: migrate frontend event page
This commit is contained in:
@@ -81,7 +81,7 @@ const windowRange = computed<WindowRange>(() => {
|
||||
return { startTs, endTs, year, month1 }
|
||||
})
|
||||
|
||||
// Analyse phase: build the 42 cells and expand every event (loop expansion +
|
||||
// Analyse phase: build the 6 * 7 cells and expand every event (loop expansion +
|
||||
// multi-day splitting). Deliberately does NOT read the visibility maps so it
|
||||
// stays cached across visibility toggles.
|
||||
const expandedCells = computed<CalendarCell[]>(() => {
|
||||
@@ -89,10 +89,10 @@ const expandedCells = computed<CalendarCell[]>(() => {
|
||||
const owned = ownedUuids.value
|
||||
const myTz = myTimezone.value
|
||||
|
||||
// build the 42 day cells
|
||||
// build the 6 * 7 day cells
|
||||
const cells: CalendarCell[] = []
|
||||
const iter = new Date(startTs * 60000)
|
||||
for (let i = 0; i < 42; i++) {
|
||||
for (let i = 0; i < 6 * 7; i++) {
|
||||
cells.push({
|
||||
month: iter.getMonth() + 1,
|
||||
day: iter.getDate(),
|
||||
@@ -119,17 +119,19 @@ const expandedCells = computed<CalendarCell[]>(() => {
|
||||
if (typeof result === 'undefined') continue
|
||||
const loopText = resolveLoopRules4Text(item[8], item[5], item[7])
|
||||
const tzWarn = myTz !== item[7]
|
||||
// lock icon = event is in one of MY collections (belongTo). (Legacy
|
||||
// lock icon = event is NOT in one of MY collections (belongTo). (Legacy
|
||||
// mistakenly keyed this by the event uuid; fixed per migration decision.)
|
||||
const isLocked = owned.has(item[1])
|
||||
const isLocked = !owned.has(item[1])
|
||||
|
||||
for (const range of result) {
|
||||
const it0 = range[0]
|
||||
const it1 = range[1]
|
||||
// try get event belong to which cell
|
||||
const eventDateTime = new Date(it0 * 60000)
|
||||
let count = Math.floor((it0 - startTs) / DAY1_SPAN)
|
||||
let exitFlag = false
|
||||
while (count < 42) {
|
||||
// then split event
|
||||
while (count < 6 * 7) {
|
||||
const eventItem: ScheduleEventItem = {
|
||||
uuid: item[0],
|
||||
belongTo: item[1],
|
||||
|
||||
@@ -1,8 +1,673 @@
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useTokenStore } from '@/stores/token';
|
||||
import MessageBox from '@/components/MessageBox.vue';
|
||||
import DateTimePicker, { TabType } from '@/components/DateTimePicker.vue';
|
||||
import { goToCalendar } from '@/router';
|
||||
import {
|
||||
getDetail as apiCalendarGetDetail,
|
||||
add as apiCalendarAdd,
|
||||
update as apiCalendarUpdate,
|
||||
deserializeDescription,
|
||||
serializeDescription,
|
||||
type CalendarRow,
|
||||
} from '@/api/calendar';
|
||||
import { getFullOwn as apiCollectionGetFullOwn, type CollectionRow } from '@/api/collection';
|
||||
import { resolveLoopRules4UI, getDayInMonth } from '@/utils/datetime';
|
||||
import { WEEK_NAMES } from '@/utils/i18n';
|
||||
import { format, getWeekday, DEFAULT_COLOR } from '@/utils/utils';
|
||||
|
||||
const token = useTokenStore();
|
||||
const route = useRoute();
|
||||
const messagebox = ref<InstanceType<typeof MessageBox>>();
|
||||
const picker = ref<InstanceType<typeof DateTimePicker>>();
|
||||
|
||||
// the event being edited (undefined = add mode)
|
||||
const editingEvent = ref<CalendarRow | undefined>(undefined);
|
||||
|
||||
// basic fields
|
||||
const title = ref<string>('');
|
||||
const description = ref<string>('');
|
||||
const color = ref<string>(DEFAULT_COLOR);
|
||||
const belongTo = ref<string>('');
|
||||
const collections = ref<CollectionRow[]>([]);
|
||||
|
||||
// datetime (3 slots share one picker)
|
||||
const startDt = ref<Date>(new Date());
|
||||
const endDt = ref<Date>(new Date());
|
||||
const loopStopDt = ref<Date>(new Date());
|
||||
const editingSlot = ref<'start' | 'end' | 'loopstop'>('start');
|
||||
|
||||
// loop method
|
||||
const loopMethod = ref<'never' | 'day' | 'week' | 'month' | 'year'>('never');
|
||||
const loopDaySpan = ref<string>('1');
|
||||
const loopWeekSpan = ref<string>('1');
|
||||
const loopWeekChecks = ref<boolean[]>([false, false, false, false, false, false, false]);
|
||||
const loopMonthSpan = ref<string>('1');
|
||||
const loopMonthMode = ref<'A' | 'B' | 'C' | 'D'>('A');
|
||||
const loopYearSpan = ref<string>('1');
|
||||
|
||||
// loop stop
|
||||
const loopStop = ref<'forever' | 'datetime' | 'times'>('forever');
|
||||
const loopStopTimes = ref<string>('1');
|
||||
|
||||
// strict mode + timezone
|
||||
const strictMode = ref<'strict' | 'rough'>('strict');
|
||||
const timezoneOption = ref<'keep' | 'replace'>('keep');
|
||||
const showTimezoneBox = ref<boolean>(false);
|
||||
|
||||
// region: v-show conditions (legacy RefreshRadioDiaplay)
|
||||
|
||||
const showBoxLoopStop = computed<boolean>(() => loopMethod.value !== 'never');
|
||||
const showBoxLoopDay = computed<boolean>(() => loopMethod.value === 'day');
|
||||
const showBoxLoopWeek = computed<boolean>(() => loopMethod.value === 'week');
|
||||
const showBoxLoopMonth = computed<boolean>(() => loopMethod.value === 'month');
|
||||
const showBoxLoopYear = computed<boolean>(() => loopMethod.value === 'year');
|
||||
const showBoxStrictMode = computed<boolean>(() => loopMethod.value === 'month' || loopMethod.value === 'year');
|
||||
const showBoxLoopStopDateTime = computed<boolean>(() => loopStop.value === 'datetime');
|
||||
const showBoxLoopStopTimes = computed<boolean>(() => loopStop.value === 'times');
|
||||
|
||||
// endregion
|
||||
|
||||
// region: month-loop option labels (legacy RefreshLoopMonthType)
|
||||
|
||||
const monthOptionLabels = computed(() => {
|
||||
const d = getDayInMonth(startDt.value.getFullYear(), startDt.value.getMonth() + 1, startDt.value.getDate());
|
||||
return {
|
||||
a: format('Day {0} in month', d[0]),
|
||||
b: format('Day {0} from the end of the month', d[1]),
|
||||
c: format('Day {1} in week {0}', d[2], d[3] + 1),
|
||||
d: format('Day {1} in week {0} from the end of the month', d[4], d[5] + 1),
|
||||
};
|
||||
})
|
||||
|
||||
// endregion
|
||||
|
||||
// region: Helpers
|
||||
|
||||
// Given a Date whose UTC components represent a wall-clock in the event's
|
||||
// timezone, return a Date whose LOCAL components equal that wall-clock. The
|
||||
// DateTimePicker edits via local accessors, so this lets us display/edit the
|
||||
// event's wall-clock directly regardless of the browser timezone.
|
||||
const wallClockToLocal = (utcWall: Date): Date => {
|
||||
return new Date(
|
||||
utcWall.getUTCFullYear(),
|
||||
utcWall.getUTCMonth(),
|
||||
utcWall.getUTCDate(),
|
||||
utcWall.getUTCHours(),
|
||||
utcWall.getUTCMinutes(),
|
||||
);
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region: Init
|
||||
|
||||
const init = async () => {
|
||||
const uuid = route.params.uuid as string | undefined;
|
||||
let isAdd = true;
|
||||
if (typeof uuid === 'string' && uuid !== '') {
|
||||
const ev = await apiCalendarGetDetail(token.currentToken, uuid);
|
||||
if (typeof ev !== 'undefined') {
|
||||
editingEvent.value = ev;
|
||||
isAdd = false;
|
||||
}
|
||||
}
|
||||
|
||||
const ev = editingEvent.value;
|
||||
const desc = isAdd ? undefined : deserializeDescription(ev![3]);
|
||||
|
||||
// title / description / color
|
||||
title.value = isAdd ? '' : ev![2];
|
||||
description.value = isAdd ? '' : desc!.description;
|
||||
color.value = isAdd ? DEFAULT_COLOR : desc!.color;
|
||||
|
||||
// collection select
|
||||
const collectionsRv = await apiCollectionGetFullOwn(token.currentToken);
|
||||
collections.value = collectionsRv ?? [];
|
||||
belongTo.value = isAdd ? '' : ev![1];
|
||||
|
||||
// start / end datetime
|
||||
if (isAdd) {
|
||||
const cur = new Date();
|
||||
cur.setMilliseconds(0);
|
||||
cur.setSeconds(0);
|
||||
cur.setMinutes(0);
|
||||
startDt.value = new Date(cur.getTime());
|
||||
cur.setHours(cur.getHours() + 2);
|
||||
endDt.value = new Date(cur.getTime());
|
||||
} else {
|
||||
// (eventDateTime + tzOffset) * 60000 yields a Date whose UTC fields are the
|
||||
// event's wall-clock in its own timezone; relabel to local for the picker.
|
||||
startDt.value = wallClockToLocal(new Date((ev![5] + ev![7]) * 60000));
|
||||
endDt.value = wallClockToLocal(new Date((ev![6] + ev![7]) * 60000));
|
||||
}
|
||||
|
||||
// timezone box (only when editing AND event tz != browser tz)
|
||||
timezoneOption.value = 'keep';
|
||||
const nowtime = new Date();
|
||||
showTimezoneBox.value = (!isAdd) && (-nowtime.getTimezoneOffset()) !== ev![7];
|
||||
|
||||
// loop rules
|
||||
let loopIsAdd = isAdd;
|
||||
let data: ReturnType<typeof resolveLoopRules4UI> = undefined;
|
||||
if (!isAdd) {
|
||||
data = resolveLoopRules4UI(ev![8]);
|
||||
if (typeof data === 'undefined') loopIsAdd = true;
|
||||
}
|
||||
|
||||
// defaults (applied regardless of mode)
|
||||
loopMonthMode.value = 'A';
|
||||
loopWeekChecks.value = [false, false, false, false, false, false, false];
|
||||
loopWeekChecks.value[getWeekday(nowtime)] = true;
|
||||
strictMode.value = 'strict';
|
||||
|
||||
if (loopIsAdd) {
|
||||
loopMethod.value = 'never';
|
||||
} else {
|
||||
const d = data!;
|
||||
switch (d[0][0]) {
|
||||
case 0: {
|
||||
// year: [0, isStrict, yearSpan]
|
||||
const r = d[0] as [number, boolean, number];
|
||||
loopMethod.value = 'year';
|
||||
loopYearSpan.value = String(r[2]);
|
||||
strictMode.value = r[1] ? 'strict' : 'rough';
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
// month: [1, isStrict, mode, monthSpan]
|
||||
const r = d[0] as [number, boolean, 'A' | 'B' | 'C' | 'D', number];
|
||||
loopMethod.value = 'month';
|
||||
loopMonthSpan.value = String(r[3]);
|
||||
loopMonthMode.value = r[2];
|
||||
strictMode.value = r[1] ? 'strict' : 'rough';
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// week: [2, 7 booleans, weekSpan]
|
||||
const r = d[0] as [number, boolean, boolean, boolean, boolean, boolean, boolean, boolean, number];
|
||||
loopMethod.value = 'week';
|
||||
loopWeekSpan.value = String(r[8]);
|
||||
loopWeekChecks.value = [r[1], r[2], r[3], r[4], r[5], r[6], r[7]];
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
// day: [3, daySpan]
|
||||
const r = d[0] as [number, number];
|
||||
loopMethod.value = 'day';
|
||||
loopDaySpan.value = String(r[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loopStopDt default now
|
||||
loopStopDt.value = new Date();
|
||||
|
||||
if (loopIsAdd) {
|
||||
loopStop.value = 'forever';
|
||||
} else {
|
||||
const d = data!;
|
||||
switch (d[1][0]) {
|
||||
case 0:
|
||||
loopStop.value = 'forever';
|
||||
break;
|
||||
case 1: {
|
||||
const r = d[1] as [number, number];
|
||||
loopStop.value = 'datetime';
|
||||
loopStopDt.value = wallClockToLocal(new Date((r[1] + ev![7]) * 60000));
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
const r = d[1] as [number, number];
|
||||
loopStop.value = 'times';
|
||||
loopStopTimes.value = String(r[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region: Picker
|
||||
|
||||
const openStartPicker = () => {
|
||||
editingSlot.value = 'start';
|
||||
picker.value?.modal(startDt.value, TabType.Minute);
|
||||
}
|
||||
|
||||
const openEndPicker = () => {
|
||||
editingSlot.value = 'end';
|
||||
picker.value?.modal(endDt.value, TabType.Minute);
|
||||
}
|
||||
|
||||
const openLoopStopPicker = () => {
|
||||
editingSlot.value = 'loopstop';
|
||||
picker.value?.modal(loopStopDt.value, TabType.Day);
|
||||
}
|
||||
|
||||
const onPickerConfirm = (d: Date) => {
|
||||
if (editingSlot.value === 'start') startDt.value = d;
|
||||
else if (editingSlot.value === 'end') endDt.value = d;
|
||||
else loopStopDt.value = d;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region: Quick helpers
|
||||
|
||||
const spot = () => {
|
||||
const d = new Date(startDt.value.getTime());
|
||||
d.setMinutes(d.getMinutes() + 1);
|
||||
endDt.value = d;
|
||||
}
|
||||
|
||||
const fullDay = () => {
|
||||
const s = new Date(startDt.value.getTime());
|
||||
s.setMinutes(0);
|
||||
s.setHours(0);
|
||||
startDt.value = s;
|
||||
const e = new Date(s.getTime());
|
||||
e.setMinutes(59);
|
||||
e.setHours(23);
|
||||
endDt.value = e;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region: Form collection + submit
|
||||
|
||||
// returns [belongTo, title, description, start, end, tzOffset, loopRules]
|
||||
// or undefined on validation failure.
|
||||
const getForm = (): [string, string, string, number, number, number, string] | undefined => {
|
||||
// basic
|
||||
if (title.value === '') return undefined;
|
||||
if (description.value === '') return undefined;
|
||||
if (color.value === '') return undefined;
|
||||
if (!belongTo.value) return undefined;
|
||||
|
||||
const isAdd = typeof editingEvent.value === 'undefined';
|
||||
const keepTimezone = timezoneOption.value === 'keep';
|
||||
const isStrict = strictMode.value === 'strict';
|
||||
|
||||
// time
|
||||
let eventDateTimeStart: number;
|
||||
let eventDateTimeEnd: number;
|
||||
let timezoneOffset: number;
|
||||
if (!isAdd && !keepTimezone) {
|
||||
// keep the event's original timezone (event[7]); recompute the instant
|
||||
// from the wall-clock components (NOT getTime, which uses browser tz).
|
||||
timezoneOffset = editingEvent.value![7];
|
||||
eventDateTimeStart = Math.floor(Date.UTC(
|
||||
startDt.value.getFullYear(), startDt.value.getMonth(), startDt.value.getDate(),
|
||||
startDt.value.getHours(), startDt.value.getMinutes(),
|
||||
) / 60000) - timezoneOffset;
|
||||
eventDateTimeEnd = Math.floor(Date.UTC(
|
||||
endDt.value.getFullYear(), endDt.value.getMonth(), endDt.value.getDate(),
|
||||
endDt.value.getHours(), endDt.value.getMinutes(),
|
||||
) / 60000) - timezoneOffset;
|
||||
} else {
|
||||
// use the browser timezone; the wall-clock IS local so getTime is correct.
|
||||
timezoneOffset = -startDt.value.getTimezoneOffset();
|
||||
eventDateTimeStart = Math.floor(startDt.value.getTime() / 60000);
|
||||
eventDateTimeEnd = Math.floor(endDt.value.getTime() / 60000);
|
||||
}
|
||||
|
||||
// loopRules
|
||||
let loopRules: string;
|
||||
if (loopMethod.value === 'never') {
|
||||
loopRules = '';
|
||||
} else if (loopMethod.value === 'day') {
|
||||
loopRules = `D${loopDaySpan.value}`;
|
||||
} else if (loopMethod.value === 'week') {
|
||||
let cache = '';
|
||||
for (let i = 0; i < 7; i++) cache += loopWeekChecks.value[i] ? 'T' : 'F';
|
||||
loopRules = `W${cache}${loopWeekSpan.value}`;
|
||||
} else if (loopMethod.value === 'month') {
|
||||
loopRules = `M${isStrict ? 'S' : 'R'}${loopMonthMode.value}${loopMonthSpan.value}`;
|
||||
} else {
|
||||
// year
|
||||
loopRules = `Y${isStrict ? 'S' : 'R'}${loopYearSpan.value}`;
|
||||
}
|
||||
|
||||
// loop stop (only when looping)
|
||||
if (loopRules !== '') {
|
||||
loopRules += '-';
|
||||
if (loopStop.value === 'forever') {
|
||||
loopRules += 'F';
|
||||
} else if (loopStop.value === 'datetime') {
|
||||
let ts: number;
|
||||
if (!isAdd && !keepTimezone) {
|
||||
ts = Math.floor(Date.UTC(
|
||||
loopStopDt.value.getFullYear(), loopStopDt.value.getMonth(), loopStopDt.value.getDate(),
|
||||
23, 59,
|
||||
) / 60000) - timezoneOffset;
|
||||
} else {
|
||||
ts = Math.floor(loopStopDt.value.getTime() / 60000);
|
||||
}
|
||||
loopRules += `D${ts}`;
|
||||
} else {
|
||||
// times
|
||||
loopRules += `T${loopStopTimes.value}`;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
belongTo.value,
|
||||
title.value,
|
||||
serializeDescription(description.value, color.value),
|
||||
eventDateTimeStart,
|
||||
eventDateTimeEnd,
|
||||
timezoneOffset,
|
||||
loopRules,
|
||||
];
|
||||
}
|
||||
|
||||
const submit = async () => {
|
||||
const submitData = getForm();
|
||||
if (typeof submitData === 'undefined') {
|
||||
messagebox.value?.show("Your filled event form is not fufilled or have error. Please check it and try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
const isAdd = typeof editingEvent.value === 'undefined';
|
||||
if (isAdd) {
|
||||
const result = await apiCalendarAdd(
|
||||
token.currentToken,
|
||||
submitData[0],
|
||||
submitData[1],
|
||||
submitData[2],
|
||||
submitData[3],
|
||||
submitData[4],
|
||||
submitData[6],
|
||||
submitData[5],
|
||||
);
|
||||
if (typeof result === 'undefined') {
|
||||
messagebox.value?.show("An add operation failed. It may caused by wrong arguments. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data.");
|
||||
} else {
|
||||
goToCalendar();
|
||||
}
|
||||
} else {
|
||||
const ev = editingEvent.value!;
|
||||
const result = await apiCalendarUpdate(
|
||||
token.currentToken,
|
||||
ev[0],
|
||||
ev[4],
|
||||
ev[1] === submitData[0] ? undefined : submitData[0],
|
||||
ev[2] === submitData[1] ? undefined : submitData[1],
|
||||
ev[3] === submitData[2] ? undefined : submitData[2],
|
||||
ev[5] === submitData[3] ? undefined : submitData[3],
|
||||
ev[6] === submitData[4] ? undefined : submitData[4],
|
||||
ev[8] === submitData[6] ? undefined : submitData[6],
|
||||
ev[7] === submitData[5] ? undefined : submitData[5],
|
||||
);
|
||||
if (typeof result === 'undefined') {
|
||||
messagebox.value?.show("An update operation failed. It may caused by wrong arguments or lost target. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data.");
|
||||
} else {
|
||||
goToCalendar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
goToCalendar();
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
onMounted(async () => {
|
||||
await init();
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>Congratulations</h1>
|
||||
<p>This is calendar event.</p>
|
||||
<div class="eventFormBody container" style="margin-top: 20px;">
|
||||
<h1 class="title">Edit Event</h1>
|
||||
|
||||
<section class="section">
|
||||
<div class="field">
|
||||
<label class="label">Title</label>
|
||||
<div class="control">
|
||||
<input v-model="title" class="input" type="text">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Description</label>
|
||||
<div class="control">
|
||||
<textarea v-model="description" class="textarea"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Color</label>
|
||||
<div class="control">
|
||||
<input v-model="color" class="input" type="color">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Collection</label>
|
||||
<div class="control">
|
||||
<div class="select">
|
||||
<select v-model="belongTo">
|
||||
<option v-for="c in collections" :key="c[0]" :value="c[0]">{{ c[1] }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2 class="subtitle">Start Date Time</h2>
|
||||
<a class="button" @click="openStartPicker">
|
||||
<span>{{ startDt.toLocaleString() }}</span>
|
||||
</a>
|
||||
|
||||
<h2 class="subtitle">Stop Date Time</h2>
|
||||
<div class="button-list">
|
||||
<div class="control">
|
||||
<a class="button is-link" @click="spot">Spot</a>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-link" @click="fullDay">Full day</a>
|
||||
</div>
|
||||
</div>
|
||||
<a class="button" @click="openEndPicker">
|
||||
<span>{{ endDt.toLocaleString() }}</span>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2 class="subtitle">Event Loop</h2>
|
||||
<div class="button-list">
|
||||
<label class="radio">
|
||||
<input type="radio" value="never" v-model="loopMethod">
|
||||
<span>Never</span>
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" value="day" v-model="loopMethod">
|
||||
<span>Day</span>
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" value="week" v-model="loopMethod">
|
||||
<span>Week</span>
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" value="month" v-model="loopMethod">
|
||||
<span>Month</span>
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" value="year" v-model="loopMethod">
|
||||
<span>Year</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div v-show="showBoxLoopDay">
|
||||
<div class="field">
|
||||
<label class="label">Day span</label>
|
||||
<div class="control">
|
||||
<input v-model="loopDaySpan" class="input spanpicker" type="number" min="1" max="100" step="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="showBoxLoopWeek">
|
||||
<div class="field">
|
||||
<label class="label">Week span</label>
|
||||
<div class="control">
|
||||
<input v-model="loopWeekSpan" class="input spanpicker" type="number" min="1" max="100" step="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Week options</label>
|
||||
<div class="button-list">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" v-model="loopWeekChecks[0]">
|
||||
<span>{{ WEEK_NAMES[0] }}</span>
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" v-model="loopWeekChecks[1]">
|
||||
<span>{{ WEEK_NAMES[1] }}</span>
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" v-model="loopWeekChecks[2]">
|
||||
<span>{{ WEEK_NAMES[2] }}</span>
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" v-model="loopWeekChecks[3]">
|
||||
<span>{{ WEEK_NAMES[3] }}</span>
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" v-model="loopWeekChecks[4]">
|
||||
<span>{{ WEEK_NAMES[4] }}</span>
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" v-model="loopWeekChecks[5]">
|
||||
<span>{{ WEEK_NAMES[5] }}</span>
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" v-model="loopWeekChecks[6]">
|
||||
<span>{{ WEEK_NAMES[6] }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="showBoxLoopMonth">
|
||||
<div class="field">
|
||||
<label class="label">Month span</label>
|
||||
<div class="control">
|
||||
<input v-model="loopMonthSpan" class="input spanpicker" type="number" min="1" max="100" step="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Month mode</label>
|
||||
<div class="button-list">
|
||||
<label class="radio">
|
||||
<input type="radio" value="A" v-model="loopMonthMode">
|
||||
<span>{{ monthOptionLabels.a }}</span>
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" value="B" v-model="loopMonthMode">
|
||||
<span>{{ monthOptionLabels.b }}</span>
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" value="C" v-model="loopMonthMode">
|
||||
<span>{{ monthOptionLabels.c }}</span>
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" value="D" v-model="loopMonthMode">
|
||||
<span>{{ monthOptionLabels.d }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="showBoxLoopYear">
|
||||
<div class="field">
|
||||
<label class="label">Year span</label>
|
||||
<div class="control">
|
||||
<input v-model="loopYearSpan" class="input spanpicker" type="number" min="1" max="100" step="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-show="showBoxLoopStop" class="section">
|
||||
<h2 class="subtitle">Event Loop Stop</h2>
|
||||
<div class="button-list">
|
||||
<label class="radio">
|
||||
<input type="radio" value="forever" v-model="loopStop">
|
||||
<span>Forever</span>
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" value="datetime" v-model="loopStop">
|
||||
<span>Date Time</span>
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" value="times" v-model="loopStop">
|
||||
<span>Times</span>
|
||||
</label>
|
||||
</div>
|
||||
<div v-show="showBoxLoopStopDateTime">
|
||||
<a class="button" @click="openLoopStopPicker">
|
||||
<span>{{ loopStopDt.toLocaleDateString() }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div v-show="showBoxLoopStopTimes">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input v-model="loopStopTimes" class="input spanpicker" type="number" min="1" max="100" step="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-show="showBoxStrictMode" class="section">
|
||||
<h2 class="subtitle">Strict Mode in Event Loop</h2>
|
||||
<p>You can choose strict mode or rough mode in following content. This is only effect on looped event.</p>
|
||||
<div class="button-list">
|
||||
<label class="radio">
|
||||
<input type="radio" value="strict" v-model="strictMode">
|
||||
<span>Strict Mode. If ordered day is not existing, skip it.</span>
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" value="rough" v-model="strictMode">
|
||||
<span>Rough mode. If ordered day is not existing, choose the day closing with original day to arrange
|
||||
event.</span>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-show="showTimezoneBox" class="section">
|
||||
<h2 class="subtitle">Timezone</h2>
|
||||
<p>The timezone of this event is not corresponding with your current timezone. All of date and time in this page
|
||||
are shown as the original timezone of this event. You can choose a timezone option in follwing content. If you
|
||||
are not familar with this, please pick keep timezone.</p>
|
||||
<div class="button-list">
|
||||
<label class="radio">
|
||||
<input type="radio" value="keep" v-model="timezoneOption">
|
||||
<span>Keep timezone</span>
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" value="replace" v-model="timezoneOption">
|
||||
<span>Use my timezone</span>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="button-list">
|
||||
<a class="button is-success" @click="submit">Submit</a>
|
||||
<a class="button" @click="cancel">Cancel</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<MessageBox ref="messagebox" />
|
||||
<DateTimePicker ref="picker" @confirm="onPickerConfirm" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped>
|
||||
.eventFormBody > section {
|
||||
border-top: 1px solid rgba(219, 219, 219, 0.5);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user