update format

This commit is contained in:
DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2024-09-02 13:40:15 +07:00
parent b44076d99d
commit 36b3eef7fd
10 changed files with 222 additions and 226 deletions

View file

@ -1,19 +1,21 @@
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = { module.exports = {
root: true, root: true,
env: {
node: true,
es2022: true,
},
extends: [ extends: [
'plugin:vue/vue3-essential', 'plugin:vue/vue3-essential',
'eslint:recommended', 'eslint:recommended',
'@vue/typescript/recommended', '@vue/eslint-config-typescript/recommended',
'@vue/eslint-config-prettier/recommended',
], ],
rules: { overrides: [
// 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', {
// 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' files: ['cypress/e2e/**/*.{cy,spec}.{js,ts,jsx,tsx}'],
'vue/no-mutating-props': 'off', extends: ['plugin:cypress/recommended'],
// '@typescript-eslint/no-explicit-any': 'off', },
'vue/multi-word-component-names': 'off' ],
parserOptions: {
ecmaVersion: 'latest',
}, },
} }

View file

@ -1,8 +1,4 @@
<template> <template>
<!-- <nav>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</nav> -->
<router-view /> <router-view />
</template> </template>

View file

@ -1,7 +1,7 @@
import env from "./index"; import env from './index'
const leave = `${env.API_URI}/leave`; const leave = `${env.API_URI}/leave`
export default { export default {
checkin: () => `${leave}/check-in`, checkin: () => `${leave}/check-in`,
checkTime: () => `${leave}/check-time`, checkTime: () => `${leave}/check-time`,
}; }

View file

