remove old code
29
README.md
|
|
@ -1,29 +0,0 @@
|
|||
# my-app
|
||||
|
||||
## Project setup
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
|
||||
```
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
module.exports = {
|
||||
presets: ["@vue/cli-plugin-babel/preset"],
|
||||
};
|
||||
46
package.json
|
|
@ -1,46 +0,0 @@
|
|||
{
|
||||
"name": "bma-ehr-checkin",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "eslint --fix ",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@fawmi/vue-google-maps": "^0.9.79",
|
||||
"@mdi/font": "^7.3.67",
|
||||
"@quasar/extras": "^1.0.0",
|
||||
"core-js": "^3.8.3",
|
||||
"moment": "^2.29.4",
|
||||
"pinia": "^2.1.7",
|
||||
"quasar": "^2.0.0",
|
||||
"register-service-worker": "^1.7.2",
|
||||
"vue": "^3.2.13",
|
||||
"vue-class-component": "^8.0.0-0",
|
||||
"vue-router": "^4.0.3",
|
||||
"vue3-geolocation": "^1.0.0",
|
||||
"vue3-google-map": "^0.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/googlemaps": "^3.43.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"@vue/cli-plugin-babel": "~5.0.0",
|
||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||
"@vue/cli-plugin-pwa": "~5.0.0",
|
||||
"@vue/cli-plugin-router": "~5.0.0",
|
||||
"@vue/cli-plugin-typescript": "~5.0.0",
|
||||
"@vue/cli-service": "~5.0.0",
|
||||
"@vue/eslint-config-typescript": "^9.1.0",
|
||||
"eslint-config-prettier": "^8.10.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-vue": "^8.0.3",
|
||||
"prettier": "^2.4.1",
|
||||
"sass": "1.32.12",
|
||||
"sass-loader": "^12.0.0",
|
||||
"typescript": "~4.5.5",
|
||||
"vue-cli-plugin-quasar": "~5.0.2"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 4 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 799 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.00251 14.9297L0 1.07422H6.14651L8.00251 4.27503L9.84583 1.07422H16L8.00251 14.9297Z" fill="black"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 215 B |
|
|
@ -1,23 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong
|
||||
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
|
||||
properly without JavaScript enabled. Please enable it to
|
||||
continue.</strong
|
||||
>
|
||||
</noscript>
|
||||
<div id="app">
|
||||
<!-- <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyC83QLHjctDyKKyL4XZgwNfqp338xZiRnQ&libraries=places"></script> -->
|
||||
</div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
User-agent: *
|
||||
Disallow:
|
||||
30
src/App.vue
|
|
@ -1,30 +0,0 @@
|
|||
<template>
|
||||
<!-- <nav>
|
||||
<router-link to="/">Home</router-link> |
|
||||
<router-link to="/about">About</router-link>
|
||||
</nav> -->
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
nav {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
nav a {
|
||||
font-weight: bold;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
nav a.router-link-exact-active {
|
||||
color: #42b983;
|
||||
}
|
||||
</style>
|
||||
|
Before Width: | Height: | Size: 6.7 KiB |
|
|
@ -1,15 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 356 360">
|
||||
<path
|
||||
d="M43.4 303.4c0 3.8-2.3 6.3-7.1 6.3h-15v-22h14.4c4.3 0 6.2 2.2 6.2 5.2 0 2.6-1.5 4.4-3.4 5 2.8.4 4.9 2.5 4.9 5.5zm-8-13H24.1v6.9H35c2.1 0 4-1.3 4-3.8 0-2.2-1.3-3.1-3.7-3.1zm5.1 12.6c0-2.3-1.8-3.7-4-3.7H24.2v7.7h11.7c3.4 0 4.6-1.8 4.6-4zm36.3 4v2.7H56v-22h20.6v2.7H58.9v6.8h14.6v2.3H58.9v7.5h17.9zm23-5.8v8.5H97v-8.5l-11-13.4h3.4l8.9 11 8.8-11h3.4l-10.8 13.4zm19.1-1.8V298c0-7.9 5.2-10.7 12.7-10.7 7.5 0 13 2.8 13 10.7v1.4c0 7.9-5.5 10.8-13 10.8s-12.7-3-12.7-10.8zm22.7 0V298c0-5.7-3.9-8-10-8-6 0-9.8 2.3-9.8 8v1.4c0 5.8 3.8 8.1 9.8 8.1 6 0 10-2.3 10-8.1zm37.2-11.6v21.9h-2.9l-15.8-17.9v17.9h-2.8v-22h3l15.6 18v-18h2.9zm37.9 10.2v1.3c0 7.8-5.2 10.4-12.4 10.4H193v-22h11.2c7.2 0 12.4 2.8 12.4 10.3zm-3 0c0-5.3-3.3-7.6-9.4-7.6h-8.4V307h8.4c6 0 9.5-2 9.5-7.7V298zm50.8-7.6h-9.7v19.3h-3v-19.3h-9.7v-2.6h22.4v2.6zm34.4-2.6v21.9h-3v-10.1h-16.8v10h-2.8v-21.8h2.8v9.2H296v-9.2h2.9zm34.9 19.2v2.7h-20.7v-22h20.6v2.7H316v6.8h14.5v2.3H316v7.5h17.8zM24 340.2v7.3h13.9v2.4h-14v9.6H21v-22h20v2.7H24zm41.5 11.4h-9.8v7.9H53v-22h13.3c5.1 0 8 1.9 8 6.8 0 3.7-2 6.3-5.6 7l6 8.2h-3.3l-5.8-8zm-9.8-2.6H66c3.1 0 5.3-1.5 5.3-4.7 0-3.3-2.2-4.1-5.3-4.1H55.7v8.8zm47.9 6.2H89l-2 4.3h-3.2l10.7-22.2H98l10.7 22.2h-3.2l-2-4.3zm-1-2.3l-6.3-13-6 13h12.2zm46.3-15.3v21.9H146v-17.2L135.7 358h-2.1l-10.2-15.6v17h-2.8v-21.8h3l11 16.9 11.3-17h3zm35 19.3v2.6h-20.7v-22h20.6v2.7H166v6.8h14.5v2.3H166v7.6h17.8zm47-19.3l-8.3 22h-3l-7.1-18.6-7 18.6h-3l-8.2-22h3.3L204 356l6.8-18.5h3.4L221 356l6.6-18.5h3.3zm10 11.6v-1.4c0-7.8 5.2-10.7 12.7-10.7 7.6 0 13 2.9 13 10.7v1.4c0 7.9-5.4 10.8-13 10.8-7.5 0-12.7-3-12.7-10.8zm22.8 0v-1.4c0-5.7-4-8-10-8s-9.9 2.3-9.9 8v1.4c0 5.8 3.8 8.2 9.8 8.2 6.1 0 10-2.4 10-8.2zm28.3 2.4h-9.8v7.9h-2.8v-22h13.2c5.2 0 8 1.9 8 6.8 0 3.7-2 6.3-5.6 7l6 8.2h-3.3l-5.8-8zm-9.8-2.6h10.2c3 0 5.2-1.5 5.2-4.7 0-3.3-2.1-4.1-5.2-4.1h-10.2v8.8zm40.3-1.5l-6.8 5.6v6.4h-2.9v-22h2.9v12.3l15.2-12.2h3.7l-9.9 8.1 10.3 13.8h-3.6l-8.9-12z" />
|
||||
<path fill="#050A14"
|
||||
d="M188.4 71.7a10.4 10.4 0 01-20.8 0 10.4 10.4 0 1120.8 0zM224.2 45c-2.2-3.9-5-7.5-8.2-10.7l-12 7c-3.7-3.2-8-5.7-12.6-7.3a49.4 49.4 0 00-9.7 13.9 59 59 0 0140.1 14l7.6-4.4a57 57 0 00-5.2-12.5zM178 125.1c4.5 0 9-.6 13.4-1.7v-14a40 40 0 0012.5-7.2 47.7 47.7 0 00-7.1-15.3 59 59 0 01-32.2 27.7v8.7c4.4 1.2 8.9 1.8 13.4 1.8zM131.8 45c-2.3 4-4 8.1-5.2 12.5l12 7a40 40 0 000 14.4c5.7 1.5 11.3 2 16.9 1.5a59 59 0 01-8-41.7l-7.5-4.3c-3.2 3.2-6 6.7-8.2 10.6z" />
|
||||
<path fill="#00B4FF"
|
||||
d="M224.2 98.4c2.3-3.9 4-8 5.2-12.4l-12-7a40 40 0 000-14.5c-5.7-1.5-11.3-2-16.9-1.5a59 59 0 018 41.7l7.5 4.4c3.2-3.2 6-6.8 8.2-10.7zm-92.4 0c2.2 4 5 7.5 8.2 10.7l12-7a40 40 0 0012.6 7.3c4-4.1 7.3-8.8 9.7-13.8a59 59 0 01-40-14l-7.7 4.4c1.2 4.3 3 8.5 5.2 12.4zm46.2-80c-4.5 0-9 .5-13.4 1.7V34a40 40 0 00-12.5 7.2c1.5 5.7 4 10.8 7.1 15.4a59 59 0 0132.2-27.7V20a53.3 53.3 0 00-13.4-1.8z" />
|
||||
<path fill="#00B4FF"
|
||||
d="M178 9.2a62.6 62.6 0 11-.1 125.2A62.6 62.6 0 01178 9.2m0-9.2a71.7 71.7 0 100 143.5A71.7 71.7 0 00178 0z" />
|
||||
<path fill="#050A14"
|
||||
d="M96.6 212v4.3c-9.2-.8-15.4-5.8-15.4-17.8V180h4.6v18.4c0 8.6 4 12.6 10.8 13.5zm16-31.9v18.4c0 8.9-4.3 12.8-10.9 13.5v4.4c9.2-.7 15.5-5.6 15.5-18v-18.3h-4.7zM62.2 199v-2.2c0-12.7-8.8-17.4-21-17.4-12.1 0-20.7 4.7-20.7 17.4v2.2c0 12.8 8.6 17.6 20.7 17.6 1.5 0 3-.1 4.4-.3l11.8 6.2 2-3.3-8.2-4-6.4-3.1a32 32 0 01-3.6.2c-9.8 0-16-3.9-16-13.3v-2.2c0-9.3 6.2-13.1 16-13.1 9.9 0 16.3 3.8 16.3 13.1v2.2c0 5.3-2.1 8.7-5.6 10.8l4.8 2.4c3.4-2.8 5.5-7 5.5-13.2zM168 215.6h5.1L156 179.7h-4.8l17 36zM143 205l7.4-15.7-2.4-5-15.1 31.4h5.1l3.3-7h18.3l-1.8-3.7H143zm133.7 10.7h5.2l-17.3-35.9h-4.8l17 36zm-25-10.7l7.4-15.7-2.4-5-15.1 31.4h5.1l3.3-7h18.3l-1.7-3.7h-14.8zm73.8-2.5c6-1.2 9-5.4 9-11.4 0-8-4.5-10.9-12.9-10.9h-21.4v35.5h4.6v-31.3h16.5c5 0 8.5 1.4 8.5 6.7 0 5.2-3.5 7.7-8.5 7.7h-11.4v4.1h10.7l9.3 12.8h5.5l-9.9-13.2zm-117.4 9.9c-9.7 0-14.7-2.5-18.6-6.3l-2.2 3.8c5.1 5 11 6.7 21 6.7 1.6 0 3.1-.1 4.6-.3l-1.9-4h-3zm18.4-7c0-6.4-4.7-8.6-13.8-9.4l-10.1-1c-6.7-.7-9.3-2.2-9.3-5.6 0-2.5 1.4-4 4.6-5l-1.8-3.8c-4.7 1.4-7.5 4.2-7.5 8.9 0 5.2 3.4 8.7 13 9.6l11.3 1.2c6.4.6 8.9 2 8.9 5.4 0 2.7-2.1 4.7-6 5.8l1.8 3.9c5.3-1.6 8.9-4.7 8.9-10zm-20.3-21.9c7.9 0 13.3 1.8 18.1 5.7l1.8-3.9a30 30 0 00-19.6-5.9c-2 0-4 .1-5.7.3l1.9 4 3.5-.2z" />
|
||||
<path fill="#00B4FF"
|
||||
d="M.5 251.9c29.6-.5 59.2-.8 88.8-1l88.7-.3 88.7.3 44.4.4 44.4.6-44.4.6-44.4.4-88.7.3-88.7-.3a7981 7981 0 01-88.8-1z" />
|
||||
<path fill="none" d="M-565.2 324H-252v15.8h-313.2z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 1.8 MiB |
|
|
@ -1,488 +0,0 @@
|
|||
<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(checkIn)">
|
||||
<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="checkIn">ลงเวลาเข้างาน</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-sm' : ''"
|
||||
@click="router.push('/check-in/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"
|
||||
>
|
||||
{{ 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">
|
||||
<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="photo()">
|
||||
<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="checkIn == true ? 'ลงเวลาเข้างาน' : 'ลงเวลาออกงาน'"
|
||||
:color="checkIn == 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(checkIn)">
|
||||
<div class="text-body1 text-center col-12 text-weight-bold">
|
||||
<span v-if="checkIn">ลงเวลาเข้างานของคุณ</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>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import { useCounterMixin } from "@/stores/mixin";
|
||||
import { useRouter } from "vue-router";
|
||||
import moment, { Moment } from "moment";
|
||||
|
||||
import MapCheck from "@/modules/checkin/componenst/mapCheck.vue";
|
||||
import type { FormRef } from "@/modules/checkin/interface/response/checkin";
|
||||
|
||||
const mixin = useCounterMixin();
|
||||
const { date2Thai } = mixin;
|
||||
|
||||
const router = useRouter();
|
||||
const dateNow = ref<Date>(new Date());
|
||||
const Thai = ref<string | null>(date2Thai(dateNow.value));
|
||||
const checkIn = ref(true); //เช็คการเช็คอิน ถ้าลงเวลาครั้งแรกเป็นเช็คอิน(สีเขียว) true แต่ถ้าครั้ง 2 เป็นเช็คเอ้าท์(สีแดง)
|
||||
|
||||
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[]>([
|
||||
"ปฏิบัติงานที่บ้าน",
|
||||
"ลืมลงเวลาปฏิบัติงาน",
|
||||
"ไปประชุม/อบรม/สัมมนา/ปฏิบัติงานที่บ้านนอกสถานที่",
|
||||
"ขออนุญาตออกนอกสถานที่",
|
||||
"อื่นๆ",
|
||||
]);
|
||||
|
||||
const camera = ref(false);
|
||||
const dialogTime = ref(false);
|
||||
|
||||
const mediaStream = ref<MediaStream | null>(null);
|
||||
const video = ref<HTMLVideoElement | null>(null);
|
||||
const canvas = ref<HTMLCanvasElement | null>(null);
|
||||
const hasPhoto = ref<boolean>(true);
|
||||
const img = ref<any>(null);
|
||||
|
||||
const useLocationRef = ref<Object | null>(null);
|
||||
const modelRef = ref<Object | null>(null);
|
||||
const objectRef: FormRef = {
|
||||
model: modelRef,
|
||||
useLocation: useLocationRef,
|
||||
};
|
||||
|
||||
const photo = () => {
|
||||
camera.value = true;
|
||||
camera.value && setupCamera();
|
||||
};
|
||||
const confirm = () => {
|
||||
dialogTime.value = true;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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 refreshPhoto() {
|
||||
hasPhoto.value = true;
|
||||
img.value = "";
|
||||
}
|
||||
|
||||
// const time = new Date().toLocaleTimeString();
|
||||
const formattedS = ref();
|
||||
const formattedM = ref();
|
||||
const formattedH = ref();
|
||||
|
||||
onMounted(() => {
|
||||
updateClock();
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
function selectLocation() {
|
||||
if (model.value === "อื่นๆ") {
|
||||
useLocation.value = "";
|
||||
} else {
|
||||
useLocation.value = model.value;
|
||||
}
|
||||
}
|
||||
|
||||
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 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>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<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>
|
||||
23
src/main.ts
|
|
@ -1,23 +0,0 @@
|
|||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import { createPinia } from "pinia";
|
||||
import "./registerServiceWorker";
|
||||
import router from "./router";
|
||||
import { Quasar } from "quasar";
|
||||
import quasarUserOptions from "./quasar-user-options";
|
||||
// import GMap from "vue3-google-map";
|
||||
import VueGoogleMaps from "@fawmi/vue-google-maps";
|
||||
|
||||
const app = createApp(App);
|
||||
const pinia = createPinia();
|
||||
|
||||
app.use(router);
|
||||
app.use(pinia);
|
||||
app.use(Quasar, quasarUserOptions);
|
||||
app.use(VueGoogleMaps, {
|
||||
load: {
|
||||
key: "AIzaSyBzPSF5NxUZ1G8DKBnJvJPTqCR0Ct2xf58",
|
||||
},
|
||||
});
|
||||
|
||||
app.mount("#app");
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
|
||||
// declare var google: any;
|
||||
const location = ref({
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
findNearestPlace();
|
||||
});
|
||||
function findNearestPlace() {
|
||||
if (navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(position) => {
|
||||
const userLocation = {
|
||||
lat: position.coords.latitude,
|
||||
lng: position.coords.longitude,
|
||||
};
|
||||
|
||||
// ส่ง userLocation ไปยัง API เพื่อหาสถานที่ที่ใกล้ที่สุด
|
||||
findNearestPlaceFromAPI(userLocation);
|
||||
location.value = userLocation;
|
||||
},
|
||||
(error) => {
|
||||
console.error(error);
|
||||
console.log("erroe");
|
||||
}
|
||||
);
|
||||
} else {
|
||||
console.error("เบราว์เซอร์ไม่รองรับการระบุตำแหน่ง");
|
||||
}
|
||||
}
|
||||
function findNearestPlaceFromAPI(userLocation: any) {
|
||||
console.log(userLocation);
|
||||
// // // ใช้ Google Maps Places API เพื่อหาสถานที่ที่ใกล้ที่สุด
|
||||
// const placesService = new google.maps.places.PlacesService(
|
||||
// document.createElement("div")
|
||||
// );
|
||||
|
||||
// const request = {
|
||||
// location: userLocation,
|
||||
// radius: 1000, // รัศมีในเมตร
|
||||
// types: ["restaurant", "cafe", "store"], // ประเภทของสถานที่ที่คุณต้องการค้นหา
|
||||
// };
|
||||
// placesService.nearbySearch(request, (results: any, status: any) => {
|
||||
// if (status === google.maps.places.PlacesServiceStatus.OK) {
|
||||
// // ทำตามที่คุณต้องการกับผลลัพธ์ที่ได้
|
||||
// console.log("Nearby places:", results);
|
||||
// } else {
|
||||
// console.error("Error fetching nearby places:", status);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="app">
|
||||
<GMapMap
|
||||
:center="{ lat: location.lat, lng: location.lng }"
|
||||
:zoom="18"
|
||||
map-type-id="terrain"
|
||||
style="width: 59vw; height: 20rem"
|
||||
:options="{
|
||||
zoomControl: true,
|
||||
mapTypeControl: true,
|
||||
scaleControl: true,
|
||||
streetViewControl: true,
|
||||
rotateControl: true,
|
||||
fullscreenControl: true,
|
||||
}"
|
||||
>
|
||||
<GMapMarker :position="{ lat: location.lat, lng: location.lng }"
|
||||
/></GMapMap>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -1,203 +0,0 @@
|
|||
<template>
|
||||
<div class="q-pb-sm row">
|
||||
<div class="items-center col-12 row q-gutter-sm">
|
||||
<!-- ค้นหาข้อความใน table -->
|
||||
<!-- <datepicker
|
||||
menu-class-name="modalfix"
|
||||
v-model="yearly"
|
||||
:locale="'th'"
|
||||
autoApply
|
||||
year-picker
|
||||
:enableTimePicker="false"
|
||||
>
|
||||
<template #year="{ year }">{{ year + 543 }}</template>
|
||||
<template #year-overlay-value="{ value }">{{
|
||||
parseInt(value + 543)
|
||||
}}</template>
|
||||
<template #trigger>
|
||||
<q-input
|
||||
hide-bottom-space
|
||||
outlined
|
||||
dense
|
||||
lazy-rules
|
||||
:model-value="yearly + 543"
|
||||
:rules="[(val) => !!val || `${'กรุณาเลือกปีงบประมาณ'}`]"
|
||||
:label="`${'ปีงบประมาณ'}`"
|
||||
style="width: 150px"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="event" class="cursor-pointer" color="primary">
|
||||
</q-icon>
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
</datepicker> -->
|
||||
<q-space />
|
||||
<!-- <q-input
|
||||
standout
|
||||
dense
|
||||
:model-value="inputfilter"
|
||||
ref="filterRef"
|
||||
@update:model-value="updateInput"
|
||||
outlined
|
||||
debounce="300"
|
||||
placeholder="ค้นหา"
|
||||
class="gt-xs"
|
||||
style="max-width: 200px"
|
||||
>
|
||||
<template v-slot:append>
|
||||
<q-icon v-if="inputfilter == ''" name="search" />
|
||||
<q-icon
|
||||
v-if="inputfilter !== ''"
|
||||
name="clear"
|
||||
class="cursor-pointer"
|
||||
@click="resetFilter"
|
||||
/>
|
||||
</template>
|
||||
</q-input> -->
|
||||
<!-- แสดงคอลัมน์ใน table -->
|
||||
<!-- <q-select
|
||||
:model-value="inputvisible"
|
||||
@update:model-value="updateVisible"
|
||||
:display-value="$q.lang.table.columns"
|
||||
multiple
|
||||
outlined
|
||||
dense
|
||||
:options="attrs.columns"
|
||||
options-dense
|
||||
option-value="name"
|
||||
map-options
|
||||
emit-value
|
||||
style="min-width: 150px"
|
||||
class="gt-xs"
|
||||
>
|
||||
<template> </template>
|
||||
</q-select> -->
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<q-table
|
||||
ref="table"
|
||||
flat
|
||||
bordered
|
||||
class="custom-table2"
|
||||
v-bind="attrs"
|
||||
virtual-scroll
|
||||
:virtual-scroll-sticky-size-start="48"
|
||||
dense
|
||||
:pagination-label="paginationLabel"
|
||||
:pagination="initialPagination"
|
||||
:rows-per-page-options="[0]"
|
||||
:grid="grid"
|
||||
>
|
||||
<template v-slot:header="props">
|
||||
<q-tr :props="props">
|
||||
<q-th v-for="col in props.cols" :key="col.name" :props="props">
|
||||
<span class="text-weight-medium" v-html="col.label" />
|
||||
</q-th>
|
||||
<q-th auto-width v-if="inputShow" />
|
||||
</q-tr>
|
||||
</template>
|
||||
<template #body="props">
|
||||
<slot v-bind="props" name="columns"></slot>
|
||||
</template>
|
||||
<template #item="props">
|
||||
<slot v-bind="props" name="item"></slot>
|
||||
</template>
|
||||
</q-table>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, useAttrs, defineProps, defineEmits } from "vue";
|
||||
const attrs = ref<any>(useAttrs());
|
||||
const table = ref<any>(null);
|
||||
const filterRef = ref<any>(null);
|
||||
const initialPagination = ref({
|
||||
rowsPerPage: 0,
|
||||
});
|
||||
const yearly = ref<number>(new Date().getFullYear());
|
||||
|
||||
const props = defineProps({
|
||||
count: Number,
|
||||
pass: Number,
|
||||
notpass: Number,
|
||||
|
||||
inputfilter: String,
|
||||
name: String,
|
||||
icon: String,
|
||||
inputvisible: Array,
|
||||
editvisible: Boolean,
|
||||
grid: Boolean,
|
||||
|
||||
inputShow: Boolean,
|
||||
});
|
||||
|
||||
const emit = defineEmits([
|
||||
"update:inputfilter",
|
||||
"update:inputvisible",
|
||||
"update:editvisible",
|
||||
]);
|
||||
const updateInput = (value: string | number | null) => {
|
||||
emit("update:inputfilter", value);
|
||||
};
|
||||
const updateVisible = (value: []) => {
|
||||
emit("update:inputvisible", value);
|
||||
};
|
||||
|
||||
const paginationLabel = (start: string, end: string, total: string) => {
|
||||
return start + "-" + end + " ใน " + total;
|
||||
};
|
||||
|
||||
const resetFilter = () => {
|
||||
// reset ค่าที่ค้นหาเมื่อกดปุ่ม X ในกล่องค้นหา
|
||||
emit("update:inputfilter", "");
|
||||
filterRef.value.focus();
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.icon-color {
|
||||
color: #4154b3;
|
||||
}
|
||||
|
||||
.custom-table2 {
|
||||
.q-table tr:nth-child(odd) td {
|
||||
background: white;
|
||||
}
|
||||
|
||||
.q-table tr:nth-child(even) td {
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.q-table thead tr {
|
||||
background: #ecebeb;
|
||||
}
|
||||
|
||||
.q-table thead tr th {
|
||||
position: sticky;
|
||||
}
|
||||
|
||||
.q-table td:nth-of-type(2) {
|
||||
z-index: 3 !important;
|
||||
}
|
||||
|
||||
.q-table th:nth-of-type(2),
|
||||
.q-table td:nth-of-type(2) {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* this will be the loading indicator */
|
||||
.q-table thead tr:last-child th {
|
||||
/* height of all previous header rows */
|
||||
top: 48px;
|
||||
}
|
||||
|
||||
.q-table thead tr:first-child th {
|
||||
top: 0;
|
||||
}
|
||||
.text-caption {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import type { DataOption } from "@/modules/checkin/interface/index/Main";
|
||||
|
||||
const filterYear = ref<string>("");
|
||||
const yearOption = ref<DataOption[]>([{ id: "2566", name: "2566" }]);
|
||||
</script>
|
||||
<template>
|
||||
<div class="q-pb-sm row">
|
||||
<div class="items-center col-12 row q-gutter-sm">
|
||||
<q-select
|
||||
dense
|
||||
outlined
|
||||
style="width: 180px"
|
||||
label="ปีประวัติลงเวลา"
|
||||
v-model="filterYear"
|
||||
:options="yearOption"
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
/>
|
||||
<q-space />
|
||||
<q-btn
|
||||
outline
|
||||
icon="add"
|
||||
label="เพิ่มรายการลงเวลากรณีพิเศษ"
|
||||
color="secondary"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
interface DataOption {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
export type { DataOption };
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
interface FormRef {
|
||||
model: object | null;
|
||||
useLocation: object | null;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export type { FormRef };
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
/**
|
||||
* Router Checkin
|
||||
*/
|
||||
|
||||
const Checkin = () => import("@/modules/04_checkin/views/CheckIn.vue");
|
||||
const History = () => import("@/modules/04_checkin/views/History.vue");
|
||||
|
||||
/* const Checkout = () => import("@/modules/04_checkin/views/Checkout.vue");
|
||||
*/
|
||||
export default [
|
||||
{
|
||||
path: "/check-in",
|
||||
name: "Checkin",
|
||||
component: Checkin,
|
||||
meta: {
|
||||
Auth: true,
|
||||
Key: [7],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/check-in/history",
|
||||
name: "History",
|
||||
component: History,
|
||||
meta: {
|
||||
Auth: true,
|
||||
Key: [7],
|
||||
},
|
||||
},
|
||||
/* {
|
||||
path: "/check-out",
|
||||
name: "Checkout",
|
||||
component: Checkout,
|
||||
meta: {
|
||||
Auth: true,
|
||||
Key: [7],
|
||||
},
|
||||
}, */
|
||||
];
|
||||
|
|
@ -1,234 +0,0 @@
|
|||
<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="bg-secondary text-white col-12 row items-center q-px-md q-py-sm"
|
||||
>
|
||||
<div class="col-2">
|
||||
<q-btn
|
||||
icon="arrow_backt"
|
||||
unelevated
|
||||
round
|
||||
dense
|
||||
flat
|
||||
color="white"
|
||||
@click="router.go(-1)"
|
||||
/>
|
||||
</div>
|
||||
<q-space />
|
||||
<span class="text-body1 text-weight-bold col-8 text-center"
|
||||
>ประวัติการลงเวลา</span
|
||||
>
|
||||
<div class="col-2"></div>
|
||||
</div>
|
||||
<div class="col-12 q-pa-md text-grey-9">
|
||||
<ToolBar />
|
||||
<Table
|
||||
:style="$q.screen.gt.xs ? 'max-height: 64vh' : ''"
|
||||
:rows="rows"
|
||||
:columns="columns"
|
||||
:filter="filter"
|
||||
:visible-columns="visibleColumns"
|
||||
v-model:inputfilter="filter"
|
||||
v-model:inputvisible="visibleColumns"
|
||||
:pagination="initialPagination"
|
||||
:inputShow="false"
|
||||
:grid="$q.screen.gt.xs ? false : true"
|
||||
>
|
||||
<template #columns="props">
|
||||
<q-tr :props="props">
|
||||
<q-td key="no" :props="props">
|
||||
{{ props.rowIndex + 1 }}
|
||||
</q-td>
|
||||
<q-td key="date" :props="props">
|
||||
{{ props.row.date }}
|
||||
</q-td>
|
||||
<q-td key="in" :props="props">
|
||||
{{ props.row.in }}
|
||||
</q-td>
|
||||
<q-td key="loIn" :props="props">
|
||||
{{ props.row.loIn }}
|
||||
</q-td>
|
||||
<q-td key="out" :props="props">
|
||||
{{ props.row.out }}
|
||||
</q-td>
|
||||
<q-td key="loOut" :props="props">
|
||||
{{ props.row.loOut }}
|
||||
</q-td>
|
||||
<q-td key="status" :props="props">
|
||||
<span
|
||||
:class="
|
||||
props.row.status == 'ลงเวลาเรียบร้อย'
|
||||
? 'text-blue'
|
||||
: 'text-orange'
|
||||
"
|
||||
>{{ props.row.status }}</span
|
||||
>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</template>
|
||||
<template #item="props">
|
||||
<div
|
||||
class="q-pa-xs col-xs-12 col-sm-6 col-md-4 col-lg-3 grid-style-transition"
|
||||
>
|
||||
<q-card bordered flat class="q-py-sm shadow-0">
|
||||
<q-list dense>
|
||||
<q-item
|
||||
v-for="col in props.cols"
|
||||
:key="col.name"
|
||||
:props="props"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label caption>{{ col.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section side>
|
||||
<q-item-label>
|
||||
<span
|
||||
v-if="col.name == 'status'"
|
||||
:class="
|
||||
props.row.status == 'ลงเวลาเรียบร้อย'
|
||||
? 'text-blue'
|
||||
: 'text-orange'
|
||||
"
|
||||
>{{ col.value }}</span
|
||||
>
|
||||
<span v-else class="text-black">{{ col.value }}</span>
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-card>
|
||||
</div>
|
||||
</template>
|
||||
</Table>
|
||||
</div>
|
||||
</q-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type { QTableProps } from "quasar";
|
||||
import { ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import Table from "@/modules/checkin/componenst/tableHistory.vue";
|
||||
import ToolBar from "@/modules/checkin/componenst/toolBar.vue";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const filter = ref<string>("");
|
||||
const visibleColumns = ref<string[]>([
|
||||
"date",
|
||||
"in",
|
||||
"loIn",
|
||||
"out",
|
||||
"loOut",
|
||||
"status",
|
||||
]);
|
||||
const columns = ref<QTableProps["columns"]>([
|
||||
// {
|
||||
// name: "no",
|
||||
// align: "left",
|
||||
// label: "ลำดับ",
|
||||
// sortable: true,
|
||||
// field: "no",
|
||||
// headerStyle: "font-size: 14px",
|
||||
// style: "font-size: 14px; width:5%;",
|
||||
// },
|
||||
{
|
||||
name: "date",
|
||||
align: "left",
|
||||
label: "วัน/เดือน/ปี",
|
||||
sortable: true,
|
||||
field: "date",
|
||||
headerStyle: "font-size: 14px",
|
||||
style: "font-size: 14px; width:15%;",
|
||||
},
|
||||
{
|
||||
name: "in",
|
||||
align: "left",
|
||||
label: "เวลาเข้างาน",
|
||||
sortable: true,
|
||||
field: "in",
|
||||
headerStyle: "font-size: 14px",
|
||||
style: "font-size: 14px; width:15%;",
|
||||
},
|
||||
{
|
||||
name: "loIn",
|
||||
align: "left",
|
||||
label: "พิกัด",
|
||||
sortable: true,
|
||||
field: "loIn",
|
||||
headerStyle: "font-size: 14px",
|
||||
style: "font-size: 14px",
|
||||
},
|
||||
{
|
||||
name: "out",
|
||||
align: "left",
|
||||
label: "เวลาออกงาน",
|
||||
sortable: true,
|
||||
field: "out",
|
||||
headerStyle: "font-size: 14px",
|
||||
style: "font-size: 14px; width:15%;",
|
||||
},
|
||||
{
|
||||
name: "loOut",
|
||||
align: "left",
|
||||
label: "พิกัด",
|
||||
sortable: true,
|
||||
field: "loOut",
|
||||
headerStyle: "font-size: 14px",
|
||||
style: "font-size: 14px",
|
||||
},
|
||||
{
|
||||
name: "status",
|
||||
align: "left",
|
||||
label: "สถานะ",
|
||||
sortable: true,
|
||||
field: "status",
|
||||
headerStyle: "font-size: 14px",
|
||||
style: "font-size: 14px; width:10%;",
|
||||
},
|
||||
]);
|
||||
const rows = ref<any>([
|
||||
{
|
||||
no: "1",
|
||||
date: "13/08/66",
|
||||
in: "11:20",
|
||||
loIn: "สำนักงาน ก.ก ",
|
||||
out: "",
|
||||
loOut: "",
|
||||
status: "",
|
||||
},
|
||||
{
|
||||
no: "2",
|
||||
date: "12/08/66",
|
||||
in: "08:04",
|
||||
loIn: "สำนักงาน ก.ก ",
|
||||
out: "17:01",
|
||||
loOut: "สำนักงาน ก.ก ",
|
||||
status: "ลงเวลาเรียบร้อย",
|
||||
},
|
||||
{
|
||||
no: "3",
|
||||
date: "11/08/66",
|
||||
in: "08:34",
|
||||
loIn: "สำนักงาน ก.ก ",
|
||||
out: "17:36",
|
||||
loOut: "สำนักงาน ก.ก ",
|
||||
status: "สาย ทำงานครบ",
|
||||
},
|
||||
{
|
||||
no: "4",
|
||||
date: "10/08/66",
|
||||
in: "08:48",
|
||||
loIn: "สำนักงาน ก.ก ",
|
||||
out: "17:00",
|
||||
loOut: "สำนักงาน ก.ก ",
|
||||
status: "สาย ทำงานไม่ครบ",
|
||||
},
|
||||
]);
|
||||
const initialPagination = ref({
|
||||
rowsPerPage: 0,
|
||||
});
|
||||
</script>
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
import "./styles/quasar.scss";
|
||||
import "@quasar/extras/material-icons/material-icons.css";
|
||||
import "@quasar/extras/material-icons-outlined/material-icons-outlined.css";
|
||||
import "@quasar/extras/material-icons-round/material-icons-round.css";
|
||||
import "@quasar/extras/material-icons-sharp/material-icons-sharp.css";
|
||||
|
||||
// To be used on app.use(Quasar, { ... })
|
||||
export default {
|
||||
config: {},
|
||||
plugins: {},
|
||||
};
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
/* eslint-disable no-console */
|
||||
|
||||
import { register } from "register-service-worker";
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
register(`${process.env.BASE_URL}service-worker.js`, {
|
||||
ready() {
|
||||
console.log(
|
||||
"App is being served from cache by a service worker.\n" +
|
||||
"For more details, visit https://goo.gl/AFskqB"
|
||||
);
|
||||
},
|
||||
registered() {
|
||||
console.log("Service worker has been registered.");
|
||||
},
|
||||
cached() {
|
||||
console.log("Content has been cached for offline use.");
|
||||
},
|
||||
updatefound() {
|
||||
console.log("New content is downloading.");
|
||||
},
|
||||
updated() {
|
||||
console.log("New content is available; please refresh.");
|
||||
},
|
||||
offline() {
|
||||
console.log(
|
||||
"No internet connection found. App is running in offline mode."
|
||||
);
|
||||
},
|
||||
error(error) {
|
||||
console.error("Error during service worker registration:", error);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
|
||||
import HomeView from "../views/HomeView.vue";
|
||||
import history from "../modules/checkin/views/HistoryView.vue";
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: "/",
|
||||
name: "home",
|
||||
component: HomeView,
|
||||
},
|
||||
{
|
||||
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(/* webpackChunkName: "about" */ "../views/AboutView.vue"),
|
||||
},
|
||||
{
|
||||
path: "/check-in/history",
|
||||
name: "history",
|
||||
component: history,
|
||||
},
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(process.env.BASE_URL),
|
||||
routes,
|
||||
});
|
||||
|
||||
export default router;
|
||||
6
src/shims-vue.d.ts
vendored
|
|
@ -1,6 +0,0 @@
|
|||
/* eslint-disable */
|
||||
declare module "*.vue" {
|
||||
import type { DefineComponent } from "vue";
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
import { defineStore } from "pinia";
|
||||
|
||||
export const useCounterMixin = defineStore("mixin", () => {
|
||||
function date2Thai(
|
||||
srcDate: Date,
|
||||
isFullMonth: boolean = false,
|
||||
isTime: boolean = false
|
||||
) {
|
||||
if (srcDate == null) {
|
||||
return null;
|
||||
`
|
||||
`;
|
||||
}
|
||||
const date = new Date(srcDate);
|
||||
const isValidDate = Boolean(+date);
|
||||
if (!isValidDate) return srcDate.toString();
|
||||
if (isValidDate && date.getFullYear() < 1000) return srcDate.toString();
|
||||
const fullMonthThai = [
|
||||
"มกราคม",
|
||||
"กุมภาพันธ์",
|
||||
"มีนาคม",
|
||||
"เมษายน",
|
||||
"พฤษภาคม",
|
||||
"มิถุนายน",
|
||||
"กรกฎาคม",
|
||||
"สิงหาคม",
|
||||
"กันยายน",
|
||||
"ตุลาคม",
|
||||
"พฤศจิกายน",
|
||||
"ธันวาคม",
|
||||
];
|
||||
const abbrMonthThai = [
|
||||
"ม.ค.",
|
||||
"ก.พ.",
|
||||
"มี.ค.",
|
||||
"เม.ย.",
|
||||
"พ.ค.",
|
||||
"มิ.ย.",
|
||||
"ก.ค.",
|
||||
"ส.ค.",
|
||||
"ก.ย.",
|
||||
"ต.ค.",
|
||||
"พ.ย.",
|
||||
"ธ.ค.",
|
||||
];
|
||||
let dstYear = 0;
|
||||
if (date.getFullYear() > 2500) {
|
||||
dstYear = date.getFullYear();
|
||||
} else {
|
||||
dstYear = date.getFullYear() + 543;
|
||||
}
|
||||
let dstMonth = "";
|
||||
if (isFullMonth) {
|
||||
dstMonth = fullMonthThai[date.getMonth()];
|
||||
} else {
|
||||
dstMonth = abbrMonthThai[date.getMonth()];
|
||||
}
|
||||
let dstTime = "";
|
||||
if (isTime) {
|
||||
const H = date.getHours().toString().padStart(2, "0");
|
||||
const M = date.getMinutes().toString().padStart(2, "0");
|
||||
// const S = date.getSeconds().toString().padStart(2, "0")
|
||||
// dstTime = " " + H + ":" + M + ":" + S + " น."
|
||||
dstTime = " " + H + ":" + M + " น.";
|
||||
}
|
||||
return (
|
||||
date.getDate().toString().padStart(2, "0") +
|
||||
" " +
|
||||
dstMonth +
|
||||
" " +
|
||||
dstYear +
|
||||
dstTime
|
||||
);
|
||||
}
|
||||
return {
|
||||
date2Thai,
|
||||
};
|
||||
});
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@import "./quasar.variables.scss";
|
||||
@import "~quasar-styl";
|
||||
// @import '~quasar-addon-styl';
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
// It's highly recommended to change the default colors
|
||||
// to match your app's branding.
|
||||
|
||||
$primary: #02a998;
|
||||
$secondary: #016987;
|
||||
$accent: #9c27b0;
|
||||
|
||||
$dark: #35473c;
|
||||
|
||||
$positive: #21ba45;
|
||||
$negative: #c10015;
|
||||
$info: #31ccec;
|
||||
$warning: #f2c037;
|
||||
|
||||
@import "~quasar-variables-styl";
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
<template>
|
||||
<div class="about">
|
||||
<h1>This is an about page</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<template>
|
||||
<div class="home">
|
||||
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// import { Options, Vue } from "vue-class-component";
|
||||
import HelloWorld from "@/components/HelloWorld.vue"; // @ is an alias to /src
|
||||
</script>
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"moduleResolution": "node",
|
||||
"experimentalDecorators": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"useDefineForClassFields": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"types": ["webpack-env"],
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
},
|
||||
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"tests/**/*.ts",
|
||||
"tests/**/*.tsx"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
const { defineConfig } = require("@vue/cli-service");
|
||||
module.exports = defineConfig({
|
||||
transpileDependencies: ["quasar"],
|
||||
|
||||
pluginOptions: {
|
||||
quasar: {
|
||||
importStrategy: "kebab",
|
||||
rtlSupport: false,
|
||||
},
|
||||
},
|
||||
|
||||
});
|
||||