hrms-checkin/src/views/HomeView.vue

495 lines
16 KiB
Vue
Raw Normal View History

2023-11-07 11:17:13 +07:00
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { useRouter } from "vue-router";
import moment, { Moment } from "moment";
// import Type
import type { FormRef } from "@/interface/response/checkin";
// import components
import MapCheck from "@/components/MapCheckin.vue";
// import Stores
import { useCounterMixin } from "@/stores/mixin";
const mixin = useCounterMixin();
const { date2Thai } = mixin;
const router = useRouter();
const stetusCheckin = ref(true);
onMounted(() => {
updateClock();
});
//time
const dateNow = ref<Date>(new Date());
const Thai = ref<Date>(dateNow.value);
const formattedS = ref();
const formattedM = ref();
const formattedH = ref();
function updateClock() {
const date = Date.now();
let hh = moment(date).format("HH");
let mm = moment(date).format("mm");
let ss = moment(date).format("ss");
formattedS.value = ss;
formattedM.value = mm;
formattedH.value = hh;
}
setInterval(updateClock, 1000);
//location
const location = ref<string>("สำนักงาน ก.ก");
const coordinates = ref<string>("13° 43 45” N 100° 31 26” E");
const workplace = ref<string>("in-place");
const useLocation = ref<string | null>("");
const model = ref<string | null>("");
const options = ref<string[]>([
"ปฏิบัติงานที่บ้าน",
"ลืมลงเวลาปฏิบัติงาน",
"ไปประชุม/อบรม/สัมมนา/ปฏิบัติงานที่บ้านนอกสถานที่",
"ขออนุญาตออกนอกสถานที่",
"อื่นๆ",
]);
function selectLocation() {
if (model.value === "อื่นๆ") {
useLocation.value = "";
} else {
useLocation.value = model.value;
}
}
//camera
const camera = ref(false);
const hasPhoto = ref<boolean>(true);
const mediaStream = ref<MediaStream | null>(null);
const video = ref<HTMLVideoElement | null>(null);
const canvas = ref<HTMLCanvasElement | null>(null);
const img = ref<any>(null);
const openCamera = () => {
camera.value = true;
camera.value && setupCamera();
};
const setupCamera = async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
if (video.value) {
video.value.srcObject = stream;
}
mediaStream.value = stream;
} catch (error) {
console.error("Error accessing camera:", error);
}
};
function capturePhoto() {
const videoElement = video.value;
const canvasElement = canvas.value;
if (!videoElement || !canvasElement) {
console.error("Video or Canvas element not available");
return;
}
const context = canvasElement.getContext("2d");
if (!context) {
console.error("Canvas context not available");
return;
}
const desiredWidth = 150;
const desiredHeight = 200;
const zoomFactor = 10;
const videoAspectRatio = videoElement.videoWidth / videoElement.videoHeight;
const canvasAspectRatio = desiredWidth / desiredHeight;
let drawWidth, drawHeight;
if (videoAspectRatio > canvasAspectRatio) {
drawWidth = desiredWidth * zoomFactor;
drawHeight = (desiredWidth * zoomFactor) / videoAspectRatio;
} else {
drawWidth = desiredHeight * zoomFactor * videoAspectRatio;
drawHeight = desiredHeight * zoomFactor;
}
canvasElement.width = drawWidth;
canvasElement.height = drawHeight;
if (context) {
context.imageSmoothingEnabled = true;
context.imageSmoothingQuality = "low";
}
// context.drawImage(
// videoElement,
// 0,
// 0,
// canvasElement.width,
// canvasElement.height
// );
context.drawImage(
videoElement,
0,
0,
videoElement.videoWidth,
videoElement.videoHeight,
0,
0,
drawWidth,
drawHeight
);
//ไฟล์รูป
const dataURL = canvasElement.toDataURL(".image/.png");
img.value = dataURL;
console.log(img.value);
if (mediaStream.value) {
mediaStream.value.getTracks().forEach((track) => track.stop());
videoElement.srcObject = null;
hasPhoto.value = false;
}
}
function refreshPhoto() {
hasPhoto.value = true;
img.value = "";
}
// validate
const useLocationRef = ref<object | null>(null);
const modelRef = ref<object | null>(null);
const objectRef: FormRef = {
model: modelRef,
useLocation: useLocationRef,
};
function validateForm() {
const hasError = [];
for (const key in objectRef) {
if (Object.prototype.hasOwnProperty.call(objectRef, key)) {
const property = objectRef[key];
if (property.value && typeof property.value.validate === "function") {
const isValid = property.value.validate();
hasError.push(isValid);
}
}
}
if (hasError.every((result) => result === true)) {
confirm();
} else {
console.log("ไม่ผ่าน ");
}
}
// ยืนยันการลงเวลา
const dialogTime = ref<boolean>(false);
const confirm = () => {
dialogTime.value = true;
};
// 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,
};
};
</script>
<template>
<div class="col-12 row justify-center">
<div class="col-xs-12 col-sm-12 col-md-12">
<q-card flat class="row col-12 cardNone">
<div :class="getClass(stetusCheckin)">
<div class="col-2">
<!-- <q-btn
icon="mdi-arrow-left"
unelevated
round
dense
flat
color="white"
@click="router.go(-1)"
/> -->
</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
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>
</div>
<div class="col-12 q-pa-md text-grey-9">
<div class="col-12 row justify-center">
<div class="col-12 row q-py-sm justify-center">
<div
class="col-xs-12 col-sm-10 text-h6 text-center text-weight-bold"
>
{{ date2Thai(Thai) }}
</div>
<div class="row col-12 justify-center q-py-sm">
<div class="colunm">
<div class="text-h3 text-weight-bold">
{{ formattedH }}<span class="q-ma-md">:</span>
</div>
</div>
<div class="colunm">
<div class="text-h3 text-weight-bold">
{{ formattedM }}<span class="q-ma-md">:</span>
</div>
</div>
<div class="colunm">
<div class="text-h3 text-weight-bold">{{ formattedS }}</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-11 row q-col-gutter-md">
<div class="col-12 col-sm-8">
<MapCheck />
<!-- <q-card
bordered
flat
class="col-12 bg-grey-2 shadow-0"
:style="$q.screen.gt.xs ? 'height: 350px;' : 'height: 220px;'"
>
<MapCheck />
<q-img
src="@/assets/map1.png"
:style="
$q.screen.gt.xs ? 'height: 300px;' : 'height: 168px;'
"
></q-img>
<div class="q-pa-md text-weight-medium text-grey-8">
นทใกลเคยง
<span class="q-px-sm">:</span>
{{ location }}
</div>
</q-card> -->
</div>
<div class="col-12 col-sm-4">
<q-card
flat
bordered
class="card-container"
@click="openCamera()"
>
<div v-if="!camera" class="preview-placeholder">
<div class="text-center">
<q-icon
name="photo_camera"
color="blue-grey-3"
size="100px"
class="center-icon"
/>
</div>
</div>
<div v-else>
<div v-if="hasPhoto" class="video-container">
<video ref="video" autoplay class="video-element"></video>
<canvas ref="canvas" class="canvas-element"></canvas>
</div>
<div v-else class="image-container">
<q-img :src="img" class="image-element"></q-img>
<canvas ref="canvas" class="canvas-element"></canvas>
</div>
<div class="absolute-bottom-right q-ma-md">
<q-btn
v-if="hasPhoto"
round
push
icon="photo_camera"
size="md"
color="positive"
@click="capturePhoto"
/>
<q-btn
v-else
round
push
icon="refresh"
size="md"
color="negative"
@click="refreshPhoto"
/>
</div>
</div>
</q-card>
</div>
<div class="col-12 q-mb-md">
<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="
$q.screen.gt.xs
? 'row q-gutter-md q-pl-md col-sm-6 col-md-3'
: 'column col-12'
"
>
<q-radio
v-model="workplace"
checked-icon="task_alt"
unchecked-icon="panorama_fish_eye"
val="in-place"
label="ในสถานที่"
/>
<q-radio
v-model="workplace"
checked-icon="task_alt"
unchecked-icon="panorama_fish_eye"
val="off-site"
label="นอกสถานที่"
/>
</div>
<div
class="col-xs-12 col-sm-6 col-md-4"
v-if="workplace == 'off-site'"
>
<q-select
ref="modelRef"
dense
class="q-ml-md"
outlined
v-model="model"
:options="options"
prefix="ระบุสถานที่ :"
:rules="[(val) => !!val || 'กรุณาระบุสถานที่']"
lazy-rules
@update:model-value="selectLocation()"
/>
</div>
<div
class="col-xs-12 col-sm-6 col-md-4"
v-if="model == 'อื่นๆ'"
>
<q-input
ref="useLocationRef"
dense
class="q-ml-md"
outlined
v-model="useLocation"
label="ระบุสถานที่"
:rules="[(val) => !!val || 'กรุณาระบุสถานที่']"
lazy-rules
/>
</div>
</q-card>
</div>
</div>
</div>
</div>
<div class="col-12 text-right" v-if="camera && img">
<div class="col-12">
<q-separator />
</div>
<div class="col-12 q-pa-md">
<p class="text-red text-caption">
*หมายเหต คลกลงเวลาเขางานแลวระบบจะลงเวลาทนท
</p>
<q-btn
:label="stetusCheckin == true ? 'ลงเวลาเข้างาน' : 'ลงเวลาออกงาน'"
:color="stetusCheckin == true ? 'primary' : 'red-9'"
push
size="14px"
:class="$q.screen.gt.xs ? 'q-px-md' : 'full-width'"
@click="validateForm"
/>
</div>
</div>
</q-card>
</div>
</div>
<q-dialog v-model="dialogTime">
<q-card class="full-width cardNone">
<div :class="getClass(stetusCheckin)">
<div class="text-body1 text-center col-12 text-weight-bold">
<span v-if="stetusCheckin">ลงเวลาเข้างานของคุณ</span>
<span v-else>ลงเวลาออกงานของคุณ</span>
</div>
</div>
<q-card-section class="row col-12 justify-center">
<div class="bg-grey-2 rounded-borders q-pa-md col-11">
<div class="col-12 text-subtitle1 text-center text-weight-medium">
{{ Thai }}
</div>
<div class="row col-12 justify-center q-pt-sm">
<div class="text-h3 text-weight-bold">
{{ formattedH }}<span class="q-ma-md">:</span>
</div>
<div class="text-h3 text-weight-bold">{{ formattedM }}</div>
</div>
</div>
<div class="col-12 text-center row q-pt-md">
<div class="col-12 text-subtitle1 text-weight-medium text-secondary">
{{ location }}
</div>
<div class="col-12 text-grey-7">{{ coordinates }}</div>
</div>
</q-card-section>
<q-card-actions align="center" class="q-mb-md row">
<q-btn
class="col-xs-11 col-sm-6"
push
label="ตกลง"
color="secondary"
v-close-popup
/>
</q-card-actions>
</q-card>
</q-dialog>
</template>
<style scoped>
.q-card.cardImg:hover {
border: 1px solid #02a998 !important;
}
.center-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.card-container {
position: relative;
overflow: hidden;
height: 350px; /* Adjust as needed */
background: #f6f5f5;
}
.video-container,
.image-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.video-element,
.image-element {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 5px; /* Adjust as needed */
}
.canvas-element {
display: none; /* Adjust as needed */
}
</style>