@ -139,71 +139,71 @@ watch(
) )
</script> </script>
<template> <template>
<div class="col-12 row"> <div class="col-12 row">
<div class="row q-col-q-gutter-sm q-pa-md"> <div class="row q-col-q-gutter-sm q-pa-md">
<q-card <q-card
flat flat
bordered bordered
:class="$q.screen.gt.xs ? 'col-12 bg-grey-1' : 'col-12 '" :class="$q.screen.gt.xs ? 'col-12 bg-grey-1' : 'col-12 '"
> >
<q-card-section class="bg-primary text-white q-pa-sm"> <q-card-section class="bg-primary text-white q-pa-sm">
<div class="text-center text-bold">เวลาปจจ</div> <div class="text-center text-bold">เวลาปจจ</div>
</q-card-section> </q-card-section>
<!-- <div class="q-pa-sm text-primary">เวลาปจจ</div> --> <!-- <div class="q-pa-sm text-primary">เวลาปจจ</div> -->
<q-card-section class="text-center q-pa-sm"> <q-card-section class="text-center q-pa-sm">
<div class="row q-gutter-md"> <div class="row q-gutter-md">
<div class="col">{{ date2Thai(dateNow) }}</div> <div class="col">{{ date2Thai(dateNow) }}</div>
<div class="col">{{ timeNoew }}</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> </div>
</q-card-section> </q-card>
</q-card>
<q-card flat bordered class="col-12 q-mt-sm"> <!-- <q-card flat bordered class="q-pa-sm col-12 bg-grey-1 q-mt-sm" v-else>
<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>
</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="row q-gutter-md text-grey-5">
<div class="col-1"> <div class="col-1">
<q-icon color="grey-5" name="calendar_today" /> <q-icon color="grey-5" name="calendar_today" />
@ -212,66 +212,66 @@ watch(
</div> </div>
</q-card> --> </q-card> -->
<q-card <q-card
flat flat
bordered bordered
class="q-pa-sm col-12 q-mt-sm" class="q-pa-sm col-12 q-mt-sm"
:style="{ :style="{
borderColor: checkstatusBox ? '#B22222' : '', borderColor: checkstatusBox ? '#B22222' : '',
borderWidth: checkstatusBox ? '2px' : '', borderWidth: checkstatusBox ? '2px' : '',
}" }"
> >
<div class="row q-gutter-xs"> <div class="row q-gutter-xs">
<div class="col-12"> <div class="col-12">
<q-checkbox <q-checkbox
keep-color keep-color
color="primary" color="primary"
v-model="checkboxIn" v-model="checkboxIn"
label="ขอแก้ไขเวลาเข้างาน" label="ขอแก้ไขเวลาเข้างาน"
/> />
</div> </div>
<div class="col-12"> <div class="col-12">
<q-checkbox <q-checkbox
keep-color keep-color
v-model="checkboxOut" v-model="checkboxOut"
color="primary" color="primary"
label="ขอแก้ไขเวลาออกงาน" label="ขอแก้ไขเวลาออกงาน"
/> />
</div>
</div> </div>
</q-card>
<div
v-if="checkstatusBox"
class="text-red-9 q-pa-sm"
style="font-size: 10px"
>
กรณาเลอก
</div> </div>
</q-card>
<div <q-card flat bordered class="q-pa-sm col-12 q-mt-sm">
v-if="checkstatusBox" <q-input
class="text-red-9 q-pa-sm" ref="reasonRef"
style="font-size: 10px" 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>
<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>
<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> </template>
<style scoped></style> <style scoped></style>

View file

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

View file

@ -1,71 +1,71 @@
const ACCESS_TOKEN = "BMAHRIS_KEYCLOAK_IDENTITY"; const ACCESS_TOKEN = 'BMAHRIS_KEYCLOAK_IDENTITY'
interface AuthResponse { interface AuthResponse {
access_token: string; access_token: string
expires_in: number; expires_in: number
refresh_token: string; refresh_token: string
} }
const authenticated = async () => ((await getToken()) ? true : false); const authenticated = async () => ((await getToken()) ? true : false)
async function setAuthen(r: AuthResponse) { async function setAuthen(r: AuthResponse) {
await setCookie(ACCESS_TOKEN, r.access_token, r.expires_in); await setCookie(ACCESS_TOKEN, r.access_token, r.expires_in)
window.location.href = "/"; window.location.href = '/'
} }
async function logout() { async function logout() {
await deleteCookie(ACCESS_TOKEN); await deleteCookie(ACCESS_TOKEN)
window.location.href = "/login"; window.location.href = '/login'
} }
async function getToken() { async function getToken() {
return getCookie(ACCESS_TOKEN); return getCookie(ACCESS_TOKEN)
} }
// 2024-08-29T02:55:13.000Z // 2024-08-29T02:55:13.000Z
function setCookie(name: string, value: any, time: number) { function setCookie(name: string, value: any, time: number) {
let expires = ""; let expires = ''
if (time) { if (time) {
const date = new Date(); const date = new Date()
date.setTime(date.getTime() + time * 1000); date.setTime(date.getTime() + time * 1000)
// date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); // date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
expires = "; expires=" + date.toUTCString(); expires = '; expires=' + date.toUTCString()
} }
document.cookie = name + "=" + (value || "") + expires + "; path=/"; document.cookie = name + '=' + (value || '') + expires + '; path=/'
} }
function getCookie(name: string) { function getCookie(name: string) {
const nameEQ = name + "="; const nameEQ = name + '='
const ca = document.cookie.split(";"); const ca = document.cookie.split(';')
for (let i = 0; i < ca.length; i++) { for (let i = 0; i < ca.length; i++) {
let c = ca[i]; let c = ca[i]
while (c.charAt(0) == " ") c = c.substring(1, c.length); while (c.charAt(0) == ' ') c = c.substring(1, c.length)
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length)
} }
return null; return null
} }
function deleteCookie(name: string) { function deleteCookie(name: string) {
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`; document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`
} }
async function tokenParsed() { async function tokenParsed() {
const token = await getCookie(ACCESS_TOKEN); const token = await getCookie(ACCESS_TOKEN)
if (!token) { if (!token) {
return null; return null
} }
const base64Url = token.split(".")[1]; const base64Url = token.split('.')[1]
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/"); const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')
const jsonPayload = decodeURIComponent( const jsonPayload = decodeURIComponent(
window window
.atob(base64) .atob(base64)
.split("") .split('')
.map(function (c) { .map(function (c) {
return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
}) })
.join("") .join('')
); )
return JSON.parse(jsonPayload); return JSON.parse(jsonPayload)
} }
export { getToken, authenticated, logout, setAuthen, tokenParsed }; export { getToken, authenticated, logout, setAuthen, tokenParsed }

