เพิ่ม Skeleton popup info

This commit is contained in:
DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2025-07-29 15:14:23 +07:00
parent ee9470182f
commit c94e3964fc
7 changed files with 187 additions and 145 deletions

View file

@ -0,0 +1,33 @@
<script setup lang="ts">
import { computed } from "vue";
/** importType*/
import type { QTableProps } from "quasar";
const columns = defineModel<QTableProps["columns"]>("columns", {
required: true,
});
/** จำนวนคอลัมน์ */
const skeletonColumns = computed(() => columns.value?.length || 5);
</script>
<template>
<q-markup-table flat bordered>
<thead>
<tr>
<th v-for="(n, idx) in skeletonColumns" :key="idx" style="width: 150px">
<q-skeleton type="text" />
</th>
</tr>
</thead>
<tbody>
<tr v-for="n in 5" :key="n" :class="n % 2 === 0 ? 'bg-grey-2' : ''">
<td v-for="(col, idx) in skeletonColumns" :key="idx">
<q-skeleton type="text" width="80px" />
</td>
</tr>
</tbody>
</q-markup-table>
</template>

View file

@ -1,27 +1,30 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http"; import http from "@/plugins/http";
import config from "@/app.config"; import config from "@/app.config";
import { useQuasar } from "quasar"; import { useResultsPerformDataStore } from "@/modules/04_registryPerson/stores/ResultsPerformance";
import { useCounterMixin } from "@/stores/mixin";
/** importType*/ /** importType*/
import type { QTableProps } from "quasar"; import type { QTableProps } from "quasar";
import type { DataDiscipline } from "@/modules/13_salary/interface/response/Main"; import type { DataDiscipline } from "@/modules/13_salary/interface/response/Main";
/** importStore*/ /** importComponents*/
import { useResultsPerformDataStore } from "@/modules/04_registryPerson/stores/ResultsPerformance"; import SkeletonTable from "@/components/SkeletonTable.vue";
import { useCounterMixin } from "@/stores/mixin";
const $q = useQuasar(); const $q = useQuasar();
const store = useResultsPerformDataStore(); const store = useResultsPerformDataStore();
const { textRangePoint, textPoint } = store; const { textRangePoint, textPoint } = store;
const { date2Thai, showLoader, hideLoader, messageError, onSearchDataTable } = const { date2Thai, messageError, onSearchDataTable } = useCounterMixin();
useCounterMixin();
/** props*/ /** props*/
const profileId = defineModel<string>("profileId", { required: true }); const profileId = defineModel<string>("profileId", { required: true });
const employeeClass = defineModel<string>("employeeClass", { required: true }); const employeeClass = defineModel<string>("employeeClass", { required: true });
const isLoading = ref<boolean>(true);
/** Table*/ /** Table*/
const keyword = ref<string>(""); const keyword = ref<string>("");
const rows = ref<DataDiscipline[]>([]); const rows = ref<DataDiscipline[]>([]);
@ -131,7 +134,7 @@ const visibleColumns = ref<String[]>([
/** function fetch รายการข้อมูลผลการประเมินการปฏิบัติราชการ*/ /** function fetch รายการข้อมูลผลการประเมินการปฏิบัติราชการ*/
function fetchListAssessments() { function fetchListAssessments() {
showLoader(); isLoading.value = true;
http http
.get( .get(
config.API.profileNewAssessmentsByProfileId( config.API.profileNewAssessmentsByProfileId(
@ -140,14 +143,14 @@ function fetchListAssessments() {
) )
) )
.then((res) => { .then((res) => {
rows.value = res.data.result;
rowsData.value = res.data.result; rowsData.value = res.data.result;
onSearch();
}) })
.catch((err) => { .catch((err) => {
messageError($q, err); messageError($q, err);
}) })
.finally(() => { .finally(() => {
hideLoader(); isLoading.value = false;
}); });
} }
@ -195,8 +198,12 @@ onMounted(() => {
style="min-width: 140px" style="min-width: 140px"
/> />
</div> </div>
<div class="col-12"> <div class="col-12">
<SkeletonTable v-if="isLoading" :columns="columns" />
<d-table <d-table
v-else
ref="table" ref="table"
row-key="id" row-key="id"
flat flat
@ -228,5 +235,3 @@ onMounted(() => {
</div> </div>
</q-card-section> </q-card-section>
</template> </template>
<style scoped></style>

View file

@ -1,28 +1,27 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import http from "@/plugins/http";
import config from "@/app.config";
import { useQuasar } from "quasar"; import { useQuasar } from "quasar";
/** import http from "@/plugins/http";
* importType import config from "@/app.config";
*/ import { useCounterMixin } from "@/stores/mixin";
/** importType*/
import type { QTableProps } from "quasar"; import type { QTableProps } from "quasar";
import type { DataDiscipline } from "@/modules/13_salary/interface/response/Main"; import type { DataDiscipline } from "@/modules/13_salary/interface/response/Main";
/** /** importComponents*/
* importStore import SkeletonTable from "@/components/SkeletonTable.vue";
*/
import { useCounterMixin } from "@/stores/mixin";
const $q = useQuasar(); const $q = useQuasar();
const { date2Thai, showLoader, hideLoader, messageError, onSearchDataTable } = const { date2Thai, messageError, onSearchDataTable } = useCounterMixin();
useCounterMixin();
/** props*/ /** props*/
const profileId = defineModel<string>("profileId", { required: true }); const profileId = defineModel<string>("profileId", { required: true });
const employeeClass = defineModel<string>("employeeClass", { required: true }); const employeeClass = defineModel<string>("employeeClass", { required: true });
const isLoading = ref<boolean>(true);
/** Table*/ /** Table*/
const keyword = ref<string>(""); const keyword = ref<string>("");
const rows = ref<DataDiscipline[]>([]); const rows = ref<DataDiscipline[]>([]);
@ -210,7 +209,6 @@ const insigniaData = ref<any[]>([]);
/** fetch ข้อมูลรายการเครื่องราชอิสริยาภรณ์*/ /** fetch ข้อมูลรายการเครื่องราชอิสริยาภรณ์*/
async function fetchListInsignia() { async function fetchListInsignia() {
showLoader();
http http
.get( .get(
config.API.profileNewInsignByProfileId( config.API.profileNewInsignByProfileId(
@ -219,21 +217,18 @@ async function fetchListInsignia() {
) )
) )
.then((res) => { .then((res) => {
rows.value = res.data.result;
rowsData.value = res.data.result; rowsData.value = res.data.result;
onSearch();
}) })
.catch((err) => { .catch((err) => {
messageError($q, err); messageError($q, err);
})
.finally(() => {
hideLoader();
}); });
} }
/** fetch ข้อมูลเครื่องราชอิสริยาภรณ์*/ /** fetch ข้อมูลเครื่องราชอิสริยาภรณ์*/
async function fetchInsignia() { async function fetchInsignia() {
showLoader(); isLoading.value = true;
http await http
.get(config.API.insigniaOrg) .get(config.API.insigniaOrg)
.then(async (res) => { .then(async (res) => {
insigniaData.value = res.data.result; insigniaData.value = res.data.result;
@ -243,9 +238,10 @@ async function fetchInsignia() {
messageError($q, err); messageError($q, err);
}) })
.finally(() => { .finally(() => {
hideLoader(); isLoading.value = false;
}); });
} }
function onSearch() { function onSearch() {
rows.value = onSearchDataTable( rows.value = onSearchDataTable(
keyword.value, keyword.value,
@ -290,8 +286,12 @@ onMounted(async () => {
style="min-width: 140px" style="min-width: 140px"
/> />
</div> </div>
<div class="col-12"> <div class="col-12">
<SkeletonTable v-if="isLoading" :columns="columns" />
<d-table <d-table
v-else
ref="table" ref="table"
row-key="id" row-key="id"
flat flat

View file

@ -6,27 +6,19 @@ import http from "@/plugins/http";
import config from "@/app.config"; import config from "@/app.config";
import { checkPermissionGet } from "@/utils/permissions"; import { checkPermissionGet } from "@/utils/permissions";
import avatarMain from "@/assets/avatar_user.jpg"; import avatarMain from "@/assets/avatar_user.jpg";
import { useCounterMixin } from "@/stores/mixin";
/** /** importComponents */
* importCOmponents
*/
import InfoSalary from "@/modules/13_salary/components/InfoSalary.vue"; import InfoSalary from "@/modules/13_salary/components/InfoSalary.vue";
import InfoDiscipline from "@/modules/13_salary/components/InfoDiscipline.vue"; import InfoDiscipline from "@/modules/13_salary/components/InfoDiscipline.vue";
import InfoLeave from "@/modules/13_salary/components/InfoLeave.vue"; import InfoLeave from "@/modules/13_salary/components/InfoLeave.vue";
import InfoAssessments from "@/modules/07_insignia/components/2_Manage/InfoAssessments.vue"; import InfoAssessments from "@/modules/07_insignia/components/2_Manage/InfoAssessments.vue";
import InfoInsignia from "@/modules/07_insignia/components/2_Manage/InfoInsignia.vue"; import InfoInsignia from "@/modules/07_insignia/components/2_Manage/InfoInsignia.vue";
/** /** use */
* importStore
*/
import { useCounterMixin } from "@/stores/mixin";
/**
* use
*/
const $q = useQuasar(); const $q = useQuasar();
const router = useRouter(); const router = useRouter();
const { showLoader, hideLoader, messageError } = useCounterMixin(); const { messageError } = useCounterMixin();
/** /**
* props * props
@ -44,11 +36,11 @@ const props = defineProps({
const avatar = ref<string>(""); const avatar = ref<string>("");
const fullName = ref<string>(""); const fullName = ref<string>("");
const position = ref<string>(""); const position = ref<string>("");
const isSkeleton = ref<boolean>(false); const isLoading = ref<boolean>(true);
/** function เรียกข้อมูลส่วนตัว*/ /** function เรียกข้อมูลส่วนตัว*/
function fetchInformation() { function fetchInformation() {
isSkeleton.value = false; isLoading.value = true;
http http
.get(config.API.orgProfileById(profileId.value, props.employeeClass)) .get(config.API.orgProfileById(profileId.value, props.employeeClass))
.then(async (res) => { .then(async (res) => {
@ -65,7 +57,9 @@ function fetchInformation() {
}) })
.catch((err) => { .catch((err) => {
messageError($q, err); messageError($q, err);
isSkeleton.value = true; })
.finally(() => {
isLoading.value = false;
}); });
} }
@ -121,7 +115,33 @@ watch(
</q-toolbar> </q-toolbar>
<q-card-section class="col q-pt-none bg-grey-12" style="height: 100%"> <q-card-section class="col q-pt-none bg-grey-12" style="height: 100%">
<div class="q-gutter-md"> <div class="q-gutter-md">
<q-card bordered class="text-center bg-grey-12"> <div v-if="isLoading">
<q-skeleton
type="QAvatar"
size="120px"
style="background: #e3e3e3"
class="q-mx-auto q-mt-md"
/>
<q-skeleton
type="text"
width="150px"
style="background: #e3e3e3"
class="q-mx-auto q-mt-md"
/>
<q-skeleton
type="text"
width="100px"
style="background: #e3e3e3"
class="q-mx-auto q-mt-sm"
/>
<q-skeleton
type="QBtn"
width="150px"
style="background: #e3e3e3"
class="q-mx-auto q-mt-sm"
/>
</div>
<q-card v-else bordered class="text-center bg-grey-12">
<div class="q-mt-md"> <div class="q-mt-md">
<q-avatar size="120px" color="grey-4"> <q-avatar size="120px" color="grey-4">
<img <img
@ -191,39 +211,41 @@ watch(
</div> </div>
</q-card-section> </q-card-section>
<q-scroll-area <q-scroll-are
:thumb-style="thumbStyle" :thumb-style="thumbStyle"
style="height: 55vh; max-width: 100%" style="height: 55vh; max-width: 100%"
> >
<InfoSalary <div style="min-width: 1200px; overflow-x: auto">
v-if="type === 'posSalary'" <InfoSalary
v-model:profileId="profileId" v-if="type === 'posSalary'"
:employeeClass="employeeClass" v-model:profileId="profileId"
/> :employeeClass="employeeClass"
<InfoDiscipline />
v-if="type === 'discipline'" <InfoDiscipline
v-model:profileId="profileId" v-if="type === 'discipline'"
:employeeClass="employeeClass" v-model:profileId="profileId"
/> :employeeClass="employeeClass"
/>
<InfoLeave <InfoLeave
v-if="type === 'leave'" v-if="type === 'leave'"
v-model:profileId="profileId" v-model:profileId="profileId"
:employeeClass="employeeClass" :employeeClass="employeeClass"
/> />
<InfoAssessments <InfoAssessments
v-if="type === 'assessments'" v-if="type === 'assessments'"
v-model:profileId="profileId" v-model:profileId="profileId"
:employeeClass="employeeClass" :employeeClass="employeeClass"
/> />
<InfoInsignia <InfoInsignia
v-if="type === 'insignia'" v-if="type === 'insignia'"
v-model:profileId="profileId" v-model:profileId="profileId"
:employeeClass="employeeClass" :employeeClass="employeeClass"
/> />
</q-scroll-area> </div>
</q-scroll-are>
</q-card> </q-card>
</div> </div>
</div> </div>

View file

@ -1,33 +1,28 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import http from "@/plugins/http";
import config from "@/app.config";
import { useQuasar } from "quasar"; import { useQuasar } from "quasar";
/** import http from "@/plugins/http";
* importType import config from "@/app.config";
*/ import { useCounterMixin } from "@/stores/mixin";
/** importType*/
import type { QTableProps } from "quasar"; import type { QTableProps } from "quasar";
import type { DataDiscipline } from "@/modules/13_salary/interface/response/Main"; import type { DataDiscipline } from "@/modules/13_salary/interface/response/Main";
/** /** importComponents*/
* importStore import SkeletonTable from "@/components/SkeletonTable.vue";
*/
import { useCounterMixin } from "@/stores/mixin";
const $q = useQuasar(); const $q = useQuasar();
const { date2Thai, showLoader, hideLoader, messageError,onSearchDataTable } = useCounterMixin(); const { date2Thai, messageError, onSearchDataTable } = useCounterMixin();
/**
* props
*/
/** props*/
const profileId = defineModel<string>("profileId", { required: true }); const profileId = defineModel<string>("profileId", { required: true });
const employeeClass = defineModel<string>("employeeClass", { required: true }); const employeeClass = defineModel<string>("employeeClass", { required: true });
/** const isLoading = ref<boolean>(true);
* Table
*/ /** Table*/
const keyword = ref<string>(""); const keyword = ref<string>("");
const rows = ref<DataDiscipline[]>([]); const rows = ref<DataDiscipline[]>([]);
const rowsData = ref<DataDiscipline[]>([]); const rowsData = ref<DataDiscipline[]>([]);
@ -98,22 +93,20 @@ const visibleColumns = ref<string[]>([
"date", "date",
]); ]);
/** /** function fetch ข้อมูลวินัย*/
* function fetch อมลว
*/
function fetchListDiscipline() { function fetchListDiscipline() {
showLoader(); isLoading.value = true;
http http
.get(config.API.disciplineListCard(profileId.value, employeeClass.value)) .get(config.API.disciplineListCard(profileId.value, employeeClass.value))
.then((res) => { .then((res) => {
rows.value = res.data.result;
rowsData.value = res.data.result; rowsData.value = res.data.result;
onSearch();
}) })
.catch((err) => { .catch((err) => {
messageError($q, err); messageError($q, err);
}) })
.finally(() => { .finally(() => {
hideLoader(); isLoading.value = false;
}); });
} }
@ -161,8 +154,11 @@ onMounted(() => {
style="min-width: 140px" style="min-width: 140px"
/> />
</div> </div>
<div class="col-12"> <div class="col-12">
<SkeletonTable v-if="isLoading" :columns="columns" />
<d-table <d-table
v-else
ref="table" ref="table"
row-key="id" row-key="id"
flat flat

View file

@ -1,34 +1,28 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import http from "@/plugins/http";
import config from "@/app.config";
import { useQuasar } from "quasar"; import { useQuasar } from "quasar";
/** import http from "@/plugins/http";
* importType import config from "@/app.config";
*/ import { useCounterMixin } from "@/stores/mixin";
/** importType*/
import type { QTableProps } from "quasar"; import type { QTableProps } from "quasar";
import type { DataLeave } from "@/modules/13_salary/interface/response/Main"; import type { DataLeave } from "@/modules/13_salary/interface/response/Main";
/** /** importComponents*/
* importStore import SkeletonTable from "@/components/SkeletonTable.vue";
*/
import { useCounterMixin } from "@/stores/mixin";
const $q = useQuasar(); const $q = useQuasar();
const { date2Thai, showLoader, hideLoader, messageError, onSearchDataTable } = const { date2Thai, messageError, onSearchDataTable } = useCounterMixin();
useCounterMixin();
/**
* props
*/
/** props*/
const profileId = defineModel<string>("profileId", { required: true }); const profileId = defineModel<string>("profileId", { required: true });
const employeeClass = defineModel<string>("employeeClass", { required: true }); const employeeClass = defineModel<string>("employeeClass", { required: true });
/** const isLoading = ref<boolean>(true);
* Table
*/ /** Table*/
const keyword = ref<string>(""); const keyword = ref<string>("");
const rows = ref<DataLeave[]>([]); const rows = ref<DataLeave[]>([]);
const rowsData = ref<DataLeave[]>([]); const rowsData = ref<DataLeave[]>([]);
@ -104,22 +98,20 @@ const visibleColumns = ref<string[]>([
"reason", "reason",
]); ]);
/** /** function fetch ข้อมูลการลา*/
* function fetch อมลการลา
*/
function fetchListSalary() { function fetchListSalary() {
showLoader(); isLoading.value = true;
http http
.get(config.API.leaveListCard(profileId.value, employeeClass.value)) .get(config.API.leaveListCard(profileId.value, employeeClass.value))
.then((res) => { .then((res) => {
rows.value = res.data.result;
rowsData.value = res.data.result; rowsData.value = res.data.result;
onSearch();
}) })
.catch((e) => { .catch((e) => {
messageError($q, e); messageError($q, e);
}) })
.finally(() => { .finally(() => {
hideLoader(); isLoading.value = false;
}); });
} }
@ -196,7 +188,10 @@ onMounted(() => {
/> />
</div> </div>
<div class="col-12"> <div class="col-12">
<SkeletonTable v-if="isLoading" :columns="columns" />
<d-table <d-table
v-else
ref="table" ref="table"
row-key="id" row-key="id"
flat flat

View file

@ -1,48 +1,38 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, computed } from "vue"; import { ref, onMounted, computed } from "vue";
import { is, useQuasar } from "quasar";
import http from "@/plugins/http"; import http from "@/plugins/http";
import config from "@/app.config"; import config from "@/app.config";
import { useQuasar } from "quasar"; import { useCounterMixin } from "@/stores/mixin";
import type { QTableColumn } from "quasar";
/** /** importType*/
* importType import type { QTableColumn } from "quasar";
*/
import type { QTableProps } from "quasar";
import type { DataPosSalary } from "@/modules/13_salary/interface/response/Main"; import type { DataPosSalary } from "@/modules/13_salary/interface/response/Main";
import type { ResListSalary } from "@/modules/04_registryPerson/interface/response/Salary"; import type { ResListSalary } from "@/modules/04_registryPerson/interface/response/Salary";
/** import SkeletonTable from "@/components/SkeletonTable.vue";
* importStore
*/
import { useCounterMixin } from "@/stores/mixin";
import DialogPreviewCommand from "@/modules/18_command/components/DialogPreviewCommand.vue"; import DialogPreviewCommand from "@/modules/18_command/components/DialogPreviewCommand.vue";
const $q = useQuasar(); const $q = useQuasar();
const { const {
date2Thai, date2Thai,
showLoader,
hideLoader,
messageError, messageError,
onSearchDataTable, onSearchDataTable,
findOrgName, findOrgName,
findOrgNameHtml, findOrgNameHtml,
} = useCounterMixin(); } = useCounterMixin();
/** /** props*/
* props
*/
const profileId = defineModel<string>("profileId", { required: true }); const profileId = defineModel<string>("profileId", { required: true });
const employeeClass = defineModel<string>("employeeClass", { required: true }); const employeeClass = defineModel<string>("employeeClass", { required: true });
/**
* Table
*/
const modalCommand = ref<boolean>(false); const modalCommand = ref<boolean>(false);
const command = ref<string>(""); const command = ref<string>("");
const commandId = ref<string>(""); const commandId = ref<string>("");
const isLoading = ref<boolean>(true);
/** Table*/
const keyword = ref<string>(""); const keyword = ref<string>("");
const rows = ref<DataPosSalary[]>([]); const rows = ref<DataPosSalary[]>([]);
const rowsData = ref<DataPosSalary[]>([]); const rowsData = ref<DataPosSalary[]>([]);
@ -343,22 +333,20 @@ const visibleColumns = ref<string[]>([
"lastUpdatedAt", "lastUpdatedAt",
]); ]);
/** /** function fetch ข้อมูลเงินเดือน / ค่าจ้าง */
* function fetch อมลเงนเดอน / าจาง
*/
function fetchListSalary() { function fetchListSalary() {
showLoader(); isLoading.value = true;
http http
.get(config.API.salaryListCard(profileId.value, employeeClass.value)) .get(config.API.salaryListCard(profileId.value, employeeClass.value))
.then((res) => { .then((res) => {
rows.value = res.data.result;
rowsData.value = res.data.result; rowsData.value = res.data.result;
onSearch();
}) })
.catch((err) => { .catch((err) => {
messageError($q, err); messageError($q, err);
}) })
.finally(() => { .finally(() => {
hideLoader(); isLoading.value = false;
}); });
} }
@ -413,7 +401,10 @@ onMounted(() => {
/> />
</div> </div>
<div class="col-12"> <div class="col-12">
<SkeletonTable v-if="isLoading" :columns="columns" />
<d-table <d-table
v-else
ref="table" ref="table"
row-key="id" row-key="id"
flat flat