v1
This commit is contained in:
265
src/components/meetingBlock.vue
Normal file
265
src/components/meetingBlock.vue
Normal file
@@ -0,0 +1,265 @@
|
||||
<template>
|
||||
<div class="flex column items-center q-pa-lg">
|
||||
<div class="q-gutter-y-lg w100">
|
||||
<q-input
|
||||
v-model.trim="modelValue.name"
|
||||
dense
|
||||
filled
|
||||
class = "w100 fix-bottom-padding"
|
||||
:rules="[rules.name]"
|
||||
no-error-icon
|
||||
label-slot
|
||||
>
|
||||
<template #label>
|
||||
{{$t('meeting_info__name') }}
|
||||
<span class="text-red">*</span>
|
||||
</template>
|
||||
</q-input>
|
||||
|
||||
<q-input
|
||||
v-model="modelValue.description"
|
||||
dense
|
||||
filled
|
||||
autogrow
|
||||
class="w100 q-pt-sm"
|
||||
:label="$t('meeting_info__description')"
|
||||
/>
|
||||
|
||||
<div class="flex no-wrap justify-between q-gutter-x-md q-pt-sm">
|
||||
<q-input
|
||||
v-model="meetingDate"
|
||||
dense filled
|
||||
mask="##/##/####"
|
||||
:label="$t('meeting_info__date')"
|
||||
hide-bottom-space
|
||||
>
|
||||
<template #prepend>
|
||||
<q-icon name="event" class="cursor-pointer">
|
||||
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||
<q-date
|
||||
v-model="meetingDate"
|
||||
mask="DD/MM/YYYY"
|
||||
class="relative-position"
|
||||
:options="d => d >= date.formatDate(Date.now(), 'YYYY/MM/DD')"
|
||||
:navigation-min-year-month="date.formatDate(Date.now(), 'YYYY/MM')"
|
||||
>
|
||||
<div class="absolute" style="top: 0; right: 0;">
|
||||
<q-btn
|
||||
v-close-popup
|
||||
round flat
|
||||
color="white"
|
||||
icon="mdi-close"
|
||||
class="q-ma-sm"
|
||||
/>
|
||||
</div>
|
||||
</q-date>
|
||||
</q-popup-proxy>
|
||||
</q-icon>
|
||||
</template>
|
||||
</q-input>
|
||||
|
||||
<q-input
|
||||
v-model="meetingTime"
|
||||
dense filled
|
||||
mask="time"
|
||||
:rules="['time']"
|
||||
:label="$t('meeting_info__time')"
|
||||
hide-bottom-space
|
||||
>
|
||||
<template #prepend>
|
||||
<q-icon name="access_time" class="cursor-pointer">
|
||||
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||
<q-time
|
||||
v-model="meetingTime"
|
||||
mask="HH:mm"
|
||||
format24h
|
||||
class="relative-position"
|
||||
>
|
||||
<div class="absolute" style="top: 0; right: 0;">
|
||||
<q-btn
|
||||
v-close-popup
|
||||
round flat
|
||||
color="white"
|
||||
icon="mdi-close"
|
||||
class="q-ma-sm"
|
||||
/>
|
||||
</div>
|
||||
</q-time>
|
||||
</q-popup-proxy>
|
||||
</q-icon>
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
|
||||
<q-select
|
||||
v-model="modelValue.chat_attach"
|
||||
:options="chats"
|
||||
dense
|
||||
filled
|
||||
class="w100 q-pt-sm"
|
||||
:label = "$t('meeting_info__attach_chat')"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
emit-value
|
||||
map-options
|
||||
label-slot
|
||||
:disable="chats.length<=1"
|
||||
:placeholder="chats.length<=1 ? undefined : t('meeting_info__choose_chat_placeholder')"
|
||||
>
|
||||
<template #prepend>
|
||||
<q-icon name="mdi-chat-outline"/>
|
||||
</template>
|
||||
<template #label>
|
||||
{{$t('meeting_info__attach_chat') }}
|
||||
<span class="text-red" v-if="chats.length>1">*</span>
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<q-avatar rounded size="md">
|
||||
<img v-if="scope.opt.logo" :src="scope.opt.logo"/>
|
||||
<pn-auto-avatar v-else :name="scope.opt.name"/>
|
||||
</q-avatar>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.name }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
|
||||
<q-select
|
||||
v-model="modelValue.participants"
|
||||
:options="displayUsers"
|
||||
dense
|
||||
filled
|
||||
class="w100 file-input-fix q-pt-sm"
|
||||
:label = "$t('meeting_info__participants')"
|
||||
option-value="id"
|
||||
option-label="displayName"
|
||||
emit-value
|
||||
map-options
|
||||
use-chips
|
||||
multiple
|
||||
>
|
||||
<template #prepend>
|
||||
<q-icon name="mdi-account-outline"/>
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<q-avatar round size="md">
|
||||
<img v-if="scope.opt.photo" :src="scope.opt.photo"/>
|
||||
<pn-auto-avatar v-else :name="scope.opt.name"/>
|
||||
</q-avatar>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.displayName }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
|
||||
<q-file
|
||||
v-model="modelValue.files"
|
||||
:label="$t('meeting_info__attach_files')"
|
||||
outlined
|
||||
use-chips
|
||||
multiple
|
||||
dense
|
||||
class="file-input-fix q-pt-sm"
|
||||
>
|
||||
<template #prepend>
|
||||
<q-icon name="attach_file"/>
|
||||
</template>
|
||||
</q-file>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, watch, computed } from 'vue'
|
||||
import type { MeetingParams } from 'types/Meeting'
|
||||
import { useChatsStore } from 'stores/chats'
|
||||
import { useUsersStore } from 'stores/users'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { date } from 'quasar'
|
||||
const { t }= useI18n()
|
||||
|
||||
const modelValue = defineModel<MeetingParams>({
|
||||
required: true
|
||||
})
|
||||
|
||||
const emit = defineEmits(['valid'])
|
||||
const rulesErrorMessage = {
|
||||
name: t('meeting_info__error_name'),
|
||||
dateMeeting: t('meeting_info__error_date'),
|
||||
timeMeeting: t('meeting_info__error_time')
|
||||
}
|
||||
|
||||
const chatsStore = useChatsStore()
|
||||
const chats = computed(() => chatsStore.chats)
|
||||
|
||||
const usersStore = useUsersStore()
|
||||
const users = computed(() => usersStore.users)
|
||||
|
||||
const displayUsers = computed(() => {
|
||||
return users.value
|
||||
.map(el => ({ ...el, displayName: usersStore.userNameById(el.id) }))
|
||||
})
|
||||
|
||||
const meetingDate = computed({
|
||||
get: () => date.formatDate(modelValue.value.meet_date, 'DD/MM/YYYY'),
|
||||
set: (d) => updateDateTime(d, meetingTime.value)
|
||||
})
|
||||
|
||||
const meetingTime = computed({
|
||||
get: () => date.formatDate(modelValue.value.meet_date, 'HH:mm'),
|
||||
set: (t) => updateDateTime(meetingDate.value, t)
|
||||
})
|
||||
|
||||
function updateDateTime(dateStr: string, timeStr: string) {
|
||||
if (dateStr.length === 10 && timeStr.length === 5) {
|
||||
const newDate = date.extractDate(`${dateStr} ${timeStr}`, 'DD/MM/YYYY HH:mm')
|
||||
if (!isNaN(newDate.getTime())) {
|
||||
modelValue.value.meet_date = newDate.getTime()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const rules = {
|
||||
name: (val: MeetingParams['name']) => !!val?.trim() || rulesErrorMessage['name']
|
||||
}
|
||||
|
||||
const isValid = computed(() => {
|
||||
const checkName = rules.name(modelValue.value.name)
|
||||
return { name: checkName && (checkName !== rulesErrorMessage['name']) }
|
||||
})
|
||||
|
||||
watch(isValid, (newVal) => {
|
||||
const allValid = Object.values(newVal).every(v => v)
|
||||
emit('valid', allValid)
|
||||
}, { immediate: true})
|
||||
|
||||
onMounted(() => {
|
||||
if (chats.value.length === 1 && !modelValue.value.chat_attach) {
|
||||
modelValue.value.chat_attach = chats.value[0]?.id ?? null
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.fix-bottom-padding.q-field--with-bottom {
|
||||
padding-bottom: 0 !important
|
||||
}
|
||||
|
||||
.file-input-fix :deep(.q-field__append) {
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.file-input-fix :deep(.q-field__prepend) {
|
||||
height: auto !important;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user