เพิ่ม ฟิลเตอร์หน่วยงาน

This commit is contained in:
setthawutttty 2024-10-15 14:57:32 +07:00
parent a8f37ef058
commit 0f648e397e
3 changed files with 520 additions and 336 deletions

View file

@ -1,14 +1,19 @@
<script setup lang="ts">
import { cloneVNode, onMounted, ref } from "vue";
import type { QTableProps } from "quasar";
import { cloneVNode, onMounted, ref, reactive } from "vue";
import { useQuasar, type QTableProps } from "quasar";
import { useRoute } from "vue-router";
import http from "@/plugins/http";
import config from "@/app.config";
import { useCounterMixin } from "@/stores/mixin";
import { useDataStore } from "@/modules/03_logs/stores/main";
import { storeToRefs } from "pinia";
import { useRoute } from "vue-router";
import type {
Pagination,
ItemsDropdown,
DataTree,
QueryProfile,
} from "@/modules/03_logs/interface/index/Main";
import DialogDataDiff from "@/modules/03_logs/components/DialogDataDiff.vue";
@ -18,9 +23,10 @@ const route = useRoute();
import type { ResLog } from "@/modules/03_logs/interface/response/Main";
/** use*/
const $q = useQuasar();
const storeData = useDataStore();
const { logData, size, searchAfter, systemName } = storeToRefs(storeData);
const { date2Thai } = useCounterMixin();
const { date2Thai, messageError, hideLoader } = useCounterMixin();
const startTime = ref<Date | null>(null); //
const endTime = ref<Date | null>(null); //
@ -182,6 +188,66 @@ const itemsDropdown = ref<ItemsDropdown[]>([
]);
const statusOpt = ref<string[]>(["info", "warning", "error"]);
/******* โครงสร้าง *******/
const filter = ref<string>(""); //
const nodeTree = ref<DataTree[]>([]); //
const expanded = ref<Array<string>>([]); //
const orgId = ref<string>(""); // id
const qureyBody = reactive<QueryProfile>({
searchKeyword: "", //
searchField: "fullName", // field
page: 1, //
pageSize: 10, //
id: null, //
});
/**
* งกนเลอกหนวยงาน
* @param id หนวยงานทเลอก
*
* กำหนดคาของ qureyBody ใหเปนค defult และกำหนดคาของ qureyBody.id เปนหนวยงานทเลอก
* และดงขอมลรายชอคนททธดการโครงสรางในหนวยงานทเลอก
*/
async function selectedOrg(id: string) {
orgId.value = id;
qureyBody.id = id;
qureyBody.searchKeyword = "";
qureyBody.searchField = "fullName";
qureyBody.page = 1;
await storeData.fetchLog(
{
rootId: qureyBody.id ?? undefined,
size: size.value ?? undefined,
search: inputSearch.value ?? undefined,
systemName: systemName.value ?? undefined,
searchAfter: searchAfter.value ?? undefined,
sort: sortTime.value,
startDate: new Date(startDate.value) ?? undefined,
endDate: new Date(endDate.value) ?? undefined,
},
true
);
}
/**
* งกนดงขอมลโครงสราง
*
* เกบขอมลโครงสรางไวใน nodeTree
*/
async function fatchOrg() {
await http
.get(config.API.permissionOrg)
.then(async (res) => {
const data = await res.data.result;
nodeTree.value = data;
})
.catch((err) => {
messageError($q, err);
hideLoader();
})
.finally(() => {});
}
/**
* scroll และโหลดขอมลรายการ Logs เพ
@ -341,6 +407,7 @@ function updateDate() {
}
onMounted(() => {
fatchOrg();
systemName.value = route.query.system as string;
const date = new Date();
date.setHours(0, 0, 0, 0); // 00:00:00.000
@ -352,350 +419,421 @@ onMounted(() => {
<template>
<div class="bg-white q-pa-md">
<div class="row no-wrap">
<div class="row col-7 q-col-gutter-md">
<div>
<q-btn-dropdown outline color="grey-5">
<template v-slot:label>
<div class="row items-center no-wrap">
<div class="text-black">{{ labelDropdown }}</div>
</div>
<div class="row q-col-gutter-sm">
<div class="col-4">
<div class="q-mb-sm">
<q-input dense outlined v-model="filter" label="ค้นหา">
<template v-slot:append>
<q-icon
v-if="filter !== ''"
name="clear"
class="cursor-pointer"
@click="filter = ''"
/>
<q-icon v-else name="search" color="grey-5" />
</template>
<q-list>
</q-input>
</div>
<div class="bg-white tree-container q-pa-xs">
<q-tree
class="q-pa-sm q-gutter-sm"
dense
:nodes="nodeTree"
node-key="orgRootName"
label-key="orgRootName"
:filter="filter"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
v-model:expanded="expanded"
>
<template v-slot:default-header="prop">
<q-item
dense
v-for="(item, index) in itemsDropdown"
clickable
v-close-popup
@click="onItemClick(item.labal, item.val)"
class="row col-12 items-center text-dark q-py-xs q-pl-sm rounded-borders my-list"
:active="orgId == prop.node.id"
active-class="my-list-link text-primary text-weight-medium"
@click.stop="selectedOrg(prop.node.id)"
>
<q-item-section>
<q-item-label>{{ item.labal }}</q-item-label>
</q-item-section>
<div>
<div class="text-weight-medium">
{{ prop.node.orgRootName }}
</div>
<div class="text-weight-light text-grey-8">
{{
prop.node.orgRootCode == null
? null
: prop.node.orgRootCode
}}
{{
prop.node.orgRootShortName == null
? null
: prop.node.orgRootShortName
}}
</div>
</div>
</q-item>
</q-list>
</q-btn-dropdown>
</template>
</q-tree>
</div>
</div>
<q-space />
<div
class="items-center justify-end q-gutter-md col-5"
style="display: flex"
>
<!-- นหาขอความใน table -->
<q-input
standout
dense
clearable
v-model="inputSearch"
ref="filterRef"
@update:model-value="
storeData.fetchLog({
size: size ?? undefined,
search: inputSearch ?? undefined,
systemName: systemName ?? undefined,
sort: sortTime,
// date: new Date(startDate),
startDate: new Date(startDate),
endDate: new Date(endDate),
})
"
outlined
debounce="500"
placeholder="ค้นหา"
style="max-width: 200px"
class="q-ml-sm"
>
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>
<q-select
v-model="searchStatus"
label="ค้นหาสถานะ"
dense
emit-value
map-options
:options="statusOpt"
option-value="id"
option-label="name"
lazy-rules
hide-bottom-space
borderless
outlined
style="width: 150px"
:hide-dropdown-icon="false"
@update:modelValue="selectedDate()"
clearable
>
<template v-slot:selected-item="scope">
<q-chip
dense
:tabindex="scope.tabindex"
:color="
scope.opt === 'info'
? 'primary'
: scope.opt === 'warning'
? 'warning'
: scope.opt === 'error'
? 'red'
: 'white'
"
text-color="white"
class="q-ma-none text-caption"
style="border-radius: 3px; max-width: 60vw; word-wrap: break-word"
>
{{ scope.opt }}
</q-chip>
</template>
</q-select>
<!-- แสดงคอลมนใน table -->
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</div>
<div
v-if="valDropdown === 'customized'"
class="row q-col-gutter-md q-mt-xs"
>
<div class="col-3">
<datepicker
v-model="startTime"
:locale="'th'"
:max-date="endTime"
selectText="เลือก"
cancelText="ยกเลิก"
@update:modelValue="updateDate()"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
outlined
:model-value="
startTime ? date2Thai(startTime, false, true) : null
"
:label="`${'วันเริ่มต้น'}`"
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>
<template #action-preview="{ value }">
{{ date2Thai(value, false, true) }}
</template>
</datepicker>
</div>
<div class="col-3">
<datepicker
v-model="endTime"
:locale="'th'"
:enableTimePicker="true"
selectText="เลือก"
cancelText="ยกเลิก"
@update:modelValue="updateDate()"
:min-date="startTime"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
outlined
:model-value="endTime ? date2Thai(endTime, false, true) : null"
:label="`${'วันสิ้นสุด'}`"
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>
<template #action-preview="{ value }">
{{ date2Thai(value, false, true) }}
</template>
</datepicker>
</div>
</div>
<div
class="q-mt-md"
style="max-height: 70vh; overflow: scroll"
@scroll="infiniteScroll"
>
<d-table
ref="table"
:columns="columns"
:rows="storeData.logData"
row-key="name"
flat
bordered
hide-pagination
v-model:pagination="pagination"
dense
:rows-per-page-options="[0]"
class="custom-header-table"
:visible-columns="visibleColumns"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<div
v-if="col.name === 'startTimeStamp'"
class="text-weight-medium"
>
{{ col.label }}
<q-btn
v-if="sortTime === 'desc'"
flat
round
size="8px"
icon="mdi-arrow-up"
@click="
() => {
sortTime = 'asc';
selectedDate();
}
"
/>
<q-btn
v-else
flat
round
size="8px"
icon="mdi-arrow-down"
@click="
() => {
sortTime = 'desc';
selectedDate();
}
"
/>
</div>
<span v-else class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer" style="overflow: scroll">
<q-td v-for="col in props.cols" :key="col.id">
<div v-if="col.name === 'username'">
<template v-if="props.row.userName && props.row.user">
{{ props.row.userName ?? "-" }}
<div class="text-grey" style="font-size: 65%">
{{ props.row.user ?? "-" }}
<div class="col-8">
<div class="row no-wrap">
<div class="row col-7 q-col-gutter-md">
<div>
<q-btn-dropdown outline color="grey-5">
<template v-slot:label>
<div class="row items-center no-wrap">
<div class="text-black">{{ labelDropdown }}</div>
</div>
</template>
<template v-else> - </template>
</div>
<div v-else-if="col.name === 'method'">
<q-badge
:text-color="classColorMethod(col.value as string)"
style="background-color: #f0ecec"
>{{ col.value ?? "-" }}</q-badge
>
</div>
<div v-else-if="col.name === 'logType'">
<q-badge
text-color="white"
:color="
col.value === 'error'
? 'red'
: col.value === 'info'
? 'primary'
: 'warning'
"
>{{ col.value ?? "-" }}</q-badge
>
</div>
<div
v-else-if="col.name === 'responseDescription'"
:class="`text-${
props.row.logType === 'Error'
? 'red'
: props.row.logType === 'info'
? 'primary'
: 'warning'
}`"
class="ellipsis"
style="max-width: 10vw"
>
{{ col.value ?? "-" }}
<q-tooltip> {{ col.value }} </q-tooltip>
</div>
<q-list>
<q-item
dense
v-for="(item, index) in itemsDropdown"
clickable
v-close-popup
@click="onItemClick(item.labal, item.val)"
>
<q-item-section>
<q-item-label>{{ item.labal }}</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
</div>
<div v-else-if="col.name === 'dataDiff'">
<q-btn
<q-space />
<div
class="items-center justify-end q-gutter-md col-5"
style="display: flex"
>
<!-- นหาขอความใน table -->
<q-input
standout
dense
clearable
v-model="inputSearch"
ref="filterRef"
@update:model-value="
storeData.fetchLog({
size: size ?? undefined,
search: inputSearch ?? undefined,
systemName: systemName ?? undefined,
sort: sortTime,
// date: new Date(startDate),
startDate: new Date(startDate),
endDate: new Date(endDate),
})
"
outlined
debounce="500"
placeholder="ค้นหา"
style="max-width: 200px"
class="q-ml-sm"
>
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>
<q-select
v-model="searchStatus"
label="ค้นหาสถานะ"
dense
emit-value
map-options
:options="statusOpt"
option-value="id"
option-label="name"
lazy-rules
hide-bottom-space
borderless
outlined
style="width: 150px"
:hide-dropdown-icon="false"
@update:modelValue="selectedDate()"
clearable
>
<template v-slot:selected-item="scope">
<q-chip
dense
flat
round
icon="mdi-eye"
color="info"
@click.stop="
() => {
currentDataDiff = col.value;
currentlogData = {
startTimeStamp: props.row.startTimeStamp,
username: props.row.userName,
host: props.row.host,
endpoint: props.row.endpoint,
method: props.row.method,
responseCode: props.row.responseCode,
logType: props.row.logType,
responseDescription: props.row.responseDescription,
sequence: props.row.sequence,
dataDiff: props.row.dataDiff,
input: props.row.input,
output: props.row.output,
};
openDialog = true;
}
:tabindex="scope.tabindex"
:color="
scope.opt === 'info'
? 'primary'
: scope.opt === 'warning'
? 'warning'
: scope.opt === 'error'
? 'red'
: 'white'
"
text-color="white"
class="q-ma-none text-caption"
style="
border-radius: 3px;
max-width: 60vw;
word-wrap: break-word;
"
>
<q-tooltip>รายละเอยด </q-tooltip>
</q-btn>
</div>
{{ scope.opt }}
</q-chip>
</template>
</q-select>
<!-- แสดงคอลมนใน table -->
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</div>
<div
v-if="valDropdown === 'customized'"
class="row q-col-gutter-md q-mt-xs"
>
<div class="col-3">
<datepicker
v-model="startTime"
:locale="'th'"
:max-date="endTime"
selectText="เลือก"
cancelText="ยกเลิก"
@update:modelValue="updateDate()"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
outlined
:model-value="
startTime ? date2Thai(startTime, false, true) : null
"
:label="`${'วันเริ่มต้น'}`"
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>
<div v-else class="ellipsis" style="max-width: 10vw">
{{ col.value === "" || col.value === null ? "-" : col.value }}
<q-tooltip> {{ col.value }} </q-tooltip>
</div>
</q-td>
</q-tr>
</template>
</d-table>
<template #action-preview="{ value }">
{{ date2Thai(value, false, true) }}
</template>
</datepicker>
</div>
<div class="col-3">
<datepicker
v-model="endTime"
:locale="'th'"
:enableTimePicker="true"
selectText="เลือก"
cancelText="ยกเลิก"
@update:modelValue="updateDate()"
:min-date="startTime"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
outlined
:model-value="
endTime ? date2Thai(endTime, false, true) : null
"
:label="`${'วันสิ้นสุด'}`"
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>
<template #action-preview="{ value }">
{{ date2Thai(value, false, true) }}
</template>
</datepicker>
</div>
</div>
<div
class="q-mt-md"
style="max-height: 70vh; overflow: scroll"
@scroll="infiniteScroll"
>
<d-table
ref="table"
:columns="columns"
:rows="storeData.logData"
row-key="name"
flat
bordered
hide-pagination
v-model:pagination="pagination"
dense
:rows-per-page-options="[0]"
class="custom-header-table"
:visible-columns="visibleColumns"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<div
v-if="col.name === 'startTimeStamp'"
class="text-weight-medium"
>
{{ col.label }}
<q-btn
v-if="sortTime === 'desc'"
flat
round
size="8px"
icon="mdi-arrow-up"
@click="
() => {
sortTime = 'asc';
selectedDate();
}
"
/>
<q-btn
v-else
flat
round
size="8px"
icon="mdi-arrow-down"
@click="
() => {
sortTime = 'desc';
selectedDate();
}
"
/>
</div>
<span v-else class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr
:props="props"
class="cursor-pointer"
style="overflow: scroll"
>
<q-td v-for="col in props.cols" :key="col.id">
<div v-if="col.name === 'username'">
<template v-if="props.row.userName && props.row.user">
{{ props.row.userName ?? "-" }}
<div class="text-grey" style="font-size: 65%">
{{ props.row.user ?? "-" }}
</div>
</template>
<template v-else> - </template>
</div>
<div v-else-if="col.name === 'method'">
<q-badge
:text-color="classColorMethod(col.value as string)"
style="background-color: #f0ecec"
>{{ col.value ?? "-" }}</q-badge
>
</div>
<div v-else-if="col.name === 'logType'">
<q-badge
text-color="white"
:color="
col.value === 'error'
? 'red'
: col.value === 'info'
? 'primary'
: 'warning'
"
>{{ col.value ?? "-" }}</q-badge
>
</div>
<div
v-else-if="col.name === 'responseDescription'"
:class="`text-${
props.row.logType === 'Error'
? 'red'
: props.row.logType === 'info'
? 'primary'
: 'warning'
}`"
class="ellipsis"
style="max-width: 10vw"
>
{{ col.value ?? "-" }}
<q-tooltip> {{ col.value }} </q-tooltip>
</div>
<div v-else-if="col.name === 'dataDiff'">
<q-btn
dense
flat
round
icon="mdi-eye"
color="info"
@click.stop="
() => {
currentDataDiff = col.value;
currentlogData = {
startTimeStamp: props.row.startTimeStamp,
username: props.row.userName,
host: props.row.host,
endpoint: props.row.endpoint,
method: props.row.method,
responseCode: props.row.responseCode,
logType: props.row.logType,
responseDescription: props.row.responseDescription,
sequence: props.row.sequence,
dataDiff: props.row.dataDiff,
input: props.row.input,
output: props.row.output,
};
openDialog = true;
}
"
>
<q-tooltip>รายละเอยด </q-tooltip>
</q-btn>
</div>
<div v-else class="ellipsis" style="max-width: 10vw">
{{
col.value === "" || col.value === null ? "-" : col.value
}}
<q-tooltip> {{ col.value }} </q-tooltip>
</div>
</q-td>
</q-tr>
</template>
</d-table>
</div>
</div>
</div>
</div>
@ -868,7 +1006,7 @@ onMounted(() => {
</DialogDataDiff>
</template>
<style>
<style scoped>
.row-color:nth-child(2n + 1) {
background-color: #eeeeee;
}
@ -876,4 +1014,19 @@ onMounted(() => {
.dp__action_row {
padding: 2px !important;
}
.tree-container {
overflow: auto;
height: 74vh;
border: 1px solid #e6e6e7;
border-radius: 10px;
}
.my-list-link {
color: rgb(118, 168, 222);
border-radius: 5px;
background: #a3d3fb48 !important;
font-weight: 600;
border: 1px solid rgba(175, 185, 196, 0.217);
}
</style>

View file

@ -8,4 +8,33 @@ interface ItemsDropdown {
val: string;
}
export type { Pagination, ItemsDropdown };
interface DataTree {
ancestorDNA: string;
createdAt: string;
createdFullName: string;
createdUserId: string;
id: string;
lastUpdateFullName: string;
lastUpdateUserId: string;
lastUpdatedAt: string;
orgRevisionId: string;
orgRootCode: string;
orgRootFax: string;
orgRootName: string;
orgRootOrder: number;
orgRootPhoneEx: string;
orgRootPhoneIn: string;
orgRootRank: string;
orgRootRankSub: string;
orgRootShortName: string;
responsibility: string;
}
interface QueryProfile {
searchKeyword: string;
searchField: string;
page: number;
pageSize: number;
id: string | null;
}
export type { Pagination, ItemsDropdown,DataTree,QueryProfile };

View file

@ -36,6 +36,7 @@ export const useDataStore = defineStore("logStore", () => {
startDate?: Date;
endDate?: Date;
searchStatus?: string;
rootId?: string;
},
isBottom?: boolean
) {
@ -74,6 +75,7 @@ export const useDataStore = defineStore("logStore", () => {
});
}
return {
size,
logData,