start commit for admin system

This commit is contained in:
Warunee Tamkoo 2024-05-29 17:58:57 +07:00
commit badb676529
300 changed files with 58634 additions and 0 deletions

3
.env.example Normal file
View file

@ -0,0 +1,3 @@
VITE_REALM_KEYCLOAK: "realms-name"
VITE_CLIENTID_KEYCLOAK: "client-id"
VITE_URL_KEYCLOAK: "https://id.your-domain.com/"

30
.gitignore vendored Normal file
View file

@ -0,0 +1,30 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
package-lock.json

21
Dockerfile Normal file
View file

@ -0,0 +1,21 @@
# docker build . -t docker.frappet.com/demo/fe:latest
FROM node:lts as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY ./ .
RUN npm run build
FROM nginx as production-stage
RUN mkdir /app
COPY --from=build-stage /app/dist /app
COPY nginx.conf /etc/nginx/nginx.conf
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod u+x /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

7
README.md Normal file
View file

@ -0,0 +1,7 @@
# Human Resource Information System (HRIS)
## Install & Run Project
> npm install
> npm run dev
> npm run build

8
cypress.config.ts Normal file
View file

@ -0,0 +1,8 @@
import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
specPattern: 'cypress/e2e/**/*.{cy,spec}.{js,jsx,ts,tsx}',
baseUrl: 'http://localhost:4173'
}
})

View file

@ -0,0 +1,8 @@
// https://docs.cypress.io/api/introduction/api.html
describe('My First Test', () => {
it('visits the app root url', () => {
cy.visit('/')
cy.contains('h1', 'You did it!')
})
})

10
cypress/e2e/tsconfig.json Normal file
View file

@ -0,0 +1,10 @@
{
"extends": "@vue/tsconfig/tsconfig.web.json",
"include": ["./**/*", "../support/**/*"],
"compilerOptions": {
"isolatedModules": false,
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress"]
}
}

View file

@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

View file

@ -0,0 +1,39 @@
/// <reference types="cypress" />
// ***********************************************
// This example commands.ts shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
//
// declare global {
// namespace Cypress {
// interface Chainable {
// login(email: string, password: string): Chainable<void>
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
// }
// }
// }
export {}

20
cypress/support/e2e.ts Normal file
View file

@ -0,0 +1,20 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')

21
entrypoint.sh Normal file
View file

@ -0,0 +1,21 @@
#!/bin/sh
ROOT_DIR=/app
# Replace env vars in JavaScript files
echo "Replacing env constants in JS"
for file in $ROOT_DIR/assets/app.*.js* $ROOT_DIR/js/app.*.js* $ROOT_DIR/index.html $ROOT_DIR/precache-manifest*.js $ROOT_DIR/assets/index*.js* $ROOT_DIR/assets/*.js*;
do
echo "Processing $file ...";
sed -i 's|VITE_REALM_KEYCLOAK|'${VITE_REALM_KEYCLOAK}'|g' $file
sed -i 's|VITE_CLIENTID_KEYCLOAK|'${VITE_CLIENTID_KEYCLOAK}'|g' $file
sed -i 's|VITE_URL_KEYCLOAK|'${VITE_URL_KEYCLOAK}'|g' $file
sed -i 's|VITE_API_PUBLISH_URL|'${VITE_API_PUBLISH_URL}'|g' $file
sed -i 's|VITE_API_REPORT_URL|'${VITE_API_REPORT_URL}'|g' $file
done
echo "Starting Nginx"
nginx -g 'daemon off;'

2
env.d.ts vendored Normal file
View file

@ -0,0 +1,2 @@
/// <reference types="vite/client" />
declare module 'quasar-ui-q-draggable-table';

13
index.html Normal file
View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ระบบบริหารทรัพยากรบุคคล</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

30
nginx.conf Normal file
View file

@ -0,0 +1,30 @@
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root /app;
index index.html;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}

68
package.json Normal file
View file

@ -0,0 +1,68 @@
{
"name": "hris-admin",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "run-p build-only",
"preview": "vite preview",
"test:unit": "vitest --environment jsdom --root src/",
"test:e2e": "start-server-and-test preview :4173 'cypress run --e2e'",
"test:e2e:dev": "start-server-and-test 'vite dev --port 4173' :4173 'cypress open --e2e'",
"build-only": "vite build",
"type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
},
"dependencies": {
"@fullcalendar/core": "^6.0.1",
"@fullcalendar/daygrid": "^6.0.1",
"@fullcalendar/interaction": "^6.0.2",
"@fullcalendar/list": "^6.0.2",
"@fullcalendar/react": "^6.0.1",
"@fullcalendar/timegrid": "^6.0.2",
"@fullcalendar/vue3": "^6.0.1",
"@quasar/extras": "^1.15.8",
"@tato30/vue-pdf": "^1.5.1",
"@vuepic/vue-datepicker": "^3.6.3",
"axios": "^1.6.7",
"bma-org-chart": "^0.0.8",
"keycloak-js": "^20.0.2",
"moment": "^2.29.4",
"pinia": "^2.0.29",
"quasar": "^2.11.1",
"socket.io-client": "^4.7.4",
"structure-chart": "^0.0.9",
"vue": "^3.4.15",
"vue-currency-input": "^3.0.5",
"vue-router": "^4.1.6",
"vue3-datepicker": "^0.3.4"
},
"devDependencies": {
"@quasar/vite-plugin": "^1.3.0",
"@rushstack/eslint-patch": "^1.1.4",
"@types/jsdom": "^20.0.1",
"@types/node": "^18.11.12",
"@vitejs/plugin-vue": "^4.0.0",
"@vitejs/plugin-vue-jsx": "^3.0.0",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^11.0.0",
"@vue/test-utils": "^2.2.6",
"@vue/tsconfig": "^0.1.3",
"cypress": "^12.0.2",
"eslint": "^8.22.0",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-vue": "^9.3.0",
"jsdom": "^20.0.3",
"node-sass": "^9.0.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.7.1",
"quasar-ui-q-draggable-table": "^1.0.1",
"sass": "^1.69.4",
"start-server-and-test": "^1.15.2",
"typescript": "~4.7.4",
"vite": "^4.0.0",
"vitest": "^0.25.6",
"vue-table-to-excel": "^1.0.6",
"vue-tsc": "^1.0.12"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 MiB

15
src/App.vue Normal file
View file

@ -0,0 +1,15 @@
<script setup lang="ts">
</script>
<template>
<div id="azay-admin-app">
<router-view v-slot="{ Component }">
<transition>
<component :is="Component" />
</transition>
</router-view>
</div>
</template>
<style scoped>
</style>

View file

@ -0,0 +1,16 @@
/**
* API Structure + Org Chart
*/
import env from "../index";
const message = `${env.API_URI}/message`;
const reply = `${env.API_URI}/placement/noti`;
export default {
msgNotificate: `${message}/my-notifications`,
msgInbox: `${message}/my-inboxes`,
msgId: (id: string) => `${message}/my-notifications/${id}`,
msgNoread: () => `${message}/my-notifications/noread`,
msgInboxDelete: (id: string) => `${message}/my-inboxes/${id}`,
replyMessage: (id: string) => `${reply}/${id}`,
};

View file

@ -0,0 +1,14 @@
/**
* API Structure + Org Chart
*/
import env from "../index"
const treeRoot = `${env.API_URI}/Organization/treeroot`
const structChart = `${env.API_URI}/Organization/struct-chart/`
const orgChart = `${env.API_URI}/Organization/org-chart/`
export default {
chartGetTreeRoot: `${treeRoot}`,
chartGetStructure: (id: string) => `${structChart}${id}`,
chartGetOrg: (id: string) => `${orgChart}${id}`
}

View file

@ -0,0 +1,92 @@
import env from "../index";
const organization = `${env.API_URI}/org`;
const orgPos = `${env.API_URI}/org/pos`;
const orgProfile = `${env.API_URI}/org/profile`;
const orgEmployeePos = `${env.API_URI}/org/employee/pos`;
export default {
/** โครงสร้างอัตรากำลัง*/
activeOrganization: `${organization}/active`,
orgByid: (id: string) => `${organization}/${id}`,
createOrganization: `${organization}/draft`,
createOrgLevel: (type: string) => `${organization}/${type}`,
orgLevelByid: (type: string, id: string) => `${organization}/${type}/${id}`,
orgSetDateTime: (id: string) => `${organization}/set/publish/${id}`,
organizationHistoryNew: `${organization}/history`,
organizationHistoryPostNew: `${organization}/history/publish`,
/** position*/
orgPosPosition: `${orgPos}/position`,
orgPosPositionById: (id: string) => `${orgPos}/position/${id}`,
orgPosPositionExecutive : () => `${orgPos}/position/executive`,
orgPosExecutive: `${orgPos}/executive`,
orgPosType: `${orgPos}/type`,
orgPosTypeId: (id: string) => `${orgPos}/type/${id}`,
orgPosLevel: `${orgPos}/level`,
orgPosMaster: `${orgPos}/master`,
orgPosMasterById: (id: string) => `${orgPos}/master/${id}`,
orgPosMasterList: `${orgPos}/master/list`,
orgPosSort: `${orgPos}/sort`,
orgPosMove: `${orgPos}/move`,
organizationShortName: `${organization}/sort`,
organizationPublishGet: `${organization}/get/publish`,
orgPosDNA: `${orgPos}/dna`, //สืบทอดตำแหน่ง
orgPosExecutiveById: (id: string) => `${orgPos}/executive/${id}`,
orgPosHistory: (id: string) => `${orgPos}/history/${id}`,
orgSalaryPosition: `${orgPos}/position?keyword=&type=ALL`,
/**ครองตำแหน่ง */
orgSearchProfile: `${orgProfile}/search`,
orgProfile: `${orgPos}/profile`,
orgDeleteProfile: (id: string) => `${orgPos}/profile/delete/${id}`,
orgSummary: `${orgPos}/summary`,
/** report*/
orgReport: (report: string) => `${organization}/report/${report}`,
/** struct-chart*/
orgStructChart: (id: string, type: string) =>
`${organization}/struct-chart/${id}/${type}`,
// ค้นหาคนตามเงื่อนไข
orgSearchPersonal: () => `${organization}/profile/search-personal`,
/** บรรจุแต่งตั้ง*/
orgPosPlacement: `${orgPos}/placement/search`,
orgPosPlacemenTemp: `${orgPos}/placementemp/search`,
orgPosFind: `${organization}/find/node`,
orgProfileProbation: `${organization}/profile/probation`,
activeOrganizationRoot: `${organization}/active/root`,
activeOrganizationRootById: (id: string) =>
`${organization}/active/root/${id}`,
/** ข้อมูลตำแหน่งลูกจ้างประจำ*/
orgEmployeePos: `${orgEmployeePos}/position`,
orgEmployeePosById: (id: string) => `${orgEmployeePos}/position/${id}`,
orgEmployeeType: `${orgEmployeePos}/type`,
orgEmployeeTypeById: (id: string) => `${orgEmployeePos}/type/${id}`,
orgEmployeelevel: `${orgEmployeePos}/level`,
orgEmployeelevelById: (id: string) => `${orgEmployeePos}/level/${id}`,
/** อัตรากำลังลูกจ้างประจำ*/
orgSummaryEmp: `${orgEmployeePos}/summary`,
orgReportEmp: (report: string) => `${orgEmployeePos}/report/${report}`,
orgDeleteProfileEmp: (id: string) => `${orgEmployeePos}/profile/delete/${id}`,
orgPosMasterByIdEmp: (id: string) => `${orgEmployeePos}/master/${id}`,
orgPosMasterListEmp: `${orgEmployeePos}/master/list`,
orgPosMasterEmp: `${orgEmployeePos}/master`,
orgPosSortEmp: `${orgEmployeePos}/sort`,
orgPosPositionEmpById: (id: string) => `${orgEmployeePos}/position/${id}`,
orgPosMoveEmp: `${orgEmployeePos}/move`,
orgProfileEmp: `${orgEmployeePos}/profile`,
orgSearchProfileEmp: `${orgProfile}-employee/search`,
orgProfileById:(id:string,type:string)=>`${orgProfile}${type}/${id}`,
orgDeceasedProfile:`${orgPos}/profile/search`
};

View file

@ -0,0 +1,94 @@
/**
* api Tree
*
*/
import env from "../index";
//Dropdown เพิ่มโครงร้างหน่วยงาน
// const organizationAgency = `${env.API_URI_ORG_SERVICE}/organization-agency/`;
// const organizationGovernmentAgency = `${env.API_URI_ORG_SERVICE}/organization-government-agency/`;
// const organizationPath = `${env.API_URI}/Organization/tree`;
const organizationPath = `${env.API_URI_ORG_SERVICE}/Organization/`;
const postionMasterPath = `${env.API_URI_ORG_SERVICE}/PositionMaster`;
const organizationPosition = `${env.API_URI_ORG_SERVICE}/OrganizationPosition/`;
const organizationEmployee = `${env.API_URI_ORG_EMPLOYEE_SERVICE}/organization-employee/`;
const report = `${env.API_URI}/report/organization/`;
export default {
getOCType: `${report}oc-type`,
getReportAccount1: (id: string) => `${report}account1/${id}`,
getReportAccount2: (id: string) => `${report}account2/${id}`,
getReportAccount3: (id: string) => `${report}account3/${id}`,
/**api /
* @param organizationType
* @returns organizationId and organizationName
*/
listOrganizationAgency: (organizationType: string) =>
`${organizationPath}type/${organizationType}`,
listOrganizationHistoryAgency: (organizationType: string) =>
`${organizationPath}history/type/${organizationType}`,
/**api Tree
*
*/
// treeList: `${organizationPath}`,
getTreeRoot: `${organizationPath}treeroot`,
getDraftTreeRoot: `${organizationPath}history/treeroot`,
getTreeHistory: `${organizationPath}publish-history`,
getPublishFileHistory: `${organizationPath}publish-file`,
getTreeNode: (organizationId: string, keyId: string) =>
`${organizationPath}tree?organizationId=${organizationId}&keyId=${keyId}`,
getDraftTreeNode: (organizationId: string, keyId: string) =>
`${organizationPath}history/tree?organizationId=${organizationId}&keyId=${keyId}`,
addTreeDraft: `${organizationPosition}history`,
editTreeOrgDraft: (organizationId: string) =>
`${organizationPath}history/${organizationId}`,
getOrgDraft: (organizationId: string) =>
`${organizationPath}history/${organizationId}`,
// /api/v1/Organization/history?organizationPositionId=65ab2ddc-b9d2-4422-83d4-b90dd8191b6f
delTreeOrgDraft: (organizationId: string) =>
`${organizationPath}history?organizationPositionId=${organizationId}`,
getPostionMasterDraft: (showAll: boolean) =>
`${postionMasterPath}/history?showall=${showAll}`,
getPostionMaster: (showAll: boolean) =>
`${postionMasterPath}?showall=${showAll}`,
editTreePositionDraft: (organizationPositionId: string) =>
`${organizationPosition}history?organizationPositionId=${organizationPositionId}`,
// /api/v1/OrganizationPosition/history?organizationPositionId=15ec8c2b-796f-4fad-b731-667a232e723e
delTreePositionDraft: (organizationPositionId: string) =>
`${organizationPosition}history/${organizationPositionId}`,
//PUT /api/v1/OrganizationPosition/history/publish
publishOrgPoDraft: `${organizationPosition}history/publish`,
//PUT /api/v1/OrganizationPosition/history/sync
delTreeOrgPoDraft: `${organizationPosition}history/sync`,
isOrgPohasDraft: `${organizationPosition}history/has-draft`,
isOrghasDraft: `${organizationPath}history/has-draft`,
/**
* api
*/
getPositionMaster: `${postionMasterPath}/history`,
getPositionMasterId: (id: string) => `${postionMasterPath}/history/${id}`,
getPositionMasterPublish: `${postionMasterPath}/history/publish`,
getPositionMasterSync: `${postionMasterPath}/history/sync`,
getPositionMasterHistoryId: (id: string) =>
`${postionMasterPath}/history/publish/${id}`,
getPositionMasterPositionNumber: `${postionMasterPath}/position-number`,
getPositionMasterPositionNumberId: (id: string) =>
`${postionMasterPath}/position-number/${id}`,
getPositionNumberIdByOcId: (OcId: string) =>
`${postionMasterPath}/position-number/Oc/${OcId}`,
organizationEmployee: `${organizationEmployee}`,
organizationEmployeeId: (id: string) => `${organizationEmployee}${id}`,
organizationEmployeePositionId: (profileId: string) =>
`${organizationEmployee}position/${profileId}`,
organizationEmployeeSendOrder: `${organizationEmployee}report`,
};

23
src/api/file/api.file.ts Normal file
View file

@ -0,0 +1,23 @@
import env from "../index";
const url = `${env.API_URI}/salary`;
export default {
subFile: (name: string, group: string, id: string, subId: string) =>
`${url}/sub-file/${name}/${group}/${id}/${subId}`,
subFileByFileName: (
name: string,
group: string,
id: string,
subId: string,
fileName: string
) => `${url}/sub-file/${name}/${group}/${id}/${subId}/${fileName}`,
file: (name: string, group: string, id: string) =>
`${url}/file/${name}/${group}/${id}`,
fileByPath: (name: string) =>
`${url}/file/${name}`,
fileByFile: (name: string, group: string, id: string, fileName: string) =>
`${url}/file/${name}/${group}/${id}/${fileName}`,
};

70
src/api/index.ts Normal file
View file

@ -0,0 +1,70 @@
/**config api */
import { ref } from "vue";
const env = ref<string>(process.env.NODE_ENV || "development");
export const apiUrlConfigPublish = import.meta.env.VITE_API_PUBLISH_URL;
export const apiUrlConfigReport = import.meta.env.VITE_API_REPORT_URL;
// if (process.env.VUE_APP_TEST) {
// env = "test";
// }
const config = ref<any>({
development: {
API_URI: "https://bma-ehr.frappet.synology.me/api/v1",
API_URI_ORG_SERVICE: "https://bma-ehr.frappet.synology.me/api/v1", //ใช้ชั่วคราว
API_URI_ORG_EMPLOYEE_SERVICE: "https://bma-ehr.frappet.synology.me/api/v1", //ใช้ชั่วคราว
API_URI_PROFILE_SERVICE: "https://bma-ehr.frappet.synology.me/api/v1", //ใช้ชั่วคราว
API_REPORT_URI: "https://bma-ehr.frappet.synology.me/api/v1",
MEET_URI: "meet.frappet.com",
API_REPORT2_URI: "https://bma-ehr.frappet.synology.me/api/v2",
API_REPORT_TEMPLATE_URI:
"https://report-server.frappet.synology.me/api/v1/report-template",
},
test: {
API_URI: "http://localhost:5010/api/v1",
API_CANDIDATE_URI: "https://localhost:7007/api/v1",
API_REPORT_URI: "https://localhost:7007/api/v1",
MEET_URI: "meet.frappet.com",
},
production: {
// API_URI: "https://localhost:5010",
API_URI: `${window.location.protocol}//${window.location.host}/api/v1`,
API_URI_ORG_SERVICE: `${window.location.protocol}//${window.location.host}/api/v1`, //ใช้ชั่วคราว
API_URI_ORG_EMPLOYEE_SERVICE: `${window.location.protocol}//${window.location.host}/api/v1`, //ใช้ชั่วคราว
API_URI_PROFILE_SERVICE: `${window.location.protocol}//${window.location.host}/api/v1`,
API_REPORT_URI: `${window.location.protocol}//${window.location.host}/api/v1`,
MEET_URI: "meet.frappet.com",
API_REPORT2_URI: `${window.location.protocol}//${window.location.host}/api/v2`,
API_REPORT_TEMPLATE_URI: apiUrlConfigReport,
},
});
const API_URI = ref<string>(config.value[env.value].API_URI);
const API_REPORT_URI = ref<string>(config.value[env.value].API_REPORT_URI);
const API_URI_ORG_SERVICE = ref<string>(
config.value[env.value].API_URI_ORG_SERVICE
);
const API_URI_ORG_EMPLOYEE_SERVICE = ref<string>(
config.value[env.value].API_URI_ORG_EMPLOYEE_SERVICE
);
const MEET_URI = ref<string>(config.value[env.value].MEET_URI);
const API_URI_PROFILE_SERVICE = ref<string>(
config.value[env.value].API_URI_PROFILE_SERVICE
);
const API_REPORT2_URI = ref<string>(config.value[env.value].API_REPORT2_URI);
const API_REPORT_TEMPLATE_URI = ref<string>(
config.value[env.value].API_REPORT_TEMPLATE_URI
);
export default {
env: env.value,
config: config.value,
API_URI: API_URI.value,
API_REPORT_URI: API_REPORT_URI.value,
API_URI_ORG_SERVICE: API_URI_ORG_SERVICE.value,
API_URI_ORG_EMPLOYEE_SERVICE: API_URI_ORG_EMPLOYEE_SERVICE.value,
API_URI_PROFILE_SERVICE: API_URI_PROFILE_SERVICE.value,
MEET_URI: MEET_URI.value,
API_REPORT2_URI: API_REPORT2_URI.value,
API_REPORT_TEMPLATE_URI: API_REPORT_TEMPLATE_URI.value,
};

View file

@ -0,0 +1,23 @@
/**
* api
* เมนูย่อย: ปฏิทินวันหยุด
*/
import env from "../index";
const holiday = `${env.API_URI}/metadata/holiday/`;
export default {
/**
* api
*/
listHolidayHistoryAdd: (category: string) =>
`${holiday}range/add/${category}`,
listHolidayHistoryEdit: (category: string) =>
`${holiday}range/edit/${category}`,
listHolidayHistoryDelete: (category: string) =>
`${holiday}range/delete/${category}`,
listHolidayHistoryYear: (year: number) => `${holiday}${year}`,
listHolidayHistoryYearMonth: (year: number, month: number) =>
`${holiday}${year}/${month}`,
listHolidayCopy: `${holiday}copy`,
summaryHolidayHistoryYear: (year: number) => `${holiday}summary/${year}`,
};

View file

@ -0,0 +1,48 @@
/**
* api
* เมนูย่อย: ข้อมูลเครื่องราชอิสริยาภรณ์
*/
import env from "../index";
const insignia = `${env.API_URI}/metadata/insignia/`;
const insigniaType = `${env.API_URI}/metadata/insignia-type/`;
const insigniaOrg = `${env.API_URI}/org/insignia/insignia/`;
const insigniaTypeOrg = `${env.API_URI}/org/insignia/insignia-type/`;
export default {
/**
* api
*/
insignia,
listInsigniaHistory: `${insignia}history`,
listInsigniaHistoryId: (id: string) => `${insignia}history/${id}`,
listInsigniaPublished: `${insignia}history/published`,
listInsigniaPublishedHistory: `${insignia}history/published-history`,
/**
* api
*/
insigniaType,
listInsigniaTypeHistory: `${insigniaType}history`,
listInsigniaTypeHistoryId: (id: string) => `${insigniaType}history/${id}`,
listInsigniaTypePublished: `${insigniaType}history/published`,
listInsigniaTypePublishedHistory: `${insigniaType}history/published-history`,
/**
* api
*/
// listInsigniaHistory: `${insignia}history`,
// listInsigniaHistoryId: (id: string) => `${insignia}history/${id}`,
// listInsigniaPublished: `${insignia}history/published`,
// listInsigniaPublishedHistory: `${insignia}history/published-history`,
insigniaTypeNew: `${insigniaType}draft`,
insigniaTypeNewId: (id: string) => `${insigniaType}${id}`,
insigniaNewId: (id: string) => `${insignia}${id}`,
insigniaSort: (id: string) => `${insignia}sort/${id}`,
insigniaOrg,
insigniaTypeOrg,
insigniaTypeNewOrg: `${insigniaOrg}`,
insigniaTypeNewIdOrg: (id: string) => `${insigniaTypeOrg}${id}`,
insigniaNewIdOrg: (id: string) => `${insigniaOrg}${id}`,
insigniaSortOrg: (id: string) => `${insigniaOrg}sort/${id}`,
};

View file

@ -0,0 +1,133 @@
/**
* api
* เมนูย่อย: ข้อมูลโครงสร้างหน่วยงาน
*/
import env from "../index";
const organizationMain = `${env.API_URI}/metadata/main/`;
const organization = `${env.API_URI}/metadata/organization/`;
const organizationOrganization = `${env.API_URI}/metadata/organization-organization/`;
const organizationShortName = `${env.API_URI}/metadata/organization-shortname/`;
const organizationCode = `${env.API_URI_ORG_SERVICE}/PositionMaster/`;
const organizationType = `${env.API_URI}/metadata/organization-type/`;
const organizationLevel = `${env.API_URI}/metadata/organization-level/`;
const organizationStatus = `${env.API_URI}/metadata/organization-status/`;
const organizationAgency = `${env.API_URI}/metadata/organization-agency/`;
const organizationGovernmentAgency = `${env.API_URI}/metadata/organization-government-agency/`;
const organizationTelExternal = `${env.API_URI}/metadata/organization-tel-external/`;
const organizationTelInternal = `${env.API_URI}/metadata/organization-tel-internal/`;
const organizationFax = `${env.API_URI}/metadata/organization-fax/`;
const dashbord = `${env.API_URI}/metadata/dashbord/`;
export default {
/**
* api tab tab
*/
organizationMain: `${organizationMain}organization`,
organization,
countDashbordSubHistory: (type: number) => `${dashbord}${type}`,
countDashbordHistory: `${dashbord}`,
/**
* api
*/
organizationOrganization,
listOrganizationOrganizationHistory: `${organizationOrganization}history`,
listOrganizationOrganizationHistoryId: (id: string) =>
`${organizationOrganization}history/${id}`,
listOrganizationOrganizationPublished: `${organizationOrganization}history/published`,
listOrganizationOrganizationPublishedHistory: `${organizationOrganization}history/published-history`,
/**
* api
*/
organizationShortName,
listOrganizationShortNameHistory: `${organizationShortName}history`,
listOrganizationShortNameHistoryId: (id: string) =>
`${organizationShortName}history/${id}`,
listOrganizationShortNamePublished: `${organizationShortName}history/published`,
listOrganizationShortNamePublishedHistory: `${organizationShortName}history/published-history`,
organizationCode: `${organizationCode}organization-code`,
organizationGovernmentCode: (id: string) =>
`${organizationCode}government/${id}`,
organizationAgencyCode: (id: string) => `${organizationCode}agency/${id}`,
/**
* api
*/
organizationType,
listOrganizationTypeHistory: `${organizationType}history`,
listOrganizationTypeHistoryId: (id: string) =>
`${organizationType}history/${id}`,
listOrganizationTypePublished: `${organizationType}history/published`,
listOrganizationTypePublishedHistory: `${organizationType}history/published-history`,
/**
* api
*/
organizationLevel,
listOrganizationLevelHistory: `${organizationLevel}history`,
listOrganizationLevelHistoryId: (id: string) =>
`${organizationLevel}history/${id}`,
listOrganizationLevelPublished: `${organizationLevel}history/published`,
listOrganizationLevelPublishedHistory: `${organizationLevel}history/published-history`,
/**
* api
*/
organizationStatus,
listOrganizationStatusHistory: `${organizationStatus}history`,
listOrganizationStatusHistoryId: (id: string) =>
`${organizationStatus}history/${id}`,
listOrganizationStatusPublished: `${organizationStatus}history/published`,
listOrganizationStatusPublishedHistory: `${organizationStatus}history/published-history`,
/**
* api
*/
organizationAgency,
listOrganizationAgencyHistory: `${organizationAgency}history`,
listOrganizationAgencyHistoryId: (id: string) =>
`${organizationAgency}history/${id}`,
listOrganizationAgencyPublished: `${organizationAgency}history/published`,
listOrganizationAgencyPublishedHistory: `${organizationAgency}history/published-history`,
/**
* api
*/
organizationGovernmentAgency,
listOrganizationGovernmentAgencyHistory: `${organizationGovernmentAgency}history`,
listOrganizationGovernmentAgencyHistoryId: (id: string) =>
`${organizationGovernmentAgency}history/${id}`,
listOrganizationGovernmentAgencyPublished: `${organizationGovernmentAgency}history/published`,
listOrganizationGovernmentAgencyPublishedHistory: `${organizationGovernmentAgency}history/published-history`,
/**
* api
*/
organizationTelExternal,
listOrganizationTelExternalHistory: `${organizationTelExternal}history`,
listOrganizationTelExternalHistoryId: (id: string) =>
`${organizationTelExternal}history/${id}`,
listOrganizationTelExternalPublished: `${organizationTelExternal}history/published`,
listOrganizationTelExternalPublishedHistory: `${organizationTelExternal}history/published-history`,
/**
* api
*/
organizationTelInternal,
listOrganizationTelInternalHistory: `${organizationTelInternal}history`,
listOrganizationTelInternalHistoryId: (id: string) =>
`${organizationTelInternal}history/${id}`,
listOrganizationTelInternalPublished: `${organizationTelInternal}history/published`,
listOrganizationTelInternalPublishedHistory: `${organizationTelInternal}history/published-history`,
/**
* api
*/
organizationFax,
listOrganizationFaxHistory: `${organizationFax}history`,
listOrganizationFaxHistoryId: (id: string) =>
`${organizationFax}history/${id}`,
listOrganizationFaxPublished: `${organizationFax}history/published`,
listOrganizationFaxPublishedHistory: `${organizationFax}history/published-history`,
};

View file

@ -0,0 +1,140 @@
/**
* api
* เมนูย่อย: ข้อมูลเกี่ยวกับบุคคล
*/
import env from "../index";
const person = `${env.API_URI}/metadata/main/`;
const province = `${env.API_URI}/metadata/province/`;
const district = `${env.API_URI}/metadata/district/`;
const subDistrict = `${env.API_URI}/metadata/sub-district/`;
const prefix = `${env.API_URI}/metadata/prefix/`;
const bloodGroup = `${env.API_URI}/metadata/blood-group/`;
const educationLevel = `${env.API_URI}/metadata/education-level/`;
const gender = `${env.API_URI}/metadata/gender/`;
const relationship = `${env.API_URI}/metadata/relationship/`;
const religion = `${env.API_URI}/metadata/religion/`;
const orgRelationship = `${env.API_URI}/org/metadata/relationship/`;
const orgPrefix = `${env.API_URI}/org/metadata/prefix/`;
const orgRank = `${env.API_URI}/org/metadata/rank/`;
const orgBloodGroup = `${env.API_URI}/org/metadata/bloodGroup/`;
const orgGender = `${env.API_URI}/org/metadata/gender/`;
const orgReligion = `${env.API_URI}/org/metadata/religion/`;
const orgEducationLevel = `${env.API_URI}/org/metadata/educationLevel/`;
const orgProvince = `${env.API_URI}/org/metadata/province`;
const orgDistrict = `${env.API_URI}/org/metadata/district`;
const orgSubDistrict = `${env.API_URI}/org/metadata/subDistrict`;
export default {
person: `${person}person`,
/**
* api
*/
province,
listProvinceHistory: `${province}history`,
listProvinceHistoryId: (id: string) => `${province}history/${id}`,
listProvincePublished: `${province}history/published`,
listProvincePublishedHistory: `${province}history/published-history`,
/**
* api
*/
listDistrict: (id: string) => `${district}${id}`,
listDistrictHistory: `${district}history`,
listDistrictHistoryId: (id: string | string[]) => `${district}history/${id}`,
listDistrictPublishedId: (id: string) => `${district}history/published/${id}`,
listDistrictPublishedHistory: (id: string | string[]) =>
`${district}history/published-history/${id}`,
/**
* api
*/
listSubDistrict: (id: string) => `${subDistrict}${id}`,
listSubDistrictHistory: `${subDistrict}history`,
listSubDistrictHistoryProvinceId: (
provinceId: string | string[],
district: string | string[]
) => `${subDistrict}history/${provinceId}/${district}`,
listSubDistrictHistoryId: (id: string | string[]) =>
`${subDistrict}history/${id}`,
listSubDistrictPublishedId: (id: string) =>
`${subDistrict}history/published/${id}`,
listSubDistrictPublishedHistory: (id: string | string[]) =>
`${subDistrict}history/published-history/${id}`,
/**
* api
*/
prefix,
listPrefixHistory: `${prefix}history`,
listPrefixHistoryId: (id: string) => `${prefix}history/${id}`,
listPrefixPublished: `${prefix}history/published`,
listPrefixPublishedHistory: `${prefix}history/published-history`,
/**
* api
*/
gender,
listGenderHistory: `${gender}history`,
listGenderHistoryId: (id: string) => `${gender}history/${id}`,
listGenderPublished: `${gender}history/published`,
listGenderPublishedHistory: `${gender}history/published-history`,
/**
* api
*/
relationship,
listRelationshipHistory: `${relationship}history`,
listRelationshipHistoryId: (id: string) => `${relationship}history/${id}`,
listRelationshipPublished: `${relationship}history/published`,
listRelationshipPublishedHistory: `${relationship}history/published-history`,
/**
* api
*/
bloodGroup,
listBloodGroupHistory: `${bloodGroup}history`,
listBloodGroupHistoryId: (id: string) => `${bloodGroup}history/${id}`,
listBloodGroupPublished: `${bloodGroup}history/published`,
listBloodGroupPublishedHistory: `${bloodGroup}history/published-history`,
/**
* api
*/
religion,
listReligionHistory: `${religion}history`,
listReligionHistoryId: (id: string) => `${religion}history/${id}`,
listReligionPublished: `${religion}history/published`,
listReligionPublishedHistory: `${religion}history/published-history`,
/**
* api
*/
educationLevel,
listEducationLevelHistory: `${educationLevel}history`,
listEducationLevelHistoryId: (id: string) => `${educationLevel}history/${id}`,
listEducationLevelPublished: `${educationLevel}history/published`,
listEducationLevelPublishedHistory: `${educationLevel}history/published-history`,
// API ใหม่
orgRelationship,
orgRelationshipId: (id: string) => `${orgRelationship}${id}`,
orgPrefix,
orgPrefixId: (id: string) => `${orgPrefix}${id}`,
orgRank,
orgRankId: (id: string) => `${orgRank}${id}`,
orgBloodGroup,
orgBloodGroupId: (id: string) => `${orgBloodGroup}${id}`,
orgGender,
orgGenderId: (id: string) => `${orgGender}${id}`,
orgReligion,
orgReligionId: (id: string) => `${orgReligion}${id}`,
orgEducationLevel,
orgEducationLevelId: (id: string) => `${orgEducationLevel}${id}`,
orgProvince,
orgDistrict,
orgSubDistrict,
};

View file

@ -0,0 +1,105 @@
/**
* api
* เมนูย่อย: ข้อมูลตำแหน่งของข้าราชการกรุงเทพมหานคร
*/
import env from "../index";
const position = `${env.API_URI}/metadata/main/`;
const positionExecutiveSide = `${env.API_URI}/metadata/position-executive-side/`;
const positionLevel = `${env.API_URI}/metadata/position-level/`;
const positionPath = `${env.API_URI}/metadata/position-path/`;
const positionSide = `${env.API_URI}/metadata/position-path-side/`;
const positionType = `${env.API_URI}/metadata/position-type/`;
const positionStatus = `${env.API_URI}/metadata/position-status/`;
const positionLine = `${env.API_URI}/metadata/position-line/`;
const positionExecutive = `${env.API_URI}/metadata/position-executive/`;
const orgPosType = `${env.API_URI}/org/pos/type/`;
const orgPosLevel = `${env.API_URI}/org/pos/level/`;
export default {
position: `${position}position`,
/**
* api
*/
positionExecutiveSide,
listPositionExecutiveSideHistory: `${positionExecutiveSide}history`,
listPositionExecutiveSideHistoryId: (id: string) =>
`${positionExecutiveSide}history/${id}`,
listPositionExecutiveSidePublished: `${positionExecutiveSide}history/published`,
listPositionExecutiveSidePublishedHistory: `${positionExecutiveSide}history/published-history`,
/**
* api
*/
// listPositionHistory: `${position}history`,
// listPositionHistoryId: (id: string) => `${position}history/${id}`,
// listPositionPublished: `${position}history/published`,
/**
* api
*/
positionLevel,
listPositionLevelHistory: `${positionLevel}history`,
listPositionLevelHistoryId: (id: string) => `${positionLevel}history/${id}`,
listPositionLevelPublished: `${positionLevel}history/published`,
listPositionLevelPublishedHistory: `${positionLevel}history/published-history`,
/**
* api
*/
positionPath,
listPositionPathHistory: `${positionPath}history`,
listPositionPathHistoryId: (id: string) => `${positionPath}history/${id}`,
listPositionPathPublished: `${positionPath}history/published`,
listPositionPathPublishedHistory: `${positionPath}history/published-history`,
/**
* api /
*/
positionSide,
listPositionSideHistory: `${positionSide}history`,
listPositionSideHistoryId: (id: string) => `${positionSide}history/${id}`,
listPositionSidePublished: `${positionSide}history/published`,
listPositionSidePublishedHistory: `${positionSide}history/published-history`,
/**
* api
*/
positionType,
listPositionTypeHistory: `${positionType}history`,
listPositionTypeHistoryId: (id: string) => `${positionType}history/${id}`,
listPositionTypePublished: `${positionType}history/published`,
listPositionTypePublishedHistory: `${positionType}history/published-history`,
/**
* api
*/
positionStatus,
listPositionStatusHistory: `${positionStatus}history`,
listPositionStatusHistoryId: (id: string) => `${positionStatus}history/${id}`,
listPositionStatusPublished: `${positionStatus}history/published`,
listPositionStatusPublishedHistory: `${positionStatus}history/published-history`,
/**
* api
*/
positionLine,
listPositionLineHistory: `${positionLine}history`,
listPositionLineHistoryId: (id: string) => `${positionLine}history/${id}`,
listPositionLinePublished: `${positionLine}history/published`,
listPositionLinePublishedHistory: `${positionLine}history/published-history`,
/**
* api
*/
positionExecutive,
listPositionExecutiveHistory: `${positionExecutive}history`,
listPositionExecutiveHistoryId: (id: string) =>
`${positionExecutive}history/${id}`,
listPositionExecutivePublished: `${positionExecutive}history/published`,
listPositionExecutivePublishedHistory: `${positionExecutive}history/published-history`,
orgPosType,
orgPosTypeId: (id: string) => `${orgPosType}${id}`,
orgPosLevel,
orgPosLevelId: (id: string) => `${orgPosLevel}${id}`,
};

View file

@ -0,0 +1,75 @@
/**
* api
* เมนูย่อย: ข้อมูลตำแหน่งของลูกจ้างกรุงเทพมหานคร
*/
import env from "../index";
const positionEmployee = `${env.API_URI}/metadata/main/`;
const positionEmployeePosition = `${env.API_URI}/metadata/position-employee-position/`;
const positionEmployeePositionSide = `${env.API_URI}/metadata/position-employee-position-side/`;
const positionEmployeeGroup = `${env.API_URI}/metadata/position-employee-group/`;
const positionEmployeeLine = `${env.API_URI}/metadata/position-employee-line/`;
const positionEmployeeLevel = `${env.API_URI}/metadata/position-employee-level/`;
const positionEmployeeStatus = `${env.API_URI}/metadata/position-employee-status/`;
export default {
positionEmployeeStatus: `${positionEmployeeStatus}`,
positionEmployee: `${positionEmployee}positionEmployee`,
/**
* api
*/
positionEmployeePosition,
listPositionEmployeePositionHistory: `${positionEmployeePosition}history`,
listPositionEmployeePositionHistoryId: (id: string) =>
`${positionEmployeePosition}history/${id}`,
listPositionEmployeePositionPublished: `${positionEmployeePosition}history/published`,
listPositionEmployeePositionPublishedHistory: `${positionEmployeePosition}history/published-history`,
/**
* api
*/
positionEmployeePositionSide,
listPositionEmployeePositionSideHistory: `${positionEmployeePositionSide}history`,
listPositionEmployeePositionSideHistoryId: (id: string) =>
`${positionEmployeePositionSide}history/${id}`,
listPositionEmployeePositionSidePublished: `${positionEmployeePositionSide}history/published`,
listPositionEmployeePositionSidePublishedHistory: `${positionEmployeePositionSide}history/published-history`,
/**
* api
*/
positionEmployeeGroup,
listPositionEmployeeGroupHistory: `${positionEmployeeGroup}history`,
listPositionEmployeeGroupHistoryId: (id: string) =>
`${positionEmployeeGroup}history/${id}`,
listPositionEmployeeGroupPublished: `${positionEmployeeGroup}history/published`,
listPositionEmployeeGroupPublishedHistory: `${positionEmployeeGroup}history/published-history`,
/**
* api
*/
positionEmployeeLine,
listPositionEmployeeLineHistory: `${positionEmployeeLine}history`,
listPositionEmployeeLineHistoryId: (id: string) =>
`${positionEmployeeLine}history/${id}`,
listPositionEmployeeLinePublished: `${positionEmployeeLine}history/published`,
listPositionEmployeeLinePublishedHistory: `${positionEmployeeLine}history/published-history`,
/**
* api
*/
positionEmployeeLevel,
listPositionEmployeeLevelHistory: `${positionEmployeeLevel}history`,
listPositionEmployeeLevelHistoryId: (id: string) =>
`${positionEmployeeLevel}history/${id}`,
listPositionEmployeeLevelPublished: `${positionEmployeeLevel}history/published`,
listPositionEmployeeLevelPublishedHistory: `${positionEmployeeLevel}history/published-history`,
/**
* api
*/
listPositionEmployeeStatusHistory: `${positionEmployeeStatus}history`,
listPositionEmployeeStatusHistoryId: (id: string) =>
`${positionEmployeeStatus}history/${id}`,
listPositionEmployeeStatusPublished: `${positionEmployeeStatus}history/published`,
listPositionEmployeeStatusPublishedHistory: `${positionEmployeeStatus}history/published-history`,
};

View file

@ -0,0 +1,219 @@
/**
* api -
*/
import env from "../index";
const profile = `${env.API_URI_PROFILE_SERVICE}/profile/`;
const report = `${env.API_REPORT_URI}/report/profile/`;
const organizationRoot = `${env.API_URI}/profile/organization/list/root`;
const registryNew = `${env.API_URI}/org/profile/`;
export default {
/**
* api
*/
organizationRoot,
profileUser: `${profile}user`,
profileValidateId: (profileId: string) => `${profile}validate/${profileId}`,
profilePaperDownloadId: (id: string) => `${profile}paper/download/${id}`,
profilePaperId: (id: string) => `${profile}paper/${id}`,
profileMultiId: (id: string) => `${profile}multi-paper/${id}`,
profileMultiOId: (id: string) => `${profile}multi-paper-other/${id}`,
profileMultiKId: (id: string) => `${profile}multi-paper-kk1/${id}`,
profileHistoryselfId: (profileId: string) =>
`${profile}/historyself${profileId}`,
profileOrganiNameId: (id: string) => `${profile}organization/name/${id}`,
profileOrganiId: (id: string) => `${profile}organization/${id}`,
profileOrganiSelectId: (id: string) => `${profile}organization/select/${id}`,
profileOrganiRootId: (id: string) => `${profile}organization/root/${id}`,
profileLeaveOrganiId: (id: string) => `${profile}leave/organization/${id}`,
profileSearchNewOcId: (id: string) => `${profile}search/new/oc/${id}`,
profileSearchNewOcIdType: (id: string, type: string) =>
`${profile}search/new/oc/${id}/${type}`,
profileSearchId: (id: string) => `${profile}search/${id}`,
profileSearchOcId: (id: string) => `${profile}search/oc/${id}`,
profileSearchNewOcLeaveId: (id: string) =>
`${profile}search/new/oc/leave/${id}`,
profileSearchNewOcLeaveIdType: (id: string, type: string) =>
`${profile}search/new/oc/leave/${id}/${type}`,
profileSearchOcLeaveId: (id: string) => `${profile}search/oc/leave/${id}`,
profileSearchNewEmOcId: (id: string) =>
`${profile}search/new-employee/oc/${id}`,
profileSearchNewEmOcLeaveId: (id: string) =>
`${profile}search/new-employee/oc/leave/${id}`,
profileSearchPersonal: () => `${profile}search-personal`,
profileEmployeeTempId: (id: string) => `${profile}employee/temp/${id}`,
profileCoupleId: (profileId: string) => `${profile}couple/${profileId}`,
profileFatherId: (profileId: string) => `${profile}father/${profileId}`,
profileMotherId: (profileId: string) => `${profile}mother/${profileId}`,
profileAdsId: (profileId: string) => `${profile}address/current/${profileId}`,
profileAdsCurAdrsId: (profileId: string) =>
`${profile}address/currentAdrs/${profileId}`,
profileAdsRegisId: (profileId: string) =>
`${profile}address/registration/${profileId}`,
profileInsignId: (profileId: string) => `${profile}insignia/${profileId}`,
profileInsignHisId: (profileId: string) =>
`${profile}insignia/history/${profileId}`,
profileInsignInsignId: (profileId: string) =>
`${profile}insignia/insigniadiocesan/${profileId}`,
profileInsignToolList: `${profile}insignia/tool/list`,
profileChildId: (profileId: string) => `${profile}children/${profileId}`,
profileAssessmentId: (profileId: string) =>
`${profile}assessment/${profileId}`,
profileAssessmentHisId: (profileId: string) =>
`${profile}assessment/history/${profileId}`,
profileHonorId: (profileId: string) => `${profile}honor/${profileId}`,
profileHonorHisId: (profileId: string) =>
`${profile}honor/history/${profileId}`,
profileCertId: (profileId: string) => `${profile}certificate/${profileId}`,
profileCertHisId: (profileId: string) =>
`${profile}certificate/history/${profileId}`,
profileAbiliId: (profileId: string) => `${profile}ability/${profileId}`,
profileAbiliHisId: (profileId: string) =>
`${profile}ability/history/${profileId}`,
profileDutyId: (profileId: string) => `${profile}duty/${profileId}`,
profileDutyHisId: (profileId: string) =>
`${profile}duty/history/${profileId}`,
profileOtherId: (profileId: string) => `${profile}other/${profileId}`,
profileOtherHisId: (profileId: string) =>
`${profile}other/history/${profileId}`,
profileNopaidId: (profileId: string) => `${profile}nopaid/${profileId}`,
profileNopaidHisId: (profileId: string) =>
`${profile}nopaid/history/${profileId}`,
profileAvaId: (profileId: string) => `${profile}avatar/${profileId}`,
profileAvaCurId: (profileId: string) =>
`${profile}avatar/current/${profileId}`,
profileEduId: (profileId: string) => `${profile}education/${profileId}`,
profileEduHisId: (profileId: string) =>
`${profile}education/history/${profileId}`,
profileSwapEducation: `${profile}swap_education`,
profileTrainId: (profileId: string) => `${profile}training/${profileId}`,
profileTrainHisId: (profileId: string) =>
`${profile}training/history/${profileId}`,
profileDisId: (profileId: string) => `${profile}discipline/${profileId}`,
profileDisHisId: (profileId: string) =>
`${profile}discipline/history/${profileId}`,
profileSwapDiscipline: `${profile}swap_discipline`,
profileLeaveId: (leaveId: string) => `${profile}leave/${leaveId}`,
profileLeaveHisId: (profileId: string) =>
`${profile}leave/history/${profileId}`,
profileLeaveTotalId: (profileId: string) =>
`${profile}leave/total/${profileId}`,
profileSalaryId: (profileId: string) => `${profile}salary/${profileId}`,
profileSalaryHisId: (profileId: string) =>
`${profile}salary/history/${profileId}`,
profileSalarySwapId: (id: string, direction: string) =>
`${profile}salary/swap/${id}/${direction}`,
profileSalaryCopyId: (profileId: string) =>
`${profile}salary-copy/${profileId}`,
// ************************************
profileSalaryEmployeeId: (profileId: string) =>
`${profile}salary/employee/${profileId}`,
profileSalaryEmployeeHisId: (profileId: string) =>
`${profile}salary/employee/history/${profileId}`,
// ************************************
profileInforId: (profileId: string) => `${profile}information/${profileId}`,
profileInforHisId: (profileId: string) =>
`${profile}information/history/${profileId}`,
profileGovId: (profileId: string) => `${profile}government/${profileId}`,
profileGovHisId: (profileId: string) =>
`${profile}government/history/${profileId}`,
profileCalGovId: (profileId: string) => `${profile}cal/gov/${profileId}`,
profileFamiId: (profileId: string) => `${profile}family/${profileId}`,
profileFamiHisId: (profileId: string) =>
`${profile}family/history/${profileId}`,
profileAdrsId: (profileId: string) => `${profile}address/${profileId}`,
profileAdrsHisId: (profileId: string) =>
`${profile}address/history/${profileId}`,
searchProfileByOcId: (OcId: string, type: string) =>
`${profile}search/new/oc/${OcId}/${type}`,
searchProfileTemp: (type: string) => `${profile}search/new/oc/${type}`,
profileAvatarId: (profileId: string) => `${profile}avatar/${profileId}`,
profileAvatarHistoryId: (profileId: string) =>
`${profile}avatar/history/${profileId}`,
profileCheckId: (profileId: string) => `${profile}check/${profileId}`,
profileOrganizRoot: `${profile}organization/root`,
profileCalRetire: `${profile}cal/retire`,
profilePositionNumber: `${profile}position-number`,
organizationName: (ocId: string) => `${profile}organization/${ocId}`,
profileDeactive: (profileId: string) => `${profile}deactive/${profileId}`,
profileReactive: (profileId: string) => `${profile}reactive/${profileId}`,
profileReportId: (profileId: string) => `${report}kk1/${profileId}`,
profileKp7ShortId: (profileId: string) => `${report}kp7-short/${profileId}`,
profileChangeNameId: (profileId: string) =>
`${profile}changeName/${profileId}`,
profileChangeNameHisId: (profileId: string) =>
`${profile}changeName/history/${profileId}`,
profileCitizenId: (citizenId: string) => `${profile}citizenId/${citizenId}`,
profileEmployeeIn: `${profile}information/employee`,
profileHistory: (type: string) => `${profile}search/history/oc/${type}`,
//ระบบ ลูกจ้างชั่คราว
employmentId: (profileId: string) => `${profile}employment/${profileId}`,
employmentHistoryId: (employmentId: string) =>
`${profile}employment/history/${employmentId}`,
searchPersonal: () => `${profile}search-personal`,
//สร้างข้อมูลราชการ
createProfileOfficer: () => `${profile}information/officer`,
// verify
profileVerified: (profileId: string) => `${profile}verified/${profileId}`,
profileVerifiedUnlock: (profileId: string) =>
`${profile}not-verified/${profileId}`,
profileSendVerified: (profileId: string) =>
`${profile}send-verified/${profileId}`,
listVerified: () => `${profile}list-verified`,
registryNew,
registryNewId: (id: string) => `${registryNew}${id}`,
profileNewCitizenId: (citizenId: string) =>
`${registryNew}citizenId/${citizenId}`,
/**ปรับรายการเงินเดือนขึ้น-ลง*/
salarySwap: (dierction: string, id: string) =>
`${profile}salary/swap/${dierction}/${id}`,
listProfileNew:(id:string,page:number,pageSize:number)=>`${profile}search/new-page/oc/${id}/officer?page=${page}&pageSize=${pageSize}`,
orgProfileAvatar:`${registryNew}avatar`
};

View file

@ -0,0 +1,180 @@
import env from "../index";
const registryNew = `${env.API_URI}/org/profile`;
const metadata = `${env.API_URI}/org/metadata/`;
export default {
registryNew: (type: string) => `${registryNew}${type}`,
registryNewByProfileId: (profileId: string, type: string) =>
`${registryNew}${type}/${profileId}`,
// metadata
profileNewMetaMain: `${metadata}main/person`,
profileNewProvince: `${metadata}province`,
profileNewDistrictByPId: (id: string) => `${metadata}province/${id}`,
profileNewSubDistrictByDId: (id: string) => `${metadata}district/${id}`,
// ประวัติส่วนตัว
profileNewProfileById: (dataId: string, type: string) =>
`${registryNew}${type}/${dataId}`,
profileNewProfileHisById: (dataId: string, type: string) =>
`${registryNew}${type}/history/${dataId}`,
// ข้อมูลที่อยู่
profileNewAddressByProfileId: (profileId: string, type: string) =>
`${registryNew}${type}/address/${profileId}`,
profileNewAddressById: (dataId: string, type: string) =>
`${registryNew}${type}/address/${dataId}`,
profileNewAddressHisById: (dataId: string, type: string) =>
`${registryNew}${type}/address/history/${dataId}`,
// บันทึกวันที่ไม่ได้รับเงินเดือนฯ
profileNewNoPaid: (type: string) => `${registryNew}${type}/nopaid`,
profileNewNoPaidByProfileId: (profileId: string, type: string) =>
`${registryNew}${type}/nopaid/${profileId}`,
profileNewNoPaidById: (dataId: string, type: string) =>
`${registryNew}${type}/nopaid/${dataId}`,
profileNewNoPaidHisById: (dataId: string, type: string) =>
`${registryNew}${type}/nopaid/history/${dataId}`,
// เครื่องราชฯ
profileNewInsign: (type: string) => `${registryNew}${type}/insignia`,
profileNewInsignByProfileId: (profileId: string, type: string) =>
`${registryNew}${type}/insignia/${profileId}`,
profileNewInsignById: (dataId: string, type: string) =>
`${registryNew}${type}/insignia/${dataId}`,
profileNewInsignHisById: (dataId: string, type: string) =>
`${registryNew}${type}/insignia/history/${dataId}`,
// ประกาศเกียรติคุณ
profileNewHonor: (type: string) => `${registryNew}${type}/honor`,
profileNewHonorByProfileId: (profileId: string, type: string) =>
`${registryNew}${type}/honor/${profileId}`,
profileNewHonorById: (dataId: string, type: string) =>
`${registryNew}${type}/honor/${dataId}`,
profileNewHonorHisById: (dataId: string, type: string) =>
`${registryNew}${type}/honor/history/${dataId}`,
// ผลการประเมินการปฏิบัติราชการ
profileNewAssessments: (type: string) => `${registryNew}${type}/assessments`,
profileNewAssessmentsByProfileId: (profileId: string, type: string) =>
`${registryNew}${type}/assessments/${profileId}`,
profileNewAssessmentsById: (dataId: string, type: string) =>
`${registryNew}${type}/assessments/${dataId}`,
profileNewAssessmentsHisById: (dataId: string, type: string) =>
`${registryNew}${type}/assessments/history/${dataId}`,
// การฝึกอบรม
profileNewTraining: (type: string) => `${registryNew}${type}/training`,
profileNewTrainingByProfileId: (profileId: string, type: string) =>
`${registryNew}${type}/training/${profileId}`,
profileNewTrainingByTrainingId: (trainingId: string, type: string) =>
`${registryNew}${type}/training/${trainingId}`,
profileNewTrainingHisByTrainingId: (trainingId: string, type: string) =>
`${registryNew}${type}/training/history/${trainingId}`,
// ประวัติการศึกษา
profileNewEducation: (type: string) => `${registryNew}${type}/educations`,
profileNewEducationByProfileId: (profileId: string, type: string) =>
`${registryNew}${type}/educations/${profileId}`,
profileNewEducationByEducationId: (educationId: string, type: string) =>
`${registryNew}${type}/educations/${educationId}`,
profileNewEducationHisByEducationId: (educationsId: string, type: string) =>
`${registryNew}${type}/educations/history/${educationsId}`,
// ความสามารถพิเศษ
profileNewAbility: (type: string) => `${registryNew}${type}/ability`,
profileNewAbilityByProfileId: (profileId: string, type: string) =>
`${registryNew}${type}/ability/${profileId}`,
profileNewAbilityByAbilityId: (abilityId: string, type: string) =>
`${registryNew}${type}/ability/${abilityId}`,
profileNewAbilityHisByAbilityId: (abilityId: string, type: string) =>
`${registryNew}${type}/ability/history/${abilityId}`,
// ใบอนุญาตประกอบวิชาชีพ
profileNewCertificate: (type: string) => `${registryNew}${type}/certificate`,
profileNewCertificateByProfileId: (profileId: string, type: string) =>
`${registryNew}${type}/certificate/${profileId}`,
profileNewCertificateByCertificateId: (certificateId: string, type: string) =>
`${registryNew}${type}/certificate/${certificateId}`,
profileNewCertificateHisByCertificateId: (
certificateId: string,
type: string
) => `${registryNew}${type}/certificate/history/${certificateId}`,
// ข้อมูลอื่นๆ
profileNewOther: (type: string) => `${registryNew}${type}/other`,
profileNewOtherByProfileId: (profileId: string, type: string) =>
`${registryNew}${type}/other/${profileId}`,
profileNewOtherById: (dataId: string, type: string) =>
`${registryNew}${type}/other/${dataId}`,
profileNewOtherHisById: (dataId: string, type: string) =>
`${registryNew}${type}/other/history/${dataId}`,
// ข้อมูลครอบครัว
profileNewFamily: (type: string) => `${registryNew}${type}/family`,
profileNewFamilyByProfileId: (profileId: string, type: string) =>
`${registryNew}${type}/family/${profileId}`,
profileNewFamilyByFamilyId: (familyId: string, type: string) =>
`${registryNew}${type}/family/${familyId}`,
profileNewFamilyHisByFamilyId: (familyId: string, type: string) =>
`${registryNew}${type}/family/history/${familyId}`,
// วินัย
profileNewDiscipline: (type: string) => `${registryNew}${type}/discipline`,
profileNewDisciplineByProfileId: (profileId: string, type: string) =>
`${registryNew}${type}/discipline/${profileId}`,
profileNewDisciplineByDisciplineId: (disciplineId: string, type: string) =>
`${registryNew}${type}/discipline/${disciplineId}`,
profileNewDisciplineHisByDisciplineId: (disciplineId: string, type: string) =>
`${registryNew}${type}/discipline/history/${disciplineId}`,
// ปฏิบัติราชการพิเศษ
profileNewDuty: (type: string) => `${registryNew}${type}/duty`,
profileNewDutyByProfileId: (profileId: string, type: string) =>
`${registryNew}${type}/duty/${profileId}`,
profileNewDutyByDutyId: (dutyId: string, type: string) =>
`${registryNew}${type}/duty/${dutyId}`,
profileNewDutyHisByDutyId: (dutyId: string, type: string) =>
`${registryNew}${type}/duty/history/${dutyId}`,
//ข้อมูลราชการ
profileNewGovernment: (type: string) => `${registryNew}${type}/government`,
profileNewGovernmentById: (id: string, type: string) =>
`${registryNew}${type}/government/${id}`,
profileNewGovernmentHistory: (id: string, type: string) =>
`${registryNew}${type}/government/history/${id}`,
//การลา
profileNewLeave: (type: string) => `${registryNew}${type}/leave`,
profileNewLeaveById: (id: string, type: string) =>
`${registryNew}${type}/leave/${id}`,
profileNewLeaveHistory: (id: string, type: string) =>
`${registryNew}${type}/leave/history/${id}`,
profileCheckDate: () => `${env.API_URI}/leave/user/check`,
profileNewLeaveType: () => `${env.API_URI}/leave/type`,
/** ตำแหน่งเงินเดือน*/
profileSalaryNew: (type: string) => `${registryNew}${type}/salary`,
profileListSalaryNew: (id: string, type: string) =>
`${registryNew}${type}/salary/${id}`,
profileListSalaryHistoryNew: (profileId: string, type: string) =>
`${registryNew}${type}/salary/history/${profileId}`,
profileSalarySwapNew: (type: string, id: string, type2: string) =>
`${registryNew}${type2}/salary/swap/${type}/${id}`,
// ประวัติการเปลี่ยนชื่อ-นามสกุล
profileNewChangeName: (type: string) => `${registryNew}${type}/changeName`,
profileNewChangeNameByProfileId: (profileId: string, type: string) =>
`${registryNew}${type}/changeName/${profileId}`,
profileNewChangeNameByChangeNameId: (changeNameId: string, type: string) =>
`${registryNew}${type}/changeName/${changeNameId}`,
profileNewChangeNameHisByChangeNameId: (changeNameId: string, type: string) =>
`${registryNew}${type}/changeName/history/${changeNameId}`,
//ข้อมูลครอบครับ
profileFamily: (empType: string, type: string) =>
`${registryNew}${empType}/family/${type}`,
profileFamilyHistory: (id: string, empType: string, type: string) =>
`${registryNew}${empType}/family/${type}/history/${id}`,
};

View file

@ -0,0 +1,49 @@
/**
* api
*/
import env from "../index";
const reportOrder = `${env.API_REPORT2_URI}/report/order`;
const reportRetire = `${env.API_REPORT2_URI}/report/retire`;
const reportProbation = `${env.API_REPORT2_URI}/report/probation`;
const reportResign = `${env.API_REPORT2_URI}/report/resign/33`;
const reportTransfer = `${env.API_REPORT2_URI}/report/transfer`;
const reportDeceased = `${env.API_REPORT2_URI}/report/deceased`;
const reportTemplate = `${env.API_REPORT_TEMPLATE_URI}`;
export default {
reportOrderCover: (fileType: string, id: string, commandCode: string) =>
`${reportOrder}/${commandCode}/cover/${fileType}/${id}`,
reportOrderAttachment: (fileType: string, id: string, commandCode: string) =>
`${reportOrder}/${commandCode}/attachment/${fileType}/${id}`,
reportRetireList: (fileType: string, id: string) =>
`${reportRetire}/${fileType}/${id}`,
reportResignList: (fileType: string, id: string) =>
`${reportResign}/${fileType}/${id}`,
fileCover: (format: string, fileType: string, id: string) =>
`${reportOrder}/${format}/cover/${fileType}/${id}`,
fileAttachment: (format: string, fileType: string, id: string) =>
`${reportOrder}/${format}/attachment/${fileType}/${id}`,
//ระบบทดลองงาน
reportAssign: (type: string, id: string) =>
`${reportProbation}/13/${type}/${id}`,
reportEvaluateRecord1: (type: string, id: string) =>
`${reportProbation}/14/${type}/${id}`,
// reportEvaluateRecord2:(type:string, id:string) => `${reportProbation}/15/${type}/${id}`,
reportEvaluate: (type: string, id: string) =>
`${reportProbation}/16/${type}/${id}`,
reportEvaluateChairman: (type: string, id: string) =>
`${reportProbation}/17/${type}/${id}`,
reportEvaluateResult: (type: string, id: string) =>
`${reportProbation}/19/${type}/${id}`,
// reportSurvey:(type:string, id:string) => `${reportProbation}/19/${type}/${id}`,
//filetransfer
reportTransferFile: (no: number, type: string, id: string) =>
`${reportTransfer}/${no}/${type}/${id}`,
DeceasedReport: (type: string, id: string) =>
`${reportDeceased}/36/${type}/${id}`,
reportTemplate,
};

70
src/app.config.ts Normal file
View file

@ -0,0 +1,70 @@
/**ใช้รวมไฟล์ย่อยๆ ของ api แต่ละไฟล์ */
/** API Metadata */
import manageOrganization from "./api/manage/api.organization";
import managePerson from "./api/manage/api.person";
import managePosition from "./api/manage/api.position";
import managePositionEmployee from "./api/manage/api.positionEmployee";
import manageInsignia from "./api/manage/api.insignia";
import manageHoliday from "./api/manage/api.holiday";
/** API Tree List */
import organizationTreeList from "./api/02_organizational/api.treelist";
/** API Structure & Org Chart */
import organizationChart from "./api/02_organizational/api.chart";
/** API Profile List */
import profile from "./api/registry/api.profile";
import registry from "./api/registry/api.registry";
/** API dashboard */
import message from "./api/00_dashboard/api.message";
/** API reports */
import reports from "./api/reports/api.report";
/** API โครงสร้างอัตรากำลัง*/
import organization from "./api/02_organizational/api.organization";
import file from "./api/file/api.file";
// environment variables
export const compettitivePanel = import.meta.env.VITE_COMPETITIVE_EXAM_PANEL;
export const qualifyDisableExamPanel = import.meta.env
.VITE_QUALIFY_DISABLE_EMAM_PANEL;
export const qualifyExamPanel = import.meta.env.VITE_QUALIFY_EXAM_PANEL;
const API = {
//Metadata
...manageOrganization,
...managePerson,
...managePosition,
...managePositionEmployee,
...manageInsignia,
...manageHoliday,
// โครงสร้างอัตรากำลัง
...organization,
//Tree List
...organizationTreeList,
...organizationChart,
//profile
...profile,
...registry,
//dashboard
...message,
//reports
...reports,
/*file*/
...file,
};
export default {
API: API,
};

BIN
src/assets/05_modules.pdf Normal file

Binary file not shown.

BIN
src/assets/avatar_user.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

0
src/assets/base.css Normal file
View file

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

1
src/assets/logo.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69" xmlns:v="https://vecta.io/nano"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

After

Width:  |  Height:  |  Size: 308 B

35
src/assets/main.css Normal file
View file

@ -0,0 +1,35 @@
/* @import './base.css'; */
/* #app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}
a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
}
@media (hover: hover) {
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
}
}
@media (min-width: 1024px) {
body {
display: flex;
place-items: center;
}
#app {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
}
} */

View file

@ -0,0 +1,170 @@
<script setup lang="ts">
import { ref, reactive, watch } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
/** importType*/
import type { FormProfile } from "@/interface/main";
import type { DataProfile } from "@/modules/05_placement/interface/index/Main";
/** importComponents*/
import PopupPersonal from "@/components/Dialogs/PopupPersonalNew.vue";
/** impoerStore*/
import { useCounterMixin } from "@/stores/mixin";
/** use*/
const $q = useQuasar();
const { messageError, findOrgNameOld, findOrgName } = useCounterMixin();
/** propsDataProfile*/
const props = defineProps({
data: { type: Object, required: true },
type: { type: String, default: "" },
});
const profile = reactive<FormProfile>({
id: "",
avatar: "",
fullName: "",
position: "",
positionLevel: "",
organization: "",
});
const modalPersonal = ref<boolean>(false);
const profileId = ref<string | null>("");
function fetchDataProfile(data: DataProfile) {
if (data.profileId) {
fetchProfile(data.profileId);
}
profile.id = data.profileId;
profile.fullName = `${data.prefix ?? ""}${data.firstName ?? ""} ${
data.lastName ?? ""
} `;
if (data["posTypeNameOld"] !== undefined) {
profile.position =
data.positionOld == "" || data.positionOld == null
? "-"
: data.positionOld;
if (data.posTypeNameOld && data.posLevelNameOld) {
profile.positionLevel = `${data.posTypeNameOld} (${data.posLevelNameOld})`;
} else if (data.posTypeNameOld) {
profile.positionLevel = `${data.posTypeNameOld}`;
} else if (data.posLevelNameOld) {
profile.positionLevel = `(${data.posLevelNameOld})`;
} else profile.positionLevel = "-";
profile.organization = findOrgNameOld(data);
} else {
profile.position =
data.position == "" || data.position == null ? "-" : data.position;
if (data.posTypeName && data.posLevelName) {
profile.positionLevel = `${data.posTypeName} (${data.posLevelName})`;
} else if (data.posTypeName) {
profile.positionLevel = `${data.posTypeName}`;
} else if (data.posLevelName) {
profile.positionLevel = `(${data.posLevelName})`;
} else profile.positionLevel = "-";
profile.organization = findOrgName(data);
}
}
function fetchProfile(id: string) {
if (profile.avatar === "") {
http
.get(
config.API.fileByFile("ทะเบียนประวัติ", "โปรไฟล์", id, `profile-${id}`)
)
.then(async (res) => {
profile.avatar = res.data.downloadUrl;
});
}
}
function updatemodalPersonal(modal: boolean) {
modalPersonal.value = modal;
}
function onclickViewinfo() {
modalPersonal.value = true;
}
watch(
() => props.data,
() => {
const dataMain = props.data as DataProfile;
profileId.value = dataMain.profileId;
fetchDataProfile(dataMain);
}
);
</script>
<template>
<q-card bordered class="row col-12 text-dark">
<div class="bg-grey-1 q-pa-sm col-12 row items-center text-primary">
<div class="q-pl-sm text-weight-bold text-subtitle2">
{{ profile.fullName }}
</div>
<q-space />
<q-btn
v-if="profileId"
outline
color="blue"
dense
icon-right="mdi-open-in-new"
class="q-px-sm"
label="ดูข้อมูลทะเบียนประวัติ"
@click="onclickViewinfo()"
/>
</div>
<div class="col-12"><q-separator /></div>
<div class="row col-12 q-pa-md">
<div class="col-12 row bg-white q-col-gutter-md">
<div class="col-xs-3 col-sm-2 col-md-1 row">
<q-img :src="profile.avatar" v-if="profile.avatar !== ''" />
<q-img src="@/assets/avatar_user.jpg" v-else />
</div>
<div class="col-xs-6 col-sm-3 row items-center">
<div class="col-12 q-pl-md">
<div class="col-12 text-top">
{{ props.type == "employee" ? "ตำแหน่ง" : "ตำแหน่งในสายงาน" }}
</div>
<div class="col-12 text-detail">
{{ profile.position }}
</div>
</div>
</div>
<div class="col-xs-6 col-sm-3 row items-center">
<div class="col-12">
<div class="col-12 text-top">
{{ props.type == "employee" ? "กลุ่มงาน" : "ประเภทตำแหน่ง" }}
</div>
<div class="col-12 text-detail">
{{ profile.positionLevel }}
</div>
</div>
</div>
<div class="col-xs-6 col-sm-3 row items-center">
<div class="col-12">
<div class="col-12 text-top">งก</div>
<div class="col-12 text-detail">
{{ profile.organization }}
</div>
</div>
</div>
</div>
</div>
</q-card>
<PopupPersonal
:modal="modalPersonal"
:id="profile.id"
@update:modal="updatemodalPersonal"
:type="props.type"
/>
</template>
<style scoped></style>

View file

@ -0,0 +1,60 @@
<template>
<q-input
ref="inputRef"
v-model="formattedValue"
:dense="dense"
:outlined="edit"
:class="
edit == true
? 'full-width inputgreen cursor-pointer'
: 'full-width cursor-pointer'
"
:readonly="!edit"
:borderless="!edit"
hide-bottom-space
>
<template v-for="slot in slots">
<slot :name="slot" />
</template>
</q-input>
</template>
<script setup lang="ts">
import { useCurrencyInput } from "vue-currency-input";
import { ref, useSlots, watch } from "vue";
const slots = ref<any>(useSlots());
const props = defineProps({
modelValue: {
type: Number,
default: undefined,
},
dense: {
type: Boolean,
default: true,
},
edit: {
type: Boolean,
},
});
const { inputRef, formattedValue, setValue } = useCurrencyInput({
locale: "th-TH",
currency: "THB",
currencyDisplay: "hidden" as any,
hideCurrencySymbolOnFocus: true,
hideGroupingSeparatorOnFocus: true,
hideNegligibleDecimalDigitsOnFocus: true,
autoDecimalDigits: false,
useGrouping: true,
accountingSign: false,
});
watch(
() => props.modelValue,
(value: any) => {
setValue(value);
}
);
</script>

View file

@ -0,0 +1,90 @@
<template>
<q-dialog ref="dialogRef" @hide="onDialogHide" persistent>
<q-card class="q-pa-sm">
<q-card-section class="row">
<div class="q-pr-md">
<q-avatar
:icon="icon"
size="lg"
font-size="25px"
color="blue-1"
:text-color="color"
/>
</div>
<div class="col text-dark">
<span class="text-bold">{{ title }}</span>
<br />
<span>{{ message }}</span>
</div>
</q-card-section>
<q-card-actions
align="right"
class="bg-white text-teal"
v-if="onlycancel"
>
<q-btn label="ตกลง" flat color="grey-8" @click="onDialogCancel" />
<!-- <q-btn :label="textOk" :color="color" @click="onOKClick" /> -->
</q-card-actions>
<q-card-actions align="right" class="bg-white text-teal" v-else>
<q-btn label="ยกเลิก" flat color="grey-8" @click="onDialogCancel" />
<q-btn :label="textOk" :color="color" @click="onOKClick" />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script setup lang="ts">
import { useDialogPluginComponent } from "quasar";
const props = defineProps({
color: {
type: String,
default: "primary",
},
textOk: {
type: String,
default: "ตกลง",
},
title: {
type: String,
default: "หัวข้อ?",
},
message: {
type: String,
default: "ข้อความ",
},
icon: {
type: String,
default: "question_mark",
},
onlycancel: {
type: Boolean,
default: false,
},
});
defineEmits([
// REQUIRED; need to specify some events that your
// component will emit through useDialogPluginComponent()
...useDialogPluginComponent.emits,
]);
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
useDialogPluginComponent();
// dialogRef - Vue ref to be applied to QDialog
// onDialogHide - Function to be used as handler for @hide on QDialog
// onDialogOK - Function to call to settle dialog with "ok" outcome
// example: onDialogOK() - no payload
// example: onDialogOK({ /*...*/ }) - with payload
// onDialogCancel - Function to call to settle dialog with "cancel" outcome
// this is part of our example (so not required)
function onOKClick() {
// on OK, it is REQUIRED to
// call onDialogOK (with optional payload)
onDialogOK();
// or with payload: onDialogOK({ ... })
// ...and it will also hide the dialog automatically
}
</script>

View file

@ -0,0 +1,670 @@
<script setup lang="ts">
import { ref, watch, computed } from "vue";
import { useQuasar } from "quasar";
import config from "@/app.config";
import http from "@/plugins/http";
import moment from "moment";
import type { QForm } from "quasar";
import Header from "@/components/DialogHeader.vue";
import { useProfileDataStore } from "@/modules/08_registryEmployee/store";
import { useCounterMixin } from "@/stores/mixin";
const profileStore = useProfileDataStore();
const { changeRetireText, changeBirth } = profileStore;
const {
date2Thai,
dialogMessage,
showLoader,
hideLoader,
messageError,
dateToISO,
dialogConfirm,
success,
convertDate,
} = useCounterMixin();
const $q = useQuasar();
const dateBefore = ref<Date>(new Date());
const informaData = ref<any>({
cardid: null,
age: null,
prefix: null,
prefixId: null,
firstname: null,
lastname: null,
birthDate: "",
genderId: null,
bloodId: null,
nationality: "ไทย",
ethnicity: "ไทย",
statusId: null,
religionId: "ceaec498-71b4-4f82-b5a2-7d6ec988b753",
tel: null,
employeeType: null,
employeeClass: null,
profileType: null,
});
const Ops = ref<any>({
prefixOps: [],
prefixOldOps: [],
genderOps: [],
bloodOps: [],
statusOps: [],
religionOps: [],
employeeClassOps: [],
employeeTypeOps: [],
});
//
const OpsFilter = ref<any>({
prefixOps: [],
prefixOldOps: [],
genderOps: [],
bloodOps: [],
statusOps: [],
religionOps: [],
employeeClassOps: [],
employeeTypeOps: [],
});
const modal = defineModel<boolean>("modal", { required: true });
watch(
() => modal.value,
() => {
modal.value && fetchPerson();
}
);
//
const defaultCitizenData = ref<string>("");
const changeCardID = async (value: string | number | null) => {
if (value != null && typeof value == "string") {
if (value.length == 13 && value != defaultCitizenData.value) {
await checkCitizen(value);
}
}
};
const checkCitizen = async (id: string) => {
showLoader();
await http
.get(config.API.profileCitizenId(id))
.then((res) => {
const data = res.data.result.citizen;
if (!data) {
dialogMessage(
$q,
"ข้อความแจ้งเตือน",
"เลขประจำตัวประชาชนนี้มีการใช้งานแล้ว",
"warning",
undefined,
"orange",
undefined,
undefined,
true
);
informaData.value.cardid = defaultCitizenData.value;
}
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
};
// filter list
const filterSelector = (val: any, update: Function, refData: string) => {
switch (refData) {
case "prefixOps":
update(() => {
Ops.value.prefixOps = OpsFilter.value.prefixOps.filter(
(v: any) => v.name.indexOf(val) > -1
);
});
break;
case "genderOps":
update(() => {
Ops.value.genderOps = OpsFilter.value.genderOps.filter(
(v: any) => v.name.indexOf(val) > -1
);
});
break;
case "bloodOps":
update(() => {
Ops.value.bloodOps = OpsFilter.value.bloodOps.filter(
(v: any) => v.name.indexOf(val) > -1
);
});
break;
case "statusOps":
update(() => {
Ops.value.statusOps = OpsFilter.value.statusOps.filter(
(v: any) => v.name.indexOf(val) > -1
);
});
break;
case "religionOps":
update(() => {
Ops.value.religionOps = OpsFilter.value.religionOps.filter(
(v: any) => v.name.indexOf(val) > -1
);
});
break;
default:
break;
}
};
const calculateMaxDate = () => {
const today = new Date();
today.setDate(today.getDate() - 1);
return today;
};
//
const calRetire = async (birth: Date) => {
const body = {
birthDate: dateToISO(birth),
};
if (dateToISO(dateBefore.value) != dateToISO(birth)) {
showLoader();
await http
.post(config.API.profileCalRetire, body)
.then((res: any) => {
const data = res.data.result;
informaData.value.age = data.age;
informaData.value.birthDate = birth;
changeRetireText(data.retireDate);
dateBefore.value = birth;
})
.catch((e: any) => {
messageError($q, e);
inputBirthDate.value = "";
informaData.value.birthDate = "";
informaData.value.age = "";
})
.finally(() => {
hideLoader();
});
}
};
// validate
const myform = ref<QForm | null>(null);
const saveData = async () => {
if (myform.value != null) {
await myform.value.validate().then(async (saveDataTest: Boolean) => {
if (saveDataTest) {
if (inputBirthDate.value === "") dayChecked.value = true;
else dialogConfirm($q, () => addData()); // validate api
}
});
}
};
const fileData = ref<any>();
const addData = async () => {
const formData = new FormData();
if (fileData.value != null) formData.append("File", fileData.value); //
if (informaData.value.cardid != undefined)
formData.append("citizenId", informaData.value.cardid);
if (informaData.value.prefixId != undefined)
formData.append("prefixId", informaData.value.prefixId);
if (informaData.value.firstname != undefined)
formData.append("firstName", informaData.value.firstname);
if (informaData.value.lastname != undefined)
formData.append("lastName", informaData.value.lastname);
if (informaData.value.genderId != undefined)
formData.append("genderId", informaData.value.genderId);
if (informaData.value.nationality != undefined)
formData.append("nationality", informaData.value.nationality);
if (informaData.value.ethnicity != undefined)
formData.append("race", informaData.value.ethnicity);
if (informaData.value.religionId != undefined)
formData.append("religionId", informaData.value.religionId);
if (informaData.value.birthDate != "")
formData.append("birthDate", dateToISO(informaData.value.birthDate));
if (informaData.value.bloodId != undefined)
formData.append("bloodGroupId", informaData.value.bloodId);
if (informaData.value.statusId != undefined)
formData.append("relationshipId", informaData.value.statusId);
if (informaData.value.tel != undefined)
formData.append("telephoneNumber", informaData.value.tel);
if (informaData.value.employeeType != undefined)
formData.append("employeeType", informaData.value.employeeType);
if (informaData.value.employeeClass != undefined)
formData.append("employeeClass", informaData.value.employeeClass);
showLoader();
await http
.post(config.API.createProfileOfficer(), formData)
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
clearForm();
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
modal.value = false;
await changeBirth(informaData.value.birthDate ?? new Date());
hideLoader();
});
};
function clearForm() {
informaData.value = {
cardid: null,
age: null,
prefix: null,
prefixId: null,
firstname: null,
lastname: null,
birthDate: null,
genderId: null,
bloodId: null,
nationality: "ไทย",
ethnicity: "ไทย",
statusId: null,
religionId: "ceaec498-71b4-4f82-b5a2-7d6ec988b753",
tel: null,
employeeType: null,
employeeClass: null,
profileType: null,
};
}
/*** get รายการข้อมูลเกี่ยวกับบุคคล (dropdown list) */
const fetchPerson = async () => {
showLoader();
await http
.get(config.API.person)
.then((res) => {
const data = res.data.result;
let optionbloodGroups: any[] = [];
data.bloodGroups.map((r: any) => {
optionbloodGroups.push({
id: r.id.toString(),
name: r.name.toString(),
});
});
Ops.value.bloodOps = optionbloodGroups;
OpsFilter.value.bloodOps = optionbloodGroups;
let optiongenders: any[] = [];
data.genders.map((r: any) => {
optiongenders.push({
id: r.id.toString(),
name: r.name.toString(),
});
});
Ops.value.genderOps = optiongenders;
OpsFilter.value.genderOps = optiongenders;
let optionprefixs: any[] = [];
data.prefixs.map((r: any) => {
optionprefixs.push({
id: r.id.toString(),
name: r.name.toString(),
});
});
Ops.value.prefixOps = optionprefixs;
OpsFilter.value.prefixOps = optionprefixs;
let optionrelationships: any[] = [];
data.relationships.map((r: any) => {
optionrelationships.push({
id: r.id.toString(),
name: r.name.toString(),
});
});
Ops.value.statusOps = optionrelationships;
OpsFilter.value.statusOps = optionrelationships;
let optionreligions: any[] = [];
data.religions.map((r: any) => {
optionreligions.push({
id: r.id.toString(),
name: r.name.toString(),
});
});
Ops.value.religionOps = optionreligions;
OpsFilter.value.religionOps = optionreligions;
})
.catch((e: any) => {})
.finally(() => {
hideLoader();
});
};
// change date input
const inputBirthDate = ref<string>("");
const dayChecked = ref<boolean>(false);
watch(
() => inputBirthDate.value,
(value: string) => {
if (value.length === 10) {
const dateVal = convertDate(value);
if (dateVal.isValid) {
dayChecked.value = false;
calRetire(new Date(dateVal.value));
} else {
dayChecked.value = true;
inputBirthDate.value = "";
informaData.value.age = "";
}
}
}
);
</script>
<template>
<q-dialog v-model="modal">
<q-card style="min-width: 75vw">
<Header
tittle="เพิ่มข้อมูลทะเบียนประวัติข้าราชการ กทม.สามัญ"
:close="() => (modal = false)"
/>
<q-separator />
<q-card-section class="q-pt-none">
<HeaderTop header="ข้อมูลส่วนตัว" icon="mdi-account" />
<q-form ref="myform" class="col-12 q-pt-md">
<div class="row">
<div class="row col-12 q-col-gutter-x-sm q-mb-xs">
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
hide-bottom-space
outlined
v-model="informaData.cardid"
dense
@update:model-value="changeCardID"
for="#cardId"
lazy-rules
:rules="[
(val: string) => !!val || `${'กรุณากรอก เลขประจำตัวประชาชน'}`,
(val: string) =>
val.length >= 13 ||
`${'กรุณากรอกเลขประจำตัวประชาชนให้ครบ'}`,
]"
label="เลขประจำตัวประชาชน"
maxlength="13"
mask="#############"
/>
<!-- :rules="[(val:any) =>val.length != 13 ||`${'กรุณากรอกเลขประจำตัวประชาชนให้ครบ'}`,]" -->
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<selector
for="#prefixId"
hide-bottom-space
outlined
:rules="[(val: string) => !!val || `${'กรุณาเลือก คำนำหน้าชื่อ'}`]"
dense
lazy-rules
v-model="informaData.prefixId"
emit-value
map-options
option-label="name"
:options="Ops.prefixOps"
option-value="id"
:label="`${'คำนำหน้าชื่อ'}`"
use-input
input-debounce="0"
@filter="(inputValue: any, doneFn: Function) => filterSelector(inputValue, doneFn, 'prefixOps')"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
for="#firstname"
hide-bottom-space
outlined
dense
lazy-rules
v-model="informaData.firstname"
:rules="[(val: string) => !!val || `${'กรุณากรอก ชื่อ'}`]"
:label="`${'ชื่อ'}`"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
for="#lastname"
hide-bottom-space
outlined
dense
lazy-rules
v-model="informaData.lastname"
:rules="[(val: string) => !!val || `${'กรุณากรอก นามสกุล'}`]"
:label="`${'นามสกุล'}`"
/>
</div>
</div>
<div class="row col-12 q-col-gutter-x-sm q-mb-xs">
<div class="col-xs-6 col-sm-2 col-md-2">
<!-- <datepicker
v-model="informaData.birthDate"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
:max-date="calculateMaxDate()"
@update:model-value="calRetire"
>
<template #year="{ year }">
{{ year + 543 }}
</template>
<template #year-overlay-value="{ value }">
{{ parseInt(value + 543) }}
</template>
<template #trigger>
<q-input
for="#birthDate"
hide-bottom-space
outlined
dense
lazy-rules
:model-value="
informaData.birthDate == null
? null
: date2Thai(informaData.birthDate)
"
:rules="[(val: string) => !!val || `${'กรุณาเลือก วัน/เดือน/ปี เกิด'}`]"
:label="`${'วัน/เดือน/ปี เกิด'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker> -->
<q-input
outlined
v-model="inputBirthDate"
label="วัน/เดือน/ปี เกิด"
mask="##/##/####"
dense
:error="dayChecked"
error-message="กรุณากรอกวัน/เดือน/ปี เกิด"
/>
</div>
<div class="col-xs-6 col-sm-2 col-md-2">
<q-input
hide-bottom-space
dense
lazy-rules
readonly
borderless
style="padding: 0 12px"
:model-value="informaData.age"
:label="`${'อายุ'}`"
/>
</div>
<div class="col-xs-6 col-sm-2 col-md-2">
<selector
for="#genderId"
hide-bottom-space
outlined
dense
lazy-rules
v-model="informaData.genderId"
emit-value
map-options
option-label="name"
:options="Ops.genderOps"
option-value="id"
:label="`${'เพศ'}`"
use-input
input-debounce="0"
@filter="(inputValue: any,
doneFn: Function) => filterSelector(inputValue, doneFn, 'genderOps'
)"
/>
</div>
<div class="col-xs-6 col-sm-2 col-md-2">
<selector
for="#statusId"
hide-bottom-space
outlined
dense
lazy-rules
v-model="informaData.statusId"
emit-value
map-options
option-label="name"
:options="Ops.statusOps"
option-value="id"
:label="`${'สถานภาพ'}`"
use-input
input-debounce="0"
@filter="(inputValue: any,
doneFn: Function) => filterSelector(inputValue, doneFn, 'statusOps'
)"
/>
</div>
<div class="col-xs-6 col-sm-2 col-md-2">
<q-input
for="#nationality"
hide-bottom-space
outlined
dense
lazy-rules
v-model="informaData.nationality"
:label="`${'สัญชาติ'}`"
/>
</div>
<div class="col-xs-6 col-sm-2 col-md-2">
<q-input
for="#ethnicity"
hide-bottom-space
outlined
dense
lazy-rules
v-model="informaData.ethnicity"
:label="`${'เชื้อชาติ'}`"
/>
</div>
</div>
<div class="row col-12 q-col-gutter-x-sm q-mb-xs">
<div class="col-xs-6 col-sm-2 col-md-2">
<selector
for="#religionId"
hide-bottom-space
outlined
dense
lazy-rules
v-model="informaData.religionId"
emit-value
map-options
option-label="name"
:options="Ops.religionOps"
option-value="id"
:label="`${'ศาสนา'}`"
use-input
input-debounce="0"
@filter="(inputValue: any,
doneFn: Function) => filterSelector(inputValue, doneFn, 'religionOps'
)"
/>
</div>
<div class="col-xs-6 col-sm-2 col-md-2">
<selector
for="#bloodId"
hide-bottom-space
outlined
dense
lazy-rules
v-model="informaData.bloodId"
emit-value
map-options
option-label="name"
:options="Ops.bloodOps"
option-value="id"
:label="`${'หมู่เลือด'}`"
use-input
input-debounce="0"
@filter="(inputValue: any,
doneFn: Function) => filterSelector(inputValue, doneFn, 'bloodOps'
)"
clearable
/>
</div>
<div class="col-xs-6 col-sm-2 col-md-2">
<q-input
for="#tel"
hide-bottom-space
outlined
dense
lazy-rules
type="tel"
v-model="informaData.tel"
:label="`${'เบอร์โทร'}`"
mask="##########"
/>
</div>
</div>
</div>
</q-form>
<!-- <div class="col-12 q-pt-md q-pb-sm"><q-separator /></div> -->
<!-- <div class="row col-12"> -->
<!-- <q-space /> -->
<!-- <q-btn
for="#submitForm"
unelevated
dense
class="q-px-md items-center"
color="light-blue-10"
label="บันทึก"
@click="saveData"
/> -->
<!-- </div> -->
</q-card-section>
<q-separator />
<q-card-actions align="right" class="bg-white text-teal">
<!-- <q-btn flat label="OK" v-close-popup /> -->
<q-btn
for="#submitForm"
unelevated
dense
class="q-px-md items-center"
color="light-blue-10"
label="บันทึก"
@click="saveData"
/>
</q-card-actions>
</q-card>
</q-dialog>
</template>
<style scoped></style>

View file

@ -0,0 +1,29 @@
<template>
<q-toolbar>
<q-toolbar-title class="text-subtitle2 text-bold">{{
tittle
}}</q-toolbar-title>
<q-btn
icon="close"
unelevated
round
dense
@click="close"
style="color: #ff8080; background-color: #ffdede"
/>
</q-toolbar>
</template>
<script setup lang="ts">
import { ref, useAttrs } from "vue";
const props = defineProps({
tittle: String,
close: {
type: Function,
default: () => console.log("not function"),
},
});
const close = async () => {
props.close();
};
</script>

View file

@ -0,0 +1,334 @@
<script setup lang="ts">
import { ref, watch } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import { useCounterMixin } from "@/stores/mixin";
import type { ResponsePreson } from "@/interface/response/listPerson";
import { useDisciplineMainStore } from "@/modules/11_discipline/store/main";
const mainStore = useDisciplineMainStore();
const $q = useQuasar();
const mixin = useCounterMixin();
const { dialogMessageNotify, showLoader, hideLoader } = mixin;
interface typeOp {
id: string;
name: string;
}
interface tableType {
personId: string;
idcard: string;
prefix: string;
firstName: string;
lastName: string;
position: string;
positionLevel: string;
organization: string;
salary: string;
name: string;
}
const rows = ref<tableType[]>([]);
const type = ref<string>("idcard");
const search = ref<string>("");
const selected = ref<any>([]);
const typeOps = ref<typeOp[]>([
{ id: "idcard", name: "เลขประจำตัวประชาชน" },
{ id: "firstname", name: "ชื่อ" },
{ id: "lastname", name: "นามสกุล" },
]);
/** รับค่ามาจาก หน้าหลัก */
const props = defineProps({
modal: {
type: Boolean,
default: false,
required: true,
},
btnTitle: {
type: String,
default: "เพิ่ม",
},
title: {
type: String,
default: "",
},
desc: {
type: String,
default: "",
},
selectedData: {
type: Array,
default: [],
},
close: {
type: Function,
default: () => console.log("not function"),
required: true,
},
save: {
type: Function,
default: () => console.log("not function"),
required: true,
},
selecetSwitch: {
type: String,
default: "multiple",
},
});
/**ส่งค่ากลับหน้าหลัก */
const emit = defineEmits(["returnData"]);
/** ปิด dialog */
async function close() {
console.log("close");
props.close();
}
/** เปิด dialog ยืนยัน */
function savePost() {
if (selected.value.length != 0) {
// dialogConfirm($q, () => saveData());
saveData();
} else {
dialogMessageNotify($q, "กรุณาเลือกอย่างน้อย 1 รายการ");
}
}
/** save data หลังจาก ยืนยัน */
function saveData() {
props.save();
emit("returnData", selected.value);
}
const searchRef = ref<any>(null);
/** input ค้นหา */
async function searchInput() {
searchRef.value.validate();
if (!searchRef.value.hasError) {
showLoader();
const body = {
fieldName: type.value,
keyword: search.value,
};
await http
.post(config.API.orgSearchPersonal(), body)
.then((res) => {
const data = res.data.result;
const list = data.map((e: any) => ({
personId: e.id,
idcard: e.idcard,
prefix: e.prefix,
firstName: e.firstName,
lastName: e.lastName,
name: `${e.prefix}${e.firstName} ${e.lastName}`,
posNo: e.posNo ?? "-",
position: e.position ?? "-",
positionLevel: e.positionLevelName ?? "-",
salary: e.salary ?? "-",
organization: e.organization ?? "-",
phone: e.phone ?? "-",
email: e.email ?? "-",
root: e.root,
rootId: e.rootId,
rootShortName: e.rootShortName,
child1: e.child1,
child1Id: e.child1Id,
child1ShortName: e.child1ShortName,
child2: e.child2,
child2Id: e.child2Id,
child2ShortName: e.child2ShortName,
child3: e.child3,
child3Id: e.child3Id,
child3ShortName: e.child3ShortName,
child4: e.child4,
child4Id: e.child4Id,
child4ShortName: e.child4ShortName,
posMasterNo: e.posMasterNo,
posTypeId: e.posTypeId,
posTypeName: e.posTypeName,
posLevelId: e.posLevelId,
posLevelName: e.posLevelName,
}));
rows.value = list;
})
.catch((err) => {
console.log(err);
})
.finally(() => {
hideLoader();
});
}
}
/** update เมื่อเปลี่ยน option */
function updateSelect() {
search.value = "";
}
watch(
() => props.selectedData,
() => {
if (props.selectedData) {
selected.value = props.selectedData;
}
}
);
</script>
<template>
<q-dialog v-model="props.modal" persistent>
<q-card style="min-width: 60vw">
<q-toolbar>
<q-toolbar-title class="text-subtitle2 text-bold">{{
props.title
}}</q-toolbar-title>
<q-btn
icon="close"
unelevated
round
dense
@click="close"
style="color: #ff8080; background-color: #ffdede"
/>
</q-toolbar>
<q-separator />
<q-card-section class="q-pa-md">
<div class="row col-12 q-col-gutter-sm items-start">
<div class="col-12 col-sm-5 col-md-3">
<q-select
label="ค้นหาจาก"
v-model="type"
:options="typeOps"
emit-value
dense
@update:model-value="updateSelect"
map-options
outlined
option-label="name"
option-value="id"
/>
</div>
<div class="col-12 col-sm-7 col-md-9">
<q-input
ref="searchRef"
v-model="search"
outlined
clearable
dense
label="คำค้น"
:rules="[(val) => !!val || `กรุณากรอกคำค้น`]"
>
<template v-slot:after>
<q-btn
color="primary"
icon="search"
label="ค้นหา"
outline
class="full-width q-py-sm q-px-md"
@click="searchInput()"
>
</q-btn>
</template>
</q-input>
</div>
</div>
<div class="full-width">
<d-table
ref="table"
:columns="mainStore.columnsRespondent"
:rows="rows"
row-key="personId"
flat
bordered
:paging="true"
dense
class="custom-header-table"
:visible-columns="mainStore.visibleColumnsRespondent"
:selection="props.selecetSwitch"
v-model:selected="selected"
>
<template v-slot:header-selection="scope">
<q-checkbox
keep-color
color="primary"
dense
v-model="scope.checkBox"
/>
</template>
<template v-slot:header="props">
<q-tr :props="props">
<q-th class="text-center">
<q-checkbox
keep-color
color="primary"
dense
v-model="props.selected"
/>
</q-th>
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
style="color: #000000; font-weight: 500"
>
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<td class="text-center">
<q-checkbox
keep-color
color="primary"
dense
v-model="props.selected"
/>
</td>
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div v-if="col.name == 'no'">
{{ props.rowIndex + 1 }}
</div>
<div v-if="col.name === 'fullName'">
{{ props.prefix }}
</div>
<div v-else-if="col.name === 'salary'">
{{ props.row.salary.toLocaleString() }}
</div>
<div v-else>
{{ col.value }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</div>
</q-card-section>
<q-separator />
<div class="row justify-end q-px-md q-py-sm items-center">
<q-btn
dense
color="public"
id="onSubmit"
class="q-px-md q-py-xs"
@click="savePost"
>
<!-- <q-icon left name="add" /> -->
<div>{{ props.btnTitle }}</div>
<!-- icon="mdi-content-save-outline" -->
<q-tooltip>{{ props.btnTitle }}</q-tooltip>
</q-btn>
</div>
</q-card>
</q-dialog>
</template>

View file

@ -0,0 +1,901 @@
<script setup lang="ts">
import DialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar } from "quasar";
import { ref, watch, reactive } from "vue";
import type { QTableProps } from "quasar";
import CardPosition from "@/modules/05_placement/components/PersonalList/CardPosition.vue";
import http from "@/plugins/http";
import config from "@/app.config";
import type {
OrgTree,
DataTree,
PositionMain,
PositionNo,
Positions,
DataPositionNo,
FormActive,
TreeMain,
} from "@/interface/request/orgSelect/org";
const props = defineProps({
dataRows: {
type: Object,
require: true,
},
onSubmit: Function,
});
const $q = useQuasar();
const mixin = useCounterMixin();
const {
dialogConfirm,
showLoader,
messageError,
hideLoader,
date2Thai,
dialogMessageNotify,
} = mixin;
const modal = defineModel<boolean>("modal", { required: true });
const title = defineModel<string>("title", { required: true });
const type = defineModel<any>("type", { required: true });
const posType = defineModel<any>("posType", { required: true });
const posLevel = defineModel<any>("posLevel", { required: true });
const position = defineModel<any>("position", { required: true });
const orgRevisionId = ref<string>("");
const filter = ref<string>("");
const isAll = ref<boolean>(false);
const isBlank = ref<boolean>(false);
const posMasterMain = ref<any>([]);
const positionNo = ref<DataPositionNo[]>();
const itemTaps = ref<string[]>();
const filters = ref<string>("");
const positionId = ref<string>("");
const selectedPos = ref<any[]>([]);
const seletcId = ref<string>("");
const datePos = ref<Date>(new Date());
const rowsPosition = ref<Positions[]>([]);
const positionData = ref<any>();
/** active form */
const formActive = reactive<FormActive>({
activeId: "",
activeName: "",
draftId: "",
draftName: "",
orgPublishDate: null,
isPublic: false,
});
/** node */
const nodes = ref<Array<OrgTree>>([]);
const lazy = ref(nodes);
const expanded = ref<string[]>([]);
const nodeLevel = ref<number>(0);
const nodeId = ref<string>(""); // id Tree
/** columns*/
const columns = ref<QTableProps["columns"]>([
{
name: "isPosition",
align: "left",
label: "",
sortable: true,
field: "isPosition",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "no",
align: "left",
label: "ลำดับ",
sortable: true,
field: "no",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posMasterNo",
align: "left",
label: "เลขที่ตำแหน่ง",
sortable: true,
field: "posMasterNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionName",
align: "left",
label: "ตำแหน่งในสายงาน",
field: "positionName",
sortable: true,
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posTypeName",
align: "left",
label: "ประเภทตำแหน่ง",
sortable: true,
field: "posTypeName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posLevelName",
align: "left",
label: "ระดับตำแหน่ง",
sortable: true,
field: "posLevelName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionIsSelected",
align: "left",
label: "คนครอง",
sortable: true,
field: "positionIsSelected",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
]);
const columnsPostition = ref<QTableProps["columns"]>([
{
name: "no",
align: "left",
label: "ลำดับ",
sortable: false,
field: "no",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionName",
align: "left",
label: "ตำแหน่งในสายงาน",
sortable: true,
field: "positionName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionField",
align: "left",
label: "สายงาน",
sortable: true,
field: "positionField",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posTypeName",
align: "left",
label: "ประเภทตำเเหน่ง",
sortable: true,
field: "posTypeName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posLevelName",
align: "left",
label: "ระดับตำแหน่ง",
sortable: true,
field: "posLevelName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posExecutiveName",
align: "left",
label: "ตำแหน่งทางการบริหาร",
sortable: true,
field: "posExecutiveName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionExecutiveField",
align: "left",
label: "ด้านทางการบริหาร",
sortable: true,
field: "positionExecutiveField",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionArea",
align: "left",
label: "ด้าน/สาขา",
sortable: true,
field: "positionArea",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
]);
const visibleColumns = ref<string[]>([
"isPosition",
"no",
"posMasterNo",
"positionName",
"posTypeName",
"posLevelName",
"positionIsSelected",
]);
/** ปิด dialog */
function close() {
modal.value = false;
type.value = null;
filter.value = "";
isAll.value = false;
isBlank.value = false;
nodes.value = [];
expanded.value = [];
nodeLevel.value = 0;
nodeId.value = "";
}
async function getDataTable(id: string, level: number = 0) {
showLoader();
const body = {
node: level,
nodeId: id,
typeCommand: type.value,
position: posType.value ? posType.value : "",
posLevel: posLevel.value ? posLevel.value : "",
posType: position.value ? position.value : "",
isAll: isAll.value,
isBlank: isBlank.value,
};
await http
.post(config.API.orgPosPlacement, body)
.then((res) => {
const dataMain: PositionMain[] = [];
posMasterMain.value = res.data.result.data;
res.data.result.data.forEach((e: PositionNo) => {
const p = e.positions;
if (p.length !== 0) {
const a = p.find((el: Positions) => el.positionIsSelected === true);
const { id, ...rest } = a ? a : p[0];
const data: any = { ...e, ...rest };
dataMain.push(data);
}
});
const listPosNo: DataPositionNo[] = dataMain.map((e: PositionMain) => ({
id: e.id,
isPosition: e.isPosition,
posMasterNo:
e.orgShortname +
(e.posMasterNoPrefix != null ? e.posMasterNoPrefix : "") +
e.posMasterNo +
(e.posMasterNoSuffix != null ? e.posMasterNoSuffix : ""),
positionName: e.positionName,
posTypeName: e.posTypeName,
posLevelName: e.posLevelName,
positionIsSelected: e.positionIsSelected
? e.fullNameCurrentHolder
: "-",
isSit: e.isSit,
positions: e.positions,
node: e.node,
nodeId: e.nodeId,
}));
positionNo.value = listPosNo;
positionData.value = listPosNo;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
setTimeout(() => {
hideLoader();
}, 1000);
});
}
/**
* funtion เลอกขอม Tree
* @param data อม Tree
*/
function updateSelected(data: DataTree) {
if (props?.dataRows?.nodeId === data.orgTreeId) {
positionId.value = props?.dataRows?.posmasterId;
seletcId.value = props?.dataRows?.positionId;
datePos.value = props?.dataRows?.reportingDateFullDate;
} else {
positionId.value = "";
seletcId.value = "";
selectedPos.value = [];
datePos.value = new Date();
}
nodeId.value = data.orgTreeId ? data.orgTreeId : "";
nodeLevel.value = data.orgLevel;
getDataTable(data.orgTreeId, data.orgLevel);
}
/** ดึงข้อมูล active */
async function getActive() {
showLoader();
await http
.get(config.API.activeOrganization)
.then((res) => {
const data = res.data.result;
formActive.activeId = data.activeId;
formActive.activeName = data.activeName;
formActive.draftId = data.draftId;
formActive.draftName = data.draftName;
formActive.orgPublishDate = data.orgPublishDate;
formActive.isPublic = data.isPublic;
getTreeData(data.activeId);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/** ดึงข้อมูล หน่วยงาน */
async function getTreeData(id: string) {
showLoader();
await http
.get(config.API.orgByid(id))
.then((res) => {
const data = res.data.result;
nodes.value = data;
filterItemsTaps(data);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function filterItemsTaps(data: TreeMain[]) {
let orgTreeIds: string[] = [];
for (const child of data) {
orgTreeIds.push(child.orgTreeId);
if (child.children) {
orgTreeIds = orgTreeIds.concat(filterItemsTaps(child.children));
}
}
itemTaps.value = orgTreeIds;
return orgTreeIds;
}
/**
* function เลอกเลขทตำแหน
* @param id เลชทตำแหน
*/
async function onClickSelectPos(id: string) {
positionData.value = positionNo.value;
positionId.value = id;
selectedPos.value = [];
const position: DataPositionNo = positionData.value.find(
(e: DataPositionNo) => e.id === id
);
//
if (position) {
rowsPosition.value = position.positions;
if (seletcId.value) {
selectedPos.value = rowsPosition.value.filter(
(e) => e.id === seletcId.value
);
}
}
}
/**
* function fetch อม expanded tree
* @param level levelTree
* @param id treeId
*/
async function fetchPosFind(level: number, id: string) {
showLoader();
const body = {
node: level,
nodeId: id,
};
await http
.post(config.API.orgPosFind, body)
.then((res) => {
const data = res.data.result;
expanded.value = data;
nodeId.value = id;
positionId.value = props?.dataRows?.posmasterId;
seletcId.value = props?.dataRows?.positionId;
datePos.value = props?.dataRows?.reportingDateFullDate;
getDataTable(nodeId.value, level);
})
.catch((e) => {
messageError($q, e);
hideLoader();
});
}
watch(
() => modal.value,
async (n) => {
if (n == true) {
getActive();
if (props?.dataRows?.node !== null && props?.dataRows?.nodeId !== null) {
await fetchPosFind(props?.dataRows?.node, props?.dataRows?.nodeId);
if (positionId.value) {
setTimeout(async () => {
await onClickSelectPos(positionId.value);
}, 200);
}
} else {
expanded.value = [];
}
}
}
);
watch(
() => isAll.value,
(value, oldVal) => {
if (value !== oldVal) {
getDataTable(nodeId.value, nodeLevel.value);
}
}
);
watch(
() => isBlank.value,
(value, oldVal) => {
if (value !== oldVal) {
getDataTable(nodeId.value, nodeLevel.value);
}
}
);
watch(
() => positionId.value,
(n) => {
if (n) {
onClickSelectPos(n);
}
}
);
function onSubmit() {
const dataPosMaster = posMasterMain.value?.find(
(e: any) => e.id === positionId.value
);
console.log(dataPosMaster);
if (selectedPos.value.length === 0) {
dialogMessageNotify($q, "กรุณาเลือกตำแหน่ง");
} else {
dialogConfirm($q, async () => {
const body = {
personalId: props?.dataRows?.id,
node: dataPosMaster.node,
nodeId: dataPosMaster.nodeId,
orgRevisionId: formActive.activeId,
positionId: selectedPos.value[0].id,
posMasterNo: dataPosMaster.posMasterNo, //()
positionName: selectedPos.value[0].positionName, //
positionField: selectedPos.value[0].positionField, //
posTypeId: selectedPos.value[0].posTypeId, //
posTypeName: selectedPos.value[0].posTypeName, //
posLevelId: selectedPos.value[0].posLevelId, //
posLevelName: selectedPos.value[0].posLevelName, //
reportingDate: datePos.value,
posmasterId: dataPosMaster.id,
typeCommand: type.value,
};
props.onSubmit?.(body);
});
}
}
</script>
<template>
<q-dialog v-model="modal" persistent full-width>
<q-card class="no-scroll">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader :tittle="title" :close="close" />
<q-separator />
<q-card-section style="max-height: 80vh" class="scroll">
<div class="row">
<q-card
bordered
class="col-12 col-sm-3 scroll q-pa-sm"
style="height: 75vh"
>
<q-toolbar style="padding: 0">
<q-toolbar-title class="text-subtitle2 text-bold"
>เลอกหนวยงาน/วนราชการ</q-toolbar-title
>
</q-toolbar>
<q-input dense outlined v-model="filter" label="ค้นหา">
<template v-slot:append>
<q-icon
v-if="filter !== ''"
name="clear"
class="cursor-pointer"
@click="filter = ''"
/>
</template>
</q-input>
<q-tree
class="q-pa-sm q-gutter-sm"
dense
:nodes="lazy"
node-key="orgTreeId"
label-key="orgTreeName"
:filter="filter"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
v-model:expanded="expanded"
>
<template v-slot:default-header="prop">
<q-item
@click.stop="updateSelected(prop.node)"
clickable
:active="nodeId == prop.node.orgTreeId"
active-class="my-list-link text-primary text-weight-medium"
class="row col-12 items-center text-dark q-py-xs q-pl-sm rounded-borders my-list"
>
<div>
<div class="text-weight-medium">
{{ prop.node.orgTreeName }}
</div>
<div class="text-weight-light">
{{
prop.node.orgCode == null ? null : prop.node.orgCode
}}
{{
prop.node.orgTreeShortName == null
? null
: prop.node.orgTreeShortName
}}
</div>
</div>
</q-item>
</template>
</q-tree>
</q-card>
<q-card bordered class="col-12 col-sm-9 q-pa-sm">
<q-tab-panels
v-model="nodeId"
animated
transition-prev="jump-up"
transition-next="jump-up"
>
<q-tab-panel
v-for="(item, index) in itemTaps"
:key="index"
:name="item"
>
<div class="column q-col-gutter-sm" style="height: 70vh">
<!-- เลอกเลขทตำแหน -->
<div class="col-7">
<q-card
bordered
style="height: 100%; border: 1px solid #d6dee1"
>
<div
class="col-12 text-weight-medium bg-grey-1 q-py-sm q-px-md"
>
เลอกเลขทตำแหน
</div>
<div class="col-12"><q-separator /></div>
<div class="col-12 q-pa-md">
<q-toolbar style="padding: 0px">
<div class="row q-gutter-md">
<q-checkbox
keep-color
v-model="isBlank"
label="แสดงเฉพาะตำแหน่งว่าง"
color="primary"
>
<q-tooltip>แสดงเฉพาะตำแหนงวาง </q-tooltip>
</q-checkbox>
</div>
<q-space />
<div class="row q-gutter-md">
<q-checkbox
keep-color
v-model="isAll"
label="แสดงตำแหน่งทั้งหมด"
color="primary"
>
<q-tooltip
>แสดงตำแหนงทงหมดภายใตหนวยงาน/วนราชการทเลอก</q-tooltip
>
</q-checkbox>
<div>
<q-input
outlined
dense
v-model="filters"
label="ค้นหา"
>
<template v-slot:append>
<q-icon name="search" color="grey-5" />
</template>
</q-input>
</div>
<div>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
emit-value
:display-value="$q.lang.table.columns"
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 180px"
/>
</div>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="positionData"
:filter="filters"
row-key="id"
flat
bordered
:paging="true"
dense
:rows-per-page-options="[10, 25, 50, 100]"
class="tableTb"
: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"
>
<span class="text-weight-medium">{{
col.label
}}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
@click="onClickSelectPos(props.row.id)"
:class="
props.row.id === positionId
? 'bg-blue-2'
: ''
"
>
<div v-if="col.name == 'no'">
{{ props.rowIndex + 1 }}
</div>
<div v-else-if="col.name === 'posMasterNo'">
{{
props.row.isSit
? col.value + " " + "(ทับที่)"
: col.value
}}
</div>
<div v-else-if="col.name === 'isPosition'">
<div v-if="col.value">
<q-icon
name="done"
color="primary"
size="24px"
>
<q-tooltip>ตรงตามตำแหน </q-tooltip>
</q-icon>
</div>
</div>
<div v-else>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</div>
</q-card>
</div>
<!-- เลอกตำแหน -->
<div class="col-5">
<q-card
bordered
style="height: 100%; border: 1px solid #d6dee1"
>
<div
class="col-12 text-weight-medium bg-grey-1 q-py-sm q-px-md"
>
เลอกตำแหน
</div>
<div class="col-12"><q-separator /></div>
<q-tab-panels
v-model="positionId"
animated
swipeable
vertical
transition-prev="jump-up"
transition-next="jump-up"
>
<q-tab-panel
v-for="(item, index) in positionData"
:key="index"
:name="item.id"
>
<div class="col-12">
<q-toolbar style="padding: 0px">
<datepicker
menu-class-name="modalfix"
v-model="datePos"
:locale="'th'"
autoApply
borderless
:enableTimePicker="false"
week-start="0"
:min-date="datePos"
>
<template #year="{ year }">
{{ year + 543 }}
</template>
<template #year-overlay-value="{ value }">
{{ parseInt(value + 543) }}
</template>
<template #trigger>
<q-input
ref="dateRef"
outlined
dense
hide-bottom-space
:model-value="
datePos != null
? date2Thai(datePos)
: null
"
label="วันที่รายงานตัว"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</q-toolbar>
<d-table
ref="table"
:columns="columnsPostition"
:rows="rowsPosition"
row-key="id"
flat
bordered
:paging="true"
dense
:rows-per-page-options="[10, 25, 50, 100]"
class="tableTb"
selection="single"
v-model:selected="selectedPos"
>
<template v-slot:header-selection="scope">
<q-checkbox
keep-color
color="primary"
dense
v-model="scope.checkBox"
/>
</template>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width />
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
>
<span class="text-weight-medium">{{
col.label
}}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td>
<q-checkbox
keep-color
color="primary"
dense
v-model="props.selected"
/>
</q-td>
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
>
<div v-if="col.name == 'no'">
{{ props.rowIndex + 1 }}
</div>
<div
v-else-if="col.name === 'posMasterNo'"
>
{{
props.row.isSit
? col.value + " " + "(ทับที่)"
: col.value
}}
</div>
<div v-else>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</div>
</q-tab-panel>
</q-tab-panels>
</q-card>
</div>
</div>
</q-tab-panel>
</q-tab-panels>
</q-card>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="bg-white text-teal">
<q-btn label="บันทึก" color="secondary" type="submit"
><q-tooltip>นทกขอม</q-tooltip></q-btn
>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>
<style scoped>
.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

@ -0,0 +1,880 @@
<script setup lang="ts">
import DialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar } from "quasar";
import { ref, watch, reactive } from "vue";
import type { QTableProps } from "quasar";
import CardPosition from "@/modules/05_placement/components/PersonalList/CardPosition.vue";
import http from "@/plugins/http";
import config from "@/app.config";
import type {
OrgTree,
DataTree,
PositionMain,
PositionNo,
Positions,
DataPositionNo,
FormActive,
TreeMain,
} from "@/interface/request/orgSelect/org";
const props = defineProps({
dataRows: {
type: Object,
require: true,
},
onSubmit: Function,
});
const $q = useQuasar();
const mixin = useCounterMixin();
const {
dialogConfirm,
showLoader,
messageError,
hideLoader,
date2Thai,
dialogMessageNotify,
} = mixin;
const modal = defineModel<boolean>("modal", { required: true });
const title = defineModel<string>("title", { required: true });
const type = defineModel<any>("type", { required: true });
const posType = defineModel<any>("posType", { required: true });
const posLevel = defineModel<any>("posLevel", { required: true });
const position = defineModel<any>("position", { required: true });
const orgRevisionId = ref<string>("");
const filter = ref<string>("");
const isAll = ref<boolean>(false);
const isBlank = ref<boolean>(false);
const posMasterMain = ref<any>([]);
const positionNo = ref<DataPositionNo[]>();
const itemTaps = ref<string[]>();
const filters = ref<string>("");
const positionId = ref<string>("");
const selectedPos = ref<any[]>([]);
const seletcId = ref<string>("");
const datePos = ref<Date>(new Date());
const rowsPosition = ref<Positions[]>([]);
const positionData = ref<any>();
/** active form */
const formActive = reactive<FormActive>({
activeId: "",
activeName: "",
draftId: "",
draftName: "",
orgPublishDate: null,
isPublic: false,
});
/** node */
const nodes = ref<Array<OrgTree>>([]);
const lazy = ref(nodes);
const expanded = ref<string[]>([]);
const nodeLevel = ref<number>(0);
const nodeId = ref<string>(""); // id Tree
/** columns*/
const columns = ref<QTableProps["columns"]>([
{
name: "isPosition",
align: "left",
label: "",
sortable: true,
field: "isPosition",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "no",
align: "left",
label: "ลำดับ",
sortable: true,
field: "no",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posMasterNo",
align: "left",
label: "เลขที่ตำแหน่ง",
sortable: true,
field: "posMasterNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionName",
align: "left",
label: "ตำแหน่ง",
field: "positionName",
sortable: true,
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posTypeName",
align: "left",
label: "กลุ่มงาน",
sortable: true,
field: "posTypeName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posLevelName",
align: "left",
label: "ระดับชั้นงาน",
sortable: true,
field: "posLevelName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionIsSelected",
align: "left",
label: "คนครอง",
sortable: true,
field: "positionIsSelected",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
]);
const columnsPostition = ref<QTableProps["columns"]>([
{
name: "no",
align: "left",
label: "ลำดับ",
sortable: false,
field: "no",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionName",
align: "left",
label: "ตำแหน่ง",
sortable: true,
field: "positionName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionField",
align: "left",
label: "สายงาน",
sortable: true,
field: "positionField",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posTypeName",
align: "left",
label: "กลุ่มงาน",
sortable: true,
field: "posTypeName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posLevelName",
align: "left",
label: "ระดับชั้นงาน",
sortable: true,
field: "posLevelName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionArea",
align: "left",
label: "ด้าน/สาขา",
sortable: true,
field: "positionArea",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
]);
const visibleColumns = ref<string[]>([
"isPosition",
"no",
"posMasterNo",
"positionName",
"posTypeName",
"posLevelName",
"positionIsSelected",
]);
/** ปิด dialog */
function close() {
modal.value = false;
filter.value = "";
isAll.value = false;
isBlank.value = false;
type.value = null;
nodes.value = [];
expanded.value = [];
nodeLevel.value = 0;
nodeId.value = "";
}
async function getDataTable(id: string, level: number = 0) {
showLoader();
const body = {
node: level,
nodeId: id,
position: position.value ? position.value : "",
posLevel: posLevel.value ? posLevel.value : "",
posType: posType.value ? posType.value : "",
isAll: isAll.value,
isBlank: isBlank.value,
typeCommand: type.value,
};
await http
.post(config.API.orgPosPlacemenTemp, body)
.then((res) => {
const dataMain: PositionMain[] = [];
posMasterMain.value = res.data.result.data;
res.data.result.data.forEach((e: PositionNo) => {
const p = e.positions;
if (p.length !== 0) {
const a = p.find((el: Positions) => el.positionIsSelected === true);
const { id, ...rest } = a ? a : p[0];
const data: any = { ...e, ...rest };
dataMain.push(data);
}
});
const listPosNo: DataPositionNo[] = dataMain.map((e: PositionMain) => ({
id: e.id,
isPosition: e.isPosition,
posMasterNo:
e.orgShortname +
(e.posMasterNoPrefix != null ? e.posMasterNoPrefix : "") +
e.posMasterNo +
(e.posMasterNoSuffix != null ? e.posMasterNoSuffix : ""),
positionName: e.positionName,
posTypeName: e.posTypeName,
posLevelName: e.posLevelName,
positionIsSelected: e.positionIsSelected
? e.fullNameCurrentHolder
: "-",
isSit: e.isSit,
positions: e.positions,
node: e.node,
nodeId: e.nodeId,
}));
positionNo.value = listPosNo;
positionData.value = listPosNo;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
setTimeout(() => {
hideLoader();
}, 1000);
});
}
/**
* funtion เลอกขอม Tree
* @param data อม Tree
*/
function updateSelected(data: DataTree) {
if (props?.dataRows?.nodeId === data.orgTreeId) {
positionId.value = props?.dataRows?.posmasterId;
seletcId.value = props?.dataRows?.positionId;
datePos.value = props?.dataRows?.reportingDateFullDate;
} else {
positionId.value = "";
seletcId.value = "";
selectedPos.value = [];
datePos.value = new Date();
}
nodeId.value = data.orgTreeId ? data.orgTreeId : "";
nodeLevel.value = data.orgLevel;
getDataTable(data.orgTreeId, data.orgLevel);
}
/** ดึงข้อมูล active */
async function getActive() {
showLoader();
await http
.get(config.API.activeOrganization)
.then((res) => {
const data = res.data.result;
formActive.activeId = data.activeId;
formActive.activeName = data.activeName;
formActive.draftId = data.draftId;
formActive.draftName = data.draftName;
formActive.orgPublishDate = data.orgPublishDate;
formActive.isPublic = data.isPublic;
getTreeData(data.activeId);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/** ดึงข้อมูล หน่วยงาน */
async function getTreeData(id: string) {
showLoader();
await http
.get(config.API.orgByid(id))
.then((res) => {
const data = res.data.result;
nodes.value = data;
filterItemsTaps(data);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function filterItemsTaps(data: TreeMain[]) {
let orgTreeIds: string[] = [];
for (const child of data) {
orgTreeIds.push(child.orgTreeId);
if (child.children) {
orgTreeIds = orgTreeIds.concat(filterItemsTaps(child.children));
}
}
itemTaps.value = orgTreeIds;
return orgTreeIds;
}
/**
* function เลอกเลขทตำแหน
* @param id เลชทตำแหน
*/
async function onClickSelectPos(id: string) {
positionData.value = positionNo.value;
positionId.value = id;
selectedPos.value = [];
const position: DataPositionNo = positionData.value.find(
(e: DataPositionNo) => e.id === id
);
//
if (position) {
rowsPosition.value = position.positions;
if (seletcId.value) {
selectedPos.value = rowsPosition.value.filter(
(e) => e.id === seletcId.value
);
}
}
}
/**
* function fetch อม expanded tree
* @param level levelTree
* @param id treeId
*/
async function fetchPosFind(level: number, id: string) {
showLoader();
const body = {
node: level,
nodeId: id,
};
await http
.post(config.API.orgPosFind, body)
.then((res) => {
const data = res.data.result;
expanded.value = data;
nodeId.value = id;
positionId.value = props?.dataRows?.posmasterId;
seletcId.value = props?.dataRows?.positionId;
datePos.value = props?.dataRows?.reportingDateFullDate;
getDataTable(nodeId.value, level);
})
.catch((e) => {
messageError($q, e);
hideLoader();
});
}
watch(
() => modal.value,
async (n) => {
if (n == true) {
getActive();
if (props?.dataRows?.node !== null && props?.dataRows?.nodeId !== null) {
await fetchPosFind(props?.dataRows?.node, props?.dataRows?.nodeId);
if (positionId.value) {
setTimeout(async () => {
await onClickSelectPos(positionId.value);
}, 200);
}
} else {
expanded.value = [];
}
}
}
);
watch(
() => isAll.value,
(value, oldVal) => {
if (value !== oldVal) {
getDataTable(nodeId.value, nodeLevel.value);
}
}
);
watch(
() => isBlank.value,
(value, oldVal) => {
if (value !== oldVal) {
getDataTable(nodeId.value, nodeLevel.value);
}
}
);
watch(
() => positionId.value,
(n) => {
if (n) {
onClickSelectPos(n);
}
}
);
function onSubmit() {
const dataPosMaster = posMasterMain.value?.find(
(e: any) => e.id === positionId.value
);
console.log(dataPosMaster);
if (selectedPos.value.length === 0) {
dialogMessageNotify($q, "กรุณาเลือกตำแหน่ง");
} else {
dialogConfirm($q, async () => {
const body = {
personalId: props?.dataRows?.id,
node: dataPosMaster.node,
nodeId: dataPosMaster.nodeId,
orgRevisionId: formActive.activeId,
positionId: selectedPos.value[0].id,
posMasterNo: dataPosMaster.posMasterNo, //()
positionName: selectedPos.value[0].positionName, //
positionField: selectedPos.value[0].positionField, //
posTypeId: selectedPos.value[0].posTypeId, //
posTypeName: selectedPos.value[0].posTypeName, //
posLevelId: selectedPos.value[0].posLevelId, //
posLevelName: selectedPos.value[0].posLevelName, //
reportingDate: datePos.value,
posmasterId: dataPosMaster.id,
typeCommand: type.value,
};
props.onSubmit?.(body);
});
}
}
</script>
<template>
<q-dialog v-model="modal" persistent full-width>
<q-card class="no-scroll">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader :tittle="title" :close="close" />
<q-separator />
<q-card-section style="max-height: 80vh" class="scroll">
<div class="row">
<q-card
bordered
class="col-12 col-sm-3 scroll q-pa-sm"
style="height: 75vh"
>
<q-toolbar style="padding: 0">
<q-toolbar-title class="text-subtitle2 text-bold"
>เลอกหนวยงาน/วนราชการ</q-toolbar-title
>
</q-toolbar>
<q-input dense outlined v-model="filter" label="ค้นหา">
<template v-slot:append>
<q-icon
v-if="filter !== ''"
name="clear"
class="cursor-pointer"
@click="filter = ''"
/>
</template>
</q-input>
<q-tree
class="q-pa-sm q-gutter-sm"
dense
:nodes="lazy"
node-key="orgTreeId"
label-key="orgTreeName"
:filter="filter"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
v-model:expanded="expanded"
>
<template v-slot:default-header="prop">
<q-item
@click.stop="updateSelected(prop.node)"
clickable
:active="nodeId == prop.node.orgTreeId"
active-class="my-list-link text-primary text-weight-medium"
class="row col-12 items-center text-dark q-py-xs q-pl-sm rounded-borders my-list"
>
<div>
<div class="text-weight-medium">
{{ prop.node.orgTreeName }}
</div>
<div class="text-weight-light">
{{
prop.node.orgCode == null ? null : prop.node.orgCode
}}
{{
prop.node.orgTreeShortName == null
? null
: prop.node.orgTreeShortName
}}
</div>
</div>
</q-item>
</template>
</q-tree>
</q-card>
<q-card bordered class="col-12 col-sm-9 q-pa-sm">
<q-tab-panels
v-model="nodeId"
animated
transition-prev="jump-up"
transition-next="jump-up"
>
<q-tab-panel
v-for="(item, index) in itemTaps"
:key="index"
:name="item"
>
<div class="column q-col-gutter-sm" style="height: 70vh">
<!-- เลอกเลขทตำแหน -->
<div class="col-7">
<q-card
bordered
style="height: 100%; border: 1px solid #d6dee1"
>
<div
class="col-12 text-weight-medium bg-grey-1 q-py-sm q-px-md"
>
เลอกเลขทตำแหน
</div>
<div class="col-12"><q-separator /></div>
<div class="col-12 q-pa-md">
<q-toolbar style="padding: 0px">
<div class="row q-gutter-md">
<q-checkbox
keep-color
v-model="isBlank"
label="แสดงเฉพาะตำแหน่งว่าง"
color="primary"
>
<q-tooltip>แสดงเฉพาะตำแหนงวาง </q-tooltip>
</q-checkbox>
</div>
<q-space />
<div class="row q-gutter-md">
<q-checkbox
keep-color
v-model="isAll"
label="แสดงตำแหน่งทั้งหมด"
color="primary"
>
<q-tooltip
>แสดงตำแหนงทงหมดภายใตหนวยงาน/วนราชการทเลอก</q-tooltip
>
</q-checkbox>
<div>
<q-input
outlined
dense
v-model="filters"
label="ค้นหา"
>
<template v-slot:append>
<q-icon name="search" color="grey-5" />
</template>
</q-input>
</div>
<div>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
emit-value
:display-value="$q.lang.table.columns"
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 180px"
/>
</div>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="positionData"
:filter="filters"
row-key="id"
flat
bordered
:paging="true"
dense
:rows-per-page-options="[10, 25, 50, 100]"
class="tableTb"
: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"
>
<span class="text-weight-medium">{{
col.label
}}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
@click="onClickSelectPos(props.row.id)"
:class="
props.row.id === positionId
? 'bg-blue-2'
: ''
"
>
<div v-if="col.name == 'no'">
{{ props.rowIndex + 1 }}
</div>
<div v-else-if="col.name === 'posMasterNo'">
{{
props.row.isSit
? col.value + " " + "(ทับที่)"
: col.value
}}
</div>
<div v-else-if="col.name === 'isPosition'">
<div v-if="col.value">
<q-icon
name="done"
color="primary"
size="24px"
>
<q-tooltip>ตรงตามตำแหน </q-tooltip>
</q-icon>
</div>
</div>
<div v-else>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</div>
</q-card>
</div>
<!-- เลอกตำแหน -->
<div class="col-5">
<q-card
bordered
style="height: 100%; border: 1px solid #d6dee1"
>
<div
class="col-12 text-weight-medium bg-grey-1 q-py-sm q-px-md"
>
เลอกตำแหน
</div>
<div class="col-12"><q-separator /></div>
<q-tab-panels
v-model="positionId"
animated
swipeable
vertical
transition-prev="jump-up"
transition-next="jump-up"
>
<q-tab-panel
v-for="(item, index) in positionData"
:key="index"
:name="item.id"
>
<div class="col-12">
<q-toolbar style="padding: 0px">
<datepicker
menu-class-name="modalfix"
v-model="datePos"
:locale="'th'"
autoApply
borderless
:enableTimePicker="false"
week-start="0"
:min-date="datePos"
>
<template #year="{ year }">
{{ year + 543 }}
</template>
<template #year-overlay-value="{ value }">
{{ parseInt(value + 543) }}
</template>
<template #trigger>
<q-input
ref="dateRef"
outlined
dense
hide-bottom-space
:model-value="
datePos != null
? date2Thai(datePos)
: null
"
label="วันที่รายงานตัว"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</q-toolbar>
<d-table
ref="table"
:columns="columnsPostition"
:rows="rowsPosition"
row-key="id"
flat
bordered
:paging="true"
dense
:rows-per-page-options="[10, 25, 50, 100]"
class="tableTb"
selection="single"
v-model:selected="selectedPos"
>
<template v-slot:header-selection="scope">
<q-checkbox
keep-color
color="primary"
dense
v-model="scope.checkBox"
/>
</template>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width />
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
>
<span class="text-weight-medium">{{
col.label
}}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td>
<q-checkbox
keep-color
color="primary"
dense
v-model="props.selected"
/>
</q-td>
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
>
<div v-if="col.name == 'no'">
{{ props.rowIndex + 1 }}
</div>
<div
v-else-if="col.name === 'posMasterNo'"
>
{{
props.row.isSit
? col.value + " " + "(ทับที่)"
: col.value
}}
</div>
<div v-else>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</div>
</q-tab-panel>
</q-tab-panels>
</q-card>
</div>
</div>
</q-tab-panel>
</q-tab-panels>
</q-card>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="bg-white text-teal">
<q-btn label="บันทึก" color="secondary" type="submit"
><q-tooltip>นทกขอม</q-tooltip></q-btn
>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>
<style scoped>
.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

@ -0,0 +1,577 @@
<script setup lang="ts">
import DialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar } from "quasar";
import { ref, watch, reactive } from "vue";
import type { QInput, QTableProps } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import type {
OrgTree,
DataTree,
Positions,
DataPositionNo,
FormActive,
TreeMain,
} from "@/interface/request/orgSelect/org";
const rows = ref<any[]>([]);
const filterRef = ref<QInput>();
const filterModal = ref<string>("");
const props = defineProps({
dataRows: {
type: Object,
require: true,
},
onSubmit: Function,
saveData: Function,
});
const $q = useQuasar();
const mixin = useCounterMixin();
const {
dialogConfirm,
showLoader,
messageError,
hideLoader,
date2Thai,
dialogMessageNotify,
} = mixin;
const modal = defineModel<boolean>("modal", { required: true });
const title = defineModel<string>("title", { required: true });
const selectedModal = defineModel<any[]>("selectedModal");
const filter = ref<string>("");
const isAll = ref<boolean>(false);
const isBlank = ref<boolean>(false);
const positionNo = ref<DataPositionNo[]>();
const itemTaps = ref<string[]>();
const positionId = ref<string>("");
const selectedPos = ref<any[]>([]);
const seletcId = ref<string>("");
const datePos = ref<Date>(new Date());
const rowsPosition = ref<Positions[]>([]);
const positionData = ref<any>();
/** active form */
const formActive = reactive<FormActive>({
activeId: "",
activeName: "",
draftId: "",
draftName: "",
orgPublishDate: null,
isPublic: false,
});
/** node */
const nodes = ref<Array<OrgTree>>([]);
const lazy = ref(nodes);
const expanded = ref<string[]>([]);
const nodeLevel = ref<number>(0);
const nodeId = ref<string>(""); // id Tree
const columns = ref<QTableProps["columns"]>([
{
name: "no",
align: "left",
label: "ลำดับ",
field: "no",
sortable: true,
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "citizenId",
align: "left",
label: "เลขประจำตัวประชาชน",
field: "citizenId",
sortable: true,
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "name",
align: "left",
label: "ชื่อ-นามสกุล",
field: "name",
sortable: true,
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "position",
align: "left",
label: "ตำแหน่ง",
field: "position",
sortable: true,
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const visibleColumns = ref<String[]>(["no", "citizenId", "name", "position"]);
/** ปิด dialog */
function close() {
modal.value = false;
filter.value = "";
isAll.value = false;
isBlank.value = false;
nodes.value = [];
expanded.value = [];
nodeLevel.value = 0;
nodeId.value = "";
rows.value = [];
selectedModal.value = [];
}
async function getDataTable(id: string, level: number = 0) {
//new
showLoader();
http
.post(config.API.orgDeceasedProfile, {
nodeId: id,
node: level,
isAll: isAll.value,
})
.then((res) => {
const data = res.data.result.data;
rows.value = data;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
//old
// showLoader();
// const body = {
// node: level,
// nodeId: id,
// typeCommand: type.value,
// position: posType.value ? posType.value : "",
// posLevel: posLevel.value ? posLevel.value : "",
// posType: position.value ? position.value : "",
// isAll: isAll.value,
// isBlank: isBlank.value,
// };
// await http
// .post(config.API.orgPosPlacement, body)
// .then((res) => {
// const dataMain: PositionMain[] = [];
// posMasterMain.value = res.data.result.data;
// res.data.result.data.forEach((e: PositionNo) => {
// const p = e.positions;
// if (p.length !== 0) {
// const a = p.find((el: Positions) => el.positionIsSelected === true);
// const { id, ...rest } = a ? a : p[0];
// const data: any = { ...e, ...rest };
// dataMain.push(data);
// }
// });
// const listPosNo: DataPositionNo[] = dataMain.map((e: PositionMain) => ({
// id: e.id,
// isPosition: e.isPosition,
// posMasterNo:
// e.orgShortname +
// (e.posMasterNoPrefix != null ? e.posMasterNoPrefix : "") +
// e.posMasterNo +
// (e.posMasterNoSuffix != null ? e.posMasterNoSuffix : ""),
// positionName: e.positionName,
// posTypeName: e.posTypeName,
// posLevelName: e.posLevelName,
// positionIsSelected: e.positionIsSelected
// ? e.fullNameCurrentHolder
// : "-",
// isSit: e.isSit,
// positions: e.positions,
// node: e.node,
// nodeId: e.nodeId,
// }));
// positionNo.value = listPosNo;
// positionData.value = listPosNo;
// })
// .catch((err) => {
// messageError($q, err);
// })
// .finally(() => {
// setTimeout(() => {
// hideLoader();
// }, 1000);
// });
}
/**
* funtion เลอกขอม Tree
* @param data อม Tree
*/
function updateSelected(data: DataTree) {
positionId.value = "";
seletcId.value = "";
selectedPos.value = [];
datePos.value = new Date();
nodeId.value = data.orgTreeId ? data.orgTreeId : "";
nodeLevel.value = data.orgLevel;
getDataTable(data.orgTreeId, data.orgLevel);
}
/** ดึงข้อมูล active */
async function getActive() {
showLoader();
await http
.get(config.API.activeOrganization)
.then((res) => {
const data = res.data.result;
formActive.activeId = data.activeId;
formActive.activeName = data.activeName;
formActive.draftId = data.draftId;
formActive.draftName = data.draftName;
formActive.orgPublishDate = data.orgPublishDate;
formActive.isPublic = data.isPublic;
getTreeData(data.activeId);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/** ดึงข้อมูล หน่วยงาน */
async function getTreeData(id: string) {
showLoader();
await http
.get(config.API.orgByid(id))
.then((res) => {
const data = res.data.result;
nodes.value = data;
filterItemsTaps(data);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function filterItemsTaps(data: TreeMain[]) {
let orgTreeIds: string[] = [];
for (const child of data) {
orgTreeIds.push(child.orgTreeId);
if (child.children) {
orgTreeIds = orgTreeIds.concat(filterItemsTaps(child.children));
}
}
itemTaps.value = orgTreeIds;
return orgTreeIds;
}
/**
* function เลอกเลขทตำแหน
* @param id เลชทตำแหน
*/
async function onClickSelectPos(id: string) {
positionData.value = positionNo.value;
positionId.value = id;
selectedPos.value = [];
const position: DataPositionNo = positionData.value.find(
(e: DataPositionNo) => e.id === id
);
//
if (position) {
rowsPosition.value = position.positions;
if (seletcId.value) {
selectedPos.value = rowsPosition.value.filter(
(e) => e.id === seletcId.value
);
}
}
}
/**
* function fetch อม expanded tree
* @param level levelTree
* @param id treeId
*/
async function fetchPosFind(level: number, id: string) {
showLoader();
const body = {
node: level,
nodeId: id,
};
await http
.post(config.API.orgPosFind, body)
.then((res) => {
const data = res.data.result;
expanded.value = data;
nodeId.value = id;
positionId.value = props?.dataRows?.posmasterId;
seletcId.value = props?.dataRows?.positionId;
datePos.value = props?.dataRows?.reportingDateFullDate;
getDataTable(nodeId.value, level);
})
.catch((e) => {
messageError($q, e);
hideLoader();
});
}
watch(
() => modal.value,
async (n) => {
if (n == true) {
getActive();
}
}
);
watch(
() => isAll.value,
(value, oldVal) => {
if (value !== oldVal) {
getDataTable(nodeId.value, nodeLevel.value);
}
}
);
watch(
() => isBlank.value,
(value, oldVal) => {
if (value !== oldVal) {
getDataTable(nodeId.value, nodeLevel.value);
}
}
);
watch(
() => positionId.value,
(n) => {
if (n) {
onClickSelectPos(n);
}
}
);
function onSubmit() {
if (nodeId.value == "") {
dialogMessageNotify($q, "กรุณาเลือกตำแหน่ง");
} else {
dialogConfirm($q, async () => {
props.saveData?.();
close();
});
}
}
function resetFilter() {
filter.value = "";
filterRef.value!.focus();
}
</script>
<template>
<q-dialog v-model="modal" persistent full-width>
<q-card class="no-scroll">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader :tittle="title" :close="close" />
<q-separator />
<q-card-section style="max-height: 80vh" class="scroll">
<div class="row">
<q-card
bordered
class="col-12 col-sm-3 scroll q-pa-sm"
style="height: 75vh"
>
<q-input dense outlined v-model="filter" label="ค้นหา">
<template v-slot:append>
<q-icon
v-if="filter !== ''"
name="clear"
class="cursor-pointer"
@click="filter = ''"
/>
</template>
</q-input>
<q-tree
class="q-pa-sm q-gutter-sm"
dense
:nodes="lazy"
node-key="orgTreeId"
label-key="orgTreeName"
:filter="filter"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
v-model:expanded="expanded"
>
<template v-slot:default-header="prop">
<q-item
@click.stop="updateSelected(prop.node)"
clickable
:active="nodeId == prop.node.orgTreeId"
active-class="my-list-link text-primary text-weight-medium"
class="row col-12 items-center text-dark q-py-xs q-pl-sm rounded-borders my-list"
>
<div>
<div class="text-weight-medium">
{{ prop.node.orgTreeName }}
</div>
<div class="text-weight-light">
{{
prop.node.orgCode == null ? null : prop.node.orgCode
}}
{{
prop.node.orgTreeShortName == null
? null
: prop.node.orgTreeShortName
}}
</div>
</div>
</q-item>
</template>
</q-tree>
</q-card>
<q-card bordered class="col-12 col-sm-9 q-pa-sm">
<div class="col-12 row q-py-sm items-center q-col-gutter-sm">
<q-space />
<div class="items-center" style="display: flex">
<q-checkbox
keep-color
v-model="isAll"
label="แสดงทั้งหมด"
color="primary"
>
<q-tooltip>แสดงทงหมด</q-tooltip>
</q-checkbox>
<q-input
standout
dense
v-model="filterModal"
ref="filterRef"
outlined
debounce="300"
placeholder="ค้นหา"
style="max-width: 200px"
class="q-ml-sm"
>
<template v-slot:append>
<q-icon v-if="filterModal == ''" name="search" />
<q-icon
v-if="filterModal !== ''"
name="clear"
class="cursor-pointer"
@click="resetFilter"
/>
</template>
</q-input>
<q-select
v-model="visibleColumns"
:display-value="$q.lang.table.columns"
multiple
outlined
dense
:options="columns"
options-dense
option-value="name"
map-options
emit-value
style="min-width: 150px"
class="gt-xs q-ml-sm"
/>
</div>
<div class="col-12">
<d-table
:rows="rows"
:columns="columns"
:visible-columns="visibleColumns"
:filter="filterModal"
row-key="id"
selection="multiple"
v-model:selected="selectedModal"
:rows-per-page-options="[10, 25, 50, 100]"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width>
<q-checkbox
keep-color
color="primary"
dense
v-model="props.selected"
/>
</q-th>
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
style="color: #000000; font-weight: 500"
>
<span class="text-weight-medium">{{
col.label
}}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td auto-width>
<q-checkbox
keep-color
color="primary"
dense
v-model="props.selected"
/>
</q-td>
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
>
<div v-if="col.name == 'no'">
{{ props.rowIndex + 1 }}
</div>
<div v-else>
{{ col.value }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</div>
</div>
</q-card>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="bg-white text-teal">
<q-btn label="บันทึก" color="secondary" type="submit"
><q-tooltip>นทกขอม</q-tooltip></q-btn
>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>
<style scoped>
.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

@ -0,0 +1,39 @@
<script setup lang="ts">
import DialogHeader from "@/components/DialogHeader.vue";
const props = defineProps({
modal: {
type: Boolean,
default: false,
},
title: {
type: String,
default: "",
},
desc: {
type: String,
default: "",
},
clickClose: {
type: Function
}
});
</script>
<template>
<q-dialog v-model="props.modal" persistent>
<q-card style="width: 35vw; max-width: 35vw">
<q-form ref="myForm">
<DialogHeader :tittle="props.title" :close="clickClose" />
<q-separator />
<q-card-section class="q-pa-md bg-grey-1">
<div class="row col-12">
{{ props.desc }}
</div>
</q-card-section>
</q-form>
</q-card>
</q-dialog>
</template>

View file

@ -0,0 +1,406 @@
<script setup lang="ts">
import { ref, reactive, watch } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
/** importType*/
import type { PersonalImformation } from "@/components/information/interface/response/Information";
import type { Goverment } from "@/components/information/interface/response/Government";
import type { Avatar } from "@/components/information/interface/response/avatar";
/** importStore*/
import { useCounterMixin } from "@/stores/mixin";
/** use*/
const mixin = useCounterMixin();
const $q = useQuasar();
const { showLoader, hideLoader, messageError, date2Thai } = mixin;
/** props*/
const props = defineProps({
id: {
type: String,
requier: true,
},
modal: {
type: Boolean,
requier: true,
},
});
/** emit*/
const emit = defineEmits(["update:modal"]);
/** interface*/
interface StatusLoad {
val: boolean;
val2: boolean;
val3: boolean;
}
/** ตัวแปร*/
const modal = ref<boolean>(false);
const statusLoad = ref<StatusLoad>({ val: false, val2: false, val3: false }); //
const avatar = reactive<Avatar>({
avatar: "",
fullname: "",
position: "",
});
const imformation = reactive<PersonalImformation>({
prefix: "",
citizenId: "",
firstName: "",
lastName: "",
birthDate: "",
age: "",
gender: "",
});
const goverment = reactive<Goverment>({
oc: "",
posNo: "",
position: "",
positionPathSide: "",
positionLine: "",
positionType: "",
positionLevel: "",
positionExecutive: "",
positionExecutiveSide: "",
});
/**
* function เรยกขอมลรปภาพ
* @param id profileID
*/
async function fetchAvatar(id: string) {
showLoader();
await http
.get(config.API.profileAvatarId(id))
.then((res) => {
const data = res.data.result;
avatar.avatar = data.avatar;
avatar.fullname = data.fullname;
avatar.position = data.position ?? "-";
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
statusLoad.value.val = true;
loaderFunction();
});
}
/**
* function เรยกขอมลสวนต
* @param id profileID
*/
async function fetchInformation(id: string) {
showLoader();
await http
.get(config.API.profileInforId(id))
.then((res) => {
const data = res.data.result;
imformation.prefix = data.prefix;
imformation.citizenId = data.citizenId;
imformation.firstName = data.firstName;
imformation.lastName = data.lastName;
imformation.birthDate = data.birthDate ? date2Thai(data.birthDate) : "";
imformation.age = data.age;
imformation.gender = data.gender;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
statusLoad.value.val2 = true;
loaderFunction();
});
}
/**
* function เรยกขอมลขอมลราชการ
* @param id profileID
*/
async function fetchProfileGov(id: string) {
showLoader();
await http
.get(config.API.profileGovId(id))
.then((res) => {
const data = res.data.result;
goverment.oc = data.oc ?? "-";
goverment.posNo = data.posNo ?? "-";
goverment.position = data.position ?? "-";
goverment.positionPathSide = data.positionPathSide ?? "-";
goverment.positionLine = data.positionLine ?? "-";
goverment.positionType = data.positionType ?? "-";
goverment.positionLevel = data.positionLevel ?? "-";
goverment.positionExecutive = data.positionExecutive ?? "-";
goverment.positionExecutiveSide = data.positionExecutiveSide ?? "-";
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
statusLoad.value.val3 = true;
loaderFunction();
});
}
/** functoion เช็คการโหลดของข้อมูล*/
function loaderFunction() {
const allTrue = Object.values(statusLoad.value).every((val) => val);
allTrue && hideLoader();
}
function redirecToRegistry() {
window.open(`/registry/${props.id}`, "_blank");
modal.value = false;
}
watch(
() => props.modal,
async () => {
modal.value = props.modal ? props.modal : false;
modal.value &&
props.id &&
(await fetchAvatar(props.id),
await fetchInformation(props.id),
await fetchProfileGov(props.id));
}
);
watch(modal, (newValue) => {
if (!newValue) {
emit("update:modal", false);
}
});
</script>
<template>
<q-dialog v-model="modal" position="right" :maximized="true">
<q-card style="width: 420px; overflow: visible">
<q-toolbar>
<q-toolbar-title class="text-subtitle1 text-bold"
>ทะเบยนประว</q-toolbar-title
>
<q-btn
icon="close"
unelevated
round
dense
@click="emit('update:modal', false)"
style="color: red; background-color: #ffdede"
/>
</q-toolbar>
<!-- <q-card-section>
<div class="text-bold text-h6 text-center">อมลทะเบยนประว</div>
<q-space />
</q-card-section> -->
<q-card-section class="col q-pt-none bg-grey-12">
<div class="q-gutter-md">
<q-card bordered class="text-center bg-grey-12">
<div>
<q-avatar size="120px" color="grey-4">
<img
v-if="avatar.avatar"
:src="avatar.avatar"
class="bg-grey-3"
style="object-fit: cover"
/>
<img
v-else
src="@/assets/avatar_user.jpg"
class="bg-grey-3"
style="object-fit: cover"
/>
</q-avatar>
</div>
<div
class="q-mt-md text-subtitle2 text-bold"
style="font-size: 18px"
>
{{ avatar.fullname }}
</div>
<div
v-if="avatar.position != '-'"
class="q-mb-xs text-center text-grey"
>
{{ avatar.position }}
</div>
<div class="q-mt-md">
<q-btn
class="bg-white"
outline
rounded
label="ดูรายละเอียดเพิ่มเติมทั้งหมด"
color="secondary"
@click.prevent="redirecToRegistry"
/>
</div>
</q-card>
<q-scroll-area style="height: 65vh; max-width: 100%">
<div class="q-gutter-md q-pa-sm">
<q-card bordered style="border: 1px solid #d6dee1">
<div class="q-pa-md">
<div class="text-weight-bold row items-center">
<q-icon name="mdi-account" color="grey-7" />
<span class="q-ml-md">อมลสวนต </span>
</div>
<div class="row q-pa-sm">
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="imformation.citizenId"
label="เลขประจำตัวประชาชน"
></q-input>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="imformation.prefix"
label="คำนำหน้าชื่อ"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="imformation.firstName"
label="ชื่่อ"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="imformation.lastName"
label="นามสกุล"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="imformation.birthDate"
label="วัน/เดือน/ปีเกิด"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="imformation.gender"
label="เพศ"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="imformation.age"
label="อายุ"
/>
</div>
</div>
</div>
</q-card>
<q-card bordered style="border: 1px solid #d6dee1">
<div class="q-pa-md">
<div class="text-weight-bold row items-center">
<q-icon name="mdi-account-tie" color="grey-7" />
<span class="q-ml-md">อมลราชการ </span>
</div>
<div class="row q-pa-sm">
<div class="col-xs-12 col-md-12">
<q-input
borderless
readonly
:model-value="goverment.oc === '' ? '-' : goverment.oc"
label="สังกัด"
autogrow
></q-input>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="goverment.posNo"
label="ตำแหน่งเลขที่"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="goverment.position"
label="ตำแหน่ง"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="goverment.positionPathSide"
label="ด้าน/สาขา"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="goverment.positionLine"
label="สายงาน"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="goverment.positionType"
label="ประเภทตำแหน่ง"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="goverment.positionLevel"
label="ระดับตำแหน่ง"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="goverment.positionExecutive"
label="ตำแหน่งทางการบริหาร"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="goverment.positionExecutiveSide"
label="ด้านตำแหน่งทางการบริหาร"
/>
</div>
</div>
</div>
</q-card>
</div>
</q-scroll-area>
</div>
</q-card-section>
</q-card>
</q-dialog>
</template>
<style scoped></style>

View file

@ -0,0 +1,420 @@
<script setup lang="ts">
import { ref, reactive, watch, onMounted } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import { useRouter, useRoute } from "vue-router";
/** importType*/
import type { PersonalImformation } from "@/components/information/interface/response/Information";
import type { Goverment } from "@/components/information/interface/response/Government";
import type { Avatar } from "@/components/information/interface/response/avatar";
/** importStore*/
import { useCounterMixin } from "@/stores/mixin";
/** use*/
const route = useRoute();
const mixin = useCounterMixin();
const router = useRouter();
const $q = useQuasar();
const retireDate = ref<Date>();
const { showLoader, hideLoader, messageError, date2Thai } = mixin;
const empType = ref<string>(
route.name !== "appoint-employee-detail" ? "" : "-employee"
);
/** props*/
const props = defineProps({
id: {
type: String,
requier: true,
},
modal: {
type: Boolean,
requier: true,
},
type: { type: String, default: "" },
});
/** emit*/
const emit = defineEmits(["update:modal"]);
/** ตัวแปร*/
const modal = ref<boolean>(false);
const avatar = reactive<Avatar>({
avatar: "",
fullname: "",
position: "",
});
const imformation = reactive<PersonalImformation>({
prefix: "",
citizenId: "",
firstName: "",
lastName: "",
birthDate: "",
age: "",
gender: "",
});
const goverment = reactive<Goverment>({
oc: "",
posNo: "",
position: "",
positionPathSide: "",
positionLine: "",
positionType: "",
positionLevel: "",
positionExecutive: "",
positionExecutiveSide: "",
});
function calculateAge(birthDate: Date | null) {
if (!birthDate) return null;
const birthDateTimeStamp = new Date(birthDate).getTime();
const now = new Date();
const diff = now.getTime() - birthDateTimeStamp;
const ageDate = new Date(diff);
const years = ageDate.getUTCFullYear() - 1970;
const months = ageDate.getUTCMonth();
const days = ageDate.getUTCDate() - 1;
const retire = new Date(birthDate);
retire.setFullYear(retire.getFullYear() + 60);
retireDate.value = retire;
if (years > 60) {
return "อายุเกิน 60 ปี";
}
return `${years} ปี ${months} เดือน ${days} วัน`;
}
/**
* function เรยกขอมลสวนต
* @param id profileID
*/
async function fetchInformation(id: string) {
showLoader();
await http
.get(config.API.orgProfileById(id, empType.value))
.then((res) => {
const data = res.data.result;
imformation.prefix = data.prefix ? data.prefix : "-";
imformation.citizenId = data.citizenId ? data.citizenId : "-";
imformation.firstName = data.firstName ? data.firstName : "-";
imformation.lastName = data.lastName ? data.lastName : "-";
imformation.birthDate = data.birthDate ? date2Thai(data.birthDate) : "-";
imformation.age = data.birthDate ? calculateAge(data.birthDate) : "-";
imformation.gender = data.gender ?? "-";
avatar.fullname = `${data.prefix}${data.firstName} ${data.lastName}`;
avatar.position = data.position ? data.position : "-";
if (data.avatarName) {
fetchProfile(data.id as string, data.avatarName);
}
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/**
* function เรยกขอมลขอมลราชการ
* @param id profileID
*/
async function fetchProfileGov(id: string) {
showLoader();
await http
.get(config.API.profileNewGovernmentById(id, empType.value))
.then((res) => {
const data = res.data.result;
goverment.oc = data.org !== "" ? data.org : "-";
goverment.posNo = data.posMasterNo !== "" ? data.posMasterNo : "-";
goverment.position = data.position !== "" ? data.position : "-";
goverment.positionPathSide =
data.positionArea !== "" ? data.positionArea : "-";
goverment.positionLine =
data.positionField !== "" ? data.positionField : "-";
goverment.positionType = data.posType !== "" ? data.posType : "-";
goverment.positionLevel = data.posLevel !== "" ? data.posLevel : "-";
goverment.positionExecutive =
data.posExecutive !== null ? data.posExecutive : "-";
goverment.positionExecutiveSide =
data.positionExecutiveField !== "" ? data.positionExecutiveField : "-";
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function redirecToRegistry() {
router.push(`/registry-new${empType.value}/${props.id}`);
modal.value = false;
}
watch(
() => props.modal,
async () => {
modal.value = props.modal ? props.modal : false;
if (modal.value) {
if (props.id) {
fetchInformation(props.id);
fetchProfileGov(props.id);
}
}
}
);
watch(modal, (newValue) => {
if (!newValue) {
emit("update:modal", false);
}
});
async function fetchProfile(id: string, avatarName: string) {
showLoader();
await http
.get(config.API.fileByFile("ทะเบียนประวัติ", "โปรไฟล์", id, avatarName))
.then(async (res) => {
avatar.avatar = res.data.downloadUrl;
})
.finally(() => {
hideLoader();
});
}
</script>
<template>
<q-dialog v-model="modal" position="right" :maximized="true">
<q-card style="width: 420px; overflow: visible">
<q-toolbar>
<q-toolbar-title class="text-subtitle1 text-bold"
>ทะเบยนประว</q-toolbar-title
>
<q-btn
icon="close"
unelevated
round
dense
@click="emit('update:modal', false)"
style="color: red; background-color: #ffdede"
/>
</q-toolbar>
<q-card-section class="col q-pt-none bg-grey-12">
<div class="q-gutter-md">
<q-card bordered class="text-center bg-grey-12">
<div>
<q-avatar size="120px" color="grey-4">
<img
v-if="avatar.avatar"
:src="avatar.avatar"
class="bg-grey-3"
style="object-fit: cover"
/>
<img
v-else
src="@/assets/avatar_user.jpg"
class="bg-grey-3"
style="object-fit: cover"
/>
</q-avatar>
</div>
<div
class="q-mt-md text-subtitle2 text-bold"
style="font-size: 18px"
>
{{ avatar.fullname }}
</div>
<div
v-if="avatar.position != '-'"
class="q-mb-xs text-center text-grey"
>
{{ avatar.position }}
</div>
<div class="q-mt-md">
<q-btn
class="bg-white"
outline
rounded
label="ดูรายละเอียดเพิ่มเติมทั้งหมด"
color="secondary"
@click.prevent="redirecToRegistry"
/>
</div>
</q-card>
<q-scroll-area style="height: 65vh; max-width: 100%">
<div class="q-gutter-md q-pa-sm">
<q-card bordered style="border: 1px solid #d6dee1">
<div class="q-pa-md">
<div class="text-weight-bold row items-center">
<q-icon name="mdi-account" color="grey-7" />
<span class="q-ml-md">อมลสวนต </span>
</div>
<div class="row q-pa-sm">
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="imformation.citizenId"
label="เลขประจำตัวประชาชน"
></q-input>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="imformation.prefix"
label="คำนำหน้าชื่อ"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="imformation.firstName"
label="ชื่่อ"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="imformation.lastName"
label="นามสกุล"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="imformation.birthDate"
label="วัน/เดือน/ปีเกิด"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="imformation.gender"
label="เพศ"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="imformation.age"
label="อายุ"
/>
</div>
</div>
</div>
</q-card>
<q-card bordered style="border: 1px solid #d6dee1">
<div class="q-pa-md">
<div class="text-weight-bold row items-center">
<q-icon name="mdi-account-tie" color="grey-7" />
<span class="q-ml-md">อมลราชการ </span>
</div>
<div class="row q-pa-sm">
<div class="col-xs-12 col-md-12">
<q-input
borderless
readonly
:model-value="goverment.oc === '' ? '-' : goverment.oc"
label="สังกัด"
autogrow
></q-input>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="goverment.posNo"
label="ตำแหน่งเลขที่"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="goverment.position"
label="ตำแหน่ง"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="goverment.positionPathSide"
label="ด้าน/สาขา"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="goverment.positionLine"
label="สายงาน"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="goverment.positionType"
label="ประเภทตำแหน่ง"
/>
</div>
<div class="col-xs-6 col-md-6">
<q-input
borderless
readonly
:model-value="goverment.positionLevel"
label="ระดับตำแหน่ง"
/>
</div>
<div
class="col-xs-6 col-md-6"
v-if="props.type !== 'employee'"
>
<q-input
borderless
readonly
:model-value="goverment.positionExecutive"
label="ตำแหน่งทางการบริหาร"
/>
</div>
<div
class="col-xs-6 col-md-6"
v-if="props.type !== 'employee'"
>
<q-input
borderless
readonly
:model-value="goverment.positionExecutiveSide"
label="ด้านตำแหน่งทางการบริหาร"
/>
</div>
</div>
</div>
</q-card>
</div>
</q-scroll-area>
</div>
</q-card-section>
</q-card>
</q-dialog>
</template>
<style scoped></style>

View file

@ -0,0 +1,96 @@
<script setup lang="ts">
import { ref, watch } from "vue";
import DialogHeader from "@/components/DialogHeader.vue";
const reason = ref<string | undefined>("");
const modal = defineModel<boolean>("modal", { required: true });
const props = defineProps({
// modal: {
// type: Boolean,
// default: false,
// },
title: {
type: String,
default: "",
},
label: {
type: String,
default: "",
},
// clickClose: {
// type: Function,
// default: () => {},
// },
savaForm: {
type: Function,
default: () => {},
},
textReport: {
type: String,
},
});
watch(props, () => {
if (modal.value === true && props.textReport == "") {
reason.value = "";
} else {
reason.value = props.textReport;
}
});
const myForm = ref<any>();
const submit = () => {
myForm.value.validate().then((result: boolean) => {
if (result) {
props.savaForm(reason.value);
}
});
};
function closeModal() {
modal.value = false
}
</script>
<template>
<q-dialog v-model="modal" persistent>
<q-card style="width: 40vw; max-width: 40vw">
<q-form ref="myForm">
<DialogHeader :tittle="props.title" :close="closeModal" />
<q-separator />
<q-card-section class="q-pa-sm bg-grey-1">
<div class="row col-12 q-col-gutter-sm">
<div class="col-xs-12">
<div class="col-12 row q-py-sm items-center q-col-gutter-sm">
<q-input
type="textarea"
class="full-width inputgreen cursor-pointer"
hide-bottom-space
outlined
dense
lazy-rules
:rules="[(val) => !!val || `กรุณากรอก${label}`]"
v-model="reason"
:label="`${label}`"
/>
</div>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn
dense
unelevated
label="บันทึก"
color="public"
@click="submit"
class="q-px-md"
>
<!-- icon="mdi-content-save-outline" -->
<q-tooltip>นท</q-tooltip>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>

View file

@ -0,0 +1,111 @@
<script setup lang="ts">
import { ref, watch } from "vue";
import { useCounterMixin } from "@/stores/mixin";
import http from "@/plugins/http";
import config from "@/app.config";
import DialogHeader from "@/components/DialogHeader.vue";
import { useQuasar } from "quasar";
const mixin = useCounterMixin(); //
const $q = useQuasar();
const { showLoader, hideLoader, success, messageError } = mixin;
const myForm = ref<any>();
const props = defineProps({
modal: {
type: Boolean,
default: false,
},
idInbox: {
type: String,
default: "",
},
clickClose: {
type: Function,
default: () => {},
},
});
const subject = ref<string>("");
const body = ref<string>("");
async function submit() {
myForm.value.validate().then(async (result: boolean) => {
if (result) {
// props.savaForm(reason.value);
showLoader();
await http
.put(config.API.replyMessage(props.idInbox), {
subject: subject.value,
body: body.value,
})
.then((res) => {
props.clickClose()
success($q, "ส่งข้อความสำเร็จ");
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
});
}
</script>
<template>
<q-dialog v-model="props.modal" persistent>
<q-card style="width: 40vw; max-width: 40vw">
<q-form ref="myForm">
<DialogHeader tittle="ส่งข้อความ" :close="clickClose" />
<q-separator />
<q-card-section class="q-pa-sm bg-grey-1">
<div class="row col-12 q-col-gutter-sm">
<div class="col-xs-12">
<div class="col-12 row q-py-sm items-center q-col-gutter-sm">
<q-input
class="full-width inputgreen cursor-pointer"
hide-bottom-space
outlined
dense
lazy-rules
:rules="[(val) => !!val || 'กรุณากรอกหัวข้อ']"
v-model="subject"
label="หัวข้อ"
/>
<q-input
type="textarea"
class="full-width inputgreen cursor-pointer"
hide-bottom-space
outlined
dense
lazy-rules
:rules="[(val) => !!val || 'กรุณากรอกข้อความ']"
v-model="body"
label="ข้อความ"
/>
</div>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn
dense
unelevated
label="ส่งข้อความ"
color="public"
@click="submit"
class="q-px-md"
>
<!-- icon="mdi-content-save-outline" -->
<q-tooltip>นท</q-tooltip>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>

View file

@ -0,0 +1,16 @@
<!-- =============================== -->
<!-- Component Show No Result Found Message -->
<!-- When filtering Drodown(q-select) -->
<!-- อความทจะแสดงเวลาคนหาขอมลในดรอบดาวนไมเจอ -->
<!-- ใชหลายท จะได css เดยว -->
<!-- =============================== -->
<template>
<q-item>
<q-item-section class="text-black"> ไมพบขอมลทนหา </q-item-section>
</q-item>
</template>
<script setup lang="ts"></script>
<style lang="scss"></style>

View file

@ -0,0 +1,24 @@
<!-- แสดง ui การโหลด -->
<template>
<q-inner-loading :showing="loaderVisibility" class="loader">
<q-spinner-cube size="80px" color="primary" />
</q-inner-loading>
</template>
<script setup lang="ts">
import { watch, ref } from "vue";
const props = defineProps({
visibility: Boolean,
});
const loaderVisibility = ref<boolean>(props.visibility);
watch(props, (count, prevCount) => {
loaderVisibility.value = props.visibility;
});
</script>
<style lang="sass">
.loader
z-index: 1000
</style>

View file

@ -0,0 +1,213 @@
<script setup lang="ts">
import DialogHeader from "@/components/DialogHeader.vue";
import { ref, useAttrs } from "vue";
import type { QTableProps } from "quasar";
const props = defineProps({
modal: {
type: Boolean,
default: false,
},
title: {
type: String,
default: "ตรวจสอบคุณสมบัติ",
},
desc: {
type: String,
default: "",
},
clickClose: {
type: Function,
default: () => {},
},
type: {
type: String,
default: "Expert",
},
});
const attrs = ref<any>(useAttrs());
const currentPage = ref<number>(1);
const pageSize = ref<number>(10);
const maxPage = ref<number>(1);
const page = ref<number>(1);
const filter = ref<string>("");
const rowsPerPage = ref<number>(10);
/**
*pagination ของตาราง
*/
const pagination = ref({
descending: false,
page: page.value,
rowsPerPage: rowsPerPage.value,
});
const rows = ref<any[]>([]);
const columns = ref<QTableProps["columns"]>([
{
name: "degree",
align: "left",
label: "คุณวุฒิ",
sortable: false,
field: "degree",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "level",
align: "center",
label: "ระดับ",
sortable: true,
field: "level",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "Expert",
align: "center",
label: "ชำนาญการ",
sortable: false,
field: "Expert",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
]);
const visibleColumns = ref<String[]>(["degree", "level", "Expert"]);
const columnsSpecial = ref<QTableProps["columns"]>([
{
name: "degree",
align: "left",
label: "คุณวุฒิ",
sortable: false,
field: "degree",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "level",
align: "center",
label: "ระดับ",
sortable: true,
field: "level",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "Special",
align: "center",
label: "ชำนาญการพิเศษ",
sortable: false,
field: "Special",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
]);
const visibleColumnsSpecial = ref<String[]>(["degree", "level", "Special"]);
</script>
<template>
<q-dialog v-model="props.modal" persistent>
<q-card style="width: 35vw; max-width: 35vw" v-if="props.type === 'EXPERT'">
<DialogHeader :tittle="props.title" :close="clickClose" />
<q-separator />
<q-card-section class="q-pa-sm bg-grey-1">
<d-table
ref="table"
:columns="columns"
:rows="rows"
:filter="filter"
row-key="interrogated"
flat
bordered
:paging="true"
dense
class="custom-header-table"
v-bind="attrs"
:visible-columns="visibleColumns"
v-model:pagination="pagination"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width></q-th>
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
style="color: #000000; font-weight: 500"
>
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
<!-- <q-th auto-width></q-th> -->
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div v-if="col.name == 'no'">
{{ props.rowIndex + 1 }}
</div>
<div v-else>
{{ col.value }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</q-card-section>
</q-card>
<q-card style="width: 35vw; max-width: 35vw" v-else>
<DialogHeader :tittle="props.title" :close="clickClose" />
<q-separator />
<q-card-section class="q-pa-sm bg-grey-1">
<d-table
ref="table"
:columns="columnsSpecial"
:rows="rows"
:filter="filter"
row-key="interrogated"
flat
bordered
:paging="true"
dense
class="custom-header-table"
v-bind="attrs"
:visible-columns="visibleColumnsSpecial"
v-model:pagination="pagination"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width></q-th>
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
style="color: #000000; font-weight: 500"
>
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
<!-- <q-th auto-width></q-th> -->
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div v-if="col.name == 'no'">
{{ props.rowIndex + 1 }}
</div>
<div v-else>
{{ col.value }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</q-card-section>
</q-card>
</q-dialog>
</template>

View file

@ -0,0 +1,19 @@
<template>
<q-select v-bind="attrs">
<template v-for="(_, slot) in slots" v-slot:[slot]="scope">
<slot :name="slot" v-bind="scope || {}" />
</template>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-black">
ไมพบขอมลทนหา
</q-item-section>
</q-item>
</template>
</q-select>
</template>
<script setup lang="ts">
import { ref, useAttrs, useSlots } from "vue";
const attrs = ref<any>(useAttrs());
const slots = ref<any>(useSlots());
</script>

90
src/components/Table.vue Normal file
View file

@ -0,0 +1,90 @@
<template>
<q-table
ref="table"
flat
bordered
class="custom-header-table"
v-bind="attrs"
virtual-scroll
:virtual-scroll-sticky-size-start="48"
dense
:pagination-label="paginationLabel"
v-model:pagination="pagination"
>
<template v-slot:pagination="scope">
งหมด {{ attrs.rows.length }} รายการ
<q-pagination
v-model="pagination.page"
active-color="primary"
color="dark"
:max="scope.pagesNumber"
:max-pages="5"
size="sm"
boundary-links
direction-links
></q-pagination>
</template>
<template v-for="(_, slot) in slots" v-slot:[slot]="scope">
<slot :name="slot" v-bind="scope || {}" />
</template>
</q-table>
</template>
<script setup lang="ts">
import { ref, useAttrs, useSlots } from "vue";
const attrs = ref<any>(useAttrs());
const slots = ref<any>(useSlots());
const props = defineProps({
paging: {
type: Boolean,
defualt: false,
},
});
const pagination = ref({
sortBy: "desc",
descending: false,
page: 1,
rowsPerPage: 10,
});
const paginationLabel = (start: string, end: string, total: string) => {
if (props.paging == true)
return " " + start + " ถึง " + end + " จากจำนวน " + total + " รายการ";
else return start + "-" + end + " ใน " + total;
};
</script>
<style lang="scss">
.icon-color {
color: #4154b3;
}
.custom-header-table {
height: auto;
.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;
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;
}
}
</style>

View file

@ -0,0 +1,227 @@
<template>
<q-dialog :model-value="modal" persistent>
<q-card style="min-width: 70vw">
<q-form ref="myForm">
<div class="row items-center q-pa-sm">
<div class="row">
<div class="text-bold">{{ tittle }}</div>
</div>
<q-space />
<q-btn
icon="close"
unelevated
round
dense
style="color: #ff8080; background-color: #ffdede"
size="12px"
@click="close"
/>
</div>
<q-separator />
<div class="q-pa-sm">
<!-- header บน table นหา แสดงคอลมน (status nornmalData true) -->
<div class="col-12 row q-pb-sm">
<!-- <q-select
hide-bottom-space
@update:model-value="updateHistory"
outlined
dense
lazy-rules
borderless
:model-value="history"
:label="`${'วันที่'}`"
emit-value
map-options
option-label="publishedDate"
:options="optionsHistory"
option-value="id"
/> -->
<q-space />
<div class="items-center" style="display: flex">
<!-- นหาขอความใน table -->
<q-input
standout
dense
:model-value="inputfilter"
ref="filterRef"
@update:model-value="updateInput"
outlined
debounce="300"
placeholder="ค้นหา"
style="max-width: 200px"
class="q-ml-sm"
>
<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 q-ml-sm"
/>
</div>
</div>
<q-table
ref="table"
flat
bordered
class="custom-header-table"
v-bind="attrs"
virtual-scroll
:virtual-scroll-sticky-size-start="48"
dense
:pagination-label="paginationLabel"
v-model:pagination="pagination"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width v-if="boss == true" />
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
<q-th auto-width v-if="history == true" />
</q-tr>
</template>
<!-- สำหรบเรยกใช template วขางนอก -->
<template #body="props">
<slot v-bind="props" name="columns"></slot>
</template>
<template v-slot:pagination="scope">
<q-pagination
v-model="pagination.page"
active-color="primary"
color="dark"
:max="scope.pagesNumber"
:max-pages="5"
size="sm"
boundary-links
direction-links
></q-pagination>
</template>
</q-table>
</div>
<q-separator />
</q-form>
</q-card>
</q-dialog>
</template>
<script setup lang="ts">
import { ref, useAttrs, computed } from "vue";
import type { Pagination } from "@/modules/01_metadata/interface/index/Main";
const rows = ref<any>([]);
const attrs = ref<any>(useAttrs());
const filterRef = ref<any>(null);
// const history = ref<string>("");
const initialPagination = ref<Pagination>({
// descending: false,
rowsPerPage: 0,
});
const pagination = ref({
sortBy: "desc",
descending: false,
page: 1,
rowsPerPage: 10,
});
const pagesNumber = computed(() => {
return Math.ceil(rows.value.length / pagination.value.rowsPerPage);
});
const props = defineProps({
tittle: String,
inputfilter: String,
// history: String,
inputvisible: Array,
modal: Boolean,
boss: {
type: Boolean,
defualt: false,
},
history: {
type: Boolean,
defualt: false,
},
});
const emit = defineEmits([
"update:inputfilter",
"update:inputvisible",
"update:modal",
// "update:history",
]);
const updateInput = (value: string | number | null) => {
emit("update:inputfilter", value);
};
const updateVisible = (value: []) => {
emit("update:inputvisible", value);
};
const close = () => {
emit("update:modal", false);
};
// const updateHistory = (value: string) => {
// emit("update:history", value);
// props.updateHistory();
// };
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-header-table {
max-height: 64vh;
.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;
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;
}
}
</style>

View file

@ -0,0 +1,570 @@
<template>
<div class="q-px-md q-pb-md">
<!-- header บน table นหา แสดงคอลมน มแกไข เพ เผยแพรอม ยกเล (status nornmalData false) -->
<div class="col-12 row q-py-sm" v-if="nornmalData == false">
<q-btn
v-if="!editvisible == true && publicNoBtn == false"
flat
round
:disabled="editvisible == true"
:color="editvisible == true ? 'grey-7' : 'primary'"
@click="edit"
icon="mdi-pencil-outline"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<!-- ยกเล แสดงเม กดปมแกไข -->
<q-btn
v-else
flat
round
:disabled="editvisible == false"
:outline="editvisible == false"
:color="editvisible == false ? 'grey-7' : 'red'"
@click="cancel()"
icon="mdi-undo"
>
<q-tooltip>ยกเล</q-tooltip>
</q-btn>
<!-- <q-separator vertical /> -->
<div class="q-px-sm">
<q-btn
flat
round
:disabled="editvisible == false"
:color="editvisible == false ? 'grey-7' : 'add'"
@click="add"
icon="mdi-plus"
>
<q-tooltip>เพมขอม</q-tooltip>
</q-btn>
<!-- นทกราง แสดงเม กดปมแกไข อมลมการเปนแปลงหร งไมเผยแพรอม -->
<q-btn
flat
round
:disabled="!(editvisible == true && updateData == true)"
:color="
!(editvisible == true && updateData == true) ? 'grey-7' : 'public'
"
@click="checkSave"
v-if="saveNoDraft == false"
icon="mdi-content-save-outline"
>
<q-tooltip>นทกราง</q-tooltip>
</q-btn>
<!-- ลบบนทกราง แสดงเม นทกรางแล -->
<q-btn
flat
round
:disabled="publicData == true"
:color="publicData == true ? 'grey-7' : 'deep-orange'"
@click="DeleteModal"
icon="mdi-file-remove-outline"
v-if="publicNoBtn == false"
>
<q-tooltip>ลบบนทกราง</q-tooltip>
</q-btn>
</div>
<!-- เผยแพร -->
<q-btn
flat
round
:disabled="!(publicData == false || updateData == true)"
:color="
!(publicData == false || updateData == true) ? 'grey-7' : 'public'
"
@click="publishModal"
icon="mdi-cloud-upload-outline"
v-if="publicNoBtn == false"
>
<q-tooltip>เผยแพร</q-tooltip>
</q-btn>
<div class="items-center" style="display: flex">
<div
class="row items-center"
style="display: flex"
v-if="publicData == false && publicNoBtn == false"
>
<div class="text-public text-body2 text-weight-medium q-px-sm">
อมลยงไมเผยแพร
</div>
</div>
</div>
<q-space />
<div class="items-center" style="display: flex">
<!-- อความสถานะเผยแพร โดยใช parameter publicData เปนตวกำหนดขอความ -->
<!-- <div class="row items-center" style="display: flex" v-if="publicData == true">
<q-icon cener size="20px" name="label_important" class="icon-color" />
<div class="text-size">อมลเผยแพรแล</div>
</div> -->
<div
class="row items-center"
style="display: flex"
v-if="publicData == false && publicNoBtn == false"
>
<!-- <q-icon cener size="20px" name="label_important" color="amber" />
<div class="text-grey-7 text-body2 text-weight-medium q-px-sm">
อมลยงไมเผยแพร
</div> -->
</div>
<!-- นหาขอความใน table -->
<q-input
standout
dense
:model-value="inputfilter"
ref="filterRef"
@update:model-value="updateInput"
outlined
debounce="300"
placeholder="ค้นหา"
style="max-width: 200px"
class="q-ml-sm"
>
<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 q-ml-sm"
/>
</div>
</div>
<!-- header บน table นหา แสดงคอลมน (status nornmalData true) -->
<div class="col-12 row q-py-sm items-center" v-if="nornmalData == true">
<span class="text-subtitle1">{{ titleText }}</span>
<!-- <q-select
:model-value="inputvisibleFilter"
:options="optionsFilter"
style="min-width: 150px"
class="gt-xs q-ml-sm"
/> -->
<q-select
dense
outlined
:model-value="inputvisibleFilter"
:options="optionsFilter"
class="col-xs-12 col-sm-4 col-md-3"
option-value="id"
option-label="name"
map-options
emit-value
@update:model-value="updateVisibleFilter"
v-if="optionsFilter != undefined && optionsFilter.length > 0"
/>
<q-space />
<div class="items-center" style="display: flex">
<!-- นหาขอความใน table -->
<q-input
standout
dense
:model-value="inputfilter"
ref="filterRef"
@update:model-value="updateInput"
outlined
debounce="300"
placeholder="ค้นหา"
style="max-width: 200px"
class="q-ml-sm"
>
<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 q-ml-sm"
/>
</div>
</div>
<q-table
ref="table"
flat
bordered
class="custom-header-table"
v-bind="attrs"
virtual-scroll
:virtual-scroll-sticky-size-start="48"
dense
:pagination-label="paginationLabel"
v-model:pagination="pagination"
>
<!-- :rows-per-page-options="paging == true ? [25, 50, 100, 500] : []" -->
<!-- :rows-per-page-options="[0]" -->
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width v-if="boss == true" />
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
<q-th
auto-width
v-if="
editvisible == true || nextPageVisible == true || history == true
"
/>
</q-tr>
</template>
<template v-slot:pagination="scope">
<q-pagination
v-model="pagination.page"
active-color="primary"
color="dark"
:max="scope.pagesNumber"
:max-pages="5"
size="sm"
boundary-links
direction-links
></q-pagination>
</template>
<!-- สำหรบเรยกใช template วขางนอก -->
<template #body="props">
<slot v-bind="props" name="columns"></slot>
</template>
</q-table>
</div>
<!-- อมลการเผยแพรอม -->
<!-- <notifyPublishDraft v-model:showModal="modalPublish" :ok="publish" /> -->
<!-- <q-dialog v-model="modalPublish" persistent>
<q-card class="q-pa-sm">
<q-card-section class="row">
<div class="q-pr-md">
<q-avatar
icon="public"
size="lg"
font-size="25px"
color="blue-1"
text-color="public"
/>
</div>
<div class="col text-dark">
<span class="text-bold">องการเผยแพรอมลนหรอไม?</span>
<br />
<span>อมลทกำลงถกเผยแพรจะมผลใชงานทนท</span>
</div>
</q-card-section>
<q-card-actions align="right" class="bg-white text-teal">
<q-btn label="ยกเลิก" flat color="grey-8" v-close-popup />
<q-btn
label="เผยแพร่"
color="public"
@click="publish()"
v-close-popup
/>
</q-card-actions>
</q-card>
</q-dialog> -->
<!-- อมลการลบเผยแพรอม -->
<!-- <notifyDeleteDraft v-model:showModal="modalDelete" :ok="deleted" /> -->
<!-- <q-dialog v-model="modalDelete" persistent>
<q-card class="q-pa-sm">
<q-card-section class="row">
<div class="q-pr-md">
<q-avatar
icon="mdi-file-remove-outline"
size="lg"
font-size="25px"
color="red-1"
text-color="deep-orange"
/>
</div>
<div class="col text-dark">
<span class="text-bold">องการลบขอมลบนทกรางนหรอไม?</span>
<br />
<span>อมลบนทกรางทกำลงถกลบนจะมผลใชงานทนท</span>
</div>
</q-card-section>
<q-card-actions align="right" class="bg-white text-teal">
<q-btn label="ยกเลิก" flat color="grey-8" v-close-popup />
<q-btn label="ลบบันทึก" color="red" @click="deleted()" v-close-popup />
</q-card-actions>
</q-card>
</q-dialog> -->
</template>
<script setup lang="ts">
import { ref, useAttrs, computed } from "vue";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar } from "quasar";
const $q = useQuasar();
const mixin = useCounterMixin(); //
const { dialogMessage } = mixin;
const rows = ref<any>([]);
const attrs = ref<any>(useAttrs());
const table = ref<any>(null);
const filterRef = ref<any>(null);
const modalPublish = ref<boolean>(false);
const modalDelete = ref<boolean>(false);
const pagination = ref({
sortBy: "desc",
descending: false,
page: 1,
rowsPerPage: 10,
});
const pagesNumber = computed(() => {
return Math.ceil(rows.value.length / pagination.value.rowsPerPage);
});
const paginationLabel = (start: string, end: string, total: string) => {
if (props.paging == true)
return " " + start + " ถึง " + end + " จากจำนวน " + total + " รายการ";
else return start + "-" + end + " ใน " + total;
};
const props = defineProps({
inputfilter: String,
inputvisible: Array,
inputvisibleFilter: String,
editvisible: Boolean,
titleText: String,
optionsFilter: {
type: Array,
defualt: [],
},
boss: {
type: Boolean,
defualt: false,
},
saveNoDraft: {
type: Boolean,
defualt: false,
},
history: {
type: Boolean,
defualt: false,
},
paging: {
type: Boolean,
defualt: false,
},
nornmalData: {
type: Boolean,
defualt: false,
},
refreshData: {
type: Boolean,
defualt: false,
},
nextPageVisible: {
type: Boolean,
defualt: false,
},
publicData: {
type: Boolean,
defualt: true,
required: false,
},
updateData: {
type: Boolean,
defualt: true,
required: false,
},
publicNoBtn: {
type: Boolean,
defualt: false,
},
add: {
type: Function,
default: () => console.log("not function"),
},
edit: {
type: Function,
default: () => console.log("not function"),
},
save: {
type: Function,
default: () => console.log("not function"),
},
deleted: {
type: Function,
default: () => console.log("not function"),
},
cancel: {
type: Function,
default: () => console.log("not function"),
},
publish: {
type: Function,
default: () => console.log("not function"),
},
validate: {
type: Function,
default: () => console.log("not function"),
},
});
const initialPagination = ref<any>({
// descending: false,
rowsPerPage: props.paging == true ? 25 : 0,
});
const emit = defineEmits([
"update:inputfilter",
"update:inputvisible",
"update:editvisible",
"update:titleText",
"update:inputvisibleFilter",
]);
const updateEdit = (value: any) => {
emit("update:editvisible", value);
};
const updateInput = (value: any) => {
emit("update:inputfilter", value);
};
const updateVisible = (value: any) => {
emit("update:inputvisible", value);
};
const updateVisibleFilter = (value: any) => {
emit("update:inputvisibleFilter", value);
};
const checkSave = () => {
props.validate();
props.save();
// if (myForm.value !== null) {
// myForm.value.validate().then((success) => {
// if (success) {
// }
// });
// }
};
const publishModal = () => {
props.validate();
const filter = attrs.value.rows.filter((r: any) => r.name == "");
if (filter.length == 0 || attrs.value.rows.length == 0) {
// modalPublish.value = true;
dialogMessage(
$q,
"ต้องการเผยแพร่ข้อมูลนี้หรือไม่?",
"ข้อมูลที่กำลังถูกเผยแพร่นี้จะมีผลใช้งานทันที",
"public",
"เผยแพร่",
"public",
props.publish,
undefined
);
}
};
const DeleteModal = () => {
// modalDelete.value = true;
dialogMessage(
$q,
"ต้องการลบข้อมูลบันทึกร่างนี้หรือไม่?",
"ข้อมูลบันทึกร่างที่กำลังถูกลบนี้จะมีผลใช้งานทันที",
"mdi-file-remove-outline",
"ลบบันทึก",
"red",
props.deleted,
undefined
);
};
const edit = async () => {
updateEdit(!props.editvisible);
props.edit();
};
const add = async () => {
// if (myForm.value !== null) {
// myForm.value.validate();
// }
props.validate();
props.add();
await table.value.lastPage();
await table.value.scrollTo(attrs.value.rows.length - 1);
};
const deleted = async () => {
// const deletedF = () => {
if (props.publicNoBtn === false) {
updateEdit(false);
}
props.deleted();
};
const resetFilter = () => {
// reset X
emit("update:inputfilter", "");
filterRef.value.focus();
};
</script>
<style lang="scss">
.icon-color {
color: #4154b3;
}
.custom-header-table {
max-height: 64vh;
.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;
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;
}
}
</style>

View file

@ -0,0 +1,412 @@
<template>
<div class="q-px-md q-pb-md">
<!-- header บน table นหา แสดงคอลมน มแกไข เพ เผยแพรอม ยกเล-->
<div class="col-12 row q-py-sm q-col-gutter-sm">
<q-btn-group flat>
<q-btn
:push="!editvisible"
:outline="editvisible"
dense
:disabled="editvisible == true"
:color="editvisible == true ? 'grey-7' : 'primary'"
label="แก้ไข"
@click="edit"
icon="mdi-pencil-outline"
class="q-px-sm"
>
<!-- <q-tooltip>แกไขขอม</q-tooltip> -->
</q-btn>
<!-- <q-separator vertical /> -->
<q-btn
:push="editvisible"
:outline="!editvisible"
dense
:disabled="editvisible == false"
:color="editvisible == false ? 'grey-7' : 'primary'"
label="เพิ่ม"
@click="add"
icon="mdi-plus"
class="q-px-sm"
>
<!-- <q-tooltip>เพมขอม</q-tooltip> -->
</q-btn>
</q-btn-group>
<q-btn-group flat>
<!-- เผยแพร -->
<q-btn
dense
:push="publicData == false || updateData == true"
:outline="!(publicData == false || updateData == true)"
:disabled="!(publicData == false || updateData == true)"
:color="
!(publicData == false || updateData == true) ? 'grey-7' : 'public'
"
label="เผยแพร่"
@click="publishModal"
icon="mdi-file-upload-outline"
class="q-px-sm"
>
<!-- <q-tooltip>เผยแพร</q-tooltip> -->
</q-btn>
<!-- นทกราง แสดงเม กดปมแกไข อมลมการเปนแปลงหร งไมเผยแพรอม -->
<q-btn
dense
:push="
editvisible == true && (publicData == false || updateData == true)
"
:outline="
!(
editvisible == true &&
(publicData == false || updateData == true)
)
"
:disabled="
!(
editvisible == true &&
(publicData == false || updateData == true)
)
"
:color="
!(
editvisible == true &&
(publicData == false || updateData == true)
)
? 'grey-7'
: 'public'
"
label="บันทึกร่าง"
@click="checkSave"
icon="mdi-file-check-outline"
class="q-px-sm"
>
<!-- <q-tooltip>นทกราง</q-tooltip> -->
</q-btn>
<!-- ลบบนทกราง แสดงเม นทกรางแล -->
<q-btn
dense
:push="!publicData"
:disabled="publicData == true"
:outline="publicData == true"
label="ลบร่าง"
:color="publicData == true ? 'grey-7' : 'public'"
@click="deleted()"
icon="mdi-file-remove-outline"
class="q-px-sm"
>
<!-- <q-tooltip>ลบบนทกราง</q-tooltip> -->
</q-btn>
</q-btn-group>
<q-btn-group flat>
<!-- ยกเล แสดงเม กดปมแกไข -->
<q-btn
dense
:push="editvisible"
:disabled="editvisible == false"
:outline="editvisible == false"
label="ยกเลิก"
:color="editvisible == false ? 'grey-7' : 'red'"
@click="cancel()"
icon="mdi-close"
class="q-px-sm"
>
<!-- <q-tooltip>ยกเล</q-tooltip> -->
</q-btn>
</q-btn-group>
<q-space />
<div class="items-center" style="display: flex">
<!-- อความสถานะเผยแพร โดยใช parameter publicData เปนตวกำหนดขอความ -->
<!-- <div class="row items-center" style="display: flex" v-if="publicData == true">
<q-icon cener size="20px" name="label_important" class="icon-color" />
<div class="text-size">อมลเผยแพรแล</div>
</div> -->
<div
class="row items-center"
style="display: flex"
v-if="publicData == false"
>
<q-icon cener size="15px" name="label_important" color="amber" />
<div class="text-caption text-grey-7 text-weight-medium q-px-sm">
อมลยงไมเผยแพร
</div>
</div>
<!-- นหาขอความใน table -->
<q-input
standout
dense
:model-value="inputfilter"
@update:model-value="updateInput"
outlined
debounce="300"
placeholder="ค้นหา"
style="max-width: 200px"
class="q-ml-sm"
>
<template v-slot:append>
<q-icon name="search" />
</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 q-ml-sm"
/>
</div>
</div>
<q-table
ref="table"
flat
bordered
class="custom-header-table"
v-bind="attrs"
virtual-scroll
:rows-per-page-options="[0]"
:virtual-scroll-sticky-size-start="48"
dense
><!-- :pagination="initialPagination"
:pagination-label="paginationLabel" -->
<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">{{ col.label }}</span>
</q-th>
<q-th
auto-width
v-if="editvisible == true || nextPageVisible == true"
/>
</q-tr>
</template>
<!-- สำหรบเรยกใช template วขางนอก -->
<template #body="props">
<slot v-bind="props" name="columns"></slot>
</template>
</q-table>
</div>
<!-- อมลการเผยแพรอม -->
<q-dialog v-model="modal" persistent>
<q-card class="q-pa-sm">
<q-card-section class="row">
<div class="q-pr-md">
<q-avatar
icon="public"
size="lg"
font-size="25px"
color="blue-1"
text-color="public"
/>
</div>
<div class="col text-dark">
<span class="text-bold">องการเผยแพรอมลนหรอไม?</span>
<br />
<span>อมลทกำลงถกเผยแพรจะมผลใชงานทนท</span>
</div>
</q-card-section>
<q-card-actions align="right" class="bg-white text-teal">
<q-btn label="ยกเลิก" flat color="grey-8" v-close-popup />
<q-btn
label="เผยแพร่"
color="public"
@click="publish()"
v-close-popup
/>
</q-card-actions>
</q-card>
</q-dialog>
<q-dialog
:model-value="modalError"
persistent
@update:model-value="updateClose"
>
<q-card class="q-pa-sm">
<q-card-section class="row items-center">
<div class="q-pr-md">
<q-avatar
icon="mdi-alert-circle-outline"
font-size="25px"
size="lg"
color="red-1"
text-color="red"
/>
</div>
<div class="col text-dark">
<span class="text-bold">{{ modalErrorTittle }}</span>
<br />
<span>{{ modalErrorDetail }}</span>
</div>
</q-card-section>
<q-card-actions align="right" class="bg-white text-teal">
<q-btn
label="ตกลง"
color="primary"
@click="updateClose"
v-close-popup
/>
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script setup lang="ts">
import { ref, useAttrs } from "vue";
const attrs = ref<any>(useAttrs());
const table = ref<any>(null);
const modal = ref<boolean>(false);
const initialPagination = ref<any>({
descending: false,
rowsPerPage: 10,
});
const props = defineProps({
inputfilter: String,
inputvisible: Array,
editvisible: Boolean,
nextPageVisible: {
type: Boolean,
defualt: false,
},
modalError: Boolean,
modalErrorTittle: String,
modalErrorDetail: String,
publicData: {
type: Boolean,
defualt: true,
required: true,
},
updateData: {
type: Boolean,
defualt: true,
required: true,
},
add: {
type: Function,
default: () => console.log("not function"),
},
edit: {
type: Function,
default: () => console.log("not function"),
},
save: {
type: Function,
default: () => console.log("not function"),
},
deleted: {
type: Function,
default: () => console.log("not function"),
},
cancel: {
type: Function,
default: () => console.log("not function"),
},
publish: {
type: Function,
default: () => console.log("not function"),
},
});
const emit = defineEmits([
"update:inputfilter",
"update:inputvisible",
"update:editvisible",
"update:modalError",
"update:modalErrorTittle",
"update:modalErrorDetail",
]);
const updateEdit = (value: any) => {
emit("update:editvisible", value);
};
const updateInput = (value: any) => {
emit("update:inputfilter", value);
};
const updateVisible = (value: any) => {
emit("update:inputvisible", value);
};
const updateClose = (value: any) => {
// props.modalError = false;
emit("update:modalError", false);
emit("update:modalErrorTittle", "");
emit("update:modalErrorDetail", "");
};
const paginationLabel = (start: string, end: string, total: string) => {
return start + "-" + end + " ใน " + total;
};
const checkSave = () => {
props.save();
// if (myForm.value !== null) {
// myForm.value.validate().then((success) => {
// if (success) {
// }
// });
// }
};
const publishModal = () => {
const filter = attrs.value.rows.filter((r: any) => r.name == "");
if (filter.length == 0 && attrs.value.rows.length !== 0) {
modal.value = true;
}
};
const edit = async () => {
updateEdit(!props.editvisible);
props.edit();
};
const add = async () => {
// if (myForm.value !== null) {
// myForm.value.validate();
// }
props.add();
await table.value.lastPage();
await table.value.scrollTo(attrs.value.rows.length - 1);
};
</script>
<style lang="scss">
.icon-color {
color: #4154b3;
}
.custom-header-table {
max-height: 38vh;
.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;
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;
}
}
</style>

View file

View file

@ -0,0 +1,11 @@
import { describe, it, expect } from "vitest";
import { mount } from "@vue/test-utils";
// import HelloWorld from '../HelloWorld.vue'
describe("HelloWorld", () => {
it("renders properly", () => {
// const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } })
// expect(wrapper.text()).toContain('Hello Vitest')
});
});

View file

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
/>
</svg>
</template>

View file

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
<path
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
/>
</svg>
</template>

View file

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
<path
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
/>
</svg>
</template>

View file

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
/>
</svg>
</template>

View file

@ -0,0 +1,19 @@
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
aria-hidden="true"
role="img"
class="iconify iconify--mdi"
width="24"
height="24"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 24 24"
>
<path
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
fill="currentColor"
></path>
</svg>
</template>

View file

@ -0,0 +1,873 @@
<template>
<q-card flat bordered class="col-12 q-px-lg q-py-md q-mt-md">
<HeaderTop
v-model:edit="edit"
header="ข้อมูลที่อยู่"
icon="mdi-map-marker"
:save="saveData"
:history="!statusAdd()"
:changeBtn="changeBtn"
:disable="statusEdit"
:cancel="refreshData"
:historyClick="clickHistory"
:addEmployee="statusAdd()"
/>
<q-form ref="myform">
<div class="row col-12 items-center q-col-gutter-x-xs q-col-gutter-y-xs">
<div class="col-xs-12">
<q-input
:class="getClass(edit)"
hide-bottom-space
:outlined="edit"
dense
lazy-rules
type="textarea"
autogrow
:readonly="!edit"
:borderless="!edit"
v-model="addressData.address"
:rules="[(val:string) => !!val || `${'กรุณากรอก ที่อยู่ตามทะเบียนบ้าน'}`]"
:label="`${'ที่อยู่ตามทะเบียนบ้าน'}`"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<selector
:hide-dropdown-icon="!edit"
hide-bottom-space
:class="getClass(edit)"
:readonly="!edit"
:borderless="!edit"
:rules="[(val:string) => !!val || `${'กรุณาเลือก จังหวัด'}`]"
:outlined="edit"
dense
lazy-rules
v-model="addressData.provinceId"
emit-value
map-options
option-label="name"
:options="Ops.provinceOps"
option-value="id"
:label="`${'จังหวัด'}`"
@update:model-value="(value:string) => selectProvince(value, '1')"
use-input
input-debounce="0"
@filter="(inputValue:string,
doneFn:Function) => filterSelector(inputValue, doneFn,'provinceOps'
) "
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<selector
:hide-dropdown-icon="!edit"
hide-bottom-space
:class="getClass(edit)"
:readonly="!edit"
:borderless="!edit"
:rules="[(val:string) => !!val || `${'กรุณาเลือก เขต / อำเภอ'}`]"
:outlined="edit"
dense
lazy-rules
v-model="addressData.districtId"
emit-value
map-options
option-label="name"
:options="Ops.districtOps"
option-value="id"
:label="`${'เขต / อำเภอ'}`"
@update:model-value="(value:string) => selectDistrict(value, '1')"
use-input
input-debounce="0"
@filter="(inputValue:string,
doneFn:Function) => filterSelector(inputValue, doneFn,'districtOps'
) "
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<selector
:hide-dropdown-icon="!edit"
hide-bottom-space
:class="getClass(edit)"
:readonly="!edit"
:borderless="!edit"
:rules="[(val:string) => !!val || `${'กรุณาเลือก แขวง / ตำบล '}`]"
:outlined="edit"
dense
lazy-rules
v-model="addressData.subdistrictId"
emit-value
map-options
option-label="name"
:options="Ops.subdistrictOps"
option-value="id"
:label="`${'แขวง / ตำบล '}`"
@update:model-value="(value:string) => selectSubDistrict(value, '1')"
use-input
input-debounce="0"
@filter="(inputValue:string,
doneFn:Function) => filterSelector(inputValue, doneFn,'subdistrictOps'
) "
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
:class="getClass(edit)"
hide-bottom-space
dense
lazy-rules
readonly
borderless
v-model="codep"
:style="!edit ? '' : 'padding:0 12px;'"
:label="`${'รหัสไปรษณีย์'}`"
/>
</div>
<div class="col-12 q-pt-lg"><q-separator /></div>
<div class="col-xs-12 q-gutter-sm items-center flex q-my-sm">
<label class="text-bold"
>อยจจนตรงกบทอยตามทะเบยนบาน</label
>
<q-radio
v-model="addressData.same"
checked-icon="task_alt"
unchecked-icon="panorama_fish_eye"
val="1"
label="ใช่"
dense
:disable="!edit"
/>
<q-radio
v-model="addressData.same"
checked-icon="task_alt"
unchecked-icon="panorama_fish_eye"
val="0"
label="ไม่"
dense
:disable="!edit"
/>
</div>
<div class="col-xs-12" v-if="addressData.same == '0'">
<q-input
:class="getClass(edit)"
hide-bottom-space
:outlined="edit"
dense
lazy-rules
type="textarea"
autogrow
:readonly="!edit"
:borderless="!edit"
v-model="addressData.addressC"
:rules="[(val:string) => !!val || `${'กรุณากรอก ที่อยู่ปัจจุบัน'}`]"
:label="`${'ที่อยู่ปัจจุบัน'}`"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3" v-if="addressData.same == '0'">
<selector
:hide-dropdown-icon="!edit"
hide-bottom-space
:class="getClass(edit)"
:readonly="!edit"
:borderless="!edit"
:rules="[(val:string) => !!val || `${'กรุณาเลือก จังหวัด'}`]"
:outlined="edit"
dense
lazy-rules
v-model="addressData.provinceIdC"
emit-value
map-options
option-label="name"
:options="Ops.provinceOps"
option-value="id"
:label="`${'จังหวัด'}`"
@update:model-value="(value:string) => selectProvince(value, '2')"
use-input
input-debounce="0"
@filter="(inputValue:string,
doneFn:Function) => filterSelector(inputValue, doneFn,'provinceOps'
) "
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3" v-if="addressData.same == '0'">
<selector
:hide-dropdown-icon="!edit"
hide-bottom-space
:class="getClass(edit)"
:readonly="!edit"
:borderless="!edit"
:rules="[(val:string) => !!val || `${'กรุณาเลือก เขต / อำเภอ'}`]"
:outlined="edit"
dense
lazy-rules
v-model="addressData.districtIdC"
emit-value
map-options
option-label="name"
:options="Ops.districtCOps"
option-value="id"
:label="`${'เขต / อำเภอ'}`"
@update:model-value="(value:string) => selectDistrict(value, '2')"
use-input
input-debounce="0"
@filter="(inputValue:string,
doneFn:Function) => filterSelector(inputValue, doneFn,'districtCOps'
) "
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3" v-if="addressData.same == '0'">
<selector
:hide-dropdown-icon="!edit"
hide-bottom-space
:class="getClass(edit)"
:readonly="!edit"
:borderless="!edit"
:rules="[(val:string) => !!val || `${'กรุณาเลือก แขวง / ตำบล '}`]"
:outlined="edit"
dense
lazy-rules
v-model="addressData.subdistrictIdC"
emit-value
map-options
option-label="name"
:options="Ops.subdistrictCOps"
option-value="id"
:label="`${'แขวง / ตำบล '}`"
@update:model-value="(value:string) => selectSubDistrict(value, '2')"
use-input
input-debounce="0"
@filter="(inputValue:string,
doneFn:Function) => filterSelector(inputValue, doneFn,'subdistrictCOps'
) "
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3" v-if="addressData.same == '0'">
<q-input
:class="getClass(edit)"
hide-bottom-space
dense
lazy-rules
readonly
borderless
v-model="codec"
:style="!edit ? '' : 'padding:0 12px;'"
:label="`${'รหัสไปรษณีย์'}`"
/>
</div>
</div>
</q-form>
</q-card>
<HistoryTable
:rows="rowsHistory"
:columns="columnsHistory"
:filter="filterHistory"
:visible-columns="visibleColumnsHistory"
v-model:modal="modalHistory"
v-model:inputfilter="filterHistory"
v-model:inputvisible="visibleColumnsHistory"
v-model:tittle="tittleHistory"
>
<template #columns="props">
<q-tr :props="props">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div v-if="col.name == 'createdAt'" class="table_ellipsis">
{{ date2Thai(col.value) }}
</div>
<div
v-else-if="col.name == 'registrationSame'"
class="table_ellipsis"
>
{{ col.value ? "ใช่" : "ไม่" }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
</q-tr>
</template>
</HistoryTable>
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import type {
Address,
DataOption,
zipCodeOption,
} from "@/components/information/interface/profileType";
import { defaultAddress } from "@/components/information/interface/profileType";
import HeaderTop from "@/components/information/top.vue";
import type { RequestItemsHistoryObject } from "@/components/information/interface/request/Address";
import type { ResponseObject } from "@/components/information/interface/response/Address";
import HistoryTable from "@/components/TableHistory.vue";
import { useRoute } from "vue-router";
import type { AddressOps } from "@/components/information/interface/index/Main";
import type { QTableProps } from "quasar";
const props = defineProps({
statusEdit: {
type: Boolean,
required: true,
},
statusAdd: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:statusEdit"]);
const route = useRoute();
const $q = useQuasar();
const mixin = useCounterMixin();
const { date2Thai, success, messageError, showLoader, hideLoader } = mixin;
const edit = ref<boolean>(false);
const addressData = ref<Address>(defaultAddress);
const myform = ref<any>();
const codep = ref<string>("");
const codec = ref<string>("");
const rowsHistory = ref<RequestItemsHistoryObject[]>([]); //select data history
const tittleHistory = ref<string>("ประวัติแก้ไขข้อมูลที่อยู่"); //
const filterHistory = ref<string>(""); //search data table history
const modalHistory = ref<boolean>(false); //modal
const Ops = ref<AddressOps>({
provinceOps: [],
districtOps: [],
districtCOps: [],
subdistrictOps: [],
subdistrictCOps: [],
});
const OpsFilter = ref<AddressOps>({
provinceOps: [],
districtOps: [],
districtCOps: [],
subdistrictOps: [],
subdistrictCOps: [],
});
const columnsHistory = ref<QTableProps["columns"]>([
{
name: "registrationAddress",
align: "left",
label: "ที่อยู่ตามทะเบียนบ้าน",
sortable: true,
field: "registrationAddress",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "registrationProvince",
align: "left",
label: "จังหวัดตามทะเบียนบ้าน",
sortable: true,
field: "registrationProvince",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "registrationDistrict",
align: "left",
label: "เขต/อำเภอตามทะเบียนบ้าน",
sortable: true,
field: "registrationDistrict",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "registrationSubDistrict",
align: "left",
label: "แขวง/ตำบลตามทะเบียนบ้าน",
sortable: true,
field: "registrationSubDistrict",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "registrationZipCode",
align: "left",
label: "รหัสไปรษณีย์ตามทะเบียนบ้าน",
sortable: true,
field: "registrationZipCode",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "registrationSame",
align: "left",
label: "ที่อยู่ปัจจุบันตรงกับที่อยู่ตามทะเบียนบ้าน",
sortable: true,
field: "registrationSame",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "currentAddress",
align: "left",
label: "ที่อยู่ปัจจุบัน",
sortable: true,
field: "currentAddress",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "currentProvince",
align: "left",
label: "จังหวัดปัจจุบัน",
sortable: true,
field: "currentProvince",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "currentDistrict",
align: "left",
label: "เขต/อำเภอปัจจุบัน",
sortable: true,
field: "currentDistrict",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "currentSubDistrict",
align: "left",
label: "แขวง/ตำบลปัจจุบัน",
sortable: true,
field: "currentSubDistrict",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "currentZipCode",
align: "left",
label: "รหัสไปรษณีย์ปัจจุบัน",
sortable: true,
field: "currentZipCode",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "createdFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const visibleColumnsHistory = ref<String[]>([
"currentAddress",
"currentDistrict",
"currentProvince",
"currentSubDistrict",
"currentZipCode",
"registrationAddress",
"registrationDistrict",
"registrationProvince",
"registrationSame",
"registrationSubDistrict",
"registrationZipCode",
"createdFullName",
"createdAt",
]);
onMounted(async () => {
await getNewData();
emit("update:statusEdit", false);
defaultAdd();
});
const statusAdd = () => props.statusAdd;
const defaultAdd = () => {
if (props.statusAdd) {
edit.value = props.statusAdd;
addressData.value = {
address: null,
provinceId: null,
districtId: null,
subdistrictId: null,
addressC: null,
provinceIdC: null,
districtIdC: null,
subdistrictIdC: null,
same: "0",
};
}
};
const filterSelector = (val: any, update: Function, refData: string) => {
switch (refData) {
case "provinceOps":
update(() => {
Ops.value.provinceOps = OpsFilter.value.provinceOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
case "districtOps":
update(() => {
Ops.value.districtOps = OpsFilter.value.districtOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
case "districtCOps":
update(() => {
Ops.value.districtCOps = OpsFilter.value.districtCOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
case "subdistrictOps":
update(() => {
Ops.value.subdistrictOps = OpsFilter.value.subdistrictOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
case "subdistrictCOps":
update(() => {
Ops.value.subdistrictCOps = OpsFilter.value.subdistrictCOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
default:
break;
}
};
/**
* งชนดอมลประวแกไขขอมลทเลอก
*/
const clickHistory = async () => {
modalHistory.value = true;
showLoader();
await http
.get(config.API.profileAdrsHisId(route.params.id.toString()))
.then((res) => {
let data = res.data.result;
rowsHistory.value = [];
data.map((e: RequestItemsHistoryObject) => {
rowsHistory.value.push({
currentAddress: e.currentAddress,
currentDistrict: e.currentDistrict,
currentProvince: e.currentProvince,
currentSubDistrict: e.currentSubDistrict,
currentZipCode: e.currentZipCode,
registrationSame: e.registrationSame,
registrationAddress: e.registrationAddress,
registrationDistrict: e.registrationDistrict,
registrationProvince: e.registrationProvince,
registrationSubDistrict: e.registrationSubDistrict,
registrationZipCode: e.registrationZipCode,
createdFullName: e.createdFullName,
createdAt: new Date(e.createdAt),
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
};
const refreshData = async () => {
myform.value.reset();
await getNewData();
};
const getNewData = async () => {
await fetchData();
await fetchProvince();
await fetchDistrict(addressData.value.provinceId, "1");
await fetchDistrict(addressData.value.provinceIdC, "2");
await fetchSubDistrict(addressData.value.districtId, "1");
await fetchSubDistrict(addressData.value.districtIdC, "2");
};
const fetchData = async () => {
if (route.params.id) {
showLoader();
await http
.get(config.API.profileAdrsId(route.params.id.toString()))
.then((res) => {
const data: ResponseObject = res.data.result;
addressData.value.address = data.registrationAddress;
addressData.value.addressC = data.currentAddress;
addressData.value.districtId = data.registrationDistrictId;
addressData.value.districtIdC = data.currentDistrictId;
addressData.value.provinceId = data.registrationProvinceId;
addressData.value.provinceIdC = data.currentProvinceId;
addressData.value.subdistrictId = data.registrationSubDistrictId;
addressData.value.subdistrictIdC = data.currentSubDistrictId;
addressData.value.same = data.registrationSame ? "1" : "0";
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
};
const editData = async () => {
const body: ResponseObject = {
registrationSame: addressData.value.same == "1",
registrationAddress: addressData.value.address,
registrationSubDistrictId: addressData.value.subdistrictId,
registrationDistrictId: addressData.value.districtId,
registrationProvinceId: addressData.value.provinceId,
registrationZipCode: codep.value,
currentAddress:
addressData.value.same == "1"
? addressData.value.address
: addressData.value.addressC,
currentSubDistrictId:
addressData.value.same == "1"
? addressData.value.subdistrictId
: addressData.value.subdistrictIdC,
currentDistrictId:
addressData.value.same == "1"
? addressData.value.districtId
: addressData.value.districtIdC,
currentProvinceId:
addressData.value.same == "1"
? addressData.value.provinceId
: addressData.value.provinceIdC,
currentZipCode: addressData.value.same == "1" ? codep.value : codec.value,
createdAt: new Date(),
createdFullName: "-",
};
showLoader();
await http
.put(config.API.profileAdrsId(route.params.id.toString()), body)
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
edit.value = false;
emit("update:statusEdit", false);
await getNewData();
});
};
const addData = async () => {
// showLoader();
// await http
// .put(config.API.profileInforId(route.params.id.toString()), body)
// .then((res) => {
// success($q, "");
// })
// .catch((e) => {
// messageError($q, e);
// })
// .finally(async () => {
// edit.value = false;
// emit("update:statusEdit", false);
// await fetchData();
// await changeBirth(informaData.value.birthDate ?? new Date());
// });
};
const saveData = async () => {
await myform.value.validate().then(async (success: boolean) => {
if (success) {
if (props.statusAdd) {
await addData();
} else {
await editData();
}
} else {
}
});
};
const selectProvince = async (e: string | null, name: string) => {
if (e != null) {
if (name == "1") {
addressData.value.districtId = "";
addressData.value.subdistrictId = "";
codep.value = "";
} else {
addressData.value.districtIdC = "";
addressData.value.subdistrictIdC = "";
codec.value = "";
}
myform.value.resetValidation();
await fetchDistrict(e, name);
}
};
const selectDistrict = async (e: string | null, name: string) => {
if (e != null) {
if (name == "1") {
addressData.value.subdistrictId = "";
codep.value = "";
} else {
addressData.value.subdistrictIdC = "";
codec.value = "";
}
myform.value.resetValidation();
await fetchSubDistrict(e, name);
}
};
const selectSubDistrict = (e: string | null, name: string) => {
if (e != null) {
if (name == "1") {
const findcode = Ops.value.subdistrictOps.filter((r) => r.id == e);
const namecode = findcode.length > 0 ? findcode[0].zipCode : "";
codep.value = namecode;
} else {
const findcode = Ops.value.subdistrictCOps.filter((r) => r.id == e);
const namecode = findcode.length > 0 ? findcode[0].zipCode : "";
codec.value = namecode;
}
}
};
const fetchProvince = async () => {
showLoader();
await http
.get(config.API.province)
.then((res) => {
const data = res.data.result;
let option: DataOption[] = [];
data.map((r: any) => {
option.push({ id: r.id.toString(), name: r.name.toString() });
});
Ops.value.provinceOps = option;
OpsFilter.value.provinceOps = option;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
};
const fetchDistrict = async (id: string | null, position: string) => {
if (id != null && id != "") {
showLoader();
await http
.get(config.API.listDistrict(id))
.then((res) => {
const data = res.data.result;
let option: DataOption[] = [];
data.map((r: any) => {
option.push({ id: r.id.toString(), name: r.name.toString() });
});
if (position == "1") {
Ops.value.districtOps = option;
OpsFilter.value.districtOps = option;
} else {
Ops.value.districtCOps = option;
OpsFilter.value.districtCOps = option;
}
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
};
const fetchSubDistrict = async (id: string | null, position: string) => {
if (id != null && id != "") {
showLoader();
await http
.get(config.API.listSubDistrict(id))
.then((res) => {
const data = res.data.result;
let option: zipCodeOption[] = [];
data.map((r: any) => {
option.push({
id: r.id.toString(),
name: r.name.toString(),
zipCode: r.zipCode.toString(),
});
});
if (position == "1") {
Ops.value.subdistrictOps = option;
OpsFilter.value.subdistrictOps = option;
} else {
Ops.value.subdistrictCOps = option;
OpsFilter.value.subdistrictCOps = option;
}
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
};
const changeBtn = async () => {
if (edit.value == true) {
if (props.statusEdit === true) {
edit.value = false;
} else {
emit("update:statusEdit", true);
}
} else {
emit("update:statusEdit", false);
}
};
const getClass = (val: boolean) => {
return {
"full-width inputgreen cursor-pointer": val,
"full-width cursor-pointer": !val,
};
};
</script>

View file

@ -0,0 +1,980 @@
<!-- card ผลการประเมนการปฏราชการ -->
<template>
<q-card flat bordered class="col-12 q-px-lg q-pa-md">
<q-form ref="myForm">
<ProfileTable
:rows="rows"
:columns="columns"
:filter="filter"
:visible-columns="visibleColumns"
v-model:inputfilter="filter"
v-model:inputvisible="visibleColumns"
:add="clickAdd"
name="ผลการประเมินการปฏิบัติราชการ"
icon="mdi-clipboard-text"
:statusEdit="statusEdit"
>
<template #columns="props">
<q-tr :props="props">
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
@click="selectData(props)"
class="cursor-pointer"
>
<div v-if="col.name == 'date'" class="table_ellipsis">
{{ date2Thai(col.value) }}
</div>
<div v-else-if="col.name == 'name'" class="table_ellipsis">
{{ textPoint(props.row.pointSum) }}
{{ textRangePoint(props.row.pointSum) }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="info"
flat
dense
round
size="14px"
icon="mdi-history"
@click="clickHistory(props.row)"
/>
</q-td>
</q-tr>
</template>
</ProfileTable>
</q-form>
</q-card>
<!-- popup Edit window-->
<q-dialog v-model="modal" persistent>
<q-card style="width: 600px">
<q-form ref="myForm">
<DialogHeader
tittle="ผลการประเมินการปฏิบัติราชการ"
:close="clickClose"
/>
<q-separator />
<q-card-section class="q-p-sm">
<div
class="row col-12 items-center q-col-gutter-x-xs q-col-gutter-y-xs"
>
<div class="col-xs-12 col-sm-12 col-md-12">
<datepicker
menu-class-name="modalfix"
:readonly="!edit"
v-model="date"
:locale="'th'"
autoApply
:enableTimePicker="false"
@update:modelValue="clickEditRow"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
:borderless="!edit"
:model-value="date2Thai(date)"
:rules="[(val) => !!val || `${'กรุณาเลือกวันที่ได้รับ'}`]"
hide-bottom-space
:label="`${'วันที่ได้รับ'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
:style="
edit
? 'color: var(--q-primary)'
: 'color: var(--q-grey)'
"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<!-- <div class="col-6">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="point"
:rules="[(val) => !!val || `${'กรุณากรอกคะแนน'}`]"
hide-bottom-space
:label="`${'คะแนน'}`"
@update:modelValue="clickEditRow"
type="number"
/>
</div> -->
<div class="col-6">
<q-input
input-class="text-right"
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="point1Total"
:rules="[(val) => !!val || `${'กรุณากรอกส่วนที่1 (คะแนน)'}`]"
hide-bottom-space
:label="`${'ส่วนที่1 (คะแนน)'}`"
@update:modelValue="clickEditRow"
type="number"
/>
</div>
<div class="col-6">
<q-input
input-class="text-right"
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="point1"
:rules="[
(val) => !!val || `${'กรุณากรอกผลประเมินส่วนที่1 (คะแนน)'}`,
]"
hide-bottom-space
:label="`${'ผลประเมินส่วนที่1 (คะแนน)'}`"
@update:modelValue="clickEditRow"
type="number"
/>
</div>
<div class="col-6">
<q-input
input-class="text-right"
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="point2Total"
:rules="[(val) => !!val || `${'กรุณากรอกส่วนที่2 (คะแนน)'}`]"
hide-bottom-space
:label="`${'ส่วนที่2 (คะแนน)'}`"
@update:modelValue="clickEditRow"
type="number"
/>
</div>
<div class="col-6">
<q-input
input-class="text-right"
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="point2"
:rules="[
(val) => !!val || `${'กรุณากรอกผลประเมินส่วนที่2 (คะแนน)'}`,
]"
hide-bottom-space
:label="`${'ผลประเมินส่วนที่2 (คะแนน)'}`"
@update:modelValue="clickEditRow"
type="number"
/>
</div>
<div class="col-6">
<q-input
input-class="text-right"
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="pointSumTotal"
:rules="[(val) => !!val || `${'กรุณากรอกผลรวม (คะแนน)'}`]"
hide-bottom-space
:label="`${'ผลรวม (คะแนน)'}`"
@update:modelValue="clickEditRow"
type="number"
/>
</div>
<div class="col-6">
<q-input
input-class="text-right"
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="pointSum"
:rules="[
(val) => !!val || `${'กรุณากรอกผลประเมินรวม (คะแนน)'}`,
]"
hide-bottom-space
:label="`${'ผลประเมินรวม (คะแนน)'}`"
@update:modelValue="clickEditRow"
type="number"
/>
</div>
<div class="text-subtitle2 col-12 row items-center">
<q-space></q-space>
ผลการประเม:
<div class="text-bold items-center q-px-sm">
{{ textPoint(pointSum) }}
</div>
{{ textRangePoint(pointSum) }}
</div>
<!-- <div class="col-12">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="name"
:rules="[(val) => !!val || `${'กรุณากรอกชื่อแบบประเมิน'}`]"
hide-bottom-space
:label="`${'ชื่อแบบประเมิน'}`"
@update:modelValue="clickEditRow"
/>
</div> -->
</div>
</q-card-section>
<q-separator />
<DialogFooter
:cancel="clickCancel"
:edit="clickEdit"
:save="clickSave"
:validate="validateData"
:clickNext="clickNext"
:clickPrevious="clickPrevious"
:clickDelete="clickDelete"
v-model:editvisible="edit"
v-model:next="next"
v-model:previous="previous"
v-model:modalEdit="modalEdit"
/>
</q-form>
</q-card>
</q-dialog>
<HistoryTable
:rows="rowsHistory"
:columns="columnsHistory"
:filter="filterHistory"
:visible-columns="visibleColumnsHistory"
v-model:modal="modalHistory"
v-model:inputfilter="filterHistory"
v-model:inputvisible="visibleColumnsHistory"
v-model:tittle="tittleHistory"
>
<template #columns="props">
<q-tr :props="props">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div
v-if="col.name == 'date' || col.name == 'createdAt'"
class="table_ellipsis"
>
{{ date2Thai(col.value) }}
</div>
<div v-else-if="col.name == 'name'" class="table_ellipsis">
{{ textPoint(props.row.pointSum) }}
{{ textRangePoint(props.row.pointSum) }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
</q-tr>
</template>
</HistoryTable>
</template>
<script setup lang="ts">
import { onMounted, ref, watch } from "vue";
import { useRoute } from "vue-router";
import { useCounterMixin } from "@/stores/mixin";
import { useProfileDataStore } from "@/modules/04_registry/store";
import ProfileTable from "@/modules/04_registry/components/Table.vue";
import DialogHeader from "@/modules/04_registry/components/DialogHeader.vue";
import DialogFooter from "@/modules/04_registry/components/DialogFooter.vue";
import { useQuasar } from "quasar";
import type {
RequestItemsObject,
DataProps,
} from "@/modules/04_registry/interface/request/Assessment";
import type { ResponseObject } from "@/modules/04_registry/interface/response/Assessment";
import type { DataOption } from "@/modules/04_registry/interface/index/Main";
import HistoryTable from "@/components/TableHistory.vue";
import http from "@/plugins/http";
import config from "@/app.config";
import type { QTableProps } from "quasar";
const props = defineProps({
statusEdit: {
type: Boolean,
required: true,
},
profileType: {
type: String,
required: true,
},
});
const $q = useQuasar();
const store = useProfileDataStore();
const { profileData, changeProfileColumns } = store;
const mixin = useCounterMixin();
const { date2Thai, success, dateToISO, messageError, showLoader, hideLoader } =
mixin;
const route = useRoute();
const id = ref<string>("");
const name = ref<string>();
const point1Total = ref<number>();
const point1 = ref<number>();
const point2Total = ref<number>();
const point2 = ref<number>();
const pointSumTotal = ref<number>();
const pointSum = ref<number>();
const date = ref<Date>(new Date());
const myForm = ref<any>(); //form data input
const edit = ref<boolean>(false); // dialog
const modal = ref<boolean>(false); //modal add detail
const modalEdit = ref<boolean>(false); //modal
const rawItem = ref<RequestItemsObject>(); // row
const rowIndex = ref<number>(0); //index row
const previous = ref<boolean>(); //
const next = ref<boolean>(); //
const editRow = ref<boolean>(false); //
const rowsHistory = ref<RequestItemsObject[]>([]); //select data history
const tittleHistory = ref<string>("ประวัติแก้ไขผลการประเมินการปฏิบัติราชการ"); //
const filterHistory = ref<string>(""); //search data table history
const modalHistory = ref<boolean>(false); //modal
const checkValidate = ref<boolean>(false); //validate data
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const rows = ref<RequestItemsObject[]>([]);
const filter = ref<string>(""); //search data table
const visibleColumns = ref<String[]>([]);
profileData.assessment.columns.length == 0
? (visibleColumns.value = [
"point1Total",
"point1",
"point2Total",
"point2",
"pointSumTotal",
"pointSum",
"name",
"date",
])
: (visibleColumns.value = profileData.assessment.columns);
const columns = ref<QTableProps["columns"]>([
{
name: "date",
align: "left",
label: "วันที่ได้รับ",
sortable: true,
field: "date",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "point1Total",
align: "left",
label: "ส่วนที่1 (คะแนน)",
sortable: true,
field: "point1Total",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "point1",
align: "left",
label: "ผลประเมินส่วนที่1 (คะแนน)",
sortable: true,
field: "point1",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "point2Total",
align: "left",
label: "ส่วนที่2 (คะแนน)",
sortable: true,
field: "point2Total",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "point2",
align: "left",
label: "ผลประเมินส่วนที่2 (คะแนน)",
sortable: true,
field: "point2",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "pointSumTotal",
align: "left",
label: "ผลรวม (คะแนน)",
sortable: true,
field: "pointSumTotal",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "pointSum",
align: "left",
label: "ผลประเมินรวม (คะแนน)",
sortable: true,
field: "pointSum",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "name",
align: "left",
label: "ผลประเมิน",
sortable: true,
field: "name",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const columnsHistory = ref<QTableProps["columns"]>([
{
name: "point1Total",
align: "left",
label: "ส่วนที่1 (คะแนน)",
sortable: true,
field: "point1Total",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "point1",
align: "left",
label: "ผลประเมินส่วนที่1 (คะแนน)",
sortable: true,
field: "point1",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "point2Total",
align: "left",
label: "ส่วนที่2 (คะแนน)",
sortable: true,
field: "point2Total",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "point2",
align: "left",
label: "ผลประเมินส่วนที่2 (คะแนน)",
sortable: true,
field: "point2",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "pointSumTotal",
align: "left",
label: "ผลรวม (คะแนน)",
sortable: true,
field: "pointSumTotal",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "pointSum",
align: "left",
label: "ผลประเมินรวม (คะแนน)",
sortable: true,
field: "pointSum",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "name",
align: "left",
label: "ผลประเมิน",
sortable: true,
field: "name",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "createdFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const visibleColumnsHistory = ref<String[]>([
"point1Total",
"point1",
"point2Total",
"point2",
"pointSumTotal",
"pointSum",
"name",
"date",
"createdFullName",
"createdAt",
]);
watch(visibleColumns, async (count: String[], prevCount: String[]) => {
await changeProfileColumns("assessment", count);
});
onMounted(async () => {
await fetchData();
});
const fetchData = async () => {
if (profileId.value) {
showLoader();
await http
.get(config.API.profileAssessmentId(profileId.value))
.then((res) => {
let data = res.data.result;
rows.value = [];
data.map((e: ResponseObject) => {
rows.value.push({
id: e.id,
name: e.name,
point1Total: e.point1Total,
point1: e.point1,
point2Total: e.point2Total,
point2: e.point2,
pointSumTotal: e.pointSumTotal,
pointSum: e.pointSum,
date: new Date(e.date),
createdAt: new Date(e.createdAt),
createdFullName: e.createdFullName,
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
};
/**
* กดดอมลกอนหน
*/
const clickPrevious = async () => {
edit.value = false;
rowIndex.value -= 1;
await getData();
await checkRowPage();
};
/**
* กดดอมลตอไป
*/
const clickNext = async () => {
edit.value = false;
rowIndex.value += 1;
await getData();
await checkRowPage();
};
/**
* กดดอมลตอไป
*/
const getData = () => {
const row = rows.value[rowIndex.value];
name.value = row.name;
point1Total.value = row.point1Total;
point1.value = row.point1;
point2Total.value = row.point2Total;
point2.value = row.point2;
pointSumTotal.value = row.pointSumTotal;
pointSum.value = row.pointSum;
date.value = row.date;
id.value = row.id;
};
/**
* เชคปมดอม อน อไป าตองแสดงไหม
*/
const checkRowPage = () => {
editRow.value = false;
next.value = true;
previous.value = true;
if (rowIndex.value + 1 >= rows.value.length) {
next.value = false;
}
if (rowIndex.value - 1 < 0) {
previous.value = false;
}
};
/**
* กดปมแกไขใน dialog
*/
const clickEdit = () => {
editRow.value = false;
next.value = false;
previous.value = false;
};
/**
* กดปมเพมดานบน table
*/
const clickAdd = async () => {
editRow.value = false;
await addData();
};
/**
* กดบนทกใน dialog
*/
const clickSave = async () => {
myForm.value.validate().then(async (result: boolean) => {
if (result) {
if (modalEdit.value) {
await editData();
} else {
await saveData();
}
}
});
};
/**
* นทกเพมขอม
*/
const saveData = async () => {
if (profileId.value) {
showLoader();
await http
.post(config.API.profileAssessmentId(profileId.value), {
id: id.value,
name: name.value,
point1Total: point1Total.value,
point1: point1.value,
point2Total: point2Total.value,
point2: point2.value,
pointSumTotal: pointSumTotal.value,
pointSum: pointSum.value,
date: dateToISO(date.value),
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
}
};
/**
* นทกแกไขขอม
*/
const editData = async () => {
showLoader();
await http
.put(config.API.profileAssessmentId(id.value), {
id: id.value,
name: name.value,
point1Total: point1Total.value,
point1: point1.value,
point2Total: point2Total.value,
point2: point2.value,
pointSumTotal: pointSumTotal.value,
pointSum: pointSum.value,
date: dateToISO(date.value),
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
};
/**
* ลบลบขอม
*/
const clickDelete = async () => {
$q.dialog({
title: `ลบข้อมูล`,
message: `ต้องการทำการลบข้อมูลนี้ใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
})
.onOk(async () => {
showLoader();
await http
.delete(config.API.profileAssessmentId(id.value))
.then((res) => {
success($q, "ลบข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
})
.onCancel(async () => {
await fetchData();
});
};
/**
* กดป dialog
*/
const clickClose = async () => {
if (editRow.value == true) {
$q.dialog({
title: `ข้อมูลมีการแก้ไข`,
message: `ยืนยันที่จะปิดโดยไม่บันทึกใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
}).onOk(async () => {
modal.value = false;
next.value = false;
previous.value = false;
});
} else {
modal.value = false;
next.value = false;
previous.value = false;
}
};
/**
* กดเลอกขอมลทจะแกไข
* @param props props ใน row เลอก
*/
const selectData = async (props: DataProps) => {
modalEdit.value = true;
modal.value = true;
edit.value = false;
rawItem.value = props.row;
rowIndex.value = props.rowIndex;
name.value = props.row.name;
point1Total.value = props.row.point1Total;
point1.value = props.row.point1;
point2Total.value = props.row.point2Total;
point2.value = props.row.point2;
pointSumTotal.value = props.row.pointSumTotal;
pointSum.value = props.row.pointSum;
date.value = props.row.date;
id.value = props.row.id;
await checkRowPage();
};
/**
* กดปมเพมบน table
*/
const addData = () => {
modalEdit.value = false;
modal.value = true;
edit.value = true;
name.value = "";
point1Total.value = 0;
point1.value = 0;
point2Total.value = 0;
point2.value = 0;
pointSumTotal.value = 0;
pointSum.value = 0;
date.value = new Date();
};
/**
* งกนปมยกเลกการแกไขขอม
*/
const clickCancel = async () => {
if (editRow.value == true) {
$q.dialog({
title: `ข้อมูลมีการแก้ไข`,
message: `ยืนยันยกเลิกการแก้ไขใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
}).onOk(async () => {
edit.value = false;
await checkRowPage();
await getData();
});
} else {
edit.value = false;
await checkRowPage();
}
};
/**
* เชความการแกไขขอม
*/
const clickEditRow = () => {
editRow.value = true;
};
/**
* งชนดอมลประวแกไขขอมลทเลอก
* @param row อม row ประวการแกไข
*/
const clickHistory = async (row: RequestItemsObject) => {
tittleHistory.value = "ประวัติแก้ไขผลการประเมินการปฏิบัติราชการ";
modalHistory.value = true;
showLoader();
await http
.get(config.API.profileAssessmentHisId(row.id))
.then((res) => {
let data = res.data.result;
rowsHistory.value = [];
data.map((e: ResponseObject) => {
rowsHistory.value.push({
id: e.id,
name: e.name,
point1Total: e.point1Total,
point1: e.point1,
point2Total: e.point2Total,
point2: e.point2,
pointSumTotal: e.pointSumTotal,
pointSum: e.pointSum,
date: new Date(e.date),
createdAt: new Date(e.createdAt),
createdFullName: e.createdFullName,
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
};
/**
* validate input ใน dialog
*/
const validateData = async () => {
checkValidate.value = true;
await myForm.value.validate().then((result: boolean) => {
if (result == false) {
checkValidate.value = false;
}
});
};
/**
* class ดรปแบบแสดงระหวางขอมลทแกไขหรอแสดงเฉยๆ
* @param val อม input สำหรบแกไขหรอไม
*/
const getClass = (val: boolean) => {
return {
"full-width inputgreen cursor-pointer": val,
"full-width cursor-pointer": !val,
};
};
const textRangePoint = (val: number | undefined) => {
if (val == undefined) val = -1;
if (val >= 0 && val <= 60) return "(ต่ำกว่าร้อยละ 60)";
if (val >= 60 && val <= 69) return "(ร้อยละ 60 69)";
if (val >= 70 && val <= 79) return "(ร้อยละ 70 79)";
if (val >= 80 && val <= 89) return "(ร้อยละ 80 89)";
if (val >= 90 && val <= 100) return "(ร้อยละ 90 - 100)";
else return "";
};
const textPoint = (val: number | undefined) => {
if (val == undefined) val = -1;
if (val >= 0 && val <= 60) return "ต้องปรับปรุง";
if (val >= 60 && val <= 69) return "พอใช้";
if (val >= 70 && val <= 79) return "ดี";
if (val >= 80 && val <= 89) return "ดีมาก";
if (val >= 90 && val <= 100) return "ดีเด่น";
else return "-";
};
</script>
<style lang="scss">
.modalfix {
position: fixed !important;
}
</style>

View file

@ -0,0 +1,827 @@
<!-- card ใบอนญาตประกอบอาช -->
<template>
<q-card flat bordered class="col-12 q-px-lg q-py-md q-mt-md">
<q-form ref="myForm">
<ProfileTable
:rows="rows"
:columns="columns"
:filter="filter"
:visible-columns="visibleColumns"
v-model:inputfilter="filter"
v-model:inputvisible="visibleColumns"
:add="clickAdd"
:name="
profileType == 'employee'
? 'ใบอนุญาตของลูกจ้าง'
: 'ใบอนุญาตประกอบอาชีพ'
"
icon="mdi-book"
:statusEdit="statusEdit"
>
<template #columns="props">
<q-tr :props="props">
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
@click="selectData(props)"
class="cursor-pointer"
>
<div
v-if="col.name == 'issueDate' || col.name == 'expireDate'"
class="table_ellipsis"
>
{{ date2Thai(col.value) }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="info"
flat
dense
round
size="14px"
icon="mdi-history"
@click="clickHistory(props.row)"
/>
</q-td>
</q-tr>
</template>
</ProfileTable>
</q-form>
</q-card>
<!-- popup Edit window-->
<q-dialog v-model="modal" persistent>
<q-card style="width: 600px">
<q-form ref="myForm">
<DialogHeader
:tittle="
profileType == 'employee'
? 'ใบอนุญาตของลูกจ้าง'
: 'ใบอนุญาตประกอบอาชีพ'
"
:close="clickClose"
/>
<q-separator />
<q-card-section class="q-p-sm">
<div
class="row col-12 items-center q-col-gutter-x-xs q-col-gutter-y-xs"
>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="certificateType"
:rules="[(val) => !!val || `${'กรุณากรอกชื่อใบอนุญาต'}`]"
hide-bottom-space
:label="`${'ชื่อใบอนุญาต'}`"
@update:modelValue="clickEditRow"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="issuer"
:rules="[
(val) => !!val || `${'กรุณากรอกหน่วยงานผู้ออกใบอนุญาต'}`,
]"
hide-bottom-space
:label="`${'หน่วยงานผู้ออกใบอนุญาต'}`"
@update:modelValue="clickEditRow"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="certificateNo"
:rules="[(val) => !!val || `${'กรุณากรอกเลขที่ใบอนุญาต'}`]"
hide-bottom-space
:label="`${'เลขที่ใบอนุญาต'}`"
@update:modelValue="clickEditRow"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
:readonly="!edit"
v-model="issueDate"
:locale="'th'"
autoApply
:enableTimePicker="false"
@update:modelValue="clickEditRow"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
:borderless="!edit"
:model-value="date2Thai(issueDate)"
:rules="[
(val) => !!val || `${'กรุณาเลือกวันที่ออกใบอนุญาต'}`,
]"
hide-bottom-space
:label="`${'วันที่ออกใบอนุญาต'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
:style="
edit
? 'color: var(--q-primary)'
: 'color: var(--q-grey)'
"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
v-model="expireDate"
:locale="'th'"
autoApply
:enableTimePicker="false"
:min-date="minDate"
:readonly="!edit"
@update:modelValue="clickEditRow"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
:borderless="!edit"
:model-value="date2Thai(expireDate)"
:rules="[(val) => !!val || `${'กรุณาเลือกวันที่หมดอายุ'}`]"
hide-bottom-space
:label="`${'วันที่หมดอายุ'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
:style="
edit
? 'color: var(--q-primary)'
: 'color: var(--q-grey)'
"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
</div>
</q-card-section>
<q-separator />
<DialogFooter
:cancel="clickCancel"
:edit="clickEdit"
:save="clickSave"
:validate="validateData"
:clickNext="clickNext"
:clickPrevious="clickPrevious"
:clickDelete="clickDelete"
v-model:editvisible="edit"
v-model:next="next"
v-model:previous="previous"
v-model:modalEdit="modalEdit"
/>
</q-form>
</q-card>
</q-dialog>
<HistoryTable
:rows="rowsHistory"
:columns="columnsHistory"
:filter="filterHistory"
:visible-columns="visibleColumnsHistory"
v-model:modal="modalHistory"
v-model:inputfilter="filterHistory"
v-model:inputvisible="visibleColumnsHistory"
v-model:tittle="tittleHistory"
>
<template #columns="props">
<q-tr :props="props">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div
v-if="
col.name == 'expireDate' ||
col.name == 'issueDate' ||
col.name == 'createdAt'
"
class="table_ellipsis"
>
{{ date2Thai(col.value) }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
</q-tr>
</template>
</HistoryTable>
</template>
<script setup lang="ts">
import { onMounted, ref, watch } from "vue";
import { useRoute } from "vue-router";
import { useComponentProfileDataStore } from "@/components/information/store/store";
import { useCounterMixin } from "@/stores/mixin";
import ProfileTable from "@/components/information/Table.vue";
import DialogHeader from "@/components/information/DialogHeader.vue";
import DialogFooter from "@/components/information/DialogFooter.vue";
import { useQuasar } from "quasar";
import type {
RequestItemsObject,
DataProps,
} from "@/components/information/interface/request/Certificate";
import type { ResponseObject } from "@/components/information/interface/response/Certificate";
import HistoryTable from "@/components/TableHistory.vue";
import http from "@/plugins/http";
import config from "@/app.config";
import type { QTableProps } from "quasar";
const props = defineProps({
statusEdit: {
type: Boolean,
required: true,
},
profileType: {
type: String,
required: true,
},
});
const $q = useQuasar();
const store = useComponentProfileDataStore();
const { profileData, changeProfileColumns } = store;
const mixin = useCounterMixin();
const { date2Thai, success, dateToISO, messageError, showLoader, hideLoader } =
mixin;
const route = useRoute();
const id = ref<string>("");
const certificateNo = ref<string>();
const issuer = ref<string>();
const issueDate = ref<Date>(new Date());
const expireDate = ref<Date>(new Date());
const certificateType = ref<string>();
const minDate = ref<Date>();
const myForm = ref<any>(); //form data input
const edit = ref<boolean>(false); // dialog
const modal = ref<boolean>(false); //modal add detail
const modalEdit = ref<boolean>(false); //modal
const rawItem = ref<RequestItemsObject>(); // row
const rowIndex = ref<number>(0); //index row
const previous = ref<boolean>(); //
const next = ref<boolean>(); //
const editRow = ref<boolean>(false); //
const rowsHistory = ref<RequestItemsObject[]>([]); //select data history
const tittleHistory = ref<string>("ประวัติแก้ไขใบอนุญาตประกอบอาชีพ"); //
const filterHistory = ref<string>(""); //search data table history
const modalHistory = ref<boolean>(false); //modal
const checkValidate = ref<boolean>(false); //validate data
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const rows = ref<RequestItemsObject[]>([]);
const filter = ref<string>(""); //search data table
const visibleColumns = ref<String[]>([]);
profileData.certicate.columns.length == 0
? (visibleColumns.value = [
"certificateType",
"certificateNo",
"issuer",
"issueDate",
"expireDate",
])
: (visibleColumns.value = profileData.certicate.columns);
const columns = ref<QTableProps["columns"]>([
{
name: "certificateType",
align: "left",
label: "ชื่อใบอนุญาต",
sortable: true,
field: "certificateType",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "issuer",
align: "left",
label: "หน่วยงานผู้ออกใบอนุญาต",
sortable: true,
field: "issuer",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "certificateNo",
align: "left",
label: "เลขที่ใบอนุญาต",
sortable: true,
field: "certificateNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "issueDate",
align: "left",
label: "วันที่ออกใบอนุญาต",
sortable: true,
field: "issueDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "expireDate",
align: "left",
label: "วันที่หมดอายุ",
sortable: true,
field: "expireDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const columnsHistory = ref<QTableProps["columns"]>([
{
name: "certificateType",
align: "left",
label: "ชื่อใบอนุญาต",
sortable: true,
field: "certificateType",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "issuer",
align: "left",
label: "หน่วยงานผู้ออกใบอนุญาต",
sortable: true,
field: "issuer",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "certificateNo",
align: "left",
label: "เลขที่ใบอนุญาต",
sortable: true,
field: "certificateNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "issueDate",
align: "left",
label: "วันที่ออกใบอนุญาต",
sortable: true,
field: "issueDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "expireDate",
align: "left",
label: "วันที่หมดอายุ",
sortable: true,
field: "expireDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "createdFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const visibleColumnsHistory = ref<String[]>([
"certificateType",
"certificateNo",
"issuer",
"issueDate",
"expireDate",
"createdFullName",
"createdAt",
]);
watch(visibleColumns, async (count: String[], prevCount: String[]) => {
await changeProfileColumns("certicate", count);
});
onMounted(async () => {
await fetchData();
});
const fetchData = async () => {
if (profileId.value) {
showLoader();
await http
.get(config.API.profileCertId(profileId.value))
.then((res) => {
let data = res.data.result;
rows.value = [];
data.map((e: ResponseObject) => {
rows.value.push({
id: e.id,
certificateNo: e.certificateNo,
issuer: e.issuer,
issueDate: new Date(e.issueDate),
expireDate: new Date(e.expireDate),
certificateType: e.certificateType,
createdFullName: e.createdFullName,
createdAt: new Date(e.createdAt),
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
};
/**
* กดดอมลกอนหน
*/
const clickPrevious = async () => {
edit.value = false;
rowIndex.value -= 1;
await getData();
await checkRowPage();
};
/**
* กดดอมลตอไป
*/
const clickNext = async () => {
edit.value = false;
rowIndex.value += 1;
await getData();
await checkRowPage();
};
/**
* กดดอมลตอไป
*/
const getData = () => {
const row = rows.value[rowIndex.value];
certificateNo.value = row.certificateNo;
issuer.value = row.issuer;
issueDate.value = row.issueDate;
expireDate.value = row.expireDate;
certificateType.value = row.certificateType;
id.value = row.id;
};
/**
* เชคปมดอม อน อไป าตองแสดงไหม
*/
const checkRowPage = () => {
editRow.value = false;
next.value = true;
previous.value = true;
if (rowIndex.value + 1 >= rows.value.length) {
next.value = false;
}
if (rowIndex.value - 1 < 0) {
previous.value = false;
}
};
/**
* กดปมแกไขใน dialog
*/
const clickEdit = () => {
editRow.value = false;
next.value = false;
previous.value = false;
};
/**
* กดปมเพมดานบน table
*/
const clickAdd = async () => {
editRow.value = false;
await addData();
};
/**
* กดบนทกใน dialog
*/
const clickSave = async () => {
myForm.value.validate().then(async (result: boolean) => {
if (result) {
if (modalEdit.value) {
await editData();
} else {
await saveData();
}
}
});
};
/**
* นทกเพมขอม
*/
const saveData = async () => {
showLoader();
await http
.post(config.API.profileCertId(profileId.value), {
id: id.value,
certificateNo: certificateNo.value,
issuer: issuer.value,
issueDate: dateToISO(issueDate.value),
expireDate: dateToISO(expireDate.value),
certificateType: certificateType.value,
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
};
/**
* นทกแกไขขอม
*/
const editData = async () => {
showLoader();
await http
.put(config.API.profileCertId(id.value), {
id: id.value,
certificateNo: certificateNo.value,
issuer: issuer.value,
issueDate: dateToISO(issueDate.value),
expireDate: dateToISO(expireDate.value),
certificateType: certificateType.value,
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
};
/**
* ลบลบขอม
*/
const clickDelete = async () => {
$q.dialog({
title: `ลบข้อมูล`,
message: `ต้องการทำการลบข้อมูลนี้ใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
})
.onOk(async () => {
showLoader();
await http
.delete(config.API.profileCertId(id.value))
.then((res) => {
success($q, "ลบข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
})
.onCancel(async () => {
await fetchData();
});
};
/**
* กดป dialog
*/
const clickClose = async () => {
if (editRow.value == true) {
$q.dialog({
title: `ข้อมูลมีการแก้ไข`,
message: `ยืนยันที่จะปิดโดยไม่บันทึกใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
}).onOk(async () => {
modal.value = false;
next.value = false;
previous.value = false;
});
} else {
modal.value = false;
next.value = false;
previous.value = false;
}
};
/**
* กดเลอกขอมลทจะแกไข
* @param props props ใน row เลอก
*/
const selectData = async (props: DataProps) => {
modalEdit.value = true; //
modal.value = true; //modal
edit.value = false; // edit modal
rawItem.value = props.row;
rowIndex.value = props.rowIndex;
certificateNo.value = props.row.certificateNo;
issuer.value = props.row.issuer;
issueDate.value = props.row.issueDate;
expireDate.value = props.row.expireDate;
certificateType.value = props.row.certificateType;
id.value = props.row.id;
await checkRowPage();
};
/**
* กดปมเพมบน table
*/
const addData = () => {
modalEdit.value = false;
modal.value = true;
edit.value = true;
certificateNo.value = "";
issuer.value = "";
issueDate.value = new Date();
expireDate.value = new Date();
certificateType.value = "";
};
/**
* งกนปมยกเลกการแกไขขอม
*/
const clickCancel = async () => {
if (editRow.value == true) {
$q.dialog({
title: `ข้อมูลมีการแก้ไข`,
message: `ยืนยันยกเลิกการแก้ไขใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
}).onOk(async () => {
edit.value = false;
await checkRowPage();
await getData();
});
} else {
edit.value = false;
await checkRowPage();
}
};
/**
* เชความการแกไขขอม
*/
const clickEditRow = () => {
editRow.value = true;
};
/**
* งชนดอมลประวแกไขขอมลทเลอก
* @param row อม row ประวการแกไข
*/
const clickHistory = async (row: RequestItemsObject) => {
tittleHistory.value =
props.profileType == "employee"
? "ประวัติแก้ไขใบอนุญาตของลูกจ้าง"
: "ประวัติแก้ไขใบอนุญาตประกอบอาชีพ";
modalHistory.value = true;
showLoader();
await http
.get(config.API.profileCertHisId(row.id))
.then((res) => {
let data = res.data.result;
rowsHistory.value = [];
data.map((e: ResponseObject) => {
rowsHistory.value.push({
id: e.id,
certificateNo: e.certificateNo,
issuer: e.issuer,
issueDate: new Date(e.issueDate),
expireDate: new Date(e.expireDate),
certificateType: e.certificateType,
createdFullName: e.createdFullName,
createdAt: new Date(e.createdAt),
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
};
/**
* validate input ใน dialog
*/
const validateData = async () => {
checkValidate.value = true;
await myForm.value.validate().then((result: boolean) => {
if (result == false) {
checkValidate.value = false;
}
});
};
/**
* class ดรปแบบแสดงระหวางขอมลทแกไขหรอแสดงเฉยๆ
* @param val อม input สำหรบแกไขหรอไม
*/
const getClass = (val: boolean) => {
return {
"full-width inputgreen cursor-pointer": val,
"full-width cursor-pointer": !val,
};
};
</script>
<style lang="scss">
.modalfix {
position: fixed !important;
}
</style>

View file

@ -0,0 +1,838 @@
<!-- card ประกาศเกยรต -->
<template>
<q-card flat bordered class="col-12 q-px-lg q-pa-md">
<q-form ref="myForm">
<ProfileTable
:rows="rows"
:columns="columns"
:filter="filter"
:visible-columns="visibleColumns"
v-model:inputfilter="filter"
v-model:inputvisible="visibleColumns"
:add="clickAdd"
:name="
profileType == 'employee' ? 'ประกาศลูกจ้างดีเด่น' : 'ประกาศเกียรติคุณ'
"
icon="mdi-script-text"
:statusEdit="statusEdit"
>
<template #columns="props">
<q-tr :props="props">
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
@click="selectData(props)"
class="cursor-pointer"
>
<div
v-if="col.name == 'issueDate' || col.name == 'refCommandDate'"
class="table_ellipsis"
>
{{ col.value == null ? null : date2Thai(col.value) }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="info"
flat
dense
round
size="14px"
icon="mdi-history"
@click="clickHistory(props.row)"
/>
</q-td>
</q-tr>
</template>
</ProfileTable>
</q-form>
</q-card>
<!-- popup Edit window-->
<q-dialog v-model="modal" persistent>
<q-card style="width: 600px">
<q-form ref="myForm">
<DialogHeader
:tittle="
profileType == 'employee'
? 'ประกาศลูกจ้างดีเด่น'
: 'ประกาศเกียรติคุณ'
"
:close="clickClose"
/>
<q-separator />
<q-card-section class="q-p-sm">
<div
class="row col-12 items-center q-col-gutter-x-xs q-col-gutter-y-xs"
>
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
:readonly="!edit"
v-model="issueDate"
:locale="'th'"
autoApply
:enableTimePicker="false"
@update:modelValue="clickEditRow"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
:borderless="!edit"
:model-value="date2Thai(issueDate)"
:rules="[(val) => !!val || `${'กรุณาเลือกวันที่ได้รับ'}`]"
hide-bottom-space
:label="`${'วันที่ได้รับ'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
:style="
edit
? 'color: var(--q-primary)'
: 'color: var(--q-grey)'
"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-6">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
autogrow
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="issuer"
:rules="[(val) => !!val || `${'กรุณากรอกผู้มีอำนาจลงนาม'}`]"
hide-bottom-space
:label="`${'ผู้มีอำนาจลงนาม'}`"
@update:modelValue="clickEditRow"
/>
</div>
<div class="col-12">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
autogrow
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="detail"
:rules="[(val) => !!val || `${'กรุณากรอกรายละเอียด'}`]"
hide-bottom-space
:label="`${'รายละเอียด'}`"
@update:modelValue="clickEditRow"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="refCommandNo"
hide-bottom-space
:label="`${'เลขที่คำสั่ง'}`"
@update:modelValue="clickEditRow"
>
<template v-slot:append>
<q-icon
name="mdi-file"
class="cursor-pointer"
@click="resetFilter"
/>
</template>
</q-input>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
:readonly="!edit"
v-model="refCommandDate"
:locale="'th'"
autoApply
:enableTimePicker="false"
@update:modelValue="clickEditRow"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
:borderless="!edit"
:model-value="
refCommandDate == null ? null : date2Thai(refCommandDate)
"
hide-bottom-space
:label="`${'เอกสารอ้างอิง (ลงวันที่)'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
:style="
edit
? 'color: var(--q-primary)'
: 'color: var(--q-grey)'
"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
</div>
</q-card-section>
<q-separator />
<DialogFooter
:cancel="clickCancel"
:edit="clickEdit"
:save="clickSave"
:validate="validateData"
:clickNext="clickNext"
:clickPrevious="clickPrevious"
:clickDelete="clickDelete"
v-model:editvisible="edit"
v-model:next="next"
v-model:previous="previous"
v-model:modalEdit="modalEdit"
/>
</q-form>
</q-card>
</q-dialog>
<HistoryTable
:rows="rowsHistory"
:columns="columnsHistory"
:filter="filterHistory"
:visible-columns="visibleColumnsHistory"
v-model:modal="modalHistory"
v-model:inputfilter="filterHistory"
v-model:inputvisible="visibleColumnsHistory"
v-model:tittle="tittleHistory"
>
<template #columns="props">
<q-tr :props="props">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div
v-if="
col.name == 'issueDate' ||
col.name == 'createdAt' ||
col.name == 'refCommandDate'
"
class="table_ellipsis"
>
{{ col.value == null ? null : date2Thai(col.value) }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
</q-tr>
</template>
</HistoryTable>
</template>
<script setup lang="ts">
import { onMounted, ref, watch } from "vue";
import { useRoute } from "vue-router";
import { useCounterMixin } from "@/stores/mixin";
import { useComponentProfileDataStore } from "@/components/information/store/store";
import ProfileTable from "@/components/information/Table.vue";
import DialogHeader from "@/components/information/DialogHeader.vue";
import DialogFooter from "@/components/information/DialogFooter.vue";
import { useQuasar } from "quasar";
import type {
RequestItemsObject,
Columns,
DataProps,
} from "@/components/information/interface/request/Coin";
import type { ResponseObject } from "@/components/information/interface/response/Coin";
import HistoryTable from "@/components/TableHistory.vue";
import http from "@/plugins/http";
import config from "@/app.config";
import type { QTableProps } from "quasar";
const props = defineProps({
statusEdit: {
type: Boolean,
required: true,
},
profileType: {
type: String,
required: true,
},
});
const $q = useQuasar();
const store = useComponentProfileDataStore();
const { profileData, changeProfileColumns } = store;
const mixin = useCounterMixin();
const { date2Thai, success, dateToISO, messageError, showLoader, hideLoader } =
mixin;
const route = useRoute();
const id = ref<string>("");
const issuer = ref<string>();
const detail = ref<string>();
const issueDate = ref<Date>(new Date());
const refCommandNo = ref<string>();
const refCommandDate = ref<Date | null>(new Date());
const myForm = ref<any>(); //form data input
const edit = ref<boolean>(false); // dialog
const modal = ref<boolean>(false); //modal add detail
const modalEdit = ref<boolean>(false); //modal
const rawItem = ref<RequestItemsObject>(); // row
const rowIndex = ref<number>(0); //index row
const previous = ref<boolean>(); //
const next = ref<boolean>(); //
const editRow = ref<boolean>(false); //
const rowsHistory = ref<RequestItemsObject[]>([]); //select data history
const tittleHistory = ref<string>("ประวัติแก้ไขประกาศเกียรติคุณ"); //
const filterHistory = ref<string>(""); //search data table history
const modalHistory = ref<boolean>(false); //modal
const checkValidate = ref<boolean>(false); //validate data
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const rows = ref<RequestItemsObject[]>([]);
const filter = ref<string>(""); //search data table
const visibleColumns = ref<String[]>([]);
profileData.coined.columns.length == 0
? (visibleColumns.value = [
"issuer",
"detail",
"issueDate",
"refCommandNo",
"refCommandDate",
])
: (visibleColumns.value = profileData.coined.columns);
const columns = ref<QTableProps["columns"]>([
{
name: "issueDate",
align: "left",
label: "วันที่ได้รับ",
sortable: true,
field: "issueDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "issuer",
align: "left",
label: "ผู้มีอำนาจลงนาม",
sortable: true,
field: "issuer",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandNo",
align: "left",
label: "เลขที่คำสั่ง",
sortable: true,
field: "refCommandNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandDate",
align: "left",
label: "เอกสารอ้างอิง (ลงวันที่)",
sortable: true,
field: "refCommandDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const columnsHistory = ref<QTableProps["columns"]>([
{
name: "issueDate",
align: "left",
label: "วันที่ได้รับ",
sortable: true,
field: "issueDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "issuer",
align: "left",
label: "ผู้มีอำนาจลงนาม",
sortable: true,
field: "issuer",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandNo",
align: "left",
label: "เลขที่คำสั่ง",
sortable: true,
field: "refCommandNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandDate",
align: "left",
label: "เอกสารอ้างอิง (ลงวันที่)",
sortable: true,
field: "refCommandDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "createdFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const visibleColumnsHistory = ref<String[]>([
"issuer",
"detail",
"issueDate",
"refCommandNo",
"refCommandDate",
"createdFullName",
"createdAt",
]);
watch(visibleColumns, async (count: String[], prevCount: String[]) => {
await changeProfileColumns("coined", count);
});
onMounted(async () => {
await fetchData();
});
const fetchData = async () => {
if (profileId.value) {
showLoader();
await http
.get(config.API.profileHonorId(profileId.value))
.then((res) => {
let data = res.data.result;
rows.value = [];
data.map((e: ResponseObject) => {
rows.value.push({
id: e.id,
issuer: e.issuer,
detail: e.detail,
issueDate: new Date(e.issueDate),
refCommandNo: e.refCommandNo,
refCommandDate:
e.refCommandDate == null ? null : new Date(e.refCommandDate),
createdAt: new Date(e.createdAt),
createdFullName: e.createdFullName,
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
};
/**
* กดดอมลกอนหน
*/
const clickPrevious = async () => {
edit.value = false;
rowIndex.value -= 1;
await getData();
await checkRowPage();
};
/**
* กดดอมลตอไป
*/
const clickNext = async () => {
edit.value = false;
rowIndex.value += 1;
await getData();
await checkRowPage();
};
/**
* กดดอมลตอไป
*/
const getData = () => {
const row = rows.value[rowIndex.value];
issuer.value = row.issuer;
detail.value = row.detail;
issueDate.value = row.issueDate;
refCommandNo.value = row.refCommandNo;
refCommandDate.value = row.refCommandDate;
id.value = row.id;
};
/**
* เชคปมดอม อน อไป าตองแสดงไหม
*/
const checkRowPage = () => {
editRow.value = false;
next.value = true;
previous.value = true;
if (rowIndex.value + 1 >= rows.value.length) {
next.value = false;
}
if (rowIndex.value - 1 < 0) {
previous.value = false;
}
};
/**
* กดปมแกไขใน dialog
*/
const clickEdit = () => {
editRow.value = false;
next.value = false;
previous.value = false;
};
/**
* กดปมเพมดานบน table
*/
const clickAdd = async () => {
editRow.value = false;
await addData();
};
/**
* กดบนทกใน dialog
*/
const clickSave = async () => {
myForm.value.validate().then(async (result: boolean) => {
if (result) {
if (modalEdit.value) {
await editData();
} else {
await saveData();
}
}
});
};
/**
* นทกเพมขอม
*/
const saveData = async () => {
if (profileId.value) {
showLoader();
await http
.post(config.API.profileHonorId(profileId.value), {
id: id.value,
issuer: issuer.value,
detail: detail.value,
issueDate: dateToISO(issueDate.value),
refCommandNo: refCommandNo.value,
refCommandDate:
refCommandDate.value == null ? null : dateToISO(refCommandDate.value),
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
}
};
/**
* นทกแกไขขอม
*/
const editData = async () => {
showLoader();
await http
.put(config.API.profileHonorId(id.value), {
id: id.value,
issuer: issuer.value,
detail: detail.value,
issueDate: dateToISO(issueDate.value),
refCommandNo: refCommandNo.value,
refCommandDate:
refCommandDate.value == null ? null : dateToISO(refCommandDate.value),
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
};
/**
* ลบลบขอม
*/
const clickDelete = async () => {
$q.dialog({
title: `ลบข้อมูล`,
message: `ต้องการทำการลบข้อมูลนี้ใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
})
.onOk(async () => {
showLoader();
await http
.delete(config.API.profileHonorId(id.value))
.then((res) => {
success($q, "ลบข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
})
.onCancel(async () => {
await fetchData();
});
};
/**
* กดป dialog
*/
const clickClose = async () => {
if (editRow.value == true) {
$q.dialog({
title: `ข้อมูลมีการแก้ไข`,
message: `ยืนยันที่จะปิดโดยไม่บันทึกใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
}).onOk(async () => {
modal.value = false;
next.value = false;
previous.value = false;
});
} else {
modal.value = false;
next.value = false;
previous.value = false;
}
};
/**
* กดเลอกขอมลทจะแกไข
* @param props props ใน row เลอก
*/
const selectData = async (props: DataProps) => {
modalEdit.value = true;
modal.value = true;
edit.value = false;
rawItem.value = props.row;
rowIndex.value = props.rowIndex;
issuer.value = props.row.issuer;
detail.value = props.row.detail;
issueDate.value = props.row.issueDate;
refCommandNo.value = props.row.refCommandNo;
refCommandDate.value = props.row.refCommandDate;
id.value = props.row.id;
await checkRowPage();
};
/**
* กดปมเพมบน table
*/
const addData = () => {
modalEdit.value = false;
modal.value = true;
edit.value = true;
issuer.value = "";
detail.value = "";
issueDate.value = new Date();
refCommandNo.value = "";
refCommandDate.value = null;
};
/**
* งกนปมยกเลกการแกไขขอม
*/
const clickCancel = async () => {
if (editRow.value == true) {
$q.dialog({
title: `ข้อมูลมีการแก้ไข`,
message: `ยืนยันยกเลิกการแก้ไขใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
}).onOk(async () => {
edit.value = false;
await checkRowPage();
await getData();
});
} else {
edit.value = false;
await checkRowPage();
}
};
/**
* เชความการแกไขขอม
*/
const clickEditRow = () => {
editRow.value = true;
};
/**
* งชนดอมลประวแกไขขอมลทเลอก
* @param row อม row ประวการแกไข
*/
const clickHistory = async (row: RequestItemsObject) => {
tittleHistory.value =
props.profileType == "employee"
? "ประวัติแก้ไขประกาศลูกจ้างดีเด่น"
: "ประวัติแก้ไขประกาศเกียรติคุณ";
modalHistory.value = true;
showLoader();
await http
.get(config.API.profileHonorHisId(row.id))
.then((res) => {
let data = res.data.result;
rowsHistory.value = [];
data.map((e: ResponseObject) => {
rowsHistory.value.push({
id: e.id,
issuer: e.issuer,
detail: e.detail,
issueDate: new Date(e.issueDate),
refCommandNo: e.refCommandNo,
refCommandDate:
e.refCommandDate == null ? null : new Date(e.refCommandDate),
createdAt: new Date(e.createdAt),
createdFullName: e.createdFullName,
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
};
/**
* validate input ใน dialog
*/
const validateData = async () => {
checkValidate.value = true;
await myForm.value.validate().then((result: boolean) => {
if (result == false) {
checkValidate.value = false;
}
});
};
/**
* class ดรปแบบแสดงระหวางขอมลทแกไขหรอแสดงเฉยๆ
* @param val อม input สำหรบแกไขหรอไม
*/
const getClass = (val: boolean) => {
return {
"full-width inputgreen cursor-pointer": val,
"full-width cursor-pointer": !val,
};
};
const resetFilter = () => {};
</script>
<style lang="scss">
.modalfix {
position: fixed !important;
}
</style>

View file

@ -0,0 +1,135 @@
<template>
<q-card-actions class="text-primary q-py-sm">
<q-btn
flat
round
icon="mdi-menu-left"
@click="clickPrevious"
v-if="modalEdit == true"
:disable="previous == false"
:color="!previous ? 'grey-7' : 'public'"
/>
<q-btn
flat
round
icon="mdi-menu-right"
@click="clickNext"
v-if="modalEdit == true"
:disable="next == false"
:color="!next ? 'grey-7' : 'public'"
/>
<q-space />
<q-btn
v-if="modalEdit == true"
flat
round
color="red"
@click="clickDelete"
icon="mdi-delete"
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
<q-btn
v-if="!editvisible"
flat
round
:disabled="editvisible"
:color="editvisible ? 'grey-7' : 'primary'"
@click="edit"
icon="mdi-pencil-outline"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<div v-else>
<q-btn
flat
round
:disabled="!editvisible"
:outline="!editvisible"
:color="!editvisible ? 'grey-7' : 'red'"
@click="cancel()"
icon="mdi-undo"
v-if="modalEdit == true"
>
<q-tooltip>ยกเล</q-tooltip>
</q-btn>
<q-btn
flat
round
:disabled="!editvisible"
:color="!editvisible ? 'grey-7' : 'public'"
@click="checkSave"
icon="mdi-content-save-outline"
>
<q-tooltip>นท</q-tooltip>
</q-btn>
</div>
</q-card-actions>
</template>
<script setup lang="ts">
const props = defineProps({
editvisible: Boolean,
next: Boolean,
previous: Boolean,
modalEdit: Boolean,
clickNext: {
type: Function,
default: () => console.log("not function"),
},
clickPrevious: {
type: Function,
default: () => console.log("not function"),
},
cancel: {
type: Function,
default: () => console.log("not function"),
},
edit: {
type: Function,
default: () => console.log("not function"),
},
save: {
type: Function,
default: () => console.log("not function"),
},
validate: {
type: Function,
default: () => console.log("not function"),
},
clickDelete: {
type: Function,
default: () => console.log("not function"),
},
});
const emit = defineEmits([
"update:editvisible",
"update:next",
"update:previous",
]);
const updateEdit = (value: Boolean) => {
emit("update:editvisible", value);
};
const cancel = async () => {
props.cancel();
};
const clickDelete = async () => {
props.clickDelete();
};
const edit = async () => {
updateEdit(!props.editvisible);
props.edit();
};
const checkSave = () => {
props.validate();
props.save();
};
const clickNext = async () => {
await props.clickNext();
};
const clickPrevious = async () => {
await props.clickPrevious();
};
</script>

View file

@ -0,0 +1,27 @@
<template>
<q-toolbar>
<q-toolbar-title class="text-subtitle2 text-bold">{{
tittle
}}</q-toolbar-title>
<q-btn
icon="close"
unelevated
round
dense
@click="close"
style="color: #ff8080; background-color: #ffdede"
/>
</q-toolbar>
</template>
<script setup lang="ts">
const props = defineProps({
tittle: String,
close: {
type: Function,
default: () => console.log("not function"),
},
});
const close = async () => {
props.close();
};
</script>

View file

@ -0,0 +1,45 @@
<template>
<q-dialog :model-value="modal" persistent>
<q-card style="width: 600px">
<DialogHeader :tittle="props.title" :close="close" />
<q-separator />
<q-card-section
:class="props.fix ? 'q-pa-md scroll' : 'q-pa-md'"
:style="props.fix ? 'height: 50vh' : ''"
>
<slot name="body" />
</q-card-section>
<q-separator />
<q-card-actions class="text-primary">
<slot name="footer" />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script setup lang="ts">
import DialogHeader from "@/components/information/DialogHeader.vue";
const props = defineProps({
title: {
required: true,
type: String,
default: "",
},
modal: {
required: true,
type: Boolean,
default: false,
},
fix: {
required: true,
type: Boolean,
default: true,
},
});
const emit = defineEmits(["update:modal"]);
const close = async () => {
emit("update:modal", false);
};
</script>

View file

@ -0,0 +1,941 @@
<!-- card -->
<template>
<q-card flat bordered class="col-12 q-px-lg q-py-md">
<q-form ref="myForm">
<ProfileTable
:rows="rows"
:columns="columns"
:filter="filter"
:visible-columns="visibleColumns"
v-model:inputfilter="filter"
v-model:inputvisible="visibleColumns"
:add="clickAdd"
name="วินัย"
icon="mdi-scale-balance"
:statusEdit="statusEdit"
>
<template #columns="props">
<q-tr :props="props">
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
@click="selectData(props)"
class="cursor-pointer"
>
<div
v-if="col.name == 'refCommandDate' || col.name == 'date'"
class="table_ellipsis"
>
{{ col.value == null ? null : date2Thai(col.value) }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="info"
flat
dense
round
size="14px"
icon="mdi-history"
@click="clickHistory(props.row)"
/>
</q-td>
</q-tr>
</template>
</ProfileTable>
</q-form>
</q-card>
<!-- popup Edit window-->
<q-dialog v-model="modal" persistent>
<q-card style="width: 600px">
<q-form ref="myForm">
<DialogHeader tittle="วินัย" :close="clickClose" />
<q-separator />
<q-card-section class="q-p-sm">
<div
class="row col-12 items-center q-col-gutter-x-xs q-col-gutter-y-xs"
>
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
:readonly="!edit"
v-model="date"
:locale="'th'"
autoApply
:enableTimePicker="false"
@update:modelValue="clickEditRow"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
:borderless="!edit"
:model-value="date2Thai(date)"
:rules="[(val:string) => !!val || `${'กรุณาเลือกวัน เดือน ปี'}`]"
hide-bottom-space
:label="`${'วัน เดือน ปี'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
:style="
edit
? 'color: var(--q-primary)'
: 'color: var(--q-grey)'
"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="detail"
:rules="[(val:string) => !!val || `${'กรุณากรอกรายละเอียด'}`]"
hide-bottom-space
:label="`${'รายละเอียด'}`"
@update:modelValue="clickEditRow"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<selector
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="level"
:rules="[(val:string) => !!val || `${'กรุณาเลือกระดับการลงโทษทางวินัย'}`]"
hide-bottom-space
:label="`${'ระดับการลงโทษทางวินัย'}`"
@update:modelValue="clickEditRow"
emit-value
map-options
option-label="name"
:options="Ops.levelOptions"
option-value="id"
use-input
input-debounce="0"
@filter="(inputValue:string,
doneFn:Function) => filterSelector(inputValue, doneFn,'levelOptions'
) "
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="unStigma"
hide-bottom-space
:label="`${'ล้างมลทิน'}`"
@update:modelValue="clickEditRow"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="refCommandNo"
hide-bottom-space
:label="`${'เลขที่คำสั่ง'}`"
:rules="[(val:string) => !!val || `${'กรุณากรอกเลขที่คำสั่ง'}`]"
@update:modelValue="clickEditRow"
>
<template v-slot:append>
<q-icon
name="mdi-file"
class="cursor-pointer"
@click="resetFilter"
/>
</template>
</q-input>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
:readonly="!edit"
v-model="refCommandDate"
:locale="'th'"
autoApply
:enableTimePicker="false"
@update:modelValue="clickEditRow"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
:borderless="!edit"
:model-value="
refCommandDate == null ? null : date2Thai(refCommandDate)
"
hide-bottom-space
:label="`${'เอกสารอ้างอิง (ลงวันที่)'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
:style="
edit
? 'color: var(--q-primary)'
: 'color: var(--q-grey)'
"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
</div>
</q-card-section>
<q-separator />
<DialogFooter
:cancel="clickCancel"
:edit="clickEdit"
:save="clickSave"
:validate="validateData"
:clickNext="clickNext"
:clickPrevious="clickPrevious"
:clickDelete="clickDelete"
v-model:editvisible="edit"
v-model:next="next"
v-model:previous="previous"
v-model:modalEdit="modalEdit"
/>
</q-form>
</q-card>
</q-dialog>
<HistoryTable
:rows="rowsHistory"
:columns="columnsHistory"
:filter="filterHistory"
:visible-columns="visibleColumnsHistory"
v-model:modal="modalHistory"
v-model:inputfilter="filterHistory"
v-model:inputvisible="visibleColumnsHistory"
v-model:tittle="tittleHistory"
>
<template #columns="props">
<q-tr :props="props">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div
v-if="
col.name == 'date' ||
col.name == 'refCommandDate' ||
col.name == 'createdAt'
"
class="table_ellipsis"
>
{{ col.value == null ? null : date2Thai(col.value) }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
</q-tr>
</template>
</HistoryTable>
</template>
<script setup lang="ts">
import { onMounted, ref, watch } from "vue";
import { useRoute } from "vue-router";
import { useCounterMixin } from "@/stores/mixin";
import { useComponentProfileDataStore } from "@/components/information/store/store";
import ProfileTable from "@/components/information/Table.vue";
import DialogHeader from "@/components/information/DialogHeader.vue";
import DialogFooter from "@/components/information/DialogFooter.vue";
import { useQuasar } from "quasar";
import type {
RequestItemsObject,
DataProps,
} from "@/components/information/interface/request/Discipline";
import type { ResponseObject } from "@/components/information/interface/response/Discipline";
import type {
DisciplineOps,
DataOption,
} from "@/components/information/interface/index/Main";
import HistoryTable from "@/components/TableHistory.vue";
import http from "@/plugins/http";
import config from "@/app.config";
import type { QTableProps } from "quasar";
const props = defineProps({
statusEdit: {
type: Boolean,
required: true,
},
});
const $q = useQuasar();
const store = useComponentProfileDataStore();
const { profileData, changeProfileColumns } = store;
const mixin = useCounterMixin();
const { date2Thai, success, dateToISO, messageError, showLoader, hideLoader } =
mixin;
const route = useRoute();
const id = ref<string>("");
const level = ref<string>();
const Ops = ref<DisciplineOps>({
levelOptions: [
{
name: "ภาคทัณฑ์",
id: "ภาคทัณฑ์",
},
{
name: "ตัดเงินเดือน",
id: "ตัดเงินเดือน",
},
{
name: "ลดขั้นเงินเดือน",
id: "ลดขั้นเงินเดือน",
},
{
name: "ปลดออก",
id: "ปลดออก",
},
{
name: "ไล่ออก",
id: "ไล่ออก",
},
],
});
const OpsFilter = ref<DisciplineOps>({
levelOptions: [
{
name: "ภาคทัณฑ์",
id: "ภาคทัณฑ์",
},
{
name: "ตัดเงินเดือน",
id: "ตัดเงินเดือน",
},
{
name: "ลดขั้นเงินเดือน",
id: "ลดขั้นเงินเดือน",
},
{
name: "ปลดออก",
id: "ปลดออก",
},
{
name: "ไล่ออก",
id: "ไล่ออก",
},
],
});
const detail = ref<string>();
const unStigma = ref<string>();
const refCommandNo = ref<string>();
const refCommandDate = ref<Date | null>(new Date());
const date = ref<Date>(new Date());
const myForm = ref<any>(); //form data input
const edit = ref<boolean>(false); // dialog
const modal = ref<boolean>(false); //modal add detail
const modalEdit = ref<boolean>(false); //modal
const rawItem = ref<RequestItemsObject>(); // row
const rowIndex = ref<number>(0); //index row
const previous = ref<boolean>(); //
const next = ref<boolean>(); //
const editRow = ref<boolean>(false); //
const rowsHistory = ref<RequestItemsObject[]>([]); //select data history
const tittleHistory = ref<string>("ประวัติแก้ไขวินัย"); //
const filterHistory = ref<string>(""); //search data table history
const modalHistory = ref<boolean>(false); //modal
const checkValidate = ref<boolean>(false); //validate data
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const rows = ref<RequestItemsObject[]>([]);
const filter = ref<string>(""); //search data table
const visibleColumns = ref<String[]>([]);
profileData.discipline.columns.length == 0
? (visibleColumns.value = [
"level",
"detail",
"unStigma",
"refCommandNo",
"refCommandDate",
"date",
])
: (visibleColumns.value = profileData.discipline.columns);
const columns = ref<QTableProps["columns"]>([
{
name: "date",
align: "left",
label: "วัน เดือน ปี",
sortable: true,
field: "date",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "level",
align: "left",
label: "ระดับการลงโทษทางวินัย",
sortable: true,
field: "level",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "unStigma",
align: "left",
label: "ล้างมลทิน",
sortable: true,
field: "unStigma",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandNo",
align: "left",
label: "เลขที่คำสั่ง",
sortable: true,
field: "refCommandNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandDate",
align: "left",
label: "เอกสารอ้างอิง (ลงวันที่)",
sortable: true,
field: "refCommandDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const columnsHistory = ref<QTableProps["columns"]>([
{
name: "date",
align: "left",
label: "วัน เดือน ปี",
sortable: true,
field: "date",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "level",
align: "left",
label: "ระดับการลงโทษทางวินัย",
sortable: true,
field: "level",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "unStigma",
align: "left",
label: "ล้างมลทิน",
sortable: true,
field: "unStigma",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandNo",
align: "left",
label: "เลขที่คำสั่ง",
sortable: true,
field: "refCommandNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandDate",
align: "left",
label: "เอกสารอ้างอิง (ลงวันที่)",
sortable: true,
field: "refCommandDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "createdFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const visibleColumnsHistory = ref<String[]>([
"level",
"detail",
"unStigma",
"refCommandNo",
"refCommandDate",
"date",
"createdFullName",
"createdAt",
]);
watch(visibleColumns, async (count: String[], prevCount: String[]) => {
await changeProfileColumns("discipline", count);
});
onMounted(async () => {
await fetchData();
});
const filterSelector = (val: string, update: Function, refData: string) => {
switch (refData) {
case "levelOptions":
update(() => {
Ops.value.levelOptions = OpsFilter.value.levelOptions.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
default:
break;
}
};
const fetchData = async () => {
if (profileId.value) {
showLoader();
await http
.get(config.API.profileDisId(profileId.value))
.then((res) => {
let data = res.data.result;
rows.value = [];
data.map((e: ResponseObject) => {
rows.value.push({
id: e.id,
level: e.level,
detail: e.detail,
unStigma: e.unStigma,
refCommandNo: e.refCommandNo,
refCommandDate:
e.refCommandDate == null ? null : new Date(e.refCommandDate),
date: new Date(e.date),
createdFullName: e.createdFullName,
createdAt: new Date(e.createdAt),
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
};
/**
* กดดอมลกอนหน
*/
const clickPrevious = async () => {
edit.value = false;
rowIndex.value -= 1;
await getData();
await checkRowPage();
};
/**
* กดดอมลตอไป
*/
const clickNext = async () => {
edit.value = false;
rowIndex.value += 1;
await getData();
await checkRowPage();
};
/**
* กดดอมลตอไป
*/
const getData = () => {
const row = rows.value[rowIndex.value];
level.value = row.level;
detail.value = row.detail;
unStigma.value = row.unStigma;
refCommandNo.value = row.refCommandNo;
refCommandDate.value = row.refCommandDate;
date.value = row.date;
id.value = row.id;
};
/**
* เชคปมดอม อน อไป าตองแสดงไหม
*/
const checkRowPage = () => {
editRow.value = false;
next.value = true;
previous.value = true;
if (rowIndex.value + 1 >= rows.value.length) {
next.value = false;
}
if (rowIndex.value - 1 < 0) {
previous.value = false;
}
};
/**
* กดปมแกไขใน dialog
*/
const clickEdit = () => {
editRow.value = false;
next.value = false;
previous.value = false;
};
/**
* กดปมเพมดานบน table
*/
const clickAdd = async () => {
editRow.value = false;
await addData();
};
/**
* กดบนทกใน dialog
*/
const clickSave = async () => {
myForm.value.validate().then(async (result: boolean) => {
if (result) {
if (modalEdit.value) {
await editData();
} else {
await saveData();
}
}
});
};
/**
* นทกเพมขอม
*/
const saveData = async () => {
if (profileId.value) {
showLoader();
await http
.post(config.API.profileDisId(profileId.value), {
id: id.value,
level: level.value,
detail: detail.value,
unStigma: unStigma.value,
refCommandNo: refCommandNo.value,
refCommandDate:
refCommandDate.value == null ? null : dateToISO(refCommandDate.value),
date: dateToISO(date.value),
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
}
};
/**
* นทกแกไขขอม
*/
const editData = async () => {
showLoader();
await http
.put(config.API.profileDisId(id.value), {
id: id.value,
level: level.value,
detail: detail.value,
unStigma: unStigma.value,
refCommandNo: refCommandNo.value,
refCommandDate:
refCommandDate.value == null ? null : dateToISO(refCommandDate.value),
date: dateToISO(date.value),
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
};
/**
* ลบลบขอม
*/
const clickDelete = async () => {
$q.dialog({
title: `ลบข้อมูล`,
message: `ต้องการทำการลบข้อมูลนี้ใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
})
.onOk(async () => {
showLoader();
await http
.delete(config.API.profileDisId(id.value))
.then((res) => {
success($q, "ลบข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
})
.onCancel(async () => {
await fetchData();
});
};
/**
* กดป dialog
*/
const clickClose = async () => {
if (editRow.value == true) {
$q.dialog({
title: `ข้อมูลมีการแก้ไข`,
message: `ยืนยันที่จะปิดโดยไม่บันทึกใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
}).onOk(async () => {
modal.value = false;
next.value = false;
previous.value = false;
});
} else {
modal.value = false;
next.value = false;
previous.value = false;
}
};
/**
* กดเลอกขอมลทจะแกไข
* @param props props ใน row เลอก
*/
const selectData = async (props: DataProps) => {
modalEdit.value = true;
modal.value = true;
edit.value = false;
rawItem.value = props.row;
rowIndex.value = props.rowIndex;
level.value = props.row.level;
detail.value = props.row.detail;
unStigma.value = props.row.unStigma;
refCommandNo.value = props.row.refCommandNo;
refCommandDate.value = props.row.refCommandDate;
date.value = props.row.date;
id.value = props.row.id;
await checkRowPage();
};
/**
* กดปมเพมบน table
*/
const addData = () => {
modalEdit.value = false;
modal.value = true;
edit.value = true;
level.value = "";
detail.value = "";
unStigma.value = "";
refCommandNo.value = "";
refCommandDate.value = null;
date.value = new Date();
};
/**
* งกนปมยกเลกการแกไขขอม
*/
const clickCancel = async () => {
if (editRow.value == true) {
$q.dialog({
title: `ข้อมูลมีการแก้ไข`,
message: `ยืนยันยกเลิกการแก้ไขใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
}).onOk(async () => {
edit.value = false;
await checkRowPage();
await getData();
});
} else {
edit.value = false;
await checkRowPage();
}
};
/**
* เชความการแกไขขอม
*/
const clickEditRow = () => {
editRow.value = true;
};
/**
* งชนดอมลประวแกไขขอมลทเลอก
* @param row อม row ประวการแกไข
*/
const clickHistory = async (row: RequestItemsObject) => {
modalHistory.value = true;
showLoader();
await http
.get(config.API.profileDisHisId(row.id))
.then((res) => {
let data = res.data.result;
rowsHistory.value = [];
data.map((e: ResponseObject) => {
rowsHistory.value.push({
id: e.id,
level: e.level,
detail: e.detail,
unStigma: e.unStigma,
refCommandNo: e.refCommandNo,
refCommandDate:
e.refCommandDate == null ? null : new Date(e.refCommandDate),
date: new Date(e.date),
createdFullName: e.createdFullName,
createdAt: new Date(e.createdAt),
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
};
/**
* validate input ใน dialog
*/
const validateData = async () => {
checkValidate.value = true;
await myForm.value.validate().then((result: boolean) => {
if (result == false) {
checkValidate.value = false;
}
});
};
/**
* class ดรปแบบแสดงระหวางขอมลทแกไขหรอแสดงเฉยๆ
* @param val อม input สำหรบแกไขหรอไม
*/
const getClass = (val: boolean) => {
return {
"full-width inputgreen cursor-pointer": val,
"full-width cursor-pointer": !val,
};
};
const resetFilter = () => {};
</script>
<style lang="scss">
.modalfix {
position: fixed !important;
}
</style>

View file

@ -0,0 +1,269 @@
<template>
<q-card flat bordered class="col-12 row q-px-lg q-py-md">
<HeaderTop
v-model:edit="edit"
header="เอกสารหลักฐาน"
icon="mdi-file-document"
:history="false"
:changeBtn="changeBtn"
:disable="statusEdit"
:save="uploadData"
/>
<div class="row col-12 q-pt-sm">
<q-card bordered flat class="full-width">
<q-list separator>
<q-item v-for="file in files" :key="file.key" class="q-my-xs">
<q-item-section>
<q-item-label class="full-width ellipsis">
{{ file.fileName }}
</q-item-label>
<q-item-label caption> </q-item-label>
</q-item-section>
<q-item-section top side>
<div class="row col-12">
<q-btn
class="gt-xs"
size="12px"
flat
dense
round
color="blue"
icon="mdi-download-outline"
@click="downloadData(file.file)"
/>
<q-btn
class="gt-xs"
size="12px"
flat
dense
round
color="red"
icon="mdi-delete-outline"
v-show="edit"
@click="deleteData(file.id)"
/>
</div>
</q-item-section>
</q-item>
</q-list>
</q-card>
<q-input
v-if="edit"
class="q-mt-sm col-12 q-pb-xs"
outlined
dense
lazy-rules
v-model="name"
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณากรอกชื่อเอกสาร'}`]"
:label="`${'ชื่อเอกสาร'}`"
/>
<q-uploader
v-if="edit"
color="gray"
type="file"
flat
@factory="uploadData"
ref="uploader"
class="full-width"
text-color="white"
:max-size="10000000"
accept=".jpg,.png,.pdf,.csv,.doc,.docx"
bordered
label="[ไฟล์ jpg,png,pdf,csv,doc,docx ขนาดไม่เกิน 10MB]"
@added="fileAdd"
>
<template v-slot:header="scope">
<div class="row no-wrap items-center q-pa-sm q-gutter-xs">
<q-btn
v-if="scope.queuedFiles.length > 0"
icon="clear_all"
@click="scope.removeQueuedFiles"
round
dense
flat
>
<q-tooltip>ลบทงหมด</q-tooltip>
</q-btn>
<q-btn
v-if="scope.uploadedFiles.length > 0"
icon="done_all"
@click="scope.removeUploadedFiles"
round
dense
flat
>
<q-tooltip>ลบไฟลปโหลด</q-tooltip>
</q-btn>
<q-spinner v-if="scope.isUploading" class="q-uploader__spinner" />
<div class="col">
<div class="q-uploader__title">
{{ "[ไฟล์ jpg,png,pdf,csv,doc,docx ขนาดไม่เกิน 10MB]" }}
</div>
<div class="q-uploader__subtitle">
{{ scope.uploadSizeLabel }} / {{ scope.uploadProgressLabel }}
</div>
</div>
<q-btn
v-if="scope.canAddFiles"
type="a"
icon="add_box"
@click="scope.pickFiles"
round
dense
flat
>
<q-uploader-add-trigger />
<q-tooltip>เลอกไฟล</q-tooltip>
</q-btn>
<q-btn
v-if="scope.isUploading"
icon="clear"
@click="scope.abort"
round
dense
flat
>
<q-tooltip>ยกเลกการอปโหลด</q-tooltip>
</q-btn>
</div>
</template>
</q-uploader>
</div>
</q-card>
</template>
<script setup lang="ts">
import { onMounted, computed, ref, watch } from "vue";
import { useQuasar, QForm } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useRoute } from "vue-router";
import HeaderTop from "@/components/information/top.vue";
import http from "@/plugins/http";
import config from "@/app.config";
const props = defineProps({
statusEdit: {
type: Boolean,
required: true,
},
notiNoEdit: {
type: Function,
default: () => console.log("not function"),
},
});
const emit = defineEmits(["update:statusEdit"]);
const $q = useQuasar(); // show dialog
const mixin = useCounterMixin();
const route = useRoute();
const { success, messageError, showLoader, hideLoader } = mixin;
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const edit = ref<boolean>(false);
const uploader = ref<any>();
const files = ref<any>([]);
const file = ref<any>([]);
const name = ref<string>("");
onMounted(async () => {
await getData();
});
const getData = async () => {
if (profileId.value) {
showLoader();
await http
.get(config.API.profilePaperId(profileId.value))
.then((res) => {
const data = res.data.result;
files.value = data;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
};
const deleteData = async (id: string) => {
showLoader();
await http
.delete(config.API.profilePaperId(id))
.then((res) => {
const data = res.data.result;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await getData();
});
};
const uploadData = async () => {
if (profileId.value) {
if (file.value.length > 0) {
const blob = file.value.slice(0, file.value[0].size);
const newFile = new File(blob, name.value, {
type: file.value[0].type,
});
const formData = new FormData();
formData.append("avatar", newFile);
formData.append("moss", "newFile");
showLoader();
await http
.post(config.API.profilePaperId(profileId.value), formData)
.then((res) => {
const data = res.data.result;
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
uploader.value.reset();
name.value = "";
edit.value = false;
emit("update:statusEdit", false);
await getData();
});
} else {
// modalError(
// $q,
// "",
// ""
// );
getData();
}
}
};
const fileAdd = async (val: any) => {
name.value = val[0].name;
file.value = val;
};
const downloadData = async (path: string) => {
window.open(path);
};
const changeBtn = async () => {
name.value = "";
if (edit.value == true) {
if (props.statusEdit === true) {
edit.value = false;
props.notiNoEdit();
} else {
emit("update:statusEdit", true);
}
} else {
emit("update:statusEdit", false);
}
};
</script>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,761 @@
<template>
<q-card flat bordered class="col-12 q-px-lg q-py-md q-mt-md">
<HeaderTop
v-model:edit="edit"
header="ข้อมูลราชการ"
icon="mdi-account-tie"
:save="saveData"
:history="true"
:changeBtn="changeBtn"
:disable="statusEdit"
:cancel="refreshData"
:historyClick="clickHistory"
/>
<q-form ref="myform" class="col-12">
<div class="row col-12 items-center q-col-gutter-x-xs q-col-gutter-y-xs">
<div class="col-xs-12">
<q-input
:class="getClass(false)"
hide-bottom-space
dense
lazy-rules
readonly
borderless
:model-value="govermentData.ocId"
:label="`${'สังกัด'}`"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3" v-if="employeeClass != 'temp'">
<q-input
:class="getClass(false)"
hide-bottom-space
dense
lazy-rules
readonly
borderless
:model-value="govermentData.numberId"
:label="`${'ตำแหน่งเลขที่'}`"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
:class="getClass(false)"
hide-bottom-space
dense
lazy-rules
readonly
borderless
:model-value="govermentData.positionId"
:label="`${'ตำแหน่ง'}`"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
:class="getClass(false)"
hide-bottom-space
dense
lazy-rules
readonly
borderless
:model-value="govermentData.positionPathSide"
:label="`${'ด้าน/สาขา'}`"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
:class="getClass(false)"
hide-bottom-space
dense
lazy-rules
readonly
borderless
:model-value="govermentData.positionLine"
:label="`${'สายงาน'}`"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
:class="getClass(false)"
hide-bottom-space
dense
lazy-rules
readonly
borderless
:model-value="govermentData.positionType"
:label="`${'ประเภท'}`"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3" v-if="employeeClass != 'temp'">
<q-input
:class="getClass(false)"
hide-bottom-space
dense
lazy-rules
readonly
borderless
:model-value="govermentData.positionLevel"
:label="`${'ระดับ'}`"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
:class="getClass(false)"
hide-bottom-space
dense
lazy-rules
readonly
borderless
:model-value="govermentData.positionExecutive"
:label="`${'ตำแหน่งทางการบริหาร'}`"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
:class="getClass(false)"
hide-bottom-space
dense
lazy-rules
readonly
borderless
:model-value="govermentData.positionExecutiveSide"
:label="`${'ด้านตำแหน่งทางการบริหาร'}`"
/>
</div>
<div class="col-12 q-py-md"><q-separator /></div>
<div class="col-xs-6 col-sm-3 col-md-3">
<datepicker
v-model="govermentData.containDate"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
:max-date="new Date()"
:disabled="!edit"
@update:model-value="handleDate"
>
<template #year="{ year }">
{{ year + 543 }}
</template>
<template #year-overlay-value="{ value }">
{{ parseInt(value + 543) }}
</template>
<template #trigger>
<q-input
:class="getClass(edit)"
hide-bottom-space
:outlined="edit"
dense
:readonly="!edit"
:borderless="!edit"
:model-value="date2Thai(new Date(govermentData.containDate))"
:rules="[
(val) =>
!!val ||
`${
profileType == 'officer'
? 'กรุณาเลือกวันที่บรรจุ'
: 'กรุณาเลือกวันที่แต่งตั้ง'
}`,
]"
:label="`${
profileType == 'officer' ? 'วันที่บรรจุ' : 'วันที่แต่งตั้ง'
}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
:style="
edit ? 'color: var(--q-primary)' : 'color: var(--q-grey)'
"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<datepicker
v-model="govermentData.workDate"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
:max-date="new Date()"
:disabled="!edit"
>
<template #year="{ year }">
{{ year + 543 }}
</template>
<template #year-overlay-value="{ value }">
{{ parseInt(value + 543) }}
</template>
<template #trigger>
<q-input
:class="getClass(edit)"
hide-bottom-space
:outlined="edit"
dense
:readonly="!edit"
:borderless="!edit"
:model-value="date2Thai(new Date(govermentData.workDate))"
:rules="[
(val) =>
!!val ||
`${
profileType == 'officer'
? 'กรุณาเลือกเริ่มปฎิบัติราชการ'
: 'กรุณาเลือกวันที่จ้างและแต่งตั้งมีผล'
}`,
]"
:label="`${
profileType == 'officer'
? 'เริ่มปฎิบัติราชการ'
: 'วันที่จ้างและแต่งตั้งมีผล'
}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
:style="
edit ? 'color: var(--q-primary)' : 'color: var(--q-grey)'
"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-xs-6 col-sm-6 col-md-6" v-if="profileType == 'officer'">
<q-input
:class="getClass(edit)"
hide-bottom-space
:outlined="edit"
dense
lazy-rules
stack-label
:readonly="!edit"
:borderless="!edit"
v-model="govermentData.reasonSameDate"
:label="`${'เหตุผลกรณีไม่ตรงวัน'}`"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
:class="getClass(false)"
hide-bottom-space
dense
lazy-rules
readonly
borderless
:model-value="govermentData.retireDate"
:label="`${'วันเกษียณอายุ'}`"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
:class="getClass(false)"
hide-bottom-space
dense
lazy-rules
readonly
borderless
:model-value="govermentData.ageAll"
:label="`${'อายุราชการ'}`"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
:class="getClass(false)"
hide-bottom-space
dense
lazy-rules
readonly
borderless
:model-value="govermentData.absent"
:label="`${'ขาดราชการ'}`"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
:class="getClass(false)"
hide-bottom-space
dense
lazy-rules
readonly
borderless
:model-value="govermentData.age"
:label="`${'อายุราชการเกื้อกูล'}`"
/>
</div>
</div>
</q-form>
</q-card>
<HistoryTable
:rows="rowsHistory"
:columns="columnsHistory"
:filter="filterHistory"
:visible-columns="visibleColumnsHistory"
v-model:modal="modalHistory"
v-model:inputfilter="filterHistory"
v-model:inputvisible="visibleColumnsHistory"
v-model:tittle="tittleHistory"
>
<template #columns="props">
<q-tr :props="props">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div
v-if="
col.name == 'dateStart' ||
col.name == 'dateAppoint' ||
col.name == 'createdAt'
"
class="table_ellipsis"
>
{{ date2Thai(col.value) }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
</q-tr>
</template>
</HistoryTable>
</template>
<script setup lang="ts">
import { onMounted, ref, watch } from "vue";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar } from "quasar";
import type { Goverment } from "@/components/information/interface/profileType";
import { defaultGoverment } from "@/components/information/interface/profileType";
import HeaderTop from "@/components/information/top.vue";
import http from "@/plugins/http";
import config from "@/app.config";
import HistoryTable from "@/components/TableHistory.vue";
import type { RequestItemsHistoryObject } from "@/components/information/interface/request/Government";
import type { ResponseObject } from "@/components/information/interface/response/Government";
import { useRoute } from "vue-router";
import { useComponentProfileDataStore } from "@/components/information/store/store";
import { storeToRefs } from "pinia";
import type { QTableProps } from "quasar";
const props = defineProps({
statusEdit: {
type: Boolean,
required: true,
},
profileType: {
type: String,
required: true,
},
employeeClass: {
type: String,
required: true,
},
});
const emit = defineEmits(["update:statusEdit"]);
const route = useRoute();
const $q = useQuasar();
const mixin = useCounterMixin();
const { date2Thai, success, dateToISO, messageError, showLoader, hideLoader } =
mixin;
const profileStore = useComponentProfileDataStore();
const { birthDate, retireText } = storeToRefs(profileStore);
const edit = ref<boolean>(false);
const govermentData = ref<Goverment>(defaultGoverment);
const myform = ref<any>();
const rowsHistory = ref<RequestItemsHistoryObject[]>([]); //select data history
const tittleHistory = ref<string>("ประวัติแก้ไขข้อมูลราชการ"); //
const filterHistory = ref<string>(""); //search data table history
const modalHistory = ref<boolean>(false); //modal
const columnsHistory = ref<QTableProps["columns"]>([
{
name: "oc",
align: "left",
label: "สังกัด",
sortable: true,
field: "oc",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "positionPathSide",
align: "left",
label: "ตำแหน่ง",
sortable: true,
field: "positionPathSide",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "position",
align: "left",
label: "ด้าน/สาขา",
sortable: true,
field: "position",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "posNo",
align: "left",
label: "ตำแหน่งเลขที่",
sortable: true,
field: "posNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "positionLine",
align: "left",
label: "สายงาน",
sortable: true,
field: "positionLine",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "positionType",
align: "left",
label: "ประเภทตำแหน่ง",
sortable: true,
field: "positionType",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "positionLevel",
align: "left",
label: "ระดับตำแหน่ง",
sortable: true,
field: "positionLevel",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "positionExecutive",
align: "left",
label: "ตำแหน่งทางการบริหาร",
sortable: true,
field: "positionExecutive",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "positionExecutiveSide",
align: "left",
label: "ด้านทางการบริหาร",
sortable: true,
field: "positionExecutiveSide",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "dateAppoint",
align: "left",
label: "วันที่บรรจุ",
sortable: true,
field: "dateAppoint",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "dateStart",
align: "left",
label: "เริ่มปฎิบัติราชการ",
sortable: true,
field: "dateStart",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "retireDate",
align: "left",
label: "วันเกษียณอายุ",
sortable: true,
field: "retireDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "govAge",
align: "left",
label: "อายุราชการ",
sortable: true,
field: "govAge",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "govAgeAbsent",
align: "left",
label: "ขาดราชการ",
sortable: true,
field: "govAgeAbsent",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "govAgePlus",
align: "left",
label: "อายุราชการเกื้อกูล",
sortable: true,
field: "govAgePlus",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "reasonSameDate",
align: "left",
label: "เหตุผลกรณีไม่ตรงวัน",
sortable: true,
field: "reasonSameDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "createdFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const visibleColumnsHistory = ref<String[]>([
"oc",
"position",
"positionPathSide",
"posNo",
"positionLine",
"positionType",
"positionLevel",
"positionexecutive",
"positionExecutiveSide",
"dateAppoint",
"dateStart",
"retireDate",
"govAge",
"govAgeAbsent",
"govAgePlus",
"reasonSameDate",
"createdFullName",
"createdAt",
]);
onMounted(async () => {
await fetchData();
emit("update:statusEdit", false);
});
watch(birthDate, async () => {
await fetchData();
});
watch(retireText, async () => {
govermentData.value.retireDate = retireText.value;
});
const fetchData = async () => {
showLoader();
await http
.get(config.API.profileGovId(route.params.id.toString()))
.then((res) => {
const data: ResponseObject = res.data.result;
govermentData.value.ocId = data.oc;
govermentData.value.positionId = data.position;
govermentData.value.positionPathSide = data.positionPathSide;
govermentData.value.positionLine = data.positionLine;
govermentData.value.positionType = data.positionType;
govermentData.value.positionLevel = data.positionLevel;
govermentData.value.numberId = data.posNo;
govermentData.value.positionExecutive = data.positionExecutive;
govermentData.value.positionExecutiveSide = data.positionExecutiveSide;
govermentData.value.containDate = new Date(data.dateAppoint);
govermentData.value.workDate = new Date(data.dateStart);
govermentData.value.retireDate = data.retireDate;
govermentData.value.absent = data.govAgeAbsent;
govermentData.value.age = data.govAgePlus;
govermentData.value.ageAll = data.govAge;
govermentData.value.reasonSameDate = data.reasonSameDate;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
hideLoader();
});
};
const refreshData = async () => {
myform.value.reset();
await fetchData();
};
const editData = async () => {
showLoader();
await http
.put(config.API.profileGovId(route.params.id.toString()), {
dateAppoint: dateToISO(govermentData.value.containDate),
dateStart: dateToISO(govermentData.value.workDate),
reasonSameDate: govermentData.value.reasonSameDate,
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
emit("update:statusEdit", false);
edit.value = false;
await refreshData();
});
};
const saveData = async () => {
await myform.value.validate().then(async (success: boolean) => {
if (success) {
await editData();
} else {
}
});
};
const handleDate = async (modelData: Date) => {
govermentData.value.containDate = modelData;
await fetchCalAgeGov(modelData);
};
const fetchCalAgeGov = async (date: Date) => {
showLoader();
const body = {
dateAppoint: date,
};
await http
.post(config.API.profileCalGovId(route.params.id.toString()), body)
.then((res) => {
const result = res.data.result;
govermentData.value.ageAll = result;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
hideLoader();
});
};
/**
* งชนดอมลประวแกไขขอมลทเลอก
*/
const clickHistory = async () => {
modalHistory.value = true;
showLoader();
await http
.get(config.API.profileGovHisId(route.params.id.toString()))
.then((res) => {
let data = res.data.result;
rowsHistory.value = [];
data.map((e: RequestItemsHistoryObject) => {
rowsHistory.value.push({
oc: e.oc,
position: e.position,
positionPathSide: e.positionPathSide,
posNo: e.posNo,
positionLine: e.positionLine,
positionType: e.positionType,
positionLevel: e.positionLevel,
positionExecutive: e.positionExecutive,
positionExecutiveSide: e.positionExecutiveSide,
dateAppoint: new Date(e.dateAppoint),
dateStart: new Date(e.dateStart),
retireDate: e.retireDate,
govAge: e.govAge,
govAgeAbsent: e.govAgeAbsent,
govAgePlus: e.govAgePlus,
reasonSameDate: e.reasonSameDate,
createdFullName: e.createdFullName,
createdAt: new Date(e.createdAt),
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
};
const changeBtn = async () => {
if (edit.value == true) {
if (props.statusEdit === true) {
edit.value = false;
} else {
emit("update:statusEdit", true);
}
} else {
emit("update:statusEdit", false);
}
};
const getClass = (val: boolean) => {
return {
"full-width inputgreen cursor-pointer": val,
"full-width cursor-pointer": !val,
};
};
</script>

View file

@ -0,0 +1,453 @@
<template>
<q-page-sticky
position="top"
expand
class="bg-grey-2 text-white"
style="z-index: 99; padding: 0% 1% 0% 1%"
>
<div class="row col-12 q-gutter-sm q-pb-sm text-dark no-wrap items-center">
<q-btn
flat
round
class="bg-teal-1 full-height"
color="primary"
icon="mdi-chevron-left"
dense
@click="router.go(-1)"
>
</q-btn>
<q-avatar
v-if="imageUrl == null"
size="65px"
rounded
class="containerimage"
>
<img
src="@/assets/avatar_user.jpg"
class="bg-grey-3"
style="object-fit: cover"
/>
<div
class="overlay absolute-bottom text-subtitle2 text-center cursor-pointer"
@click="clickImage()"
>
<q-icon name="mdi-camera" size="18px" color="blue">
<q-tooltip>ปเดตรปภาพ</q-tooltip>
</q-icon>
<input
type="file"
style="display: none"
ref="inputImage"
accept="image/*"
@change="uploadImage"
/>
</div>
</q-avatar>
<q-avatar v-else size="65px" rounded class="containerimage">
<img :src="imageUrl" class="bg-grey-3" style="object-fit: cover" />
<div
class="overlay absolute-bottom text-subtitle2 text-center cursor-pointer"
@click="clickImage()"
>
<q-icon name="mdi-camera" size="18px" color="blue">
<q-tooltip>ปเดตรปภาพ</q-tooltip>
</q-icon>
<input
type="file"
style="display: none"
ref="inputImage"
accept="image/*"
@change="uploadImage"
/>
</div>
</q-avatar>
<div class="row items-center text-dark q-ml-md">
<div class="column">
<div class="text-bold q-pb-xs text-name">
{{ fullname }}
</div>
<div>{{ position }}</div>
</div>
</div>
<q-space />
<slot name="button-right" />
</div>
</q-page-sticky>
<!---------------------------------------- Dialog เลอก Image ---------------------------------------->
<q-dialog v-model="dialogImage" persistent>
<q-card style="width: 100vw; max-width: 60vw">
<q-card-section class="q-py-sm row">
<div class="text-h6">เลอกรปภาพ</div>
<q-space />
<q-btn
icon="close"
unelevated
round
dense
@click="closeImage"
style="color: #ff8080; background-color: #ffdede"
>
<q-tooltip>ดหนาน</q-tooltip>
</q-btn>
</q-card-section>
<q-separator />
<q-card-section class="col-12 row">
<div
class="row wrap items-start col-12 q-col-gutter-sm"
style="height: 320px; overflow: auto"
>
<div class="col-3" @click="addNewImage">
<div
style="
height: 160px;
max-width: 15vw;
display: flex !important;
justify-content: center;
align-items: center;
"
class="column rounded-borders cursor-pointer bg-active-image text-white"
>
<q-icon name="add" size="60px" color="white" />
<strong>ปโหลดรปภาพ</strong>
</div>
</div>
<div
v-for="n in images"
:key="n"
class="col-3"
@click="imageActive(n)"
>
<div :class="getClass(n)">
<q-img
v-if="n.avatar != null"
:src="n.avatar"
:class="imageClass(n)"
>
<div
class="absolute-bottom col-12 cursor-pointer flex justify-between items-center"
style="padding: 5px"
>
{{ date2Thai(n.createdDate) }}
<q-btn
v-if="!n.isActive"
icon="delete"
unelevated
dense
@click="deletePhoto(n.id)"
class="bg-white"
style="color: #ff8080"
/>
</div>
</q-img>
</div>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn
icon="check"
dense
flat
round
@click="selectAvatarHistory"
color="positive"
>
<q-tooltip>เลอกรปภาพ</q-tooltip>
</q-btn>
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
const props = defineProps({
fetchData: {
type: Function,
default: () => console.log("not function"),
},
fetchSave: {
type: Function,
default: () => console.log("not function"),
},
fetchDelete: {
type: Function,
default: () => console.log("not function"),
},
fetchUpload: {
type: Function,
default: () => console.log("not function"),
},
});
const $q = useQuasar();
const mixin = useCounterMixin();
const {
date2Thai,
dateToISO,
messageError,
dialogMessage,
success,
showLoader,
hideLoader,
} = mixin;
const route = useRoute();
const router = useRouter();
const fullname = ref<string>("");
const position = ref<string>("");
const imageUrl = ref<any>(null);
const inputImage = ref<any>(null);
const activeImage = ref<any | null>(null);
const dialogImage = ref<boolean>(false);
const images = ref<any>([]);
onMounted(async () => {
await fetchData();
});
const closeImage = () => {
dialogImage.value = false;
};
const clickImage = async () => {
// ***************************************************************************************************
// ****************** fetch data ******************
// ***************************************************************************************************
await fetchAvatarHistory();
dialogImage.value = true;
};
const uploadImage = async (e: any) => {
let input = e.target.files;
if (input.length > 0) {
const formData = new FormData();
formData.append("FileData", input[0]);
showLoader();
await http
.post(config.API.profileAvatarId(route.params.id.toString()), formData)
.then((res) => {})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
dialogImage.value = false;
});
return;
}
};
const selectAvatarHistory = async () => {
if (activeImage.value == null) {
dialogMessage(
$q,
"ไม่สามารถเปลี่ยนรูปได้",
"กรุณาเลือกรูปที่ต้องการเปลี่ยน",
"warning",
undefined,
"orange",
undefined,
undefined,
true
);
return;
}
showLoader();
await http
.put(config.API.profileAvatarId(route.params.id.toString()), {
avatar: activeImage.value.avatarId,
})
.then((res) => {
dialogImage.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
// ***************************************************************************************************
// ****************** fetch data ID ******************
// ***************************************************************************************************
};
const imageActive = (n: any) => {
activeImage.value = n;
};
const addNewImage = () => {
inputImage.value.click();
imageActive(null);
};
const deletePhoto = async (id: string) => {
dialogMessage(
$q,
"ลบข้อมูล",
"ต้องการทำการลบข้อมูลนี้ใช่หรือไม่",
"delete",
undefined,
"red",
async () => await fetchDataDelete(id),
undefined,
false
);
};
const fetchDataDelete = async (id: string) => {
showLoader();
await http
.delete(config.API.profileAvatarHistoryId(id))
.then((res) => {
success($q, "ลบรูปภาพสำเร็จ");
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
await clickImage();
// dialogImage.value = false;
});
};
const fetchData = async () => {
showLoader();
await http
.get(config.API.profileAvatarId(route.params.id.toString()))
.then((res) => {
const data = res.data.result;
fullname.value = data.fullname;
imageUrl.value = data.avatar;
position.value = data.position;
// profileType.value = data.profileType;
// employeeClass.value =
// data.employeeClass == null ? "" : data.employeeClass;
// const reason = reasonOptions.value.filter(
// (r: DataOption) => r.id == data.leaveReason
// );
// if (reason.length > 0) {
// leaveReason.value = ` (: ${reason[0].name})`;
// } else {
// leaveReason.value = "";
// }
// reasonStatus.value = reason.length > 0 ? true : false;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
};
const fetchAvatarHistory = async () => {
showLoader();
await http
.get(config.API.profileAvatarHistoryId(route.params.id.toString()))
.then((res) => {
const data = res.data.result;
images.value = [];
data.map((e: any) => {
images.value.push({
id: e.id,
avatar: e.avatar,
avatarId: e.avatarId,
createdDate: new Date(e.createdDate),
isActive: e.isActive,
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
};
const imageClass = (n: any) => {
const val = n == activeImage.value;
return {
"rounded-borders bg-grey-2 image-size-default": val,
"rounded-borders bg-grey-2 image-size-full": !val,
};
};
const getClass = (n: any) => {
const val = n == activeImage.value;
return {
"rounded-borders border-green shadow-1": val,
"rounded-borders shadow-1": !val,
};
};
</script>
<style lang="scss" scoped>
.image-size-default {
height: 150px;
max-width: 15vw;
}
.image-size-full {
height: 160px;
max-width: 15vw;
}
.border-green {
border: 5px solid #52c688;
}
.area-div {
background-color: transparent;
}
.text-header {
font-size: 1.2em;
}
.containerimage {
position: relative;
}
.containerimage:hover .overlay {
opacity: 1;
}
.overlay {
background-color: #e4f2ffd2 !important;
opacity: 0;
padding: 2px !important;
}
.information:target {
padding-top: 84px;
}
.border-custom {
border: 0.5px solid #c3c3c3;
}
.bg-active-image {
background-color: #52c688;
}
.text-name {
font-size: 1.25em;
overflow: hidden;
}
</style>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,973 @@
<!-- card ประวการเปลยนช-นามสก -->
<template>
<div class="col-12">
<q-form ref="myForm">
<div class="col-12 q-pt-md q-pb-sm"><q-separator /></div>
<ProfileTable
:rows="rows"
:columns="columns"
:filter="filter"
:visible-columns="visibleColumns"
v-model:inputfilter="filter"
v-model:inputvisible="visibleColumns"
:add="clickAdd"
name="ประวัติการเปลี่ยนชื่อ-นามสกุล"
icon="mdi-book"
:statusEdit="!statusEdit"
>
<template #columns="props">
<q-tr :props="props">
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
@click="statusEdit ? selectData(props) : ''"
class="cursor-pointer"
>
<div v-if="col.name == 'status'" class="table_ellipsis">
{{ typeChangeName(col.value) }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
v-if="props.row.file != null"
color="green"
flat
dense
round
size="14px"
icon="mdi-file-document-outline"
@click="openFile(props.row.file)"
/>
<q-btn
color="info"
flat
dense
round
size="14px"
icon="mdi-history"
@click="clickHistory(props.row)"
/>
</q-td>
</q-tr>
</template>
</ProfileTable>
</q-form>
</div>
<!-- popup Edit window-->
<q-dialog v-model="modal" persistent>
<q-card style="width: 600px">
<q-form ref="myForm">
<DialogHeader tittle="เปลี่ยนชื่อ-นามสกุล" :close="clickClose" />
<q-separator />
<q-card-section class="q-p-sm">
<div
class="row col-12 items-center q-col-gutter-x-xs q-col-gutter-y-xs"
>
<div class="col-xs-6 col-sm-6 col-md-6">
<selector
:hide-dropdown-icon="!edit"
hide-bottom-space
:class="getClass(edit)"
:readonly="!edit"
:borderless="!edit"
:rules="[(val:string) => !!val || `${'กรุณาเลือก สถานะการเปลี่ยนชื่อ'}`]"
:outlined="edit"
dense
lazy-rules
v-model="status"
emit-value
map-options
option-label="name"
:options="Ops.statusOps"
option-value="id"
:label="`${'สถานะการเปลี่ยนชื่อ'}`"
use-input
input-debounce="0"
@filter="(inputValue:any,
doneFn:Function) => filterSelector(inputValue, doneFn,'statusOps'
) "
/>
</div>
<q-space class="col-6" />
<div class="col-xs-4 col-sm-4 col-md-4">
<selector
:hide-dropdown-icon="!edit"
hide-bottom-space
:class="getClass(edit)"
:readonly="!edit"
:borderless="!edit"
:rules="[(val:string) => !!val || `${'กรุณาเลือก คำนำหน้าชื่อ'}`]"
:outlined="edit"
dense
lazy-rules
v-model="prefixId"
emit-value
map-options
option-label="name"
:options="Ops.prefixOps"
option-value="id"
:label="`${'คำนำหน้าชื่อ'}`"
use-input
input-debounce="0"
@filter="(inputValue:any,
doneFn:Function) => filterSelector(inputValue, doneFn,'prefixOps'
) "
/>
</div>
<div class="col-xs-4 col-sm-4 col-md-4">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit || status == 'lastName'"
:borderless="!edit"
v-model="firstName"
:rules="[(val) => !!val || `${'กรุณากรอกชื่อ'}`]"
hide-bottom-space
:label="`${'ชื่อ'}`"
@update:modelValue="clickEditRow"
/>
</div>
<div class="col-xs-4 col-sm-4 col-md-4">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit || status == 'firstName'"
:borderless="!edit"
v-model="lastName"
:rules="[(val) => !!val || `${'กรุณากรอกนามสกุล'}`]"
hide-bottom-space
:label="`${'นามสกุล'}`"
@update:modelValue="clickEditRow"
/>
</div>
<q-btn
v-if="file != null && !edit"
color="green"
flat
dense
round
size="14px"
icon="mdi-file-document-outline"
@click="openFile(file)"
/>
<q-uploader
v-if="edit"
color="gray"
type="file"
flat
ref="uploader"
class="full-width"
text-color="white"
:max-size="10000000"
accept=".jpg,.png,.pdf,.csv,.doc,.docx"
bordered
label="[ไฟล์ jpg,png,pdf,csv,doc,docx ขนาดไม่เกิน 10MB]"
@added="fileAdd"
>
<template v-slot:header="scope">
<div class="row no-wrap items-center q-pa-sm q-gutter-xs">
<q-btn
v-if="scope.queuedFiles.length > 0"
icon="clear_all"
@click="scope.removeQueuedFiles"
round
dense
flat
>
<q-tooltip>ลบทงหมด</q-tooltip>
</q-btn>
<q-btn
v-if="scope.uploadedFiles.length > 0"
icon="done_all"
@click="scope.removeUploadedFiles"
round
dense
flat
>
<q-tooltip>ลบไฟลปโหลด</q-tooltip>
</q-btn>
<q-spinner
v-if="scope.isUploading"
class="q-uploader__spinner"
/>
<div class="col">
<div class="q-uploader__title">
{{ "[ไฟล์ jpg,png,pdf,csv,doc,docx ขนาดไม่เกิน 10MB]" }}
</div>
<div class="q-uploader__subtitle">
{{ scope.uploadSizeLabel }} /
{{ scope.uploadProgressLabel }}
</div>
</div>
<q-btn
v-if="scope.canAddFiles"
type="a"
icon="add_box"
@click="scope.pickFiles"
round
dense
flat
>
<q-uploader-add-trigger />
<q-tooltip>เลอกไฟล</q-tooltip>
</q-btn>
<q-btn
v-if="scope.isUploading"
icon="clear"
@click="scope.abort"
round
dense
flat
>
<q-tooltip>ยกเลกการอปโหลด</q-tooltip>
</q-btn>
</div>
</template>
</q-uploader>
</div>
</q-card-section>
<q-separator />
<DialogFooter
:cancel="clickCancel"
:edit="clickEdit"
:save="clickSave"
:validate="validateData"
:clickNext="clickNext"
:clickPrevious="clickPrevious"
:clickDelete="clickDelete"
v-model:editvisible="edit"
v-model:next="next"
v-model:previous="previous"
v-model:modalEdit="modalEdit"
/>
</q-form>
</q-card>
</q-dialog>
<HistoryTable
:rows="rowsHistory"
:columns="columnsHistory"
:filter="filterHistory"
:visible-columns="visibleColumnsHistory"
v-model:modal="modalHistory"
v-model:inputfilter="filterHistory"
v-model:inputvisible="visibleColumnsHistory"
v-model:tittle="tittleHistory"
:history="true"
>
<template #columns="props">
<q-tr :props="props">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div v-if="col.name == 'createdAt'" class="table_ellipsis">
{{ date2Thai(col.value) }}
</div>
<div v-else-if="col.name == 'status'" class="table_ellipsis">
{{ typeChangeName(col.value) }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
v-if="props.row.file != null"
color="green"
flat
dense
round
size="14px"
icon="mdi-file-document-outline"
@click="openFile(props.row.file)"
/>
</q-td>
</q-tr>
</template>
</HistoryTable>
</template>
<script setup lang="ts">
import { onMounted, ref, watch } from "vue";
import { useRoute } from "vue-router";
import { useComponentProfileDataStore } from "@/components/information/store/store";
import { useCounterMixin } from "@/stores/mixin";
import ProfileTable from "@/components/information/Table.vue";
import DialogHeader from "@/components/information/DialogHeader.vue";
import DialogFooter from "@/components/information/DialogFooter.vue";
import { useQuasar } from "quasar";
import type {
RequestItemsObject,
DataProps,
} from "@/components/information/interface/request/OldName";
import type { ResponseObject } from "@/components/information/interface/response/OldName";
import type {
OldNameOps,
DataOption,
} from "@/components/information/interface/index/Main";
import HistoryTable from "@/components/TableHistory.vue";
import http from "@/plugins/http";
import config from "@/app.config";
import type { QTableProps } from "quasar";
const props = defineProps({
statusEdit: {
type: Boolean,
required: true,
},
fetchDataInformation: {
type: Function,
default: () => console.log("not function"),
},
perfixId: {
type: String,
required: true,
},
firstname: {
type: String,
required: true,
},
lastname: {
type: String,
required: true,
},
});
const $q = useQuasar();
const store = useComponentProfileDataStore();
const { profileData, changeProfileColumns } = store;
const mixin = useCounterMixin();
const {
date2Thai,
success,
dateToISO,
messageError,
typeChangeName,
dialogMessage,
showLoader,
hideLoader,
} = mixin;
const route = useRoute();
const id = ref<string>("");
const prefixId = ref<string>();
const firstName = ref<string>();
const lastName = ref<string>();
const status = ref<string>();
const nameFile = ref<string>("");
const minDate = ref<Date>();
const myForm = ref<any>(); //form data input
const edit = ref<boolean>(false); // dialog
const modal = ref<boolean>(false); //modal add detail
const modalEdit = ref<boolean>(false); //modal
const rawItem = ref<RequestItemsObject>(); // row
const rowIndex = ref<number>(0); //index row
const previous = ref<boolean>(); //
const next = ref<boolean>(); //
const editRow = ref<boolean>(false); //
const rowsHistory = ref<RequestItemsObject[]>([]); //select data history
const tittleHistory = ref<string>("ประวัติแก้ไขประวัติการเปลี่ยนชื่อ-นามสกุล"); //
const filterHistory = ref<string>(""); //search data table history
const modalHistory = ref<boolean>(false); //modal
const checkValidate = ref<boolean>(false); //validate data
const profileId = ref<string>(route.params.id.toString());
const file = ref<string | null>(null);
const fileUpload = ref<any>([]);
const rows = ref<RequestItemsObject[]>([]);
const filter = ref<string>(""); //search data table
const visibleColumns = ref<String[]>([]);
const Ops = ref<OldNameOps>({
prefixOps: [],
statusOps: [
{
id: "firstName",
name: "เปลี่ยนชื่อ",
},
{
id: "lastName",
name: "เปลี่ยนนามสกุล",
},
{
id: "all",
name: "เปลี่ยนชื่อ-นามสกุล",
},
],
});
const OpsFilter = ref<OldNameOps>({
prefixOps: [],
statusOps: [
{
id: "firstName",
name: "เปลี่ยนชื่อ",
},
{
id: "lastName",
name: "เปลี่ยนนามสกุล",
},
{
id: "all",
name: "เปลี่ยนชื่อ-นามสกุล",
},
],
});
profileData.oldName.columns.length == 0
? (visibleColumns.value = ["prefix", "firstName", "lastName", "status"])
: (visibleColumns.value = profileData.oldName.columns);
const columns = ref<QTableProps["columns"]>([
{
name: "prefix",
align: "left",
label: "คำนำหน้าชื่อ",
sortable: true,
field: "prefix",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "firstName",
align: "left",
label: "ชื่อ",
sortable: true,
field: "firstName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastName",
align: "left",
label: "นามสกุล",
sortable: true,
field: "lastName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "status",
align: "left",
label: "สถานะการเปลี่ยนชื่อ",
sortable: true,
field: "status",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const columnsHistory = ref<QTableProps["columns"]>([
{
name: "prefix",
align: "left",
label: "คำนำหน้าชื่อ",
sortable: true,
field: "prefix",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "firstName",
align: "left",
label: "ชื่อ",
sortable: true,
field: "firstName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastName",
align: "left",
label: "นามสกุล",
sortable: true,
field: "lastName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "status",
align: "left",
label: "สถานะการเปลี่ยนชื่อ",
sortable: true,
field: "status",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "createdFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const visibleColumnsHistory = ref<String[]>([
"prefix",
"firstName",
"lastName",
"status",
"createdFullName",
"createdAt",
]);
watch(visibleColumns, async (count: String[], prevCount: String[]) => {
await changeProfileColumns("oldName", count);
});
onMounted(async () => {
await fetchPerson();
await fetchData();
});
const fetchData = async () => {
showLoader();
await http
.get(config.API.profileChangeNameId(profileId.value))
.then((res) => {
let data = res.data.result;
rows.value = [];
data.map((e: ResponseObject) => {
rows.value.push({
id: e.id,
prefix: e.prefix,
prefixId: e.prefixId,
firstName: e.firstName,
lastName: e.lastName,
status: e.status,
file: e.file,
createdFullName: e.createdFullName,
createdAt: new Date(e.createdAt),
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
};
/**
* get รายการ อมลเกยวกบบคคล
*/
const fetchPerson = async () => {
showLoader();
await http
.get(config.API.person)
.then((res) => {
const data = res.data.result;
let optionprefixs: DataOption[] = [];
data.prefixs.map((r: any) => {
optionprefixs.push({
id: r.id.toString(),
name: r.name.toString(),
});
});
Ops.value.prefixOps = optionprefixs;
OpsFilter.value.prefixOps = optionprefixs;
})
.catch((e: any) => {})
.finally(() => {
hideLoader();
});
};
/**
* กดดอมลกอนหน
*/
const clickPrevious = async () => {
edit.value = false;
rowIndex.value -= 1;
await getData();
await checkRowPage();
};
/**
* กดดอมลตอไป
*/
const clickNext = async () => {
edit.value = false;
rowIndex.value += 1;
await getData();
await checkRowPage();
};
/**
* กดดอมลตอไป
*/
const getData = () => {
const row = rows.value[rowIndex.value];
prefixId.value = row.prefixId;
firstName.value = row.firstName;
lastName.value = row.lastName;
status.value = row.status;
file.value = row.file;
id.value = row.id;
};
/**
* เชคปมดอม อน อไป าตองแสดงไหม
*/
const checkRowPage = () => {
editRow.value = false;
next.value = true;
previous.value = true;
if (rowIndex.value + 1 >= rows.value.length) {
next.value = false;
}
if (rowIndex.value - 1 < 0) {
previous.value = false;
}
};
/**
* กดปมแกไขใน dialog
*/
const clickEdit = () => {
editRow.value = false;
next.value = false;
previous.value = false;
};
/**
* กดปมเพมดานบน table
*/
const clickAdd = async () => {
editRow.value = false;
await addData();
};
/**
* กดบนทกใน dialog
*/
const clickSave = async () => {
myForm.value.validate().then(async (result: boolean) => {
if (result) {
if (modalEdit.value) {
await editData();
} else {
await saveData();
}
}
});
};
/**
* นทกเพมขอม
*/
const saveData = async () => {
if (fileUpload.value.length > 0) {
const blob = fileUpload.value.slice(0, fileUpload.value[0].size);
const newFile = new File(blob, nameFile.value, {
type: fileUpload.value[0].type,
});
const formData = new FormData();
formData.append("", newFile);
if (prefixId.value != undefined)
formData.append("prefixId", prefixId.value);
if (firstName.value != undefined)
formData.append("firstName", firstName.value);
if (lastName.value != undefined)
formData.append("lastName", lastName.value);
if (status.value != undefined) formData.append("status", status.value);
showLoader();
await http
.post(config.API.profileChangeNameId(profileId.value), formData)
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
await props.fetchDataInformation();
});
} else {
dialogMessage(
$q,
"ไม่สามารถบันทึกข้อมูลได้",
"กรุณาอัปโหลดเอกสารหลักฐาน",
"warning",
undefined,
"orange",
undefined,
undefined,
true
);
}
};
/**
* นทกแกไขขอม
*/
const editData = async () => {
const formData = new FormData();
if (fileUpload.value.length > 0) {
const blob = fileUpload.value.slice(0, fileUpload.value[0].size);
const newFile = new File(blob, nameFile.value, {
type: fileUpload.value[0].type,
});
formData.append("", newFile);
}
if (prefixId.value != undefined) formData.append("prefixId", prefixId.value);
if (firstName.value != undefined)
formData.append("firstName", firstName.value);
if (lastName.value != undefined) formData.append("lastName", lastName.value);
if (status.value != undefined) formData.append("status", status.value);
showLoader();
await http
.put(config.API.profileChangeNameId(id.value), formData)
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
await props.fetchDataInformation();
});
};
/**
* ลบลบขอม
*/
const clickDelete = async () => {
$q.dialog({
title: `ลบข้อมูล`,
message: `ต้องการทำการลบข้อมูลนี้ใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
})
.onOk(async () => {
showLoader();
await http
.delete(config.API.profileChangeNameId(id.value))
.then((res) => {
success($q, "ลบข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
})
.onCancel(async () => {
await fetchData();
});
};
/**
* กดป dialog
*/
const clickClose = async () => {
if (editRow.value == true) {
$q.dialog({
title: `ข้อมูลมีการแก้ไข`,
message: `ยืนยันที่จะปิดโดยไม่บันทึกใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
}).onOk(async () => {
modal.value = false;
next.value = false;
previous.value = false;
});
} else {
modal.value = false;
next.value = false;
previous.value = false;
}
};
/**
* กดเลอกขอมลทจะแกไข
* @param props props ใน row เลอก
*/
const selectData = async (props: DataProps) => {
modalEdit.value = true; //
modal.value = true; //modal
edit.value = false; // edit modal
rawItem.value = props.row;
rowIndex.value = props.rowIndex;
prefixId.value = props.row.prefixId;
firstName.value = props.row.firstName;
lastName.value = props.row.lastName;
status.value = props.row.status;
file.value = props.row.file;
id.value = props.row.id;
await checkRowPage();
};
/**
* กดปมเพมบน table
*/
const addData = () => {
modalEdit.value = false;
modal.value = true;
edit.value = true;
prefixId.value = props.perfixId;
firstName.value = props.firstname;
lastName.value = props.lastname;
status.value = "";
file.value = "";
};
/**
* งกนปมยกเลกการแกไขขอม
*/
const clickCancel = async () => {
if (editRow.value == true) {
$q.dialog({
title: `ข้อมูลมีการแก้ไข`,
message: `ยืนยันยกเลิกการแก้ไขใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
}).onOk(async () => {
edit.value = false;
await checkRowPage();
await getData();
});
} else {
edit.value = false;
await checkRowPage();
}
};
/**
* เชความการแกไขขอม
*/
const clickEditRow = () => {
editRow.value = true;
};
/**
* งชนดอมลประวแกไขขอมลทเลอก
* @param row อม row ประวการแกไข
*/
const clickHistory = async (row: RequestItemsObject) => {
modalHistory.value = true;
showLoader();
await http
.get(config.API.profileChangeNameHisId(row.id))
.then((res) => {
let data = res.data.result;
rowsHistory.value = [];
data.map((e: ResponseObject) => {
rowsHistory.value.push({
id: e.id,
prefix: e.prefix,
prefixId: e.prefixId,
firstName: e.firstName,
lastName: e.lastName,
status: e.status,
file: e.file,
createdFullName: e.createdFullName,
createdAt: new Date(e.createdAt),
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
};
const filterSelector = (val: any, update: Function, refData: string) => {
switch (refData) {
case "prefixOps":
update(() => {
Ops.value.prefixOps = OpsFilter.value.prefixOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
case "statusOps":
update(() => {
Ops.value.statusOps = OpsFilter.value.statusOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
default:
break;
}
};
const fileAdd = async (val: any) => {
nameFile.value = val[0].name;
fileUpload.value = val;
};
const openFile = async (val: string | null) => {
if (val != null) window.open(val);
};
/**
* validate input ใน dialog
*/
const validateData = async () => {
checkValidate.value = true;
await myForm.value.validate().then((result: boolean) => {
if (result == false) {
checkValidate.value = false;
}
});
};
/**
* class ดรปแบบแสดงระหวางขอมลทแกไขหรอแสดงเฉยๆ
* @param val อม input สำหรบแกไขหรอไม
*/
const getClass = (val: boolean) => {
return {
"full-width inputgreen cursor-pointer": val,
"full-width cursor-pointer": !val,
};
};
</script>
<style lang="scss">
.modalfix {
position: fixed !important;
}
</style>

View file

@ -0,0 +1,619 @@
<!-- card นๆ -->
<template>
<q-card flat bordered class="col-12 q-px-lg q-py-md">
<q-form ref="myForm">
<ProfileTable
:rows="rows"
:columns="columns"
:filter="filter"
:visible-columns="visibleColumns"
v-model:inputfilter="filter"
v-model:inputvisible="visibleColumns"
:add="clickAdd"
name="อื่นๆ"
icon="mdi-bookmark"
:statusEdit="statusEdit"
>
<template #columns="props">
<q-tr :props="props">
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
@click="selectData(props)"
class="cursor-pointer"
>
<div v-if="col.name == 'date'" class="table_ellipsis">
{{ date2Thai(col.value) }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="info"
flat
dense
round
size="14px"
icon="mdi-history"
@click="clickHistory(props.row)"
/>
</q-td>
</q-tr>
</template>
</ProfileTable>
</q-form>
</q-card>
<!-- popup Edit window-->
<q-dialog v-model="modal" persistent>
<q-card style="width: 600px">
<q-form ref="myForm">
<DialogHeader tittle="อื่นๆ" :close="clickClose" />
<q-separator />
<q-card-section class="q-p-sm">
<div
class="row col-12 items-center q-col-gutter-x-xs q-col-gutter-y-xs"
>
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
:readonly="!edit"
v-model="date"
:locale="'th'"
autoApply
:enableTimePicker="false"
@update:modelValue="clickEditRow"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
:borderless="!edit"
:model-value="date2Thai(date)"
:rules="[(val) => !!val || `${'กรุณาเลือกวันที่'}`]"
hide-bottom-space
:label="`${'วันที่'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
:style="
edit
? 'color: var(--q-primary)'
: 'color: var(--q-grey)'
"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
autogrow
:readonly="!edit"
:borderless="!edit"
v-model="detail"
:rules="[(val) => !!val || `${'กรุณากรอกรายละเอียด'}`]"
hide-bottom-space
:label="`${'รายละเอียด'}`"
@update:modelValue="clickEditRow"
/>
</div>
</div>
</q-card-section>
<q-separator />
<DialogFooter
:cancel="clickCancel"
:edit="clickEdit"
:save="clickSave"
:validate="validateData"
:clickNext="clickNext"
:clickPrevious="clickPrevious"
:clickDelete="clickDelete"
v-model:editvisible="edit"
v-model:next="next"
v-model:previous="previous"
v-model:modalEdit="modalEdit"
/>
</q-form>
</q-card>
</q-dialog>
<HistoryTable
:rows="rowsHistory"
:columns="columnsHistory"
:filter="filterHistory"
:visible-columns="visibleColumnsHistory"
v-model:modal="modalHistory"
v-model:inputfilter="filterHistory"
v-model:inputvisible="visibleColumnsHistory"
v-model:tittle="tittleHistory"
>
<template #columns="props">
<q-tr :props="props">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div
v-if="col.name == 'date' || col.name == 'createdAt'"
class="table_ellipsis"
>
{{ date2Thai(col.value) }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
</q-tr>
</template>
</HistoryTable>
</template>
<script setup lang="ts">
import { onMounted, ref, watch } from "vue";
import { useRoute } from "vue-router";
import { useComponentProfileDataStore } from "@/components/information/store/store";
import { useCounterMixin } from "@/stores/mixin";
import ProfileTable from "@/components/information/Table.vue";
import DialogHeader from "@/components/information/DialogHeader.vue";
import DialogFooter from "@/components/information/DialogFooter.vue";
import { useQuasar } from "quasar";
import type {
RequestItemsObject,
DataProps,
} from "@/components/information/interface/request/Other";
import type { ResponseObject } from "@/components/information/interface/response/Other";
import HistoryTable from "@/components/TableHistory.vue";
import http from "@/plugins/http";
import config from "@/app.config";
import type { QTableProps } from "quasar";
const props = defineProps({
statusEdit: {
type: Boolean,
required: true,
},
});
const $q = useQuasar();
const store = useComponentProfileDataStore();
const { profileData, changeProfileColumns } = store;
const mixin = useCounterMixin();
const { date2Thai, success, messageError, showLoader, hideLoader } = mixin;
const route = useRoute();
const id = ref<string>("");
const date = ref<Date>(new Date());
const detail = ref<string>();
const myForm = ref<any>(); //form data input
const edit = ref<boolean>(false); // dialog
const modal = ref<boolean>(false); //modal add detail
const modalEdit = ref<boolean>(false); //modal
const rawItem = ref<RequestItemsObject>(); // row
const rowIndex = ref<number>(0); //index row
const previous = ref<boolean>(); //
const next = ref<boolean>(); //
const editRow = ref<boolean>(false); //
const rowsHistory = ref<RequestItemsObject[]>([]); //select data history
const tittleHistory = ref<string>("ประวัติแก้ไขอื่นๆ"); //
const filterHistory = ref<string>(""); //search data table history
const modalHistory = ref<boolean>(false); //modal
const checkValidate = ref<boolean>(false); //validate data
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const rows = ref<RequestItemsObject[]>([]);
const filter = ref<string>(""); //search data table
const visibleColumns = ref<String[]>([]);
profileData.other.columns.length == 0
? (visibleColumns.value = ["date", "detail"])
: (visibleColumns.value = profileData.other.columns);
const columns = ref<QTableProps["columns"]>([
{
name: "date",
align: "left",
label: "วันที่",
sortable: true,
field: "date",
headerStyle: "font-size: 14px",
style: "font-size: 14px; width: 50px;",
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const columnsHistory = ref<QTableProps["columns"]>([
{
name: "date",
align: "left",
label: "วันที่",
sortable: true,
field: "date",
headerStyle: "font-size: 14px",
style: "font-size: 14px; width: 50px;",
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "createdFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const visibleColumnsHistory = ref<String[]>([
"date",
"detail",
"createdFullName",
"createdAt",
]);
watch(visibleColumns, async (count: String[], prevCount: String[]) => {
await changeProfileColumns("other", count);
});
onMounted(async () => {
await fetchData();
});
const fetchData = async () => {
if (profileId.value) {
showLoader();
await http
.get(config.API.profileOtherId(profileId.value))
.then((res) => {
let data = res.data.result;
rows.value = [];
data.map((e: ResponseObject) => {
rows.value.push({
id: e.id,
date: new Date(e.date),
detail: e.detail,
createdFullName: e.createdFullName,
createdAt: new Date(e.createdAt),
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
};
/**
* กดดอมลกอนหน
*/
const clickPrevious = async () => {
edit.value = false;
rowIndex.value -= 1;
await getData();
await checkRowPage();
};
/**
* กดดอมลตอไป
*/
const clickNext = async () => {
edit.value = false;
rowIndex.value += 1;
await getData();
await checkRowPage();
};
/**
* กดดอมลตอไป
*/
const getData = () => {
const row = rows.value[rowIndex.value];
date.value = row.date;
detail.value = row.detail;
id.value = row.id;
};
/**
* เชคปมดอม อน อไป าตองแสดงไหม
*/
const checkRowPage = () => {
editRow.value = false;
next.value = true;
previous.value = true;
if (rowIndex.value + 1 >= rows.value.length) {
next.value = false;
}
if (rowIndex.value - 1 < 0) {
previous.value = false;
}
};
/**
* กดปมแกไขใน dialog
*/
const clickEdit = () => {
editRow.value = false;
next.value = false;
previous.value = false;
};
/**
* กดปมเพมดานบน table
*/
const clickAdd = async () => {
editRow.value = false;
await addData();
};
/**
* กดบนทกใน dialog
*/
const clickSave = async () => {
myForm.value.validate().then(async (result: boolean) => {
if (result) {
if (modalEdit.value) {
await editData();
} else {
await saveData();
}
}
});
};
/**
* นทกเพมขอม
*/
const saveData = async () => {
if (profileId.value) {
showLoader();
await http
.post(config.API.profileOtherId(profileId.value), {
id: id.value,
date: date.value,
detail: detail.value,
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
}
};
/**
* นทกแกไขขอม
*/
const editData = async () => {
showLoader();
await http
.put(config.API.profileOtherId(id.value), {
id: id.value,
date: date.value,
detail: detail.value,
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
};
/**
* ลบลบขอม
*/
const clickDelete = async () => {
$q.dialog({
title: `ลบข้อมูล`,
message: `ต้องการทำการลบข้อมูลนี้ใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
})
.onOk(async () => {
showLoader();
await http
.delete(config.API.profileOtherId(id.value))
.then((res) => {
success($q, "ลบข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
})
.onCancel(async () => {
await fetchData();
});
};
/**
* กดป dialog
*/
const clickClose = async () => {
if (editRow.value == true) {
$q.dialog({
title: `ข้อมูลมีการแก้ไข`,
message: `ยืนยันที่จะปิดโดยไม่บันทึกใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
}).onOk(async () => {
modal.value = false;
next.value = false;
previous.value = false;
});
} else {
modal.value = false;
next.value = false;
previous.value = false;
}
};
/**
* กดเลอกขอมลทจะแกไข
* @param props props ใน row เลอก
*/
const selectData = async (props: DataProps) => {
modalEdit.value = true;
modal.value = true;
edit.value = false;
rawItem.value = props.row;
rowIndex.value = props.rowIndex;
date.value = props.row.date;
detail.value = props.row.detail;
id.value = props.row.id;
await checkRowPage();
};
/**
* กดปมเพมบน table
*/
const addData = () => {
modalEdit.value = false;
modal.value = true;
edit.value = true;
date.value = new Date();
detail.value = "";
};
/**
* งกนปมยกเลกการแกไขขอม
*/
const clickCancel = async () => {
if (editRow.value == true) {
$q.dialog({
title: `ข้อมูลมีการแก้ไข`,
message: `ยืนยันยกเลิกการแก้ไขใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
}).onOk(async () => {
edit.value = false;
await checkRowPage();
await getData();
});
} else {
edit.value = false;
await checkRowPage();
}
};
/**
* เชความการแกไขขอม
*/
const clickEditRow = () => {
editRow.value = true;
};
/**
* งชนดอมลประวแกไขขอมลทเลอก
* @param row อม row ประวการแกไข
*/
const clickHistory = async (row: RequestItemsObject) => {
modalHistory.value = true;
showLoader();
await http
.get(config.API.profileOtherHisId(row.id))
.then((res) => {
let data = res.data.result;
rowsHistory.value = [];
data.map((e: ResponseObject) => {
rowsHistory.value.push({
id: e.id,
date: e.date,
detail: e.detail,
createdFullName: e.createdFullName,
createdAt: new Date(e.createdAt),
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
};
/**
* validate input ใน dialog
*/
const validateData = async () => {
checkValidate.value = true;
await myForm.value.validate().then((result: boolean) => {
if (result == false) {
checkValidate.value = false;
}
});
};
/**
* class ดรปแบบแสดงระหวางขอมลทแกไขหรอแสดงเฉยๆ
* @param val อม input สำหรบแกไขหรอไม
*/
const getClass = (val: boolean) => {
return {
"full-width inputgreen cursor-pointer": val,
"full-width cursor-pointer": !val,
};
};
</script>

View file

@ -0,0 +1,824 @@
<!-- card นทกวนทไมไดบเงนเดอนหรอไดบเงนเดอนไมเตมหรอวนทไดปฏหนาทอยในเขตทไดประดาศใชกฏอยการศ -->
<template>
<q-card flat bordered class="col-12 q-px-lg q-py-md">
<q-form ref="myForm">
<ProfileTable
:rows="rows"
:columns="columns"
:filter="filter"
:visible-columns="visibleColumns"
v-model:inputfilter="filter"
v-model:inputvisible="visibleColumns"
:add="clickAdd"
name="วันที่มิได้ปฏิบัติหน้าที่อยู่ในเขตที่ได้มีประกาศใช้กฎอัยการศึก"
icon="mdi-calendar-remove"
:statusEdit="statusEdit"
>
<template #columns="props">
<q-tr :props="props">
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
@click="selectData(props)"
class="cursor-pointer"
>
<div
v-if="col.name == 'date' || col.name == 'refCommandDate'"
class="table_ellipsis"
>
{{ col.value == null ? null : date2Thai(col.value) }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="info"
flat
dense
round
size="14px"
icon="mdi-history"
@click="clickHistory(props.row)"
/>
</q-td>
</q-tr>
</template>
</ProfileTable>
</q-form>
</q-card>
<!-- popup Edit window-->
<q-dialog v-model="modal" persistent>
<q-card style="width: 600px">
<q-form ref="myForm">
<DialogHeader
tittle="วันที่มิได้ปฏิบัติหน้าที่อยู่ในเขตที่ได้มีประกาศใช้กฎอัยการศึก"
:close="clickClose"
/>
<q-separator />
<q-card-section class="q-p-sm">
<div
class="row col-12 items-center q-col-gutter-x-xs q-col-gutter-y-xs"
>
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
:readonly="!edit"
v-model="date"
:locale="'th'"
autoApply
:enableTimePicker="false"
@update:modelValue="clickEditRow"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
:borderless="!edit"
:model-value="date2Thai(date)"
:rules="[(val) => !!val || `${'กรุณาเลือกวัน เดือน ปี'}`]"
hide-bottom-space
:label="`${'วัน เดือน ปี'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
:style="
edit
? 'color: var(--q-primary)'
: 'color: var(--q-grey)'
"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-6">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
autogrow
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="reference"
:rules="[(val) => !!val || `${'กรุณากรอกเอกสารอ้างอิง'}`]"
hide-bottom-space
:label="`${'เอกสารอ้างอิง'}`"
@update:modelValue="clickEditRow"
/>
</div>
<div class="col-12">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
autogrow
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="detail"
:rules="[(val) => !!val || `${'กรุณากรอกรายละเอียด'}`]"
hide-bottom-space
:label="`${'รายละเอียด'}`"
@update:modelValue="clickEditRow"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="refCommandNo"
hide-bottom-space
:label="`${'เลขที่คำสั่ง'}`"
@update:modelValue="clickEditRow"
>
<template v-slot:append>
<q-icon
name="mdi-file"
class="cursor-pointer"
@click="resetFilter"
/>
</template>
</q-input>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
:readonly="!edit"
v-model="refCommandDate"
:locale="'th'"
autoApply
:enableTimePicker="false"
@update:modelValue="clickEditRow"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
:borderless="!edit"
:model-value="
refCommandDate == null ? null : date2Thai(refCommandDate)
"
hide-bottom-space
:label="`${'เอกสารอ้างอิง (ลงวันที่)'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
:style="
edit
? 'color: var(--q-primary)'
: 'color: var(--q-grey)'
"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
</div>
</q-card-section>
<q-separator />
<DialogFooter
:cancel="clickCancel"
:edit="clickEdit"
:save="clickSave"
:validate="validateData"
:clickNext="clickNext"
:clickPrevious="clickPrevious"
:clickDelete="clickDelete"
v-model:editvisible="edit"
v-model:next="next"
v-model:previous="previous"
v-model:modalEdit="modalEdit"
/>
</q-form>
</q-card>
</q-dialog>
<HistoryTable
:rows="rowsHistory"
:columns="columnsHistory"
:filter="filterHistory"
:visible-columns="visibleColumnsHistory"
v-model:modal="modalHistory"
v-model:inputfilter="filterHistory"
v-model:inputvisible="visibleColumnsHistory"
v-model:tittle="tittleHistory"
>
<template #columns="props">
<q-tr :props="props">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div
v-if="
col.name == 'date' ||
col.name == 'createdAt' ||
col.name == 'refCommandDate'
"
class="table_ellipsis"
>
{{ col.value == null ? null : date2Thai(col.value) }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
</q-tr>
</template>
</HistoryTable>
</template>
<script setup lang="ts">
import { onMounted, ref, watch } from "vue";
import { useRoute } from "vue-router";
import { useComponentProfileDataStore } from "@/components/information/store/store";
import { useCounterMixin } from "@/stores/mixin";
import ProfileTable from "@/components/information/Table.vue";
import DialogHeader from "@/components/information/DialogHeader.vue";
import DialogFooter from "@/components/information/DialogFooter.vue";
import { useQuasar } from "quasar";
import type {
RequestItemsObject,
DataProps,
} from "@/components/information/interface/request/Record";
import type { ResponseObject } from "@/components/information/interface/response/Record";
import HistoryTable from "@/components/TableHistory.vue";
import http from "@/plugins/http";
import config from "@/app.config";
import type { QTableProps } from "quasar";
const props = defineProps({
statusEdit: {
type: Boolean,
required: true,
},
});
const $q = useQuasar();
const store = useComponentProfileDataStore();
const { profileData, changeProfileColumns } = store;
const mixin = useCounterMixin();
const { date2Thai, success, dateToISO, messageError, showLoader, hideLoader } =
mixin;
const route = useRoute();
const id = ref<string>("");
const date = ref<Date>(new Date());
const detail = ref<string>();
const reference = ref<string>();
const refCommandNo = ref<string>();
const refCommandDate = ref<Date | null>(new Date());
const myForm = ref<any>(); //form data input
const edit = ref<boolean>(false); // dialog
const modal = ref<boolean>(false); //modal add detail
const modalEdit = ref<boolean>(false); //modal
const rawItem = ref<RequestItemsObject>(); // row
const rowIndex = ref<number>(0); //index row
const previous = ref<boolean>(); //
const next = ref<boolean>(); //
const editRow = ref<boolean>(false); //
const rowsHistory = ref<RequestItemsObject[]>([]); //select data history
const tittleHistory = ref<string>(
"ประวัติแก้ไขวันที่มิได้ปฏิบัติหน้าที่อยู่ในเขตที่ได้มีประกาศใช้กฎอัยการศึก"
);
const filterHistory = ref<string>(""); //search data table history
const modalHistory = ref<boolean>(false); //modal
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const checkValidate = ref<boolean>(false); //validate data
const rows = ref<RequestItemsObject[]>([]);
const filter = ref<string>(""); //search data table
const visibleColumns = ref<String[]>([]);
profileData.record.columns.length == 0
? (visibleColumns.value = [
"date",
"detail",
"reference",
"refCommandNo",
"refCommandDate",
])
: (visibleColumns.value = profileData.record.columns);
const columns = ref<QTableProps["columns"]>([
{
name: "date",
align: "left",
label: "วัน เดือน ปี",
sortable: true,
field: "date",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "reference",
align: "left",
label: "เอกสารอ้างอิง",
sortable: true,
field: "reference",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandNo",
align: "left",
label: "เลขที่คำสั่ง",
sortable: true,
field: "refCommandNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandDate",
align: "left",
label: "เอกสารอ้างอิง (ลงวันที่)",
sortable: true,
field: "refCommandDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const columnsHistory = ref<QTableProps["columns"]>([
{
name: "date",
align: "left",
label: "วัน เดือน ปี",
sortable: true,
field: "date",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "reference",
align: "left",
label: "เอกสารอ้างอิง",
sortable: true,
field: "reference",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandNo",
align: "left",
label: "เลขที่คำสั่ง",
sortable: true,
field: "refCommandNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandDate",
align: "left",
label: "เอกสารอ้างอิง (ลงวันที่)",
sortable: true,
field: "refCommandDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "createdFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const visibleColumnsHistory = ref<String[]>([
"date",
"detail",
"reference",
"refCommandNo",
"refCommandDate",
"createdFullName",
"createdAt",
]);
watch(visibleColumns, async (count: String[], prevCount: String[]) => {
await changeProfileColumns("record", count);
});
onMounted(async () => {
await fetchData();
});
const fetchData = async () => {
if (profileId.value != "") {
showLoader();
await http
.get(config.API.profileNopaidId(profileId.value))
.then((res) => {
let data = res.data.result;
rows.value = [];
data.map((e: ResponseObject) => {
rows.value.push({
id: e.id,
date: new Date(e.date),
detail: e.detail,
reference: e.reference,
refCommandNo: e.refCommandNo,
refCommandDate:
e.refCommandDate == null ? null : new Date(e.refCommandDate),
createdFullName: e.createdFullName,
createdAt: new Date(e.createdAt),
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
};
/**
* กดดอมลกอนหน
*/
const clickPrevious = async () => {
edit.value = false;
rowIndex.value -= 1;
await getData();
await checkRowPage();
};
/**
* กดดอมลตอไป
*/
const clickNext = async () => {
edit.value = false;
rowIndex.value += 1;
await getData();
await checkRowPage();
};
/**
* กดดอมลตอไป
*/
const getData = () => {
const row = rows.value[rowIndex.value];
date.value = row.date;
detail.value = row.detail;
reference.value = row.reference;
refCommandNo.value = row.refCommandNo;
refCommandDate.value = row.refCommandDate;
id.value = row.id;
};
/**
* เชคปมดอม อน อไป าตองแสดงไหม
*/
const checkRowPage = () => {
editRow.value = false;
next.value = true;
previous.value = true;
if (rowIndex.value + 1 >= rows.value.length) {
next.value = false;
}
if (rowIndex.value - 1 < 0) {
previous.value = false;
}
};
/**
* กดปมแกไขใน dialog
*/
const clickEdit = () => {
editRow.value = false;
next.value = false;
previous.value = false;
};
/**
* กดปมเพมดานบน table
*/
const clickAdd = async () => {
editRow.value = false;
await addData();
};
/**
* กดบนทกใน dialog
*/
const clickSave = async () => {
myForm.value.validate().then(async (result: boolean) => {
if (result) {
if (modalEdit.value) {
await editData();
} else {
await saveData();
}
}
});
};
/**
* นทกเพมขอม
*/
const saveData = async () => {
if (profileId.value) {
showLoader();
await http
.post(config.API.profileNopaidId(profileId.value), {
id: id.value,
date: dateToISO(date.value),
detail: detail.value,
reference: reference.value,
refCommandNo: refCommandNo.value,
refCommandDate:
refCommandDate.value == null ? null : dateToISO(refCommandDate.value),
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
}
};
/**
* นทกแกไขขอม
*/
const editData = async () => {
showLoader();
await http
.put(config.API.profileNopaidId(id.value), {
id: id.value,
date: dateToISO(date.value),
detail: detail.value,
reference: reference.value,
refCommandNo: refCommandNo.value,
refCommandDate:
refCommandDate.value == null ? null : dateToISO(refCommandDate.value),
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
};
/**
* ลบลบขอม
*/
const clickDelete = async () => {
$q.dialog({
title: `ลบข้อมูล`,
message: `ต้องการทำการลบข้อมูลนี้ใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
})
.onOk(async () => {
showLoader();
await http
.delete(config.API.profileNopaidId(id.value))
.then((res) => {
success($q, "ลบข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
})
.onCancel(async () => {
await fetchData();
});
};
/**
* กดป dialog
*/
const clickClose = async () => {
if (editRow.value == true) {
$q.dialog({
title: `ข้อมูลมีการแก้ไข`,
message: `ยืนยันที่จะปิดโดยไม่บันทึกใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
}).onOk(async () => {
modal.value = false;
next.value = false;
previous.value = false;
});
} else {
modal.value = false;
next.value = false;
previous.value = false;
}
};
/**
* กดเลอกขอมลทจะแกไข
* @param props props ใน row เลอก
*/
const selectData = async (props: DataProps) => {
modalEdit.value = true;
modal.value = true;
edit.value = false;
rawItem.value = props.row;
rowIndex.value = props.rowIndex;
date.value = props.row.date;
detail.value = props.row.detail;
reference.value = props.row.reference;
refCommandNo.value = props.row.refCommandNo;
refCommandDate.value = props.row.refCommandDate;
id.value = props.row.id;
await checkRowPage();
};
/**
* กดปมเพมบน table
*/
const addData = () => {
modalEdit.value = false;
modal.value = true;
edit.value = true;
date.value = new Date();
detail.value = "";
reference.value = "";
refCommandNo.value = "";
refCommandDate.value = null;
};
/**
* งกนปมยกเลกการแกไขขอม
*/
const clickCancel = async () => {
if (editRow.value == true) {
$q.dialog({
title: `ข้อมูลมีการแก้ไข`,
message: `ยืนยันยกเลิกการแก้ไขใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
}).onOk(async () => {
edit.value = false;
await checkRowPage();
await getData();
});
} else {
edit.value = false;
await checkRowPage();
}
};
/**
* เชความการแกไขขอม
*/
const clickEditRow = () => {
editRow.value = true;
};
/**
* งชนดอมลประวแกไขขอมลทเลอก
* @param row อม row ประวการแกไข
*/
const clickHistory = async (row: RequestItemsObject) => {
modalHistory.value = true;
showLoader();
await http
.get(config.API.profileNopaidHisId(row.id))
.then((res) => {
let data = res.data.result;
rowsHistory.value = [];
data.map((e: ResponseObject) => {
rowsHistory.value.push({
id: e.id,
date: new Date(e.date),
detail: e.detail,
reference: e.reference,
refCommandNo: e.refCommandNo,
refCommandDate:
e.refCommandDate == null ? null : new Date(e.refCommandDate),
createdFullName: e.createdFullName,
createdAt: new Date(e.createdAt),
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
};
/**
* validate input ใน dialog
*/
const validateData = async () => {
checkValidate.value = true;
await myForm.value.validate().then((result: boolean) => {
if (result == false) {
checkValidate.value = false;
}
});
};
/**
* class ดรปแบบแสดงระหวางขอมลทแกไขหรอแสดงเฉยๆ
* @param val อม input สำหรบแกไขหรอไม
*/
const getClass = (val: boolean) => {
return {
"full-width inputgreen cursor-pointer": val,
"full-width cursor-pointer": !val,
};
};
const resetFilter = () => {};
</script>
<style lang="scss">
.modalfix {
position: fixed !important;
}
</style>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,215 @@
<template>
<div class="q-pb-sm row">
<HeaderTop
v-model:edit="editBtn"
:header="name"
:icon="icon"
:add="checkAdd"
:addleave="checkAddLeave"
:addData="true"
:history="false"
:disable="statusEdit"
:iconAdd="iconAdd"
/>
<q-space />
<div class="items-center" style="display: flex" v-if="headerShow">
<!-- นหาขอความใน table -->
<q-input
standout
dense
:model-value="inputfilter"
ref="filterRef"
@update:model-value="updateInput"
outlined
debounce="300"
placeholder="ค้นหา"
style="max-width: 200px"
class="q-ml-sm"
>
<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 q-ml-sm"
/>
</div>
</div>
<q-table
ref="table"
flat
bordered
class="custom-header-table"
v-bind="attrs"
virtual-scroll
:virtual-scroll-sticky-size-start="48"
dense
:pagination-label="paginationLabel"
:pagination="initialPagination"
:rows-per-page-options="[0]"
>
<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">{{ col.label }}</span>
</q-th>
<q-th auto-width />
</q-tr>
</template>
<template #body="props">
<slot v-bind="props" name="columns"></slot>
</template>
</q-table>
</template>
<script setup lang="ts">
import { ref, useAttrs } from "vue";
import HeaderTop from "@/components/information/top.vue";
import type { Pagination } from "@/components/information/interface/store/main";
const attrs = ref<any>(useAttrs());
const table = ref<any>(null);
const filterRef = ref<any>(null);
const editBtn = ref<boolean>(false);
const initialPagination = ref<Pagination>({
rowsPerPage: 0,
});
const props = defineProps({
inputfilter: String,
name: String,
icon: String,
iconAdd: {
type: String,
default: null,
},
inputvisible: Array,
editvisible: Boolean,
headerShow: {
type: Boolean,
default: true,
},
statusEdit: {
type: Boolean,
required: true,
},
edit: {
type: Function,
default: () => console.log("not function"),
},
add: {
type: Function,
default: () => console.log("not function"),
},
addleave: {
type: Function,
default: () => console.log("not function"),
},
cancel: {
type: Function,
default: () => console.log("not function"),
},
validate: {
type: Function,
default: () => console.log("not function"),
},
});
const emit = defineEmits([
"update:inputfilter",
"update:inputvisible",
"update:editvisible",
]);
const updateEdit = (value: Boolean) => {
emit("update:editvisible", value);
};
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 checkAddLeave = () => {
props.addleave();
};
const checkAdd = () => {
props.add();
};
const edit = async () => {
updateEdit(!props.editvisible);
props.edit();
};
const cancel = async () => {
updateEdit(!props.editvisible);
props.cancel();
};
const resetFilter = () => {
// reset X
emit("update:inputfilter", "");
filterRef.value.focus();
};
</script>
<style lang="scss">
.icon-color {
color: #4154b3;
}
.custom-header-table {
max-height: 64vh;
.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;
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;
}
}
</style>

View file

@ -0,0 +1,692 @@
<template>
<q-table
ref="table"
flat
:class="
$q.screen.gt.xs
? 'q-px-md custom-header-table2'
: 'q-px-sm custom-header-table2 custom-table-height'
"
v-bind="attrs"
dense
virtual-scroll
:virtual-scroll-sticky-size-start="48"
:pagination-label="paginationLabel"
:pagination="initialPagination"
:rows-per-page-options="[0]"
>
<template v-slot:top="props">
<div class="q-py-sm col-12 row">
<q-space />
<div class="items-center row col-12 q-gutter-x-sm">
<q-btn
size="13px"
color="grey-7"
dense
flat
round
@click="onTab"
class="shadow-1"
icon="chevron_right"
v-if="!isTab"
>
<q-tooltip>เป</q-tooltip>
</q-btn>
<q-btn
size="12px"
flat
round
color="blue"
@click="clickSearchPanel"
icon="mdi-filter-variant"
v-show="searchPanel == false"
>
<q-tooltip>นหาขนส</q-tooltip>
</q-btn>
<q-space />
<q-input
v-show="searchPanel == false"
standout
dense
:model-value="inputfilter"
ref="filterRef"
@update:model-value="updateInput"
outlined
debounce="300"
placeholder="ค้นหา"
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 q-ml-sm"
/>
</div>
</div>
<div class="items-center">
<q-slide-transition
v-show="searchPanel"
class="q-mb-sm"
:duration="150"
>
<div class="my-content">
<div
class="row q-pa-xs items-center bg-blue-1"
style="border-radius: 4px 4px 0px 0px"
>
<q-icon
size="20px"
color="blue-9"
name="mdi-filter-variant"
class="q-mx-sm"
/>
<div class="text-blue-9 text-subtitle2 text-weight-medium">
<span>นหาขอมลทะเบยนประว</span>
</div>
<q-space />
<q-btn
color="blue-9"
icon="mdi-close"
size="12px"
flat
round
dense
@click="clickSearchPanel"
/>
</div>
<q-separator color="blue-1" />
<div class="dialog-card-contain">
<q-card-section class="q-pa-md">
<div class="row col-12 q-col-gutter-sm">
<div
:class="
$q.screen.lt.md
? ' row col-12 q-col-gutter-xs'
: 'no-wrap row col-12 q-col-gutter-xs'
"
>
<selector
class="col-3"
hide-bottom-space
:rules="[(val:string) => !!val || `${'กรุณาเลือก ประเภท'}`]"
outlined
dense
lazy-rules
:model-value="employeeClass"
emit-value
map-options
:options="employeeClassOps"
option-label="name"
option-value="id"
:label="`${'ประเภท'}`"
use-input
input-debounce="0"
@update:model-value="updateEmployeeClass"
/>
<!-- @filter="(inputValue:any,
doneFn:Function) => filterSelector(inputValue, doneFn,'employeeTypeOps'
) " -->
<q-input
class="col-3"
clearable
dense
outlined
lazy-rules
:model-value="profileId"
hide-bottom-space
label="เลขประจำตัวประชาชน"
@update:model-value="updateProfileId"
type="number"
/>
<q-input
class="col-2"
clearable
dense
outlined
lazy-rules
:model-value="fullName"
hide-bottom-space
label="ชื่อ-นามสกุล"
@update:model-value="updateFullname"
/>
<datepicker
class="col-2"
menu-class-name="modalfix"
:model-value="retireYear"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
@update:model-value="updateRetireYear"
clearable
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
class="inputgreen cursor-pointer q-mb-sm"
hide-bottom-space
outlined
dense
borderless
:model-value="
retireYear == null ? null : retireYear + 543
"
:label="`${'ปีเกษียณ'}`"
clearable
@clear="clearDate"
>
<!-- <template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template> -->
</q-input>
</template>
</datepicker>
<q-input
class="col-2"
clearable
dense
outlined
lazy-rules
:model-value="govAge"
hide-bottom-space
label="อายุราชการ(ปี)"
type="number"
@update:model-value="updateGovAge"
/>
</div>
<div
:class="
$q.screen.lt.md
? ' row col-12 q-col-gutter-xs'
: 'no-wrap row col-12 q-col-gutter-xs'
"
>
<q-input
v-if="employeeClass == 'officer'"
clearable
dense
outlined
lazy-rules
:model-value="positionPath"
hide-bottom-space
label="ตำแหน่งในสายงาน"
@update:model-value="updatePositionPath"
class="col-3"
/>
<q-input
v-if="employeeClass == 'officer'"
clearable
dense
outlined
lazy-rules
:model-value="positionLevel"
hide-bottom-space
label="ระดับตำแหน่ง"
@update:model-value="updatePositionLevel"
class="col-3"
/>
<q-input
v-if="employeeClass == 'officer'"
clearable
dense
outlined
lazy-rules
:model-value="positionExecutive"
hide-bottom-space
label="ตำแหน่งทางการบริหาร"
@update:model-value="updatePositionExecutive"
class="col-3"
/>
<q-input
v-if="employeeClass != 'officer'"
clearable
dense
outlined
lazy-rules
:model-value="employeePosition"
hide-bottom-space
label="ตำแหน่ง"
@update:model-value="updateEmployeePosition"
class="col-3"
/>
<q-input
v-if="employeeClass != 'officer'"
clearable
dense
outlined
lazy-rules
:model-value="employeeLevel"
hide-bottom-space
label="ระดับชั้นงาน"
@update:model-value="updateEmployeeLevel"
class="col-3"
/>
<q-input
clearable
dense
outlined
lazy-rules
:model-value="posNo"
hide-bottom-space
label="ตำแหน่งเลขที่"
@update:model-value="updatePosNo"
class="col-3"
/>
</div>
<div
:class="
$q.screen.lt.md
? ' row col-12 q-col-gutter-xs'
: 'no-wrap row col-12 q-col-gutter-xs'
"
>
<datepicker
class="col-2"
menu-class-name="modalfix"
:model-value="reportYear"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
@update:model-value="updateReportYear"
clearable
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
class="inputgreen cursor-pointer q-mb-sm"
hide-bottom-space
outlined
dense
borderless
:model-value="
reportYear == null ? null : reportYear + 543
"
:label="`${'ปีงบประมาณ'}`"
clearable
@clear="clearReportDate"
>
<!-- <template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template> -->
</q-input>
</template>
</datepicker>
<q-input
clearable
dense
outlined
lazy-rules
:model-value="reportNo"
hide-bottom-space
label="เลขที่คำสั่ง"
@update:model-value="updateReportNo"
class="col-3"
/>
<q-input
clearable
dense
outlined
lazy-rules
:model-value="reportType"
hide-bottom-space
label="ประเภทคำสั่ง"
@update:model-value="updateReportType"
class="col-3"
/>
</div>
<div class="col-12 row items-center">
<q-toggle
dense
:model-value="isShowRetire"
color="primary"
@update:model-value="updateIsShowRetire"
class="q-pr-md"
>
แสดงขอมลผนจากราชการ
</q-toggle>
<q-toggle
dense
:model-value="isProbation"
color="primary"
@update:model-value="updateIsProbation"
>
ทดลองปฏหนาทราชการ
</q-toggle>
<q-space />
<div>
<q-btn
dense
color="primary"
icon="mdi-magnify"
label="ค้นหา"
class="q-px-md"
@click="doSearch"
/>
</div>
</div>
<!-- <div class="col-12 row q-col-gutter-y-md">
<div class="row q-gutter-md">
<q-radio
dense
:model-value="profileType"
val="officer"
label="ข้าราชการสามัญ"
class="q-pt-sm"
@update:model-value="updateProfileType"
/>
<q-radio
dense
:model-value="profileType"
val="employee"
class="q-pt-sm"
@update:model-value="updateProfileType"
>
กจาง
</q-radio>
</div>
<q-space />
<div>
<q-btn
dense
color="primary"
icon="mdi-magnify"
label="ค้นหา"
class="q-px-md"
@click="doSearch"
/>
</div>
</div> -->
</div>
</q-card-section>
</div>
</div>
</q-slide-transition>
</div>
</template>
<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">{{ col.label }}</span>
</q-th>
<q-th auto-width />
</q-tr>
</template>
<template #body="props">
<slot v-bind="props" name="columns"></slot>
</template>
</q-table>
</template>
<script setup lang="ts">
import { ref, useAttrs } from "vue";
import type {
Pagination,
DataOption,
} from "@/components/information/interface/store/main";
const attrs = ref<any>(useAttrs());
const table = ref<any>(null);
const filterRef = ref<any>(null);
const searchPanel = ref<boolean>(true);
const initialPagination = ref<Pagination>({
rowsPerPage: 0,
});
const employeeClassOps = ref<DataOption[]>([
{ id: "officer", name: "ข้าราชการ กทม.สามัญ" },
// { id: "employee", name: "()" },
{ id: "perm", name: "ลูกจ้างประจำ" },
{ id: "temp", name: "ลูกจ้างชั่วคราว" },
]);
const props = defineProps({
inputfilter: String,
inputvisible: Array,
profileId: String,
employeeClass: String,
fullName: String,
oldFullName: String,
isShowRetire: Boolean,
isProbation: Boolean,
// profileType: String,
retireYear: Number || null,
govAge: Number || null,
positionPath: String,
positionLevel: String,
positionExecutive: String,
employeePosition: String,
employeeLevel: String,
posNo: String,
reportYear: Number || null,
reportNo: String,
reportType: String,
isTab: {
type: Boolean,
default: true,
},
doSearch: {
type: Function,
default: () => console.log("not function"),
},
onExport: {
type: Function,
default: () => console.log("not function"),
},
onTab: {
type: Function,
default: () => console.log("not function"),
},
});
const emit = defineEmits([
"update:inputfilter",
"update:inputvisible",
"update:profileId",
"update:employeeClass",
"update:fullName",
"update:oldFullName",
"update:oldName",
"update:retireYear",
"update:govAge",
"update:isShowRetire",
// "update:profileType",
"update:isProbation",
"update:positionPath",
"update:positionLevel",
"update:positionExecutive",
"update:employeePosition",
"update:employeeLevel",
"update:posNo",
"update:reportYear",
"update:reportNo",
"update:reportType",
]);
const updateInput = (value: string | number | null) => {
emit("update:inputfilter", value);
};
const updateVisible = (value: []) => {
emit("update:inputvisible", value);
};
const updateProfileId = (value: string | number | null) => {
emit("update:profileId", value);
};
const updateEmployeeClass = (value: string | number | null) => {
emit("update:employeeClass", value);
};
const updateFullname = (value: string | number | null) => {
emit("update:fullName", value);
};
const updateRetireYear = (value: string | number | null) => {
emit("update:retireYear", value);
};
const updateGovAge = (value: string | number | null) => {
emit("update:govAge", value);
};
const updateIsShowRetire = (value: string | number | null) => {
emit("update:isShowRetire", value);
};
const updateIsProbation = (value: string | number | null) => {
emit("update:isProbation", value);
};
const updatePositionPath = (value: string | number | null) => {
emit("update:positionPath", value);
};
const updatePositionLevel = (value: string | number | null) => {
emit("update:positionLevel", value);
};
const updatePositionExecutive = (value: string | number | null) => {
emit("update:positionExecutive", value);
};
const updateEmployeePosition = (value: string | number | null) => {
emit("update:employeePosition", value);
};
const updateEmployeeLevel = (value: string | number | null) => {
emit("update:employeeLevel", value);
};
const updatePosNo = (value: string | number | null) => {
emit("update:posNo", value);
};
const updateReportYear = (value: string | number | null) => {
emit("update:reportYear", value);
};
const updateReportNo = (value: string | number | null) => {
emit("update:reportNo", value);
};
const updateReportType = (value: string | number | null) => {
emit("update:reportType", value);
};
// const updateProfileType = (value: string | number | null) => {
// emit("update:profileType", value);
// };
const clearDate = () => {
emit("update:retireYear", null);
};
const clearReportDate = () => {
emit("update:reportYear", null);
};
const clickSearchPanel = () => {
emit("update:retireYear", null);
emit("update:govAge", null);
emit("update:oldName", null);
emit("update:oldFullName", null);
emit("update:fullName", null);
emit("update:profileId", null);
emit("update:employeeClass", null);
searchPanel.value = !searchPanel.value;
};
const paginationLabel = (start: string, end: string, total: string) => {
return start + "-" + end + " ใน " + total;
};
const doSearch = () => {
props.doSearch();
};
const onExport = () => {
props.onExport();
};
const onTab = () => {
props.onTab();
};
const resetFilter = () => {
// reset X
emit("update:inputfilter", "");
filterRef.value.focus();
};
</script>
<style lang="scss">
.icon-color {
color: #4154b3;
}
.my-content {
background: #fff;
border: 1px solid rgba(133, 133, 133, 0.2);
border-radius: 5px;
}
.custom-table-height {
height: auto !important;
}
.custom-header-table2 {
.q-table__top {
padding: 0px !important;
}
.q-table__bottom {
border-top: none !important;
}
.q-table__middle {
border-radius: 5px;
border: 1px solid rgba(133, 133, 133, 0.2) !important;
}
.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;
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;
}
}
</style>

View file

@ -0,0 +1,683 @@
<!-- card ความสามารถพเศษ -->
<template>
<q-card flat bordered class="col-12 q-px-lg q-py-md">
<q-form ref="myForm">
<ProfileTable
:rows="rows"
:columns="columns"
:filter="filter"
:visible-columns="visibleColumns"
v-model:inputfilter="filter"
v-model:inputvisible="visibleColumns"
:add="clickAdd"
name="ความสามารถพิเศษ"
icon="mdi-folder-star"
:statusEdit="statusEdit"
>
<template #columns="props">
<q-tr :props="props">
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
@click="selectData(props)"
class="cursor-pointer"
>
<div class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="info"
flat
dense
round
size="14px"
icon="mdi-history"
@click="clickHistory(props.row)"
/>
</q-td>
</q-tr>
</template>
</ProfileTable>
</q-form>
</q-card>
<!-- popup Edit window-->
<q-dialog v-model="modal" persistent>
<q-card style="width: 600px">
<q-form ref="myForm">
<DialogHeader tittle="ความสามารถพิเศษ" :close="clickClose" />
<q-separator />
<q-card-section class="q-p-sm">
<div
class="row col-12 items-center q-col-gutter-x-xs q-col-gutter-y-xs"
>
<div class="col-6">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
autogrow
:borderless="!edit"
v-model="field"
:rules="[(val) => !!val || `${'กรุณากรอกด้าน'}`]"
hide-bottom-space
:label="`${'ด้าน'}`"
@update:modelValue="clickEditRow"
/>
</div>
<div class="col-6">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
autogrow
:borderless="!edit"
v-model="detail"
:rules="[(val) => !!val || `${'กรุณากรอกรายละเอียด'}`]"
hide-bottom-space
:label="`${'รายละเอียด'}`"
@update:modelValue="clickEditRow"
/>
</div>
<div class="col-12">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
autogrow
:borderless="!edit"
v-model="remark"
hide-bottom-space
:label="`${'หมายเหตุ'}`"
@update:modelValue="clickEditRow"
/>
</div>
<div class="col-12">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
autogrow
:borderless="!edit"
v-model="reference"
:rules="[(val) => !!val || `${'กรุณากรอกเอกสารอ้างอิง'}`]"
hide-bottom-space
:label="`${'เอกสารอ้างอิง'}`"
@update:modelValue="clickEditRow"
/>
</div>
</div>
</q-card-section>
<q-separator />
<DialogFooter
:cancel="clickCancel"
:edit="clickEdit"
:save="clickSave"
:validate="validateData"
:clickNext="clickNext"
:clickPrevious="clickPrevious"
:clickDelete="clickDelete"
v-model:editvisible="edit"
v-model:next="next"
v-model:previous="previous"
v-model:modalEdit="modalEdit"
/>
</q-form>
</q-card>
</q-dialog>
<HistoryTable
:rows="rowsHistory"
:columns="columnsHistory"
:filter="filterHistory"
:visible-columns="visibleColumnsHistory"
v-model:modal="modalHistory"
v-model:inputfilter="filterHistory"
v-model:inputvisible="visibleColumnsHistory"
v-model:tittle="tittleHistory"
>
<template #columns="props">
<q-tr :props="props">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div v-if="col.name == 'createdAt'" class="table_ellipsis">
{{ date2Thai(col.value) }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
</q-tr>
</template>
</HistoryTable>
</template>
<script setup lang="ts">
import { onMounted, ref, watch } from "vue";
import { useRoute } from "vue-router";
import { useComponentProfileDataStore } from "@/components/information/store/store";
import { useCounterMixin } from "@/stores/mixin";
import ProfileTable from "@/components/information/Table.vue";
import DialogHeader from "@/components/information/DialogHeader.vue";
import DialogFooter from "@/components/information/DialogFooter.vue";
import { useQuasar } from "quasar";
import type {
RequestItemsObject,
DataProps,
} from "@/components/information/interface/request/Talent";
import type { ResponseObject } from "@/components/information/interface/response/Talent";
import HistoryTable from "@/components/TableHistory.vue";
import http from "@/plugins/http";
import config from "@/app.config";
import type { QTableProps } from "quasar";
const props = defineProps({
statusEdit: {
type: Boolean,
required: true,
},
});
const $q = useQuasar();
const store = useComponentProfileDataStore();
const { profileData, changeProfileColumns } = store;
const mixin = useCounterMixin();
const { date2Thai, success, dateToISO, messageError, showLoader, hideLoader } =
mixin;
const route = useRoute();
const id = ref<string>("");
const field = ref<string>();
const detail = ref<string>();
const remark = ref<string>();
const reference = ref<string>();
const myForm = ref<any>(); //form data input
const edit = ref<boolean>(false); // dialog
const modal = ref<boolean>(false); //modal add detail
const modalEdit = ref<boolean>(false); //modal
const rawItem = ref<RequestItemsObject>(); // row
const rowIndex = ref<number>(0); //index row
const previous = ref<boolean>(); //
const next = ref<boolean>(); //
const editRow = ref<boolean>(false); //
const rowsHistory = ref<RequestItemsObject[]>([]); //select data history
const tittleHistory = ref<string>("ประวัติแก้ไขความสามารถพิเศษ"); //
const filterHistory = ref<string>(""); //search data table history
const modalHistory = ref<boolean>(false); //modal
const checkValidate = ref<boolean>(false); //validate data
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const rows = ref<RequestItemsObject[]>([]);
const filter = ref<string>(""); //search data table
const visibleColumns = ref<String[]>([]);
profileData.talent.columns.length == 0
? (visibleColumns.value = ["field", "detail", "remark", "reference"])
: (visibleColumns.value = profileData.talent.columns);
const columns = ref<QTableProps["columns"]>([
{
name: "field",
align: "left",
label: "ด้าน",
sortable: true,
field: "field",
headerStyle: "font-size: 14px; width: 50px;",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "remark",
align: "left",
label: "หมายเหตุ",
sortable: true,
field: "remark",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "reference",
align: "left",
label: "เอกสารอ้างอิง",
sortable: true,
field: "reference",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const columnsHistory = ref<QTableProps["columns"]>([
{
name: "field",
align: "left",
label: "ด้าน",
sortable: true,
field: "field",
headerStyle: "font-size: 14px; width: 50px;",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "remark",
align: "left",
label: "หมายเหตุ",
sortable: true,
field: "remark",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "reference",
align: "left",
label: "เอกสารอ้างอิง",
sortable: true,
field: "reference",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "createdFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const visibleColumnsHistory = ref<String[]>([
"field",
"detail",
"remark",
"reference",
"createdFullName",
"createdAt",
]);
watch(visibleColumns, async (count: String[], prevCount: String[]) => {
await changeProfileColumns("talent", count);
});
onMounted(async () => {
await fetchData();
});
const fetchData = async () => {
if (profileId.value) {
showLoader();
await http
.get(config.API.profileAbiliId(profileId.value))
.then((res) => {
let data = res.data.result;
rows.value = [];
data.map((e: ResponseObject) => {
rows.value.push({
id: e.id,
field: e.field,
detail: e.detail,
remark: e.remark,
reference: e.reference,
createdFullName: e.createdFullName,
createdAt: new Date(e.createdAt),
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
};
/**
* กดดอมลกอนหน
*/
const clickPrevious = async () => {
edit.value = false;
rowIndex.value -= 1;
await getData();
await checkRowPage();
};
/**
* กดดอมลตอไป
*/
const clickNext = async () => {
edit.value = false;
rowIndex.value += 1;
await getData();
await checkRowPage();
};
/**
* กดดอมลตอไป
*/
const getData = () => {
const row = rows.value[rowIndex.value];
field.value = row.field;
detail.value = row.detail;
remark.value = row.remark;
reference.value = row.reference;
id.value = row.id;
};
/**
* เชคปมดอม อน อไป าตองแสดงไหม
*/
const checkRowPage = () => {
editRow.value = false;
next.value = true;
previous.value = true;
if (rowIndex.value + 1 >= rows.value.length) {
next.value = false;
}
if (rowIndex.value - 1 < 0) {
previous.value = false;
}
};
/**
* กดปมแกไขใน dialog
*/
const clickEdit = () => {
editRow.value = false;
next.value = false;
previous.value = false;
};
/**
* กดปมเพมดานบน table
*/
const clickAdd = async () => {
editRow.value = false;
await addData();
};
/**
* กดบนทกใน dialog
*/
const clickSave = async () => {
myForm.value.validate().then(async (result: boolean) => {
if (result) {
if (modalEdit.value) {
await editData();
} else {
await saveData();
}
}
});
};
/**
* นทกเพมขอม
*/
const saveData = async () => {
if (profileId.value) {
showLoader();
await http
.post(config.API.profileAbiliId(profileId.value), {
id: id.value,
field: field.value,
detail: detail.value,
remark: remark.value,
reference: reference.value,
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
}
};
/**
* นทกแกไขขอม
*/
const editData = async () => {
showLoader();
await http
.put(config.API.profileAbiliId(id.value), {
id: id.value,
field: field.value,
detail: detail.value,
remark: remark.value,
reference: reference.value,
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
};
/**
* ลบลบขอม
*/
const clickDelete = async () => {
$q.dialog({
title: `ลบข้อมูล`,
message: `ต้องการทำการลบข้อมูลนี้ใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
})
.onOk(async () => {
showLoader();
await http
.delete(config.API.profileAbiliId(id.value))
.then((res) => {
success($q, "ลบข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
})
.onCancel(async () => {
await fetchData();
});
};
/**
* กดป dialog
*/
const clickClose = async () => {
if (editRow.value == true) {
$q.dialog({
title: `ข้อมูลมีการแก้ไข`,
message: `ยืนยันที่จะปิดโดยไม่บันทึกใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
}).onOk(async () => {
modal.value = false;
next.value = false;
previous.value = false;
});
} else {
modal.value = false;
next.value = false;
previous.value = false;
}
};
/**
* กดเลอกขอมลทจะแกไข
* @param props props ใน row เลอก
*/
const selectData = async (props: DataProps) => {
modalEdit.value = true;
modal.value = true;
edit.value = false;
rawItem.value = props.row;
rowIndex.value = props.rowIndex;
field.value = props.row.field;
detail.value = props.row.detail;
remark.value = props.row.remark;
reference.value = props.row.reference;
id.value = props.row.id;
await checkRowPage();
};
/**
* กดปมเพมบน table
*/
const addData = () => {
modalEdit.value = false;
modal.value = true;
edit.value = true;
field.value = "";
detail.value = "";
remark.value = "";
reference.value = "";
};
/**
* งกนปมยกเลกการแกไขขอม
*/
const clickCancel = async () => {
if (editRow.value == true) {
$q.dialog({
title: `ข้อมูลมีการแก้ไข`,
message: `ยืนยันยกเลิกการแก้ไขใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
}).onOk(async () => {
edit.value = false;
await checkRowPage();
await getData();
});
} else {
edit.value = false;
await checkRowPage();
}
};
/**
* เชความการแกไขขอม
*/
const clickEditRow = () => {
editRow.value = true;
};
/**
* งชนดอมลประวแกไขขอมลทเลอก
* @param row อม row ประวการแกไข
*/
const clickHistory = async (row: RequestItemsObject) => {
modalHistory.value = true;
showLoader();
await http
.get(config.API.profileAbiliHisId(row.id))
.then((res) => {
let data = res.data.result;
rowsHistory.value = [];
data.map((e: ResponseObject) => {
rowsHistory.value.push({
id: e.id,
field: e.field,
detail: e.detail,
remark: e.remark,
reference: e.reference,
createdFullName: e.createdFullName,
createdAt: new Date(e.createdAt),
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
};
/**
* validate input ใน dialog
*/
const validateData = async () => {
checkValidate.value = true;
await myForm.value.validate().then((result: boolean) => {
if (result == false) {
checkValidate.value = false;
}
});
};
/**
* class ดรปแบบแสดงระหวางขอมลทแกไขหรอแสดงเฉยๆ
* @param val อม input สำหรบแกไขหรอไม
*/
const getClass = (val: boolean) => {
return {
"full-width inputgreen cursor-pointer": val,
"full-width cursor-pointer": !val,
};
};
</script>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,907 @@
<!-- card ปฏราชการพเศษ -->
<template>
<q-card flat bordered class="col-12 q-px-lg q-py-md">
<q-form ref="myForm">
<ProfileTable
:rows="rows"
:columns="columns"
:filter="filter"
:visible-columns="visibleColumns"
v-model:inputfilter="filter"
v-model:inputvisible="visibleColumns"
:add="clickAdd"
name="ปฏิบัติราชการพิเศษ"
icon="mdi-bookmark"
:statusEdit="statusEdit"
>
<template #columns="props">
<q-tr :props="props">
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
@click="selectData(props)"
class="cursor-pointer"
>
<div
v-if="
col.name == 'dateStart' ||
col.name == 'dateEnd' ||
col.name == 'refCommandDate'
"
class="table_ellipsis"
>
{{ col.value == null ? null : date2Thai(col.value) }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="info"
flat
dense
round
size="14px"
icon="mdi-history"
@click="clickHistory(props.row)"
/>
</q-td>
</q-tr>
</template>
</ProfileTable>
</q-form>
</q-card>
<!-- popup Edit window-->
<q-dialog v-model="modal" persistent>
<q-card style="width: 600px">
<q-form ref="myForm">
<DialogHeader tittle="ปฏิบัติราชการพิเศษ" :close="clickClose" />
<q-separator />
<q-card-section class="q-p-sm">
<div
class="row col-12 items-center q-col-gutter-x-xs q-col-gutter-y-xs"
>
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
:readonly="!edit"
v-model="dateStart"
:locale="'th'"
autoApply
:enableTimePicker="false"
@update:modelValue="clickEditRow"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
:borderless="!edit"
:model-value="date2Thai(dateStart)"
:rules="[(val) => !!val || `${'กรุณาเลือกวันที่เริ่มต้น'}`]"
hide-bottom-space
:label="`${'วันที่เริ่มต้น'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
:style="
edit
? 'color: var(--q-primary)'
: 'color: var(--q-grey)'
"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
v-model="dateEnd"
:locale="'th'"
autoApply
:enableTimePicker="false"
:min-date="minDate"
:readonly="!edit"
@update:modelValue="clickEditRow"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
:borderless="!edit"
:model-value="date2Thai(dateEnd)"
:rules="[(val) => !!val || `${'กรุณาเลือกวันที่สิ้นสุด'}`]"
hide-bottom-space
:label="`${'วันที่สิ้นสุด'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
:style="
edit
? 'color: var(--q-primary)'
: 'color: var(--q-grey)'
"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-12">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
autogrow
:readonly="!edit"
:borderless="!edit"
v-model="reference"
:rules="[(val) => !!val || `${'กรุณากรอกเอกสารอ้างอิง'}`]"
hide-bottom-space
:label="`${'เอกสารอ้างอิง'}`"
@update:modelValue="clickEditRow"
/>
</div>
<div class="col-12">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
autogrow
:readonly="!edit"
:borderless="!edit"
v-model="detail"
:rules="[(val) => !!val || `${'กรุณากรอกรายละเอียด'}`]"
hide-bottom-space
:label="`${'รายละเอียด'}`"
@update:modelValue="clickEditRow"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="refCommandNo"
hide-bottom-space
:label="`${'เลขที่คำสั่ง'}`"
@update:modelValue="clickEditRow"
>
<template v-slot:append>
<q-icon
name="mdi-file"
class="cursor-pointer"
@click="resetFilter"
/>
</template>
</q-input>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
:readonly="!edit"
v-model="refCommandDate"
:locale="'th'"
autoApply
:enableTimePicker="false"
@update:modelValue="clickEditRow"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
:class="getClass(edit)"
:outlined="edit"
dense
:borderless="!edit"
:model-value="
refCommandDate == null ? null : date2Thai(refCommandDate)
"
hide-bottom-space
:label="`${'เอกสารอ้างอิง (ลงวันที่)'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
:style="
edit
? 'color: var(--q-primary)'
: 'color: var(--q-grey)'
"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
</div>
</q-card-section>
<q-separator />
<DialogFooter
:cancel="clickCancel"
:edit="clickEdit"
:save="clickSave"
:validate="validateData"
:clickNext="clickNext"
:clickPrevious="clickPrevious"
:clickDelete="clickDelete"
v-model:editvisible="edit"
v-model:next="next"
v-model:previous="previous"
v-model:modalEdit="modalEdit"
/>
</q-form>
</q-card>
</q-dialog>
<HistoryTable
:rows="rowsHistory"
:columns="columnsHistory"
:filter="filterHistory"
:visible-columns="visibleColumnsHistory"
v-model:modal="modalHistory"
v-model:inputfilter="filterHistory"
v-model:inputvisible="visibleColumnsHistory"
v-model:tittle="tittleHistory"
>
<template #columns="props">
<q-tr :props="props">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div
v-if="
col.name == 'dateStart' ||
col.name == 'dateEnd' ||
col.name == 'createdAt' ||
col.name == 'refCommandDate'
"
class="table_ellipsis"
>
{{ col.value == null ? null : date2Thai(col.value) }}
</div>
<div v-else class="table_ellipsis">
{{ col.value }}
</div>
</q-td>
</q-tr>
</template>
</HistoryTable>
</template>
<script setup lang="ts">
import { onMounted, ref, watch } from "vue";
import { useRoute } from "vue-router";
import { useComponentProfileDataStore } from "@/components/information/store/store";
import { useCounterMixin } from "@/stores/mixin";
import ProfileTable from "@/components/information/Table.vue";
import DialogHeader from "@/components/information/DialogHeader.vue";
import DialogFooter from "@/components/information/DialogFooter.vue";
import { useQuasar } from "quasar";
import type {
RequestItemsObject,
DataProps,
} from "@/components/information/interface/request/Work";
import type { ResponseObject } from "@/components/information/interface/response/Work";
import HistoryTable from "@/components/TableHistory.vue";
import http from "@/plugins/http";
import config from "@/app.config";
import type { QTableProps } from "quasar";
const props = defineProps({
statusEdit: {
type: Boolean,
required: true,
},
});
const $q = useQuasar();
const store = useComponentProfileDataStore();
const { profileData, changeProfileColumns } = store;
const mixin = useCounterMixin();
const { date2Thai, success, dateToISO, messageError, showLoader, hideLoader } =
mixin;
const route = useRoute();
const id = ref<string>("");
const dateStart = ref<Date>(new Date());
const dateEnd = ref<Date>(new Date());
const detail = ref<string>();
const reference = ref<string>();
const minDate = ref<Date>();
const refCommandNo = ref<string>();
const refCommandDate = ref<Date | null>(new Date());
const myForm = ref<any>(); //form data input
const edit = ref<boolean>(false); // dialog
const modal = ref<boolean>(false); //modal add detail
const modalEdit = ref<boolean>(false); //modal
const rawItem = ref<RequestItemsObject>(); // row
const rowIndex = ref<number>(0); //index row
const previous = ref<boolean>(); //
const next = ref<boolean>(); //
const editRow = ref<boolean>(false); //
const rowsHistory = ref<RequestItemsObject[]>([]); //select data history
const tittleHistory = ref<string>("ประวัติแก้ไขปฏิบัติราชการพิเศษ"); //
const filterHistory = ref<string>(""); //search data table history
const modalHistory = ref<boolean>(false); //modal
const checkValidate = ref<boolean>(false); //validate data
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const rows = ref<RequestItemsObject[]>([]);
const filter = ref<string>(""); //search data table
const visibleColumns = ref<String[]>([]);
profileData.work.columns.length == 0
? (visibleColumns.value = [
"dateStart",
"dateEnd",
"detail",
"reference",
"refCommandNo",
"refCommandDate",
])
: (visibleColumns.value = profileData.work.columns);
const columns = ref<QTableProps["columns"]>([
{
name: "dateStart",
align: "left",
label: "เริ่มต้น",
sortable: true,
field: "dateStart",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "dateEnd",
align: "left",
label: "สิ้นสุด",
sortable: true,
field: "dateEnd",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "reference",
align: "left",
label: "เอกสารอ้างอิง",
sortable: true,
field: "reference",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandNo",
align: "left",
label: "เลขที่คำสั่ง",
sortable: true,
field: "refCommandNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandDate",
align: "left",
label: "เอกสารอ้างอิง (ลงวันที่)",
sortable: true,
field: "refCommandDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const columnsHistory = ref<QTableProps["columns"]>([
{
name: "dateStart",
align: "left",
label: "เริ่มต้น",
sortable: true,
field: "dateStart",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "dateEnd",
align: "left",
label: "สิ้นสุด",
sortable: true,
field: "dateEnd",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "reference",
align: "left",
label: "เอกสารอ้างอิง",
sortable: true,
field: "reference",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandNo",
align: "left",
label: "เลขที่คำสั่ง",
sortable: true,
field: "refCommandNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandDate",
align: "left",
label: "เอกสารอ้างอิง (ลงวันที่)",
sortable: true,
field: "refCommandDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "createdFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const visibleColumnsHistory = ref<String[]>([
"dateStart",
"dateEnd",
"detail",
"reference",
"refCommandNo",
"refCommandDate",
"createdFullName",
"createdAt",
]);
watch(dateStart, (count, prevCount) => {
minDate.value = new Date(dateStart.value);
});
watch(visibleColumns, async (count: String[], prevCount: String[]) => {
await changeProfileColumns("work", count);
});
onMounted(async () => {
await fetchData();
});
const fetchData = async () => {
if (profileId.value) {
showLoader();
await http
.get(config.API.profileDutyId(profileId.value))
.then((res) => {
let data = res.data.result;
rows.value = [];
data.map((e: ResponseObject) => {
rows.value.push({
id: e.id,
dateStart: new Date(e.dateStart),
dateEnd: new Date(e.dateEnd),
detail: e.detail,
reference: e.reference,
refCommandNo: e.refCommandNo,
refCommandDate:
e.refCommandDate == null ? null : new Date(e.refCommandDate),
createdFullName: e.createdFullName,
createdAt: new Date(e.createdAt),
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
};
/**
* กดดอมลกอนหน
*/
const clickPrevious = async () => {
edit.value = false;
rowIndex.value -= 1;
await getData();
await checkRowPage();
};
/**
* กดดอมลตอไป
*/
const clickNext = async () => {
edit.value = false;
rowIndex.value += 1;
await getData();
await checkRowPage();
};
/**
* กดดอมลตอไป
*/
const getData = () => {
const row = rows.value[rowIndex.value];
dateStart.value = row.dateStart;
dateEnd.value = row.dateEnd;
detail.value = row.detail;
reference.value = row.reference;
refCommandNo.value = row.refCommandNo;
refCommandDate.value = row.refCommandDate;
id.value = row.id;
};
/**
* เชคปมดอม อน อไป าตองแสดงไหม
*/
const checkRowPage = () => {
editRow.value = false;
next.value = true;
previous.value = true;
if (rowIndex.value + 1 >= rows.value.length) {
next.value = false;
}
if (rowIndex.value - 1 < 0) {
previous.value = false;
}
};
/**
* กดปมแกไขใน dialog
*/
const clickEdit = () => {
editRow.value = false;
next.value = false;
previous.value = false;
};
/**
* กดปมเพมดานบน table
*/
const clickAdd = async () => {
editRow.value = false;
await addData();
};
/**
* กดบนทกใน dialog
*/
const clickSave = async () => {
myForm.value.validate().then(async (result: boolean) => {
if (result) {
if (modalEdit.value) {
await editData();
} else {
await saveData();
}
}
});
};
/**
* นทกเพมขอม
*/
const saveData = async () => {
if (profileId.value) {
showLoader();
await http
.post(config.API.profileDutyId(profileId.value), {
id: id.value,
dateStart: dateToISO(dateStart.value),
dateEnd: dateToISO(dateEnd.value),
detail: detail.value,
reference: reference.value,
refCommandNo: refCommandNo.value,
refCommandDate:
refCommandDate.value == null ? null : dateToISO(refCommandDate.value),
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
}
};
/**
* นทกแกไขขอม
*/
const editData = async () => {
showLoader();
await http
.put(config.API.profileDutyId(id.value), {
id: id.value,
dateStart: dateToISO(dateStart.value),
dateEnd: dateToISO(dateEnd.value),
detail: detail.value,
reference: reference.value,
refCommandNo: refCommandNo.value,
refCommandDate:
refCommandDate.value == null ? null : dateToISO(refCommandDate.value),
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
};
/**
* ลบลบขอม
*/
const clickDelete = async () => {
$q.dialog({
title: `ลบข้อมูล`,
message: `ต้องการทำการลบข้อมูลนี้ใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
})
.onOk(async () => {
showLoader();
await http
.delete(config.API.profileDutyId(id.value))
.then((res) => {
success($q, "ลบข้อมูลสำเร็จ");
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
})
.onCancel(async () => {
await fetchData();
});
};
/**
* กดป dialog
*/
const clickClose = async () => {
if (editRow.value == true) {
$q.dialog({
title: `ข้อมูลมีการแก้ไข`,
message: `ยืนยันที่จะปิดโดยไม่บันทึกใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
}).onOk(async () => {
modal.value = false;
next.value = false;
previous.value = false;
});
} else {
modal.value = false;
next.value = false;
previous.value = false;
}
};
/**
* กดเลอกขอมลทจะแกไข
* @param props props ใน row เลอก
*/
const selectData = async (props: DataProps) => {
modalEdit.value = true;
modal.value = true;
edit.value = false;
rawItem.value = props.row;
rowIndex.value = props.rowIndex;
dateStart.value = props.row.dateStart;
dateEnd.value = props.row.dateEnd;
detail.value = props.row.detail;
reference.value = props.row.reference;
refCommandNo.value = props.row.refCommandNo;
refCommandDate.value = props.row.refCommandDate;
id.value = props.row.id;
await checkRowPage();
};
/**
* กดปมเพมบน table
*/
const addData = () => {
modalEdit.value = false;
modal.value = true;
edit.value = true;
dateStart.value = new Date();
dateEnd.value = new Date();
detail.value = "";
reference.value = "";
refCommandNo.value = "";
refCommandDate.value = null;
};
/**
* งกนปมยกเลกการแกไขขอม
*/
const clickCancel = async () => {
if (editRow.value == true) {
$q.dialog({
title: `ข้อมูลมีการแก้ไข`,
message: `ยืนยันยกเลิกการแก้ไขใช่หรือไม่?`,
cancel: "ยกเลิก",
ok: "ยืนยัน",
persistent: true,
}).onOk(async () => {
edit.value = false;
await checkRowPage();
await getData();
});
} else {
edit.value = false;
await checkRowPage();
}
};
/**
* เชความการแกไขขอม
*/
const clickEditRow = () => {
editRow.value = true;
};
/**
* งชนดอมลประวแกไขขอมลทเลอก
* @param row อม row ประวการแกไข
*/
const clickHistory = async (row: RequestItemsObject) => {
modalHistory.value = true;
showLoader();
await http
.get(config.API.profileDutyHisId(row.id))
.then((res) => {
let data = res.data.result;
rowsHistory.value = [];
data.map((e: ResponseObject) => {
rowsHistory.value.push({
id: e.id,
dateStart: new Date(e.dateStart),
dateEnd: new Date(e.dateEnd),
detail: e.detail,
reference: e.reference,
refCommandNo: e.refCommandNo,
refCommandDate:
e.refCommandDate == null ? null : new Date(e.refCommandDate),
createdFullName: e.createdFullName,
createdAt: new Date(e.createdAt),
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
};
/**
* validate input ใน dialog
*/
const validateData = async () => {
checkValidate.value = true;
await myForm.value.validate().then((result: boolean) => {
if (result == false) {
checkValidate.value = false;
}
});
};
/**
* class ดรปแบบแสดงระหวางขอมลทแกไขหรอแสดงเฉยๆ
* @param val อม input สำหรบแกไขหรอไม
*/
const getClass = (val: boolean) => {
return {
"full-width inputgreen cursor-pointer": val,
"full-width cursor-pointer": !val,
};
};
const resetFilter = () => {};
</script>
<style lang="scss">
.modalfix {
position: fixed !important;
}
</style>

View file

@ -0,0 +1,84 @@
import type { zipCodeOption } from "@/components/information/interface/index/profileType";
interface Pagination {
rowsPerPage: number;
}
interface DataOption {
id: string;
name: string;
disable?: boolean;
}
interface DataOptionLeave {
id: string;
name: string;
totalLeave: number;
}
interface DataOptionInsignia {
id: string;
name: string;
typeName: string;
}
interface treeTab {
id: string;
label: string;
children: treeTab[];
}
interface InformationOps {
prefixOps: DataOption[];
prefixOldOps: DataOption[];
genderOps: DataOption[];
bloodOps: DataOption[];
statusOps: DataOption[];
religionOps: DataOption[];
employeeClassOps: DataOption[];
employeeTypeOps: DataOption[];
}
interface OldNameOps {
prefixOps: DataOption[];
statusOps: DataOption[];
}
interface AddressOps {
provinceOps: DataOption[];
districtOps: DataOption[];
districtCOps: DataOption[];
subdistrictOps: zipCodeOption[];
subdistrictCOps: zipCodeOption[];
}
interface FamilyOps {
prefixOps: DataOption[];
}
interface EduOps {
levelOptions: DataOption[];
positionPathOptions: DataOption[];
}
interface InsigniaOps {
insigniaOptions: DataOptionInsignia[];
}
interface DisciplineOps {
levelOptions: DataOption[];
}
export type {
Pagination,
DataOption,
DataOptionInsignia,
treeTab,
InformationOps,
OldNameOps,
AddressOps,
FamilyOps,
EduOps,
InsigniaOps,
DisciplineOps,
DataOptionLeave,
};

View file

@ -0,0 +1,210 @@
//interface class array object {name string ,id number}
import type { childrenFamily } from "@/modules/04_registry/interface/response/Family";
interface ChangeActive {
name: string;
id: number;
}
//ข้อมูลส่วนตัว
interface Information {
cardid: string | null;
prefix: string | null;
age: string | null;
prefixId: string | null;
firstname: string | null;
lastname: string | null;
birthDate: Date | null;
genderId: string | null;
bloodId: string | null;
nationality: string | null;
ethnicity: string | null;
statusId: string | null;
religionId: string | null;
tel: string | null;
employeeType: string | null;
employeeClass: string | null;
profileType: string | null;
}
interface Family {
prefixC: string | null; // couple
prefixIdC: string | null;
firstnameC: string | null;
lastnameC: string | null;
lastnameCOld: string | null;
occupationC: string | null;
citizenIdC: string | null;
liveC: string | null;
prefixM: string | null; // male
prefixIdM: string | null;
firstnameM: string | null;
lastnameM: string | null;
occupationM: string | null;
citizenIdM: string | null;
liveM: string | null;
prefixF: string | null; // female
prefixIdF: string | null;
firstnameF: string | null;
lastnameF: string | null;
occupationF: string | null;
citizenIdF: string | null;
liveF: string | null;
same: string | null;
childrens: childrenFamily[];
}
interface Address {
address: string | null;
provinceId: string | null;
districtId: string | null;
subdistrictId: string | null;
addressC: string | null;
provinceIdC: string | null;
districtIdC: string | null;
subdistrictIdC: string | null;
same: string | null;
codec: string | null;
codep: string | null;
}
interface Goverment {
ocId: string | null;
positionId: string | null;
positionPathSide: string | null;
positionLine: string | null;
positionType: string | null;
positionLevel: string | null;
numberId: string | null;
positionExecutive: string | null;
positionExecutiveSide: string | null;
containDate: Date;
workDate: Date;
retireDate: string | null;
absent: number | null;
age: number | null;
ageAll: string | null;
reasonSameDate: string | null;
}
interface DataOption {
id: string;
name: string;
zipCode?: string;
}
interface zipCodeOption {
id: string;
name: string;
zipCode: string;
}
const defaultGoverment: Goverment = {
ocId: null,
positionId: null,
positionPathSide: null,
positionLine: null,
positionType: null,
positionLevel: null,
numberId: null,
positionExecutive: null,
positionExecutiveSide: null,
containDate: new Date(),
workDate: new Date(),
retireDate: null,
absent: 0,
age: 0,
ageAll: null,
reasonSameDate: null,
};
const defaultAddress: Address = {
address: null,
provinceId: null,
districtId: null,
subdistrictId: null,
addressC: null,
provinceIdC: null,
districtIdC: null,
subdistrictIdC: null,
codec: null,
codep: null,
same: "0",
};
const defaultInformation: Information = {
cardid: null,
age: null,
prefix: null,
prefixId: null,
firstname: null,
lastname: null,
birthDate: null,
genderId: null,
bloodId: null,
nationality: null,
ethnicity: null,
statusId: null,
religionId: null,
tel: null,
employeeType: null,
employeeClass: null,
profileType: null,
};
const defaultTempInformation: any = {
employeeMoneyIncrease: null,
employeeMoneyAllowance: null,
employeeMoneyEmployee: null,
employeeMoneyEmployer: null,
positionEmployeeGroupId: null,
positionEmployeePositionId: null,
positionEmployeeLineId: null,
employeeTypeIndividual: null,
employeeOc: null,
employeeWage: null,
};
const defaultFamily: Family = {
prefixC: null,
prefixIdC: null,
firstnameC: null,
lastnameC: null,
lastnameCOld: null,
occupationC: null,
citizenIdC: null,
liveC: "1",
prefixM: null,
prefixIdM: null,
firstnameM: null,
lastnameM: null,
occupationM: null,
citizenIdM: null,
liveM: "1",
prefixF: null,
prefixIdF: null,
firstnameF: null,
lastnameF: null,
occupationF: null,
citizenIdF: null,
liveF: "1",
same: "0",
childrens: [],
};
export {
defaultInformation,
defaultFamily,
defaultAddress,
defaultGoverment,
defaultTempInformation,
};
export type {
ChangeActive,
Information,
Family,
Address,
Goverment,
DataOption,
zipCodeOption,
};

View file

@ -0,0 +1,187 @@
//interface class array object {name string ,id number}
import type { childrenFamily } from "@/modules/04_registry/interface/response/Family";
interface ChangeActive {
name: string;
id: number;
}
//ข้อมูลส่วนตัว
interface Information {
cardid: string | null;
prefix: string | null;
age: string | null;
prefixId: string | null;
firstname: string | null;
lastname: string | null;
birthDate: Date | null;
genderId: string | null;
bloodId: string | null;
nationality: string | null;
ethnicity: string | null;
statusId: string | null;
religionId: string | null;
tel: string | null;
employeeType: string | null;
employeeClass: string | null;
profileType: string | null;
}
interface Family {
prefixC: string | null; // couple
prefixIdC: string | null;
firstnameC: string | null;
lastnameC: string | null;
lastnameCOld: string | null;
occupationC: string | null;
citizenIdC: string | null;
liveC: string | null;
prefixM: string | null; // male
prefixIdM: string | null;
firstnameM: string | null;
lastnameM: string | null;
occupationM: string | null;
citizenIdM: string | null;
liveM: string | null;
prefixF: string | null; // female
prefixIdF: string | null;
firstnameF: string | null;
lastnameF: string | null;
occupationF: string | null;
citizenIdF: string | null;
liveF: string | null;
same: string | null;
childrens: childrenFamily[];
}
interface Address {
address: string | null;
provinceId: string | null;
districtId: string | null;
subdistrictId: string | null;
addressC: string | null;
provinceIdC: string | null;
districtIdC: string | null;
subdistrictIdC: string | null;
same: string | null;
}
interface Goverment {
ocId: string | null;
positionId: string | null;
positionPathSide: string | null;
positionLine: string | null;
positionType: string | null;
positionLevel: string | null;
numberId: string | null;
positionExecutive: string | null;
positionExecutiveSide: string | null;
containDate: Date;
workDate: Date;
retireDate: string | null;
absent: number | null;
age: number | null;
ageAll: string | null;
reasonSameDate: string | null;
}
interface DataOption {
id: string;
name: string;
zipCode?: string;
}
interface zipCodeOption {
id: string;
name: string;
zipCode: string;
}
const defaultGoverment: Goverment = {
ocId: null,
positionId: null,
positionPathSide: null,
positionLine: null,
positionType: null,
positionLevel: null,
numberId: null,
positionExecutive: null,
positionExecutiveSide: null,
containDate: new Date(),
workDate: new Date(),
retireDate: null,
absent: 0,
age: 0,
ageAll: null,
reasonSameDate: null,
};
const defaultAddress: Address = {
address: null,
provinceId: null,
districtId: null,
subdistrictId: null,
addressC: null,
provinceIdC: null,
districtIdC: null,
subdistrictIdC: null,
same: "0",
};
const defaultInformation: Information = {
cardid: null,
age: null,
prefix: null,
prefixId: null,
firstname: null,
lastname: null,
birthDate: null,
genderId: null,
bloodId: null,
nationality: null,
ethnicity: null,
statusId: null,
religionId: null,
tel: null,
employeeType: null,
employeeClass: null,
profileType: null,
};
const defaultFamily: Family = {
prefixC: null,
prefixIdC: null,
firstnameC: null,
lastnameC: null,
lastnameCOld: null,
occupationC: null,
citizenIdC: null,
liveC: "1",
prefixM: null,
prefixIdM: null,
firstnameM: null,
lastnameM: null,
occupationM: null,
citizenIdM: null,
liveM: "1",
prefixF: null,
prefixIdF: null,
firstnameF: null,
lastnameF: null,
occupationF: null,
citizenIdF: null,
liveF: "1",
same: "0",
childrens: [],
};
export { defaultInformation, defaultFamily, defaultAddress, defaultGoverment };
export type {
ChangeActive,
Information,
Family,
Address,
Goverment,
DataOption,
zipCodeOption,
};

View file

@ -0,0 +1,55 @@
interface DataProps {
row: RequestItemsObject;
rowIndex: number;
}
//ข้อมูล
interface RequestItemsObject {
currentAddress: String | null;
currentDistrictId: String | null;
currentProvinceId: String | null;
currentSubDistrictId: String | null;
currentZipCode: String | null;
registrationAddress: String | null;
registrationDistrictId: String | null;
registrationProvinceId: String | null;
registrationSame: Boolean | null;
registrationSubDistrictId: String | null;
registrationZipCode: String | null;
}
interface RequestItemsHistoryObject {
currentAddress: String | null;
currentDistrict: String | null;
currentProvince: String | null;
currentSubDistrict: String | null;
currentZipCode: String | null;
registrationAddress: String | null;
registrationDistrict: String | null;
registrationProvince: String | null;
registrationSame: Boolean | null;
registrationSubDistrict: String | null;
registrationZipCode: String | null;
createdFullName: String | null;
createdAt: Date;
}
//columns
interface Columns {
[index: number]: {
name: String;
align: String;
label: String;
sortable: Boolean;
field: String;
headerStyle: String;
style: String;
};
}
export type {
RequestItemsObject,
Columns,
DataProps,
RequestItemsHistoryObject,
};

View file

@ -0,0 +1,34 @@
interface DataProps {
row: RequestItemsObject;
rowIndex: number;
}
//ข้อมูล
interface RequestItemsObject {
id: string;
point1Total: number;
point1: number;
point2Total: number;
point2: number;
pointSumTotal: number;
pointSum: number;
name: string;
date: Date;
createdFullName: string;
createdAt: Date;
}
//columns
interface Columns {
[index: number]: {
name: String;
align: String;
label: String;
sortable: Boolean;
field: String;
headerStyle: String;
style: String;
};
}
export type { RequestItemsObject, Columns, DataProps };

Some files were not shown because too many files have changed in this diff Show more