ปรับ ui checkin

This commit is contained in:
Tanyalak 2024-01-19 15:34:09 +07:00
parent 4dbf1de05d
commit f9ec074f03
10 changed files with 705 additions and 695 deletions

BIN
src/assets/logo1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View file

@ -129,10 +129,8 @@ onMounted(async () => {
<template>
<!-- Loading skeleton -->
<div v-if="!poiPlaceName" class="q-pa-md">
<div class="q-gutter-md">
<q-skeleton height="35vh" width="100%" class="bg-grey-4" />
</div>
<div v-if="!poiPlaceName" class="col-12">
<q-skeleton height="35vh" width="100%" class="bg-grey-4" />
</div>
<q-card v-show="poiPlaceName" bordered flat class="col-12 bg-grey-2 shadow-0">
@ -142,7 +140,7 @@ onMounted(async () => {
:class="
$q.screen.gt.xs
? 'q-pa-xs text-weight-medium text-grey-8'
: ' text-weight-medium text-grey-8'
: 'q-pa-xs text-weight-medium text-grey-8'
"
>
นทใกลเคยง

View file

@ -139,138 +139,139 @@ watch(
)
</script>
<template>
<q-card-section class="col q-pt-none">
<div class="row q-pa-sm q-col-q-gutter-sm">
<q-card
flat
bordered
:class="$q.screen.gt.xs ? 'col-12 bg-grey-1' : 'col-12 '"
>
<q-card-section class="bg-primary text-white q-pa-sm">
<div class="text-center text-bold">เวลาปจจ</div>
</q-card-section>
<!-- <div class="q-pa-sm text-primary">เวลาปจจ</div> -->
<q-card-section class="text-center q-pa-sm">
<div class="row q-gutter-md">
<div class="col">{{ date2Thai(dateNow) }}</div>
<div class="col">{{ timeNoew }}</div>
</div>
</q-card-section>
</q-card>
<q-card flat bordered class="col-12 q-mt-sm">
<div class="q-pa-sm">
<datepicker
:readonly="!statusAction"
v-model="date"
:locale="'th'"
:enableTimePicker="false"
week-start="0"
autoApply
outlined
lazy-rules
:max-date="dateNow"
>
<template #year="{ year }">
{{ year + 543 }}
</template>
<template #year-overlay-value="{ value }">
{{ parseInt(value + 543) }}
</template>
<template #trigger>
<q-input
:readonly="!statusAction"
ref="dateRef"
outlined
dense
:model-value="date !== null ? date2Thai(new Date(date)) : null"
:label="`${'วันที่ขอแก้ไข'}`"
format-header="YYYY-MM-DD"
lazy-rules
:rules="[(val) => !!val || 'กรุณาเลือกวันที่']"
hide-bottom-space
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input> </template
></datepicker>
<div class="col-12 row">
<div class="row q-col-q-gutter-sm q-pa-md">
<q-card
flat
bordered
:class="$q.screen.gt.xs ? 'col-12 bg-grey-1' : 'col-12 '"
>
<q-card-section class="bg-primary text-white q-pa-sm">
<div class="text-center text-bold">เวลาปจจ</div>
</q-card-section>
<!-- <div class="q-pa-sm text-primary">เวลาปจจ</div> -->
<q-card-section class="text-center q-pa-sm">
<div class="row q-gutter-md">
<div class="col">{{ date2Thai(dateNow) }}</div>
<div class="col">{{ timeNoew }}</div>
</div>
</q-card>
</q-card-section>
</q-card>
<!-- <q-card flat bordered class="q-pa-sm col-12 bg-grey-1 q-mt-sm" v-else>
<div class="row q-gutter-md text-grey-5">
<div class="col-1">
<q-icon color="grey-5" name="calendar_today" />
</div>
<div class="col">{{ dataByIdVal.checkInDate }}</div>
</div>
</q-card> -->
<q-card
flat
bordered
class="q-pa-sm col-12 q-mt-sm"
:style="{
borderColor: checkstatusBox ? '#B22222' : '',
borderWidth: checkstatusBox ? '2px' : '',
}"
>
<div class="row q-gutter-xs">
<div class="col-12">
<q-checkbox
keep-color
color="primary"
v-model="checkboxIn"
label="ขอแก้ไขเวลาเข้างาน"
/>
</div>
<div class="col-12">
<q-checkbox
keep-color
v-model="checkboxOut"
color="primary"
label="ขอแก้ไขเวลาออกงาน"
/>
</div>
</div>
</q-card>
<div
v-if="checkstatusBox"
class="text-red-9 q-pa-sm"
style="font-size: 10px"
>
กรณาเลอก
</div>
<q-card flat bordered class="q-pa-sm col-12 q-mt-sm">
<q-input
ref="reasonRef"
dense
<q-card flat bordered class="col-12 q-mt-sm">
<div class="q-pa-sm">
<datepicker
:readonly="!statusAction"
v-model="date"
:locale="'th'"
:enableTimePicker="false"
week-start="0"
autoApply
outlined
v-model="reason"
label="เหตุผล"
type="textarea"
:rows="4"
label-color="grey-5"
:rules="[(val) => !!val || 'กรุณากรอกเหตุผล']"
lazy-rules
hide-bottom-space
class="custom-aqua-border"
/>
</q-card>
:max-date="dateNow"
>
<template #year="{ year }">
{{ year + 543 }}
</template>
<template #year-overlay-value="{ value }">
{{ parseInt(value + 543) }}
</template>
<template #trigger>
<q-input
:readonly="!statusAction"
ref="dateRef"
outlined
dense
:model-value="date !== null ? date2Thai(new Date(date)) : null"
:label="`${'วันที่ขอแก้ไข'}`"
format-header="YYYY-MM-DD"
lazy-rules
:rules="[(val) => !!val || 'กรุณาเลือกวันที่']"
hide-bottom-space
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input> </template
></datepicker>
</div>
</q-card>
<!-- <q-card flat bordered class="q-pa-sm col-12 bg-grey-1 q-mt-sm" v-else>
<div class="row q-gutter-md text-grey-5">
<div class="col-1">
<q-icon color="grey-5" name="calendar_today" />
</div>
<div class="col">{{ dataByIdVal.checkInDate }}</div>
</div>
</q-card> -->
<q-card
flat
bordered
class="q-pa-sm col-12 q-mt-sm"
:style="{
borderColor: checkstatusBox ? '#B22222' : '',
borderWidth: checkstatusBox ? '2px' : '',
}"
>
<div class="row q-gutter-xs">
<div class="col-12">
<q-checkbox
keep-color
color="primary"
v-model="checkboxIn"
label="ขอแก้ไขเวลาเข้างาน"
/>
</div>
<div class="col-12">
<q-checkbox
keep-color
v-model="checkboxOut"
color="primary"
label="ขอแก้ไขเวลาออกงาน"
/>
</div>
</div>
</q-card>
<div
v-if="checkstatusBox"
class="text-red-9 q-pa-sm"
style="font-size: 10px"
>
กรณาเลอก
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn dense color="secondary" label="บันทึก" @click="onCkickSave" />
</q-card-actions>
<q-card flat bordered class="q-pa-sm col-12 q-mt-sm">
<q-input
ref="reasonRef"
dense
outlined
v-model="reason"
label="เหตุผล"
type="textarea"
:rows="4"
label-color="grey-5"
:rules="[(val) => !!val || 'กรุณากรอกเหตุผล']"
lazy-rules
hide-bottom-space
class="custom-aqua-border"
/>
</q-card>
</div>
<div class="col-12"><q-separator /></div>
<div class="row col-12 q-pa-sm">
<q-space/>
<q-btn color="secondary" label="บันทึก" @click="onCkickSave" />
</div>
</div>
</template>
<style scoped></style>

View file

@ -41,7 +41,7 @@ watch(
</script>
<template>
<q-dialog v-model="props.modal" persistent>
<q-card class="column" style="width: 300px; min-height: 600px">
<q-card class="column" :style="$q.screen.lt.sm ? 'width: 100%;': 'width: 450px;'">
<HeaderPopup :title="props.title" :clickClose="clickClosePopup" />
<FormTime
:dataById="data"

View file

@ -376,7 +376,7 @@ function onClickOpenPopupRejrct(data: any) {
icon="edit"
label="ขอแก้ไข"
color="info"
size="sm"
size="12px"
@click="openPopup(props.row)"
/>
</div>

View file

@ -58,89 +58,86 @@ const monthYearThai = (val: DataDateMonthObject) => {
}
</script>
<template>
<div class="q-pb-sm row">
<div class="items-center col-12 row q-gutter-sm">
<datepicker
v-if="tab === 'history'"
menu-class-name="modalfix"
v-model="filterYear"
class="col-xs-5 col-sm-3 col-md-2"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
@update:modelValue="filterYearFn('year')"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
lazy-rules
outlined
:model-value="filterYear + 543"
:label="`${'ปีงบประมาณ'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
<div class="items-center col-12 row q-pb-sm">
<datepicker
v-if="tab === 'history'"
menu-class-name="modalfix"
v-model="filterYear"
class="col-xs-5 col-sm-3 col-md-2"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
@update:modelValue="filterYearFn('year')"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
lazy-rules
outlined
:model-value="filterYear + 543"
:label="`${'ปีงบประมาณ'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
<datepicker
v-else-if="tab === 'time'"
menu-class-name="modalfix"
v-model="dateMonth"
class="col-xs-5 col-sm-3 col-md-2"
:locale="'th'"
autoApply
month-picker
:enableTimePicker="false"
@update:modelValue="filterYearFn('mount')"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
lazy-rules
outlined
:model-value="monthYearThai(dateMonth)"
:label="`${'ปีงบประมาณ'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
<datepicker
v-else-if="tab === 'time'"
menu-class-name="modalfix"
v-model="dateMonth"
class="col-xs-5 col-sm-3 col-md-2"
:locale="'th'"
autoApply
month-picker
:enableTimePicker="false"
@update:modelValue="filterYearFn('mount')"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
lazy-rules
outlined
:model-value="monthYearThai(dateMonth)"
:label="`${'ปีงบประมาณ'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
<q-space />
<q-btn
unelevated
outline
icon="add"
color="light-blue"
:class="$q.screen.gt.xs ? 'q-px-sm bg-blue-1' : ''"
:label="$q.screen.gt.xs ? 'เพิ่มรายการลงเวลากรณีพิเศษ' : ''"
@click="onClickopen"
/>
</div>
<q-space />
<q-btn
unelevated
icon="add"
:dense="$q.screen.lt.sm"
color="secondary"
:label="$q.screen.gt.xs ? 'เพิ่มรายการลงเวลากรณีพิเศษ' : ''"
@click="onClickopen"
/>
</div>
<Popup

View file

@ -1,6 +1,7 @@
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '@/views/HomeView.vue'
import MapView from '@/views/MapView.vue'
import MianView from '@/views/MianView.vue'
import keycloak from '@/plugins/keycloak'
@ -8,56 +9,63 @@ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView,
meta: {
Auth: true,
},
},
{
path: '/map',
name: 'map',
component: MapView,
meta: {
Auth: true,
},
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('@/views/AboutView.vue'),
meta: {
Auth: true,
},
},
{
path: '/history',
name: 'history',
component: () => import('@/views/HistoryView.vue'),
meta: {
Auth: true,
},
},
// {
// path: '/camera',
// name: 'camera',
// component: () => import('@/views/SampleCamera.vue'),
// },
/**
* 404 Not Found
* ref: https://router.vuejs.org/guide/essentials/dynamic-matching.html#catch-all-404-not-found-route
*/
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('@/views/ErrorNotFoundPage.vue'),
meta: {
Auth: true,
},
path: "/",
name: "home",
component: MianView,
children: [
{
path: '/',
name: 'home',
component: HomeView,
meta: {
Auth: true,
},
},
{
path: '/map',
name: 'map',
component: MapView,
meta: {
Auth: true,
},
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('@/views/AboutView.vue'),
meta: {
Auth: true,
},
},
{
path: '/history',
name: 'history',
component: () => import('@/views/HistoryView.vue'),
meta: {
Auth: true,
},
},
// {
// path: '/camera',
// name: 'camera',
// component: () => import('@/views/SampleCamera.vue'),
// },
/**
* 404 Not Found
* ref: https://router.vuejs.org/guide/essentials/dynamic-matching.html#catch-all-404-not-found-route
*/
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('@/views/ErrorNotFoundPage.vue'),
meta: {
Auth: true,
},
},
]
},
],
})