View file

@ -3,20 +3,18 @@
* - Helper Functions * - Helper Functions
*/ */
const filters = { const filters = {
/**
/** * compactNumber Social Media 1,000 1K 1,000,000 1M
* compactNumber Social Media 1,000 1K 1,000,000 1M * : {{ $filters.compactNumber(value) }}
* : {{ $filters.compactNumber(value) }} *
* * @param val
* @param val * @returns
* @returns */
*/ compactNumber(val: number) {
compactNumber (val: number) { const formatter = Intl.NumberFormat('en', { notation: 'compact' })
const formatter = Intl.NumberFormat('en', { notation: 'compact'}) return formatter.format(val)
return formatter.format(val) },
}
} }
export default filters; export default filters

View file

@ -1,9 +1,9 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from "vue"; import { defineComponent } from 'vue'
export default defineComponent({ export default defineComponent({
name: "Error404NotFound", name: 'Error404NotFound',
}); })
</script> </script>
<template> <template>

View file

@ -384,7 +384,7 @@ onMounted(async () => {
v-model="model" v-model="model"
:options="options" :options="options"
prefix="ระบุสถานที่ :" prefix="ระบุสถานที่ :"
:rules="[(val) => !!val || 'กรุณาระบุสถานที่']" :rules="[(val:string) => !!val || 'กรุณาระบุสถานที่']"
lazy-rules lazy-rules
@update:model-value="selectLocation()" @update:model-value="selectLocation()"
hide-bottom-space hide-bottom-space
@ -401,7 +401,7 @@ onMounted(async () => {
outlined outlined
v-model="useLocation" v-model="useLocation"
label="ระบุสถานที่" label="ระบุสถานที่"
:rules="[(val) => !!val || 'กรุณาระบุสถานที่']" :rules="[(val:string) => !!val || 'กรุณาระบุสถานที่']"
lazy-rules lazy-rules
/> />
</div> </div>
@ -470,9 +470,7 @@ onMounted(async () => {
{{ date2Thai(Thai) }} {{ date2Thai(Thai) }}
</div> </div>
<div class="row col-12 justify-center q-pt-sm"> <div class="row col-12 justify-center q-pt-sm">
<div class="text-h3 text-weight-bold"> <div class="text-h3 text-weight-bold"></div>
<!-- {{ formattedH }}<span class="q-ma-md">:</span> -->
</div>
<div class="text-h3 text-weight-bold">{{ timeChickin }}</div> <div class="text-h3 text-weight-bold">{{ timeChickin }}</div>
</div> </div>
</div> </div>

View file

@ -1,13 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, watch } from 'vue' import { ref, onMounted, watch } from 'vue'
import { useRouter } from 'vue-router'
import { useQuasar } from 'quasar' import { useQuasar } from 'quasar'
import { useRouter } from 'vue-router'
import http from '@/plugins/http' import http from '@/plugins/http'
import config from '@/app.config' import config from '@/app.config'
import { logout, tokenParsed } from '@/plugins/auth' import { logout, tokenParsed } from '@/plugins/auth'
import { useCounterMixin } from '@/stores/mixin'
import type { notiType } from '@/interface/index/Main' import type { notiType } from '@/interface/index/Main'
import { useCounterMixin } from '@/stores/mixin'
import type { Noti } from '@/interface/response/Main' import type { Noti } from '@/interface/response/Main'
const mixin = useCounterMixin() const mixin = useCounterMixin()
@ -15,21 +16,32 @@ const { date2Thai, hideLoader, messageError, dialogRemove, success } = mixin
const router = useRouter() const router = useRouter()
const $q = useQuasar() const $q = useQuasar()
const notiTrigger = ref<boolean>(false) const fullName = ref<string>('') //
const notiList = ref<notiType[]>([]) const notiTrigger = ref<boolean>(false) // ,
const totalNotiList = ref<number>(0) const notiList = ref<notiType[]>([]) //
const totalNoti = ref<number>(0) const totalNotiList = ref<number>(0) //
/*** ฟังก์ชั่นดึงข้อมูลจำนวนการแจ้งเตือน */ const totalNoti = ref<number>(0) //
async function fetchTotolNotificate() { const statusLoad = ref<boolean>(false) //
await http.get(config.API.msgNotificateTotal).then((res) => { //
totalNoti.value = res.data.result const thaiOptions: Intl.DateTimeFormatOptions = {
}) hour: '2-digit',
// .catch((err) => { minute: '2-digit',
// messageError($q, err) }
// })
/**
* งกนดงขอมลจำนวนการแจงเตอน
*/
async function fetchTotolNotificate() {
await http
.get(config.API.msgNotificateTotal)
.then(async (res) => {
totalNoti.value = await res.data.result
})
.catch((err) => {
messageError($q, err)
})
} }
const statusLoad = ref<boolean>(false)
/** /**
* งกนดงขอมลรายการแจงเตอน * งกนดงขอมลรายการแจงเตอน
* @param index page องการโหลดขอม * @param index page องการโหลดขอม
@ -45,7 +57,6 @@ async function fetchNotifications(index: number, type: string) {
if (type === 'DEL') { if (type === 'DEL') {
notiList.value = [] notiList.value = []
} }
response.map((e: Noti) => { response.map((e: Noti) => {
list.push({ list.push({
id: e.id, id: e.id,
@ -61,9 +72,9 @@ async function fetchNotifications(index: number, type: string) {
notiList.value.push(...list) notiList.value.push(...list)
statusLoad.value = totalNotiList.value === 0 ? true : false statusLoad.value = totalNotiList.value === 0 ? true : false
}) })
// .catch((err) => { .catch((err) => {
// messageError($q, err) messageError($q, err)
// }) })
} }
/** /**
@ -74,22 +85,24 @@ async function onClickDelete(id: string, index: number) {
dialogRemove($q, async () => { dialogRemove($q, async () => {
await http await http
.delete(config.API.msgId(id)) .delete(config.API.msgId(id))
.then(() => { .then(async () => {
notiList.value.splice(index, 1) notiList.value.splice(index, 1)
totalNotiList.value-- totalNotiList.value--
notiList.value.length === 14 && (await fetchNotifications(1, 'DEL'))
success($q, 'ลบข้อมูลสำเร็จ') success($q, 'ลบข้อมูลสำเร็จ')
}) })
.catch((e) => { .catch((e) => {
messageError($q, e) messageError($q, e)
}) })
.finally(async () => { .finally(async () => {
notiList.value.length === 14 && fetchNotifications(1, 'DEL')
hideLoader() hideLoader()
}) })
}) })
} }
/** function logout*/ /**
* function logout
*/
function onClickLogout() { function onClickLogout() {
$q.dialog({ $q.dialog({
title: 'ยืนยันการออกจากระบบ', title: 'ยืนยันการออกจากระบบ',
@ -102,18 +115,14 @@ function onClickLogout() {
}) })
} }
const thaiOptions: Intl.DateTimeFormatOptions = {
hour: '2-digit',
minute: '2-digit',
}
const page = ref<number>(0) const page = ref<number>(0)
/** /**
* โหลดรายการแจงเตอนเพมเม scroll * โหลดรายการแจงเตอนเพมเม scroll
* @param index * @param index
* @param done * @param done
*/ */
function onLoad(index: any, done: any) { function onLoad(index: number, done: Function) {
if ( if (
notiList.value.length < totalNotiList.value || notiList.value.length < totalNotiList.value ||
(notiList.value.length == 0 && totalNotiList.value === 0) (notiList.value.length == 0 && totalNotiList.value === 0)
@ -140,7 +149,6 @@ watch(
} }
) )
const fullName = ref<string>('')
onMounted(async () => { onMounted(async () => {
fetchTotolNotificate() fetchTotolNotificate()
const checkTokenParsed = await tokenParsed() const checkTokenParsed = await tokenParsed()
@ -251,12 +259,6 @@ onMounted(async () => {
}} }}
. <q-space /> . <q-space />
</q-item-label> </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> </q-item-section>
<div> <div>
<q-btn <q-btn