View file

@ -131,7 +131,6 @@ watch(
<div
class="bg-secondary text-white col-12 row items-center q-px-md q-py-sm"
>
<div class="col-2">
<q-btn
icon="arrow_back"
round
@ -140,20 +139,11 @@ watch(
color="white"
@click="router.go(-1)"
/>
</div>
<q-space />
<span class="text-body1 text-weight-bold col-8 text-center"
<span class="text-body1 text-weight-bold q-pl-md"
>ประวการลงเวลา
</span>
<div class="col-2"></div>
</div>
<div class="col-12 q-pa-md text-grey-9">
<ToolBar
:fetchData="functionFetch"
@update:year="updateYear"
:tab="stores.tab"
/>
<q-card bordered flat>
<div class="col-12 text-grey-9">
<q-tabs
v-model="stores.tab"
dense
@ -172,6 +162,11 @@ watch(
<q-tab-panels v-model="stores.tab" animated>
<q-tab-panel name="history">
<ToolBar
:fetchData="functionFetch"
@update:year="updateYear"
:tab="stores.tab"
/>
<TableHistory
:fetchData="functionFetch"
:page-size="pageSize"
@ -185,6 +180,11 @@ watch(
</q-tab-panel>
<q-tab-panel name="time">
<ToolBar
:fetchData="functionFetch"
@update:year="updateYear"
:tab="stores.tab"
/>
<TableHistory
:fetchData="functionFetch"
:page-size="pageSize"
@ -198,7 +198,6 @@ watch(
</q-tab-panel>
</q-tab-panels>
<!-- </q-card> -->
</q-card>
<!-- <Table
:fetchData="fetchlistHistory"
:page-size="pageSize"

View file

@ -10,7 +10,6 @@ import config from '@/app.config'
// import Type
import type { FormRef } from '@/interface/response/checkin'
import type { notiType } from '@/interface/index/Main'
// import components
import MapCheck from '@/components/AscGISMap.vue'
@ -24,10 +23,7 @@ const {
showLoader,
hideLoader,
messageError,
dialogRemove,
success,
} = mixin
const router = useRouter()
const $q = useQuasar()
const dialogTime = ref<boolean>(false)
@ -51,79 +47,6 @@ async function fetchCheckTime() {
})
}
const notiTrigger = ref<boolean>(false)
const notiList = ref<notiType[]>([])
const totalNotiList = ref<number>(0)
/** function เรียกข้อมุลแจ้งเตือน */
async function fetchNotifications(index: number, type: string) {
showLoader()
await http
.get(config.API.msgNotificate + `?page=${index}&pageSize=${20}`)
.then((res) => {
const response = res.data.result.data
const list: notiType[] = []
if (type === 'DEL') {
totalNotiList.value = res.data.result.total
if (notiList.value.length === 14) {
notiList.value = []
response.map((e: any) => {
list.push({
id: e.id,
sender:
e.createdFullName == '' || e.createdFullName == null
? 'เจ้าหน้าที่'[0]
: e.createdFullName[0],
body: e.body ?? '',
timereceive: e.receiveDate ? date2Thai(e.receiveDate) : '-',
})
})
notiList.value.push(...list)
}
} else {
response.map((e: any) => {
list.push({
id: e.id,
sender:
e.createdFullName == '' || e.createdFullName == null
? 'เจ้าหน้าที่'[0]
: e.createdFullName[0],
body: e.body ?? '',
timereceive: e.receiveDate ? date2Thai(e.receiveDate) : '-',
})
})
notiList.value.push(...list)
totalNotiList.value = res.data.result.total
}
})
.catch((err) => {
messageError($q, err)
})
.finally(() => {
hideLoader()
})
}
/**
* function ลบรายการแจงเตอน
* @param id noti
*/
async function onClickDelete(id: string, index: number) {
dialogRemove($q, async () => {
await http
.delete(config.API.msgId(id))
.then(() => {
notiList.value.splice(index, 1)
success($q, 'ลบข้อมูลสำเร็จ')
})
.catch((e) => {
messageError($q, e)
})
.finally(async () => {
fetchNotifications(1, 'DEL')
hideLoader()
})
})
}
/** ref อัพเดทเวลา*/
const dateNow = ref<Date>(new Date())
@ -307,27 +230,6 @@ async function onClickConfirm() {
dialogTime.value = false
}
/** function logout*/
function onClickLogout() {
$q.dialog({
title: 'ยืนยันการออกจากระบบ',
message: `ต้องการออกจากระบบใช้หรือไม่?`,
cancel: 'ยกเลิก',
ok: 'ยืนยัน',
persistent: true,
}).onOk(() => {
keycloak.logout()
})
}
function onLoad(index: any, done: any) {
if (notiList.value.length < totalNotiList.value) {
setTimeout(() => {
fetchNotifications(index + 1, 'NOMAL')
done()
}, 2000)
}
}
// class
const getClass = (val: boolean) => {
@ -341,11 +243,6 @@ const fullName = ref<string>('')
/** Hook*/
onMounted(async () => {
await fetchCheckTime()
await fetchNotifications(1, 'NOMAL')
if (keycloak.tokenParsed != null) {
fullName.value = keycloak.tokenParsed.name
}
updateClock()
})
</script>
@ -356,283 +253,16 @@ onMounted(async () => {
<q-card flat class="row col-12 cardNone">
<!-- <q-header elevated class="bg-purple"> -->
<q-toolbar :class="getClass(stetusCheckin)">
<q-btn
icon="history"
unelevated
rounded
dense
flat
color="white"
:label="$q.screen.gt.xs ? 'ประวัติการลงเวลา' : ''"
:class="$q.screen.gt.xs ? 'q-px-md' : ''"
@click="router.push('/history')"
/>
<q-space />
<strong v-if="stetusCheckin" style="font-size: 1rem">
ลงเวลาเขางาน
</strong>
<strong v-else style="font-size: 1rem">ลงเวลาออกงาน</strong>
<q-space />
<q-btn
round
dense
flat
size="13px"
class="q-mx-md"
:disable="totalNotiList == 0"
>
<q-icon name="notifications" size="24px" color="white" />
<q-badge
rounded
v-show="totalNotiList !== 0"
color="negative"
text-color="white"
floating
>{{ totalNotiList }}</q-badge
>
<q-menu v-model="notiTrigger" max-width="480px" :offset="[0, 10]">
<div class="q-px-md q-py-sm row col-12 items-center">
<div class="text-subtitle1 text-weight-medium">
การแจงเตอน
</div>
</div>
<q-infinite-scroll @load="onLoad" :offset="250">
<div
v-for="(item, index) in notiList"
:key="index"
class="caption"
>
<q-item v-ripple class="mytry q-py-sm" dense>
<q-item-section avatar top style="min-width: 10px">
<q-avatar
rounded
color="primary"
size="25px"
text-color="white"
>
<span class="text-weight-medium text-uppercase">{{
item.sender[0]
}}</span>
</q-avatar>
</q-item-section>
<q-item-section>
<q-item-label caption class="text-black">{{
item.body
}}</q-item-label>
<q-item-label
caption
class="row items-center text-grey-7"
style="font-size: 12px"
>{{ item.timereceive }}</q-item-label
>
</q-item-section>
<div>
<q-btn
size="sm"
unelevated
dense
icon="mdi-close"
class="mybtn q-mx-xs"
@click="onClickDelete(item.id, index)"
></q-btn>
</div>
</q-item>
</div>
<template v-slot:loading v-if="notiList.length < totalNotiList">
<div class="row justify-center q-my-md">
<q-spinner-dots color="primary" size="40px" />
</div>
</template>
</q-infinite-scroll>
<!-- <q-list
style="min-width: 300px"
v-for="n in notiList"
:key="n.id"
>
<q-item v-ripple class="mytry q-py-xs" dense>
<q-item-section avatar top style="min-width: 40px">
<q-avatar color="primary" size="22px" text-color="white">
<span class="text-weight-medium text-uppercase">{{
n.sender
}}</span>
</q-avatar>
</q-item-section>
<q-item-section>
<q-item-label caption class="text-black">{{
n.body
}}</q-item-label>
<q-item-label
caption
class="row items-center text-grey-7"
>{{ date2Thai(n.timereceive) }}</q-item-label
>
</q-item-section>
<q-btn
size="sm"
unelevated
dense
icon="mdi-close"
class="mybtn q-mx-xs"
@click="onClickDelete(n.id)"
></q-btn>
</q-item>
<q-separator color="grey-2" />
</q-list> -->
</q-menu>
</q-btn>
<q-btn round dense flat icon="account_circle" style="font-size: 16px">
<q-menu>
<div class="q-pa-md" style="max-width: 500px">
<q-list>
<q-item>
<q-item-section avatar>
<q-icon color="primary" name="account_circle" />
</q-item-section>
<q-item-section>{{ fullName }}</q-item-section>
</q-item>
<q-item class="text-center">
<q-item-section>
<q-item-label>
<q-btn
color="primary"
label="ออกจากระบบ"
push
size="sm"
@click="onClickLogout"
/></q-item-label>
</q-item-section>
</q-item>
</q-list>
</div> </q-menu
></q-btn>
<div class="row col-12 justify-center">
<strong v-if="stetusCheckin" style="font-size: 1rem">
ลงเวลาเขางาน
</strong>
<strong v-else style="font-size: 1rem">ลงเวลาออกงาน</strong>
</div>
</q-toolbar>
<!-- </q-header> -->
<!-- <div :class="getClass(stetusCheckin)">
<div class="col-2">
<q-btn
icon="history"
unelevated
rounded
dense
flat
color="white"
:label="$q.screen.gt.xs ? 'ประวัติการลงเวลา' : ''"
:class="$q.screen.gt.xs ? 'q-px-md' : ''"
@click="router.push('/history')"
/>
</div>
<span class="text-body1 text-weight-bold col-8 text-center">
<span v-if="stetusCheckin">ลงเวลาเข้างาน</span>
<span v-else>ลงเวลาออกงาน</span>
</span>
<div class="col-2 text-right">
<q-btn
round
dense
flat
size="13px"
class="q-mx-md"
:disable="notiList.length === 0"
>
<q-icon name="notifications" size="24px" color="white" />
<q-badge
rounded
v-show="notiList.length > 0"
color="negative"
text-color="white"
floating
>{{ notiList.length }}</q-badge
>
<q-menu v-model="notiTrigger" max-width="480px" :offset="[0, 10]">
<div class="q-px-md q-py-sm row col-12 items-center">
<div class="text-subtitle1 text-weight-medium">
การแจงเตอน
</div>
</div>
<q-list
style="min-width: 300px"
v-for="n in notiList"
:key="n.id"
>
<q-item v-ripple class="mytry q-py-xs" dense>
<q-item-section avatar top style="min-width: 40px">
<q-avatar color="primary" size="22px" text-color="white">
<span class="text-weight-medium text-uppercase">{{
n.sender
}}</span>
</q-avatar>
</q-item-section>
<q-item-section>
<q-item-label caption class="text-black">{{
n.body
}}</q-item-label>
<q-item-label
caption
class="row items-center text-grey-7"
>{{ date2Thai(n.timereceive) }}</q-item-label
>
</q-item-section>
<q-btn
size="sm"
unelevated
dense
icon="mdi-close"
class="mybtn q-mx-xs"
@click="onClickDelete(n.id)"
></q-btn>
</q-item>
<q-separator color="grey-2" />
</q-list>
</q-menu>
</q-btn>
<q-btn
round
dense
flat
icon="account_circle"
style="font-size: 16px"
>
<q-menu>
<div class="q-pa-md" style="max-width: 500px">
<q-list>
<q-item>
<q-item-section avatar>
<q-icon color="primary" name="account_circle" />
</q-item-section>
<q-item-section>{{ fullName }}</q-item-section>
</q-item>
<q-item class="text-center">
<q-item-section>
<q-item-label>
<q-btn
color="primary"
label="ออกจากระบบ"
push
size="sm"
@click="onClickLogout"
/></q-item-label>
</q-item-section>
</q-item>
</q-list>
</div> </q-menu
></q-btn>
</div>
</div> -->
<div class="col-12 q-pa-md text-grey-9">
<div class="col-12 text-grey-9">
<div class="col-12 row justify-center">
<div class="col-12 row q-py-sm justify-center">
<div class="col-12 row q-pt-md justify-center">
<div
class="col-xs-12 col-sm-10 text-h6 text-center text-weight-bold"
>
@ -654,13 +284,12 @@ onMounted(async () => {
</div>
</div>
</div>
<div class="col-xs-12 col-md-11 row q-col-gutter-md">
<div class="col-12 col-sm-8">
<MapCheck @update:location="updateLocation" />
<div class="col-xs-12 col-md-11 q-pa-md q-col-gutter-md row">
<div class="col-xs-12 col-sm-8">
<MapCheck @update:location="updateLocation" class="col-12" />
<!-- <AscMaps /> -->
</div>
<div class="col-12 col-sm-4">
<div class="col-xs-12 col-sm-4">
<q-card flat bordered class="card-container">
<div
v-if="!cameraIsOn && img == null"
@ -676,7 +305,7 @@ onMounted(async () => {
/>
</div>
</div>
<div>
<div class="col-12 row items-center">
<!-- แสดงกลองตอนกดถายภาพ -->
<Camera
:resolution="{ width: photoWidth, height: photoHeight }"
@ -717,7 +346,7 @@ onMounted(async () => {
</q-card>
</div>
<div class="col-12 q-mb-md">
<div class="col-xs-12 col-sm-12 items-center">
<q-card
bordered
flat
@ -765,6 +394,7 @@ onMounted(async () => {
:rules="[(val) => !!val || 'กรุณาระบุสถานที่']"
lazy-rules
@update:model-value="selectLocation()"
hide-bottom-space
/>
</div>
<div
@ -783,45 +413,55 @@ onMounted(async () => {
/>
</div>
</q-card>
<q-card bordered flat class="q-pa-sm">
<div class="col-12">
</div>
<div class="col-xs-12 col-sm-12">
<q-card
bordered
flat
:class="
$q.screen.gt.xs
? 'q-px-md q-py-sm row items-center shadow-0'
: 'q-pa-md row items-center shadow-0'
"
>
<div class="text-weight-bold">หมายเหต</div>
<div class="col-12 q-pt-sm">
<q-input
outlined
v-model="remark"
label="หมายเหตุ"
lazy-rules
dense
/>
</div>
</q-card>
<div class="col-12 text-right">
<q-separator />
<div class="col-12 q-pa-md">
<p
:class="
$q.screen.gt.xs
? 'text-red text-caption '
: 'text-red text-caption text-center'
"
>
*หมายเหต คลกลงเวลาเขางานแลวระบบจะลงเวลาทนท
</p>
<q-btn
:label="
stetusCheckin == true ? 'ลงเวลาเข้างาน' : 'ลงเวลาออกงาน'
"
:color="img == null ? 'grey-6' : 'primary'"
push
size="14px"
:class="$q.screen.gt.xs ? 'q-px-md' : 'full-width'"
:disable="camera && img ? false : true"
@click="validateForm"
/>
</div>
</div>
</div>
</div>
</div>
<div class="col-12 text-right">
<q-separator />
<div class="col-12 q-pa-md">
<p
:class="
$q.screen.gt.xs
? 'text-red text-caption '
: 'text-red text-caption text-center'
"
>
*หมายเหต คลกลงเวลาเขางานแลวระบบจะลงเวลาทนท
</p>
<q-btn
:label="
stetusCheckin == true ? 'ลงเวลาเข้างาน' : 'ลงเวลาออกงาน'
"
:color="img == null ? 'grey-6' : 'primary'"
push
size="18px"
:class="$q.screen.gt.xs ? 'q-px-md' : 'full-width q-pa-sm'"
:disable="camera && img ? false : true"
@click="validateForm"
/>
</div>
</div>
</div>
@ -889,8 +529,9 @@ onMounted(async () => {
.card-container {
position: relative;
overflow: hidden;
height: 39vh; /* Adjust as needed */
height: autu; /* Adjust as needed */
background: #f6f5f5;
height: 35vh !important;
}
.image-container {
position: absolute;
@ -904,7 +545,7 @@ onMounted(async () => {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 5px; /* Adjust as needed */
border-radius: 8px; /* Adjust as needed */
}
.preview-placeholder {

366
src/views/MianView.vue Normal file
View file

@ -0,0 +1,366 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useQuasar } from 'quasar'
import keycloak from '@/plugins/keycloak'
import http from '@/plugins/http'
import config from '@/app.config'
// import Type
import type { notiType } from '@/interface/index/Main'
// import Stores
import { useCounterMixin } from '@/stores/mixin'
const mixin = useCounterMixin()
const {
date2Thai,
showLoader,
hideLoader,
messageError,
dialogRemove,
success,
} = mixin
const router = useRouter()
const $q = useQuasar()
const notiTrigger = ref<boolean>(false)
const notiList = ref<notiType[]>([])
const totalNotiList = ref<number>(0)
/** function เรียกข้อมุลแจ้งเตือน */
async function fetchNotifications(index: number, type: string) {
showLoader()
await http
.get(config.API.msgNotificate + `?page=${index}&pageSize=${20}`)
.then((res) => {
const response = res.data.result.data
const list: notiType[] = []
if (type === 'DEL') {
totalNotiList.value = res.data.result.total
if (notiList.value.length === 14) {
notiList.value = []
response.map((e: any) => {
list.push({
id: e.id,
sender:
e.createdFullName == '' || e.createdFullName == null
? 'เจ้าหน้าที่'[0]
: e.createdFullName[0],
body: e.body ?? '',
timereceive: e.receiveDate ? date2Thai(e.receiveDate) : '-',
})
})
notiList.value.push(...list)
}
} else {
response.map((e: any) => {
list.push({
id: e.id,
sender:
e.createdFullName == '' || e.createdFullName == null
? 'เจ้าหน้าที่'[0]
: e.createdFullName[0],
body: e.body ?? '',
timereceive: e.receiveDate ? date2Thai(e.receiveDate) : '-',
})
})
notiList.value.push(...list)
totalNotiList.value = res.data.result.total
}
})
.catch((err) => {
messageError($q, err)
})
.finally(() => {
hideLoader()
})
}
/**
* function ลบรายการแจงเตอน
* @param id noti
*/
async function onClickDelete(id: string, index: number) {
dialogRemove($q, async () => {
await http
.delete(config.API.msgId(id))
.then(() => {
notiList.value.splice(index, 1)
success($q, 'ลบข้อมูลสำเร็จ')
})
.catch((e) => {
messageError($q, e)
})
.finally(async () => {
fetchNotifications(1, 'DEL')
hideLoader()
})
})
}
/** function logout*/
function onClickLogout() {
$q.dialog({
title: 'ยืนยันการออกจากระบบ',
message: `ต้องการออกจากระบบใช้หรือไม่?`,
cancel: 'ยกเลิก',
ok: 'ยืนยัน',
persistent: true,
}).onOk(() => {
keycloak.logout()
})
}
function onLoad(index: any, done: any) {
if (notiList.value.length < totalNotiList.value) {
setTimeout(() => {
fetchNotifications(index + 1, 'NOMAL')
done()
}, 2000)
}
}
// class
const getClass = (val: boolean) => {
return {
'bg-primary text-white col-12 row items-center q-px-md q-py-sm': val,
'bg-red-9 text-white col-12 row items-center q-px-md q-py-sm': !val,
}
}
const fullName = ref<string>('')
onMounted(async () => {
await fetchNotifications(1, 'NOMAL')
if (keycloak.tokenParsed != null) {
fullName.value = keycloak.tokenParsed.name
}
})
</script>
<template>
<q-layout view="hHh LpR fFr">
<!-- header -->
<q-header flat class="text-dark col-12 bg-top header-br" height-hint="7">
<q-toolbar
class="q-my-xs items-center"
:style="$q.screen.gt.xs ? 'padding: 1% 2%;' : 'padding: 1% 4%;'"
>
<div class="row items-center">
<q-avatar style="background: linear-gradient(#4CE2D3, #02A998);">
<q-img
src="@/assets/logo1.png"
spinner-color="white"
style="height: 35px; width: 35px"
/>
</q-avatar>
<div class="row q-ml-md text-left items-center gt-xs">
<div
style="color: #ffffff; line-height: 10px"
class="text-body2 text-weight-bolder col-12"
>
ระบบ<span class="text-primary">ทรพยากรบคคล</span>
</div>
<div class="text-caption text-white">กรงเทพมหานคร</div>
</div>
</div>
<q-space />
<q-btn
icon="history"
unelevated
rounded
dense
flat
color="white"
@click="router.push('/history')"
/>
<q-btn
round
dense
flat
size="13px"
class="q-mx-md"
:disable="totalNotiList == 0"
>
<q-icon name="notifications" size="24px" color="white" />
<q-badge
rounded
v-show="totalNotiList !== 0"
color="negative"
text-color="white"
floating
>{{ totalNotiList }}</q-badge
>
<q-menu v-model="notiTrigger" max-width="480px" :offset="[0, 10]">
<div class="q-px-md q-py-sm row col-12 items-center">
<div class="text-subtitle1 text-weight-medium">
การแจงเตอน
</div>
</div>
<q-infinite-scroll @load="onLoad" :offset="250">
<div
v-for="(item, index) in notiList"
:key="index"
class="caption"
>
<q-item v-ripple class="mytry q-py-sm" dense>
<q-item-section avatar top style="min-width: 10px">
<q-avatar
rounded
color="primary"
size="25px"
text-color="white"
>
<span class="text-weight-medium text-uppercase">{{
item.sender[0]
}}</span>
</q-avatar>
</q-item-section>
<q-item-section>
<q-item-label caption class="text-black">{{
item.body
}}</q-item-label>
<q-item-label
caption
class="row items-center text-grey-7"
style="font-size: 12px"
>{{ item.timereceive }}</q-item-label
>
</q-item-section>
<div>
<q-btn
size="sm"
unelevated
dense
icon="mdi-close"
class="mybtn q-mx-xs"
@click="onClickDelete(item.id, index)"
></q-btn>
</div>
</q-item>
</div>
<template v-slot:loading v-if="notiList.length < totalNotiList">
<div class="row justify-center q-my-md">
<q-spinner-dots color="primary" size="40px" />
</div>
</template>
</q-infinite-scroll>
</q-menu>
</q-btn>
<q-btn round dense color="white" flat icon="account_circle" style="font-size: 16px">
<q-menu>
<div class="q-pa-md" style="max-width: 500px">
<q-list>
<q-item>
<q-item-section avatar>
<q-icon color="primary" name="account_circle" />
</q-item-section>
<q-item-section>{{ fullName }}</q-item-section>
</q-item>
<q-item class="text-center">
<q-item-section>
<q-item-label>
<q-btn
color="primary"
label="ออกจากระบบ"
push
size="sm"
@click="onClickLogout"
/></q-item-label>
</q-item-section>
</q-item>
</q-list>
</div> </q-menu
></q-btn>
</q-toolbar>
</q-header>
<div
class="bg-top"
:style="$q.screen.gt.xs ? 'height: 200px;' : 'height: 200px;'"
/>
<q-page-container class="bg-grey-2 q-pb-md">
<q-page
:style="
$q.screen.gt.xs
? 'padding: 1.8% 2%; margin-top: -200px;'
: 'padding: 5% 4%; margin-top: -200px;'
"
>
<router-view :key="$route.fullPath" />
</q-page>
</q-page-container>
</q-layout>
</template>
<style>
.bg-drawer {
background: #242a3d;
}
.menu {
padding-bottom: 5px;
padding-top: 5px;
}
.tabsHome .q-tab__icon {
font-size: 18px;
}
.border-100 {
border-radius: 100px;
}
.bg-top {
background: #273238 !important;
}
.header-br {
border-bottom: 1px solid #fdfdfd31;
}
.menuActive {
background: #1e2234;
border-radius: 0 100px 100px 0;
margin-right: 4%;
}
.q-card {
box-shadow: 3px 3px 20px -10px rgba(151, 150, 150, 0.261) !important;
/* border: 1px solid #eeeded; */
border-radius: 8px;
}
.q-menu {
box-shadow: 3px 3px 10px 1px rgba(95, 95, 95, 0.15) !important;
}
.toptitle {
font-size: 1.2rem;
font-weight: bold;
margin-bottom: 1.2%;
}
.q-field--outlined .q-field__control {
border-radius: 5px;
}
.q-field--outlined .q-field__control:before {
border-color: #c8d3db;
}
/* .q-field--outlined .q-icon {
color: #7474747f;
} */
.shadow-0 {
box-shadow: none !important;
}
.btnBlue {
background-color: #016987;
color: #fff;
}
</style>