Compare commits
108 commits
1101fa68d8
...
ff5767cdd1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff5767cdd1 | ||
|
|
02c7598aec | ||
|
|
93700a2b54 | ||
|
|
d67b9b2f02 | ||
|
|
18844c70bc | ||
|
|
84d3e0d777 | ||
|
|
a0b7fb3a1b | ||
|
|
21699b14c5 | ||
|
|
8c9e9abc18 | ||
|
|
ef81522561 | ||
|
|
dfc17e9623 | ||
|
|
5c12bcbab7 | ||
|
|
9a8363091d | ||
|
|
aac82ce477 | ||
|
|
9f6d972c91 | ||
|
|
92b4db45d2 | ||
|
|
56a63185a1 | ||
|
|
28395b4f80 | ||
|
|
1d38dbc6cf | ||
|
|
a4a101712c | ||
|
|
2a2bfa3180 | ||
|
|
8cf93d0016 | ||
|
|
63036b03fd | ||
|
|
4040da58f9 | ||
|
|
cf67ed3d47 | ||
|
|
8d8ad40de1 | ||
|
|
74291c0552 | ||
|
|
88f40dcb47 | ||
|
|
03b03b4bc8 | ||
|
|
285b821c16 | ||
|
|
648ed38181 | ||
|
|
ac42ee60d8 | ||
|
|
1e6be274e2 | ||
|
|
ea21ec4632 | ||
|
|
73562a59c1 | ||
|
|
7897103a1b | ||
|
|
36cef7ceb6 | ||
|
|
461dd359b1 | ||
|
|
efeb1b51eb | ||
|
|
fd5d4b7979 | ||
|
|
181ddc8f03 | ||
|
|
d7e53b764c | ||
|
|
d95d72806d | ||
|
|
550ed55de0 | ||
|
|
145784ee40 | ||
|
|
e189b9a880 | ||
|
|
4e86a90b0e | ||
|
|
08b0dcbce0 | ||
|
|
1d5f77f3a6 | ||
|
|
82f48a4b80 | ||
|
|
d909be2fc4 | ||
|
|
586fbed4e3 | ||
|
|
0efe78a37a | ||
|
|
febfbf4828 | ||
|
|
d1bb504174 | ||
|
|
af37904ce0 | ||
|
|
0a5b6af649 | ||
|
|
71b06c82bd | ||
|
|
b1295d00ff | ||
|
|
d3e5aec842 | ||
|
|
2e813f6e88 | ||
|
|
ed5a05709a | ||
|
|
a40f9f9775 | ||
|
|
5b1ccadf92 | ||
|
|
a5d73ba1ff | ||
|
|
69f368ede1 | ||
|
|
bc5097a0a8 | ||
|
|
79abde8629 | ||
|
|
bd38c008a6 | ||
|
|
7fcb4d7744 | ||
|
|
1a8be5ac34 | ||
|
|
3efe8e19f4 | ||
|
|
ace3af2a4b | ||
|
|
f22a7e09b3 | ||
|
|
0de6921636 | ||
|
|
98ab120e56 | ||
|
|
6f2471c33b | ||
|
|
2511690d54 | ||
|
|
25b62de139 | ||
|
|
68e1abb4cb | ||
|
|
bc507b7b4c | ||
|
|
6f16964859 | ||
|
|
80f68cd702 | ||
|
|
174c30875e | ||
|
|
18d5c4ff82 | ||
|
|
d5d95648b1 | ||
|
|
12ec914603 | ||
|
|
789502c1b2 | ||
|
|
c8b4339cf6 | ||
|
|
2d94d163d2 | ||
|
|
f8b56fd37e | ||
|
|
88cabff86e | ||
|
|
3c85f955c2 | ||
|
|
50bb4638c5 | ||
|
|
0a87843b3b | ||
|
|
4fb26bf54b | ||
|
|
e2f8f3332a | ||
|
|
a24303377f | ||
|
|
416424b8eb | ||
|
|
71c1f9c770 | ||
|
|
9312701096 | ||
|
|
0e685a99f7 | ||
|
|
3646956038 | ||
|
|
1e9a5abc1c | ||
|
|
af792678dd | ||
|
|
90589b3daf | ||
|
|
fb23ec5fd4 | ||
|
|
0dca8a7029 |
119 changed files with 7439 additions and 16930 deletions
|
|
@ -1,9 +0,0 @@
|
||||||
/dist
|
|
||||||
/src-capacitor
|
|
||||||
/src-cordova
|
|
||||||
/.quasar
|
|
||||||
/node_modules
|
|
||||||
.eslintrc.cjs
|
|
||||||
/src-ssr
|
|
||||||
/quasar.config.*.temporary.compiled*
|
|
||||||
/tests
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
|
|
||||||
parserOptions: {
|
|
||||||
parser: require.resolve('@typescript-eslint/parser'),
|
|
||||||
extraFileExtensions: ['.vue'],
|
|
||||||
},
|
|
||||||
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
es2021: true,
|
|
||||||
node: true,
|
|
||||||
'vue/setup-compiler-macros': true,
|
|
||||||
},
|
|
||||||
|
|
||||||
extends: [
|
|
||||||
// https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#usage
|
|
||||||
// ESLint typescript rules
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
|
|
||||||
// Uncomment any of the lines below to choose desired strictness,
|
|
||||||
// but leave only one uncommented!
|
|
||||||
// See https://eslint.vuejs.org/rules/#available-rules
|
|
||||||
'plugin:vue/vue3-essential',
|
|
||||||
|
|
||||||
// https://github.com/prettier/eslint-config-prettier#installation
|
|
||||||
// usage with Prettier, provided by 'eslint-config-prettier'.
|
|
||||||
'prettier',
|
|
||||||
],
|
|
||||||
|
|
||||||
plugins: [
|
|
||||||
// required to apply rules which need type information
|
|
||||||
'@typescript-eslint',
|
|
||||||
|
|
||||||
// https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files
|
|
||||||
// required to lint *.vue files
|
|
||||||
'vue',
|
|
||||||
],
|
|
||||||
|
|
||||||
globals: {
|
|
||||||
ga: 'readonly', // Google Analytics
|
|
||||||
cordova: 'readonly',
|
|
||||||
__statics: 'readonly',
|
|
||||||
__QUASAR_SSR__: 'readonly',
|
|
||||||
__QUASAR_SSR_SERVER__: 'readonly',
|
|
||||||
__QUASAR_SSR_CLIENT__: 'readonly',
|
|
||||||
__QUASAR_SSR_PWA__: 'readonly',
|
|
||||||
process: 'readonly',
|
|
||||||
Capacitor: 'readonly',
|
|
||||||
chrome: 'readonly',
|
|
||||||
},
|
|
||||||
|
|
||||||
// add your custom rules here
|
|
||||||
rules: {
|
|
||||||
quotes: ['warn', 'single', { avoidEscape: true }],
|
|
||||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
||||||
'@typescript-eslint/no-unused-vars': 'warn',
|
|
||||||
'prefer-promise-reject-errors': 'off',
|
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
@ -9,7 +9,7 @@ env:
|
||||||
REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
|
REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
|
||||||
CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }}/${{ vars.CONTAINER_IMAGE_NAME }}:latest
|
CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }}/${{ vars.CONTAINER_IMAGE_NAME }}:latest
|
||||||
jobs:
|
jobs:
|
||||||
gitea-release:
|
build-deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
|
@ -51,7 +51,7 @@ jobs:
|
||||||
"description": "**Details:**\n- Image: `${{ env.CONTAINER_IMAGE_NAME }}`\n- Deployed by: `${{ github.actor }}`",
|
"description": "**Details:**\n- Image: `${{ env.CONTAINER_IMAGE_NAME }}`\n- Deployed by: `${{ github.actor }}`",
|
||||||
"color": 3066993,
|
"color": 3066993,
|
||||||
"footer": {
|
"footer": {
|
||||||
"text": "Gitea Local Release Notification",
|
"text": "Local Release Notification",
|
||||||
"icon_url": "https://example.com/success-icon.png"
|
"icon_url": "https://example.com/success-icon.png"
|
||||||
},
|
},
|
||||||
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
|
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
|
||||||
|
|
@ -68,7 +68,7 @@ jobs:
|
||||||
"description": "**Details:**\n- Image: `${{ env.CONTAINER_IMAGE_NAME }}`\n- Attempted by: `${{ github.actor }}`",
|
"description": "**Details:**\n- Image: `${{ env.CONTAINER_IMAGE_NAME }}`\n- Attempted by: `${{ github.actor }}`",
|
||||||
"color": 15158332,
|
"color": 15158332,
|
||||||
"footer": {
|
"footer": {
|
||||||
"text": "Gitea Local Release Notification",
|
"text": "Local Release Notification",
|
||||||
"icon_url": "https://example.com/failure-icon.png"
|
"icon_url": "https://example.com/failure-icon.png"
|
||||||
},
|
},
|
||||||
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
|
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
|
||||||
|
|
|
||||||
12398
package-lock.json
generated
12398
package-lock.json
generated
File diff suppressed because it is too large
Load diff
56
package.json
56
package.json
|
|
@ -7,23 +7,24 @@
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint --ext .js,.ts,.vue ./",
|
|
||||||
"format": "prettier --write \"**/*.{js,ts,vue,scss,html,md,json}\" --ignore-path .gitignore",
|
"format": "prettier --write \"**/*.{js,ts,vue,scss,html,md,json}\" --ignore-path .gitignore",
|
||||||
"test": "echo \"No test specified\" && exit 0",
|
"test": "echo \"No test specified\" && exit 0",
|
||||||
"dev": "quasar dev",
|
"dev": "quasar dev",
|
||||||
"build": "quasar build",
|
"build": "quasar build",
|
||||||
|
"postinstall": "quasar prepare",
|
||||||
"changelog:generate": "git-cliff -o CHANGELOG.md"
|
"changelog:generate": "git-cliff -o CHANGELOG.md"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@peaceroad/markdown-it-figure-with-p-caption": "^0.11.0",
|
"@peaceroad/markdown-it-figure-with-p-caption": "^0.11.0",
|
||||||
"@quasar/extras": "^1.16.12",
|
"@quasar/extras": "^1.16.17",
|
||||||
"@tato30/vue-pdf": "^1.11.0",
|
"@tato30/vue-pdf": "^1.11.3",
|
||||||
"@vuepic/vue-datepicker": "^8.8.1",
|
"@vuepic/vue-datepicker": "^8.8.1",
|
||||||
"apexcharts": "^4.5.0",
|
"apexcharts": "^4.5.0",
|
||||||
"axios": "^1.7.4",
|
"axios": "^1.8.4",
|
||||||
"cropperjs": "^1.6.2",
|
"cropperjs": "^1.6.2",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
"keycloak-js": "^25.0.4",
|
"keycloak-js": "^25.0.6",
|
||||||
"markdown-it": "^14.1.0",
|
"markdown-it": "^14.1.0",
|
||||||
"markdown-it-anchor": "^9.2.0",
|
"markdown-it-anchor": "^9.2.0",
|
||||||
"markdown-it-highlightjs": "^4.2.0",
|
"markdown-it-highlightjs": "^4.2.0",
|
||||||
|
|
@ -31,49 +32,44 @@
|
||||||
"markdown-it-html5-media": "^0.7.1",
|
"markdown-it-html5-media": "^0.7.1",
|
||||||
"markdown-it-image-figures": "^2.1.1",
|
"markdown-it-image-figures": "^2.1.1",
|
||||||
"markdown-it-video": "^0.6.3",
|
"markdown-it-video": "^0.6.3",
|
||||||
"mime": "^4.0.4",
|
"mime": "^4.0.6",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"number-to-words": "^1.2.4",
|
"number-to-words": "^1.2.4",
|
||||||
"open-props": "^1.7.5",
|
"open-props": "^1.7.14",
|
||||||
"pinia": "^2.2.2",
|
"pinia": "^2.3.1",
|
||||||
"quasar": "^2.16.9",
|
"quasar": "^2.18.1",
|
||||||
"signature_pad": "^5.0.2",
|
"signature_pad": "^5.0.7",
|
||||||
"socket.io-client": "^4.7.5",
|
|
||||||
"tesseract.js": "^5.1.1",
|
"tesseract.js": "^5.1.1",
|
||||||
"thai-baht-text": "^2.0.5",
|
"thai-baht-text": "^2.0.5",
|
||||||
"udsv": "^0.6.0",
|
"udsv": "^0.6.0",
|
||||||
"uuid": "^10.0.0",
|
"uuid": "^10.0.0",
|
||||||
"vue": "^3.4.38",
|
"vue": "^3.5.13",
|
||||||
"vue-dragscroll": "^4.0.6",
|
"vue-dragscroll": "^4.0.6",
|
||||||
"vue-i18n": "^9.14.0",
|
"vue-i18n": "^11.1.2",
|
||||||
"vue-pdf": "^4.3.0",
|
"vue-pdf": "^4.3.0",
|
||||||
"vue-router": "^4.4.3",
|
"vue-router": "^4.5.0",
|
||||||
"vue3-apexcharts": "^1.7.0"
|
"vue-tsc": "^2.2.8",
|
||||||
|
"vue3-apexcharts": "^1.8.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@faker-js/faker": "^9.3.0",
|
"@faker-js/faker": "^9.6.0",
|
||||||
"@iconify/vue": "^4.1.2",
|
"@iconify/vue": "^4.3.0",
|
||||||
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
"@intlify/unplugin-vue-i18n": "^6.0.5",
|
||||||
"@playwright/test": "^1.46.1",
|
"@playwright/test": "^1.51.1",
|
||||||
"@quasar/app-vite": "2.0.0-beta.19",
|
"@quasar/app-vite": "^2.2.0",
|
||||||
"@types/markdown-it": "^14.1.2",
|
"@types/markdown-it": "^14.1.2",
|
||||||
"@types/markdown-it-highlightjs": "^3.3.4",
|
"@types/markdown-it-highlightjs": "^3.3.4",
|
||||||
"@types/node": "^20.16.1",
|
"@types/node": "^20.17.28",
|
||||||
"@types/number-to-words": "^1.2.3",
|
"@types/number-to-words": "^1.2.3",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
"autoprefixer": "^10.4.21",
|
||||||
"@typescript-eslint/parser": "^7.18.0",
|
|
||||||
"autoprefixer": "^10.4.20",
|
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"eslint": "^8.57.0",
|
"prettier": "^3.5.3",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
|
||||||
"eslint-plugin-vue": "^9.27.0",
|
|
||||||
"prettier": "^3.3.3",
|
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.5.4",
|
||||||
"vue-component-type-helpers": "^2.1.10"
|
"vue-component-type-helpers": "^2.2.8"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^24 || ^22 || ^20 || ^18",
|
"node": "^28 || ^26 || ^24 || ^22 || ^20 || ^18",
|
||||||
"npm": ">= 6.13.4",
|
"npm": ">= 6.13.4",
|
||||||
"yarn": ">= 1.21.1"
|
"yarn": ">= 1.21.1"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3642
pnpm-lock.yaml
generated
3642
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
18
postcss.config.js
Normal file
18
postcss.config.js
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import autoprefixer from 'autoprefixer';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
plugins: [
|
||||||
|
autoprefixer({
|
||||||
|
overrideBrowserslist: [
|
||||||
|
'last 4 Chrome versions',
|
||||||
|
'last 4 Firefox versions',
|
||||||
|
'last 4 Edge versions',
|
||||||
|
'last 4 Safari versions',
|
||||||
|
'last 4 Android versions',
|
||||||
|
'last 4 ChromeAndroid versions',
|
||||||
|
'last 4 FirefoxAndroid versions',
|
||||||
|
'last 4 iOS versions',
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
BIN
public/img-group.png
Normal file
BIN
public/img-group.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
|
|
@ -1,5 +1,28 @@
|
||||||
{
|
{
|
||||||
"eng": {
|
"eng": {
|
||||||
|
"visaType": [
|
||||||
|
{
|
||||||
|
"label": "Non-LA",
|
||||||
|
"value": "nla"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Non-B",
|
||||||
|
"value": "nb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "TV.60",
|
||||||
|
"value": "tv60"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Non-TR",
|
||||||
|
"value": "ntr"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "TV.30",
|
||||||
|
"value": "tv30"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
"workerStatus": [
|
"workerStatus": [
|
||||||
{
|
{
|
||||||
"label": "Normal",
|
"label": "Normal",
|
||||||
|
|
@ -154,7 +177,8 @@
|
||||||
{ "label": "VS2", "value": "VS2" },
|
{ "label": "VS2", "value": "VS2" },
|
||||||
{ "label": "WO", "value": "WO" },
|
{ "label": "WO", "value": "WO" },
|
||||||
{ "label": "WP390", "value": "WP390" },
|
{ "label": "WP390", "value": "WP390" },
|
||||||
{ "label": "WP44", "value": "WP44" }
|
{ "label": "WP44", "value": "WP44" },
|
||||||
|
{ "label": "CUST", "value": "CUST" }
|
||||||
],
|
],
|
||||||
|
|
||||||
"prefix": [
|
"prefix": [
|
||||||
|
|
@ -183,29 +207,44 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
"training": [
|
"border": [
|
||||||
{
|
{
|
||||||
"label": "Myanmar Labor Training Center - Mae Sot, Tak Province",
|
"label": "Mae Sot, Tak Province",
|
||||||
"value": "trainingTak"
|
"value": "trainingTak"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Myanmar Labor Training Center - Kawthoung, Ranong Province",
|
"label": "Koh Song, Ranong province",
|
||||||
"value": "trainingRanong"
|
"value": "trainingRanong"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Laos Labor Training Center - Nong Khai, Nong Khai Province",
|
"label": "Nong Khai, Nong Khai Province",
|
||||||
"value": "trainingNongKhai"
|
"value": "trainingNongKhai"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Cambodian Labor Training Center - Aranyaprathet, Sa Kaeo Province",
|
"label": "Aranyaprathet, Sa Kaeo Province",
|
||||||
"value": "trainingSaKaeo"
|
"value": "trainingSaKaeo"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Cambodian Labor Training Center - Ban Laem, Chanthaburi Province",
|
"label": "Ban Laem, Chanthaburi Province",
|
||||||
"value": "trainingChanthaburi"
|
"value": "trainingChanthaburi"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"training": [
|
||||||
|
{
|
||||||
|
"label": "The first center accepts work. and end of employment Tak Province",
|
||||||
|
"value": "trainingTak"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "The first center accepts work. and end of employment Nong Khai Province",
|
||||||
|
"value": "trainingNongKhai"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "The first center accepts work. and end of employment Sa Kaeo Province",
|
||||||
|
"value": "trainingSaKaeo"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
"nationality": [
|
"nationality": [
|
||||||
{
|
{
|
||||||
"label": "Thai",
|
"label": "Thai",
|
||||||
|
|
@ -1050,6 +1089,29 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"tha": {
|
"tha": {
|
||||||
|
"visaType": [
|
||||||
|
{
|
||||||
|
"label": "Non-LA",
|
||||||
|
"value": "nla"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Non-B",
|
||||||
|
"value": "nb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "ผผ.60",
|
||||||
|
"value": "tv60"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Non-TR",
|
||||||
|
"value": "ntr"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "ผผ.30",
|
||||||
|
"value": "tv30"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
"workerStatus": [
|
"workerStatus": [
|
||||||
{
|
{
|
||||||
"label": "ปกติ",
|
"label": "ปกติ",
|
||||||
|
|
@ -1204,7 +1266,8 @@
|
||||||
{ "label": "VS2", "value": "VS2" },
|
{ "label": "VS2", "value": "VS2" },
|
||||||
{ "label": "WO", "value": "WO" },
|
{ "label": "WO", "value": "WO" },
|
||||||
{ "label": "WP390", "value": "WP390" },
|
{ "label": "WP390", "value": "WP390" },
|
||||||
{ "label": "WP44", "value": "WP44" }
|
{ "label": "WP44", "value": "WP44" },
|
||||||
|
{ "label": "CUST", "value": "CUST" }
|
||||||
],
|
],
|
||||||
|
|
||||||
"prefix": [
|
"prefix": [
|
||||||
|
|
@ -1233,29 +1296,44 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
"training": [
|
"border": [
|
||||||
{
|
{
|
||||||
"label": "สถานที่อบรมแรงงานเมียนมา-แม่สอด จ.ตาก",
|
"label": "แม่สอด จ.ตาก",
|
||||||
"value": "trainingTak"
|
"value": "trainingTak"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "สถานที่อบรมแรงงานเมียนมา-เกาะสอง จ.ระนอง",
|
"label": "เกาะสอง จ.ระนอง",
|
||||||
"value": "trainingRanong"
|
"value": "trainingRanong"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "สถานที่อบรมแรงงานลาว-หนองคาย จ.หนองคาย",
|
"label": "หนองคาย จ.หนองคาย",
|
||||||
"value": "trainingNongKhai"
|
"value": "trainingNongKhai"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "สถานที่อบรมแรงงานกัมพูชา-อรัญประเทศ จ.สระแก้ว",
|
"label": "อรัญประเทศ จ.สระแก้ว",
|
||||||
"value": "trainingSaKaeo"
|
"value": "trainingSaKaeo"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "สถานที่อบรมแรงงานกัมพูชา-บ้านแหลม จ.จันทบุรี",
|
"label": "บ้านแหลม จ.จันทบุรี",
|
||||||
"value": "trainingChanthaburi"
|
"value": "trainingChanthaburi"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"training": [
|
||||||
|
{
|
||||||
|
"label": "ศูนย์แรกรับเข้าทำงาน และสิ้นสุดการจ้าง จังหวัดตาก",
|
||||||
|
"value": "trainingTak"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "ศูนย์แรกรับเข้าทำงาน และสิ้นสุดการจ้าง จังหวัดหนองคาย",
|
||||||
|
"value": "trainingNongKhai"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "ศูนย์แรกรับเข้าทำงาน และสิ้นสุดการจ้าง จังหวัดสระแก้ว",
|
||||||
|
"value": "trainingSaKaeo"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
"nationality": [
|
"nationality": [
|
||||||
{
|
{
|
||||||
"label": "ไทย",
|
"label": "ไทย",
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,22 @@
|
||||||
/* eslint-env node */
|
|
||||||
|
|
||||||
// Configuration for your app
|
// Configuration for your app
|
||||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
|
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
|
||||||
|
|
||||||
import { configure } from 'quasar/wrappers';
|
import { defineConfig } from '#q-app/wrappers';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
export default configure((ctx) => {
|
export default defineConfig((ctx) => {
|
||||||
return {
|
return {
|
||||||
eslint: {
|
|
||||||
fix: true,
|
|
||||||
warnings: true,
|
|
||||||
errors: true,
|
|
||||||
},
|
|
||||||
boot: ['i18n', 'axios', 'components'],
|
boot: ['i18n', 'axios', 'components'],
|
||||||
css: ['app.scss'],
|
css: ['app.scss'],
|
||||||
extras: ['mdi-v7'],
|
extras: ['mdi-v7'],
|
||||||
build: {
|
build: {
|
||||||
target: {
|
target: {
|
||||||
browser: ['es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1'],
|
browser: ['es2022', 'firefox115', 'chrome115', 'safari14'],
|
||||||
node: 'node20',
|
node: 'node20',
|
||||||
},
|
},
|
||||||
|
typescript: {
|
||||||
|
vueShim: true,
|
||||||
|
},
|
||||||
vueRouterMode: 'history',
|
vueRouterMode: 'history',
|
||||||
vitePlugins: [
|
vitePlugins: [
|
||||||
[
|
[
|
||||||
|
|
@ -35,7 +31,7 @@ export default configure((ctx) => {
|
||||||
devServer: {
|
devServer: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
open: false,
|
open: false,
|
||||||
port: 5173,
|
port: 5174,
|
||||||
},
|
},
|
||||||
framework: {
|
framework: {
|
||||||
config: {},
|
config: {},
|
||||||
|
|
|
||||||
167
reports.json
167
reports.json
|
|
@ -1,167 +0,0 @@
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"configFile": "/Users/linping/Desktop/Chamomind&FrappeT/JWS_TestScript/playwright.config.ts",
|
|
||||||
"rootDir": "/Users/linping/Desktop/Chamomind&FrappeT/JWS_TestScript/tests",
|
|
||||||
"forbidOnly": false,
|
|
||||||
"fullyParallel": true,
|
|
||||||
"globalSetup": null,
|
|
||||||
"globalTeardown": null,
|
|
||||||
"globalTimeout": 0,
|
|
||||||
"grep": {},
|
|
||||||
"grepInvert": null,
|
|
||||||
"maxFailures": 0,
|
|
||||||
"metadata": {
|
|
||||||
"actualWorkers": 1
|
|
||||||
},
|
|
||||||
"preserveOutput": "always",
|
|
||||||
"reporter": [
|
|
||||||
[
|
|
||||||
"json",
|
|
||||||
{
|
|
||||||
"outputFile": "reports.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
],
|
|
||||||
"reportSlowTests": {
|
|
||||||
"max": 5,
|
|
||||||
"threshold": 15000
|
|
||||||
},
|
|
||||||
"quiet": false,
|
|
||||||
"projects": [
|
|
||||||
{
|
|
||||||
"outputDir": "/Users/linping/Desktop/Chamomind&FrappeT/JWS_TestScript/test-results",
|
|
||||||
"repeatEach": 1,
|
|
||||||
"retries": 0,
|
|
||||||
"metadata": {},
|
|
||||||
"id": "chromium",
|
|
||||||
"name": "chromium",
|
|
||||||
"testDir": "/Users/linping/Desktop/Chamomind&FrappeT/JWS_TestScript/tests",
|
|
||||||
"testIgnore": [],
|
|
||||||
"testMatch": [
|
|
||||||
"**/*.@(spec|test).?(c|m)[jt]s?(x)"
|
|
||||||
],
|
|
||||||
"timeout": 30000
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"shard": null,
|
|
||||||
"updateSnapshots": "missing",
|
|
||||||
"version": "1.44.1",
|
|
||||||
"workers": 1,
|
|
||||||
"webServer": null
|
|
||||||
},
|
|
||||||
"suites": [
|
|
||||||
{
|
|
||||||
"title": "01-Admin-BranchManagement/JWS_BM_001_CreateHeadquarters.spec.ts",
|
|
||||||
"file": "01-Admin-BranchManagement/JWS_BM_001_CreateHeadquarters.spec.ts",
|
|
||||||
"column": 0,
|
|
||||||
"line": 0,
|
|
||||||
"specs": [
|
|
||||||
{
|
|
||||||
"title": "Login",
|
|
||||||
"ok": true,
|
|
||||||
"tags": [],
|
|
||||||
"tests": [
|
|
||||||
{
|
|
||||||
"timeout": 30000,
|
|
||||||
"annotations": [],
|
|
||||||
"expectedStatus": "passed",
|
|
||||||
"projectId": "chromium",
|
|
||||||
"projectName": "chromium",
|
|
||||||
"results": [
|
|
||||||
{
|
|
||||||
"workerIndex": 4,
|
|
||||||
"status": "passed",
|
|
||||||
"duration": 3024,
|
|
||||||
"errors": [],
|
|
||||||
"stdout": [],
|
|
||||||
"stderr": [],
|
|
||||||
"retry": 0,
|
|
||||||
"startTime": "2024-07-30T02:59:00.817Z",
|
|
||||||
"attachments": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"status": "expected"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": "8c5091bd59605f227965-8109f0f4a59e27330a76",
|
|
||||||
"file": "01-Admin-BranchManagement/JWS_BM_001_CreateHeadquarters.spec.ts",
|
|
||||||
"line": 16,
|
|
||||||
"column": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Create Branch Managenment",
|
|
||||||
"ok": true,
|
|
||||||
"tags": [],
|
|
||||||
"tests": [
|
|
||||||
{
|
|
||||||
"timeout": 30000,
|
|
||||||
"annotations": [],
|
|
||||||
"expectedStatus": "passed",
|
|
||||||
"projectId": "chromium",
|
|
||||||
"projectName": "chromium",
|
|
||||||
"results": [
|
|
||||||
{
|
|
||||||
"workerIndex": 4,
|
|
||||||
"status": "passed",
|
|
||||||
"duration": 5091,
|
|
||||||
"errors": [],
|
|
||||||
"stdout": [],
|
|
||||||
"stderr": [],
|
|
||||||
"retry": 0,
|
|
||||||
"startTime": "2024-07-30T02:59:05.659Z",
|
|
||||||
"attachments": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"status": "expected"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": "8c5091bd59605f227965-5a0d70f27623401a3479",
|
|
||||||
"file": "01-Admin-BranchManagement/JWS_BM_001_CreateHeadquarters.spec.ts",
|
|
||||||
"line": 27,
|
|
||||||
"column": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Create Branch Managenment Second",
|
|
||||||
"ok": true,
|
|
||||||
"tags": [],
|
|
||||||
"tests": [
|
|
||||||
{
|
|
||||||
"timeout": 30000,
|
|
||||||
"annotations": [],
|
|
||||||
"expectedStatus": "passed",
|
|
||||||
"projectId": "chromium",
|
|
||||||
"projectName": "chromium",
|
|
||||||
"results": [
|
|
||||||
{
|
|
||||||
"workerIndex": 4,
|
|
||||||
"status": "passed",
|
|
||||||
"duration": 5029,
|
|
||||||
"errors": [],
|
|
||||||
"stdout": [],
|
|
||||||
"stderr": [],
|
|
||||||
"retry": 0,
|
|
||||||
"startTime": "2024-07-30T02:59:10.755Z",
|
|
||||||
"attachments": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"status": "expected"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": "8c5091bd59605f227965-d619bd2184e7f07d4970",
|
|
||||||
"file": "01-Admin-BranchManagement/JWS_BM_001_CreateHeadquarters.spec.ts",
|
|
||||||
"line": 52,
|
|
||||||
"column": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"errors": [],
|
|
||||||
"stats": {
|
|
||||||
"startTime": "2024-07-30T02:59:00.334Z",
|
|
||||||
"duration": 15556.794999999925,
|
|
||||||
"expected": 3,
|
|
||||||
"skipped": 0,
|
|
||||||
"unexpected": 0,
|
|
||||||
"flaky": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import axios, { AxiosInstance } from 'axios';
|
import axios, { AxiosInstance } from 'axios';
|
||||||
import { boot } from 'quasar/wrappers';
|
import { defineBoot } from '#q-app/wrappers';
|
||||||
import { getToken } from 'src/services/keycloak';
|
import { getToken } from 'src/services/keycloak';
|
||||||
import { dialog } from 'stores/utils';
|
import { dialog } from 'stores/utils';
|
||||||
import useLoader from 'stores/loader';
|
import useLoader from 'stores/loader';
|
||||||
import useFlowStore from 'src/stores/flow';
|
import useFlowStore from 'src/stores/flow';
|
||||||
|
|
||||||
declare module '@vue/runtime-core' {
|
declare module 'vue' {
|
||||||
interface ComponentCustomProperties {
|
interface ComponentCustomProperties {
|
||||||
$axios: AxiosInstance;
|
$axios: AxiosInstance;
|
||||||
$api: AxiosInstance;
|
$api: AxiosInstance;
|
||||||
|
|
@ -24,10 +24,10 @@ function parseError(
|
||||||
status: number,
|
status: number,
|
||||||
body?: { status: number; message: string; code: string },
|
body?: { status: number; message: string; code: string },
|
||||||
) {
|
) {
|
||||||
if (status === 422) return 'invalideData';
|
if (status === 422) return 'invalidData';
|
||||||
if (body && body.code) return body.code;
|
if (body && body.code) return body.code;
|
||||||
|
|
||||||
return 'errorOccure';
|
return 'errorOccurred';
|
||||||
}
|
}
|
||||||
|
|
||||||
api.interceptors.request.use(async (config) => {
|
api.interceptors.request.use(async (config) => {
|
||||||
|
|
@ -64,7 +64,7 @@ api.interceptors.response.use(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export default boot(({ app }) => {
|
export default defineBoot(({ app }) => {
|
||||||
// for use inside Vue files (Options API) through this.$axios and this.$api
|
// for use inside Vue files (Options API) through this.$axios and this.$api
|
||||||
|
|
||||||
app.config.globalProperties.$axios = axios;
|
app.config.globalProperties.$axios = axios;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { boot } from 'quasar/wrappers';
|
import { defineBoot } from '#q-app/wrappers';
|
||||||
import VueDatePicker from '@vuepic/vue-datepicker';
|
import VueDatePicker from '@vuepic/vue-datepicker';
|
||||||
import '@vuepic/vue-datepicker/dist/main.css';
|
import '@vuepic/vue-datepicker/dist/main.css';
|
||||||
import GlobalDialog from 'components/GlobalDialog.vue';
|
import GlobalDialog from 'components/GlobalDialog.vue';
|
||||||
|
|
@ -6,7 +6,7 @@ import GlobalLoading from 'components/GlobalLoading.vue';
|
||||||
import VueDragscroll from 'vue-dragscroll';
|
import VueDragscroll from 'vue-dragscroll';
|
||||||
import VueApexCharts from 'vue3-apexcharts';
|
import VueApexCharts from 'vue3-apexcharts';
|
||||||
|
|
||||||
export default boot(({ app }) => {
|
export default defineBoot(({ app }) => {
|
||||||
app.component('global-dialog', GlobalDialog);
|
app.component('global-dialog', GlobalDialog);
|
||||||
app.component('global-loading', GlobalLoading);
|
app.component('global-loading', GlobalLoading);
|
||||||
app.component('VueDatePicker', VueDatePicker);
|
app.component('VueDatePicker', VueDatePicker);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import { boot } from 'quasar/wrappers';
|
import { defineBoot } from '#q-app/wrappers';
|
||||||
import { createI18n } from 'vue-i18n';
|
import { createI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import messages from 'src/i18n';
|
import messages from 'src/i18n';
|
||||||
|
import { Lang } from 'src/utils/ui';
|
||||||
|
|
||||||
export type MessageLanguages = keyof typeof messages;
|
export type MessageLanguages = keyof typeof messages;
|
||||||
// Type-define 'eng' as the master schema for the resource
|
// Type-define 'eng' as the master schema for the resource
|
||||||
|
|
@ -21,16 +22,17 @@ declare module 'vue-i18n' {
|
||||||
}
|
}
|
||||||
/* eslint-enable @typescript-eslint/no-empty-interface */
|
/* eslint-enable @typescript-eslint/no-empty-interface */
|
||||||
|
|
||||||
export const i18n = createI18n({
|
export const i18n = createI18n<
|
||||||
|
{ message: MessageSchema },
|
||||||
|
MessageLanguages,
|
||||||
|
false
|
||||||
|
>({
|
||||||
locale: 'tha',
|
locale: 'tha',
|
||||||
legacy: false,
|
legacy: false,
|
||||||
messages: {
|
messages,
|
||||||
'en-US': {},
|
|
||||||
...messages,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default boot(({ app }) => {
|
export default defineBoot(({ app }) => {
|
||||||
// Set i18n instance on app
|
// Set i18n instance on app
|
||||||
app.use(i18n);
|
app.use(i18n);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ defineProps<{
|
||||||
outlined?: boolean;
|
outlined?: boolean;
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
view?: boolean;
|
view?: boolean;
|
||||||
|
single?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
defineEmits<{
|
defineEmits<{
|
||||||
|
|
@ -121,7 +122,7 @@ watch(
|
||||||
/>
|
/>
|
||||||
{{ $t(`${title}`) }}
|
{{ $t(`${title}`) }}
|
||||||
<AddButton
|
<AddButton
|
||||||
v-if="!readonly"
|
v-if="!readonly && !single"
|
||||||
id="btn-add-bank"
|
id="btn-add-bank"
|
||||||
icon-only
|
icon-only
|
||||||
class="q-ml-sm"
|
class="q-ml-sm"
|
||||||
|
|
@ -141,7 +142,10 @@ watch(
|
||||||
style="padding-block: 0.01px"
|
style="padding-block: 0.01px"
|
||||||
spaced="lg"
|
spaced="lg"
|
||||||
/>
|
/>
|
||||||
<span class="col-12 app-text-muted-2 flex justify-between items-center">
|
<span
|
||||||
|
v-if="!single"
|
||||||
|
class="col-12 app-text-muted-2 flex justify-between items-center"
|
||||||
|
>
|
||||||
{{ `${$t('branch.form.bankAccountNo')} ${i + 1}` }}
|
{{ `${$t('branch.form.bankAccountNo')} ${i + 1}` }}
|
||||||
<div class="row items-center">
|
<div class="row items-center">
|
||||||
<div style="height: 30.8px" />
|
<div style="height: 30.8px" />
|
||||||
|
|
@ -172,7 +176,8 @@ watch(
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="bordered q-mr-sm rounded col text-center overflow-hidden"
|
v-if="!single"
|
||||||
|
class="bordered q-mr-sm rounded col-4 text-center overflow-hidden"
|
||||||
:class="{ 'pointer-none': readonly, 'q-my-sm': $q.screen.lt.md }"
|
:class="{ 'pointer-none': readonly, 'q-my-sm': $q.screen.lt.md }"
|
||||||
>
|
>
|
||||||
<ImageHover
|
<ImageHover
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import useUserStore from 'stores/user';
|
import useUserStore from 'stores/user';
|
||||||
import useOptionStore from 'stores/options';
|
import useOptionStore from 'stores/options';
|
||||||
import { UserAttachmentDelete } from 'stores/user/types';
|
import { UserAttachmentDelete, AgencyStatus } from 'stores/user/types';
|
||||||
import { dialog, selectFilterOptionRefMod } from 'stores/utils';
|
import { dialog } from 'stores/utils';
|
||||||
import { onMounted, ref, watch } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { Icon } from '@iconify/vue';
|
import { Icon } from '@iconify/vue';
|
||||||
import { QSelect } from 'quasar';
|
|
||||||
import DatePicker from '../shared/DatePicker.vue';
|
import DatePicker from '../shared/DatePicker.vue';
|
||||||
|
import SelectInput from 'src/components/shared/SelectInput.vue';
|
||||||
|
|
||||||
import SelectOffice from 'components/shared/select-muliple/SelectOffice.vue';
|
import SelectOffice from 'components/shared/select-muliple/SelectOffice.vue';
|
||||||
|
|
||||||
|
|
@ -29,15 +29,16 @@ const discountCondition = defineModel<string | null | undefined>(
|
||||||
const sourceNationality = defineModel<string | null | undefined>(
|
const sourceNationality = defineModel<string | null | undefined>(
|
||||||
'sourceNationality',
|
'sourceNationality',
|
||||||
);
|
);
|
||||||
const importNationality = defineModel<string | null | undefined>(
|
const importNationality = defineModel<string[] | null | undefined>(
|
||||||
'importNationality',
|
'importNationality',
|
||||||
);
|
);
|
||||||
const trainingPlace = defineModel<string | null | undefined>('trainingPlace');
|
const trainingPlace = defineModel<string | null | undefined>('trainingPlace');
|
||||||
const checkpoint = defineModel<string | null | undefined>('checkPoint');
|
const checkpoint = defineModel<string | null | undefined>('checkpoint');
|
||||||
const checkpointEN = defineModel<string | null | undefined>('checkPointEn');
|
const userFile = defineModel<File[]>('userFile');
|
||||||
const agencyFile = defineModel<File[]>('agencyFile');
|
const userFileList =
|
||||||
const agencyFileList =
|
defineModel<{ name: string; url: string }[]>('userFileList');
|
||||||
defineModel<{ name: string; url: string }[]>('agencyFileList');
|
const remark = defineModel<string | null | undefined>('remark');
|
||||||
|
const agencyStatus = defineModel<string | null | undefined>('agencyStatus');
|
||||||
|
|
||||||
const attachmentRef = ref();
|
const attachmentRef = ref();
|
||||||
|
|
||||||
|
|
@ -69,66 +70,12 @@ function deleteFile(name: string) {
|
||||||
userStore.deleteAttachment(userId.value, payload);
|
userStore.deleteAttachment(userId.value, payload);
|
||||||
const result = await userStore.fetchAttachment(userId.value);
|
const result = await userStore.fetchAttachment(userId.value);
|
||||||
if (result) {
|
if (result) {
|
||||||
agencyFileList.value = result;
|
userFileList.value = result;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cancel: () => {},
|
cancel: () => {},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const nationalityOptions = ref<Record<string, unknown>[]>([]);
|
|
||||||
let nationalityFilter: (
|
|
||||||
value: string,
|
|
||||||
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void,
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
const trainingPlaceOptions = ref<Record<string, unknown>[]>([]);
|
|
||||||
let trainingPlaceFilter: (
|
|
||||||
value: string,
|
|
||||||
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void,
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
const responsibleAreaOptions = ref<Record<string, unknown>[]>([]);
|
|
||||||
let responsibleAreaFilter: (
|
|
||||||
value: string,
|
|
||||||
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void,
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (optionStore.globalOption?.nationality) {
|
|
||||||
nationalityFilter = selectFilterOptionRefMod(
|
|
||||||
ref(optionStore.globalOption.nationality),
|
|
||||||
nationalityOptions,
|
|
||||||
'label',
|
|
||||||
);
|
|
||||||
trainingPlaceFilter = selectFilterOptionRefMod(
|
|
||||||
ref(optionStore.globalOption.training),
|
|
||||||
trainingPlaceOptions,
|
|
||||||
'label',
|
|
||||||
);
|
|
||||||
responsibleAreaFilter = selectFilterOptionRefMod(
|
|
||||||
ref(optionStore.globalOption.area),
|
|
||||||
responsibleAreaOptions,
|
|
||||||
'label',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => optionStore.globalOption,
|
|
||||||
() => {
|
|
||||||
nationalityFilter = selectFilterOptionRefMod(
|
|
||||||
ref(optionStore.globalOption.nationality),
|
|
||||||
nationalityOptions,
|
|
||||||
'label',
|
|
||||||
);
|
|
||||||
trainingPlaceFilter = selectFilterOptionRefMod(
|
|
||||||
ref(optionStore.globalOption.training),
|
|
||||||
trainingPlaceOptions,
|
|
||||||
'label',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="row col-12">
|
<div class="row col-12">
|
||||||
|
|
@ -186,11 +133,12 @@ watch(
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SelectOffice
|
<SelectOffice
|
||||||
|
v-if="userType === 'MESSENGER'"
|
||||||
for="input-responsible-area"
|
for="input-responsible-area"
|
||||||
v-model:value="responsibleArea"
|
v-model:value="responsibleArea"
|
||||||
v-if="userType === 'MESSENGER'"
|
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:label="$t('personnel.form.responsibleArea')"
|
:label="$t('personnel.form.responsibleArea')"
|
||||||
|
class="col"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
@ -218,138 +166,103 @@ watch(
|
||||||
class="row col-12 q-col-gutter-sm"
|
class="row col-12 q-col-gutter-sm"
|
||||||
style="margin-left: 0px; padding-left: 0px"
|
style="margin-left: 0px; padding-left: 0px"
|
||||||
>
|
>
|
||||||
<q-select
|
<SelectInput
|
||||||
outlined
|
:model-value="readonly ? sourceNationality || '-' : sourceNationality"
|
||||||
clearable
|
|
||||||
use-input
|
|
||||||
fill-input
|
|
||||||
emit-value
|
|
||||||
map-options
|
|
||||||
hide-selected
|
|
||||||
hide-bottom-space
|
|
||||||
input-debounce="0"
|
|
||||||
option-value="value"
|
|
||||||
option-label="label"
|
|
||||||
class="col-md-3 col-6"
|
|
||||||
id="input-source-nationality"
|
id="input-source-nationality"
|
||||||
for="input-source-nationality"
|
for="input-source-nationality"
|
||||||
:dense="dense"
|
:option="optionStore.globalOption.nationality"
|
||||||
:readonly="readonly"
|
class="col-md-3 col-6"
|
||||||
:hide-dropdown-icon="readonly"
|
:readonly
|
||||||
|
clearable
|
||||||
:label="$t('personnel.form.sourceNationality')"
|
:label="$t('personnel.form.sourceNationality')"
|
||||||
:options="nationalityOptions"
|
|
||||||
@filter="nationalityFilter"
|
|
||||||
:model-value="readonly ? sourceNationality || '-' : sourceNationality"
|
|
||||||
@update:model-value="
|
@update:model-value="
|
||||||
(v) => (typeof v === 'string' ? (sourceNationality = v) : '')
|
(v) => (typeof v === 'string' ? (sourceNationality = v) : '')
|
||||||
"
|
"
|
||||||
@clear="sourceNationality = ''"
|
/>
|
||||||
>
|
|
||||||
<template v-slot:no-option>
|
<SelectInput
|
||||||
<q-item>
|
v-model="importNationality"
|
||||||
<q-item-section class="text-grey">
|
|
||||||
{{ $t('general.noData') }}
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</template>
|
|
||||||
</q-select>
|
|
||||||
<q-select
|
|
||||||
outlined
|
|
||||||
clearable
|
|
||||||
use-input
|
|
||||||
fill-input
|
|
||||||
emit-value
|
|
||||||
map-options
|
|
||||||
hide-selected
|
|
||||||
hide-bottom-space
|
|
||||||
input-debounce="0"
|
|
||||||
option-value="value"
|
|
||||||
option-label="label"
|
|
||||||
class="col-md-3 col-6"
|
|
||||||
id="input-import-nationality"
|
id="input-import-nationality"
|
||||||
for="input-import-nationality"
|
for="input-import-nationality"
|
||||||
:dense="dense"
|
:option="optionStore.globalOption.nationality"
|
||||||
:readonly="readonly"
|
class="col-md-3 col-6"
|
||||||
:hide-dropdown-icon="readonly"
|
:readonly
|
||||||
:label="$t('personnel.form.importNationality')"
|
multiple
|
||||||
:options="nationalityOptions"
|
:hideSelected="false"
|
||||||
@filter="nationalityFilter"
|
|
||||||
:model-value="readonly ? importNationality || '-' : importNationality"
|
|
||||||
@update:model-value="
|
|
||||||
(v) => (typeof v === 'string' ? (importNationality = v) : '')
|
|
||||||
"
|
|
||||||
@clear="importNationality = ''"
|
|
||||||
>
|
|
||||||
<template v-slot:no-option>
|
|
||||||
<q-item>
|
|
||||||
<q-item-section class="text-grey">
|
|
||||||
{{ $t('general.noData') }}
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</template>
|
|
||||||
</q-select>
|
|
||||||
<q-select
|
|
||||||
outlined
|
|
||||||
clearable
|
clearable
|
||||||
use-input
|
fillInput
|
||||||
fill-input
|
:label="$t('personnel.form.importNationality')"
|
||||||
emit-value
|
/>
|
||||||
map-options
|
|
||||||
hide-selected
|
<SelectInput
|
||||||
hide-bottom-space
|
|
||||||
input-debounce="0"
|
|
||||||
option-label="label"
|
|
||||||
option-value="label"
|
|
||||||
class="col-md-6 col-12"
|
|
||||||
id="select-trainig-place"
|
|
||||||
for="select-trainig-place"
|
|
||||||
:dense="dense"
|
|
||||||
:readonly="readonly"
|
|
||||||
:hide-dropdown-icon="readonly"
|
|
||||||
:label="$t('personnel.form.trainingPlace')"
|
|
||||||
:options="trainingPlaceOptions"
|
|
||||||
@filter="trainingPlaceFilter"
|
|
||||||
:model-value="readonly ? trainingPlace || '-' : trainingPlace"
|
|
||||||
@update:model-value="
|
|
||||||
(v) => (typeof v === 'string' ? (trainingPlace = v) : '')
|
|
||||||
"
|
|
||||||
@clear="trainingPlace = ''"
|
|
||||||
>
|
|
||||||
<template v-slot:no-option>
|
|
||||||
<q-item>
|
|
||||||
<q-item-section class="text-grey">
|
|
||||||
{{ $t('general.noData') }}
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</template>
|
|
||||||
</q-select>
|
|
||||||
<q-input
|
|
||||||
for="input-checkpoint"
|
|
||||||
:dense="dense"
|
|
||||||
outlined
|
|
||||||
:readonly="readonly"
|
|
||||||
:label="$t('personnel.form.checkpoint')"
|
|
||||||
class="col-6"
|
|
||||||
:model-value="readonly ? checkpoint || '-' : checkpoint"
|
:model-value="readonly ? checkpoint || '-' : checkpoint"
|
||||||
|
id="select-checkpoint"
|
||||||
|
for="select-checkpoint"
|
||||||
|
:option="optionStore.globalOption.border"
|
||||||
|
class="col-md-6 col-12"
|
||||||
|
:readonly
|
||||||
|
:label="$t('personnel.form.checkpoint')"
|
||||||
|
clearable
|
||||||
@update:model-value="
|
@update:model-value="
|
||||||
(v) => (typeof v === 'string' ? (checkpoint = v) : '')
|
(v) => (typeof v === 'string' ? (checkpoint = v) : '')
|
||||||
"
|
"
|
||||||
@clear="checkpoint = ''"
|
/>
|
||||||
|
|
||||||
|
<SelectInput
|
||||||
|
:model-value="readonly ? trainingPlace || '-' : trainingPlace"
|
||||||
|
id="select-trainig-place"
|
||||||
|
for="select-trainig-place"
|
||||||
|
:option="optionStore.globalOption.training"
|
||||||
|
class="col-md-8 col-12"
|
||||||
|
:readonly
|
||||||
|
:label="$t('personnel.form.trainingPlace')"
|
||||||
|
clearable
|
||||||
|
@update:model-value="
|
||||||
|
(v) => (typeof v === 'string' ? (trainingPlace = v) : '')
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SelectInput
|
||||||
|
:model-value="readonly ? agencyStatus || '-' : agencyStatus"
|
||||||
|
id="select-checkpoint-en"
|
||||||
|
for="select-checkpoint-en"
|
||||||
|
:option="[
|
||||||
|
{ label: $t('personnel.form.normal'), value: AgencyStatus.Normal },
|
||||||
|
{
|
||||||
|
label: $t('personnel.form.canceled'),
|
||||||
|
value: AgencyStatus.Canceled,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('personnel.form.blacklist'),
|
||||||
|
value: AgencyStatus.Blacklist,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
class="col-md-4 col-12"
|
||||||
|
:readonly
|
||||||
|
:label="$t('personnel.form.agencyStatus')"
|
||||||
|
clearable
|
||||||
|
@update:model-value="
|
||||||
|
(v) => (typeof v === 'string' ? (agencyStatus = v) : '')
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
<q-input
|
<q-input
|
||||||
for="input-checkpoint-en"
|
for="input-discount-condition"
|
||||||
:dense="dense"
|
:dense="dense"
|
||||||
outlined
|
outlined
|
||||||
:readonly="readonly"
|
:readonly
|
||||||
:label="$t('personnel.form.checkpointEN')"
|
:label="$t('general.remark')"
|
||||||
class="col-6"
|
class="col-12"
|
||||||
:model-value="readonly ? checkpointEN || '-' : checkpointEN"
|
type="textarea"
|
||||||
|
:model-value="readonly ? remark || '-' : remark"
|
||||||
@update:model-value="
|
@update:model-value="
|
||||||
(v) => (typeof v === 'string' ? (checkpointEN = v) : '')
|
(v) => (typeof v === 'string' ? (remark = v) : '')
|
||||||
"
|
"
|
||||||
@clear="checkpointEN = ''"
|
@clear="remark = ''"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<q-file
|
<q-file
|
||||||
|
v-if="userType"
|
||||||
ref="attachmentRef"
|
ref="attachmentRef"
|
||||||
for="input-attachment"
|
for="input-attachment"
|
||||||
:dense="dense"
|
:dense="dense"
|
||||||
|
|
@ -358,8 +271,8 @@ watch(
|
||||||
multiple
|
multiple
|
||||||
append
|
append
|
||||||
:label="$t('personnel.form.attachment')"
|
:label="$t('personnel.form.attachment')"
|
||||||
class="col-12"
|
class="col"
|
||||||
v-model="agencyFile"
|
v-model="userFile"
|
||||||
>
|
>
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<Icon
|
<Icon
|
||||||
|
|
@ -386,12 +299,12 @@ watch(
|
||||||
</template>
|
</template>
|
||||||
</q-file>
|
</q-file>
|
||||||
|
|
||||||
<div v-if="agencyFileList && agencyFileList?.length > 0" class="col-12">
|
<div v-if="userFileList && userFileList?.length > 0" class="col-12">
|
||||||
<q-list bordered separator class="rounded" style="padding: 0">
|
<q-list bordered separator class="rounded" style="padding: 0">
|
||||||
<q-item
|
<q-item
|
||||||
id="attachment-file"
|
id="attachment-file"
|
||||||
for="attachment-file"
|
for="attachment-file"
|
||||||
v-for="item in agencyFileList"
|
v-for="item in userFileList"
|
||||||
clickable
|
clickable
|
||||||
:key="item.url"
|
:key="item.url"
|
||||||
class="items-center row"
|
class="items-center row"
|
||||||
|
|
@ -421,5 +334,4 @@ watch(
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { QSelect } from 'quasar';
|
|
||||||
import useOptionStore from 'stores/options';
|
import useOptionStore from 'stores/options';
|
||||||
import { selectFilterOptionRefMod } from 'stores/utils';
|
|
||||||
import { calculateAge, disabledAfterToday } from 'src/utils/datetime';
|
import { calculateAge, disabledAfterToday } from 'src/utils/datetime';
|
||||||
import { ref, onMounted, watch } from 'vue';
|
import { watch } from 'vue';
|
||||||
import { capitalize } from 'vue';
|
import SelectInput from '../shared/SelectInput.vue';
|
||||||
import DatePicker from '../shared/DatePicker.vue';
|
import DatePicker from '../shared/DatePicker.vue';
|
||||||
|
|
||||||
const optionStore = useOptionStore();
|
const optionStore = useOptionStore();
|
||||||
|
|
@ -23,6 +21,8 @@ const midNameEN = defineModel<string | null>('midNameEn');
|
||||||
const citizenId = defineModel<string>('citizenId');
|
const citizenId = defineModel<string>('citizenId');
|
||||||
const citizenIssue = defineModel<Date | null>('citizenIssue');
|
const citizenIssue = defineModel<Date | null>('citizenIssue');
|
||||||
const citizenExpire = defineModel<Date | null>('citizenExpire');
|
const citizenExpire = defineModel<Date | null>('citizenExpire');
|
||||||
|
const contactName = defineModel<string>('contactName');
|
||||||
|
const contactTel = defineModel<string>('contactTel');
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
dense?: boolean;
|
dense?: boolean;
|
||||||
|
|
@ -30,72 +30,18 @@ const props = defineProps<{
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
separator?: boolean;
|
separator?: boolean;
|
||||||
employee?: boolean;
|
employee?: boolean;
|
||||||
|
agency?: boolean;
|
||||||
title?: string;
|
title?: string;
|
||||||
prefixId: string;
|
prefixId: string;
|
||||||
hideNameEn?: boolean;
|
hideNameEn?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const prefixNameOptions = ref<Record<string, unknown>[]>([]);
|
function matchPreFixName() {
|
||||||
let prefixNameFilter: (
|
|
||||||
value: string,
|
|
||||||
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void,
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
const genderOptions = ref<Record<string, unknown>[]>([]);
|
|
||||||
let genderFilter: (
|
|
||||||
value: string,
|
|
||||||
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void,
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
const nationalityOptions = ref<Record<string, unknown>[]>([]);
|
|
||||||
let nationalityFilter: (
|
|
||||||
value: string,
|
|
||||||
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void,
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
function matPreFixName() {
|
|
||||||
if (gender.value === 'male') prefixName.value = 'mr';
|
if (gender.value === 'male') prefixName.value = 'mr';
|
||||||
if (gender.value === 'female') prefixName.value = 'mrs';
|
if (gender.value === 'female' && prefixName.value === 'mr') {
|
||||||
|
prefixName.value = 'mrs';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
prefixNameFilter = selectFilterOptionRefMod(
|
|
||||||
ref(optionStore.globalOption?.prefix),
|
|
||||||
prefixNameOptions,
|
|
||||||
'label',
|
|
||||||
);
|
|
||||||
genderFilter = selectFilterOptionRefMod(
|
|
||||||
ref(optionStore.globalOption?.gender),
|
|
||||||
genderOptions,
|
|
||||||
'label',
|
|
||||||
);
|
|
||||||
nationalityFilter = selectFilterOptionRefMod(
|
|
||||||
ref(optionStore.globalOption?.nationality),
|
|
||||||
nationalityOptions,
|
|
||||||
'label',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => optionStore.globalOption,
|
|
||||||
() => {
|
|
||||||
prefixNameFilter = selectFilterOptionRefMod(
|
|
||||||
ref(optionStore.globalOption.prefix),
|
|
||||||
prefixNameOptions,
|
|
||||||
'label',
|
|
||||||
);
|
|
||||||
genderFilter = selectFilterOptionRefMod(
|
|
||||||
ref(optionStore.globalOption.gender),
|
|
||||||
genderOptions,
|
|
||||||
'label',
|
|
||||||
);
|
|
||||||
nationalityFilter = selectFilterOptionRefMod(
|
|
||||||
ref(optionStore.globalOption.nationality),
|
|
||||||
nationalityOptions,
|
|
||||||
'label',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => prefixName.value,
|
() => prefixName.value,
|
||||||
|
|
@ -110,7 +56,7 @@ watch(
|
||||||
() => gender.value,
|
() => gender.value,
|
||||||
() => {
|
() => {
|
||||||
if (props.readonly) return;
|
if (props.readonly) return;
|
||||||
matPreFixName();
|
matchPreFixName();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -150,40 +96,19 @@ watch(
|
||||||
for="input-citizen-id"
|
for="input-citizen-id"
|
||||||
/>
|
/>
|
||||||
<div class="col-12 row" style="display: flex; gap: var(--size-2)">
|
<div class="col-12 row" style="display: flex; gap: var(--size-2)">
|
||||||
<q-select
|
<SelectInput
|
||||||
outlined
|
|
||||||
use-input
|
|
||||||
fill-input
|
|
||||||
emit-value
|
|
||||||
map-options
|
|
||||||
hide-selected
|
|
||||||
hide-bottom-space
|
|
||||||
input-debounce="0"
|
|
||||||
option-label="label"
|
|
||||||
option-value="value"
|
|
||||||
hide-dropdown-icon
|
hide-dropdown-icon
|
||||||
autocomplete="off"
|
:readonly
|
||||||
class="col-md-1 col-6"
|
:option="optionStore.globalOption?.prefix"
|
||||||
:dense="dense"
|
:id="`${prefixId}-select-prefix-name`"
|
||||||
:readonly="readonly"
|
|
||||||
:options="prefixNameOptions"
|
|
||||||
:for="`${prefixId}-select-prefix-name`"
|
:for="`${prefixId}-select-prefix-name`"
|
||||||
:label="$t('personnel.form.prefixName')"
|
:rules="
|
||||||
@filter="prefixNameFilter"
|
agency ? [] : [(val: string) => !!val || $t('form.error.required')]
|
||||||
:model-value="readonly ? prefixName || '-' : prefixName"
|
|
||||||
@update:model-value="
|
|
||||||
(v) => (typeof v === 'string' ? (prefixName = v) : '')
|
|
||||||
"
|
"
|
||||||
@clear="prefixName = ''"
|
:label="$t('personnel.form.prefixName')"
|
||||||
>
|
class="col-md-1 col-6"
|
||||||
<template v-slot:no-option>
|
v-model="prefixName"
|
||||||
<q-item>
|
/>
|
||||||
<q-item-section class="text-grey">
|
|
||||||
{{ $t('general.noData') }}
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</template>
|
|
||||||
</q-select>
|
|
||||||
|
|
||||||
<q-input
|
<q-input
|
||||||
:for="`${prefixId}-input-first-name`"
|
:for="`${prefixId}-input-first-name`"
|
||||||
|
|
@ -194,7 +119,11 @@ watch(
|
||||||
class="col"
|
class="col"
|
||||||
:label="$t('personnel.form.firstName')"
|
:label="$t('personnel.form.firstName')"
|
||||||
v-model="firstName"
|
v-model="firstName"
|
||||||
:rules="[(val: string) => !!val || $t('form.error.required')]"
|
:rules="
|
||||||
|
employee || agency
|
||||||
|
? []
|
||||||
|
: [(val: string) => !!val || $t('form.error.required')]
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<q-input
|
<q-input
|
||||||
|
|
@ -229,24 +158,16 @@ watch(
|
||||||
class="col-12 row"
|
class="col-12 row"
|
||||||
style="display: flex; gap: var(--size-2)"
|
style="display: flex; gap: var(--size-2)"
|
||||||
>
|
>
|
||||||
<q-input
|
<SelectInput
|
||||||
:for="`${prefixId}-input-first-name`"
|
hide-dropdown-icon
|
||||||
:dense="dense"
|
:readonly
|
||||||
outlined
|
:option="optionStore.rawOption?.eng.prefix"
|
||||||
hide-bottom-space
|
:id="`${prefixId}-select-prefix-name-en`"
|
||||||
:readonly="readonly"
|
:for="`${prefixId}-select-prefix-name-en`"
|
||||||
:disable="!readonly"
|
:rules="[(val: string) => !!val || $t('form.error.required')]"
|
||||||
|
:label="$t('personnel.form.prefixName')"
|
||||||
class="col-md-1 col-6"
|
class="col-md-1 col-6"
|
||||||
label="Title"
|
v-model="prefixName"
|
||||||
:model-value="
|
|
||||||
readonly
|
|
||||||
? capitalize(prefixName || '') || '-'
|
|
||||||
: capitalize(prefixName || '')
|
|
||||||
"
|
|
||||||
@update:model-value="
|
|
||||||
(v) => (typeof v === 'string' ? (prefixName = v) : '')
|
|
||||||
"
|
|
||||||
@clear="prefixName = ''"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<q-input
|
<q-input
|
||||||
|
|
@ -287,10 +208,16 @@ watch(
|
||||||
class="col"
|
class="col"
|
||||||
label="Surname"
|
label="Surname"
|
||||||
v-model="lastNameEN"
|
v-model="lastNameEN"
|
||||||
:rules="[
|
:rules="
|
||||||
|
employee
|
||||||
|
? []
|
||||||
|
: [
|
||||||
(val: string) =>
|
(val: string) =>
|
||||||
!val || /^[A-Za-z\s]+$/.test(val) || $t('form.error.letterOnly'),
|
!val ||
|
||||||
]"
|
/^[A-Za-z\s]+$/.test(val) ||
|
||||||
|
$t('form.error.letterOnly'),
|
||||||
|
]
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -351,39 +278,16 @@ watch(
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<q-select
|
<SelectInput
|
||||||
v-if="!employee"
|
v-if="!employee"
|
||||||
outlined
|
:readonly
|
||||||
use-input
|
:option="optionStore.globalOption?.gender"
|
||||||
fill-input
|
:id="`${prefixId}-select-gender`"
|
||||||
emit-value
|
|
||||||
map-options
|
|
||||||
hide-selected
|
|
||||||
hide-bottom-space
|
|
||||||
input-debounce="0"
|
|
||||||
option-label="label"
|
|
||||||
option-value="value"
|
|
||||||
autocomplete="off"
|
|
||||||
class="col-md-2 col-6"
|
|
||||||
:dense="dense"
|
|
||||||
:readonly="readonly"
|
|
||||||
:options="genderOptions"
|
|
||||||
:hide-dropdown-icon="readonly"
|
|
||||||
:for="`${prefixId}-select-gender`"
|
:for="`${prefixId}-select-gender`"
|
||||||
:label="$t('form.gender')"
|
:label="$t('form.gender')"
|
||||||
@filter="genderFilter"
|
class="col-md-2 col-6"
|
||||||
:model-value="readonly ? gender || '-' : gender"
|
v-model="gender"
|
||||||
@update:model-value="(v) => (typeof v === 'string' ? (gender = v) : '')"
|
/>
|
||||||
@clear="gender = ''"
|
|
||||||
>
|
|
||||||
<template v-slot:no-option>
|
|
||||||
<q-item>
|
|
||||||
<q-item-section class="text-grey">
|
|
||||||
{{ $t('general.noData') }}
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</template>
|
|
||||||
</q-select>
|
|
||||||
|
|
||||||
<DatePicker
|
<DatePicker
|
||||||
v-model="birthDate"
|
v-model="birthDate"
|
||||||
|
|
@ -392,11 +296,15 @@ watch(
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:label="$t('form.birthDate')"
|
:label="$t('form.birthDate')"
|
||||||
:disabled-dates="disabledAfterToday"
|
:disabled-dates="disabledAfterToday"
|
||||||
:rules="[
|
:rules="
|
||||||
|
employee
|
||||||
|
? []
|
||||||
|
: [
|
||||||
(val: string) =>
|
(val: string) =>
|
||||||
!!val ||
|
!!val ||
|
||||||
$t('form.error.selectField', { field: $t('form.birthDate') }),
|
$t('form.error.selectField', { field: $t('form.birthDate') }),
|
||||||
]"
|
]
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<q-input
|
<q-input
|
||||||
|
|
@ -456,72 +364,67 @@ watch(
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<q-select
|
<SelectInput
|
||||||
v-if="employee"
|
v-if="employee"
|
||||||
outlined
|
:readonly
|
||||||
clearable
|
:option="optionStore.globalOption?.gender"
|
||||||
use-input
|
:id="`${prefixId}-select-gender`"
|
||||||
fill-input
|
|
||||||
emit-value
|
|
||||||
map-options
|
|
||||||
hide-selected
|
|
||||||
autocomplete="off"
|
|
||||||
hide-bottom-space
|
|
||||||
input-debounce="0"
|
|
||||||
option-label="label"
|
|
||||||
option-value="value"
|
|
||||||
class="col-md-2 col-6"
|
|
||||||
:dense="dense"
|
|
||||||
v-model="gender"
|
|
||||||
:readonly="readonly"
|
|
||||||
:options="genderOptions"
|
|
||||||
:hide-dropdown-icon="readonly"
|
|
||||||
:for="`${prefixId}-select-gender`"
|
:for="`${prefixId}-select-gender`"
|
||||||
:label="$t('form.gender')"
|
:label="$t('form.gender')"
|
||||||
:rules="[(val: string) => !!val || $t('form.error.required')]"
|
|
||||||
@filter="genderFilter"
|
|
||||||
>
|
|
||||||
<template v-slot:no-option>
|
|
||||||
<q-item>
|
|
||||||
<q-item-section class="text-grey">
|
|
||||||
{{ $t('general.noData') }}
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</template>
|
|
||||||
</q-select>
|
|
||||||
<q-select
|
|
||||||
v-if="employee"
|
|
||||||
outlined
|
|
||||||
clearable
|
|
||||||
use-input
|
|
||||||
fill-input
|
|
||||||
emit-value
|
|
||||||
map-options
|
|
||||||
hide-selected
|
|
||||||
hide-bottom-space
|
|
||||||
autocomplete="off"
|
|
||||||
input-debounce="0"
|
|
||||||
option-label="label"
|
|
||||||
option-value="value"
|
|
||||||
v-model="nationality"
|
|
||||||
class="col-md-2 col-6"
|
class="col-md-2 col-6"
|
||||||
:dense="dense"
|
v-model="gender"
|
||||||
:readonly="readonly"
|
/>
|
||||||
:options="nationalityOptions"
|
<SelectInput
|
||||||
:hide-dropdown-icon="readonly"
|
v-if="employee"
|
||||||
|
:readonly
|
||||||
|
:option="optionStore.globalOption.nationality"
|
||||||
|
:id="`${prefixId}-select-nationality`"
|
||||||
:for="`${prefixId}-select-nationality`"
|
:for="`${prefixId}-select-nationality`"
|
||||||
:label="$t('general.nationality')"
|
:label="$t('general.nationality')"
|
||||||
:rules="[(val: string) => !!val || $t('form.error.required')]"
|
class="col-md-2 col-6"
|
||||||
@filter="nationalityFilter"
|
v-model="nationality"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
v-if="agency"
|
||||||
|
for="input-agencies-contact-name"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
:readonly="readonly"
|
||||||
|
hide-bottom-space
|
||||||
|
class="col-md-4 col-12"
|
||||||
|
:label="$t('personnel.form.contactName')"
|
||||||
|
:model-value="readonly ? contactName || '-' : contactName"
|
||||||
|
@update:model-value="
|
||||||
|
(v) => (typeof v === 'string' ? (contactName = v) : '')
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
v-if="agency"
|
||||||
|
for="input-agencies-contact-tel"
|
||||||
|
id="input-agencies-contact-tel"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
:readonly="readonly"
|
||||||
|
hide-bottom-space
|
||||||
|
class="col-md-4 col-12"
|
||||||
|
:label="$t('personnel.form.contactTel')"
|
||||||
|
:model-value="readonly ? contactTel || '-' : contactTel"
|
||||||
|
@update:model-value="
|
||||||
|
(v) => (typeof v === 'string' ? (contactTel = v) : '')
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<template v-slot:no-option>
|
<template #prepend>
|
||||||
<q-item>
|
<q-icon
|
||||||
<q-item-section class="text-grey">
|
size="xs"
|
||||||
{{ $t('general.noData') }}
|
name="mdi-phone-outline"
|
||||||
</q-item-section>
|
class="cursor-pointer"
|
||||||
</q-item>
|
color="primary"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</q-select>
|
</q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ const employeeOther = defineModel<EmployeeOtherCreate>('employeeOther');
|
||||||
:readonly="readonly || employeeOther.statusSave"
|
:readonly="readonly || employeeOther.statusSave"
|
||||||
hide-bottom-space
|
hide-bottom-space
|
||||||
class="col-md-3 col-6"
|
class="col-md-3 col-6"
|
||||||
:label="$t('form.firstName')"
|
:label="$t('general.nativeLanguage', { msg: $t('form.firstName') })"
|
||||||
:model-value="employeeOther.fatherFirstName"
|
:model-value="employeeOther.fatherFirstName"
|
||||||
@update:model-value="
|
@update:model-value="
|
||||||
(v) =>
|
(v) =>
|
||||||
|
|
@ -122,7 +122,7 @@ const employeeOther = defineModel<EmployeeOtherCreate>('employeeOther');
|
||||||
:readonly="readonly || employeeOther.statusSave"
|
:readonly="readonly || employeeOther.statusSave"
|
||||||
hide-bottom-space
|
hide-bottom-space
|
||||||
class="col-md-3 col-6"
|
class="col-md-3 col-6"
|
||||||
:label="$t('form.lastName')"
|
:label="$t('general.nativeLanguage', { msg: $t('form.lastName') })"
|
||||||
:model-value="employeeOther.fatherLastName"
|
:model-value="employeeOther.fatherLastName"
|
||||||
@update:model-value="
|
@update:model-value="
|
||||||
(v) =>
|
(v) =>
|
||||||
|
|
@ -177,7 +177,7 @@ const employeeOther = defineModel<EmployeeOtherCreate>('employeeOther');
|
||||||
:readonly="readonly || employeeOther.statusSave"
|
:readonly="readonly || employeeOther.statusSave"
|
||||||
hide-bottom-space
|
hide-bottom-space
|
||||||
class="col-md-3 col-6"
|
class="col-md-3 col-6"
|
||||||
:label="$t('form.firstName')"
|
:label="$t('general.nativeLanguage', { msg: $t('form.firstName') })"
|
||||||
:model-value="employeeOther.motherFirstName"
|
:model-value="employeeOther.motherFirstName"
|
||||||
@update:model-value="
|
@update:model-value="
|
||||||
(v) =>
|
(v) =>
|
||||||
|
|
@ -193,7 +193,7 @@ const employeeOther = defineModel<EmployeeOtherCreate>('employeeOther');
|
||||||
:readonly="readonly || employeeOther.statusSave"
|
:readonly="readonly || employeeOther.statusSave"
|
||||||
hide-bottom-space
|
hide-bottom-space
|
||||||
class="col-md-3 col-6"
|
class="col-md-3 col-6"
|
||||||
:label="$t('form.lastName')"
|
:label="$t('general.nativeLanguage', { msg: $t('form.lastName') })"
|
||||||
:model-value="employeeOther.motherLastName"
|
:model-value="employeeOther.motherLastName"
|
||||||
@update:model-value="
|
@update:model-value="
|
||||||
(v) =>
|
(v) =>
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ onMounted(() => {
|
||||||
'label',
|
'label',
|
||||||
);
|
);
|
||||||
passportIssuingCountryFilter = selectFilterOptionRefMod(
|
passportIssuingCountryFilter = selectFilterOptionRefMod(
|
||||||
ref(optionStore.globalOption.nationality),
|
ref(optionStore.rawOption?.eng.nationality),
|
||||||
passportIssuingCountryOptions,
|
passportIssuingCountryOptions,
|
||||||
'label',
|
'label',
|
||||||
);
|
);
|
||||||
|
|
@ -121,13 +121,13 @@ onMounted(() => {
|
||||||
);
|
);
|
||||||
|
|
||||||
genderFilter = selectFilterOptionRefMod(
|
genderFilter = selectFilterOptionRefMod(
|
||||||
ref(optionStore.globalOption?.gender),
|
ref(optionStore.rawOption?.eng.gender),
|
||||||
genderOptions,
|
genderOptions,
|
||||||
'label',
|
'label',
|
||||||
);
|
);
|
||||||
|
|
||||||
nationalityFilter = selectFilterOptionRefMod(
|
nationalityFilter = selectFilterOptionRefMod(
|
||||||
ref(optionStore.globalOption?.nationality),
|
ref(optionStore.rawOption?.eng.nationality),
|
||||||
nationalityOptions,
|
nationalityOptions,
|
||||||
'label',
|
'label',
|
||||||
);
|
);
|
||||||
|
|
@ -152,7 +152,7 @@ watch(
|
||||||
);
|
);
|
||||||
|
|
||||||
passportIssuingCountryFilter = selectFilterOptionRefMod(
|
passportIssuingCountryFilter = selectFilterOptionRefMod(
|
||||||
ref(optionStore.globalOption.nationality),
|
ref(optionStore.rawOption?.eng.nationality),
|
||||||
passportIssuingCountryOptions,
|
passportIssuingCountryOptions,
|
||||||
'label',
|
'label',
|
||||||
);
|
);
|
||||||
|
|
@ -164,13 +164,13 @@ watch(
|
||||||
);
|
);
|
||||||
|
|
||||||
genderFilter = selectFilterOptionRefMod(
|
genderFilter = selectFilterOptionRefMod(
|
||||||
ref(optionStore.globalOption?.gender),
|
ref(optionStore.rawOption?.eng.gender),
|
||||||
genderOptions,
|
genderOptions,
|
||||||
'label',
|
'label',
|
||||||
);
|
);
|
||||||
|
|
||||||
nationalityFilter = selectFilterOptionRefMod(
|
nationalityFilter = selectFilterOptionRefMod(
|
||||||
ref(optionStore.globalOption?.nationality),
|
ref(optionStore.rawOption?.eng.nationality),
|
||||||
nationalityOptions,
|
nationalityOptions,
|
||||||
'label',
|
'label',
|
||||||
);
|
);
|
||||||
|
|
@ -258,7 +258,7 @@ watch(
|
||||||
:options="workerStatusOptions"
|
:options="workerStatusOptions"
|
||||||
:hide-dropdown-icon="readonly"
|
:hide-dropdown-icon="readonly"
|
||||||
:for="`${prefixId}-select-visa-type`"
|
:for="`${prefixId}-select-visa-type`"
|
||||||
:label="$t('customerEmployee.form.workerType')"
|
:label="$t('customerEmployee.form.workerStatus')"
|
||||||
@filter="workerStatusFilter"
|
@filter="workerStatusFilter"
|
||||||
:model-value="readonly ? workerStatus || '-' : workerStatus"
|
:model-value="readonly ? workerStatus || '-' : workerStatus"
|
||||||
@update:model-value="
|
@update:model-value="
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,11 @@ const entryCount = defineModel<number>('entryCount');
|
||||||
const issuePlace = defineModel<string>('issuePlace');
|
const issuePlace = defineModel<string>('issuePlace');
|
||||||
const issueCountry = defineModel<string>('issueCountry');
|
const issueCountry = defineModel<string>('issueCountry');
|
||||||
const issueDate = defineModel<Date | null | string>('visaIssueDate');
|
const issueDate = defineModel<Date | null | string>('visaIssueDate');
|
||||||
const type = defineModel<string>('visaType');
|
const type = defineModel<string>('type');
|
||||||
const expireDate = defineModel<Date>('expireDate');
|
const expireDate = defineModel<Date>('expireDate');
|
||||||
const remark = defineModel<string>('remark');
|
const remark = defineModel<string>('remark');
|
||||||
const workerType = defineModel<string>('workerType');
|
const workerType = defineModel<string>('workerType');
|
||||||
const number = defineModel<string>('visaNumber');
|
const number = defineModel<string>('number');
|
||||||
|
|
||||||
const calculatedVisaDate = computed(() => {
|
const calculatedVisaDate = computed(() => {
|
||||||
if (!issueDate.value) return undefined;
|
if (!issueDate.value) return undefined;
|
||||||
|
|
@ -78,6 +78,12 @@ onMounted(async () => {
|
||||||
await fetchProvince();
|
await fetchProvince();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const visaIssueCountryOptions = ref<Record<string, unknown>[]>([]);
|
||||||
|
let visaIssueCountryFilter: (
|
||||||
|
value: string,
|
||||||
|
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void,
|
||||||
|
) => void;
|
||||||
|
|
||||||
const visaTypeOptions = ref<Record<string, unknown>[]>([]);
|
const visaTypeOptions = ref<Record<string, unknown>[]>([]);
|
||||||
let visaTypeFilter: (
|
let visaTypeFilter: (
|
||||||
value: string,
|
value: string,
|
||||||
|
|
@ -92,11 +98,17 @@ let workerTypeFilter: (
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
visaTypeFilter = selectFilterOptionRefMod(
|
visaTypeFilter = selectFilterOptionRefMod(
|
||||||
ref(optionStore.globalOption?.nationality),
|
ref(optionStore.globalOption?.visaType),
|
||||||
visaTypeOptions,
|
visaTypeOptions,
|
||||||
'label',
|
'label',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
visaIssueCountryFilter = selectFilterOptionRefMod(
|
||||||
|
ref(optionStore.globalOption?.nationality),
|
||||||
|
visaIssueCountryOptions,
|
||||||
|
'label',
|
||||||
|
);
|
||||||
|
|
||||||
workerTypeFilter = selectFilterOptionRefMod(
|
workerTypeFilter = selectFilterOptionRefMod(
|
||||||
ref(optionStore.globalOption?.workerType),
|
ref(optionStore.globalOption?.workerType),
|
||||||
workerTypeOptions,
|
workerTypeOptions,
|
||||||
|
|
@ -107,8 +119,14 @@ onMounted(() => {
|
||||||
watch(
|
watch(
|
||||||
() => optionStore.globalOption,
|
() => optionStore.globalOption,
|
||||||
() => {
|
() => {
|
||||||
|
visaIssueCountryFilter = selectFilterOptionRefMod(
|
||||||
|
ref(optionStore.globalOption?.nationality),
|
||||||
|
visaIssueCountryOptions,
|
||||||
|
'label',
|
||||||
|
);
|
||||||
|
|
||||||
visaTypeFilter = selectFilterOptionRefMod(
|
visaTypeFilter = selectFilterOptionRefMod(
|
||||||
optionStore.globalOption.nationality,
|
optionStore.globalOption.visaType,
|
||||||
visaTypeOptions,
|
visaTypeOptions,
|
||||||
'label',
|
'label',
|
||||||
);
|
);
|
||||||
|
|
@ -422,11 +440,11 @@ watch(
|
||||||
class="col-md-4 col-6"
|
class="col-md-4 col-6"
|
||||||
:dense="dense"
|
:dense="dense"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:options="visaTypeOptions"
|
:options="visaIssueCountryOptions"
|
||||||
:hide-dropdown-icon="readonly"
|
:hide-dropdown-icon="readonly"
|
||||||
:for="`${prefixId}-select-issue-country`"
|
:for="`${prefixId}-select-issue-country`"
|
||||||
:label="$t('customerEmployee.form.issueCountry')"
|
:label="$t('customerEmployee.form.issueCountry')"
|
||||||
@filter="visaTypeFilter"
|
@filter="visaIssueCountryFilter"
|
||||||
:model-value="readonly ? issueCountry || '-' : issueCountry"
|
:model-value="readonly ? issueCountry || '-' : issueCountry"
|
||||||
@update:model-value="
|
@update:model-value="
|
||||||
(v) => (typeof v === 'string' ? (issueCountry = v) : '')
|
(v) => (typeof v === 'string' ? (issueCountry = v) : '')
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { QSelect } from 'quasar';
|
||||||
import { CustomerBranch } from 'stores/customer/types';
|
import { CustomerBranch } from 'stores/customer/types';
|
||||||
import { selectFilterOptionRefMod } from 'stores/utils';
|
import { selectFilterOptionRefMod } from 'stores/utils';
|
||||||
import { onMounted, ref, watch } from 'vue';
|
import { onMounted, ref, watch } from 'vue';
|
||||||
|
import SelectCustomer from 'components/shared/select/SelectCustomer.vue';
|
||||||
import {
|
import {
|
||||||
EditButton,
|
EditButton,
|
||||||
DeleteButton,
|
DeleteButton,
|
||||||
|
|
@ -11,6 +12,7 @@ import {
|
||||||
} from 'components/button';
|
} from 'components/button';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import useOptionStore from 'stores/options';
|
import useOptionStore from 'stores/options';
|
||||||
|
import { formatAddress } from 'src/utils/address';
|
||||||
|
|
||||||
const { locale } = useI18n();
|
const { locale } = useI18n();
|
||||||
|
|
||||||
|
|
@ -21,6 +23,13 @@ const optionsBranch = defineModel<{ id: string; name: string }[]>(
|
||||||
);
|
);
|
||||||
|
|
||||||
// employee
|
// employee
|
||||||
|
|
||||||
|
const customerBranchId = defineModel<string>('customerBranchId');
|
||||||
|
|
||||||
|
const currentCustomerBranch = defineModel<CustomerBranch>(
|
||||||
|
'currentCustomerBranch',
|
||||||
|
);
|
||||||
|
|
||||||
const customerBranch = defineModel<{
|
const customerBranch = defineModel<{
|
||||||
id: string;
|
id: string;
|
||||||
address: string;
|
address: string;
|
||||||
|
|
@ -107,180 +116,14 @@ defineEmits<{
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 row" style="gap: var(--size-2)">
|
<div class="col-12 row" style="gap: var(--size-2)">
|
||||||
<q-select
|
<SelectCustomer
|
||||||
:id="`${prefixId}-select-employer-branch`"
|
v-model:value="customerBranchId"
|
||||||
:for="`${prefixId}-select-employer-branch`"
|
v-model:value-option="currentCustomerBranch"
|
||||||
:use-input="!customerBranch"
|
|
||||||
autocomplete="off"
|
|
||||||
input-debounce="0"
|
|
||||||
:hide-dropdown-icon="readonly"
|
|
||||||
:dense="dense"
|
|
||||||
outlined
|
|
||||||
:readonly="readonly"
|
|
||||||
hide-bottom-space
|
|
||||||
class="col-12"
|
|
||||||
:label="$t('customer.form.branchCode')"
|
:label="$t('customer.form.branchCode')"
|
||||||
v-model="customerBranch"
|
class="col-12 field-two"
|
||||||
:option-value="
|
required
|
||||||
(v) => ({
|
:readonly
|
||||||
id: v.id,
|
|
||||||
address: v.address,
|
|
||||||
addressEN: v.addressEN,
|
|
||||||
provinceId: v.provinceId,
|
|
||||||
districtId: v.districtId,
|
|
||||||
subDistrictId: v.subDistrictId,
|
|
||||||
zipCode: v.zipCode,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
emit-value
|
|
||||||
map-options
|
|
||||||
:options="employeeOwnerOption"
|
|
||||||
@filter="(val, update) => $emit('filterOwnerBranch', val, update)"
|
|
||||||
:rules="[
|
|
||||||
(val: string) =>
|
|
||||||
!!val ||
|
|
||||||
$t('form.error.selectField', {
|
|
||||||
field: $t('customerEmployee.branch'),
|
|
||||||
}),
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<template v-slot:option="scope">
|
|
||||||
<q-item
|
|
||||||
v-if="scope.opt"
|
|
||||||
v-bind="scope.itemProps"
|
|
||||||
class="row items-start col-12 no-padding"
|
|
||||||
>
|
|
||||||
<div class="q-ma-sm">
|
|
||||||
<i class="isax isax-frame5" style="color: var(--brand-1)" />
|
|
||||||
</div>
|
|
||||||
<div class="q-mt-sm">
|
|
||||||
<div>
|
|
||||||
<span v-if="scope.opt.customer.customerType">
|
|
||||||
<span style="font-weight: 600">
|
|
||||||
{{
|
|
||||||
scope.opt.customer.customerType === 'CORP'
|
|
||||||
? $t('customer.form.registerName')
|
|
||||||
: $t('customer.form.ownerName')
|
|
||||||
}}:
|
|
||||||
</span>
|
|
||||||
{{
|
|
||||||
scope.opt.customer.customerType === 'CORP'
|
|
||||||
? $i18n.locale === 'eng'
|
|
||||||
? scope.opt.registerNameEN
|
|
||||||
: scope.opt.registerName
|
|
||||||
: $i18n.locale === 'eng'
|
|
||||||
? `${optionStore.mapOption(scope.opt.namePrefix)} ${scope.opt.firstNameEN} ${scope.opt.lastNameEN}` ||
|
|
||||||
'-'
|
|
||||||
: `${optionStore.mapOption(scope.opt.namePrefix)} ${scope.opt.firstName} ${scope.opt.lastName}` ||
|
|
||||||
'-'
|
|
||||||
}}
|
|
||||||
({{ scope.opt.code }})
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="text-caption app-text-muted-2 q-mb-xs">
|
|
||||||
<span v-if="scope.opt.customer" class="col column">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
`branch.form.title.${scope.opt.code.endsWith('-00') ? 'branchHQLabel' : 'branchLabel'}`,
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
|
|
||||||
{{
|
|
||||||
!scope.opt.code.endsWith('-00')
|
|
||||||
? +scope.opt.code.split('-')[1]
|
|
||||||
: ''
|
|
||||||
}}
|
|
||||||
</span>
|
|
||||||
<span v-if="scope.opt.province" class="col">
|
|
||||||
{{ $t('general.address') }}
|
|
||||||
{{
|
|
||||||
$i18n.locale === 'eng'
|
|
||||||
? `${scope.opt.addressEN || ''}, ${scope.opt.mooEN && `${$t('form.moo')} ${scope.opt.mooEN},`} ${scope.opt.soiEN && `${$t('form.soi')} ${scope.opt.soiEN},`} ${scope.opt.streetEN && `${scope.opt.streetEN} Rd,`} ${scope.opt.subDistrict.nameEN || ''}, ${scope.opt.district.nameEN || ''}, ${scope.opt.province.nameEN || ''}`
|
|
||||||
: `${scope.opt.address || ''}, ${scope.opt.moo && `${$t('form.moo')} ${scope.opt.moo},`} ${scope.opt.soi && `${$t('form.soi')} ${scope.opt.soi},`} ${scope.opt.street && `${$t('form.road')} ${scope.opt.street},`} ${scope.opt.subDistrict.name || ''}, ${scope.opt.district.name || ''}, ${scope.opt.province.name || ''}`
|
|
||||||
}}
|
|
||||||
{{ scope.opt.subDistrict?.zipCode || '' }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-item>
|
|
||||||
<q-separator class="q-mx-sm" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:selected-item="scope">
|
|
||||||
<div
|
|
||||||
v-if="scope.opt"
|
|
||||||
class="row items-center no-wrap"
|
|
||||||
style="width: 1px"
|
|
||||||
>
|
|
||||||
<div class="q-mr-sm">
|
|
||||||
<span style="font-weight: 600">
|
|
||||||
{{
|
|
||||||
scope.opt.customer.customerType === 'CORP'
|
|
||||||
? $t('customer.form.registerName')
|
|
||||||
: $t('customer.form.ownerName')
|
|
||||||
}}:
|
|
||||||
</span>
|
|
||||||
{{
|
|
||||||
scope.opt.customer.customerType === 'CORP'
|
|
||||||
? $i18n.locale === 'eng'
|
|
||||||
? scope.opt.registerNameEN
|
|
||||||
: scope.opt.registerName
|
|
||||||
: $i18n.locale === 'eng'
|
|
||||||
? `${optionStore.mapOption(scope.opt.namePrefix)} ${scope.opt.firstNameEN} ${scope.opt.lastNameEN}` ||
|
|
||||||
'-'
|
|
||||||
: `${optionStore.mapOption(scope.opt.namePrefix)} ${scope.opt.firstName} ${scope.opt.lastName}` ||
|
|
||||||
'-'
|
|
||||||
}}
|
|
||||||
({{ scope.opt.code }})
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="text-caption app-text-muted-2"
|
|
||||||
v-if="scope.opt.customer && scope.opt.province"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
`branch.form.title.${scope.opt.code.endsWith('-00') ? 'branchHQLabel' : 'branchLabel'}`,
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
|
|
||||||
{{
|
|
||||||
!scope.opt.code.endsWith('-00')
|
|
||||||
? +scope.opt.code.split('-')[1]
|
|
||||||
: ''
|
|
||||||
}}
|
|
||||||
|
|
||||||
{{ $t('general.address') }}
|
|
||||||
{{
|
|
||||||
$i18n.locale === 'eng'
|
|
||||||
? `${scope.opt.addressEN || ''}, ${scope.opt.mooEN && `${$t('form.moo')} ${scope.opt.mooEN},`} ${scope.opt.soiEN && `${$t('form.soi')} ${scope.opt.soiEN},`} ${scope.opt.streetEN && `${scope.opt.streetEN} Rd,`} ${scope.opt.subDistrict.nameEN || ''}, ${scope.opt.district.nameEN || ''}, ${scope.opt.province.nameEN || ''}`
|
|
||||||
: `${scope.opt.address || ''}, ${scope.opt.moo && `${$t('form.moo')} ${scope.opt.moo},`} ${scope.opt.soi && `${$t('form.soi')} ${scope.opt.soi},`} ${scope.opt.street && `${$t('form.road')} ${scope.opt.street},`} ${scope.opt.subDistrict.name || ''}, ${scope.opt.district.name || ''}, ${scope.opt.province.name || ''}`
|
|
||||||
}}
|
|
||||||
{{ scope.opt.subDistrict?.zipCode || '' }}
|
|
||||||
<q-tooltip v-if="scope.opt.customer && scope.opt.province">
|
|
||||||
{{ $t('customerBranch.form.title') }}:
|
|
||||||
|
|
||||||
{{ $t('general.address') }}
|
|
||||||
{{
|
|
||||||
$i18n.locale === 'eng'
|
|
||||||
? `${scope.opt.addressEN || ''}, ${scope.opt.mooEN && `${$t('form.moo')} ${scope.opt.mooEN},`} ${scope.opt.soiEN && `${$t('form.soi')} ${scope.opt.soiEN},`} ${scope.opt.streetEN && `${scope.opt.streetEN} Rd,`} ${scope.opt.subDistrict.nameEN || ''}, ${scope.opt.district.nameEN || ''}, ${scope.opt.province.nameEN || ''}`
|
|
||||||
: `${scope.opt.address || ''}, ${scope.opt.moo && `${$t('form.moo')} ${scope.opt.moo},`} ${scope.opt.soi && `${$t('form.soi')} ${scope.opt.soi},`} ${scope.opt.street && `${$t('form.road')} ${scope.opt.street},`} ${scope.opt.subDistrict.name || ''}, ${scope.opt.district.name || ''}, ${scope.opt.province.name || ''}`
|
|
||||||
}}
|
|
||||||
{{ scope.opt.subDistrict?.zipCode || '' }}
|
|
||||||
</q-tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:append>
|
|
||||||
<q-icon
|
|
||||||
v-if="!readonly && customerBranch"
|
|
||||||
name="mdi-close-circle"
|
|
||||||
@click.stop="customerBranch = undefined"
|
|
||||||
class="cursor-pointer clear-btn"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
|
||||||
</q-select>
|
|
||||||
|
|
||||||
<q-input
|
<q-input
|
||||||
:for="`${prefixId}-input-code`"
|
:for="`${prefixId}-input-code`"
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,14 @@ import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import useUserStore from 'src/stores/user';
|
import useUserStore from 'src/stores/user';
|
||||||
import useOptionStore from 'src/stores/options';
|
import useOptionStore from 'src/stores/options';
|
||||||
|
import { useWorkflowTemplate } from 'src/stores/workflow-template';
|
||||||
import { baseUrl } from 'stores/utils';
|
import { baseUrl } from 'stores/utils';
|
||||||
import { getRole } from 'src/services/keycloak';
|
import { getRole } from 'src/services/keycloak';
|
||||||
import {
|
import {
|
||||||
WorkflowUserInTable,
|
WorkflowUserInTable,
|
||||||
WorkflowTemplatePayload,
|
WorkflowTemplatePayload,
|
||||||
WorkFlowPayloadStep,
|
WorkFlowPayloadStep,
|
||||||
|
Group,
|
||||||
} from 'src/stores/workflow-template/types';
|
} from 'src/stores/workflow-template/types';
|
||||||
import { User } from 'src/stores/user/types';
|
import { User } from 'src/stores/user/types';
|
||||||
|
|
||||||
|
|
@ -20,6 +22,7 @@ import ToggleButton from 'src/components/button/ToggleButton.vue';
|
||||||
import NoData from '../NoData.vue';
|
import NoData from '../NoData.vue';
|
||||||
import SelectBranch from '../shared/select/SelectBranch.vue';
|
import SelectBranch from '../shared/select/SelectBranch.vue';
|
||||||
import AddButton from '../button/AddButton.vue';
|
import AddButton from '../button/AddButton.vue';
|
||||||
|
import { QField } from 'quasar';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
|
|
@ -29,6 +32,7 @@ defineProps<{
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const optionStore = useOptionStore();
|
const optionStore = useOptionStore();
|
||||||
|
const workflowStore = useWorkflowTemplate();
|
||||||
|
|
||||||
const userInTable = defineModel<WorkflowUserInTable[]>('userInTable', {
|
const userInTable = defineModel<WorkflowUserInTable[]>('userInTable', {
|
||||||
default: [],
|
default: [],
|
||||||
|
|
@ -43,7 +47,7 @@ const flowData = defineModel<WorkflowTemplatePayload>('flowData', {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const objectOptions = [
|
let objectOptions = [
|
||||||
...(optionStore.globalOption?.agenciesType || []),
|
...(optionStore.globalOption?.agenciesType || []),
|
||||||
{ label: t('flow.customer'), value: 'customer' },
|
{ label: t('flow.customer'), value: 'customer' },
|
||||||
{ label: t('flow.officer'), value: 'officer' },
|
{ label: t('flow.officer'), value: 'officer' },
|
||||||
|
|
@ -51,7 +55,9 @@ const objectOptions = [
|
||||||
const options = ref(objectOptions);
|
const options = ref(objectOptions);
|
||||||
const role = ref<string[]>([]);
|
const role = ref<string[]>([]);
|
||||||
const userList = ref<User[]>([]);
|
const userList = ref<User[]>([]);
|
||||||
|
const groupList = ref<Group[]>([]);
|
||||||
const responsiblePersonSearch = ref('');
|
const responsiblePersonSearch = ref('');
|
||||||
|
const responsibleMenu = ref(false);
|
||||||
|
|
||||||
async function getUserList(opts?: { query: string }) {
|
async function getUserList(opts?: { query: string }) {
|
||||||
const resUser = await userStore.fetchList({
|
const resUser = await userStore.fetchList({
|
||||||
|
|
@ -60,10 +66,10 @@ async function getUserList(opts?: { query: string }) {
|
||||||
if (resUser) userList.value = resUser.result;
|
if (resUser) userList.value = resUser.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// async function getUserById(responsiblePersonId: string) {
|
async function getGroupList() {
|
||||||
// const resUser = await userStore.fetchById(responsiblePersonId);
|
const resGroup = await workflowStore.getGroupList();
|
||||||
// if (resUser) userInTable.value.push(resUser);
|
if (resGroup) groupList.value = resGroup;
|
||||||
// }
|
}
|
||||||
|
|
||||||
function selectResponsiblePerson(stepIndex: number, responsiblePerson: User) {
|
function selectResponsiblePerson(stepIndex: number, responsiblePerson: User) {
|
||||||
const currStep = flowData.value.step[stepIndex];
|
const currStep = flowData.value.step[stepIndex];
|
||||||
|
|
@ -78,6 +84,7 @@ function selectResponsiblePerson(stepIndex: number, responsiblePerson: User) {
|
||||||
userInTable.value[stepIndex] = {
|
userInTable.value[stepIndex] = {
|
||||||
name: flowData.value.step[stepIndex].name,
|
name: flowData.value.step[stepIndex].name,
|
||||||
responsiblePerson: [],
|
responsiblePerson: [],
|
||||||
|
responsibleGroup: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,6 +108,33 @@ function selectResponsiblePerson(stepIndex: number, responsiblePerson: User) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function selectResponsibleGroup(stepIndex: number, responsibleGroup: string) {
|
||||||
|
const currStep = flowData.value.step[stepIndex];
|
||||||
|
const existGroupIndex = currStep.responsibleGroup?.findIndex(
|
||||||
|
(p) => p === responsibleGroup,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (existGroupIndex === -1) {
|
||||||
|
currStep.responsibleGroup?.push(responsibleGroup);
|
||||||
|
|
||||||
|
if (!userInTable.value[stepIndex]) {
|
||||||
|
userInTable.value[stepIndex] = {
|
||||||
|
name: flowData.value.step[stepIndex].name,
|
||||||
|
responsiblePerson: [],
|
||||||
|
responsibleGroup: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
userInTable.value[stepIndex]?.responsibleGroup.push(responsibleGroup);
|
||||||
|
} else {
|
||||||
|
currStep.responsibleGroup?.splice(Number(existGroupIndex), 1);
|
||||||
|
userInTable.value[stepIndex]?.responsibleGroup.splice(
|
||||||
|
Number(existGroupIndex),
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function selectItem(
|
function selectItem(
|
||||||
val: Record<string, unknown>,
|
val: Record<string, unknown>,
|
||||||
responsibleInstitution?: string[],
|
responsibleInstitution?: string[],
|
||||||
|
|
@ -142,6 +176,7 @@ watch(
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
role.value = getRole() || [];
|
role.value = getRole() || [];
|
||||||
await getUserList();
|
await getUserList();
|
||||||
|
await getGroupList();
|
||||||
await userStore.fetchHqOption();
|
await userStore.fetchHqOption();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -467,33 +502,29 @@ onMounted(async () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- RESPONSIBLE-PERSON -->
|
<!-- RESPONSIBLE-PERSON -->
|
||||||
<q-select
|
<q-field
|
||||||
v-if="step.responsiblePersonId"
|
v-if="step.responsiblePersonId"
|
||||||
behavior="menu"
|
|
||||||
:for="`select-responsible-person-${index}-${onDrawer ? 'drawer' : 'dialog'}`"
|
:for="`select-responsible-person-${index}-${onDrawer ? 'drawer' : 'dialog'}`"
|
||||||
:bg-color="readonly ? 'transparent' : ''"
|
:bg-color="readonly ? 'transparent' : ''"
|
||||||
:readonly
|
:readonly
|
||||||
outlined
|
outlined
|
||||||
dense
|
:stack-label="
|
||||||
v-model="step.responsiblePersonId"
|
userInTable[index]?.responsiblePerson.length > 0 ||
|
||||||
multiple
|
userInTable[index]?.responsibleGroup.length > 0
|
||||||
:options="[1, 2, 3]"
|
"
|
||||||
hide-bottom-space
|
|
||||||
option-label="label"
|
|
||||||
option-value="value"
|
|
||||||
emit-value
|
|
||||||
:label="$t('flow.responsiblePerson')"
|
:label="$t('flow.responsiblePerson')"
|
||||||
|
dense
|
||||||
class="col-md-6 col-12"
|
class="col-md-6 col-12"
|
||||||
:hide-dropdown-icon="readonly"
|
:class="{ 'cursor-pointer': !readonly }"
|
||||||
>
|
>
|
||||||
<template v-slot:selected-item="scope">
|
<template #control>
|
||||||
<div class="column full-width">
|
<q-item
|
||||||
<div
|
dense
|
||||||
class="row items-center no-wrap"
|
class="items-center full-width no-padding"
|
||||||
v-for="person in userInTable[
|
v-for="person in userInTable[
|
||||||
index
|
index
|
||||||
]?.responsiblePerson.filter(
|
]?.responsiblePerson.filter((p) =>
|
||||||
(p) => p.id === scope.opt,
|
step.responsiblePersonId.includes(p.id),
|
||||||
)"
|
)"
|
||||||
:key="person.id"
|
:key="person.id"
|
||||||
>
|
>
|
||||||
|
|
@ -547,12 +578,52 @@ onMounted(async () => {
|
||||||
{{ person.code }}
|
{{ person.code }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</q-item>
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:option></template>
|
<div
|
||||||
<q-menu v-if="!readonly" :offset="[0, 4]">
|
v-if="step.responsibleGroup.length > 0"
|
||||||
|
class="full-width app-text-muted text-weight-medium"
|
||||||
|
style="font-size: 10px"
|
||||||
|
>
|
||||||
|
{{ $t('general.group') }}
|
||||||
|
</div>
|
||||||
|
<q-item
|
||||||
|
class="items-center full-width no-padding"
|
||||||
|
v-for="group in userInTable[
|
||||||
|
index
|
||||||
|
]?.responsibleGroup.filter((g) =>
|
||||||
|
step.responsibleGroup.includes(g),
|
||||||
|
)"
|
||||||
|
:key="group"
|
||||||
|
dense
|
||||||
|
>
|
||||||
|
<q-avatar class="q-ml-sm" size="md">
|
||||||
|
<q-img
|
||||||
|
class="text-center"
|
||||||
|
:ratio="1"
|
||||||
|
:src="`/img-group.png`"
|
||||||
|
/>
|
||||||
|
</q-avatar>
|
||||||
|
<span class="q-pl-md">
|
||||||
|
{{ group }}
|
||||||
|
</span>
|
||||||
|
</q-item>
|
||||||
|
</template>
|
||||||
|
<template #append>
|
||||||
|
<q-icon
|
||||||
|
name="mdi-menu-down"
|
||||||
|
:class="{ rotated: responsibleMenu }"
|
||||||
|
class="transition-rotate"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<q-menu
|
||||||
|
v-if="!readonly"
|
||||||
|
no-focus
|
||||||
|
no-refocus
|
||||||
|
:offset="[0, 4]"
|
||||||
|
@before-show="() => (responsibleMenu = true)"
|
||||||
|
@before-hide="() => (responsibleMenu = false)"
|
||||||
|
>
|
||||||
<q-list>
|
<q-list>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-input
|
<q-input
|
||||||
|
|
@ -581,6 +652,7 @@ onMounted(async () => {
|
||||||
{{ $t('general.noData') }}
|
{{ $t('general.noData') }}
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item
|
<q-item
|
||||||
|
v-else
|
||||||
v-for="(person, i) in userList"
|
v-for="(person, i) in userList"
|
||||||
dense
|
dense
|
||||||
:key="i"
|
:key="i"
|
||||||
|
|
@ -655,6 +727,7 @@ onMounted(async () => {
|
||||||
{{ $t('personnel.MESSENGER') }}
|
{{ $t('personnel.MESSENGER') }}
|
||||||
</span>
|
</span>
|
||||||
<q-item
|
<q-item
|
||||||
|
dense
|
||||||
clickable
|
clickable
|
||||||
@click="step.messengerByArea = !step.messengerByArea"
|
@click="step.messengerByArea = !step.messengerByArea"
|
||||||
class="column"
|
class="column"
|
||||||
|
|
@ -670,9 +743,49 @@ onMounted(async () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
|
||||||
|
<span class="text-caption app-text-muted-2 q-px-md">
|
||||||
|
{{ $t('general.group') }}
|
||||||
|
</span>
|
||||||
|
<q-item
|
||||||
|
v-if="groupList.length === 0"
|
||||||
|
class="app-text-muted q-px-lg"
|
||||||
|
>
|
||||||
|
{{ $t('general.noData') }}
|
||||||
|
</q-item>
|
||||||
|
<q-item
|
||||||
|
v-else
|
||||||
|
v-for="(group, i) in groupList"
|
||||||
|
dense
|
||||||
|
clickable
|
||||||
|
@click="selectResponsibleGroup(index, group.name)"
|
||||||
|
class="column"
|
||||||
|
>
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-checkbox
|
||||||
|
size="xs"
|
||||||
|
:model-value="
|
||||||
|
step.responsibleGroup.includes(group.name)
|
||||||
|
"
|
||||||
|
@click.stop="
|
||||||
|
selectResponsibleGroup(index, group.name)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<q-avatar class="q-ml-sm" size="md">
|
||||||
|
<q-img
|
||||||
|
class="text-center"
|
||||||
|
:ratio="1"
|
||||||
|
:src="`/img-group.png`"
|
||||||
|
/>
|
||||||
|
</q-avatar>
|
||||||
|
<div class="column q-pl-md">
|
||||||
|
<span>{{ group.name }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-menu>
|
</q-menu>
|
||||||
</q-select>
|
</q-field>
|
||||||
|
|
||||||
<!-- RESPONSIBLE-AGENCIES, RESPONSIBLE-INSTITUTION -->
|
<!-- RESPONSIBLE-AGENCIES, RESPONSIBLE-INSTITUTION -->
|
||||||
<q-select
|
<q-select
|
||||||
|
|
@ -815,4 +928,11 @@ onMounted(async () => {
|
||||||
:deep(.q-dialog.fullscreen.no-pointer-events.q-dialog--modal) {
|
:deep(.q-dialog.fullscreen.no-pointer-events.q-dialog--modal) {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.transition-rotate {
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
.rotated {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -244,16 +244,19 @@ withDefaults(
|
||||||
<template v-if="col.name === '#calcVat'">
|
<template v-if="col.name === '#calcVat'">
|
||||||
<q-checkbox
|
<q-checkbox
|
||||||
v-if="priceDisplay?.price && props.rowIndex === 0"
|
v-if="priceDisplay?.price && props.rowIndex === 0"
|
||||||
|
:disable="readonly"
|
||||||
v-model="calcVat"
|
v-model="calcVat"
|
||||||
size="xs"
|
size="xs"
|
||||||
/>
|
/>
|
||||||
<q-checkbox
|
<q-checkbox
|
||||||
v-if="priceDisplay?.agentPrice && props.rowIndex === 1"
|
v-if="priceDisplay?.agentPrice && props.rowIndex === 1"
|
||||||
|
:disable="readonly"
|
||||||
v-model="agentPriceCalcVat"
|
v-model="agentPriceCalcVat"
|
||||||
size="xs"
|
size="xs"
|
||||||
/>
|
/>
|
||||||
<q-checkbox
|
<q-checkbox
|
||||||
v-if="priceDisplay?.serviceCharge && props.rowIndex === 2"
|
v-if="priceDisplay?.serviceCharge && props.rowIndex === 2"
|
||||||
|
:disable="readonly"
|
||||||
v-model="serviceChargeCalcVat"
|
v-model="serviceChargeCalcVat"
|
||||||
size="xs"
|
size="xs"
|
||||||
/>
|
/>
|
||||||
|
|
@ -271,6 +274,8 @@ withDefaults(
|
||||||
flat
|
flat
|
||||||
outlined
|
outlined
|
||||||
dense
|
dense
|
||||||
|
:readonly
|
||||||
|
:hide-dropdown-icon="readonly"
|
||||||
v-model="vatIncluded"
|
v-model="vatIncluded"
|
||||||
></q-select>
|
></q-select>
|
||||||
<q-select
|
<q-select
|
||||||
|
|
@ -285,6 +290,8 @@ withDefaults(
|
||||||
flat
|
flat
|
||||||
outlined
|
outlined
|
||||||
dense
|
dense
|
||||||
|
:readonly
|
||||||
|
:hide-dropdown-icon="readonly"
|
||||||
v-model="agentPriceVatIncluded"
|
v-model="agentPriceVatIncluded"
|
||||||
></q-select>
|
></q-select>
|
||||||
<q-select
|
<q-select
|
||||||
|
|
@ -299,6 +306,8 @@ withDefaults(
|
||||||
flat
|
flat
|
||||||
outlined
|
outlined
|
||||||
dense
|
dense
|
||||||
|
:readonly
|
||||||
|
:hide-dropdown-icon="readonly"
|
||||||
v-model="serviceChargeVatIncluded"
|
v-model="serviceChargeVatIncluded"
|
||||||
></q-select>
|
></q-select>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@ defineProps<{
|
||||||
const group = defineModel('group', { default: '' });
|
const group = defineModel('group', { default: '' });
|
||||||
const name = defineModel('name', { default: '' });
|
const name = defineModel('name', { default: '' });
|
||||||
const nameEn = defineModel('nameEn', { default: '' });
|
const nameEn = defineModel('nameEn', { default: '' });
|
||||||
|
const contactName = defineModel('contactName', { default: '' });
|
||||||
|
const email = defineModel('email', { default: '' });
|
||||||
|
const contactTel = defineModel('contactTel', { default: '' });
|
||||||
|
|
||||||
type Options = { label: string; value: string };
|
type Options = { label: string; value: string };
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -35,7 +38,7 @@ type Options = { label: string; value: string };
|
||||||
|
|
||||||
<div class="col-12 row q-col-gutter-sm">
|
<div class="col-12 row q-col-gutter-sm">
|
||||||
<SelectInput
|
<SelectInput
|
||||||
:class="{ col: $q.screen.lt.md }"
|
class="col"
|
||||||
:disable="!readonly && onDrawer"
|
:disable="!readonly && onDrawer"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
for="input-agencies-code"
|
for="input-agencies-code"
|
||||||
|
|
@ -62,7 +65,7 @@ type Options = { label: string; value: string };
|
||||||
outlined
|
outlined
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
hide-bottom-space
|
hide-bottom-space
|
||||||
class="col-md col-12"
|
class="col-md-4 col-12"
|
||||||
:label="$t('agencies.name')"
|
:label="$t('agencies.name')"
|
||||||
v-model="name"
|
v-model="name"
|
||||||
:rules="[(val: string) => !!val || $t('form.error.required')]"
|
:rules="[(val: string) => !!val || $t('form.error.required')]"
|
||||||
|
|
@ -73,10 +76,78 @@ type Options = { label: string; value: string };
|
||||||
outlined
|
outlined
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
hide-bottom-space
|
hide-bottom-space
|
||||||
class="col-md col-12"
|
class="col-md-4 col-12"
|
||||||
:label="'Agencies Name'"
|
:label="'Agencies Name'"
|
||||||
v-model="nameEn"
|
v-model="nameEn"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
for="input-agencies-contact-name"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
:readonly="readonly"
|
||||||
|
hide-bottom-space
|
||||||
|
class="col-md-4 col-12"
|
||||||
|
:label="$t('agencies.contactName')"
|
||||||
|
:model-value="readonly ? contactName || '-' : contactName"
|
||||||
|
@update:model-value="
|
||||||
|
(v) => (typeof v === 'string' ? (contactName = v) : '')
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
for="input-agencies-email"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
hide-bottom-space
|
||||||
|
:readonly="readonly"
|
||||||
|
:label="$t('form.email')"
|
||||||
|
:rules="
|
||||||
|
readonly
|
||||||
|
? undefined
|
||||||
|
: [
|
||||||
|
(v: string) =>
|
||||||
|
!v ||
|
||||||
|
/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/g.test(v) ||
|
||||||
|
$t('form.error.invalid'),
|
||||||
|
]
|
||||||
|
"
|
||||||
|
class="col-md-4 col-12"
|
||||||
|
:model-value="readonly ? email || '-' : email"
|
||||||
|
@update:model-value="(v) => (typeof v === 'string' ? (email = v) : '')"
|
||||||
|
@clear="email = ''"
|
||||||
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<q-icon
|
||||||
|
size="xs"
|
||||||
|
name="mdi-email-outline"
|
||||||
|
class="cursor-pointer"
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
<q-input
|
||||||
|
for="input-agencies-contact-tel"
|
||||||
|
id="input-agencies-contact-tel"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
:readonly="readonly"
|
||||||
|
hide-bottom-space
|
||||||
|
class="col-md-4 col-12"
|
||||||
|
:label="$t('agencies.contactTel')"
|
||||||
|
:model-value="readonly ? contactTel || '-' : contactTel"
|
||||||
|
@update:model-value="
|
||||||
|
(v) => (typeof v === 'string' ? (contactTel = v) : '')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<q-icon
|
||||||
|
size="xs"
|
||||||
|
name="mdi-phone-outline"
|
||||||
|
class="cursor-pointer"
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { useQuasar } from 'quasar';
|
||||||
import SignaturePad from 'signature_pad';
|
import SignaturePad from 'signature_pad';
|
||||||
import Cropper from 'cropperjs';
|
import Cropper from 'cropperjs';
|
||||||
|
|
||||||
defineExpose({ clearCanvas, clearUpload });
|
defineExpose({ setCanvas, getCanvas, clearCanvas, clearUpload });
|
||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const isDarkActive = computed(() => $q.dark.isActive);
|
const isDarkActive = computed(() => $q.dark.isActive);
|
||||||
|
|
@ -18,7 +18,7 @@ const cropper = ref();
|
||||||
|
|
||||||
const tab = ref('draw');
|
const tab = ref('draw');
|
||||||
const uploadFile = ref<File | undefined>(undefined);
|
const uploadFile = ref<File | undefined>(undefined);
|
||||||
const profileUrl = ref<string | null>('');
|
const imgUrl = ref<string | null>('');
|
||||||
const inputFile = (() => {
|
const inputFile = (() => {
|
||||||
const element = document.createElement('input');
|
const element = document.createElement('input');
|
||||||
element.type = 'file';
|
element.type = 'file';
|
||||||
|
|
@ -26,7 +26,7 @@ const inputFile = (() => {
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.addEventListener('load', () => {
|
reader.addEventListener('load', () => {
|
||||||
if (typeof reader.result === 'string') profileUrl.value = reader.result;
|
if (typeof reader.result === 'string') imgUrl.value = reader.result;
|
||||||
});
|
});
|
||||||
|
|
||||||
element.addEventListener('change', () => {
|
element.addEventListener('change', () => {
|
||||||
|
|
@ -39,12 +39,11 @@ const inputFile = (() => {
|
||||||
return element;
|
return element;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
async function initializeSignaturePad(canva?: HTMLCanvasElement) {
|
async function initializeSignaturePad() {
|
||||||
if (canva) {
|
const canvas = canvasRef.value;
|
||||||
signaturePad.value = new SignaturePad(canva, {
|
|
||||||
backgroundColor: isDarkActive.value
|
if (canvas) {
|
||||||
? 'rgb(21,25,29)'
|
signaturePad.value = new SignaturePad(canvas, {
|
||||||
: 'rgb(248,249,250)',
|
|
||||||
penColor: 'blue',
|
penColor: 'blue',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -77,34 +76,34 @@ function changeColor(color: string) {
|
||||||
currentColor.value = color;
|
currentColor.value = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setCanvas() {
|
||||||
|
const data = signaturePad.value.toDataURL('image/png');
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCanvas(signature: string) {
|
||||||
|
signaturePad.value.fromDataURL(signature);
|
||||||
|
}
|
||||||
|
|
||||||
function clearCanvas() {
|
function clearCanvas() {
|
||||||
signaturePad.value.clear();
|
signaturePad.value.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearUpload() {
|
function clearUpload() {
|
||||||
profileUrl.value = '';
|
imgUrl.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
|
||||||
() => tab.value,
|
|
||||||
async () => {
|
|
||||||
await initializeSignaturePad(canvasRef.value);
|
|
||||||
await initializeCropper(imageRef.value);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await initializeSignaturePad(canvasRef.value);
|
await initializeSignaturePad();
|
||||||
await initializeCropper(imageRef.value);
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="surface-1 bordered rounded full-width">
|
<div class="surface-1 column full-width full-height">
|
||||||
<q-tabs
|
<q-tabs
|
||||||
v-model="tab"
|
v-model="tab"
|
||||||
dense
|
dense
|
||||||
align="left"
|
align="left"
|
||||||
class="text-grey"
|
class="text-grey surface-2"
|
||||||
active-color="primary"
|
active-color="primary"
|
||||||
indicator-color="primary"
|
indicator-color="primary"
|
||||||
>
|
>
|
||||||
|
|
@ -112,18 +111,18 @@ onMounted(async () => {
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<q-tab
|
<q-tab
|
||||||
name="draw"
|
name="draw"
|
||||||
label="Draw"
|
:label="$t('general.draw')"
|
||||||
style="border-top-left-radius: var(--radius-2)"
|
style="border-top-left-radius: var(--radius-2)"
|
||||||
/>
|
/>
|
||||||
<q-tab name="upload" label="Upload" />
|
<q-tab name="upload" :label="$t('general.upload')" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="q-pr-md">
|
<div class="q-pr-md">
|
||||||
<q-btn
|
<q-btn
|
||||||
|
v-if="tab === 'upload'"
|
||||||
dense
|
dense
|
||||||
flat
|
flat
|
||||||
v-if="tab === 'upload'"
|
:label="$t('general.newUpload')"
|
||||||
:label="$t('newUpload')"
|
|
||||||
color="info"
|
color="info"
|
||||||
@click="inputFile.click()"
|
@click="inputFile.click()"
|
||||||
/>
|
/>
|
||||||
|
|
@ -132,89 +131,66 @@ onMounted(async () => {
|
||||||
</q-tabs>
|
</q-tabs>
|
||||||
<q-separator />
|
<q-separator />
|
||||||
|
|
||||||
<div v-show="tab === 'draw'" class="q-pa-md">
|
<section v-show="tab === 'draw'" class="q-pa-md col">
|
||||||
<div class="column relative-position">
|
<div class="column relative-position">
|
||||||
<div class="absolute-top-right q-ma-md q-gutter-x-md row items-center">
|
<article
|
||||||
|
class="absolute-top-right q-ma-md q-gutter-x-md row items-center"
|
||||||
|
>
|
||||||
<span
|
<span
|
||||||
|
v-for="color in ['black', 'red', 'blue']"
|
||||||
|
:key="color"
|
||||||
|
:class="{ active: currentColor === color }"
|
||||||
class="dot"
|
class="dot"
|
||||||
:class="{ active: currentColor === 'black' }"
|
:style="`background-color: ${color}`"
|
||||||
style="background-color: black"
|
@click="changeColor(color)"
|
||||||
@click="changeColor('black')"
|
|
||||||
>
|
>
|
||||||
<q-icon
|
<q-icon
|
||||||
v-if="currentColor === 'black'"
|
v-if="currentColor === color"
|
||||||
name="mdi-check"
|
name="mdi-check"
|
||||||
color="white"
|
color="white"
|
||||||
size="sm"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span
|
</article>
|
||||||
:class="{ active: currentColor === 'red' }"
|
|
||||||
class="dot"
|
|
||||||
style="background-color: red"
|
|
||||||
@click="changeColor('red')"
|
|
||||||
>
|
|
||||||
<q-icon
|
|
||||||
v-if="currentColor === 'red'"
|
|
||||||
name="mdi-check"
|
|
||||||
color="white"
|
|
||||||
size="sm"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
:class="{ active: currentColor === 'blue' }"
|
|
||||||
class="dot"
|
|
||||||
style="background-color: blue"
|
|
||||||
@click="changeColor('blue')"
|
|
||||||
>
|
|
||||||
<q-icon
|
|
||||||
v-if="currentColor === 'blue'"
|
|
||||||
name="mdi-check"
|
|
||||||
color="white"
|
|
||||||
size="sm"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<canvas
|
<canvas
|
||||||
class="signature-canvas"
|
class="signature-canvas"
|
||||||
ref="canvasRef"
|
ref="canvasRef"
|
||||||
id="signature-pad"
|
id="signature-pad"
|
||||||
width="700"
|
width="766"
|
||||||
height="310"
|
height="364"
|
||||||
></canvas>
|
></canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
<div v-show="tab === 'upload'" class="q-pa-md">
|
<section v-show="tab === 'upload'" class="q-pa-md col">
|
||||||
<div
|
<div
|
||||||
class="bordered upload-border rounded column items-center justify-center"
|
class="bordered upload-border rounded column items-center justify-center full-height"
|
||||||
style="height: 312px"
|
|
||||||
>
|
>
|
||||||
<q-img
|
<q-img
|
||||||
v-show="profileUrl"
|
v-show="imgUrl"
|
||||||
ref="imageRef"
|
ref="imageRef"
|
||||||
:src="profileUrl ?? ''"
|
:src="imgUrl ?? ''"
|
||||||
style="object-fit: cover; width: 100%; height: 100%"
|
style="object-fit: cover; width: 100%; height: 100%"
|
||||||
/>
|
/>
|
||||||
<div v-if="!profileUrl">
|
<div v-if="!imgUrl">
|
||||||
<q-icon
|
<q-icon
|
||||||
name="mdi-cloud-upload"
|
name="mdi-cloud-upload"
|
||||||
size="10rem"
|
size="10rem"
|
||||||
style="color: hsla(var(--text-mute) / 0.2)"
|
style="color: hsla(var(--text-mute) / 0.2)"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div class="text-center">
|
||||||
<q-btn
|
<q-btn
|
||||||
unelevated
|
unelevated
|
||||||
color="info"
|
color="info"
|
||||||
:label="$t('uploadFile')"
|
:label="$t('general.upload')"
|
||||||
icon="mdi-plus"
|
icon="mdi-plus"
|
||||||
@click="inputFile.click()"
|
@click="inputFile.click()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { Icon } from '@iconify/vue/dist/iconify.js';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
hideIcon?: boolean;
|
hideIcon?: boolean;
|
||||||
|
icon?: string;
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -10,10 +13,13 @@ defineProps<{
|
||||||
v-if="!hideIcon"
|
v-if="!hideIcon"
|
||||||
id="btn-add"
|
id="btn-add"
|
||||||
padding="sm"
|
padding="sm"
|
||||||
icon="mdi-plus"
|
:icon="icon ? undefined : 'mdi-plus'"
|
||||||
direction="up"
|
direction="up"
|
||||||
class="color-btn"
|
class="color-btn"
|
||||||
>
|
>
|
||||||
|
<template #icon v-if="icon">
|
||||||
|
<Icon :icon width="24" />
|
||||||
|
</template>
|
||||||
<slot>
|
<slot>
|
||||||
<q-fab-action
|
<q-fab-action
|
||||||
padding="xs"
|
padding="xs"
|
||||||
|
|
@ -29,10 +35,12 @@ defineProps<{
|
||||||
fab
|
fab
|
||||||
id="btn-add"
|
id="btn-add"
|
||||||
padding="sm"
|
padding="sm"
|
||||||
icon="mdi-plus"
|
:icon="icon ? undefined : 'mdi-plus'"
|
||||||
direction="up"
|
direction="up"
|
||||||
class="color-btn"
|
class="color-btn"
|
||||||
/>
|
>
|
||||||
|
<Icon v-if="icon" :icon width="24" />
|
||||||
|
</q-btn>
|
||||||
</q-page-sticky>
|
</q-page-sticky>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
import MainButton from './MainButton.vue';
|
import MainButton from './MainButton.vue';
|
||||||
|
|
||||||
defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'click', v: MouseEvent): void;
|
(e: 'click', v: MouseEvent): void;
|
||||||
|
(e: 'fileSelected', v: File[]): void;
|
||||||
}>();
|
}>();
|
||||||
defineProps<{
|
defineProps<{
|
||||||
iconOnly?: boolean;
|
iconOnly?: boolean;
|
||||||
|
|
@ -10,15 +12,29 @@ defineProps<{
|
||||||
outlined?: boolean;
|
outlined?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
dark?: boolean;
|
dark?: boolean;
|
||||||
|
importFile?: boolean;
|
||||||
|
|
||||||
label?: string;
|
label?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const inputRef = ref<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
|
function triggerFileInput() {
|
||||||
|
inputRef.value?.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFileChange(event: Event) {
|
||||||
|
const files = (event.target as HTMLInputElement).files;
|
||||||
|
if (files && files.length > 0) {
|
||||||
|
emit('fileSelected', Array.from(files));
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MainButton
|
<MainButton
|
||||||
@click="(e) => $emit('click', e)"
|
@click="(e) => (importFile ? triggerFileInput() : $emit('click', e))"
|
||||||
v-bind="{ ...$props, ...$attrs }"
|
v-bind="{ ...$props, ...$attrs }"
|
||||||
:icon="icon || 'mdi-import'"
|
:icon="icon || 'mdi-import'"
|
||||||
color="var(--info-bg)"
|
color="var(--info-bg)"
|
||||||
|
|
@ -26,4 +42,13 @@ defineProps<{
|
||||||
>
|
>
|
||||||
{{ label || $t('general.import') }}
|
{{ label || $t('general.import') }}
|
||||||
</MainButton>
|
</MainButton>
|
||||||
|
|
||||||
|
<input
|
||||||
|
ref="inputRef"
|
||||||
|
type="file"
|
||||||
|
@change="(e) => handleFileChange(e)"
|
||||||
|
hidden
|
||||||
|
accept=".xls, .xlsx , .csv"
|
||||||
|
multiple
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
32
src/components/button/PasteButton.vue
Normal file
32
src/components/button/PasteButton.vue
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import MainButton from './MainButton.vue';
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
(e: 'click', v: MouseEvent): void;
|
||||||
|
}>();
|
||||||
|
defineProps<{
|
||||||
|
iconOnly?: boolean;
|
||||||
|
solid?: boolean;
|
||||||
|
outlined?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
dark?: boolean;
|
||||||
|
|
||||||
|
label?: string;
|
||||||
|
icon?: string;
|
||||||
|
|
||||||
|
amount?: number;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<MainButton
|
||||||
|
@click="(e) => $emit('click', e)"
|
||||||
|
v-bind="{ ...$props, ...$attrs }"
|
||||||
|
:icon="icon || 'mdi-file-replace'"
|
||||||
|
color="207 96% 32%"
|
||||||
|
:title="iconOnly ? $t('general.paste') : undefined"
|
||||||
|
>
|
||||||
|
{{ label || $t('general.paste') }}
|
||||||
|
{{ amount && amount > 0 ? `(${amount})` : '' }}
|
||||||
|
</MainButton>
|
||||||
|
</template>
|
||||||
|
|
@ -14,3 +14,4 @@ export { default as PrintButton } from './PrintButton.vue';
|
||||||
export { default as StateButton } from './StateButton.vue';
|
export { default as StateButton } from './StateButton.vue';
|
||||||
export { default as NextButton } from './NextButton.vue';
|
export { default as NextButton } from './NextButton.vue';
|
||||||
export { default as ImportButton } from './ImportButton.vue';
|
export { default as ImportButton } from './ImportButton.vue';
|
||||||
|
export { default as PasteButton } from './PasteButton.vue';
|
||||||
|
|
|
||||||
189
src/components/shared/AdvanceSearch.vue
Normal file
189
src/components/shared/AdvanceSearch.vue
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
import { dateFormatJS } from 'src/utils/datetime';
|
||||||
|
import SelectInput from './SelectInput.vue';
|
||||||
|
import VueDatePicker from '@vuepic/vue-datepicker';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
active?: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const date = defineModel<string[]>();
|
||||||
|
|
||||||
|
const dateRange = ref<string>('');
|
||||||
|
const isDateSelect = ref(false);
|
||||||
|
|
||||||
|
function mapDateRange(val: string) {
|
||||||
|
const today = dayjs();
|
||||||
|
let start: dayjs.Dayjs, end: dayjs.Dayjs;
|
||||||
|
|
||||||
|
switch (val) {
|
||||||
|
case 'toDay':
|
||||||
|
start = today.startOf('day');
|
||||||
|
end = today.endOf('day');
|
||||||
|
break;
|
||||||
|
case 'yesterday':
|
||||||
|
start = today.subtract(1, 'day').startOf('day');
|
||||||
|
end = today.subtract(1, 'day').endOf('day');
|
||||||
|
break;
|
||||||
|
case 'thisWeek':
|
||||||
|
start = today.startOf('week');
|
||||||
|
end = today.endOf('week');
|
||||||
|
break;
|
||||||
|
case 'lastWeek':
|
||||||
|
start = today.subtract(1, 'week').startOf('week');
|
||||||
|
end = today.subtract(1, 'week').endOf('week');
|
||||||
|
break;
|
||||||
|
case 'thisMonth':
|
||||||
|
start = today.startOf('month');
|
||||||
|
end = today.endOf('month');
|
||||||
|
break;
|
||||||
|
case 'lastMonth':
|
||||||
|
start = today.subtract(1, 'month').startOf('month');
|
||||||
|
end = today.subtract(1, 'month').endOf('month');
|
||||||
|
break;
|
||||||
|
case 'thisYear':
|
||||||
|
start = today.startOf('year');
|
||||||
|
end = today.endOf('year');
|
||||||
|
break;
|
||||||
|
case 'lastYear':
|
||||||
|
start = today.subtract(1, 'year').startOf('year');
|
||||||
|
end = today.subtract(1, 'year').endOf('year');
|
||||||
|
break;
|
||||||
|
case 'last7Days':
|
||||||
|
start = today.subtract(6, 'day').startOf('day');
|
||||||
|
end = today.endOf('day');
|
||||||
|
break;
|
||||||
|
case 'last30Days':
|
||||||
|
start = today.subtract(29, 'day').startOf('day');
|
||||||
|
end = today.endOf('day');
|
||||||
|
break;
|
||||||
|
case 'last90Days':
|
||||||
|
start = today.subtract(89, 'day').startOf('day');
|
||||||
|
end = today.endOf('day');
|
||||||
|
break;
|
||||||
|
case 'customDateRange':
|
||||||
|
start = today.startOf('day');
|
||||||
|
end = today.endOf('day');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [start.toDate().toISOString(), end.toDate().toISOString()];
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => dateRange.value,
|
||||||
|
() => {
|
||||||
|
if (!dateRange.value) return;
|
||||||
|
date.value = mapDateRange(dateRange.value);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => date.value,
|
||||||
|
() => {
|
||||||
|
if (date.value && date.value.length === 0) dateRange.value = '';
|
||||||
|
},
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<q-btn
|
||||||
|
size="xs"
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
unelevated
|
||||||
|
icon="mdi-tune-variant"
|
||||||
|
:flat="active ? false : !dateRange"
|
||||||
|
:color="active || dateRange ? 'info' : undefined"
|
||||||
|
>
|
||||||
|
<q-menu
|
||||||
|
:offset="[5, 10]"
|
||||||
|
max-width="300px"
|
||||||
|
class="bordered"
|
||||||
|
:persistent="isDateSelect"
|
||||||
|
>
|
||||||
|
<div class="q-pa-sm">
|
||||||
|
<div class="text-weight-medium">
|
||||||
|
{{ $t('general.advanceSearch') }}
|
||||||
|
</div>
|
||||||
|
<SelectInput
|
||||||
|
v-model="dateRange"
|
||||||
|
:label="$t('general.period')"
|
||||||
|
:option="[
|
||||||
|
{ label: $t('dateRange.today'), value: 'toDay' },
|
||||||
|
{ label: $t('dateRange.yesterday'), value: 'yesterday' },
|
||||||
|
{ label: $t('dateRange.thisWeek'), value: 'thisWeek' },
|
||||||
|
{ label: $t('dateRange.lastWeek'), value: 'lastWeek' },
|
||||||
|
{ label: $t('dateRange.thisMonth'), value: 'thisMonth' },
|
||||||
|
{ label: $t('dateRange.lastMonth'), value: 'lastMonth' },
|
||||||
|
{ label: $t('dateRange.thisYear'), value: 'thisYear' },
|
||||||
|
{ label: $t('dateRange.lastYear'), value: 'lastYear' },
|
||||||
|
{ label: $t('dateRange.last7Days'), value: 'last7Days' },
|
||||||
|
{ label: $t('dateRange.last30Days'), value: 'last30Days' },
|
||||||
|
{ label: $t('dateRange.last90Days'), value: 'last90Days' },
|
||||||
|
{
|
||||||
|
label: $t('dateRange.customDateRange'),
|
||||||
|
value: 'customDateRange',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
clearable
|
||||||
|
@clear="() => (date = [])"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<VueDatePicker
|
||||||
|
v-if="dateRange === 'customDateRange'"
|
||||||
|
utc
|
||||||
|
range
|
||||||
|
teleport
|
||||||
|
auto-apply
|
||||||
|
for="select-date-range"
|
||||||
|
class="q-mt-sm"
|
||||||
|
v-model="date"
|
||||||
|
:dark="$q.dark.isActive"
|
||||||
|
:locale="$i18n.locale === 'tha' ? 'th' : 'en'"
|
||||||
|
@open="() => (isDateSelect = true)"
|
||||||
|
@closed="() => (isDateSelect = false)"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<q-input
|
||||||
|
placeholder="DD/MM/YYYY"
|
||||||
|
hide-bottom-space
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
for="select-date-range"
|
||||||
|
:model-value="
|
||||||
|
date
|
||||||
|
? dateFormatJS({ date: date[0] }) +
|
||||||
|
' - ' +
|
||||||
|
dateFormatJS({ date: date[1] })
|
||||||
|
: ''
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<q-icon name="mdi-calendar-outline" class="app-text-muted" />
|
||||||
|
</template>
|
||||||
|
<q-tooltip>
|
||||||
|
{{
|
||||||
|
date
|
||||||
|
? dateFormatJS({ date: date[0] }) +
|
||||||
|
' - ' +
|
||||||
|
dateFormatJS({ date: date[1] })
|
||||||
|
: ''
|
||||||
|
}}
|
||||||
|
</q-tooltip>
|
||||||
|
</q-input>
|
||||||
|
</template>
|
||||||
|
</VueDatePicker>
|
||||||
|
|
||||||
|
<slot></slot>
|
||||||
|
<!-- <SelectInput :label="$t('general.documentStatus')" :option="[]" /> -->
|
||||||
|
</div>
|
||||||
|
</q-menu>
|
||||||
|
<q-tooltip v-if="$q.screen.gt.sm">
|
||||||
|
{{ $t('general.advanceSearch') }}
|
||||||
|
</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</template>
|
||||||
|
|
@ -25,7 +25,11 @@ withDefaults(
|
||||||
alt="Image"
|
alt="Image"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="data.length > 3" class="avatar remaining-count">
|
<div
|
||||||
|
v-if="data.length > 3"
|
||||||
|
class="avatar remaining-count"
|
||||||
|
style="cursor: default"
|
||||||
|
>
|
||||||
<q-tooltip>
|
<q-tooltip>
|
||||||
<div v-for="(person, i) in data.slice(3)" :key="i + 3">
|
<div v-for="(person, i) in data.slice(3)" :key="i + 3">
|
||||||
{{ person.name }}
|
{{ person.name }}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ const props = withDefaults(
|
||||||
useUpload?: boolean;
|
useUpload?: boolean;
|
||||||
useCancel?: boolean;
|
useCancel?: boolean;
|
||||||
useRejectCancel?: boolean;
|
useRejectCancel?: boolean;
|
||||||
|
useCopy?: boolean;
|
||||||
disableCancel?: boolean;
|
disableCancel?: boolean;
|
||||||
disableDelete?: boolean;
|
disableDelete?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
|
|
@ -31,6 +32,7 @@ defineEmits<{
|
||||||
(e: 'link'): void;
|
(e: 'link'): void;
|
||||||
(e: 'upload'): void;
|
(e: 'upload'): void;
|
||||||
(e: 'delete'): void;
|
(e: 'delete'): void;
|
||||||
|
(e: 'copy'): void;
|
||||||
(e: 'cancel'): void;
|
(e: 'cancel'): void;
|
||||||
(e: 'rejectCancel'): void;
|
(e: 'rejectCancel'): void;
|
||||||
(e: 'changeStatus'): void;
|
(e: 'changeStatus'): void;
|
||||||
|
|
@ -172,6 +174,27 @@ watch(
|
||||||
</span>
|
</span>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
|
||||||
|
<q-item
|
||||||
|
v-if="useCopy"
|
||||||
|
v-close-popup
|
||||||
|
dense
|
||||||
|
clickable
|
||||||
|
class="row q-py-sm"
|
||||||
|
style="white-space: nowrap"
|
||||||
|
:id="`btn-kebab-copy-${idName}`"
|
||||||
|
@click.stop="() => $emit('copy')"
|
||||||
|
>
|
||||||
|
<q-icon
|
||||||
|
size="xs"
|
||||||
|
class="col-3"
|
||||||
|
name="mdi-content-copy"
|
||||||
|
style="color: hsl(var(--teal-5-hsl))"
|
||||||
|
/>
|
||||||
|
<span class="col-9 q-px-md flex items-center">
|
||||||
|
{{ $t('general.copy') }}
|
||||||
|
</span>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
<q-item
|
<q-item
|
||||||
v-if="useCancel"
|
v-if="useCancel"
|
||||||
v-close-popup
|
v-close-popup
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ const props = withDefaults(
|
||||||
disable?: boolean;
|
disable?: boolean;
|
||||||
multiple?: boolean;
|
multiple?: boolean;
|
||||||
hideInput?: boolean;
|
hideInput?: boolean;
|
||||||
|
hideDropdownIcon?: boolean;
|
||||||
|
|
||||||
rules?: ((value: string) => string | true)[];
|
rules?: ((value: string) => string | true)[];
|
||||||
}>(),
|
}>(),
|
||||||
|
|
@ -82,7 +83,7 @@ watch(
|
||||||
:hide-selected
|
:hide-selected
|
||||||
hide-bottom-space
|
hide-bottom-space
|
||||||
:fill-input="fillInput && !!model"
|
:fill-input="fillInput && !!model"
|
||||||
:hide-dropdown-icon="readonly"
|
:hide-dropdown-icon="readonly || hideDropdownIcon"
|
||||||
input-debounce="500"
|
input-debounce="500"
|
||||||
:option-value="
|
:option-value="
|
||||||
typeof props.optionValue === 'string' ? props.optionValue : 'value'
|
typeof props.optionValue === 'string' ? props.optionValue : 'value'
|
||||||
|
|
@ -103,6 +104,11 @@ watch(
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
:rules
|
:rules
|
||||||
|
@clear="
|
||||||
|
() => {
|
||||||
|
multiple ? (model = []) : (model = '');
|
||||||
|
}
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<template v-if="$slots.prepend" v-slot:prepend>
|
<template v-if="$slots.prepend" v-slot:prepend>
|
||||||
<slot name="prepend"></slot>
|
<slot name="prepend"></slot>
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,8 @@ onMounted(async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
await getSelectedOption();
|
await getSelectedOption();
|
||||||
|
|
||||||
|
valueOption.value = selectOptions.value.find((v) => v.id === value.value);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -158,7 +160,7 @@ onMounted(async () => {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #option="{ opt, scope }">
|
<template #option="{ opt, scope }">
|
||||||
<q-item v-bind="scope.itemProps">
|
<q-item @click="valueOption = opt" v-bind="scope.itemProps">
|
||||||
<SelectCustomerItem :data="opt" :simple :simple-branch-no />
|
<SelectCustomerItem :data="opt" :simple :simple-branch-no />
|
||||||
</q-item>
|
</q-item>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ const { getOptions, setFirstValue, getSelectedOption, filter } =
|
||||||
const ret = await getList({
|
const ret = await getList({
|
||||||
query: query === '' ? undefined : query,
|
query: query === '' ? undefined : query,
|
||||||
...props.params,
|
...props.params,
|
||||||
|
activeOnly: true,
|
||||||
});
|
});
|
||||||
if (ret) return ret.result;
|
if (ret) return ret.result;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,9 @@ const columns = [
|
||||||
field: (v: Employee) =>
|
field: (v: Employee) =>
|
||||||
locale.value === Lang.English
|
locale.value === Lang.English
|
||||||
? `${v.firstNameEN} ${v.lastNameEN}`
|
? `${v.firstNameEN} ${v.lastNameEN}`
|
||||||
: `${v.firstName} ${v.lastName}`,
|
: v.firstName
|
||||||
|
? `${v.firstName} ${v.lastName}`
|
||||||
|
: `${v.firstNameEN} ${v.lastNameEN}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'birthDate',
|
name: 'birthDate',
|
||||||
|
|
|
||||||
2
src/env.d.ts
vendored
2
src/env.d.ts
vendored
|
|
@ -1,5 +1,3 @@
|
||||||
/* eslint-disable */
|
|
||||||
|
|
||||||
declare namespace NodeJS {
|
declare namespace NodeJS {
|
||||||
interface ProcessEnv {
|
interface ProcessEnv {
|
||||||
NODE_ENV: string;
|
NODE_ENV: string;
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ export default {
|
||||||
branchStatus: 'Branch Status',
|
branchStatus: 'Branch Status',
|
||||||
success: 'Success',
|
success: 'Success',
|
||||||
taxNo: 'Legal Person',
|
taxNo: 'Legal Person',
|
||||||
contactName: 'Contact Name',
|
contactName: 'Contact Person',
|
||||||
image: 'Image of ',
|
image: 'Image of ',
|
||||||
apply: 'Apply',
|
apply: 'Apply',
|
||||||
licenseNumber: 'License number',
|
licenseNumber: 'License number',
|
||||||
|
|
@ -151,6 +151,15 @@ export default {
|
||||||
dueDate: 'Due date',
|
dueDate: 'Due date',
|
||||||
year: 'year',
|
year: 'year',
|
||||||
tableOfContent: 'Table of Contents',
|
tableOfContent: 'Table of Contents',
|
||||||
|
draw: 'Draw',
|
||||||
|
newUpload: 'New Upload',
|
||||||
|
nativeLanguage: '{msg} Native Language',
|
||||||
|
copy: 'Copy',
|
||||||
|
paste: 'Paste',
|
||||||
|
period: 'Period',
|
||||||
|
documentStatus: 'Document Status',
|
||||||
|
advanceSearch: 'Advance Search',
|
||||||
|
totalPeople: '{meg} people',
|
||||||
},
|
},
|
||||||
|
|
||||||
menu: {
|
menu: {
|
||||||
|
|
@ -245,7 +254,8 @@ export default {
|
||||||
|
|
||||||
manual: {
|
manual: {
|
||||||
title: 'Manual',
|
title: 'Manual',
|
||||||
usage: 'การใช้งาน',
|
usage: 'Usage',
|
||||||
|
troubleshooting: 'Troubleshooting',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -374,7 +384,7 @@ export default {
|
||||||
branchLabel: 'Branch',
|
branchLabel: 'Branch',
|
||||||
branchHQLabel: 'Headoffice',
|
branchHQLabel: 'Headoffice',
|
||||||
taxNo: 'Legal Person',
|
taxNo: 'Legal Person',
|
||||||
contactName: 'Contact Name',
|
contactName: 'Contact Person',
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
captionManage: 'Manage',
|
captionManage: 'Manage',
|
||||||
|
|
@ -395,8 +405,8 @@ export default {
|
||||||
code: 'Headoffice Code',
|
code: 'Headoffice Code',
|
||||||
codeBranch: 'Branch Code',
|
codeBranch: 'Branch Code',
|
||||||
taxNo: 'Tax Identification Number',
|
taxNo: 'Tax Identification Number',
|
||||||
contactName: 'Contact Name',
|
contactName: 'Contact Person',
|
||||||
contactTelephone: 'Contact Telephone',
|
contactTelephone: 'Contact Number',
|
||||||
branchName: 'Branch Name',
|
branchName: 'Branch Name',
|
||||||
branchNameEN: 'Branch Name (EN)',
|
branchNameEN: 'Branch Name (EN)',
|
||||||
servicePointName: 'Service Point Name',
|
servicePointName: 'Service Point Name',
|
||||||
|
|
@ -449,7 +459,7 @@ export default {
|
||||||
responsibleArea: 'Responsibel Area',
|
responsibleArea: 'Responsibel Area',
|
||||||
discount: 'Discount Condition',
|
discount: 'Discount Condition',
|
||||||
sourceNationality: 'Source Nationality',
|
sourceNationality: 'Source Nationality',
|
||||||
importNationality: 'import Nationality',
|
importNationality: 'Import Nationality',
|
||||||
trainingPlace: 'Training Place',
|
trainingPlace: 'Training Place',
|
||||||
checkpoint: 'Checkpoint',
|
checkpoint: 'Checkpoint',
|
||||||
checkpointEN: 'Checkpoint (EN)',
|
checkpointEN: 'Checkpoint (EN)',
|
||||||
|
|
@ -457,6 +467,12 @@ export default {
|
||||||
citizenId: 'Citizen ID',
|
citizenId: 'Citizen ID',
|
||||||
citizenIssue: 'Citizen Issue',
|
citizenIssue: 'Citizen Issue',
|
||||||
citizenExpire: 'Citizen Expire',
|
citizenExpire: 'Citizen Expire',
|
||||||
|
agencyStatus: 'Agency Status',
|
||||||
|
normal: 'Normal',
|
||||||
|
canceled: 'Canceled',
|
||||||
|
blacklist: 'Black list',
|
||||||
|
contactName: 'Contact Person',
|
||||||
|
contactTel: 'Contact Number',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
customer: {
|
customer: {
|
||||||
|
|
@ -470,7 +486,6 @@ export default {
|
||||||
powerOfAttorney: 'Power of Attorney',
|
powerOfAttorney: 'Power of Attorney',
|
||||||
others: 'Others',
|
others: 'Others',
|
||||||
},
|
},
|
||||||
|
|
||||||
employer: 'Employer',
|
employer: 'Employer',
|
||||||
employerLegalEntity: 'Legal Entity',
|
employerLegalEntity: 'Legal Entity',
|
||||||
employerNaturalPerson: 'Natrual Person',
|
employerNaturalPerson: 'Natrual Person',
|
||||||
|
|
@ -492,15 +507,12 @@ export default {
|
||||||
religion: 'Religion',
|
religion: 'Religion',
|
||||||
issueDate: 'Issue Date',
|
issueDate: 'Issue Date',
|
||||||
passportExpiryDate: 'Passport Expiry Date',
|
passportExpiryDate: 'Passport Expiry Date',
|
||||||
|
|
||||||
ownerName: 'Customer Name',
|
ownerName: 'Customer Name',
|
||||||
firstName: 'First Name ',
|
firstName: 'First Name ',
|
||||||
lastName: 'Last Name ',
|
lastName: 'Last Name ',
|
||||||
firstNameEN: 'First Name in English',
|
firstNameEN: 'First Name in English',
|
||||||
lastNameEN: 'Last Name in English',
|
lastNameEN: 'Last Name in English',
|
||||||
|
|
||||||
cardNumber: 'ID Card Number',
|
cardNumber: 'ID Card Number',
|
||||||
|
|
||||||
prefixName: 'Prefix',
|
prefixName: 'Prefix',
|
||||||
legalPersonNo: 'Legal Entity Registration Number',
|
legalPersonNo: 'Legal Entity Registration Number',
|
||||||
registerName: 'Company Name',
|
registerName: 'Company Name',
|
||||||
|
|
@ -508,7 +520,6 @@ export default {
|
||||||
registerDate: 'Registered On',
|
registerDate: 'Registered On',
|
||||||
registerCompanyName: 'Registered Name',
|
registerCompanyName: 'Registered Name',
|
||||||
authorizedCapital: 'Authorized Capital',
|
authorizedCapital: 'Authorized Capital',
|
||||||
|
|
||||||
workplace: 'Workplace',
|
workplace: 'Workplace',
|
||||||
workplaceEN: 'Workplace (EN)',
|
workplaceEN: 'Workplace (EN)',
|
||||||
address: 'Address',
|
address: 'Address',
|
||||||
|
|
@ -516,7 +527,6 @@ export default {
|
||||||
branchCode: 'Branch Code',
|
branchCode: 'Branch Code',
|
||||||
customerCode: 'Employer Code',
|
customerCode: 'Employer Code',
|
||||||
legalPersonCode: 'Legal Entity Code',
|
legalPersonCode: 'Legal Entity Code',
|
||||||
|
|
||||||
codeAbbrev: 'Company Abbreviation',
|
codeAbbrev: 'Company Abbreviation',
|
||||||
codeNumber: 'Company Number',
|
codeNumber: 'Company Number',
|
||||||
registeredBranch: 'Registered Branch',
|
registeredBranch: 'Registered Branch',
|
||||||
|
|
@ -554,7 +564,7 @@ export default {
|
||||||
jobPosition: 'Job Position',
|
jobPosition: 'Job Position',
|
||||||
address: 'Address',
|
address: 'Address',
|
||||||
workPlace: 'Workplace',
|
workPlace: 'Workplace',
|
||||||
contactName: 'Contact Name',
|
contactName: 'Contact Person',
|
||||||
contactPhone: 'Contact Phone',
|
contactPhone: 'Contact Phone',
|
||||||
totalEmployee: 'Total Employee',
|
totalEmployee: 'Total Employee',
|
||||||
officeTel: 'Headoffice Telephone',
|
officeTel: 'Headoffice Telephone',
|
||||||
|
|
@ -792,7 +802,7 @@ export default {
|
||||||
employee: 'Employee',
|
employee: 'Employee',
|
||||||
employeeName: 'Full Name',
|
employeeName: 'Full Name',
|
||||||
workName: 'Work Name',
|
workName: 'Work Name',
|
||||||
contactName: 'Contact Name',
|
contactName: 'Contact Person',
|
||||||
documentReceivePoint: 'Document Drop-Off Point"',
|
documentReceivePoint: 'Document Drop-Off Point"',
|
||||||
dueDate: 'Quotation Due Date',
|
dueDate: 'Quotation Due Date',
|
||||||
specialCondition: 'Special Conditions',
|
specialCondition: 'Special Conditions',
|
||||||
|
|
@ -874,7 +884,7 @@ export default {
|
||||||
SplitCustom: 'Custom Installments Bill',
|
SplitCustom: 'Custom Installments Bill',
|
||||||
BillFull: 'Full Amount Bill',
|
BillFull: 'Full Amount Bill',
|
||||||
BillSplit: 'Installments Bill',
|
BillSplit: 'Installments Bill',
|
||||||
BillCustomSplit: 'Custom Installments Bill',
|
BillSplitCustom: 'Custom Installments Bill',
|
||||||
},
|
},
|
||||||
|
|
||||||
status: {
|
status: {
|
||||||
|
|
@ -910,6 +920,9 @@ export default {
|
||||||
code: 'Agencies Code',
|
code: 'Agencies Code',
|
||||||
group: 'Agencies Group',
|
group: 'Agencies Group',
|
||||||
name: 'Agencies Name',
|
name: 'Agencies Name',
|
||||||
|
contactName: 'Contact Person',
|
||||||
|
contactTel: 'Contact Number',
|
||||||
|
bankInfo: 'Bank Information',
|
||||||
},
|
},
|
||||||
|
|
||||||
requestList: {
|
requestList: {
|
||||||
|
|
@ -931,8 +944,9 @@ export default {
|
||||||
localEmployee: 'Local Employee',
|
localEmployee: 'Local Employee',
|
||||||
nonLocalEmployee: 'Non Local Employee',
|
nonLocalEmployee: 'Non Local Employee',
|
||||||
noWorkflowTemplate: 'A workflow template has not been selected.',
|
noWorkflowTemplate: 'A workflow template has not been selected.',
|
||||||
|
|
||||||
salesRepresentative: 'Sales Representative',
|
salesRepresentative: 'Sales Representative',
|
||||||
|
|
||||||
|
dataOffice: 'Employment Office District',
|
||||||
ref: 'Reference',
|
ref: 'Reference',
|
||||||
action: {
|
action: {
|
||||||
title: 'Action',
|
title: 'Action',
|
||||||
|
|
@ -996,7 +1010,7 @@ export default {
|
||||||
issueBranch: 'Issue Branch',
|
issueBranch: 'Issue Branch',
|
||||||
issueDate: 'Issue Date',
|
issueDate: 'Issue Date',
|
||||||
madeBy: 'Made By',
|
madeBy: 'Made By',
|
||||||
contactName: 'Contact Name',
|
contactName: 'Contact Person',
|
||||||
workOrderCode: 'Work Order Code',
|
workOrderCode: 'Work Order Code',
|
||||||
workOrderName: 'Work Order Name',
|
workOrderName: 'Work Order Name',
|
||||||
telephone: 'Telephone',
|
telephone: 'Telephone',
|
||||||
|
|
@ -1055,6 +1069,10 @@ export default {
|
||||||
confirmDebitNoteAccept: 'Confirm acceptance of the debit note.',
|
confirmDebitNoteAccept: 'Confirm acceptance of the debit note.',
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
|
copy: 'Copy',
|
||||||
|
warningPaste:
|
||||||
|
'Do you want to replace the data with the newly copied information?',
|
||||||
|
warningCopyEmpty: 'You have not copied any data yet',
|
||||||
quotationAccept: 'Once accepted, no further modifications can be made',
|
quotationAccept: 'Once accepted, no further modifications can be made',
|
||||||
beingUse: '"{msg}" is being used.',
|
beingUse: '"{msg}" is being used.',
|
||||||
incompleteDataEntry: 'Incomplete data entry on {tap} page',
|
incompleteDataEntry: 'Incomplete data entry on {tap} page',
|
||||||
|
|
@ -1072,10 +1090,8 @@ export default {
|
||||||
confirmSavingStatus:
|
confirmSavingStatus:
|
||||||
'Do you want to confirm the saving of the status change data?',
|
'Do you want to confirm the saving of the status change data?',
|
||||||
confirmSending: 'Do you confirm the submission of the task?',
|
confirmSending: 'Do you confirm the submission of the task?',
|
||||||
confirmValidate: 'Do you confirm the validation?',
|
|
||||||
warningSelectDeliveryStaff:
|
warningSelectDeliveryStaff:
|
||||||
'You have not yet selected a document delivery staff.',
|
'You have not yet selected a document delivery staff.',
|
||||||
|
|
||||||
confirmEndWorkWarning:
|
confirmEndWorkWarning:
|
||||||
"Do you want to end the work now? The current statuses 'Pending', 'In Progress', 'To Be Reprocessed' will be changed to 'Redo All'.",
|
"Do you want to end the work now? The current statuses 'Pending', 'In Progress', 'To Be Reprocessed' will be changed to 'Redo All'.",
|
||||||
confirmEndWork: 'Do you want to end the work?',
|
confirmEndWork: 'Do you want to end the work?',
|
||||||
|
|
@ -1183,13 +1199,14 @@ export default {
|
||||||
'Product with the same name already exists. If you want to create with this name please select another code.',
|
'Product with the same name already exists. If you want to create with this name please select another code.',
|
||||||
userExists: 'User already exits.',
|
userExists: 'User already exits.',
|
||||||
sameNameExists: 'Same name exists.',
|
sameNameExists: 'Same name exists.',
|
||||||
|
samePropertyNameExists: 'Same property name exists.',
|
||||||
|
|
||||||
validateError: 'Validate Error',
|
validateError: 'Validate Error',
|
||||||
codeMisMatch: 'Code Mismatch',
|
codeMisMatch: 'Code Mismatch',
|
||||||
crossCompanyNotPermit: 'Cannot move between different headoffice',
|
crossCompanyNotPermit: 'Cannot move between different headoffice',
|
||||||
errorOccure:
|
errorOccurred:
|
||||||
'An error has occurred, causing the system to be unable to function. Please try again later.',
|
'An error has occurred, causing the system to be unable to function. Please try again later.',
|
||||||
invalideData: 'The information is incorrect. Please try again later.',
|
invalidData: 'The information is incorrect. Please try again later.',
|
||||||
authFailed: 'Authentication Failed. Please try again later. ',
|
authFailed: 'Authentication Failed. Please try again later. ',
|
||||||
installmentsValidateFailed:
|
installmentsValidateFailed:
|
||||||
'Validation failed. Each installment must include at least one product. Please review and update the installments accordingly.',
|
'Validation failed. Each installment must include at least one product. Please review and update the installments accordingly.',
|
||||||
|
|
@ -1476,4 +1493,19 @@ export default {
|
||||||
type: 'Type',
|
type: 'Type',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
dateRange: {
|
||||||
|
today: 'Today',
|
||||||
|
yesterday: 'Yesterday',
|
||||||
|
thisWeek: 'This Week',
|
||||||
|
lastWeek: 'Last Week',
|
||||||
|
thisMonth: 'This Month',
|
||||||
|
lastMonth: 'Last Month',
|
||||||
|
thisYear: 'This Year',
|
||||||
|
lastYear: 'Last Year',
|
||||||
|
last7Days: 'Last 7 Days',
|
||||||
|
last30Days: 'Last 30 Days',
|
||||||
|
last90Days: 'Last 90 Days',
|
||||||
|
customDateRange: 'Custom Date Range',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import eng from './eng';
|
import eng from './eng';
|
||||||
import tha from './tha';
|
import tha from './tha'; // spellchecker:disable-line
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
eng,
|
eng,
|
||||||
tha,
|
tha, // spellchecker:disable-line
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,15 @@ export default {
|
||||||
dueDate: 'วันครบกำหนด',
|
dueDate: 'วันครบกำหนด',
|
||||||
year: 'ปี',
|
year: 'ปี',
|
||||||
tableOfContent: 'สารบัญ',
|
tableOfContent: 'สารบัญ',
|
||||||
|
draw: 'วาด',
|
||||||
|
newUpload: 'อัปโหลดใหม่',
|
||||||
|
nativeLanguage: '{msg} ภาษาต้นทาง',
|
||||||
|
copy: 'คัดลอก',
|
||||||
|
paste: 'วาง',
|
||||||
|
period: 'ช่วงเวลา',
|
||||||
|
documentStatus: 'สถานะเอกสาร',
|
||||||
|
advanceSearch: 'ค้นหาขั้นสูง',
|
||||||
|
totalPeople: '{meg} คน',
|
||||||
},
|
},
|
||||||
|
|
||||||
menu: {
|
menu: {
|
||||||
|
|
@ -246,6 +255,7 @@ export default {
|
||||||
manual: {
|
manual: {
|
||||||
title: 'คู่มือ',
|
title: 'คู่มือ',
|
||||||
usage: 'การใช้งาน',
|
usage: 'การใช้งาน',
|
||||||
|
troubleshooting: 'การแก้ปัญหา',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -453,6 +463,12 @@ export default {
|
||||||
citizenId: 'เลขที่บัตรประชาชน',
|
citizenId: 'เลขที่บัตรประชาชน',
|
||||||
citizenIssue: 'วันที่ออกบัตร',
|
citizenIssue: 'วันที่ออกบัตร',
|
||||||
citizenExpire: 'วันที่หมดอายุ',
|
citizenExpire: 'วันที่หมดอายุ',
|
||||||
|
agencyStatus: 'สถานะเอเจนซี่',
|
||||||
|
normal: 'ปกติ',
|
||||||
|
canceled: 'ยกเลิก',
|
||||||
|
blacklist: 'แบล็คลิสต์',
|
||||||
|
contactName: 'ชื่อผู้ติดต่อ',
|
||||||
|
contactTel: 'เบอร์โทรศัพท์ผู้ติดต่อ',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
customer: {
|
customer: {
|
||||||
|
|
@ -571,7 +587,7 @@ export default {
|
||||||
family: 'ข้อมูลครอบครัว',
|
family: 'ข้อมูลครอบครัว',
|
||||||
},
|
},
|
||||||
workerStatus: 'สถานะคนงาน',
|
workerStatus: 'สถานะคนงาน',
|
||||||
previousPassportNumber: 'หมายเลขอันเก่าหนังสือเดินทาง',
|
previousPassportNumber: 'หมายเลขหนังสือเดินทางเล่มเก่า',
|
||||||
employerBranch: 'สาขานายจ้าง',
|
employerBranch: 'สาขานายจ้าง',
|
||||||
employeeCode: 'รหัสลูกจ้าง',
|
employeeCode: 'รหัสลูกจ้าง',
|
||||||
nrcNo: 'เลขบัตรประจำตัวคนซึ่งไม่มีสัญชาติไทย (N.R.C No.)',
|
nrcNo: 'เลขบัตรประจำตัวคนซึ่งไม่มีสัญชาติไทย (N.R.C No.)',
|
||||||
|
|
@ -778,7 +794,7 @@ export default {
|
||||||
branch: 'สาขาที่ออกใบเสนอราคา',
|
branch: 'สาขาที่ออกใบเสนอราคา',
|
||||||
branchVirtual: 'จุดรับบริการที่ออกใบเสนอราคา',
|
branchVirtual: 'จุดรับบริการที่ออกใบเสนอราคา',
|
||||||
customer: 'ลูกค้า',
|
customer: 'ลูกค้า',
|
||||||
newCustomer: 'แรงงานใหม่',
|
newCustomer: 'ลูกค้าใหม่',
|
||||||
employeeList: 'รายชื่อแรงงาน',
|
employeeList: 'รายชื่อแรงงาน',
|
||||||
employee: 'แรงงาน',
|
employee: 'แรงงาน',
|
||||||
employeeName: 'ชื่อ-นามสกุล แรงงาน',
|
employeeName: 'ชื่อ-นามสกุล แรงงาน',
|
||||||
|
|
@ -901,6 +917,9 @@ export default {
|
||||||
code: 'รหัสหน่วยงาน',
|
code: 'รหัสหน่วยงาน',
|
||||||
group: 'กลุ่มหน่วยงาน',
|
group: 'กลุ่มหน่วยงาน',
|
||||||
name: 'ชื่อหน่วยงาน',
|
name: 'ชื่อหน่วยงาน',
|
||||||
|
contactName: 'ชื่อผู้ติดต่อ',
|
||||||
|
contactTel: 'เบอร์โทรผู้ติดต่อ',
|
||||||
|
bankInfo: 'ข้อมูลธนาคาร',
|
||||||
},
|
},
|
||||||
|
|
||||||
requestList: {
|
requestList: {
|
||||||
|
|
@ -922,6 +941,7 @@ export default {
|
||||||
nonLocalEmployee: 'พนักงานนอกพื้นที่',
|
nonLocalEmployee: 'พนักงานนอกพื้นที่',
|
||||||
noWorkflowTemplate: 'คุณไม่ได้เลือกแม่แบบขั้นตอนการทำงาน',
|
noWorkflowTemplate: 'คุณไม่ได้เลือกแม่แบบขั้นตอนการทำงาน',
|
||||||
salesRepresentative: 'พนักงานขาย',
|
salesRepresentative: 'พนักงานขาย',
|
||||||
|
dataOffice: 'สำนักงานเขตจัดหางาน',
|
||||||
ref: 'อ้างอิง',
|
ref: 'อ้างอิง',
|
||||||
action: {
|
action: {
|
||||||
title: 'จัดการ',
|
title: 'จัดการ',
|
||||||
|
|
@ -1040,6 +1060,9 @@ export default {
|
||||||
confirmDebitNoteAccept: 'ยืนยันการตอบรับใบเพิ่มหนี้',
|
confirmDebitNoteAccept: 'ยืนยันการตอบรับใบเพิ่มหนี้',
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
|
copy: 'คัดลอก',
|
||||||
|
warningPaste: 'คุณต้องการที่จะเเทนที่ข้อมูลที่คัดลอกมาใหม่ใช่หรือไม่',
|
||||||
|
warningCopyEmpty: 'คุณยังไม่ได้คัดลอกข้อมูล',
|
||||||
quotationAccept: 'เมื่อตอบรับเเล้วจะไม่สามารถแก้ไขได้อีก',
|
quotationAccept: 'เมื่อตอบรับเเล้วจะไม่สามารถแก้ไขได้อีก',
|
||||||
beingUse: '"{msg}" มีการใช้งานอยู่',
|
beingUse: '"{msg}" มีการใช้งานอยู่',
|
||||||
incompleteDataEntry: 'กรอกข้อมูลไม่ครบในหน้า {tap}',
|
incompleteDataEntry: 'กรอกข้อมูลไม่ครบในหน้า {tap}',
|
||||||
|
|
@ -1161,13 +1184,14 @@ export default {
|
||||||
'สินค้าที่มีชื่อเดียวกันมีในระบบแล้ว หากคุณต้องการสร้างด้วยชื่อนี้โปรดเลือกรหัสอื่น',
|
'สินค้าที่มีชื่อเดียวกันมีในระบบแล้ว หากคุณต้องการสร้างด้วยชื่อนี้โปรดเลือกรหัสอื่น',
|
||||||
userExists: 'ชื่อผู้ใช้นี้มีอยู่ในระบบอยู่แล้ว',
|
userExists: 'ชื่อผู้ใช้นี้มีอยู่ในระบบอยู่แล้ว',
|
||||||
sameNameExists: 'ชื่อนี้ถูกใช้ไปแล้ว',
|
sameNameExists: 'ชื่อนี้ถูกใช้ไปแล้ว',
|
||||||
|
samePropertyNameExists: 'คุณสมบัตินี้มีอยู่ในระบบอยู่แล้ว',
|
||||||
|
|
||||||
validateError: 'เกิดข้อผิดพลาดจากการตรวจสอบ',
|
validateError: 'เกิดข้อผิดพลาดจากการตรวจสอบ',
|
||||||
codeMisMatch: 'รหัสไม่ตรงกัน',
|
codeMisMatch: 'รหัสไม่ตรงกัน',
|
||||||
crossCompanyNotPermit: 'ไม่สามารถดำเนินการระหว่างสำนักงานใหญ่อื่นได้',
|
crossCompanyNotPermit: 'ไม่สามารถดำเนินการระหว่างสำนักงานใหญ่อื่นได้',
|
||||||
errorOccure:
|
errorOccurred:
|
||||||
'เกิดข้อผิดพลาดทำให้ระบบไม่สามารถทำงานได้ กรุณาลองใหม่ในภายหลัง',
|
'เกิดข้อผิดพลาดทำให้ระบบไม่สามารถทำงานได้ กรุณาลองใหม่ในภายหลัง',
|
||||||
invalideData: 'ข้อมูลไม่ถูกต้อง กรุณาตรวจสอบใหม่อีกครั้ง',
|
invalidData: 'ข้อมูลไม่ถูกต้อง กรุณาตรวจสอบใหม่อีกครั้ง',
|
||||||
authFailed: 'การยืนยันตัวตนล้มเหลว กรุณาลองใหม่ในภายหลัง',
|
authFailed: 'การยืนยันตัวตนล้มเหลว กรุณาลองใหม่ในภายหลัง',
|
||||||
installmentsValidateFailed:
|
installmentsValidateFailed:
|
||||||
'ข้อมูลงวดไม่ถูกต้อง กรุณาตรวจสอบและยืนยันว่าแต่ละงวดมีสินค้าอย่างน้อยหนึ่งรายการ',
|
'ข้อมูลงวดไม่ถูกต้อง กรุณาตรวจสอบและยืนยันว่าแต่ละงวดมีสินค้าอย่างน้อยหนึ่งรายการ',
|
||||||
|
|
@ -1457,4 +1481,19 @@ export default {
|
||||||
type: 'ประเภท',
|
type: 'ประเภท',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
dateRange: {
|
||||||
|
today: 'วันนี้',
|
||||||
|
yesterday: 'เมื่อวานนี้',
|
||||||
|
thisWeek: 'สัปดาห์นี้',
|
||||||
|
lastWeek: 'สัปดาห์ที่แล้ว',
|
||||||
|
thisMonth: 'เดือนนี้',
|
||||||
|
lastMonth: 'เดือนที่แล้ว',
|
||||||
|
thisYear: 'ปีนี้',
|
||||||
|
lastYear: 'ปีที่แล้ว',
|
||||||
|
last7Days: '7 วันที่ผ่านมา',
|
||||||
|
last30Days: '30 วันที่ผ่านมา',
|
||||||
|
last90Days: '90 วันที่ผ่านมา',
|
||||||
|
customDateRange: 'กำหนดช่วงวันที่เอง',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -215,6 +215,10 @@ function initMenu() {
|
||||||
label: 'usage',
|
label: 'usage',
|
||||||
route: '/manual',
|
route: '/manual',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'troubleshooting',
|
||||||
|
route: '/troubleshooting',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,6 @@ const leftDrawerMini = ref(false);
|
||||||
const unread = computed<number>(
|
const unread = computed<number>(
|
||||||
() => notificationData.value.filter((v) => !v.read).length || 0,
|
() => notificationData.value.filter((v) => !v.read).length || 0,
|
||||||
);
|
);
|
||||||
// const filterRole = ref<string[]>();
|
|
||||||
const userImage = ref<string>();
|
const userImage = ref<string>();
|
||||||
const userGender = ref('');
|
const userGender = ref('');
|
||||||
const canvasRef = ref();
|
const canvasRef = ref();
|
||||||
|
|
@ -124,6 +123,17 @@ function readNoti(id: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function signatureSubmit() {
|
||||||
|
const signature = canvasRef.value.setCanvas();
|
||||||
|
userStore.setSignature(signature);
|
||||||
|
canvasModal.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function signatureFetch() {
|
||||||
|
const ret = await userStore.getSignature();
|
||||||
|
if (ret) canvasRef.value.getCanvas(ret);
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
initTheme();
|
initTheme();
|
||||||
initLang();
|
initLang();
|
||||||
|
|
@ -367,12 +377,28 @@ onMounted(async () => {
|
||||||
<div class="col column text-caption q-pl-md ellipsis">
|
<div class="col column text-caption q-pl-md ellipsis">
|
||||||
<span class="block ellipsis full-width text-weight-bold">
|
<span class="block ellipsis full-width text-weight-bold">
|
||||||
{{ item.title }}
|
{{ item.title }}
|
||||||
|
<q-tooltip
|
||||||
|
anchor="top middle"
|
||||||
|
self="bottom middle"
|
||||||
|
:delay="300"
|
||||||
|
:offset="[10, 10]"
|
||||||
|
>
|
||||||
|
{{ item.title }}
|
||||||
|
</q-tooltip>
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
class="block ellipsis full-width text-stone"
|
class="block ellipsis full-width text-stone"
|
||||||
:class="{ 'text-weight-medium': !item.read }"
|
:class="{ 'text-weight-medium': !item.read }"
|
||||||
>
|
>
|
||||||
{{ item.detail }}
|
{{ item.detail }}
|
||||||
|
<q-tooltip
|
||||||
|
anchor="top middle"
|
||||||
|
self="bottom middle"
|
||||||
|
:delay="300"
|
||||||
|
:offset="[10, 10]"
|
||||||
|
>
|
||||||
|
{{ item.detail }}
|
||||||
|
</q-tooltip>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
|
|
@ -382,15 +408,6 @@ onMounted(async () => {
|
||||||
>
|
>
|
||||||
{{ moment(item.createdAt).fromNow() }}
|
{{ moment(item.createdAt).fromNow() }}
|
||||||
</span>
|
</span>
|
||||||
<q-tooltip
|
|
||||||
anchor="top middle"
|
|
||||||
self="bottom middle"
|
|
||||||
:delay="1000"
|
|
||||||
:offset="[10, 10]"
|
|
||||||
>
|
|
||||||
{{ item.title }}
|
|
||||||
{{ item.detail }}
|
|
||||||
</q-tooltip>
|
|
||||||
</q-item>
|
</q-item>
|
||||||
</section>
|
</section>
|
||||||
<section
|
<section
|
||||||
|
|
@ -495,13 +512,15 @@ onMounted(async () => {
|
||||||
no-app-box
|
no-app-box
|
||||||
:title="$t('menu.profile.addSignature')"
|
:title="$t('menu.profile.addSignature')"
|
||||||
:close="() => (canvasModal = false)"
|
:close="() => (canvasModal = false)"
|
||||||
|
:submit="signatureSubmit"
|
||||||
|
:show="signatureFetch"
|
||||||
>
|
>
|
||||||
<CanvasComponent ref="canvasRef" v-model:modal="canvasModal" />
|
<CanvasComponent ref="canvasRef" v-model:modal="canvasModal" />
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<q-btn
|
<q-btn
|
||||||
flat
|
flat
|
||||||
dense
|
dense
|
||||||
:label="$t('clear')"
|
:label="$t('general.clear')"
|
||||||
@click="
|
@click="
|
||||||
() => {
|
() => {
|
||||||
canvasRef.clearCanvas(), canvasRef.clearUpload();
|
canvasRef.clearCanvas(), canvasRef.clearUpload();
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ const options = [
|
||||||
label: 'menu.profile.signature',
|
label: 'menu.profile.signature',
|
||||||
value: 'signature',
|
value: 'signature',
|
||||||
color: 'grey',
|
color: 'grey',
|
||||||
disabled: true,
|
disabled: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'mdi-brightness-6',
|
icon: 'mdi-brightness-6',
|
||||||
|
|
|
||||||
2
src/markdown-it.d.ts
vendored
Normal file
2
src/markdown-it.d.ts
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
declare module 'markdown-it-image-figures';
|
||||||
|
declare module 'markdown-it-html5-media';
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// NOTE: Library
|
// NOTE: Library
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { onMounted } from 'vue';
|
import { onMounted, watch } from 'vue';
|
||||||
|
|
||||||
// NOTE: Components
|
// NOTE: Components
|
||||||
|
|
||||||
|
|
@ -10,22 +10,33 @@ import { onMounted } from 'vue';
|
||||||
import { useManualStore } from 'src/stores/manual';
|
import { useManualStore } from 'src/stores/manual';
|
||||||
import { useNavigator } from 'src/stores/navigator';
|
import { useNavigator } from 'src/stores/navigator';
|
||||||
import { Icon } from '@iconify/vue/dist/iconify.js';
|
import { Icon } from '@iconify/vue/dist/iconify.js';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
// NOTE: Variable
|
// NOTE: Variable
|
||||||
|
const route = useRoute();
|
||||||
const manualStore = useManualStore();
|
const manualStore = useManualStore();
|
||||||
const navigatorStore = useNavigator();
|
const navigatorStore = useNavigator();
|
||||||
const { dataManual } = storeToRefs(manualStore);
|
const { dataManual, dataTroubleshooting } = storeToRefs(manualStore);
|
||||||
|
|
||||||
async function fetchManual() {
|
|
||||||
const res = await manualStore.getManual();
|
|
||||||
dataManual.value = res ? res : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
navigatorStore.current.title = 'menu.manual.title';
|
navigatorStore.current.title = 'menu.manual.title';
|
||||||
navigatorStore.current.path = [{ text: '' }];
|
navigatorStore.current.path = [{ text: '' }];
|
||||||
await fetchManual();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.name,
|
||||||
|
async () => {
|
||||||
|
if (route.name === 'Manual') {
|
||||||
|
const res = await manualStore.getManual();
|
||||||
|
dataManual.value = res ? res : [];
|
||||||
|
}
|
||||||
|
if (route.name === 'Troubleshooting') {
|
||||||
|
const res = await manualStore.getTroubleshooting();
|
||||||
|
dataTroubleshooting.value = res ? res : [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -34,7 +45,7 @@ onMounted(async () => {
|
||||||
>
|
>
|
||||||
<section class="scroll q-gutter-y-sm">
|
<section class="scroll q-gutter-y-sm">
|
||||||
<q-expansion-item
|
<q-expansion-item
|
||||||
v-for="v in dataManual"
|
v-for="v in $route.name === 'Manual' ? dataManual : dataTroubleshooting"
|
||||||
:key="v.labelEN"
|
:key="v.labelEN"
|
||||||
:content-inset-level="0.5"
|
:content-inset-level="0.5"
|
||||||
class="rounded overflow-hidden bordered"
|
class="rounded overflow-hidden bordered"
|
||||||
|
|
@ -58,7 +69,11 @@ onMounted(async () => {
|
||||||
clickable
|
clickable
|
||||||
dense
|
dense
|
||||||
class="dot items-center rounded q-my-xs"
|
class="dot items-center rounded q-my-xs"
|
||||||
:to="`/manual/${v.category}/${x.name}`"
|
:to="
|
||||||
|
$route.name === 'Manual'
|
||||||
|
? `/manual/${v.category}/${x.name}`
|
||||||
|
: `/troubleshooting/${v.category}/${x.name}`
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
v-if="!!x.icon"
|
v-if="!!x.icon"
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,7 @@ import hljs from 'highlight.js';
|
||||||
import { nextTick, onMounted, onUnmounted, ref } from 'vue';
|
import { nextTick, onMounted, onUnmounted, ref } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
// @ts-expect-error
|
|
||||||
import mditFigureWithPCaption from 'markdown-it-image-figures';
|
import mditFigureWithPCaption from 'markdown-it-image-figures';
|
||||||
// @ts-expect-error
|
|
||||||
import mditMedia from 'markdown-it-html5-media';
|
import mditMedia from 'markdown-it-html5-media';
|
||||||
import mditAnchor from 'markdown-it-anchor';
|
import mditAnchor from 'markdown-it-anchor';
|
||||||
import mditHighlight from 'markdown-it-highlightjs';
|
import mditHighlight from 'markdown-it-highlightjs';
|
||||||
|
|
@ -58,6 +56,8 @@ onUnmounted(() => {
|
||||||
|
|
||||||
async function getContent() {
|
async function getContent() {
|
||||||
if (!category.value || !page.value) return;
|
if (!category.value || !page.value) return;
|
||||||
|
|
||||||
|
if (ROUTE.name === 'ManualView') {
|
||||||
const res = await manualStore.getManualByPage({
|
const res = await manualStore.getManualByPage({
|
||||||
category: category.value,
|
category: category.value,
|
||||||
pageName: page.value,
|
pageName: page.value,
|
||||||
|
|
@ -68,6 +68,18 @@ async function getContent() {
|
||||||
contentParsed.value = md.parse(text, {});
|
contentParsed.value = md.parse(text, {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ROUTE.name === 'TroubleshootingView') {
|
||||||
|
const res = await manualStore.getTroubleshootingByPage({
|
||||||
|
category: category.value,
|
||||||
|
pageName: page.value,
|
||||||
|
});
|
||||||
|
if (res && res.ok) {
|
||||||
|
const text = await res.text();
|
||||||
|
content.value = text;
|
||||||
|
contentParsed.value = md.parse(text, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onScroll() {
|
function onScroll() {
|
||||||
let current = '';
|
let current = '';
|
||||||
|
|
@ -186,7 +198,9 @@ async function scrollTo(id: string) {
|
||||||
md.render(
|
md.render(
|
||||||
content.replaceAll(
|
content.replaceAll(
|
||||||
'assets/',
|
'assets/',
|
||||||
`${baseUrl}/manual/${category}/assets/`,
|
$route.name === 'ManualView'
|
||||||
|
? `${baseUrl}/manual/${category}/assets/`
|
||||||
|
: `${baseUrl}/troubleshooting/${category}/assets/`,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
|
@ -317,4 +331,20 @@ async function scrollTo(id: string) {
|
||||||
.markdown :deep(video) {
|
.markdown :deep(video) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.markdown :deep(table) {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown :deep(:where(table th)) {
|
||||||
|
background: var(--surface-2);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown :deep(:where(table td, table th)) {
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
padding: 0.25rem 1rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { Icon } from '@iconify/vue';
|
||||||
import { BranchContact } from 'stores/branch-contact/types';
|
import { BranchContact } from 'stores/branch-contact/types';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import type { QSelect, QTableProps, QTableSlots } from 'quasar';
|
import type { QTableProps, QTableSlots } from 'quasar';
|
||||||
import { resetScrollBar } from 'src/stores/utils';
|
import { resetScrollBar } from 'src/stores/utils';
|
||||||
import useBranchStore from 'stores/branch';
|
import useBranchStore from 'stores/branch';
|
||||||
import useFlowStore from 'stores/flow';
|
import useFlowStore from 'stores/flow';
|
||||||
|
|
@ -52,6 +52,7 @@ import {
|
||||||
UndoButton,
|
UndoButton,
|
||||||
} from 'components/button';
|
} from 'components/button';
|
||||||
import { useNavigator } from 'src/stores/navigator';
|
import { useNavigator } from 'src/stores/navigator';
|
||||||
|
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
@ -72,7 +73,6 @@ const typeBranchItem = [
|
||||||
color: 'var(--blue-6-hsl)',
|
color: 'var(--blue-6-hsl)',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const refFilter = ref<InstanceType<typeof QSelect>>();
|
|
||||||
const holdDialog = ref(false);
|
const holdDialog = ref(false);
|
||||||
const isSubCreate = ref(false);
|
const isSubCreate = ref(false);
|
||||||
const columns = [
|
const columns = [
|
||||||
|
|
@ -175,6 +175,8 @@ const qrCodeDialog = ref(false);
|
||||||
const qrCodeimageUrl = ref<string>('');
|
const qrCodeimageUrl = ref<string>('');
|
||||||
const formLastSubBranch = ref<number>(0);
|
const formLastSubBranch = ref<number>(0);
|
||||||
|
|
||||||
|
const searchDate = ref<string[]>([]);
|
||||||
|
|
||||||
const branchStore = useBranchStore();
|
const branchStore = useBranchStore();
|
||||||
const flowStore = useFlowStore();
|
const flowStore = useFlowStore();
|
||||||
const { locale } = useI18n();
|
const { locale } = useI18n();
|
||||||
|
|
@ -715,12 +717,20 @@ async function fetchList(opts: {
|
||||||
tree?: boolean;
|
tree?: boolean;
|
||||||
withHead?: boolean;
|
withHead?: boolean;
|
||||||
filter?: 'head' | 'sub';
|
filter?: 'head' | 'sub';
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string;
|
||||||
}) {
|
}) {
|
||||||
await branchStore.fetchList(opts);
|
await branchStore.fetchList(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(inputSearch, () => {
|
watch([inputSearch, searchDate], () => {
|
||||||
fetchList({ tree: true, query: inputSearch.value, withHead: true });
|
fetchList({
|
||||||
|
tree: true,
|
||||||
|
query: inputSearch.value,
|
||||||
|
withHead: true,
|
||||||
|
startDate: searchDate.value[0],
|
||||||
|
endDate: searchDate.value[1],
|
||||||
|
});
|
||||||
currentSubBranch.value = undefined;
|
currentSubBranch.value = undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1170,26 +1180,49 @@ watch(currentHq, () => {
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<q-icon name="mdi-magnify" />
|
<q-icon name="mdi-magnify" />
|
||||||
</template>
|
</template>
|
||||||
<template v-if="$q.screen.lt.md" v-slot:append>
|
<template v-slot:append>
|
||||||
<span class="row">
|
<q-separator vertical inset class="q-mr-xs" />
|
||||||
<q-separator vertical />
|
|
||||||
<q-btn
|
<AdvanceSearch
|
||||||
icon="mdi-filter-variant"
|
v-model="searchDate"
|
||||||
unelevated
|
:active="$q.screen.lt.md && statusFilter !== 'all'"
|
||||||
class="q-ml-sm"
|
>
|
||||||
padding="4px"
|
<div
|
||||||
size="sm"
|
v-if="$q.screen.lt.md"
|
||||||
rounded
|
class="q-mt-sm text-weight-medium"
|
||||||
@click="refFilter?.showPopup"
|
>
|
||||||
|
{{ $t('general.status') }}
|
||||||
|
</div>
|
||||||
|
<q-select
|
||||||
|
v-if="$q.screen.lt.md"
|
||||||
|
v-model="statusFilter"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
autocomplete="off"
|
||||||
|
option-value="value"
|
||||||
|
option-label="label"
|
||||||
|
map-options
|
||||||
|
emit-value
|
||||||
|
:for="'field-select-status'"
|
||||||
|
:options="[
|
||||||
|
{ label: $t('general.all'), value: 'all' },
|
||||||
|
{
|
||||||
|
label: $t('status.ACTIVE'),
|
||||||
|
value: 'statusACTIVE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('status.INACTIVE'),
|
||||||
|
value: 'statusINACTIVE',
|
||||||
|
},
|
||||||
|
]"
|
||||||
/>
|
/>
|
||||||
</span>
|
</AdvanceSearch>
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<div class="row col-md-6 justify-end">
|
<div class="row col-md-6 justify-end">
|
||||||
<q-select
|
<q-select
|
||||||
v-show="$q.screen.gt.sm"
|
v-if="$q.screen.gt.sm"
|
||||||
ref="refFilter"
|
|
||||||
v-model="statusFilter"
|
v-model="statusFilter"
|
||||||
outlined
|
outlined
|
||||||
dense
|
dense
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ import useOptionStore from 'stores/options';
|
||||||
import useAddressStore from 'stores/address';
|
import useAddressStore from 'stores/address';
|
||||||
import useMyBranch from 'src/stores/my-branch';
|
import useMyBranch from 'src/stores/my-branch';
|
||||||
import { calculateAge } from 'src/utils/datetime';
|
import { calculateAge } from 'src/utils/datetime';
|
||||||
import { QSelect, useQuasar, type QTableProps } from 'quasar';
|
import { useQuasar, type QTableProps } from 'quasar';
|
||||||
import { dialog, baseUrl } from 'stores/utils';
|
import { dialog, baseUrl, setPrefixName } from 'stores/utils';
|
||||||
import { useNavigator } from 'src/stores/navigator';
|
import { useNavigator } from 'src/stores/navigator';
|
||||||
import { isRoleInclude, resetScrollBar } from 'src/stores/utils';
|
import { isRoleInclude, resetScrollBar } from 'src/stores/utils';
|
||||||
import { BranchUserStats } from 'stores/branch/types';
|
import { BranchUserStats } from 'stores/branch/types';
|
||||||
|
|
@ -49,6 +49,7 @@ import FormPerson from 'components/02_personnel-management/FormPerson.vue';
|
||||||
import FormByType from 'components/02_personnel-management/FormByType.vue';
|
import FormByType from 'components/02_personnel-management/FormByType.vue';
|
||||||
import FormInformation from 'components/02_personnel-management/FormInformation.vue';
|
import FormInformation from 'components/02_personnel-management/FormInformation.vue';
|
||||||
import PaginationPageSize from 'src/components/PaginationPageSize.vue';
|
import PaginationPageSize from 'src/components/PaginationPageSize.vue';
|
||||||
|
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
||||||
|
|
||||||
const { locale, t } = useI18n();
|
const { locale, t } = useI18n();
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
|
|
@ -73,7 +74,6 @@ const isImageEdit = ref(false);
|
||||||
const imageDialog = ref(false);
|
const imageDialog = ref(false);
|
||||||
const infoDrawerEdit = ref(false);
|
const infoDrawerEdit = ref(false);
|
||||||
const refreshImageState = ref(false);
|
const refreshImageState = ref(false);
|
||||||
const refFilter = ref<InstanceType<typeof QSelect>>();
|
|
||||||
const firstScroll = ref(false);
|
const firstScroll = ref(false);
|
||||||
|
|
||||||
const inputSearch = ref('');
|
const inputSearch = ref('');
|
||||||
|
|
@ -93,12 +93,14 @@ const currentUser = ref<User>();
|
||||||
const userCode = ref<string>();
|
const userCode = ref<string>();
|
||||||
|
|
||||||
const statusToggle = ref(true);
|
const statusToggle = ref(true);
|
||||||
const agencyFile = ref<File[]>([]);
|
const userFile = ref<File[]>([]);
|
||||||
const agencyFileList = ref<{ name: string; url: string }[]>([]);
|
const userFileList = ref<{ name: string; url: string }[]>([]);
|
||||||
|
|
||||||
const typeStats = ref<UserTypeStats>();
|
const typeStats = ref<UserTypeStats>();
|
||||||
const userStats = ref<BranchUserStats[]>();
|
const userStats = ref<BranchUserStats[]>();
|
||||||
|
|
||||||
|
const searchDate = ref<[]>([]);
|
||||||
|
|
||||||
const urlProfile = ref<string>();
|
const urlProfile = ref<string>();
|
||||||
const profileFileImg = ref<File | null>(null);
|
const profileFileImg = ref<File | null>(null);
|
||||||
const imageList = ref<{ selectedImage: string; list: string[] }>();
|
const imageList = ref<{ selectedImage: string; list: string[] }>();
|
||||||
|
|
@ -124,7 +126,7 @@ const defaultFormData = {
|
||||||
streetEN: '',
|
streetEN: '',
|
||||||
street: '',
|
street: '',
|
||||||
trainingPlace: null,
|
trainingPlace: null,
|
||||||
importNationality: null,
|
importNationality: [],
|
||||||
sourceNationality: null,
|
sourceNationality: null,
|
||||||
licenseExpireDate: null,
|
licenseExpireDate: null,
|
||||||
licenseIssueDate: null,
|
licenseIssueDate: null,
|
||||||
|
|
@ -151,6 +153,10 @@ const defaultFormData = {
|
||||||
citizenExpire: null,
|
citizenExpire: null,
|
||||||
citizenIssue: null,
|
citizenIssue: null,
|
||||||
citizenId: '',
|
citizenId: '',
|
||||||
|
contactName: '',
|
||||||
|
contactTel: '',
|
||||||
|
remark: '',
|
||||||
|
agencyStatus: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
const formData = ref<UserCreate>({
|
const formData = ref<UserCreate>({
|
||||||
|
|
@ -172,7 +178,7 @@ const formData = ref<UserCreate>({
|
||||||
streetEN: '',
|
streetEN: '',
|
||||||
street: '',
|
street: '',
|
||||||
trainingPlace: null,
|
trainingPlace: null,
|
||||||
importNationality: null,
|
importNationality: [],
|
||||||
sourceNationality: null,
|
sourceNationality: null,
|
||||||
licenseExpireDate: null,
|
licenseExpireDate: null,
|
||||||
licenseIssueDate: null,
|
licenseIssueDate: null,
|
||||||
|
|
@ -199,6 +205,10 @@ const formData = ref<UserCreate>({
|
||||||
citizenExpire: null,
|
citizenExpire: null,
|
||||||
citizenIssue: null,
|
citizenIssue: null,
|
||||||
citizenId: '',
|
citizenId: '',
|
||||||
|
contactName: '',
|
||||||
|
contactTel: '',
|
||||||
|
remark: '',
|
||||||
|
agencyStatus: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const fieldSelectedOption = ref<{ label: string; value: string }[]>([
|
const fieldSelectedOption = ref<{ label: string; value: string }[]>([
|
||||||
|
|
@ -327,7 +337,7 @@ function onClose(excludeDialog?: boolean) {
|
||||||
urlProfile.value = '';
|
urlProfile.value = '';
|
||||||
profileFileImg.value = null;
|
profileFileImg.value = null;
|
||||||
infoDrawerEdit.value = false;
|
infoDrawerEdit.value = false;
|
||||||
agencyFile.value = [];
|
userFile.value = [];
|
||||||
isEdit.value = false;
|
isEdit.value = false;
|
||||||
statusToggle.value = true;
|
statusToggle.value = true;
|
||||||
isImageEdit.value = false;
|
isImageEdit.value = false;
|
||||||
|
|
@ -336,6 +346,8 @@ function onClose(excludeDialog?: boolean) {
|
||||||
mapUserType(currentTab.value);
|
mapUserType(currentTab.value);
|
||||||
imageList.value = { selectedImage: '', list: [] };
|
imageList.value = { selectedImage: '', list: [] };
|
||||||
onCreateImageList.value = { selectedImage: '', list: [] };
|
onCreateImageList.value = { selectedImage: '', list: [] };
|
||||||
|
userFileList.value = [];
|
||||||
|
userFile.value = [];
|
||||||
flowStore.rotate();
|
flowStore.rotate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -356,12 +368,10 @@ async function openDialog(
|
||||||
isEdit.value = true;
|
isEdit.value = true;
|
||||||
await assignFormData(id);
|
await assignFormData(id);
|
||||||
|
|
||||||
if (formData.value.userType === 'AGENCY') {
|
|
||||||
const result = await userStore.fetchAttachment(id);
|
const result = await userStore.fetchAttachment(id);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
agencyFileList.value = result;
|
userFileList.value = result;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (userStore.userOption.hqOpts.length !== 0 && !id) {
|
if (userStore.userOption.hqOpts.length !== 0 && !id) {
|
||||||
|
|
@ -419,15 +429,15 @@ async function onSubmit(excludeDialog?: boolean) {
|
||||||
: '';
|
: '';
|
||||||
const formDataEdit = {
|
const formDataEdit = {
|
||||||
...formData.value,
|
...formData.value,
|
||||||
|
checkpointEN: formData.value.checkpoint,
|
||||||
status: !statusToggle.value ? 'INACTIVE' : 'ACTIVE',
|
status: !statusToggle.value ? 'INACTIVE' : 'ACTIVE',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
await userStore.editById(currentUser.value.id, formDataEdit);
|
await userStore.editById(currentUser.value.id, formDataEdit);
|
||||||
|
|
||||||
if (currentUser.value.id && formDataEdit.userType === 'AGENCY') {
|
if (userFile.value) {
|
||||||
if (!agencyFile.value) return;
|
|
||||||
const payload: UserAttachmentCreate = {
|
const payload: UserAttachmentCreate = {
|
||||||
file: agencyFile.value,
|
file: userFile.value,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (payload?.file) {
|
if (payload?.file) {
|
||||||
|
|
@ -450,16 +460,15 @@ async function onSubmit(excludeDialog?: boolean) {
|
||||||
: hqId.value
|
: hqId.value
|
||||||
? hqId.value
|
? hqId.value
|
||||||
: '';
|
: '';
|
||||||
|
formData.value.checkpointEN = formData.value.checkpoint;
|
||||||
const result = await userStore.create(
|
const result = await userStore.create(
|
||||||
formData.value,
|
formData.value,
|
||||||
onCreateImageList.value,
|
onCreateImageList.value,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result && formData.value.userType === 'AGENCY') {
|
if (userFile.value && result) {
|
||||||
if (!agencyFile.value) return;
|
|
||||||
|
|
||||||
const payload: UserAttachmentCreate = {
|
const payload: UserAttachmentCreate = {
|
||||||
file: agencyFile.value,
|
file: userFile.value,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (payload?.file) {
|
if (payload?.file) {
|
||||||
|
|
@ -546,6 +555,7 @@ async function triggerChangeStatus(id: string, status: string) {
|
||||||
async function assignFormData(idEdit: string) {
|
async function assignFormData(idEdit: string) {
|
||||||
if (!userData.value) return;
|
if (!userData.value) return;
|
||||||
const foundUser = userData.value.result.find((user) => user.id === idEdit);
|
const foundUser = userData.value.result.find((user) => user.id === idEdit);
|
||||||
|
console.log(foundUser);
|
||||||
|
|
||||||
if (foundUser) {
|
if (foundUser) {
|
||||||
currentUser.value = foundUser;
|
currentUser.value = foundUser;
|
||||||
|
|
@ -567,7 +577,10 @@ async function assignFormData(idEdit: string) {
|
||||||
street: foundUser.street,
|
street: foundUser.street,
|
||||||
streetEN: foundUser.streetEN,
|
streetEN: foundUser.streetEN,
|
||||||
trainingPlace: foundUser.trainingPlace,
|
trainingPlace: foundUser.trainingPlace,
|
||||||
importNationality: foundUser.importNationality,
|
importNationality:
|
||||||
|
typeof foundUser.importNationality === 'string'
|
||||||
|
? [foundUser.importNationality]
|
||||||
|
: foundUser.importNationality,
|
||||||
sourceNationality: foundUser.sourceNationality,
|
sourceNationality: foundUser.sourceNationality,
|
||||||
licenseNo: foundUser.licenseNo,
|
licenseNo: foundUser.licenseNo,
|
||||||
discountCondition: foundUser.discountCondition,
|
discountCondition: foundUser.discountCondition,
|
||||||
|
|
@ -587,6 +600,8 @@ async function assignFormData(idEdit: string) {
|
||||||
responsibleArea: foundUser.responsibleArea,
|
responsibleArea: foundUser.responsibleArea,
|
||||||
status: foundUser.status,
|
status: foundUser.status,
|
||||||
selectedImage: foundUser.selectedImage,
|
selectedImage: foundUser.selectedImage,
|
||||||
|
contactName: foundUser.contactName,
|
||||||
|
contactTel: foundUser.contactTel,
|
||||||
licenseExpireDate:
|
licenseExpireDate:
|
||||||
(foundUser.licenseExpireDate &&
|
(foundUser.licenseExpireDate &&
|
||||||
new Date(foundUser.licenseExpireDate)) ||
|
new Date(foundUser.licenseExpireDate)) ||
|
||||||
|
|
@ -603,6 +618,8 @@ async function assignFormData(idEdit: string) {
|
||||||
(foundUser.citizenIssue && new Date(foundUser.citizenIssue)) || null,
|
(foundUser.citizenIssue && new Date(foundUser.citizenIssue)) || null,
|
||||||
citizenExpire:
|
citizenExpire:
|
||||||
(foundUser.citizenExpire && new Date(foundUser.citizenExpire)) || null,
|
(foundUser.citizenExpire && new Date(foundUser.citizenExpire)) || null,
|
||||||
|
remark: foundUser.remark || '',
|
||||||
|
agencyStatus: foundUser.agencyStatus || '',
|
||||||
};
|
};
|
||||||
|
|
||||||
formData.value.status === 'ACTIVE' || 'CREATED'
|
formData.value.status === 'ACTIVE' || 'CREATED'
|
||||||
|
|
@ -661,6 +678,8 @@ async function fetchUserList(mobileFetch?: boolean) {
|
||||||
: statusFilter.value === 'statusACTIVE'
|
: statusFilter.value === 'statusACTIVE'
|
||||||
? 'ACTIVE'
|
? 'ACTIVE'
|
||||||
: 'INACTIVE',
|
: 'INACTIVE',
|
||||||
|
startDate: searchDate.value[0],
|
||||||
|
endDate: searchDate.value[1],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
|
@ -735,11 +754,11 @@ watch(
|
||||||
formData.value.responsibleArea = null;
|
formData.value.responsibleArea = null;
|
||||||
formData.value.discountCondition = null;
|
formData.value.discountCondition = null;
|
||||||
formData.value.sourceNationality = null;
|
formData.value.sourceNationality = null;
|
||||||
formData.value.importNationality = null;
|
formData.value.importNationality = [];
|
||||||
formData.value.trainingPlace = null;
|
formData.value.trainingPlace = null;
|
||||||
formData.value.checkpoint = null;
|
formData.value.checkpoint = null;
|
||||||
formData.value.checkpointEN = null;
|
formData.value.checkpointEN = null;
|
||||||
agencyFile.value = [];
|
userFile.value = [];
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -750,7 +769,7 @@ watch(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
watch([inputSearch, statusFilter, pageSize], async () => {
|
watch([inputSearch, statusFilter, pageSize, searchDate], async () => {
|
||||||
if (userData.value) userData.value.result = [];
|
if (userData.value) userData.value.result = [];
|
||||||
currentPage.value = 1;
|
currentPage.value = 1;
|
||||||
|
|
||||||
|
|
@ -872,26 +891,45 @@ watch(
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<q-icon name="mdi-magnify" />
|
<q-icon name="mdi-magnify" />
|
||||||
</template>
|
</template>
|
||||||
<template v-if="$q.screen.lt.md" v-slot:append>
|
<template v-slot:append>
|
||||||
<span class="row">
|
<q-separator vertical inset class="q-mr-xs" />
|
||||||
<q-separator vertical />
|
<AdvanceSearch
|
||||||
<q-btn
|
v-model="searchDate"
|
||||||
icon="mdi-filter-variant"
|
:active="$q.screen.lt.md && statusFilter !== 'all'"
|
||||||
unelevated
|
>
|
||||||
class="q-ml-sm"
|
<div
|
||||||
padding="4px"
|
v-if="$q.screen.lt.md"
|
||||||
size="sm"
|
class="q-mt-sm text-weight-medium"
|
||||||
rounded
|
>
|
||||||
@click="refFilter?.showPopup"
|
{{ $t('general.status') }}
|
||||||
|
</div>
|
||||||
|
<q-select
|
||||||
|
v-if="$q.screen.lt.md"
|
||||||
|
v-model="statusFilter"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
option-value="value"
|
||||||
|
option-label="label"
|
||||||
|
map-options
|
||||||
|
emit-value
|
||||||
|
autocomplete="off"
|
||||||
|
:for="'field-select-status'"
|
||||||
|
:options="[
|
||||||
|
{ label: $t('general.all'), value: 'all' },
|
||||||
|
{ label: $t('general.active'), value: 'statusACTIVE' },
|
||||||
|
{
|
||||||
|
label: $t('general.inactive'),
|
||||||
|
value: 'statusINACTIVE',
|
||||||
|
},
|
||||||
|
]"
|
||||||
/>
|
/>
|
||||||
</span>
|
</AdvanceSearch>
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<div class="row col-md-5" style="white-space: nowrap">
|
<div class="row col-md-5" style="white-space: nowrap">
|
||||||
<q-select
|
<q-select
|
||||||
v-show="$q.screen.gt.sm"
|
v-if="$q.screen.gt.sm"
|
||||||
ref="refFilter"
|
|
||||||
v-model="statusFilter"
|
v-model="statusFilter"
|
||||||
outlined
|
outlined
|
||||||
dense
|
dense
|
||||||
|
|
@ -1551,7 +1589,18 @@ watch(
|
||||||
v-model:toggle-status="formData.status"
|
v-model:toggle-status="formData.status"
|
||||||
hideFade
|
hideFade
|
||||||
:toggle-title="$t('status.title')"
|
:toggle-title="$t('status.title')"
|
||||||
:title="`${locale === 'eng' ? `${formData.firstNameEN} ${formData.lastNameEN}` : `${formData.firstName} ${formData.lastName}`}`"
|
:title="
|
||||||
|
setPrefixName(
|
||||||
|
{
|
||||||
|
namePrefix: formData.namePrefix,
|
||||||
|
firstName: formData.firstName,
|
||||||
|
lastName: formData.lastName,
|
||||||
|
firstNameEN: formData.firstNameEN,
|
||||||
|
lastNameEN: formData.lastNameEN,
|
||||||
|
},
|
||||||
|
{ locale },
|
||||||
|
)
|
||||||
|
"
|
||||||
:caption="userCode"
|
:caption="userCode"
|
||||||
:img="
|
:img="
|
||||||
`${baseUrl}/user/${currentUser.id}/profile-image/${formData.selectedImage}`.concat(
|
`${baseUrl}/user/${currentUser.id}/profile-image/${formData.selectedImage}`.concat(
|
||||||
|
|
@ -1736,12 +1785,15 @@ watch(
|
||||||
v-model:citizen-id="formData.citizenId"
|
v-model:citizen-id="formData.citizenId"
|
||||||
v-model:citizen-issue="formData.citizenIssue"
|
v-model:citizen-issue="formData.citizenIssue"
|
||||||
v-model:citizen-expire="formData.citizenExpire"
|
v-model:citizen-expire="formData.citizenExpire"
|
||||||
|
v-model:contact-name="formData.contactName"
|
||||||
|
v-model:contact-tel="formData.contactTel"
|
||||||
:title="'personnel.form.personalInformation'"
|
:title="'personnel.form.personalInformation'"
|
||||||
prefix-id="drawer-info-personnel"
|
prefix-id="drawer-info-personnel"
|
||||||
dense
|
dense
|
||||||
outlined
|
outlined
|
||||||
separator
|
separator
|
||||||
:readonly="!infoDrawerEdit"
|
:readonly="!infoDrawerEdit"
|
||||||
|
:agency="formData.userType === 'AGENCY'"
|
||||||
class="q-mb-xl"
|
class="q-mb-xl"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -1781,10 +1833,11 @@ watch(
|
||||||
v-model:import-nationality="formData.importNationality"
|
v-model:import-nationality="formData.importNationality"
|
||||||
v-model:training-place="formData.trainingPlace"
|
v-model:training-place="formData.trainingPlace"
|
||||||
v-model:checkpoint="formData.checkpoint"
|
v-model:checkpoint="formData.checkpoint"
|
||||||
v-model:checkpoint-en="formData.checkpointEN"
|
v-model:user-file="userFile"
|
||||||
v-model:agency-file="agencyFile"
|
v-model:user-file-list="userFileList"
|
||||||
v-model:agency-file-list="agencyFileList"
|
|
||||||
v-model:user-id="currentUser.id"
|
v-model:user-id="currentUser.id"
|
||||||
|
v-model:remark="formData.remark"
|
||||||
|
v-model:agency-status="formData.agencyStatus"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1828,7 +1881,18 @@ watch(
|
||||||
}[formData.gender]
|
}[formData.gender]
|
||||||
"
|
"
|
||||||
:toggleTitle="$t('status.title')"
|
:toggleTitle="$t('status.title')"
|
||||||
:title="`${locale === 'eng' ? `${formData.firstNameEN} ${formData.lastNameEN}` : `${formData.firstName} ${formData.lastName}`}`"
|
:title="
|
||||||
|
setPrefixName(
|
||||||
|
{
|
||||||
|
namePrefix: formData.namePrefix,
|
||||||
|
firstName: formData.firstName,
|
||||||
|
lastName: formData.lastName,
|
||||||
|
firstNameEN: formData.firstNameEN,
|
||||||
|
lastNameEN: formData.lastNameEN,
|
||||||
|
},
|
||||||
|
{ locale },
|
||||||
|
)
|
||||||
|
"
|
||||||
:fallbackImg="
|
:fallbackImg="
|
||||||
{
|
{
|
||||||
male: '/no-img-man.png',
|
male: '/no-img-man.png',
|
||||||
|
|
@ -1854,7 +1918,6 @@ watch(
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="col"
|
class="col"
|
||||||
id="personnel-form"
|
|
||||||
:class="{
|
:class="{
|
||||||
'q-px-lg q-pb-lg': $q.screen.gt.sm,
|
'q-px-lg q-pb-lg': $q.screen.gt.sm,
|
||||||
'q-px-md q-pb-sm': !$q.screen.gt.sm,
|
'q-px-md q-pb-sm': !$q.screen.gt.sm,
|
||||||
|
|
@ -1898,7 +1961,7 @@ watch(
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
name: $t('personnel.form.workInformation'),
|
name: $t('personnel.form.workInformation'),
|
||||||
anchor: 'dialog-info-work',
|
anchor: 'dialog-form-work',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
|
|
@ -1914,6 +1977,7 @@ watch(
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
id="personnel-form"
|
||||||
class="col-md-10 col-12 full-height scroll"
|
class="col-md-10 col-12 full-height scroll"
|
||||||
:class="{
|
:class="{
|
||||||
'q-py-md q-pr-md ': $q.screen.gt.sm,
|
'q-py-md q-pr-md ': $q.screen.gt.sm,
|
||||||
|
|
@ -1939,6 +2003,7 @@ watch(
|
||||||
id="dialog-form-personal"
|
id="dialog-form-personal"
|
||||||
prefix-id="form-dialog-personnel"
|
prefix-id="form-dialog-personnel"
|
||||||
dense
|
dense
|
||||||
|
:agency="formData.userType === 'AGENCY'"
|
||||||
outlined
|
outlined
|
||||||
separator
|
separator
|
||||||
:title="'personnel.form.personalInformation'"
|
:title="'personnel.form.personalInformation'"
|
||||||
|
|
@ -1957,6 +2022,8 @@ watch(
|
||||||
v-model:citizen-id="formData.citizenId"
|
v-model:citizen-id="formData.citizenId"
|
||||||
v-model:citizen-issue="formData.citizenIssue"
|
v-model:citizen-issue="formData.citizenIssue"
|
||||||
v-model:citizen-expire="formData.citizenExpire"
|
v-model:citizen-expire="formData.citizenExpire"
|
||||||
|
v-model:contact-name="formData.contactName"
|
||||||
|
v-model:contact-tel="formData.contactTel"
|
||||||
class="q-mb-xl"
|
class="q-mb-xl"
|
||||||
/>
|
/>
|
||||||
<AddressForm
|
<AddressForm
|
||||||
|
|
@ -1992,8 +2059,10 @@ watch(
|
||||||
v-model:import-nationality="formData.importNationality"
|
v-model:import-nationality="formData.importNationality"
|
||||||
v-model:training-place="formData.trainingPlace"
|
v-model:training-place="formData.trainingPlace"
|
||||||
v-model:checkpoint="formData.checkpoint"
|
v-model:checkpoint="formData.checkpoint"
|
||||||
v-model:checkpoint-en="formData.checkpointEN"
|
v-model:agency-status="formData.agencyStatus"
|
||||||
v-model:agency-file="agencyFile"
|
v-model:remark="formData.remark"
|
||||||
|
v-model:user-file="userFile"
|
||||||
|
v-model:user-file-list="userFileList"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch, onMounted, computed } from 'vue';
|
import { ref, watch, onMounted, computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { QSelect, useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { getUserId, getRole } from 'src/services/keycloak';
|
import { getUserId, getRole } from 'src/services/keycloak';
|
||||||
import { baseUrl, waitAll } from 'src/stores/utils';
|
import { baseUrl, setPrefixName, waitAll } from 'src/stores/utils';
|
||||||
import { dateFormat } from 'src/utils/datetime';
|
import { dateFormat } from 'src/utils/datetime';
|
||||||
import { dialogCheckData } from 'stores/utils';
|
import { dialogCheckData } from 'stores/utils';
|
||||||
|
|
||||||
|
|
@ -86,6 +86,7 @@ import { nextTick } from 'vue';
|
||||||
import FormEmployeeVisa from 'components/03_customer-management/FormEmployeeVisa.vue';
|
import FormEmployeeVisa from 'components/03_customer-management/FormEmployeeVisa.vue';
|
||||||
import PaginationPageSize from 'src/components/PaginationPageSize.vue';
|
import PaginationPageSize from 'src/components/PaginationPageSize.vue';
|
||||||
import { AddButton } from 'components/button';
|
import { AddButton } from 'components/button';
|
||||||
|
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
||||||
|
|
||||||
const { t, locale } = useI18n();
|
const { t, locale } = useI18n();
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
|
|
@ -101,28 +102,33 @@ const employeeFormStore = useEmployeeForm();
|
||||||
const optionStore = useOptionStore();
|
const optionStore = useOptionStore();
|
||||||
const ocrStore = useOcrStore();
|
const ocrStore = useOcrStore();
|
||||||
|
|
||||||
const refFilter = ref<InstanceType<typeof QSelect>>();
|
|
||||||
const statusEmployeeCreate = ref<boolean>(false);
|
|
||||||
const mrz = ref<Awaited<ReturnType<typeof parseResultMRZ>>>();
|
const mrz = ref<Awaited<ReturnType<typeof parseResultMRZ>>>();
|
||||||
const tabFieldRequired = ref<{ [key: string]: (keyof CustomerBranchCreate)[] }>(
|
|
||||||
{
|
|
||||||
main: [],
|
|
||||||
business: ['businessType', 'jobPosition'],
|
|
||||||
address: [
|
|
||||||
'address',
|
|
||||||
'addressEN',
|
|
||||||
'provinceId',
|
|
||||||
'districtId',
|
|
||||||
'subDistrictId',
|
|
||||||
],
|
|
||||||
contact: [],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const { state: customerFormState, currentFormData: customerFormData } =
|
const {
|
||||||
storeToRefs(customerFormStore);
|
fetchListOfOptionBranch,
|
||||||
const { state: employeeFormState, currentFromDataEmployee } =
|
customerFormUndo,
|
||||||
storeToRefs(employeeFormStore);
|
customerConfirmUnsave,
|
||||||
|
deleteCustomerById,
|
||||||
|
validateTabField,
|
||||||
|
deleteCustomerBranchById,
|
||||||
|
} = customerFormStore;
|
||||||
|
|
||||||
|
const { employeeFormUndo, employeeConfirmUnsave, deleteEmployeeById } =
|
||||||
|
employeeFormStore;
|
||||||
|
|
||||||
|
const {
|
||||||
|
state: customerFormState,
|
||||||
|
currentFormData: customerFormData,
|
||||||
|
registerAbleBranchOption,
|
||||||
|
tabFieldRequired,
|
||||||
|
} = storeToRefs(customerFormStore);
|
||||||
|
const {
|
||||||
|
state: employeeFormState,
|
||||||
|
currentFromDataEmployee,
|
||||||
|
onCreateImageList,
|
||||||
|
statusEmployeeCreate,
|
||||||
|
refreshImageState,
|
||||||
|
} = storeToRefs(employeeFormStore);
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
navigatorStore.current.title = 'menu.customer';
|
navigatorStore.current.title = 'menu.customer';
|
||||||
|
|
@ -136,6 +142,14 @@ async function init() {
|
||||||
|
|
||||||
gridView.value = $q.screen.lt.md ? true : false;
|
gridView.value = $q.screen.lt.md ? true : false;
|
||||||
|
|
||||||
|
if (route.query.tab === 'customer') {
|
||||||
|
currentTab.value = 'employer';
|
||||||
|
if (route.query.id) openSpecificCustomer(route.query.id as string);
|
||||||
|
} else if (route.query.tab === 'employee') {
|
||||||
|
currentTab.value = 'employee';
|
||||||
|
if (route.query.id) openSpecificEmployee(route.query.id as string);
|
||||||
|
}
|
||||||
|
|
||||||
if (route.name === 'CustomerManagement') await fetchListCustomer(true);
|
if (route.name === 'CustomerManagement') await fetchListCustomer(true);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|
@ -175,6 +189,7 @@ const statsCustomerType = ref<CustomerStats>({
|
||||||
});
|
});
|
||||||
|
|
||||||
// NOTE: Page State
|
// NOTE: Page State
|
||||||
|
const searchDate = ref<string[]>([]);
|
||||||
const currentTab = ref<'employer' | 'employee'>('employer');
|
const currentTab = ref<'employer' | 'employee'>('employer');
|
||||||
const inputSearch = ref('');
|
const inputSearch = ref('');
|
||||||
const currentStatus = ref<Status | 'All'>('All');
|
const currentStatus = ref<Status | 'All'>('All');
|
||||||
|
|
@ -209,17 +224,19 @@ const dialogCustomerImageUpload = ref<InstanceType<typeof ImageUploadDialog>>();
|
||||||
const dialogEmployeeImageUpload = ref<InstanceType<typeof ImageUploadDialog>>();
|
const dialogEmployeeImageUpload = ref<InstanceType<typeof ImageUploadDialog>>();
|
||||||
|
|
||||||
// image
|
// image
|
||||||
const refreshImageState = ref(false);
|
|
||||||
const imageList = ref<{ selectedImage: string; list: string[] }>();
|
const imageList = ref<{ selectedImage: string; list: string[] }>();
|
||||||
const onCreateImageList = ref<{
|
|
||||||
selectedImage: string;
|
|
||||||
list: { url: string; imgFile: File | null; name: string }[];
|
|
||||||
}>({ selectedImage: '', list: [] });
|
|
||||||
|
|
||||||
watch(() => route.name, init);
|
watch(() => route.name, init);
|
||||||
watch(
|
watch(
|
||||||
[currentTab, currentStatus, inputSearch, customerTypeSelected, pageSize],
|
[
|
||||||
async ([tabName]) => {
|
currentTab,
|
||||||
|
currentStatus,
|
||||||
|
inputSearch,
|
||||||
|
customerTypeSelected,
|
||||||
|
pageSize,
|
||||||
|
searchDate,
|
||||||
|
],
|
||||||
|
async ([tabName], [oldTabName]) => {
|
||||||
|
// if (tabName !== oldTabName) searchDate.value = [];
|
||||||
if (tabName === 'employer') {
|
if (tabName === 'employer') {
|
||||||
currentPageCustomer.value = 1;
|
currentPageCustomer.value = 1;
|
||||||
currentBtnOpen.value = [];
|
currentBtnOpen.value = [];
|
||||||
|
|
@ -276,8 +293,6 @@ const fieldSelected = ref<string[]>(
|
||||||
].filter((v, index, self) => self.indexOf(v) === index),
|
].filter((v, index, self) => self.indexOf(v) === index),
|
||||||
);
|
);
|
||||||
|
|
||||||
const registerAbleBranchOption = ref<{ id: string; name: string }[]>();
|
|
||||||
|
|
||||||
const branch = ref<CustomerBranch[]>();
|
const branch = ref<CustomerBranch[]>();
|
||||||
|
|
||||||
const customerStats = [
|
const customerStats = [
|
||||||
|
|
@ -294,81 +309,6 @@ const fieldCustomer = [
|
||||||
const employeeHistoryDialog = ref(false);
|
const employeeHistoryDialog = ref(false);
|
||||||
const employeeHistory = ref<EmployeeHistory[]>();
|
const employeeHistory = ref<EmployeeHistory[]>();
|
||||||
|
|
||||||
function validateTabField<T = CustomerBranchCreate>(
|
|
||||||
value: T,
|
|
||||||
fieldRequired: { [key: string]: (keyof T)[] },
|
|
||||||
) {
|
|
||||||
const list: string[] = [];
|
|
||||||
|
|
||||||
for (const tab in fieldRequired) {
|
|
||||||
for (const field of fieldRequired[tab]) {
|
|
||||||
if (!value[field] && !list.includes(tab)) list.push(tab);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteCustomerById(id: string) {
|
|
||||||
dialog({
|
|
||||||
color: 'negative',
|
|
||||||
icon: 'mdi-alert',
|
|
||||||
title: t('dialog.title.confirmDelete'),
|
|
||||||
actionText: t('general.delete'),
|
|
||||||
persistent: true,
|
|
||||||
message: t('dialog.message.confirmDelete'),
|
|
||||||
action: async () => {
|
|
||||||
await customerStore.deleteById(id);
|
|
||||||
|
|
||||||
await fetchListCustomer(true, $q.screen.xs);
|
|
||||||
customerFormState.value.dialogModal = false;
|
|
||||||
flowStore.rotate();
|
|
||||||
},
|
|
||||||
cancel: () => {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteCustomerBranchById(id: string) {
|
|
||||||
return await new Promise((resolve) => {
|
|
||||||
dialog({
|
|
||||||
color: 'negative',
|
|
||||||
icon: 'mdi-alert',
|
|
||||||
title: t('dialog.title.confirmDelete'),
|
|
||||||
actionText: t('general.delete'),
|
|
||||||
persistent: true,
|
|
||||||
message: t('dialog.message.confirmDelete'),
|
|
||||||
action: async () => {
|
|
||||||
await customerStore.deleteBranchById(id);
|
|
||||||
flowStore.rotate();
|
|
||||||
resolve(true);
|
|
||||||
},
|
|
||||||
cancel: () => {
|
|
||||||
resolve(false);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
async function fetchListOfOptionBranch() {
|
|
||||||
if (registerAbleBranchOption.value) return;
|
|
||||||
|
|
||||||
const uid = getUserId();
|
|
||||||
const role = getRole();
|
|
||||||
|
|
||||||
if (!uid) return; // should not possible as the system require login to be able to access resource.
|
|
||||||
|
|
||||||
if (role?.includes('system')) {
|
|
||||||
const result = await userBranchStore.fetchListOptionBranch();
|
|
||||||
if (result && result.total > 0)
|
|
||||||
registerAbleBranchOption.value = result.result;
|
|
||||||
} else {
|
|
||||||
const result = await userBranchStore.fetchListMyBranch(uid);
|
|
||||||
if (result && result.total > 0)
|
|
||||||
registerAbleBranchOption.value = result.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Assign (first) branch of the user as register branch of the data
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchListCustomer(fetchStats = false, mobileFetch?: boolean) {
|
async function fetchListCustomer(fetchStats = false, mobileFetch?: boolean) {
|
||||||
const total = statsCustomerType.value.PERS + statsCustomerType.value.CORP;
|
const total = statsCustomerType.value.PERS + statsCustomerType.value.CORP;
|
||||||
|
|
||||||
|
|
@ -386,6 +326,8 @@ async function fetchListCustomer(fetchStats = false, mobileFetch?: boolean) {
|
||||||
? 'ACTIVE'
|
? 'ACTIVE'
|
||||||
: 'INACTIVE',
|
: 'INACTIVE',
|
||||||
query: inputSearch.value,
|
query: inputSearch.value,
|
||||||
|
startDate: searchDate.value[0],
|
||||||
|
endDate: searchDate.value[1],
|
||||||
customerType: (
|
customerType: (
|
||||||
{
|
{
|
||||||
all: undefined,
|
all: undefined,
|
||||||
|
|
@ -439,6 +381,8 @@ async function fetchListEmployee(opt?: {
|
||||||
query: inputSearch.value,
|
query: inputSearch.value,
|
||||||
passport: true,
|
passport: true,
|
||||||
visa: true,
|
visa: true,
|
||||||
|
startDate: searchDate.value[0],
|
||||||
|
endDate: searchDate.value[1],
|
||||||
});
|
});
|
||||||
if (resultListEmployee) {
|
if (resultListEmployee) {
|
||||||
maxPageEmployee.value = Math.ceil(
|
maxPageEmployee.value = Math.ceil(
|
||||||
|
|
@ -503,64 +447,6 @@ async function toggleStatusCustomer(id: string, status: boolean) {
|
||||||
await fetchListCustomer(false, $q.screen.xs);
|
await fetchListCustomer(false, $q.screen.xs);
|
||||||
flowStore.rotate();
|
flowStore.rotate();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteEmployeeById(opts: {
|
|
||||||
id?: string;
|
|
||||||
type?: 'passport' | 'visa' | 'healthCheck' | 'work';
|
|
||||||
index?: number;
|
|
||||||
}) {
|
|
||||||
dialog({
|
|
||||||
color: 'negative',
|
|
||||||
icon: 'mdi-alert',
|
|
||||||
title: t('dialog.title.confirmDelete'),
|
|
||||||
actionText: t('general.delete'),
|
|
||||||
persistent: true,
|
|
||||||
message: t('dialog.message.confirmDelete'),
|
|
||||||
action: async () => {
|
|
||||||
if (opts.type === 'passport' && opts.index !== undefined) {
|
|
||||||
await employeeFormStore.deletePassport(opts.index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.type === 'visa' && opts.index !== undefined) {
|
|
||||||
await employeeFormStore.deleteVisa(opts.index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.type === 'healthCheck' && opts.index !== undefined) {
|
|
||||||
await employeeFormStore.deleteHealthCheck(opts.index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.type === 'work' && opts.index !== undefined) {
|
|
||||||
await employeeFormStore.deleteWorkHistory(opts.index);
|
|
||||||
} else {
|
|
||||||
if (!!opts.id) {
|
|
||||||
const result = await employeeStore.deleteById(opts.id);
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
employeeFormState.value.drawerModal = false;
|
|
||||||
employeeFormState.value.dialogModal = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (route.name !== 'CustomerBranchManagement') {
|
|
||||||
await fetchListEmployee(
|
|
||||||
currentTab.value === 'employer'
|
|
||||||
? {
|
|
||||||
page: 1,
|
|
||||||
pageSize: 999,
|
|
||||||
customerId: customerFormState.value.currentCustomerId,
|
|
||||||
}
|
|
||||||
: { fetchStats: true, mobileFetch: $q.screen.xs },
|
|
||||||
);
|
|
||||||
|
|
||||||
flowStore.rotate();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
cancel: () => {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function openHistory(id: string) {
|
async function openHistory(id: string) {
|
||||||
const res = await employeeStore.getEditHistory(id);
|
const res = await employeeStore.getEditHistory(id);
|
||||||
employeeHistory.value = res.reverse();
|
employeeHistory.value = res.reverse();
|
||||||
|
|
@ -594,64 +480,6 @@ async function editEmployeeFormPersonal(id: string) {
|
||||||
employeeFormState.value.drawerModal = true;
|
employeeFormState.value.drawerModal = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function employeeConfirmUnsave(close = true) {
|
|
||||||
dialog({
|
|
||||||
color: 'warning',
|
|
||||||
icon: 'mdi-alert',
|
|
||||||
title: t('form.warning.title'),
|
|
||||||
actionText: t('general.ok'),
|
|
||||||
persistent: true,
|
|
||||||
message: t('form.warning.unsave'),
|
|
||||||
action: () => {
|
|
||||||
employeeFormStore.resetFormDataEmployee();
|
|
||||||
onCreateImageList.value = { selectedImage: '', list: [] };
|
|
||||||
employeeFormState.value.editReadonly = true;
|
|
||||||
employeeFormState.value.dialogModal = !close;
|
|
||||||
employeeFormState.value.drawerModal = !close;
|
|
||||||
},
|
|
||||||
cancel: () => {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function employeeFormUndo(close = true) {
|
|
||||||
if (employeeFormStore.isFormDataDifferent()) {
|
|
||||||
return employeeConfirmUnsave(close);
|
|
||||||
}
|
|
||||||
employeeFormStore.resetFormDataEmployee();
|
|
||||||
employeeFormState.value.editReadonly = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function customerConfirmUnsave(close = true) {
|
|
||||||
dialog({
|
|
||||||
color: 'warning',
|
|
||||||
icon: 'mdi-alert',
|
|
||||||
title: t('form.warning.title'),
|
|
||||||
actionText: t('general.ok'),
|
|
||||||
persistent: true,
|
|
||||||
message: t('form.warning.unsave'),
|
|
||||||
|
|
||||||
action: () => {
|
|
||||||
customerFormStore.resetForm();
|
|
||||||
customerFormState.value.readonly = true;
|
|
||||||
|
|
||||||
if (!customerFormState.value.drawerModal) {
|
|
||||||
customerFormState.value.dialogModal = !close;
|
|
||||||
} else {
|
|
||||||
customerFormState.value.drawerModal = !close;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cancel: () => {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function customerFormUndo(close = true) {
|
|
||||||
if (customerFormStore.isFormDataDifferent()) {
|
|
||||||
return customerConfirmUnsave(close);
|
|
||||||
}
|
|
||||||
customerFormStore.resetForm();
|
|
||||||
customerFormState.value.readonly = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createCustomerForm(customerType: 'CORP' | 'PERS') {
|
async function createCustomerForm(customerType: 'CORP' | 'PERS') {
|
||||||
customerFormState.value.dialogModal = true;
|
customerFormState.value.dialogModal = true;
|
||||||
customerFormState.value.dialogType = 'create';
|
customerFormState.value.dialogType = 'create';
|
||||||
|
|
@ -683,10 +511,34 @@ async function fetchImageList(
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function openSpecificCustomer(id: string) {
|
||||||
|
await customerFormStore.assignFormData(id);
|
||||||
|
await fetchImageList(
|
||||||
|
id,
|
||||||
|
customerFormData.value.selectedImage || '',
|
||||||
|
'customer',
|
||||||
|
);
|
||||||
|
customerFormState.value.branchIndex = -1;
|
||||||
|
customerFormState.value.drawerModal = true;
|
||||||
|
customerFormState.value.editCustomerId = id;
|
||||||
|
customerFormState.value.dialogType = 'info';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openSpecificEmployee(id: string) {
|
||||||
|
await employeeFormStore.assignFormDataEmployee(id);
|
||||||
|
await fetchImageList(
|
||||||
|
id,
|
||||||
|
currentFromDataEmployee.value.selectedImage || '',
|
||||||
|
'employee',
|
||||||
|
);
|
||||||
|
employeeFormState.value.dialogType = 'info';
|
||||||
|
employeeFormState.value.drawerModal = true;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: When in employee form, if select address same as customer then auto fill
|
// TODO: When in employee form, if select address same as customer then auto fill
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => employeeFormState.value.formDataEmployeeOwner,
|
() => employeeFormState.value.currentCustomerBranch,
|
||||||
(e) => {
|
(e) => {
|
||||||
if (!e) return;
|
if (!e) return;
|
||||||
if (employeeFormState.value.formDataEmployeeSameAddr) {
|
if (employeeFormState.value.formDataEmployeeSameAddr) {
|
||||||
|
|
@ -703,21 +555,21 @@ watch(
|
||||||
watch(
|
watch(
|
||||||
() => employeeFormState.value.formDataEmployeeSameAddr,
|
() => employeeFormState.value.formDataEmployeeSameAddr,
|
||||||
(isSame) => {
|
(isSame) => {
|
||||||
if (!employeeFormState.value.formDataEmployeeOwner) return;
|
if (!employeeFormState.value.currentCustomerBranch) return;
|
||||||
if (isSame) {
|
if (isSame) {
|
||||||
currentFromDataEmployee.value.address =
|
currentFromDataEmployee.value.address =
|
||||||
employeeFormState.value.formDataEmployeeOwner.address;
|
employeeFormState.value.currentCustomerBranch.address;
|
||||||
currentFromDataEmployee.value.addressEN =
|
currentFromDataEmployee.value.addressEN =
|
||||||
employeeFormState.value.formDataEmployeeOwner.addressEN;
|
employeeFormState.value.currentCustomerBranch.addressEN;
|
||||||
currentFromDataEmployee.value.provinceId =
|
currentFromDataEmployee.value.provinceId =
|
||||||
employeeFormState.value.formDataEmployeeOwner.provinceId;
|
employeeFormState.value.currentCustomerBranch.provinceId;
|
||||||
currentFromDataEmployee.value.districtId =
|
currentFromDataEmployee.value.districtId =
|
||||||
employeeFormState.value.formDataEmployeeOwner.districtId;
|
employeeFormState.value.currentCustomerBranch.districtId;
|
||||||
currentFromDataEmployee.value.subDistrictId =
|
currentFromDataEmployee.value.subDistrictId =
|
||||||
employeeFormState.value.formDataEmployeeOwner.subDistrictId;
|
employeeFormState.value.currentCustomerBranch.subDistrictId;
|
||||||
}
|
}
|
||||||
currentFromDataEmployee.value.customerBranchId =
|
currentFromDataEmployee.value.customerBranchId =
|
||||||
employeeFormState.value.formDataEmployeeOwner.id;
|
employeeFormState.value.currentCustomerBranch.id;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -936,26 +788,43 @@ const emptyCreateDialog = ref(false);
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<q-icon name="mdi-magnify" />
|
<q-icon name="mdi-magnify" />
|
||||||
</template>
|
</template>
|
||||||
<template v-if="$q.screen.lt.md" v-slot:append>
|
<template v-slot:append>
|
||||||
<span class="row">
|
<q-separator vertical inset class="q-mr-xs" />
|
||||||
<q-separator vertical />
|
<AdvanceSearch
|
||||||
<q-btn
|
v-model="searchDate"
|
||||||
icon="mdi-filter-variant"
|
:active="$q.screen.lt.md && currentStatus !== 'All'"
|
||||||
unelevated
|
>
|
||||||
class="q-ml-sm"
|
<div
|
||||||
padding="4px"
|
v-if="$q.screen.lt.md"
|
||||||
size="sm"
|
class="q-mt-sm text-weight-medium"
|
||||||
rounded
|
>
|
||||||
@click="refFilter?.showPopup"
|
{{ $t('general.status') }}
|
||||||
|
</div>
|
||||||
|
<q-select
|
||||||
|
v-if="$q.screen.lt.md"
|
||||||
|
id="select-status"
|
||||||
|
for="select-status"
|
||||||
|
v-model="currentStatus"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
autocomplete="off"
|
||||||
|
option-value="value"
|
||||||
|
option-label="label"
|
||||||
|
map-options
|
||||||
|
emit-value
|
||||||
|
:options="[
|
||||||
|
{ label: $t('general.all'), value: 'All' },
|
||||||
|
{ label: $t('status.ACTIVE'), value: 'ACTIVE' },
|
||||||
|
{ label: $t('status.INACTIVE'), value: 'INACTIVE' },
|
||||||
|
]"
|
||||||
/>
|
/>
|
||||||
</span>
|
</AdvanceSearch>
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<div class="row col-md-5" style="white-space: nowrap">
|
<div class="row col-md-5" style="white-space: nowrap">
|
||||||
<q-select
|
<q-select
|
||||||
v-show="$q.screen.gt.sm"
|
v-if="$q.screen.gt.sm"
|
||||||
ref="refFilter"
|
|
||||||
id="select-status"
|
id="select-status"
|
||||||
for="select-status"
|
for="select-status"
|
||||||
v-model="currentStatus"
|
v-model="currentStatus"
|
||||||
|
|
@ -1540,7 +1409,13 @@ const emptyCreateDialog = ref(false);
|
||||||
customerFormState.branchIndex = 0;
|
customerFormState.branchIndex = 0;
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
@delete="deleteCustomerById(props.row.id)"
|
@delete="
|
||||||
|
deleteCustomerById(
|
||||||
|
props.row.id,
|
||||||
|
async () =>
|
||||||
|
await fetchListCustomer(true, $q.screen.xs),
|
||||||
|
)
|
||||||
|
"
|
||||||
@change-status="
|
@change-status="
|
||||||
async () => {
|
async () => {
|
||||||
triggerChangeStatus(
|
triggerChangeStatus(
|
||||||
|
|
@ -1588,7 +1463,23 @@ const emptyCreateDialog = ref(false);
|
||||||
"
|
"
|
||||||
@delete="
|
@delete="
|
||||||
(item: any) => {
|
(item: any) => {
|
||||||
deleteEmployeeById({ id: item.id });
|
deleteEmployeeById({
|
||||||
|
id: item.id,
|
||||||
|
fetch: async () =>
|
||||||
|
await fetchListEmployee(
|
||||||
|
currentTab === 'employer'
|
||||||
|
? {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 999,
|
||||||
|
customerId:
|
||||||
|
customerFormState.currentCustomerId,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
fetchStats: true,
|
||||||
|
mobileFetch: $q.screen.xs,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
@toggle-status="
|
@toggle-status="
|
||||||
|
|
@ -1763,7 +1654,16 @@ const emptyCreateDialog = ref(false);
|
||||||
customerFormState.branchIndex = 0;
|
customerFormState.branchIndex = 0;
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
@delete="deleteCustomerById(props.row.id)"
|
@delete="
|
||||||
|
deleteCustomerById(
|
||||||
|
props.row.id,
|
||||||
|
async () =>
|
||||||
|
await fetchListCustomer(
|
||||||
|
true,
|
||||||
|
$q.screen.xs,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
"
|
||||||
@change-status="
|
@change-status="
|
||||||
triggerChangeStatus(
|
triggerChangeStatus(
|
||||||
props.row.id,
|
props.row.id,
|
||||||
|
|
@ -1898,7 +1798,23 @@ const emptyCreateDialog = ref(false);
|
||||||
@edit="(item: any) => editEmployeeFormPersonal(item.id)"
|
@edit="(item: any) => editEmployeeFormPersonal(item.id)"
|
||||||
@delete="
|
@delete="
|
||||||
(item: any) => {
|
(item: any) => {
|
||||||
deleteEmployeeById({ id: item.id });
|
deleteEmployeeById({
|
||||||
|
id: item.id,
|
||||||
|
fetch: async () =>
|
||||||
|
await fetchListEmployee(
|
||||||
|
currentTab === 'employer'
|
||||||
|
? {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 999,
|
||||||
|
customerId:
|
||||||
|
customerFormState.currentCustomerId,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
fetchStats: true,
|
||||||
|
mobileFetch: $q.screen.xs,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
@toggle-status="
|
@toggle-status="
|
||||||
|
|
@ -2036,7 +1952,7 @@ const emptyCreateDialog = ref(false);
|
||||||
async (currentBranch) => {
|
async (currentBranch) => {
|
||||||
createEmployeeForm();
|
createEmployeeForm();
|
||||||
await nextTick();
|
await nextTick();
|
||||||
employeeFormState.formDataEmployeeOwner = { ...currentBranch };
|
employeeFormState.currentBranchId = currentBranch.id;
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
v-model:branch="branch"
|
v-model:branch="branch"
|
||||||
|
|
@ -2150,7 +2066,16 @@ const emptyCreateDialog = ref(false);
|
||||||
"
|
"
|
||||||
:title="
|
:title="
|
||||||
customerFormData.customerType === 'PERS'
|
customerFormData.customerType === 'PERS'
|
||||||
? `${customerFormData.customerBranch[0]?.firstName} ${customerFormData.customerBranch[0]?.lastName}`
|
? setPrefixName(
|
||||||
|
{
|
||||||
|
namePrefix: customerFormData.customerBranch[0]?.namePrefix,
|
||||||
|
firstName: customerFormData.customerBranch[0]?.firstName,
|
||||||
|
lastName: customerFormData.customerBranch[0]?.lastName,
|
||||||
|
firstNameEN: customerFormData.customerBranch[0]?.firstNameEN,
|
||||||
|
lastNameEN: customerFormData.customerBranch[0]?.lastNameEN,
|
||||||
|
},
|
||||||
|
{ locale },
|
||||||
|
)
|
||||||
: customerFormData.customerBranch[0]?.registerName
|
: customerFormData.customerBranch[0]?.registerName
|
||||||
"
|
"
|
||||||
:caption="
|
:caption="
|
||||||
|
|
@ -2259,13 +2184,16 @@ const emptyCreateDialog = ref(false);
|
||||||
id="form-basic-info-customer"
|
id="form-basic-info-customer"
|
||||||
:onCreate="customerFormState.dialogType === 'create'"
|
:onCreate="customerFormState.dialogType === 'create'"
|
||||||
@edit="
|
@edit="
|
||||||
(customerFormState.dialogType = 'edit'),
|
((customerFormState.dialogType = 'edit'),
|
||||||
(customerFormState.readonly = false)
|
(customerFormState.readonly = false))
|
||||||
"
|
"
|
||||||
@cancel="() => customerFormUndo(false)"
|
@cancel="() => customerFormUndo(false)"
|
||||||
@delete="
|
@delete="
|
||||||
customerFormState.editCustomerId &&
|
customerFormState.editCustomerId &&
|
||||||
deleteCustomerById(customerFormState.editCustomerId)
|
deleteCustomerById(
|
||||||
|
customerFormState.editCustomerId,
|
||||||
|
async () => await fetchListCustomer(true, $q.screen.xs),
|
||||||
|
)
|
||||||
"
|
"
|
||||||
:customer-type="customerFormData.customerType"
|
:customer-type="customerFormData.customerType"
|
||||||
v-model:registered-branch-id="customerFormData.registeredBranchId"
|
v-model:registered-branch-id="customerFormData.registeredBranchId"
|
||||||
|
|
@ -2435,7 +2363,11 @@ const emptyCreateDialog = ref(false);
|
||||||
if (!customerFormState.editCustomerId) return;
|
if (!customerFormState.editCustomerId) return;
|
||||||
|
|
||||||
if (idx === 0) {
|
if (idx === 0) {
|
||||||
deleteCustomerById(customerFormState.editCustomerId);
|
deleteCustomerById(
|
||||||
|
customerFormState.editCustomerId,
|
||||||
|
async () =>
|
||||||
|
await fetchListCustomer(true, $q.screen.xs),
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!!customerFormData.customerBranch?.[idx].id) {
|
if (!!customerFormData.customerBranch?.[idx].id) {
|
||||||
|
|
@ -2591,6 +2523,20 @@ const emptyCreateDialog = ref(false);
|
||||||
"
|
"
|
||||||
:toggleTitle="$t('status.title')"
|
:toggleTitle="$t('status.title')"
|
||||||
hideFade
|
hideFade
|
||||||
|
:title="
|
||||||
|
currentFromDataEmployee
|
||||||
|
? setPrefixName(
|
||||||
|
{
|
||||||
|
namePrefix: currentFromDataEmployee.namePrefix,
|
||||||
|
firstName: currentFromDataEmployee.firstName,
|
||||||
|
lastName: currentFromDataEmployee.lastName,
|
||||||
|
firstNameEN: currentFromDataEmployee.firstNameEN,
|
||||||
|
lastNameEN: currentFromDataEmployee.lastNameEN,
|
||||||
|
},
|
||||||
|
{ locale },
|
||||||
|
)
|
||||||
|
: '-'
|
||||||
|
"
|
||||||
@view="
|
@view="
|
||||||
() => {
|
() => {
|
||||||
employeeFormState.imageDialog = true;
|
employeeFormState.imageDialog = true;
|
||||||
|
|
@ -2870,12 +2816,30 @@ const emptyCreateDialog = ref(false);
|
||||||
id="btn-info-basic-delete"
|
id="btn-info-basic-delete"
|
||||||
icon-only
|
icon-only
|
||||||
@click="
|
@click="
|
||||||
() => deleteEmployeeById({ id: currentFromDataEmployee.id })
|
() =>
|
||||||
|
deleteEmployeeById({
|
||||||
|
id: currentFromDataEmployee.id,
|
||||||
|
fetch: async () =>
|
||||||
|
await fetchListEmployee(
|
||||||
|
currentTab === 'employer'
|
||||||
|
? {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 999,
|
||||||
|
customerId:
|
||||||
|
customerFormState.currentCustomerId,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
fetchStats: true,
|
||||||
|
mobileFetch: $q.screen.xs,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
})
|
||||||
"
|
"
|
||||||
type="button"
|
type="button"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<BasicInformation
|
<BasicInformation
|
||||||
no-action
|
no-action
|
||||||
id="form-information"
|
id="form-information"
|
||||||
|
|
@ -2888,11 +2852,13 @@ const emptyCreateDialog = ref(false);
|
||||||
title="form.field.basicInformation"
|
title="form.field.basicInformation"
|
||||||
:readonly="!employeeFormState.isEmployeeEdit"
|
:readonly="!employeeFormState.isEmployeeEdit"
|
||||||
:employee-owner-option="employeeStore.ownerOption || []"
|
:employee-owner-option="employeeStore.ownerOption || []"
|
||||||
v-model:customer-branch="employeeFormState.formDataEmployeeOwner"
|
v-model:customer-branch-id="employeeFormState.currentBranchId"
|
||||||
|
v-model:current-customer-branch="
|
||||||
|
employeeFormState.currentCustomerBranch
|
||||||
|
"
|
||||||
v-model:employee-id="employeeFormState.currentEmployeeCode"
|
v-model:employee-id="employeeFormState.currentEmployeeCode"
|
||||||
v-model:nrc-no="currentFromDataEmployee.nrcNo"
|
v-model:nrc-no="currentFromDataEmployee.nrcNo"
|
||||||
v-model:code="currentFromDataEmployee.code"
|
v-model:code="currentFromDataEmployee.code"
|
||||||
@filter-owner-branch="employeeFormStore.employeeFilterOwnerBranch"
|
|
||||||
class="q-mb-xl"
|
class="q-mb-xl"
|
||||||
/>
|
/>
|
||||||
<FormPerson
|
<FormPerson
|
||||||
|
|
@ -2918,6 +2884,7 @@ const emptyCreateDialog = ref(false);
|
||||||
class="q-mb-xl"
|
class="q-mb-xl"
|
||||||
/>
|
/>
|
||||||
<AddressForm
|
<AddressForm
|
||||||
|
disabledRule
|
||||||
id="form-personal-address"
|
id="form-personal-address"
|
||||||
prefix-id="form-employee"
|
prefix-id="form-employee"
|
||||||
:readonly="!employeeFormState.isEmployeeEdit"
|
:readonly="!employeeFormState.isEmployeeEdit"
|
||||||
|
|
@ -2940,7 +2907,7 @@ const emptyCreateDialog = ref(false);
|
||||||
class="q-mb-xl"
|
class="q-mb-xl"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="row q-mb-md" id="drawer-info-file-upload">
|
<div class="row q-mb-md" id="form-info-file-upload">
|
||||||
<div class="col-12 q-pb-sm text-weight-bold text-body1">
|
<div class="col-12 q-pb-sm text-weight-bold text-body1">
|
||||||
<q-icon
|
<q-icon
|
||||||
flat
|
flat
|
||||||
|
|
@ -3448,7 +3415,24 @@ const emptyCreateDialog = ref(false);
|
||||||
@click.stop="
|
@click.stop="
|
||||||
() => {
|
() => {
|
||||||
employeeFormState.currentIndexPassport = index;
|
employeeFormState.currentIndexPassport = index;
|
||||||
deleteEmployeeById({ type: 'passport', index });
|
deleteEmployeeById({
|
||||||
|
type: 'passport',
|
||||||
|
index,
|
||||||
|
fetch: async () =>
|
||||||
|
await fetchListEmployee(
|
||||||
|
currentTab === 'employer'
|
||||||
|
? {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 999,
|
||||||
|
customerId:
|
||||||
|
customerFormState.currentCustomerId,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
fetchStats: true,
|
||||||
|
mobileFetch: $q.screen.xs,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -3599,7 +3583,24 @@ const emptyCreateDialog = ref(false);
|
||||||
@click.stop="
|
@click.stop="
|
||||||
() => {
|
() => {
|
||||||
employeeFormState.currentIndexVisa = index;
|
employeeFormState.currentIndexVisa = index;
|
||||||
deleteEmployeeById({ type: 'visa', index });
|
deleteEmployeeById({
|
||||||
|
type: 'visa',
|
||||||
|
index,
|
||||||
|
fetch: async () =>
|
||||||
|
await fetchListEmployee(
|
||||||
|
currentTab === 'employer'
|
||||||
|
? {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 999,
|
||||||
|
customerId:
|
||||||
|
customerFormState.currentCustomerId,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
fetchStats: true,
|
||||||
|
mobileFetch: $q.screen.xs,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -3671,7 +3672,23 @@ const emptyCreateDialog = ref(false);
|
||||||
@delete="
|
@delete="
|
||||||
(index) => {
|
(index) => {
|
||||||
employeeFormState.currentIndexCheckup = index;
|
employeeFormState.currentIndexCheckup = index;
|
||||||
deleteEmployeeById({ type: 'healthCheck', index });
|
deleteEmployeeById({
|
||||||
|
type: 'healthCheck',
|
||||||
|
index,
|
||||||
|
fetch: async () =>
|
||||||
|
await fetchListEmployee(
|
||||||
|
currentTab === 'employer'
|
||||||
|
? {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 999,
|
||||||
|
customerId: customerFormState.currentCustomerId,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
fetchStats: true,
|
||||||
|
mobileFetch: $q.screen.xs,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
@save="
|
@save="
|
||||||
|
|
@ -3757,7 +3774,23 @@ const emptyCreateDialog = ref(false);
|
||||||
@delete="
|
@delete="
|
||||||
(index) => {
|
(index) => {
|
||||||
employeeFormState.currentIndexWorkHistory = index;
|
employeeFormState.currentIndexWorkHistory = index;
|
||||||
deleteEmployeeById({ type: 'work', index });
|
deleteEmployeeById({
|
||||||
|
type: 'work',
|
||||||
|
index,
|
||||||
|
fetch: async () =>
|
||||||
|
await fetchListEmployee(
|
||||||
|
currentTab === 'employer'
|
||||||
|
? {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 999,
|
||||||
|
customerId: customerFormState.currentCustomerId,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
fetchStats: true,
|
||||||
|
mobileFetch: $q.screen.xs,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
@save="
|
@save="
|
||||||
|
|
@ -4104,7 +4137,17 @@ const emptyCreateDialog = ref(false);
|
||||||
"
|
"
|
||||||
:title="
|
:title="
|
||||||
customerFormData.customerType === 'PERS'
|
customerFormData.customerType === 'PERS'
|
||||||
? `${customerFormData.customerBranch[0]?.firstName} ${customerFormData.customerBranch[0]?.lastName}`
|
? setPrefixName(
|
||||||
|
{
|
||||||
|
namePrefix: customerFormData.customerBranch[0]?.namePrefix,
|
||||||
|
firstName: customerFormData.customerBranch[0]?.firstName,
|
||||||
|
lastName: customerFormData.customerBranch[0]?.lastName,
|
||||||
|
firstNameEN:
|
||||||
|
customerFormData.customerBranch[0]?.firstNameEN,
|
||||||
|
lastNameEN: customerFormData.customerBranch[0]?.lastNameEN,
|
||||||
|
},
|
||||||
|
{ locale },
|
||||||
|
)
|
||||||
: customerFormData.customerBranch[0]?.registerName
|
: customerFormData.customerBranch[0]?.registerName
|
||||||
"
|
"
|
||||||
:caption="
|
:caption="
|
||||||
|
|
@ -4218,13 +4261,16 @@ const emptyCreateDialog = ref(false);
|
||||||
id="form-basic-info-customer"
|
id="form-basic-info-customer"
|
||||||
:onCreate="customerFormState.dialogType === 'create'"
|
:onCreate="customerFormState.dialogType === 'create'"
|
||||||
@edit="
|
@edit="
|
||||||
(customerFormState.dialogType = 'edit'),
|
((customerFormState.dialogType = 'edit'),
|
||||||
(customerFormState.readonly = false)
|
(customerFormState.readonly = false))
|
||||||
"
|
"
|
||||||
@cancel="() => customerFormUndo(false)"
|
@cancel="() => customerFormUndo(false)"
|
||||||
@delete="
|
@delete="
|
||||||
customerFormState.editCustomerId &&
|
customerFormState.editCustomerId &&
|
||||||
deleteCustomerById(customerFormState.editCustomerId)
|
deleteCustomerById(
|
||||||
|
customerFormState.editCustomerId,
|
||||||
|
async () => await fetchListCustomer(true, $q.screen.xs),
|
||||||
|
)
|
||||||
"
|
"
|
||||||
:customer-type="customerFormData.customerType"
|
:customer-type="customerFormData.customerType"
|
||||||
v-model:registered-branch-id="customerFormData.registeredBranchId"
|
v-model:registered-branch-id="customerFormData.registeredBranchId"
|
||||||
|
|
@ -4524,9 +4570,16 @@ const emptyCreateDialog = ref(false);
|
||||||
fallback-cover="/images/employee-banner.png"
|
fallback-cover="/images/employee-banner.png"
|
||||||
:title="
|
:title="
|
||||||
employeeFormState.currentEmployee
|
employeeFormState.currentEmployee
|
||||||
? $i18n.locale === 'eng'
|
? setPrefixName(
|
||||||
? `${employeeFormState.currentEmployee.firstNameEN} ${employeeFormState.currentEmployee.lastNameEN}`
|
{
|
||||||
: `${employeeFormState.currentEmployee.firstName} ${employeeFormState.currentEmployee.lastName}`
|
namePrefix: employeeFormState.currentEmployee.namePrefix,
|
||||||
|
firstName: employeeFormState.currentEmployee.firstName,
|
||||||
|
lastName: employeeFormState.currentEmployee.lastName,
|
||||||
|
firstNameEN: employeeFormState.currentEmployee.firstNameEN,
|
||||||
|
lastNameEN: employeeFormState.currentEmployee.lastNameEN,
|
||||||
|
},
|
||||||
|
{ locale },
|
||||||
|
)
|
||||||
: '-'
|
: '-'
|
||||||
"
|
"
|
||||||
:caption="currentFromDataEmployee.code"
|
:caption="currentFromDataEmployee.code"
|
||||||
|
|
@ -4821,7 +4874,23 @@ const emptyCreateDialog = ref(false);
|
||||||
icon-only
|
icon-only
|
||||||
@click="
|
@click="
|
||||||
() =>
|
() =>
|
||||||
deleteEmployeeById({ id: currentFromDataEmployee.id })
|
deleteEmployeeById({
|
||||||
|
id: currentFromDataEmployee.id,
|
||||||
|
fetch: async () =>
|
||||||
|
await fetchListEmployee(
|
||||||
|
currentTab === 'employer'
|
||||||
|
? {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 999,
|
||||||
|
customerId:
|
||||||
|
customerFormState.currentCustomerId,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
fetchStats: true,
|
||||||
|
mobileFetch: $q.screen.xs,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
})
|
||||||
"
|
"
|
||||||
type="button"
|
type="button"
|
||||||
/>
|
/>
|
||||||
|
|
@ -4835,16 +4904,13 @@ const emptyCreateDialog = ref(false);
|
||||||
outlined
|
outlined
|
||||||
title="form.field.basicInformation"
|
title="form.field.basicInformation"
|
||||||
:readonly="!employeeFormState.isEmployeeEdit"
|
:readonly="!employeeFormState.isEmployeeEdit"
|
||||||
:employee-owner-option="employeeStore.ownerOption"
|
v-model:customer-branch-id="employeeFormState.currentBranchId"
|
||||||
v-model:customer-branch="
|
v-model:current-customer-branch="
|
||||||
employeeFormState.formDataEmployeeOwner
|
employeeFormState.currentCustomerBranch
|
||||||
"
|
"
|
||||||
v-model:employee-id="employeeFormState.currentEmployeeCode"
|
v-model:employee-id="employeeFormState.currentEmployeeCode"
|
||||||
v-model:nrc-no="currentFromDataEmployee.nrcNo"
|
v-model:nrc-no="currentFromDataEmployee.nrcNo"
|
||||||
v-model:code="currentFromDataEmployee.code"
|
v-model:code="currentFromDataEmployee.code"
|
||||||
@filter-owner-branch="
|
|
||||||
employeeFormStore.employeeFilterOwnerBranch
|
|
||||||
"
|
|
||||||
class="q-mb-xl"
|
class="q-mb-xl"
|
||||||
/>
|
/>
|
||||||
<FormPerson
|
<FormPerson
|
||||||
|
|
@ -4871,6 +4937,7 @@ const emptyCreateDialog = ref(false);
|
||||||
<AddressForm
|
<AddressForm
|
||||||
id="drawer-form-personal-address"
|
id="drawer-form-personal-address"
|
||||||
employee
|
employee
|
||||||
|
disabledRule
|
||||||
v-model:address="currentFromDataEmployee.address"
|
v-model:address="currentFromDataEmployee.address"
|
||||||
v-model:address-en="currentFromDataEmployee.addressEN"
|
v-model:address-en="currentFromDataEmployee.addressEN"
|
||||||
v-model:moo="currentFromDataEmployee.moo"
|
v-model:moo="currentFromDataEmployee.moo"
|
||||||
|
|
@ -5450,6 +5517,20 @@ const emptyCreateDialog = ref(false);
|
||||||
deleteEmployeeById({
|
deleteEmployeeById({
|
||||||
type: 'passport',
|
type: 'passport',
|
||||||
index,
|
index,
|
||||||
|
fetch: async () =>
|
||||||
|
await fetchListEmployee(
|
||||||
|
currentTab === 'employer'
|
||||||
|
? {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 999,
|
||||||
|
customerId:
|
||||||
|
customerFormState.currentCustomerId,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
fetchStats: true,
|
||||||
|
mobileFetch: $q.screen.xs,
|
||||||
|
},
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
|
|
@ -5621,7 +5702,24 @@ const emptyCreateDialog = ref(false);
|
||||||
icon-only
|
icon-only
|
||||||
@click.stop="
|
@click.stop="
|
||||||
() => {
|
() => {
|
||||||
deleteEmployeeById({ type: 'visa', index });
|
deleteEmployeeById({
|
||||||
|
type: 'visa',
|
||||||
|
index,
|
||||||
|
fetch: async () =>
|
||||||
|
await fetchListEmployee(
|
||||||
|
currentTab === 'employer'
|
||||||
|
? {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 999,
|
||||||
|
customerId:
|
||||||
|
customerFormState.currentCustomerId,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
fetchStats: true,
|
||||||
|
mobileFetch: $q.screen.xs,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -5742,7 +5840,24 @@ const emptyCreateDialog = ref(false);
|
||||||
@delete="
|
@delete="
|
||||||
(index) => {
|
(index) => {
|
||||||
employeeFormState.currentIndexCheckup = index;
|
employeeFormState.currentIndexCheckup = index;
|
||||||
deleteEmployeeById({ type: 'healthCheck', index });
|
deleteEmployeeById({
|
||||||
|
type: 'healthCheck',
|
||||||
|
index,
|
||||||
|
fetch: async () =>
|
||||||
|
await fetchListEmployee(
|
||||||
|
currentTab === 'employer'
|
||||||
|
? {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 999,
|
||||||
|
customerId:
|
||||||
|
customerFormState.currentCustomerId,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
fetchStats: true,
|
||||||
|
mobileFetch: $q.screen.xs,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
|
@ -5812,7 +5927,24 @@ const emptyCreateDialog = ref(false);
|
||||||
@delete="
|
@delete="
|
||||||
(index) => {
|
(index) => {
|
||||||
employeeFormState.currentIndexWorkHistory = index;
|
employeeFormState.currentIndexWorkHistory = index;
|
||||||
deleteEmployeeById({ type: 'work', index });
|
deleteEmployeeById({
|
||||||
|
type: 'work',
|
||||||
|
index,
|
||||||
|
fetch: async () =>
|
||||||
|
await fetchListEmployee(
|
||||||
|
currentTab === 'employer'
|
||||||
|
? {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 999,
|
||||||
|
customerId:
|
||||||
|
customerFormState.currentCustomerId,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
fetchStats: true,
|
||||||
|
mobileFetch: $q.screen.xs,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
@save="
|
@save="
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,50 @@
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import {
|
import {
|
||||||
|
CustomerBranch,
|
||||||
CustomerBranchCreate,
|
CustomerBranchCreate,
|
||||||
CustomerCreate,
|
CustomerCreate,
|
||||||
CustomerType,
|
CustomerType,
|
||||||
} from 'stores/customer/types';
|
} from 'stores/customer/types';
|
||||||
import { Employee, EmployeeCreate } from 'stores/employee/types';
|
import { Employee, EmployeeCreate } from 'stores/employee/types';
|
||||||
|
|
||||||
|
import { dialog } from 'stores/utils';
|
||||||
import useMyBranch from 'stores/my-branch';
|
import useMyBranch from 'stores/my-branch';
|
||||||
import useCustomerStore from 'stores/customer';
|
import useCustomerStore from 'stores/customer';
|
||||||
import useEmployeeStore from 'stores/employee';
|
import useEmployeeStore from 'stores/employee';
|
||||||
import useFlowStore from 'stores/flow';
|
import useFlowStore from 'stores/flow';
|
||||||
|
import useMyBranchStore from 'stores/my-branch';
|
||||||
|
|
||||||
import { baseUrl } from 'src/stores/utils';
|
import { baseUrl } from 'src/stores/utils';
|
||||||
|
import { getRole, getUserId } from 'src/services/keycloak';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
export const useCustomerForm = defineStore('form-customer', () => {
|
export const useCustomerForm = defineStore('form-customer', () => {
|
||||||
const customerStore = useCustomerStore();
|
const customerStore = useCustomerStore();
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const flowStore = useFlowStore();
|
||||||
|
|
||||||
|
const userBranchStore = useMyBranchStore();
|
||||||
|
|
||||||
|
const registerAbleBranchOption = ref<{ id: string; name: string }[]>();
|
||||||
|
|
||||||
|
const tabFieldRequired = ref<{
|
||||||
|
[key: string]: (keyof CustomerBranchCreate)[];
|
||||||
|
}>({
|
||||||
|
main: [],
|
||||||
|
business: ['businessType', 'jobPosition'],
|
||||||
|
address: [
|
||||||
|
'address',
|
||||||
|
'addressEN',
|
||||||
|
'provinceId',
|
||||||
|
'districtId',
|
||||||
|
'subDistrictId',
|
||||||
|
],
|
||||||
|
contact: [],
|
||||||
|
});
|
||||||
|
|
||||||
const defaultFormData: CustomerCreate = {
|
const defaultFormData: CustomerCreate = {
|
||||||
// code: '',
|
// code: '',
|
||||||
// namePrefix: '',
|
// namePrefix: '',
|
||||||
|
|
@ -360,7 +388,118 @@ export const useCustomerForm = defineStore('form-customer', () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchListOfOptionBranch() {
|
||||||
|
if (registerAbleBranchOption.value) return;
|
||||||
|
|
||||||
|
const uid = getUserId();
|
||||||
|
const role = getRole();
|
||||||
|
|
||||||
|
if (!uid) return; // should not possible as the system require login to be able to access resource.
|
||||||
|
|
||||||
|
if (role?.includes('system')) {
|
||||||
|
const result = await userBranchStore.fetchListOptionBranch();
|
||||||
|
if (result && result.total > 0)
|
||||||
|
registerAbleBranchOption.value = result.result;
|
||||||
|
} else {
|
||||||
|
const result = await userBranchStore.fetchListMyBranch(uid);
|
||||||
|
if (result && result.total > 0)
|
||||||
|
registerAbleBranchOption.value = result.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Assign (first) branch of the user as register branch of the data
|
||||||
|
}
|
||||||
|
|
||||||
|
function customerFormUndo(close = true) {
|
||||||
|
if (isFormDataDifferent()) {
|
||||||
|
return customerConfirmUnsave(close);
|
||||||
|
}
|
||||||
|
resetForm();
|
||||||
|
state.value.readonly = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function customerConfirmUnsave(close = true) {
|
||||||
|
dialog({
|
||||||
|
color: 'warning',
|
||||||
|
icon: 'mdi-alert',
|
||||||
|
title: t('form.warning.title'),
|
||||||
|
actionText: t('general.ok'),
|
||||||
|
persistent: true,
|
||||||
|
message: t('form.warning.unsave'),
|
||||||
|
|
||||||
|
action: () => {
|
||||||
|
resetForm();
|
||||||
|
state.value.readonly = true;
|
||||||
|
|
||||||
|
if (!state.value.drawerModal) {
|
||||||
|
state.value.dialogModal = !close;
|
||||||
|
} else {
|
||||||
|
state.value.drawerModal = !close;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cancel: () => {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteCustomerById(
|
||||||
|
id: string,
|
||||||
|
fetch?: (...args: unknown[]) => unknown,
|
||||||
|
) {
|
||||||
|
dialog({
|
||||||
|
color: 'negative',
|
||||||
|
icon: 'mdi-alert',
|
||||||
|
title: t('dialog.title.confirmDelete'),
|
||||||
|
actionText: t('general.delete'),
|
||||||
|
persistent: true,
|
||||||
|
message: t('dialog.message.confirmDelete'),
|
||||||
|
action: async () => {
|
||||||
|
await customerStore.deleteById(id);
|
||||||
|
await fetch();
|
||||||
|
state.value.dialogModal = false;
|
||||||
|
flowStore.rotate();
|
||||||
|
},
|
||||||
|
cancel: () => {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateTabField<T = CustomerBranchCreate>(
|
||||||
|
value: T,
|
||||||
|
fieldRequired: { [key: string]: (keyof T)[] },
|
||||||
|
) {
|
||||||
|
const list: string[] = [];
|
||||||
|
|
||||||
|
for (const tab in fieldRequired) {
|
||||||
|
for (const field of fieldRequired[tab]) {
|
||||||
|
if (!value[field] && !list.includes(tab)) list.push(tab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteCustomerBranchById(id: string) {
|
||||||
|
return await new Promise((resolve) => {
|
||||||
|
dialog({
|
||||||
|
color: 'negative',
|
||||||
|
icon: 'mdi-alert',
|
||||||
|
title: t('dialog.title.confirmDelete'),
|
||||||
|
actionText: t('general.delete'),
|
||||||
|
persistent: true,
|
||||||
|
message: t('dialog.message.confirmDelete'),
|
||||||
|
action: async () => {
|
||||||
|
await customerStore.deleteBranchById(id);
|
||||||
|
flowStore.rotate();
|
||||||
|
resolve(true);
|
||||||
|
},
|
||||||
|
cancel: () => {
|
||||||
|
resolve(false);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
tabFieldRequired,
|
||||||
|
registerAbleBranchOption,
|
||||||
state,
|
state,
|
||||||
resetFormData,
|
resetFormData,
|
||||||
currentFormData,
|
currentFormData,
|
||||||
|
|
@ -370,13 +509,18 @@ export const useCustomerForm = defineStore('form-customer', () => {
|
||||||
submitFormCustomer,
|
submitFormCustomer,
|
||||||
addCurrentCustomerBranch,
|
addCurrentCustomerBranch,
|
||||||
deleteAttachment,
|
deleteAttachment,
|
||||||
|
fetchListOfOptionBranch,
|
||||||
|
customerFormUndo,
|
||||||
|
customerConfirmUnsave,
|
||||||
|
deleteCustomerById,
|
||||||
|
validateTabField,
|
||||||
|
deleteCustomerBranchById,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useCustomerBranchForm = defineStore('form-customer-branch', () => {
|
export const useCustomerBranchForm = defineStore('form-customer-branch', () => {
|
||||||
const customerStore = useCustomerStore();
|
const customerStore = useCustomerStore();
|
||||||
const customerFormStore = useCustomerForm();
|
const customerFormStore = useCustomerForm();
|
||||||
|
|
||||||
const defaultFormData: CustomerBranchCreate & {
|
const defaultFormData: CustomerBranchCreate & {
|
||||||
id?: string;
|
id?: string;
|
||||||
codeCustomer?: string;
|
codeCustomer?: string;
|
||||||
|
|
@ -581,11 +725,23 @@ export const useCustomerBranchForm = defineStore('form-customer-branch', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useEmployeeForm = defineStore('form-employee', () => {
|
export const useEmployeeForm = defineStore('form-employee', () => {
|
||||||
|
const { t } = useI18n();
|
||||||
const customerStore = useCustomerStore();
|
const customerStore = useCustomerStore();
|
||||||
const employeeStore = useEmployeeStore();
|
const employeeStore = useEmployeeStore();
|
||||||
const flowStore = useFlowStore();
|
const flowStore = useFlowStore();
|
||||||
const branchStore = useMyBranch();
|
const branchStore = useMyBranch();
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const refreshImageState = ref(false);
|
||||||
|
|
||||||
|
const onCreateImageList = ref<{
|
||||||
|
selectedImage: string;
|
||||||
|
list: { url: string; imgFile: File | null; name: string }[];
|
||||||
|
}>({ selectedImage: '', list: [] });
|
||||||
|
|
||||||
|
const statusEmployeeCreate = ref<boolean>(false);
|
||||||
|
|
||||||
const state = ref<{
|
const state = ref<{
|
||||||
dialogType: 'info' | 'create' | 'edit';
|
dialogType: 'info' | 'create' | 'edit';
|
||||||
imageDialog: boolean;
|
imageDialog: boolean;
|
||||||
|
|
@ -594,6 +750,8 @@ export const useEmployeeForm = defineStore('form-employee', () => {
|
||||||
drawerModal: boolean;
|
drawerModal: boolean;
|
||||||
isImageEdit: boolean;
|
isImageEdit: boolean;
|
||||||
|
|
||||||
|
currentBranchId: string;
|
||||||
|
currentCustomerBranch?: CustomerBranch;
|
||||||
currentEmployeeCode: string;
|
currentEmployeeCode: string;
|
||||||
currentEmployee: Employee | null;
|
currentEmployee: Employee | null;
|
||||||
currentIndexPassport: number;
|
currentIndexPassport: number;
|
||||||
|
|
@ -627,6 +785,7 @@ export const useEmployeeForm = defineStore('form-employee', () => {
|
||||||
| undefined;
|
| undefined;
|
||||||
ocr: boolean;
|
ocr: boolean;
|
||||||
}>({
|
}>({
|
||||||
|
currentBranchId: '',
|
||||||
isImageEdit: false,
|
isImageEdit: false,
|
||||||
currentIndexPassport: -1,
|
currentIndexPassport: -1,
|
||||||
currentIndexVisa: -1,
|
currentIndexVisa: -1,
|
||||||
|
|
@ -1089,6 +1248,8 @@ export const useEmployeeForm = defineStore('form-employee', () => {
|
||||||
selectedImage: string;
|
selectedImage: string;
|
||||||
list: { url: string; imgFile: File | null; name: string }[];
|
list: { url: string; imgFile: File | null; name: string }[];
|
||||||
}) {
|
}) {
|
||||||
|
let employeeId: string | undefined = undefined;
|
||||||
|
|
||||||
currentFromDataEmployee.value.firstName =
|
currentFromDataEmployee.value.firstName =
|
||||||
currentFromDataEmployee.value.firstName.trim();
|
currentFromDataEmployee.value.firstName.trim();
|
||||||
currentFromDataEmployee.value.middleName =
|
currentFromDataEmployee.value.middleName =
|
||||||
|
|
@ -1107,7 +1268,7 @@ export const useEmployeeForm = defineStore('form-employee', () => {
|
||||||
const res = await employeeStore.create(
|
const res = await employeeStore.create(
|
||||||
{
|
{
|
||||||
...currentFromDataEmployee.value,
|
...currentFromDataEmployee.value,
|
||||||
customerBranchId: state.value.formDataEmployeeOwner?.id || '',
|
customerBranchId: state.value.currentBranchId || '',
|
||||||
|
|
||||||
employeeWork: [],
|
employeeWork: [],
|
||||||
employeeCheckup: [],
|
employeeCheckup: [],
|
||||||
|
|
@ -1117,6 +1278,7 @@ export const useEmployeeForm = defineStore('form-employee', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
|
employeeId = res.id;
|
||||||
await assignFormDataEmployee(res.id);
|
await assignFormDataEmployee(res.id);
|
||||||
currentFromDataEmployee.value.id = res.id;
|
currentFromDataEmployee.value.id = res.id;
|
||||||
state.value.statusSavePersonal = true;
|
state.value.statusSavePersonal = true;
|
||||||
|
|
@ -1138,10 +1300,12 @@ export const useEmployeeForm = defineStore('form-employee', () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (res) {
|
if (res) {
|
||||||
|
employeeId = res.id;
|
||||||
await assignFormDataEmployee(res.id);
|
await assignFormDataEmployee(res.id);
|
||||||
state.value.statusSavePersonal = true;
|
state.value.statusSavePersonal = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return employeeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function assignFormDataEmployee(id?: string) {
|
async function assignFormDataEmployee(id?: string) {
|
||||||
|
|
@ -1183,7 +1347,19 @@ export const useEmployeeForm = defineStore('form-employee', () => {
|
||||||
employeePassport: structuredClone(
|
employeePassport: structuredClone(
|
||||||
payload.employeePassport?.length === 0
|
payload.employeePassport?.length === 0
|
||||||
? state.value.dialogModal
|
? state.value.dialogModal
|
||||||
? defaultFormData.employeePassport
|
? defaultFormData.employeePassport.map((v) => ({
|
||||||
|
...v,
|
||||||
|
namePrefix: payload.namePrefix,
|
||||||
|
firstName: payload.firstName,
|
||||||
|
firstNameEN: payload.firstNameEN,
|
||||||
|
middleName: payload.middleName,
|
||||||
|
middleNameEN: payload.middleNameEN,
|
||||||
|
lastName: payload.lastName,
|
||||||
|
lastNameEN: payload.lastNameEN,
|
||||||
|
gender: payload.gender,
|
||||||
|
nationality: payload.nationality,
|
||||||
|
birthDate: payload.dateOfBirth,
|
||||||
|
}))
|
||||||
: []
|
: []
|
||||||
: payload.employeePassport,
|
: payload.employeePassport,
|
||||||
),
|
),
|
||||||
|
|
@ -1270,6 +1446,8 @@ export const useEmployeeForm = defineStore('form-employee', () => {
|
||||||
state.value.currentIndexVisa = -1;
|
state.value.currentIndexVisa = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.value.currentBranchId = payload.customerBranchId;
|
||||||
|
|
||||||
const foundBranch = await customerStore.fetchListCustomerBranchById(
|
const foundBranch = await customerStore.fetchListCustomerBranchById(
|
||||||
payload.customerBranchId,
|
payload.customerBranchId,
|
||||||
);
|
);
|
||||||
|
|
@ -1325,17 +1503,17 @@ export const useEmployeeForm = defineStore('form-employee', () => {
|
||||||
issueDate: new Date(),
|
issueDate: new Date(),
|
||||||
type: '',
|
type: '',
|
||||||
expireDate: new Date(),
|
expireDate: new Date(),
|
||||||
birthDate: new Date(),
|
birthDate: currentFromDataEmployee.value.dateOfBirth,
|
||||||
workerStatus: '',
|
workerStatus: '',
|
||||||
nationality: '',
|
nationality: currentFromDataEmployee.value.nationality,
|
||||||
gender: '',
|
gender: currentFromDataEmployee.value.gender,
|
||||||
lastNameEN: '',
|
lastNameEN: currentFromDataEmployee.value.lastNameEN,
|
||||||
lastName: '',
|
lastName: currentFromDataEmployee.value.lastName,
|
||||||
middleNameEN: '',
|
middleNameEN: currentFromDataEmployee.value.middleNameEN,
|
||||||
middleName: '',
|
middleName: currentFromDataEmployee.value.middleName,
|
||||||
firstNameEN: '',
|
firstNameEN: currentFromDataEmployee.value.firstNameEN,
|
||||||
firstName: '',
|
firstName: currentFromDataEmployee.value.firstName,
|
||||||
namePrefix: '',
|
namePrefix: currentFromDataEmployee.value.namePrefix,
|
||||||
number: '',
|
number: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1396,7 +1574,88 @@ export const useEmployeeForm = defineStore('form-employee', () => {
|
||||||
(currentFromDataEmployee.value.employeeWork?.length || 0) - 1;
|
(currentFromDataEmployee.value.employeeWork?.length || 0) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function employeeFormUndo(close = true) {
|
||||||
|
if (isFormDataDifferent()) {
|
||||||
|
return employeeConfirmUnsave(close);
|
||||||
|
}
|
||||||
|
resetFormDataEmployee();
|
||||||
|
state.value.editReadonly = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function employeeConfirmUnsave(close = true) {
|
||||||
|
dialog({
|
||||||
|
color: 'warning',
|
||||||
|
icon: 'mdi-alert',
|
||||||
|
title: t('form.warning.title'),
|
||||||
|
actionText: t('general.ok'),
|
||||||
|
persistent: true,
|
||||||
|
message: t('form.warning.unsave'),
|
||||||
|
action: () => {
|
||||||
|
resetFormDataEmployee();
|
||||||
|
onCreateImageList.value = { selectedImage: '', list: [] };
|
||||||
|
state.value.editReadonly = true;
|
||||||
|
state.value.dialogModal = !close;
|
||||||
|
state.value.drawerModal = !close;
|
||||||
|
},
|
||||||
|
cancel: () => {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteEmployeeById(opts: {
|
||||||
|
id?: string;
|
||||||
|
type?: 'passport' | 'visa' | 'healthCheck' | 'work';
|
||||||
|
index?: number;
|
||||||
|
fetch?: (...args: unknown[]) => unknown;
|
||||||
|
removeArray?: (...args: unknown[]) => unknown;
|
||||||
|
}) {
|
||||||
|
dialog({
|
||||||
|
color: 'negative',
|
||||||
|
icon: 'mdi-alert',
|
||||||
|
title: t('dialog.title.confirmDelete'),
|
||||||
|
actionText: t('general.delete'),
|
||||||
|
persistent: true,
|
||||||
|
message: t('dialog.message.confirmDelete'),
|
||||||
|
action: async () => {
|
||||||
|
if (opts.type === 'passport' && opts.index !== undefined) {
|
||||||
|
await deletePassport(opts.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.type === 'visa' && opts.index !== undefined) {
|
||||||
|
await deleteVisa(opts.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.type === 'healthCheck' && opts.index !== undefined) {
|
||||||
|
await deleteHealthCheck(opts.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.type === 'work' && opts.index !== undefined) {
|
||||||
|
await deleteWorkHistory(opts.index);
|
||||||
|
} else {
|
||||||
|
if (!!opts.id) {
|
||||||
|
const result = await employeeStore.deleteById(opts.id);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
state.value.drawerModal = false;
|
||||||
|
state.value.dialogModal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (route.name !== 'CustomerBranchManagement') {
|
||||||
|
await opts.fetch?.();
|
||||||
|
flowStore.rotate();
|
||||||
|
}
|
||||||
|
opts.removeArray?.();
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel: () => {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
refreshImageState,
|
||||||
|
statusEmployeeCreate,
|
||||||
|
onCreateImageList,
|
||||||
state,
|
state,
|
||||||
currentFromDataEmployee,
|
currentFromDataEmployee,
|
||||||
resetEmployeeData,
|
resetEmployeeData,
|
||||||
|
|
@ -1423,5 +1682,9 @@ export const useEmployeeForm = defineStore('form-employee', () => {
|
||||||
employeeFilterOwnerBranch,
|
employeeFilterOwnerBranch,
|
||||||
|
|
||||||
isFormDataDifferent,
|
isFormDataDifferent,
|
||||||
|
|
||||||
|
employeeFormUndo,
|
||||||
|
employeeConfirmUnsave,
|
||||||
|
deleteEmployeeById,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ async function addStep() {
|
||||||
flowData.value.step.push({
|
flowData.value.step.push({
|
||||||
responsibleInstitution: [],
|
responsibleInstitution: [],
|
||||||
responsiblePersonId: [],
|
responsiblePersonId: [],
|
||||||
|
responsibleGroup: [],
|
||||||
value: [],
|
value: [],
|
||||||
detail: '',
|
detail: '',
|
||||||
name: '',
|
name: '',
|
||||||
|
|
@ -166,6 +167,7 @@ function triggerPropertiesDialog(step: WorkFlowPayloadStep) {
|
||||||
id="flow-form-dialog"
|
id="flow-form-dialog"
|
||||||
>
|
>
|
||||||
<FormFlow
|
<FormFlow
|
||||||
|
v-model:user-in-table="userInTable"
|
||||||
v-model:flow-data="flowData"
|
v-model:flow-data="flowData"
|
||||||
v-model:register-branch-id="registerBranchId"
|
v-model:register-branch-id="registerBranchId"
|
||||||
@trigger-properties="triggerPropertiesDialog"
|
@trigger-properties="triggerPropertiesDialog"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, reactive, ref, watch } from 'vue';
|
import { onMounted, reactive, ref, watch } from 'vue';
|
||||||
import { QSelect, QTableProps } from 'quasar';
|
import { QTableProps } from 'quasar';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
|
@ -22,6 +22,7 @@ import NoData from 'src/components/NoData.vue';
|
||||||
import KebabAction from 'src/components/shared/KebabAction.vue';
|
import KebabAction from 'src/components/shared/KebabAction.vue';
|
||||||
import PaginationPageSize from 'src/components/PaginationPageSize.vue';
|
import PaginationPageSize from 'src/components/PaginationPageSize.vue';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
|
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const workflowStore = useWorkflowTemplate();
|
const workflowStore = useWorkflowTemplate();
|
||||||
|
|
@ -45,6 +46,7 @@ const pageState = reactive({
|
||||||
addModal: false,
|
addModal: false,
|
||||||
viewDrawer: false,
|
viewDrawer: false,
|
||||||
isDrawerEdit: true,
|
isDrawerEdit: true,
|
||||||
|
searchDate: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const fieldSelected = ref<('order' | 'name' | 'step')[]>([
|
const fieldSelected = ref<('order' | 'name' | 'step')[]>([
|
||||||
|
|
@ -68,7 +70,6 @@ const fieldSelectedOption = ref<{ label: string; value: string }[]>([
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const refFilter = ref<InstanceType<typeof QSelect>>();
|
|
||||||
const currWorkflowData = ref<WorkflowTemplate>();
|
const currWorkflowData = ref<WorkflowTemplate>();
|
||||||
const formDataWorkflow = ref<WorkflowTemplatePayload>({
|
const formDataWorkflow = ref<WorkflowTemplatePayload>({
|
||||||
status: 'CREATED',
|
status: 'CREATED',
|
||||||
|
|
@ -102,6 +103,7 @@ const columns = [
|
||||||
function triggerDialog(type: 'add' | 'edit' | 'view') {
|
function triggerDialog(type: 'add' | 'edit' | 'view') {
|
||||||
if (type === 'add') {
|
if (type === 'add') {
|
||||||
registeredBranchId.value = '';
|
registeredBranchId.value = '';
|
||||||
|
userInTable.value = [];
|
||||||
formDataWorkflow.value = {
|
formDataWorkflow.value = {
|
||||||
status: 'CREATED',
|
status: 'CREATED',
|
||||||
name: '',
|
name: '',
|
||||||
|
|
@ -206,7 +208,7 @@ async function submit() {
|
||||||
...formDataWorkflow.value,
|
...formDataWorkflow.value,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await workflowStore.creatWorkflowTemplate({
|
await workflowStore.createWorkflowTemplate({
|
||||||
registeredBranchId: registeredBranchId.value,
|
registeredBranchId: registeredBranchId.value,
|
||||||
...formDataWorkflow.value,
|
...formDataWorkflow.value,
|
||||||
});
|
});
|
||||||
|
|
@ -222,7 +224,11 @@ function assignFormData(workflowData: WorkflowTemplate) {
|
||||||
status: workflowData.status,
|
status: workflowData.status,
|
||||||
name: workflowData.name,
|
name: workflowData.name,
|
||||||
step: workflowData.step.map((s, i) => {
|
step: workflowData.step.map((s, i) => {
|
||||||
userInTable.value[i] = { name: s.name, responsiblePerson: [] };
|
userInTable.value[i] = {
|
||||||
|
name: s.name,
|
||||||
|
responsiblePerson: [],
|
||||||
|
responsibleGroup: [],
|
||||||
|
};
|
||||||
s.responsiblePerson.forEach((p) => {
|
s.responsiblePerson.forEach((p) => {
|
||||||
userInTable.value[i].responsiblePerson.push({
|
userInTable.value[i].responsiblePerson.push({
|
||||||
id: p.user.id,
|
id: p.user.id,
|
||||||
|
|
@ -236,12 +242,16 @@ function assignFormData(workflowData: WorkflowTemplate) {
|
||||||
code: p.user.code,
|
code: p.user.code,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
s.responsibleGroup.forEach((g) => {
|
||||||
|
userInTable.value[i].responsibleGroup.push(g);
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
id: s.id,
|
id: s.id,
|
||||||
name: s.name,
|
name: s.name,
|
||||||
detail: s.detail,
|
detail: s.detail,
|
||||||
messengerByArea: s.messengerByArea || false,
|
messengerByArea: s.messengerByArea || false,
|
||||||
value: s.value.length > 0 ? JSON.parse(JSON.stringify(s.value)) : [],
|
value: s.value.length > 0 ? JSON.parse(JSON.stringify(s.value)) : [],
|
||||||
|
responsibleGroup: s.responsibleGroup.map((g) => g),
|
||||||
responsiblePersonId: s.responsiblePerson.map((p) => p.userId),
|
responsiblePersonId: s.responsiblePerson.map((p) => p.userId),
|
||||||
responsibleInstitution: JSON.parse(
|
responsibleInstitution: JSON.parse(
|
||||||
JSON.stringify(s.responsibleInstitution),
|
JSON.stringify(s.responsibleInstitution),
|
||||||
|
|
@ -282,6 +292,8 @@ async function fetchWorkflowList(mobileFetch?: boolean) {
|
||||||
: statusFilter.value === 'statusACTIVE'
|
: statusFilter.value === 'statusACTIVE'
|
||||||
? 'ACTIVE'
|
? 'ACTIVE'
|
||||||
: 'INACTIVE',
|
: 'INACTIVE',
|
||||||
|
startDate: pageState.searchDate[0],
|
||||||
|
endDate: pageState.searchDate[1],
|
||||||
});
|
});
|
||||||
if (res) {
|
if (res) {
|
||||||
workflowData.value =
|
workflowData.value =
|
||||||
|
|
@ -311,11 +323,14 @@ watch(
|
||||||
fetchWorkflowList();
|
fetchWorkflowList();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
watch([() => pageState.inputSearch, workflowPageSize], () => {
|
watch(
|
||||||
|
[() => pageState.inputSearch, workflowPageSize, () => pageState.searchDate],
|
||||||
|
() => {
|
||||||
workflowData.value = [];
|
workflowData.value = [];
|
||||||
workflowPage.value = 1;
|
workflowPage.value = 1;
|
||||||
fetchWorkflowList();
|
fetchWorkflowList();
|
||||||
});
|
},
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
|
|
@ -392,26 +407,44 @@ watch([() => pageState.inputSearch, workflowPageSize], () => {
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<q-icon name="mdi-magnify" />
|
<q-icon name="mdi-magnify" />
|
||||||
</template>
|
</template>
|
||||||
<template v-if="$q.screen.lt.md" v-slot:append>
|
<template v-slot:append>
|
||||||
<span class="row">
|
<q-separator vertical inset class="q-mr-xs" />
|
||||||
<q-separator vertical />
|
<AdvanceSearch
|
||||||
<q-btn
|
v-model="pageState.searchDate"
|
||||||
icon="mdi-filter-variant"
|
:active="$q.screen.lt.md && statusFilter !== 'all'"
|
||||||
unelevated
|
>
|
||||||
class="q-ml-sm"
|
<div
|
||||||
padding="4px"
|
v-if="$q.screen.lt.md"
|
||||||
size="sm"
|
class="q-mt-sm text-weight-medium"
|
||||||
rounded
|
>
|
||||||
@click="refFilter?.showPopup"
|
{{ $t('general.status') }}
|
||||||
|
</div>
|
||||||
|
<q-select
|
||||||
|
v-if="$q.screen.lt.md"
|
||||||
|
v-model="statusFilter"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
option-value="value"
|
||||||
|
option-label="label"
|
||||||
|
map-options
|
||||||
|
emit-value
|
||||||
|
:for="'field-select-status'"
|
||||||
|
:options="[
|
||||||
|
{ label: $t('general.all'), value: 'all' },
|
||||||
|
{ label: $t('general.active'), value: 'statusACTIVE' },
|
||||||
|
{
|
||||||
|
label: $t('general.inactive'),
|
||||||
|
value: 'statusINACTIVE',
|
||||||
|
},
|
||||||
|
]"
|
||||||
/>
|
/>
|
||||||
</span>
|
</AdvanceSearch>
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<div class="row col-md-5" style="white-space: nowrap">
|
<div class="row col-md-5" style="white-space: nowrap">
|
||||||
<q-select
|
<q-select
|
||||||
v-show="$q.screen.gt.sm"
|
v-if="$q.screen.gt.sm"
|
||||||
ref="refFilter"
|
|
||||||
v-model="statusFilter"
|
v-model="statusFilter"
|
||||||
outlined
|
outlined
|
||||||
dense
|
dense
|
||||||
|
|
@ -509,12 +542,12 @@ watch([() => pageState.inputSearch, workflowPageSize], () => {
|
||||||
class="col surface-2 flex items-center justify-center"
|
class="col surface-2 flex items-center justify-center"
|
||||||
>
|
>
|
||||||
<NoData
|
<NoData
|
||||||
v-if="pageState.total !== 0"
|
v-if="pageState.total !== 0 || pageState.searchDate.length > 0"
|
||||||
:not-found="!!pageState.inputSearch"
|
:not-found="!!pageState.inputSearch"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CreateButton
|
<CreateButton
|
||||||
v-if="pageState.total === 0"
|
v-if="pageState.total === 0 && pageState.searchDate.length === 0"
|
||||||
@click="triggerDialog('add')"
|
@click="triggerDialog('add')"
|
||||||
label="general.add"
|
label="general.add"
|
||||||
:i18n-args="{ text: $t('flow.title') }"
|
:i18n-args="{ text: $t('flow.title') }"
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { nextTick, ref, watch, reactive } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { onMounted } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { QSelect, useQuasar, type QTableProps } from 'quasar';
|
import { useQuasar, type QTableProps } from 'quasar';
|
||||||
|
|
||||||
import DialogProperties from 'src/components/dialog/DialogProperties.vue';
|
import DialogProperties from 'src/components/dialog/DialogProperties.vue';
|
||||||
import ProductCardComponent from 'components/04_product-service/ProductCardComponent.vue';
|
import ProductCardComponent from 'components/04_product-service/ProductCardComponent.vue';
|
||||||
|
|
@ -33,6 +33,7 @@ import {
|
||||||
SaveButton,
|
SaveButton,
|
||||||
UndoButton,
|
UndoButton,
|
||||||
ToggleButton,
|
ToggleButton,
|
||||||
|
ImportButton,
|
||||||
} from 'components/button';
|
} from 'components/button';
|
||||||
import TableProduct from 'src/components/04_product-service/TableProduct.vue';
|
import TableProduct from 'src/components/04_product-service/TableProduct.vue';
|
||||||
import PaginationPageSize from 'src/components/PaginationPageSize.vue';
|
import PaginationPageSize from 'src/components/PaginationPageSize.vue';
|
||||||
|
|
@ -40,7 +41,7 @@ import PaginationPageSize from 'src/components/PaginationPageSize.vue';
|
||||||
import useFlowStore from 'stores/flow';
|
import useFlowStore from 'stores/flow';
|
||||||
|
|
||||||
import { dateFormat } from 'src/utils/datetime';
|
import { dateFormat } from 'src/utils/datetime';
|
||||||
import { formatNumberDecimal, isRoleInclude } from 'stores/utils';
|
import { formatNumberDecimal, isRoleInclude, notify } from 'stores/utils';
|
||||||
const { getWorkflowTemplate } = useWorkflowTemplate();
|
const { getWorkflowTemplate } = useWorkflowTemplate();
|
||||||
|
|
||||||
import { Status } from 'stores/types';
|
import { Status } from 'stores/types';
|
||||||
|
|
@ -67,6 +68,8 @@ import {
|
||||||
} from 'src/stores/workflow-template/types';
|
} from 'src/stores/workflow-template/types';
|
||||||
import { useWorkflowTemplate } from 'src/stores/workflow-template';
|
import { useWorkflowTemplate } from 'src/stores/workflow-template';
|
||||||
import { deepEquals } from 'src/utils/arr';
|
import { deepEquals } from 'src/utils/arr';
|
||||||
|
import { toRaw } from 'vue';
|
||||||
|
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
||||||
|
|
||||||
const flowStore = useFlowStore();
|
const flowStore = useFlowStore();
|
||||||
const navigatorStore = useNavigator();
|
const navigatorStore = useNavigator();
|
||||||
|
|
@ -96,10 +99,13 @@ const {
|
||||||
createWork,
|
createWork,
|
||||||
editWork,
|
editWork,
|
||||||
deleteWork,
|
deleteWork,
|
||||||
|
|
||||||
|
importProduct,
|
||||||
} = productServiceStore;
|
} = productServiceStore;
|
||||||
|
|
||||||
const { workNameItems } = storeToRefs(productServiceStore);
|
const { workNameItems } = storeToRefs(productServiceStore);
|
||||||
const allStat = ref<{ mode: string; count: number }[]>([]);
|
const allStat = ref<{ mode: string; count: number }[]>([]);
|
||||||
|
|
||||||
const stat = ref<
|
const stat = ref<
|
||||||
{
|
{
|
||||||
icon: string;
|
icon: string;
|
||||||
|
|
@ -161,8 +167,6 @@ const splitterModel = computed(() =>
|
||||||
$q.screen.lt.md ? (productMode.value !== 'group' ? 0 : 100) : 25,
|
$q.screen.lt.md ? (productMode.value !== 'group' ? 0 : 100) : 25,
|
||||||
);
|
);
|
||||||
|
|
||||||
const refFilterGroup = ref<InstanceType<typeof QSelect>>();
|
|
||||||
const refFilterProductService = ref<InstanceType<typeof QSelect>>();
|
|
||||||
const holdDialog = ref(false);
|
const holdDialog = ref(false);
|
||||||
const imageDialog = ref(false);
|
const imageDialog = ref(false);
|
||||||
const currentNode = ref<ProductGroup & { type: string }>();
|
const currentNode = ref<ProductGroup & { type: string }>();
|
||||||
|
|
@ -520,6 +524,7 @@ const currentStatusGroupType = ref<Status>('CREATED');
|
||||||
const currentIdGroupType = ref('');
|
const currentIdGroupType = ref('');
|
||||||
|
|
||||||
const currentStatus = ref<Status | 'All'>('All');
|
const currentStatus = ref<Status | 'All'>('All');
|
||||||
|
const searchDate = ref<string[]>([]);
|
||||||
|
|
||||||
// img
|
// img
|
||||||
const isImageEdit = ref<boolean>(false);
|
const isImageEdit = ref<boolean>(false);
|
||||||
|
|
@ -615,6 +620,8 @@ async function fetchListGroups(mobileFetch?: boolean) {
|
||||||
: currentStatus.value === 'ACTIVE'
|
: currentStatus.value === 'ACTIVE'
|
||||||
? 'ACTIVE'
|
? 'ACTIVE'
|
||||||
: 'INACTIVE',
|
: 'INACTIVE',
|
||||||
|
startDate: searchDate.value[0],
|
||||||
|
endDate: searchDate.value[1],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
|
|
@ -675,6 +682,8 @@ async function fetchListOfProduct(mobileFetch?: boolean) {
|
||||||
? 'ACTIVE'
|
? 'ACTIVE'
|
||||||
: undefined,
|
: undefined,
|
||||||
productGroupId: currentIdGroup.value,
|
productGroupId: currentIdGroup.value,
|
||||||
|
startDate: searchDate.value[0],
|
||||||
|
endDate: searchDate.value[1],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
|
|
@ -720,6 +729,8 @@ async function fetchListOfService(mobileFetch?: boolean) {
|
||||||
? 'ACTIVE'
|
? 'ACTIVE'
|
||||||
: undefined,
|
: undefined,
|
||||||
productGroupId: currentIdGroup.value,
|
productGroupId: currentIdGroup.value,
|
||||||
|
startDate: searchDate.value[0],
|
||||||
|
endDate: searchDate.value[1],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
|
|
@ -1590,6 +1601,7 @@ async function enterNext(type: 'service' | 'product') {
|
||||||
inputSearchProductAndService.value = '';
|
inputSearchProductAndService.value = '';
|
||||||
currentStatus.value = 'All';
|
currentStatus.value = 'All';
|
||||||
filterStat.value = [];
|
filterStat.value = [];
|
||||||
|
searchDate.value = [];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
expandedTree.value.length > 1 &&
|
expandedTree.value.length > 1 &&
|
||||||
|
|
@ -1745,7 +1757,7 @@ watch(currentStatus, async () => {
|
||||||
flowStore.rotate();
|
flowStore.rotate();
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(inputSearch, async () => {
|
watch([inputSearch, () => searchDate.value], async () => {
|
||||||
if (productMode.value === 'group') {
|
if (productMode.value === 'group') {
|
||||||
productGroup.value = [];
|
productGroup.value = [];
|
||||||
currentPageGroup.value = 1;
|
currentPageGroup.value = 1;
|
||||||
|
|
@ -1754,7 +1766,7 @@ watch(inputSearch, async () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(inputSearchProductAndService, async () => {
|
watch([inputSearchProductAndService, () => searchDate.value], async () => {
|
||||||
product.value = [];
|
product.value = [];
|
||||||
service.value = [];
|
service.value = [];
|
||||||
currentPageServiceAndProduct.value = 1;
|
currentPageServiceAndProduct.value = 1;
|
||||||
|
|
@ -1831,6 +1843,51 @@ function handleSubmitSameWorkflow() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function copy(id: string) {
|
||||||
|
{
|
||||||
|
const res = await fetchListServiceById(id);
|
||||||
|
if (res) {
|
||||||
|
formService.value = {
|
||||||
|
code: res.code.slice(0, -3),
|
||||||
|
name: res.name,
|
||||||
|
detail: res.detail,
|
||||||
|
attributes: res.attributes,
|
||||||
|
work: res.work.map((v) => ({
|
||||||
|
id: v.id,
|
||||||
|
name: v.name,
|
||||||
|
attributes: v.attributes,
|
||||||
|
product: v.productOnWork.map((productOnWorkItem) => ({
|
||||||
|
id: productOnWorkItem.product.id,
|
||||||
|
installmentNo: productOnWorkItem.installmentNo,
|
||||||
|
stepCount: productOnWorkItem.stepCount,
|
||||||
|
})),
|
||||||
|
})),
|
||||||
|
status: res.status,
|
||||||
|
productGroupId: res.productGroupId,
|
||||||
|
selectedImage: res.selectedImage,
|
||||||
|
installments: res.installments,
|
||||||
|
};
|
||||||
|
|
||||||
|
workItems.value = res.work.map((item) => {
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
name: item.name,
|
||||||
|
attributes: item.attributes,
|
||||||
|
product: item.productOnWork.map((productOnWorkItem) => {
|
||||||
|
return {
|
||||||
|
...productOnWorkItem.product,
|
||||||
|
nameEn: productOnWorkItem.product.name,
|
||||||
|
installmentNo: productOnWorkItem.installmentNo,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dialogService.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => formService.value.attributes.workflowId,
|
() => formService.value.attributes.workflowId,
|
||||||
async (a, b) => {
|
async (a, b) => {
|
||||||
|
|
@ -1948,19 +2005,34 @@ watch(
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<q-icon name="mdi-magnify" />
|
<q-icon name="mdi-magnify" />
|
||||||
</template>
|
</template>
|
||||||
<template v-if="$q.screen.lt.md" v-slot:append>
|
<template v-slot:append>
|
||||||
<span class="row">
|
<q-separator vertical inset class="q-mr-xs" />
|
||||||
<q-separator vertical />
|
<AdvanceSearch
|
||||||
<q-btn
|
v-model="searchDate"
|
||||||
icon="mdi-filter-variant"
|
:active="$q.screen.lt.md && currentStatus !== 'All'"
|
||||||
unelevated
|
>
|
||||||
class="q-ml-sm"
|
<div class="q-mt-sm text-weight-medium">
|
||||||
padding="4px"
|
{{ $t('general.status') }}
|
||||||
size="sm"
|
</div>
|
||||||
rounded
|
<q-select
|
||||||
@click="refFilterGroup?.showPopup"
|
v-model="currentStatus"
|
||||||
|
for="select-status"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
option-value="value"
|
||||||
|
option-label="label"
|
||||||
|
map-options
|
||||||
|
emit-value
|
||||||
|
:options="[
|
||||||
|
{ label: $t('general.all'), value: 'All' },
|
||||||
|
{ label: $t('general.active'), value: 'ACTIVE' },
|
||||||
|
{
|
||||||
|
label: $t('general.inactive'),
|
||||||
|
value: 'INACTIVE',
|
||||||
|
},
|
||||||
|
]"
|
||||||
/>
|
/>
|
||||||
</span>
|
</AdvanceSearch>
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -2115,26 +2187,44 @@ watch(
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<q-icon name="mdi-magnify" />
|
<q-icon name="mdi-magnify" />
|
||||||
</template>
|
</template>
|
||||||
<template v-if="$q.screen.lt.md" v-slot:append>
|
<template v-slot:append>
|
||||||
<span class="row">
|
<q-separator vertical inset class="q-mr-xs" />
|
||||||
<q-separator vertical />
|
<AdvanceSearch
|
||||||
<q-btn
|
v-model="searchDate"
|
||||||
icon="mdi-filter-variant"
|
:active="$q.screen.lt.md && currentStatus !== 'All'"
|
||||||
unelevated
|
>
|
||||||
class="q-ml-sm"
|
<div
|
||||||
padding="4px"
|
v-if="$q.screen.lt.md"
|
||||||
size="sm"
|
class="q-mt-sm text-weight-medium"
|
||||||
rounded
|
>
|
||||||
@click="refFilterGroup?.showPopup"
|
{{ $t('general.status') }}
|
||||||
|
</div>
|
||||||
|
<q-select
|
||||||
|
v-if="$q.screen.lt.md"
|
||||||
|
v-model="currentStatus"
|
||||||
|
for="select-status"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
option-value="value"
|
||||||
|
option-label="label"
|
||||||
|
map-options
|
||||||
|
emit-value
|
||||||
|
:options="[
|
||||||
|
{ label: $t('general.all'), value: 'All' },
|
||||||
|
{ label: $t('general.active'), value: 'ACTIVE' },
|
||||||
|
{
|
||||||
|
label: $t('general.inactive'),
|
||||||
|
value: 'INACTIVE',
|
||||||
|
},
|
||||||
|
]"
|
||||||
/>
|
/>
|
||||||
</span>
|
</AdvanceSearch>
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<div class="row col-md-6" style="white-space: nowrap">
|
<div class="row col-md-6" style="white-space: nowrap">
|
||||||
<q-select
|
<q-select
|
||||||
v-show="$q.screen.gt.sm"
|
v-show="$q.screen.gt.sm"
|
||||||
ref="refFilterGroup"
|
|
||||||
v-model="currentStatus"
|
v-model="currentStatus"
|
||||||
for="select-status"
|
for="select-status"
|
||||||
outlined
|
outlined
|
||||||
|
|
@ -2155,7 +2245,6 @@ watch(
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
></q-select>
|
></q-select>
|
||||||
|
|
||||||
<q-select
|
<q-select
|
||||||
v-if="modeView === false"
|
v-if="modeView === false"
|
||||||
id="select-field"
|
id="select-field"
|
||||||
|
|
@ -2178,7 +2267,6 @@ watch(
|
||||||
multiple
|
multiple
|
||||||
dense
|
dense
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<q-btn-toggle
|
<q-btn-toggle
|
||||||
v-model="modeView"
|
v-model="modeView"
|
||||||
id="btn-mode"
|
id="btn-mode"
|
||||||
|
|
@ -2618,26 +2706,65 @@ watch(
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<q-icon name="mdi-magnify" />
|
<q-icon name="mdi-magnify" />
|
||||||
</template>
|
</template>
|
||||||
<template v-if="$q.screen.lt.md" v-slot:append>
|
<template v-slot:append>
|
||||||
<span class="row">
|
<q-separator vertical inset class="q-mr-xs" />
|
||||||
<q-separator vertical />
|
<AdvanceSearch
|
||||||
<q-btn
|
v-model="searchDate"
|
||||||
icon="mdi-filter-variant"
|
:active="$q.screen.lt.md && currentStatus !== 'All'"
|
||||||
unelevated
|
>
|
||||||
class="q-ml-sm"
|
<div
|
||||||
padding="4px"
|
v-if="$q.screen.lt.md"
|
||||||
size="sm"
|
class="q-mt-sm text-weight-medium"
|
||||||
rounded
|
>
|
||||||
@click="refFilterProductService?.showPopup"
|
{{ $t('general.status') }}
|
||||||
|
</div>
|
||||||
|
<q-select
|
||||||
|
v-if="$q.screen.lt.md"
|
||||||
|
:for="'field-select-status'"
|
||||||
|
v-model="currentStatus"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
option-value="value"
|
||||||
|
option-label="label"
|
||||||
|
map-options
|
||||||
|
emit-value
|
||||||
|
:options="[
|
||||||
|
{ label: $t('general.all'), value: 'All' },
|
||||||
|
{ label: $t('general.active'), value: 'ACTIVE' },
|
||||||
|
{
|
||||||
|
label: $t('general.inactive'),
|
||||||
|
value: 'INACTIVE',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
@update:model-value="fetchStatus()"
|
||||||
/>
|
/>
|
||||||
</span>
|
</AdvanceSearch>
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
<div
|
||||||
|
class="flex q-mr-auto q-pl-sm"
|
||||||
|
v-if="productAndServiceTab === 'product'"
|
||||||
|
>
|
||||||
|
<input ref="fileImport" type="file" hidden />
|
||||||
|
<ImportButton
|
||||||
|
type="file"
|
||||||
|
import-file
|
||||||
|
icon-only
|
||||||
|
@file-selected="
|
||||||
|
(file) => {
|
||||||
|
importProduct(
|
||||||
|
currentIdGroup,
|
||||||
|
file,
|
||||||
|
async () => await fetchListOfProduct(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row col-md-6" style="white-space: nowrap">
|
<div class="row col-md-6" style="white-space: nowrap">
|
||||||
<q-select
|
<q-select
|
||||||
v-show="$q.screen.gt.sm"
|
v-show="$q.screen.gt.sm"
|
||||||
ref="refFilterProductService"
|
|
||||||
:for="'field-select-status'"
|
:for="'field-select-status'"
|
||||||
v-model="currentStatus"
|
v-model="currentStatus"
|
||||||
outlined
|
outlined
|
||||||
|
|
@ -2659,7 +2786,6 @@ watch(
|
||||||
]"
|
]"
|
||||||
@update:model-value="fetchStatus()"
|
@update:model-value="fetchStatus()"
|
||||||
></q-select>
|
></q-select>
|
||||||
|
|
||||||
<q-select
|
<q-select
|
||||||
v-if="modeView === false"
|
v-if="modeView === false"
|
||||||
:hide-dropdown-icon="$q.screen.lt.sm"
|
:hide-dropdown-icon="$q.screen.lt.sm"
|
||||||
|
|
@ -2694,7 +2820,6 @@ watch(
|
||||||
multiple
|
multiple
|
||||||
dense
|
dense
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<q-btn-toggle
|
<q-btn-toggle
|
||||||
v-model="modeView"
|
v-model="modeView"
|
||||||
id="btn-mode"
|
id="btn-mode"
|
||||||
|
|
@ -3127,8 +3252,14 @@ watch(
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<KebabAction
|
<KebabAction
|
||||||
|
:use-copy="productAndServiceTab === 'service'"
|
||||||
:status="props.row.status"
|
:status="props.row.status"
|
||||||
:id-name="props.row.name"
|
:id-name="props.row.name"
|
||||||
|
@copy="
|
||||||
|
() => {
|
||||||
|
copy(props.row.id);
|
||||||
|
}
|
||||||
|
"
|
||||||
@view="
|
@view="
|
||||||
async () => {
|
async () => {
|
||||||
if (props.row.type === 'product') {
|
if (props.row.type === 'product') {
|
||||||
|
|
@ -4422,6 +4553,7 @@ watch(
|
||||||
@click="serviceTreeView = false"
|
@click="serviceTreeView = false"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SaveButton id="btn-info-basic-save" icon-only type="submit" />
|
<SaveButton id="btn-info-basic-save" icon-only type="submit" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { onMounted, reactive, ref } from 'vue';
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
import { QSelect, QTableProps } from 'quasar';
|
import { QTableProps } from 'quasar';
|
||||||
import { useNavigator } from 'src/stores/navigator';
|
import { useNavigator } from 'src/stores/navigator';
|
||||||
import { useProperty } from 'src/stores/property';
|
import { useProperty } from 'src/stores/property';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
|
|
@ -14,19 +14,23 @@ import { FloatingActionButton, PaginationComponent } from 'src/components';
|
||||||
import PaginationPageSize from 'src/components/PaginationPageSize.vue';
|
import PaginationPageSize from 'src/components/PaginationPageSize.vue';
|
||||||
import PropertyDialog from './PropertyDialog.vue';
|
import PropertyDialog from './PropertyDialog.vue';
|
||||||
import { Property } from 'src/stores/property/types';
|
import { Property } from 'src/stores/property/types';
|
||||||
import { dialog } from 'src/stores/utils';
|
import { dialog, toCamelCase } from 'src/stores/utils';
|
||||||
import CreateButton from 'src/components/AddButton.vue';
|
import CreateButton from 'src/components/AddButton.vue';
|
||||||
|
import useOptionStore from 'stores/options';
|
||||||
|
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t, locale } = useI18n();
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const navigatorStore = useNavigator();
|
const navigatorStore = useNavigator();
|
||||||
const propertyStore = useProperty();
|
const propertyStore = useProperty();
|
||||||
|
const optionStore = useOptionStore();
|
||||||
const {
|
const {
|
||||||
data: propertyData,
|
data: propertyData,
|
||||||
page: propertyPage,
|
page: propertyPage,
|
||||||
pageSize: propertyPageSize,
|
pageSize: propertyPageSize,
|
||||||
pageMax: propertyPageMax,
|
pageMax: propertyPageMax,
|
||||||
} = storeToRefs(propertyStore);
|
} = storeToRefs(propertyStore);
|
||||||
|
const { globalOption } = storeToRefs(optionStore);
|
||||||
|
|
||||||
const currPropertyData = ref<Property>();
|
const currPropertyData = ref<Property>();
|
||||||
const formProperty = ref<Property>({
|
const formProperty = ref<Property>({
|
||||||
|
|
@ -35,7 +39,6 @@ const formProperty = ref<Property>({
|
||||||
type: {},
|
type: {},
|
||||||
});
|
});
|
||||||
const statusFilter = ref<'all' | 'statusACTIVE' | 'statusINACTIVE'>('all');
|
const statusFilter = ref<'all' | 'statusACTIVE' | 'statusINACTIVE'>('all');
|
||||||
const refFilter = ref<InstanceType<typeof QSelect>>();
|
|
||||||
const fieldSelected = ref<('order' | 'name' | 'type')[]>([
|
const fieldSelected = ref<('order' | 'name' | 'type')[]>([
|
||||||
'order',
|
'order',
|
||||||
'name',
|
'name',
|
||||||
|
|
@ -115,6 +118,7 @@ const pageState = reactive({
|
||||||
addModal: false,
|
addModal: false,
|
||||||
viewDrawer: false,
|
viewDrawer: false,
|
||||||
isDrawerEdit: true,
|
isDrawerEdit: true,
|
||||||
|
searchDate: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
async function fetchPropertyList(mobileFetch?: boolean) {
|
async function fetchPropertyList(mobileFetch?: boolean) {
|
||||||
|
|
@ -131,6 +135,8 @@ async function fetchPropertyList(mobileFetch?: boolean) {
|
||||||
: statusFilter.value === 'statusACTIVE'
|
: statusFilter.value === 'statusACTIVE'
|
||||||
? 'ACTIVE'
|
? 'ACTIVE'
|
||||||
: 'INACTIVE',
|
: 'INACTIVE',
|
||||||
|
startDate: pageState.searchDate[0],
|
||||||
|
endDate: pageState.searchDate[1],
|
||||||
});
|
});
|
||||||
if (res) {
|
if (res) {
|
||||||
propertyData.value =
|
propertyData.value =
|
||||||
|
|
@ -146,6 +152,7 @@ async function fetchPropertyList(mobileFetch?: boolean) {
|
||||||
|
|
||||||
function triggerDialog(type: 'add' | 'edit' | 'view') {
|
function triggerDialog(type: 'add' | 'edit' | 'view') {
|
||||||
if (type === 'add') {
|
if (type === 'add') {
|
||||||
|
resetForm();
|
||||||
pageState.addModal = true;
|
pageState.addModal = true;
|
||||||
pageState.isDrawerEdit = true;
|
pageState.isDrawerEdit = true;
|
||||||
}
|
}
|
||||||
|
|
@ -271,6 +278,31 @@ async function submit() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await fetchPropertyList($q.screen.xs);
|
await fetchPropertyList($q.screen.xs);
|
||||||
|
|
||||||
|
// assign new property to global option
|
||||||
|
const propList = propertyData.value.map((v) => {
|
||||||
|
return {
|
||||||
|
label: locale.value === 'eng' ? v.nameEN : v.name,
|
||||||
|
value: toCamelCase(v.nameEN),
|
||||||
|
type: v.type.type,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const existingValues = new Set(
|
||||||
|
globalOption.value.propertiesField.map(
|
||||||
|
(item: { value: string }) => item.value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
const newProps = propList.filter((prop) => !existingValues.has(prop.value));
|
||||||
|
if (newProps) {
|
||||||
|
globalOption.value.propertiesField.splice(
|
||||||
|
globalOption.value.propertiesField.length - 4,
|
||||||
|
0,
|
||||||
|
...newProps,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
currPropertyData.value;
|
||||||
resetForm();
|
resetForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -288,11 +320,14 @@ watch(
|
||||||
fetchPropertyList();
|
fetchPropertyList();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
watch([() => pageState.inputSearch, propertyPageSize], () => {
|
watch(
|
||||||
|
[() => pageState.inputSearch, propertyPageSize, () => pageState.searchDate],
|
||||||
|
() => {
|
||||||
propertyData.value = [];
|
propertyData.value = [];
|
||||||
propertyPage.value = 1;
|
propertyPage.value = 1;
|
||||||
fetchPropertyList();
|
fetchPropertyList();
|
||||||
});
|
},
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
|
|
@ -369,26 +404,44 @@ watch([() => pageState.inputSearch, propertyPageSize], () => {
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<q-icon name="mdi-magnify" />
|
<q-icon name="mdi-magnify" />
|
||||||
</template>
|
</template>
|
||||||
<template v-if="$q.screen.lt.md" v-slot:append>
|
<template v-slot:append>
|
||||||
<span class="row">
|
<q-separator vertical inset class="q-mr-xs" />
|
||||||
<q-separator vertical />
|
<AdvanceSearch
|
||||||
<q-btn
|
v-model="pageState.searchDate"
|
||||||
icon="mdi-filter-variant"
|
:active="$q.screen.lt.md && statusFilter !== 'all'"
|
||||||
unelevated
|
>
|
||||||
class="q-ml-sm"
|
<div
|
||||||
padding="4px"
|
v-if="$q.screen.lt.md"
|
||||||
size="sm"
|
class="q-mt-sm text-weight-medium"
|
||||||
rounded
|
>
|
||||||
@click="refFilter?.showPopup"
|
{{ $t('general.status') }}
|
||||||
|
</div>
|
||||||
|
<q-select
|
||||||
|
v-if="$q.screen.lt.md"
|
||||||
|
v-model="statusFilter"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
option-value="value"
|
||||||
|
option-label="label"
|
||||||
|
map-options
|
||||||
|
emit-value
|
||||||
|
:for="'field-select-status'"
|
||||||
|
:options="[
|
||||||
|
{ label: $t('general.all'), value: 'all' },
|
||||||
|
{ label: $t('general.active'), value: 'statusACTIVE' },
|
||||||
|
{
|
||||||
|
label: $t('general.inactive'),
|
||||||
|
value: 'statusINACTIVE',
|
||||||
|
},
|
||||||
|
]"
|
||||||
/>
|
/>
|
||||||
</span>
|
</AdvanceSearch>
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<div class="row col-md-5" style="white-space: nowrap">
|
<div class="row col-md-5" style="white-space: nowrap">
|
||||||
<q-select
|
<q-select
|
||||||
v-show="$q.screen.gt.sm"
|
v-if="$q.screen.gt.sm"
|
||||||
ref="refFilter"
|
|
||||||
v-model="statusFilter"
|
v-model="statusFilter"
|
||||||
outlined
|
outlined
|
||||||
dense
|
dense
|
||||||
|
|
@ -483,11 +536,11 @@ watch([() => pageState.inputSearch, propertyPageSize], () => {
|
||||||
class="col surface-2 flex items-center justify-center"
|
class="col surface-2 flex items-center justify-center"
|
||||||
>
|
>
|
||||||
<NoData
|
<NoData
|
||||||
v-if="pageState.total !== 0"
|
v-if="pageState.total !== 0 || pageState.searchDate.length > 0"
|
||||||
:not-found="!!pageState.inputSearch"
|
:not-found="!!pageState.inputSearch"
|
||||||
/>
|
/>
|
||||||
<CreateButton
|
<CreateButton
|
||||||
v-if="pageState.total === 0"
|
v-if="pageState.total === 0 && pageState.searchDate.length === 0"
|
||||||
@click="triggerDialog('add')"
|
@click="triggerDialog('add')"
|
||||||
label="general.add"
|
label="general.add"
|
||||||
:i18n-args="{ text: $t('flow.title') }"
|
:i18n-args="{ text: $t('flow.title') }"
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@
|
||||||
import { onMounted, reactive, ref, watch, computed } from 'vue';
|
import { onMounted, reactive, ref, watch, computed } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
// NOTE: Import stores
|
// NOTE: Import stores
|
||||||
|
import useCustomerStore from 'stores/customer';
|
||||||
import { useQuotationStore } from 'src/stores/quotations';
|
import { useQuotationStore } from 'src/stores/quotations';
|
||||||
import { isRoleInclude } from 'stores/utils';
|
import { dialog, isRoleInclude, notify, setPrefixName } from 'stores/utils';
|
||||||
import { useNavigator } from 'src/stores/navigator';
|
import { useNavigator } from 'src/stores/navigator';
|
||||||
import useFlowStore from 'src/stores/flow';
|
import useFlowStore from 'src/stores/flow';
|
||||||
import useMyBranch from 'stores/my-branch';
|
import useMyBranch from 'stores/my-branch';
|
||||||
|
|
@ -38,26 +40,44 @@ import { AddressForm } from 'components/form';
|
||||||
import {
|
import {
|
||||||
EmployerFormBusiness,
|
EmployerFormBusiness,
|
||||||
EmployerFormAbout,
|
EmployerFormAbout,
|
||||||
|
EmployerFormBasicInfo,
|
||||||
|
EmployerFormBranch,
|
||||||
} from 'src/pages/03_customer-management/components';
|
} from 'src/pages/03_customer-management/components';
|
||||||
|
|
||||||
import { useCustomerForm } from 'src/pages/03_customer-management/form';
|
import { useCustomerForm } from 'src/pages/03_customer-management/form';
|
||||||
import { Quotation } from 'src/stores/quotations/types';
|
import { Quotation } from 'src/stores/quotations/types';
|
||||||
import TableQuotation from 'src/components/05_quotation/TableQuotation.vue';
|
import TableQuotation from 'src/components/05_quotation/TableQuotation.vue';
|
||||||
import PaginationPageSize from 'src/components/PaginationPageSize.vue';
|
import PaginationPageSize from 'src/components/PaginationPageSize.vue';
|
||||||
|
import { DialogContainer, DialogHeader } from 'src/components/dialog';
|
||||||
|
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
||||||
|
|
||||||
|
const { t, locale } = useI18n();
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const quotationFormStore = useQuotationForm();
|
const quotationFormStore = useQuotationForm();
|
||||||
const customerFormStore = useCustomerForm();
|
const customerFormStore = useCustomerForm();
|
||||||
const flowStore = useFlowStore();
|
const flowStore = useFlowStore();
|
||||||
const userBranch = useMyBranch();
|
const userBranch = useMyBranch();
|
||||||
const navigatorStore = useNavigator();
|
const navigatorStore = useNavigator();
|
||||||
|
const customerStore = useCustomerStore();
|
||||||
|
|
||||||
|
const {
|
||||||
|
fetchListOfOptionBranch,
|
||||||
|
customerFormUndo,
|
||||||
|
deleteCustomerById,
|
||||||
|
validateTabField,
|
||||||
|
deleteCustomerBranchById,
|
||||||
|
} = customerFormStore;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
currentFormData: quotationFormData,
|
currentFormData: quotationFormData,
|
||||||
currentFormState: quotationFormState,
|
currentFormState: quotationFormState,
|
||||||
} = storeToRefs(quotationFormStore);
|
} = storeToRefs(quotationFormStore);
|
||||||
const { state: customerFormState, currentFormData: customerFormData } =
|
const {
|
||||||
storeToRefs(customerFormStore);
|
state: customerFormState,
|
||||||
|
currentFormData: customerFormData,
|
||||||
|
registerAbleBranchOption,
|
||||||
|
tabFieldRequired,
|
||||||
|
} = storeToRefs(customerFormStore);
|
||||||
const { currentMyBranch } = storeToRefs(userBranch);
|
const { currentMyBranch } = storeToRefs(userBranch);
|
||||||
|
|
||||||
const fieldSelectedOption = computed(() => {
|
const fieldSelectedOption = computed(() => {
|
||||||
|
|
@ -90,6 +110,7 @@ const pageState = reactive({
|
||||||
quotationModal: false,
|
quotationModal: false,
|
||||||
employeeModal: false,
|
employeeModal: false,
|
||||||
receiptModal: false,
|
receiptModal: false,
|
||||||
|
searchDate: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
pageState.fieldSelected = [...columnQuotation.map((v) => v.name)];
|
pageState.fieldSelected = [...columnQuotation.map((v) => v.name)];
|
||||||
|
|
@ -170,8 +191,7 @@ async function submitCustomer() {
|
||||||
customerFormData.value.registeredBranchId = isRoleInclude(['system'])
|
customerFormData.value.registeredBranchId = isRoleInclude(['system'])
|
||||||
? branchId.value
|
? branchId.value
|
||||||
: currentMyBranch.value.id;
|
: currentMyBranch.value.id;
|
||||||
await customerFormStore.submitFormCustomer();
|
await customerFormStore.addCurrentCustomerBranch();
|
||||||
|
|
||||||
customerFormState.value.dialogModal = false;
|
customerFormState.value.dialogModal = false;
|
||||||
// customerFormState.value.dialogType = 'info';
|
// customerFormState.value.dialogType = 'info';
|
||||||
}
|
}
|
||||||
|
|
@ -241,6 +261,16 @@ const {
|
||||||
stats: quotationStats,
|
stats: quotationStats,
|
||||||
} = storeToRefs(quotationStore);
|
} = storeToRefs(quotationStore);
|
||||||
|
|
||||||
|
const customerNameInfo = computed(() => {
|
||||||
|
if (customerFormData.value.customerBranch === undefined) return;
|
||||||
|
|
||||||
|
const name =
|
||||||
|
locale.value === 'eng'
|
||||||
|
? `${customerFormData.value.customerBranch[0]?.firstNameEN} ${customerFormData.value.customerBranch[0]?.lastNameEN}`
|
||||||
|
: `${customerFormData.value.customerBranch[0]?.firstName} ${customerFormData.value.customerBranch[0]?.lastName}`;
|
||||||
|
return name || '-';
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
pageState.gridView = $q.screen.lt.md ? true : false;
|
pageState.gridView = $q.screen.lt.md ? true : false;
|
||||||
navigatorStore.current.title = 'quotation.title';
|
navigatorStore.current.title = 'quotation.title';
|
||||||
|
|
@ -299,6 +329,8 @@ async function fetchQuotationList(mobileFetch?: boolean) {
|
||||||
: 'Issued',
|
: 'Issued',
|
||||||
query: pageState.inputSearch,
|
query: pageState.inputSearch,
|
||||||
urgentFirst: true,
|
urgentFirst: true,
|
||||||
|
startDate: pageState.searchDate[0],
|
||||||
|
endDate: pageState.searchDate[1],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
|
@ -322,7 +354,12 @@ async function fetchQuotationList(mobileFetch?: boolean) {
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
[() => pageState.currentTab, () => pageState.inputSearch, quotationPageSize],
|
[
|
||||||
|
() => pageState.currentTab,
|
||||||
|
() => pageState.inputSearch,
|
||||||
|
() => pageState.searchDate,
|
||||||
|
quotationPageSize,
|
||||||
|
],
|
||||||
() => {
|
() => {
|
||||||
quotationPage.value = 1;
|
quotationPage.value = 1;
|
||||||
quotationData.value = [];
|
quotationData.value = [];
|
||||||
|
|
@ -489,6 +526,10 @@ async function storeDataLocal(id: string) {
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<q-icon name="mdi-magnify" />
|
<q-icon name="mdi-magnify" />
|
||||||
</template>
|
</template>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-separator vertical inset class="q-mr-xs" />
|
||||||
|
<AdvanceSearch v-model="pageState.searchDate" />
|
||||||
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<div class="row col-md-5 justify-end" style="white-space: nowrap">
|
<div class="row col-md-5 justify-end" style="white-space: nowrap">
|
||||||
|
|
@ -911,10 +952,26 @@ async function storeDataLocal(id: string) {
|
||||||
|
|
||||||
<!-- NOTE: START - Customer / Employer Form -->
|
<!-- NOTE: START - Customer / Employer Form -->
|
||||||
|
|
||||||
<DialogForm
|
<DialogContainer
|
||||||
hide-footer
|
:model-value="customerFormState.dialogModal"
|
||||||
ref="formDialogRef"
|
:on-open="
|
||||||
v-model:modal="customerFormState.dialogModal"
|
async () => {
|
||||||
|
customerFormStore.resetForm(customerFormState.dialogType === 'create');
|
||||||
|
onCreateImageList = { selectedImage: '', list: [] };
|
||||||
|
customerFormState.customerImageUrl = '';
|
||||||
|
await fetchListOfOptionBranch();
|
||||||
|
await customerFormStore.addCurrentCustomerBranch();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
:on-close="
|
||||||
|
() => {
|
||||||
|
customerFormState.dialogModal = false;
|
||||||
|
onCreateImageList = { selectedImage: '', list: [] };
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<DialogHeader
|
||||||
:title="
|
:title="
|
||||||
customerFormState.dialogType === 'create'
|
customerFormState.dialogType === 'create'
|
||||||
? $t(`general.add`, {
|
? $t(`general.add`, {
|
||||||
|
|
@ -922,19 +979,22 @@ async function storeDataLocal(id: string) {
|
||||||
})
|
})
|
||||||
: `${$t('customer.employer')} `
|
: `${$t('customer.employer')} `
|
||||||
"
|
"
|
||||||
:submit="
|
|
||||||
() => {
|
|
||||||
submitCustomer();
|
|
||||||
}
|
|
||||||
"
|
|
||||||
:close="
|
|
||||||
() => {
|
|
||||||
customerFormState.dialogModal = false;
|
|
||||||
customerFormStore.resetForm(true);
|
|
||||||
setDefaultCustomer();
|
|
||||||
}
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
|
<template #title-after>
|
||||||
|
<span
|
||||||
|
:style="`color: hsla(var(--${customerFormData.customerType === 'PERS' ? 'teal-10-hsl' : 'violet-11-hsl'})/1)`"
|
||||||
|
>
|
||||||
|
:
|
||||||
|
{{
|
||||||
|
customerFormData.customerType === 'CORP'
|
||||||
|
? $t('customer.employerLegalEntity')
|
||||||
|
: $t('customer.employerNaturalPerson')
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</DialogHeader>
|
||||||
|
</template>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
:class="{
|
:class="{
|
||||||
'q-mx-lg q-my-md': $q.screen.gt.sm,
|
'q-mx-lg q-my-md': $q.screen.gt.sm,
|
||||||
|
|
@ -942,10 +1002,10 @@ async function storeDataLocal(id: string) {
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<ProfileBanner
|
<ProfileBanner
|
||||||
prefix="dialog"
|
|
||||||
v-if="customerFormData.customerBranch !== undefined"
|
v-if="customerFormData.customerBranch !== undefined"
|
||||||
active
|
active
|
||||||
hide-fade
|
hide-fade
|
||||||
|
prefix="dialog"
|
||||||
:fallback-cover="`/images/customer-${customerFormData.customerType}-banner-bg.jpg`"
|
:fallback-cover="`/images/customer-${customerFormData.customerType}-banner-bg.jpg`"
|
||||||
:img="
|
:img="
|
||||||
customerFormState.customerImageUrl ||
|
customerFormState.customerImageUrl ||
|
||||||
|
|
@ -961,13 +1021,22 @@ async function storeDataLocal(id: string) {
|
||||||
"
|
"
|
||||||
:title="
|
:title="
|
||||||
customerFormData.customerType === 'PERS'
|
customerFormData.customerType === 'PERS'
|
||||||
? `${customerFormData.customerBranch[0]?.firstName || ''} ${customerFormData.customerBranch[0]?.lastName || ''}`
|
? setPrefixName(
|
||||||
: customerFormData.customerBranch[0]?.registerName || ''
|
{
|
||||||
|
namePrefix: customerFormData.customerBranch[0]?.namePrefix,
|
||||||
|
firstName: customerFormData.customerBranch[0]?.firstName,
|
||||||
|
lastName: customerFormData.customerBranch[0]?.lastName,
|
||||||
|
firstNameEN: customerFormData.customerBranch[0]?.firstNameEN,
|
||||||
|
lastNameEN: customerFormData.customerBranch[0]?.lastNameEN,
|
||||||
|
},
|
||||||
|
{ locale },
|
||||||
|
)
|
||||||
|
: customerFormData.customerBranch[0]?.registerName
|
||||||
"
|
"
|
||||||
:caption="
|
:caption="
|
||||||
customerFormData.customerType === 'PERS'
|
customerFormData.customerType === 'PERS'
|
||||||
? `${customerFormData.customerBranch[0]?.firstNameEN || ''} ${customerFormData.customerBranch[0]?.lastNameEN || ''}`
|
? `${customerFormData.customerBranch[0]?.firstNameEN} ${customerFormData.customerBranch[0]?.lastNameEN}`
|
||||||
: customerFormData.customerBranch[0]?.registerNameEN || ''
|
: customerFormData.customerBranch[0]?.registerNameEN
|
||||||
"
|
"
|
||||||
@view="
|
@view="
|
||||||
() => {
|
() => {
|
||||||
|
|
@ -981,143 +1050,295 @@ async function storeDataLocal(id: string) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="col"
|
style="flex: 1; width: 100%; overflow-y: auto"
|
||||||
|
id="customer-form"
|
||||||
:class="{
|
:class="{
|
||||||
'q-px-lg q-pb-lg': $q.screen.gt.sm,
|
'q-px-lg q-pb-lg': $q.screen.gt.sm,
|
||||||
'q-px-md q-pb-sm': !$q.screen.gt.sm,
|
'q-px-md q-pb-sm': !$q.screen.gt.sm,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
|
<div class="col surface-1 full-height rounded bordered scroll row">
|
||||||
<div
|
<div
|
||||||
style="overflow-y: auto"
|
class="col"
|
||||||
class="row full-width full-height surface-1 rounded bordered relative-position"
|
style="height: 100%; max-height: 100; overflow-y: auto"
|
||||||
>
|
|
||||||
<div
|
|
||||||
:class="{
|
|
||||||
'q-py-md q-px-lg': $q.screen.gt.sm,
|
|
||||||
'q-py-sm q-px-lg': !$q.screen.gt.sm,
|
|
||||||
}"
|
|
||||||
style="position: absolute; z-index: 99999; top: 0; right: 0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-if="customerFormData.status !== 'INACTIVE'"
|
|
||||||
class="surface-1 row rounded"
|
|
||||||
>
|
|
||||||
<SaveButton
|
|
||||||
v-if="
|
|
||||||
customerFormState.dialogType === 'edit' ||
|
|
||||||
customerFormState.dialogType === 'create'
|
|
||||||
"
|
|
||||||
id="btn-info-basic-save"
|
|
||||||
icon-only
|
|
||||||
type="submit"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="col full-height rounded scroll row q-py-md q-pl-md q-pr-sm"
|
|
||||||
v-if="$q.screen.gt.sm"
|
v-if="$q.screen.gt.sm"
|
||||||
>
|
>
|
||||||
|
<div class="q-py-md q-pl-md q-pr-sm">
|
||||||
<SideMenu
|
<SideMenu
|
||||||
:menu="[
|
:menu="[
|
||||||
{
|
{
|
||||||
name: $t('form.customerInformation'),
|
name: $t('form.field.basicInformation'),
|
||||||
anchor: 'form-information',
|
anchor: 'form-basic-info-customer',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: $t('customerBranch.tab.business'),
|
name: $t('customer.form.group.branch'),
|
||||||
anchor: 'form-business',
|
anchor: 'form-branch-customer-branch',
|
||||||
},
|
useBtn: true,
|
||||||
{
|
|
||||||
name: $t('form.address'),
|
|
||||||
anchor: 'form-address',
|
|
||||||
},
|
},
|
||||||
|
...(customerFormData.customerBranch?.map((v, i) => ({
|
||||||
|
name:
|
||||||
|
i === 0
|
||||||
|
? $t('customer.form.headQuarters.title')
|
||||||
|
: $t('customer.form.branch.title', {
|
||||||
|
name: i,
|
||||||
|
}),
|
||||||
|
anchor: `form-branch-customer-no-${i}`,
|
||||||
|
sub: true,
|
||||||
|
})) || []),
|
||||||
]"
|
]"
|
||||||
background="transparent"
|
background="transparent"
|
||||||
:active="{
|
:active="{
|
||||||
background: 'hsla(var(--blue-6-hsl) / .2)',
|
background: 'hsla(var(--blue-6-hsl) / .2)',
|
||||||
foreground: 'var(--blue-6)',
|
foreground: 'var(--blue-6)',
|
||||||
}"
|
}"
|
||||||
scroll-element="#branch-form"
|
scroll-element="#customer-form-content"
|
||||||
|
>
|
||||||
|
<template v-slot:btn-form-branch-customer-branch>
|
||||||
|
<q-btn
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
icon="mdi-plus"
|
||||||
|
size="sm"
|
||||||
|
rounded
|
||||||
|
padding="0px 0px"
|
||||||
|
style="color: var(--stone-9)"
|
||||||
|
@click.stop="submitCustomer()"
|
||||||
|
v-if="
|
||||||
|
customerFormState.branchIndex === -1 &&
|
||||||
|
!!customerFormState.editCustomerId &&
|
||||||
|
customerFormState.dialogType !== 'create'
|
||||||
|
"
|
||||||
|
:disabled="!customerFormState.readonly"
|
||||||
/>
|
/>
|
||||||
|
</template>
|
||||||
|
</SideMenu>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="col-12 col-md-10 full-height q-col-gutter-sm"
|
class="col-12 col-md-10"
|
||||||
:class="{
|
:class="{
|
||||||
'q-py-md q-pr-md ': $q.screen.gt.sm,
|
'q-py-md q-pr-md ': $q.screen.gt.sm,
|
||||||
'q-py-md q-px-lg': !$q.screen.gt.sm,
|
'q-pa-sm': !$q.screen.gt.sm,
|
||||||
}"
|
}"
|
||||||
id="branch-form"
|
id="customer-form-content"
|
||||||
style="overflow-y: auto"
|
style="height: 100%; max-height: 100%; overflow-y: auto"
|
||||||
>
|
>
|
||||||
<EmployerFormAbout
|
<EmployerFormBasicInfo
|
||||||
id="form-information"
|
prefixId="form"
|
||||||
show-title
|
v-if="
|
||||||
:index="'0'"
|
customerFormData.customerBranch !== undefined &&
|
||||||
:customerType="customerFormData.customerType"
|
customerFormData.customerBranch.length > 0
|
||||||
:readonly="customerFormState.dialogType === 'info'"
|
|
||||||
v-model:citizen-id="formDataCustomerBranch.citizenId"
|
|
||||||
v-model:prefix-name="formDataCustomerBranch.namePrefix"
|
|
||||||
v-model:first-name="formDataCustomerBranch.firstName"
|
|
||||||
v-model:last-name="formDataCustomerBranch.lastName"
|
|
||||||
v-model:first-name-en="formDataCustomerBranch.firstNameEN"
|
|
||||||
v-model:last-name-en="formDataCustomerBranch.lastNameEN"
|
|
||||||
v-model:gender="formDataCustomerBranch.gender"
|
|
||||||
v-model:birth-date="formDataCustomerBranch.birthDate"
|
|
||||||
v-model:customer-name="formDataCustomerBranch.customerName"
|
|
||||||
v-model:legal-person-no="formDataCustomerBranch.legalPersonNo"
|
|
||||||
v-model:register-name="formDataCustomerBranch.registerName"
|
|
||||||
v-model:register-name-en="formDataCustomerBranch.registerNameEN"
|
|
||||||
v-model:register-date="formDataCustomerBranch.registerDate"
|
|
||||||
v-model:authorized-capital="
|
|
||||||
formDataCustomerBranch.authorizedCapital
|
|
||||||
"
|
"
|
||||||
v-model:telephone-no="formDataCustomerBranch.telephoneNo"
|
class="q-mb-xl"
|
||||||
|
:readonly="
|
||||||
|
(customerFormState.dialogType === 'edit' &&
|
||||||
|
customerFormState.readonly === true) ||
|
||||||
|
customerFormState.dialogType === 'info'
|
||||||
|
"
|
||||||
|
:action-disabled="customerFormState.branchIndex !== -1"
|
||||||
|
id="form-basic-info-customer"
|
||||||
|
:onCreate="customerFormState.dialogType === 'create'"
|
||||||
|
@edit="
|
||||||
|
((customerFormState.dialogType = 'edit'),
|
||||||
|
(customerFormState.readonly = false))
|
||||||
|
"
|
||||||
|
@cancel="() => customerFormUndo(false)"
|
||||||
|
@delete="
|
||||||
|
customerFormState.editCustomerId &&
|
||||||
|
deleteCustomerById(customerFormState.editCustomerId)
|
||||||
|
"
|
||||||
|
:customer-type="customerFormData.customerType"
|
||||||
|
v-model:registered-branch-id="customerFormData.registeredBranchId"
|
||||||
|
v-model:customer-name="customerNameInfo"
|
||||||
|
v-model:register-name="
|
||||||
|
customerFormData.customerBranch[0].registerName
|
||||||
|
"
|
||||||
|
v-model:citizen-id="customerFormData.customerBranch[0].citizenId"
|
||||||
|
v-model:legal-person-no="
|
||||||
|
customerFormData.customerBranch[0].legalPersonNo
|
||||||
|
"
|
||||||
|
v-model:business-type="
|
||||||
|
customerFormData.customerBranch[0].businessType
|
||||||
|
"
|
||||||
|
v-model:job-position="
|
||||||
|
customerFormData.customerBranch[0].jobPosition
|
||||||
|
"
|
||||||
|
v-model:telephone-no="
|
||||||
|
customerFormData.customerBranch[0].telephoneNo
|
||||||
|
"
|
||||||
|
v-model:branch-options="registerAbleBranchOption"
|
||||||
/>
|
/>
|
||||||
|
<div class="row q-col-gutter-sm" id="form-branch-customer-branch">
|
||||||
|
<div class="col-12 text-weight-bold text-body1 row items-center">
|
||||||
|
<q-icon
|
||||||
|
flat
|
||||||
|
size="xs"
|
||||||
|
class="q-pa-sm rounded q-mr-xs"
|
||||||
|
color="info"
|
||||||
|
name="mdi-briefcase-outline"
|
||||||
|
style="background-color: var(--surface-3)"
|
||||||
|
/>
|
||||||
|
<span>{{ $t('customer.form.group.branch') }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<EmployerFormBusiness
|
<template
|
||||||
id="form-business"
|
v-for="(_, idx) in customerFormData.customerBranch"
|
||||||
show-title
|
:key="idx"
|
||||||
prefix-id="dialog"
|
>
|
||||||
dense
|
<!-- v-if="customerFormData.customerBranch" -->
|
||||||
outlined
|
<q-form
|
||||||
:readonly="customerFormState.dialogType === 'info'"
|
class="full-width"
|
||||||
v-model:bussiness-type="formDataCustomerBranch.businessType"
|
greedy
|
||||||
v-model:job-position="formDataCustomerBranch.jobPosition"
|
@submit.prevent="
|
||||||
v-model:job-description="formDataCustomerBranch.jobDescription"
|
async () => {
|
||||||
v-model:pay-date="formDataCustomerBranch.payDate"
|
if (!customerFormData.customerBranch) return;
|
||||||
v-model:pay-date-en="formDataCustomerBranch.payDateEN"
|
|
||||||
v-model:wage-rate="formDataCustomerBranch.wageRate"
|
if (customerFormData.customerType === 'PERS') {
|
||||||
v-model:wage-rate-text="formDataCustomerBranch.wageRateText"
|
tabFieldRequired.main = [
|
||||||
/>
|
'citizenId',
|
||||||
<AddressForm
|
'namePrefix',
|
||||||
id="form-address"
|
'firstName',
|
||||||
prefix-id="employer"
|
'firstNameEN',
|
||||||
dense
|
'lastName',
|
||||||
outlined
|
'lastNameEN',
|
||||||
use-employment
|
'gender',
|
||||||
:title="$t('form.address')"
|
'birthDate',
|
||||||
:addressTitle="$t('form.address')"
|
];
|
||||||
:addressTitleEN="$t('form.address', { suffix: '(EN)' })"
|
}
|
||||||
:readonly="customerFormState.dialogType === 'info'"
|
if (customerFormData.customerType === 'CORP') {
|
||||||
v-model:bussiness-type="formDataCustomerBranch.businessType"
|
tabFieldRequired.main = ['legalPersonNo', 'registerName'];
|
||||||
v-model:address="formDataCustomerBranch.address"
|
}
|
||||||
v-model:address-en="formDataCustomerBranch.addressEN"
|
let tapIsUndefined = validateTabField(
|
||||||
v-model:street="formDataCustomerBranch.street"
|
customerFormData.customerBranch?.[idx],
|
||||||
v-model:street-en="formDataCustomerBranch.streetEN"
|
tabFieldRequired,
|
||||||
v-model:moo="formDataCustomerBranch.moo"
|
);
|
||||||
v-model:moo-en="formDataCustomerBranch.mooEN"
|
|
||||||
v-model:soi="formDataCustomerBranch.soi"
|
if (tapIsUndefined.length > 0) {
|
||||||
v-model:soi-en="formDataCustomerBranch.soiEN"
|
return dialog({
|
||||||
v-model:province-id="formDataCustomerBranch.provinceId"
|
color: 'warning',
|
||||||
v-model:district-id="formDataCustomerBranch.districtId"
|
icon: 'mdi-alert',
|
||||||
v-model:sub-district-id="formDataCustomerBranch.subDistrictId"
|
title: t('dialog.title.incompleteDataEntry'),
|
||||||
v-model:home-code="formDataCustomerBranch.homeCode"
|
actionText: t('general.ok'),
|
||||||
|
persistent: true,
|
||||||
|
message: t('dialog.message.incompleteDataEntry', {
|
||||||
|
tap: `${tapIsUndefined.map((v: string) => t(`customerBranch.tab.${v}`)).join(', ')}`,
|
||||||
|
}),
|
||||||
|
action: async () => {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!customerFormData.customerBranch[idx].id) {
|
||||||
|
let res: any;
|
||||||
|
if (idx === 0) {
|
||||||
|
res =
|
||||||
|
await customerFormStore.submitFormCustomer(
|
||||||
|
onCreateImageList,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
res = await customerStore.createBranch({
|
||||||
|
...customerFormData.customerBranch[idx],
|
||||||
|
customerId: customerFormState.editCustomerId || '',
|
||||||
|
id: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (res) {
|
||||||
|
customerFormState.readonly = true;
|
||||||
|
notify('create', $t('general.success'));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!customerFormState.editCustomerId) return;
|
||||||
|
await customerStore.editBranchById(
|
||||||
|
customerFormData.customerBranch[idx].id || '',
|
||||||
|
{
|
||||||
|
...customerFormData.customerBranch[idx],
|
||||||
|
id: undefined,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadResult =
|
||||||
|
customerFormData.customerBranch[idx].file?.map(
|
||||||
|
async (v) => {
|
||||||
|
if (!v.file) return;
|
||||||
|
|
||||||
|
const ext = v.file.name.split('.').at(-1);
|
||||||
|
let filename = v.group + '-' + new Date().getTime();
|
||||||
|
if (ext) filename += `.${ext}`;
|
||||||
|
|
||||||
|
const res = await customerStore.putAttachment({
|
||||||
|
branchId:
|
||||||
|
customerFormData.customerBranch?.[idx].id || '',
|
||||||
|
file: v.file,
|
||||||
|
filename,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
await customerFormStore.assignFormData(
|
||||||
|
customerFormState.editCustomerId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) || [];
|
||||||
|
|
||||||
|
for (const r of uploadResult) await r;
|
||||||
|
|
||||||
|
await customerFormStore.assignFormData(
|
||||||
|
customerFormState.editCustomerId,
|
||||||
|
);
|
||||||
|
|
||||||
|
customerFormStore.resetForm();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<!-- v-if="!!customerFormState.editCustomerId" -->
|
||||||
|
|
||||||
|
<EmployerFormBranch
|
||||||
|
:index="idx"
|
||||||
|
prefixId="form"
|
||||||
|
v-if="customerFormData.customerBranch"
|
||||||
|
v-model:customer="customerFormData"
|
||||||
|
v-model:customer-branch="customerFormData.customerBranch[idx]"
|
||||||
|
:onCreate="customerFormState.dialogType === 'create'"
|
||||||
|
:customer-type="customerFormData.customerType"
|
||||||
|
:readonly="customerFormState.branchIndex !== idx"
|
||||||
|
:action-disabled="
|
||||||
|
!customerFormState.readonly ||
|
||||||
|
(customerFormState.branchIndex !== -1 &&
|
||||||
|
customerFormState.branchIndex !== idx)
|
||||||
|
"
|
||||||
|
@edit="() => (customerFormState.branchIndex = idx)"
|
||||||
|
@cancel="() => customerFormUndo(false)"
|
||||||
|
@delete="
|
||||||
|
async () => {
|
||||||
|
if (!customerFormState.editCustomerId) return;
|
||||||
|
|
||||||
|
if (idx === 0) {
|
||||||
|
deleteCustomerById(customerFormState.editCustomerId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!!customerFormData.customerBranch?.[idx].id) {
|
||||||
|
const action = await deleteCustomerBranchById(
|
||||||
|
customerFormData.customerBranch[idx].id || '',
|
||||||
|
);
|
||||||
|
if (action) {
|
||||||
|
await customerFormStore.assignFormData(
|
||||||
|
customerFormState.editCustomerId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
customerFormStore.resetForm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
@save="() => {}"
|
||||||
/>
|
/>
|
||||||
|
</q-form>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DialogForm>
|
</div>
|
||||||
|
</DialogContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import { deleteItem } from 'stores/utils';
|
||||||
|
|
||||||
// NOTE Import Types
|
// NOTE Import Types
|
||||||
import { RequestData, RequestDataStatus } from 'src/stores/request-list/types';
|
import { RequestData, RequestDataStatus } from 'src/stores/request-list/types';
|
||||||
import { View } from './types.ts';
|
import { View } from './types';
|
||||||
import {
|
import {
|
||||||
PayCondition,
|
PayCondition,
|
||||||
ProductRelation,
|
ProductRelation,
|
||||||
|
|
@ -76,6 +76,7 @@ import { api } from 'src/boot/axios';
|
||||||
import { RouterLink, useRoute } from 'vue-router';
|
import { RouterLink, useRoute } from 'vue-router';
|
||||||
import { initLang, initTheme, Lang } from 'src/utils/ui';
|
import { initLang, initTheme, Lang } from 'src/utils/ui';
|
||||||
import { convertTemplate } from 'src/utils/string-template';
|
import { convertTemplate } from 'src/utils/string-template';
|
||||||
|
import { getRole } from 'src/services/keycloak';
|
||||||
|
|
||||||
type Node = {
|
type Node = {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
|
|
@ -163,7 +164,9 @@ const selectedWorkerItem = computed(() => {
|
||||||
employeeName:
|
employeeName:
|
||||||
locale.value === Lang.English
|
locale.value === Lang.English
|
||||||
? `${e.firstNameEN} ${e.lastNameEN}`
|
? `${e.firstNameEN} ${e.lastNameEN}`
|
||||||
: `${e.firstName} ${e.lastName}`,
|
: e.firstName
|
||||||
|
? `${e.firstName} ${e.lastName}`
|
||||||
|
: `${e.firstNameEN} ${e.lastNameEN}`,
|
||||||
birthDate: dateFormatJS({ date: e.dateOfBirth }),
|
birthDate: dateFormatJS({ date: e.dateOfBirth }),
|
||||||
gender: e.gender,
|
gender: e.gender,
|
||||||
age: calculateAge(e.dateOfBirth),
|
age: calculateAge(e.dateOfBirth),
|
||||||
|
|
@ -211,6 +214,17 @@ const attachmentData = ref<
|
||||||
url?: string;
|
url?: string;
|
||||||
}[]
|
}[]
|
||||||
>([]);
|
>([]);
|
||||||
|
const hideBtnApproveInvoice = computed(() => {
|
||||||
|
const role = getRole();
|
||||||
|
const allowedRoles = [
|
||||||
|
'system',
|
||||||
|
'head_of_admin',
|
||||||
|
'admin',
|
||||||
|
'head_of_accountant',
|
||||||
|
'accountant',
|
||||||
|
];
|
||||||
|
return !role || !role.some((r) => allowedRoles.includes(r));
|
||||||
|
});
|
||||||
|
|
||||||
const getToolbarConfig = computed(() => {
|
const getToolbarConfig = computed(() => {
|
||||||
const toolbar = [['left', 'center', 'justify'], ['toggle'], ['clip']];
|
const toolbar = [['left', 'center', 'justify'], ['toggle'], ['clip']];
|
||||||
|
|
@ -578,8 +592,9 @@ async function convertDataToFormSubmit() {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
...newWorkerList.value.map((v) => {
|
...newWorkerList.value.map((v) => {
|
||||||
const { attachment, ...payload } = v;
|
{
|
||||||
return payload;
|
return v.id;
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
@ -1850,7 +1865,7 @@ function covertToNode() {
|
||||||
name: selectedWorker.map(
|
name: selectedWorker.map(
|
||||||
(v, i) =>
|
(v, i) =>
|
||||||
`${i + 1}. ` +
|
`${i + 1}. ` +
|
||||||
`${v.namePrefix}. ${v.firstNameEN} ${v.lastNameEN}`.toUpperCase(),
|
`${v.employeePassport.length !== 0 ? v.employeePassport[0].number + '_' : ''} ${v.namePrefix}.${v.firstNameEN ? `${v.firstNameEN} ${v.lastNameEN}` : `${v.firstName} ${v.lastName}`} `.toUpperCase(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
@ -2317,6 +2332,7 @@ function covertToNode() {
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<MainButton
|
<MainButton
|
||||||
|
v-if="!hideBtnApproveInvoice"
|
||||||
solid
|
solid
|
||||||
icon="mdi-account-multiple-check-outline"
|
icon="mdi-account-multiple-check-outline"
|
||||||
color="207 96% 32%"
|
color="207 96% 32%"
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { Icon } from '@iconify/vue';
|
||||||
import { ref, watch, computed } from 'vue';
|
import { ref, watch, computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import { View } from './types.ts';
|
import { View } from './types';
|
||||||
|
|
||||||
import { formatNumberDecimal, commaInput } from 'stores/utils';
|
import { formatNumberDecimal, commaInput } from 'stores/utils';
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@ import SelectInput from 'src/components/shared/SelectInput.vue';
|
||||||
|
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { precisionRound } from 'src/utils/arithmetic';
|
import { precisionRound } from 'src/utils/arithmetic';
|
||||||
import { PayCondition } from 'src/stores/quotations/types.ts';
|
import { PayCondition } from 'src/stores/quotations/types';
|
||||||
|
|
||||||
defineEmits<{
|
defineEmits<{
|
||||||
(e: 'changePayType', type: PayCondition): void;
|
(e: 'changePayType', type: PayCondition): void;
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -221,6 +221,7 @@ export const useQuotationForm = defineStore('form-quotation', () => {
|
||||||
newWorkerList.value.push({
|
newWorkerList.value.push({
|
||||||
//passportNo: obj.data.passportNo,
|
//passportNo: obj.data.passportNo,
|
||||||
//documentExpireDate: obj.data.documentExpireDate,
|
//documentExpireDate: obj.data.documentExpireDate,
|
||||||
|
id: obj.data.id,
|
||||||
lastNameEN: obj.data.lastNameEN,
|
lastNameEN: obj.data.lastNameEN,
|
||||||
lastName: obj.data.lastName,
|
lastName: obj.data.lastName,
|
||||||
middleNameEN: obj.data.middleNameEN,
|
middleNameEN: obj.data.middleNameEN,
|
||||||
|
|
|
||||||
|
|
@ -600,7 +600,7 @@ function print() {
|
||||||
details?.worker.map(
|
details?.worker.map(
|
||||||
(v, i) =>
|
(v, i) =>
|
||||||
`${i + 1}. ` +
|
`${i + 1}. ` +
|
||||||
`${v.namePrefix}. ${v.firstNameEN} ${v.lastNameEN}`.toUpperCase(),
|
`${v.namePrefix}. ${v.firstNameEN ? `${v.firstNameEN} ${v.lastNameEN}` : `${v.firstName} ${v.lastName}`} `.toUpperCase(),
|
||||||
) || [],
|
) || [],
|
||||||
},
|
},
|
||||||
}) || '-'
|
}) || '-'
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import DrawerInfo from 'src/components/DrawerInfo.vue';
|
||||||
import DialogForm from 'src/components/DialogForm.vue';
|
import DialogForm from 'src/components/DialogForm.vue';
|
||||||
import ProfileBanner from 'src/components/ProfileBanner.vue';
|
import ProfileBanner from 'src/components/ProfileBanner.vue';
|
||||||
import SideMenu from 'src/components/SideMenu.vue';
|
import SideMenu from 'src/components/SideMenu.vue';
|
||||||
|
import FormBank from 'src/components/01_branch-management/FormBank.vue';
|
||||||
import FormBasicInfoAgencies from 'src/components/07_agencies-management/FormBasicInfoAgencies.vue';
|
import FormBasicInfoAgencies from 'src/components/07_agencies-management/FormBasicInfoAgencies.vue';
|
||||||
import {
|
import {
|
||||||
UndoButton,
|
UndoButton,
|
||||||
|
|
@ -19,6 +20,8 @@ import {
|
||||||
} from 'src/components/button';
|
} from 'src/components/button';
|
||||||
import AddressForm from 'src/components/form/AddressForm.vue';
|
import AddressForm from 'src/components/form/AddressForm.vue';
|
||||||
import ImageUploadDialog from 'src/components/ImageUploadDialog.vue';
|
import ImageUploadDialog from 'src/components/ImageUploadDialog.vue';
|
||||||
|
import { BankBook } from 'src/stores/branch/types';
|
||||||
|
import QrCodeUploadDialog from 'src/components/QrCodeUploadDialog.vue';
|
||||||
|
|
||||||
const institutionStore = useInstitution();
|
const institutionStore = useInstitution();
|
||||||
|
|
||||||
|
|
@ -27,10 +30,14 @@ const drawerModel = defineModel<boolean>('drawerModel', {
|
||||||
required: true,
|
required: true,
|
||||||
default: false,
|
default: false,
|
||||||
});
|
});
|
||||||
const onCreateImageList = defineModel<{
|
const imageListOnCreate = defineModel<{
|
||||||
selectedImage: string;
|
selectedImage: string;
|
||||||
list: { url: string; imgFile: File | null; name: string }[];
|
list: { url: string; imgFile: File | null; name: string }[];
|
||||||
}>('onCreateImageList', { default: { selectedImage: '', list: [] } });
|
}>('imageListOnCreate', { default: { selectedImage: '', list: [] } });
|
||||||
|
const deletesStatusQrCodeBankImag = defineModel<number[]>(
|
||||||
|
'deletesStatusQrCodeBankImag',
|
||||||
|
{ default: [] },
|
||||||
|
);
|
||||||
|
|
||||||
const imageState = reactive({
|
const imageState = reactive({
|
||||||
imageDialog: false,
|
imageDialog: false,
|
||||||
|
|
@ -43,6 +50,14 @@ const imageState = reactive({
|
||||||
const imageFile = ref<File | null>(null);
|
const imageFile = ref<File | null>(null);
|
||||||
const imageList = ref<{ selectedImage: string; list: string[] }>();
|
const imageList = ref<{ selectedImage: string; list: string[] }>();
|
||||||
|
|
||||||
|
const qrCodeDialog = ref(false);
|
||||||
|
const qrCodeImageUrl = ref<string>('');
|
||||||
|
const currentIndexQrCodeBank = ref<number>(-1);
|
||||||
|
const statusQrCodeFile = ref<File | undefined>(undefined);
|
||||||
|
const refQrCodeUpload = ref();
|
||||||
|
const statusQrCodeUrl = ref<string>('');
|
||||||
|
const statusDeletesQrCode = ref<boolean>(false);
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
|
|
@ -73,6 +88,9 @@ const data = defineModel<InstitutionPayload>('data', {
|
||||||
group: '',
|
group: '',
|
||||||
name: '',
|
name: '',
|
||||||
nameEN: '',
|
nameEN: '',
|
||||||
|
contactName: '',
|
||||||
|
contactEmail: '',
|
||||||
|
contactTel: '',
|
||||||
code: '',
|
code: '',
|
||||||
addressEN: '',
|
addressEN: '',
|
||||||
address: '',
|
address: '',
|
||||||
|
|
@ -88,6 +106,19 @@ const data = defineModel<InstitutionPayload>('data', {
|
||||||
selectedImage: '',
|
selectedImage: '',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const formBankBook = defineModel<BankBook[]>('formBankBook', {
|
||||||
|
default: [
|
||||||
|
{
|
||||||
|
bankName: '',
|
||||||
|
accountNumber: '',
|
||||||
|
bankBranch: '',
|
||||||
|
accountName: '',
|
||||||
|
accountType: '',
|
||||||
|
currentlyUse: true,
|
||||||
|
bankUrl: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
function viewImage() {
|
function viewImage() {
|
||||||
imageState.imageDialog = true;
|
imageState.imageDialog = true;
|
||||||
|
|
@ -141,10 +172,39 @@ async function submitImage(name: string) {
|
||||||
function clearImageState() {
|
function clearImageState() {
|
||||||
imageState.imageDialog = false;
|
imageState.imageDialog = false;
|
||||||
imageFile.value = null;
|
imageFile.value = null;
|
||||||
onCreateImageList.value = { selectedImage: '', list: [] };
|
imageListOnCreate.value = { selectedImage: '', list: [] };
|
||||||
imageState.refreshImageState = false;
|
imageState.refreshImageState = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function triggerEditQrCodeBank(opts?: { save?: boolean }) {
|
||||||
|
if (opts?.save === undefined) {
|
||||||
|
qrCodeDialog.value = true;
|
||||||
|
statusDeletesQrCode.value = false;
|
||||||
|
statusQrCodeUrl.value =
|
||||||
|
formBankBook.value[currentIndexQrCodeBank.value].bankUrl || '';
|
||||||
|
statusDeletesQrCode.value = false;
|
||||||
|
} else {
|
||||||
|
formBankBook.value[currentIndexQrCodeBank.value].bankUrl =
|
||||||
|
statusQrCodeUrl.value;
|
||||||
|
|
||||||
|
formBankBook.value[currentIndexQrCodeBank.value].bankQr =
|
||||||
|
statusQrCodeFile.value;
|
||||||
|
|
||||||
|
if (statusDeletesQrCode.value === true) {
|
||||||
|
deletesStatusQrCodeBankImag.value.push(currentIndexQrCodeBank.value);
|
||||||
|
}
|
||||||
|
if (statusDeletesQrCode.value === false) {
|
||||||
|
deletesStatusQrCodeBankImag.value =
|
||||||
|
deletesStatusQrCodeBankImag.value.filter(
|
||||||
|
(item) => item !== currentIndexQrCodeBank.value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentIndexQrCodeBank.value = -1;
|
||||||
|
statusDeletesQrCode.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => imageFile.value,
|
() => imageFile.value,
|
||||||
() => {
|
() => {
|
||||||
|
|
@ -159,7 +219,6 @@ watch(
|
||||||
imageList.value
|
imageList.value
|
||||||
? (imageList.value.selectedImage = data.value.selectedImage || '')
|
? (imageList.value.selectedImage = data.value.selectedImage || '')
|
||||||
: '';
|
: '';
|
||||||
console.log(imageState.imageUrl);
|
|
||||||
imageState.refreshImageState = false;
|
imageState.refreshImageState = false;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -237,11 +296,15 @@ watch(
|
||||||
:menu="[
|
:menu="[
|
||||||
{
|
{
|
||||||
name: $t('form.field.basicInformation'),
|
name: $t('form.field.basicInformation'),
|
||||||
anchor: 'agencies-basic-info',
|
anchor: 'agencies-form-basic-info',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: $t('general.address'),
|
name: $t('general.address'),
|
||||||
anchor: 'agencies-address-info',
|
anchor: 'agencies-form-address-info',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: $t('agencies.bankInfo'),
|
||||||
|
anchor: 'agencies-form-bank-info',
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
background="transparent"
|
background="transparent"
|
||||||
|
|
@ -275,14 +338,17 @@ watch(
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<FormBasicInfoAgencies
|
<FormBasicInfoAgencies
|
||||||
id="agencies-basic-info"
|
id="agencies-form-basic-info"
|
||||||
class="q-mb-xl"
|
class="q-mb-xl"
|
||||||
v-model:group="data.group"
|
v-model:group="data.group"
|
||||||
v-model:name="data.name"
|
v-model:name="data.name"
|
||||||
v-model:name-en="data.nameEN"
|
v-model:name-en="data.nameEN"
|
||||||
|
v-model:contact-name="data.contactName"
|
||||||
|
v-model:email="data.contactEmail"
|
||||||
|
v-model:contact-tel="data.contactTel"
|
||||||
/>
|
/>
|
||||||
<AddressForm
|
<AddressForm
|
||||||
id="agencies-address-info"
|
id="agencies-form-address-info"
|
||||||
dense
|
dense
|
||||||
:prefix-id="''"
|
:prefix-id="''"
|
||||||
v-model:address="data.address"
|
v-model:address="data.address"
|
||||||
|
|
@ -297,6 +363,25 @@ watch(
|
||||||
v-model:district-id="data.districtId"
|
v-model:district-id="data.districtId"
|
||||||
v-model:sub-district-id="data.subDistrictId"
|
v-model:sub-district-id="data.subDistrictId"
|
||||||
/>
|
/>
|
||||||
|
<FormBank
|
||||||
|
id="agencies-form-bank-info"
|
||||||
|
title="agencies.bankInfo"
|
||||||
|
class="q-pt-xl"
|
||||||
|
dense
|
||||||
|
v-model:bank-book-list="formBankBook"
|
||||||
|
@view-qr="
|
||||||
|
(i) => {
|
||||||
|
currentIndexQrCodeBank = i;
|
||||||
|
triggerEditQrCodeBank();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
@edit-qr="
|
||||||
|
(i) => {
|
||||||
|
currentIndexQrCodeBank = i;
|
||||||
|
refQrCodeUpload && refQrCodeUpload.browse();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DialogForm>
|
</DialogForm>
|
||||||
|
|
@ -427,13 +512,17 @@ watch(
|
||||||
name: $t('general.address'),
|
name: $t('general.address'),
|
||||||
anchor: 'agencies-address-info',
|
anchor: 'agencies-address-info',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: $t('agencies.bankInfo'),
|
||||||
|
anchor: 'agencies-bank-info',
|
||||||
|
},
|
||||||
]"
|
]"
|
||||||
background="transparent"
|
background="transparent"
|
||||||
:active="{
|
:active="{
|
||||||
background: 'hsla(var(--blue-6-hsl) / .2)',
|
background: 'hsla(var(--blue-6-hsl) / .2)',
|
||||||
foreground: 'var(--blue-6)',
|
foreground: 'var(--blue-6)',
|
||||||
}"
|
}"
|
||||||
scroll-element="#agencies-form-content"
|
scroll-element="#agencies-view-content"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -444,7 +533,7 @@ watch(
|
||||||
'q-py-md q-pr-md ': $q.screen.gt.sm,
|
'q-py-md q-pr-md ': $q.screen.gt.sm,
|
||||||
'q-pa-sm': !$q.screen.gt.sm,
|
'q-pa-sm': !$q.screen.gt.sm,
|
||||||
}"
|
}"
|
||||||
id="user-form-content"
|
id="agencies-view-content"
|
||||||
style="height: 100%; max-height: 100; overflow-y: auto"
|
style="height: 100%; max-height: 100; overflow-y: auto"
|
||||||
>
|
>
|
||||||
<FormBasicInfoAgencies
|
<FormBasicInfoAgencies
|
||||||
|
|
@ -455,6 +544,9 @@ watch(
|
||||||
v-model:group="data.group"
|
v-model:group="data.group"
|
||||||
v-model:name="data.name"
|
v-model:name="data.name"
|
||||||
v-model:name-en="data.nameEN"
|
v-model:name-en="data.nameEN"
|
||||||
|
v-model:contact-name="data.contactName"
|
||||||
|
v-model:email="data.contactEmail"
|
||||||
|
v-model:contact-tel="data.contactTel"
|
||||||
/>
|
/>
|
||||||
<AddressForm
|
<AddressForm
|
||||||
id="agencies-address-info"
|
id="agencies-address-info"
|
||||||
|
|
@ -473,6 +565,26 @@ watch(
|
||||||
v-model:district-id="data.districtId"
|
v-model:district-id="data.districtId"
|
||||||
v-model:sub-district-id="data.subDistrictId"
|
v-model:sub-district-id="data.subDistrictId"
|
||||||
/>
|
/>
|
||||||
|
<FormBank
|
||||||
|
id="agencies-bank-info"
|
||||||
|
title="agencies.bankInfo"
|
||||||
|
class="q-pt-xl"
|
||||||
|
dense
|
||||||
|
:readonly
|
||||||
|
v-model:bank-book-list="formBankBook"
|
||||||
|
@view-qr="
|
||||||
|
(i) => {
|
||||||
|
currentIndexQrCodeBank = i;
|
||||||
|
triggerEditQrCodeBank();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
@edit-qr="
|
||||||
|
(i) => {
|
||||||
|
currentIndexQrCodeBank = i;
|
||||||
|
refQrCodeUpload && refQrCodeUpload.browse();
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -482,7 +594,7 @@ watch(
|
||||||
<ImageUploadDialog
|
<ImageUploadDialog
|
||||||
v-model:dialog-state="imageState.imageDialog"
|
v-model:dialog-state="imageState.imageDialog"
|
||||||
v-model:file="imageFile"
|
v-model:file="imageFile"
|
||||||
v-model:on-create-data-list="onCreateImageList"
|
v-model:on-create-data-list="imageListOnCreate"
|
||||||
v-model:image-url="imageState.imageUrl"
|
v-model:image-url="imageState.imageUrl"
|
||||||
v-model:data-list="imageList"
|
v-model:data-list="imageList"
|
||||||
:on-create="model"
|
:on-create="model"
|
||||||
|
|
@ -512,5 +624,31 @@ watch(
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ImageUploadDialog>
|
</ImageUploadDialog>
|
||||||
|
|
||||||
|
<QrCodeUploadDialog
|
||||||
|
ref="refQrCodeUpload"
|
||||||
|
v-model:dialog-state="qrCodeDialog"
|
||||||
|
v-model:file="statusQrCodeFile as File"
|
||||||
|
v-model:image-url="statusQrCodeUrl"
|
||||||
|
@save="
|
||||||
|
(_file) => {
|
||||||
|
qrCodeDialog = false;
|
||||||
|
if (currentIndexQrCodeBank !== -1) {
|
||||||
|
triggerEditQrCodeBank({ save: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
@clear="statusDeletesQrCode = true"
|
||||||
|
clearButton
|
||||||
|
>
|
||||||
|
<template #error>
|
||||||
|
<div
|
||||||
|
class="full-width full-height flex items-center justify-center"
|
||||||
|
style="color: gray"
|
||||||
|
>
|
||||||
|
<q-icon size="15rem" name="mdi-qrcode" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</QrCodeUploadDialog>
|
||||||
</template>
|
</template>
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { QSelect, QTableProps } from 'quasar';
|
import { QTableProps } from 'quasar';
|
||||||
import { dialog } from 'src/stores/utils';
|
import { dialog } from 'src/stores/utils';
|
||||||
import { onMounted, reactive, ref, watch } from 'vue';
|
import { onMounted, reactive, ref, watch } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
@ -21,6 +21,7 @@ import FloatingActionButton from 'src/components/FloatingActionButton.vue';
|
||||||
import CreateButton from 'src/components/AddButton.vue';
|
import CreateButton from 'src/components/AddButton.vue';
|
||||||
import NoData from 'src/components/NoData.vue';
|
import NoData from 'src/components/NoData.vue';
|
||||||
import AgenciesDialog from './AgenciesDialog.vue';
|
import AgenciesDialog from './AgenciesDialog.vue';
|
||||||
|
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
|
|
@ -78,13 +79,17 @@ const pageState = reactive({
|
||||||
addModal: false,
|
addModal: false,
|
||||||
viewDrawer: false,
|
viewDrawer: false,
|
||||||
isDrawerEdit: true,
|
isDrawerEdit: true,
|
||||||
|
searchDate: [],
|
||||||
});
|
});
|
||||||
|
const deletesStatusQrCodeBankImag = ref<number[]>([]);
|
||||||
const blankFormData: InstitutionPayload = {
|
const blankFormData: InstitutionPayload = {
|
||||||
group: '',
|
group: '',
|
||||||
code: '',
|
code: '',
|
||||||
name: '',
|
name: '',
|
||||||
nameEN: '',
|
nameEN: '',
|
||||||
|
contactName: '',
|
||||||
|
contactEmail: '',
|
||||||
|
contactTel: '',
|
||||||
addressEN: '',
|
addressEN: '',
|
||||||
address: '',
|
address: '',
|
||||||
soi: '',
|
soi: '',
|
||||||
|
|
@ -98,14 +103,23 @@ const blankFormData: InstitutionPayload = {
|
||||||
provinceId: '',
|
provinceId: '',
|
||||||
selectedImage: '',
|
selectedImage: '',
|
||||||
status: 'CREATED',
|
status: 'CREATED',
|
||||||
|
bank: [
|
||||||
|
{
|
||||||
|
bankName: '',
|
||||||
|
accountNumber: '',
|
||||||
|
bankBranch: '',
|
||||||
|
accountName: '',
|
||||||
|
accountType: '',
|
||||||
|
currentlyUse: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusFilter = ref<'all' | 'statusACTIVE' | 'statusINACTIVE'>('all');
|
const statusFilter = ref<'all' | 'statusACTIVE' | 'statusINACTIVE'>('all');
|
||||||
const refFilter = ref<InstanceType<typeof QSelect>>();
|
|
||||||
const refAgenciesDialog = ref();
|
const refAgenciesDialog = ref();
|
||||||
const formData = ref<InstitutionPayload>(structuredClone(blankFormData));
|
const formData = ref<InstitutionPayload>(structuredClone(blankFormData));
|
||||||
const currAgenciesData = ref<Institution>();
|
const currAgenciesData = ref<Institution>();
|
||||||
const onCreateImageList = ref<{
|
const imageListOnCreate = ref<{
|
||||||
selectedImage: string;
|
selectedImage: string;
|
||||||
list: { url: string; imgFile: File | null; name: string }[];
|
list: { url: string; imgFile: File | null; name: string }[];
|
||||||
}>({ selectedImage: '', list: [] });
|
}>({ selectedImage: '', list: [] });
|
||||||
|
|
@ -160,6 +174,18 @@ function assignFormData(data: Institution) {
|
||||||
provinceId: data.provinceId,
|
provinceId: data.provinceId,
|
||||||
selectedImage: data.selectedImage,
|
selectedImage: data.selectedImage,
|
||||||
status: data.status,
|
status: data.status,
|
||||||
|
contactEmail: data.contactEmail,
|
||||||
|
contactName: data.contactName,
|
||||||
|
contactTel: data.contactTel,
|
||||||
|
bank: data.bank.map((v) => ({
|
||||||
|
bankName: v.bankName,
|
||||||
|
accountNumber: v.accountNumber,
|
||||||
|
bankBranch: v.bankBranch,
|
||||||
|
accountName: v.accountName,
|
||||||
|
accountType: v.accountType,
|
||||||
|
currentlyUse: v.currentlyUse,
|
||||||
|
bankUrl: `${baseUrl}/institution/${data.id}/bank-qr/${v.id}?ts=${Date.now()}`,
|
||||||
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,6 +195,9 @@ async function submit(opt?: { selectedImage: string }) {
|
||||||
code: formData.value.code,
|
code: formData.value.code,
|
||||||
name: formData.value.name,
|
name: formData.value.name,
|
||||||
nameEN: formData.value.nameEN,
|
nameEN: formData.value.nameEN,
|
||||||
|
contactName: formData.value.contactName,
|
||||||
|
contactEmail: formData.value.contactEmail,
|
||||||
|
contactTel: formData.value.contactTel,
|
||||||
addressEN: formData.value.addressEN,
|
addressEN: formData.value.addressEN,
|
||||||
address: formData.value.address,
|
address: formData.value.address,
|
||||||
soi: formData.value.soi,
|
soi: formData.value.soi,
|
||||||
|
|
@ -181,7 +210,11 @@ async function submit(opt?: { selectedImage: string }) {
|
||||||
districtId: formData.value.districtId,
|
districtId: formData.value.districtId,
|
||||||
provinceId: formData.value.provinceId,
|
provinceId: formData.value.provinceId,
|
||||||
status: formData.value.status,
|
status: formData.value.status,
|
||||||
|
bank: formData.value.bank.map((v) => ({
|
||||||
|
...v,
|
||||||
|
})),
|
||||||
};
|
};
|
||||||
|
console.log('payload', payload);
|
||||||
if (
|
if (
|
||||||
(pageState.isDrawerEdit && currAgenciesData.value?.id) ||
|
(pageState.isDrawerEdit && currAgenciesData.value?.id) ||
|
||||||
(opt?.selectedImage && currAgenciesData.value?.id)
|
(opt?.selectedImage && currAgenciesData.value?.id)
|
||||||
|
|
@ -192,6 +225,7 @@ async function submit(opt?: { selectedImage: string }) {
|
||||||
id: currAgenciesData.value.id,
|
id: currAgenciesData.value.id,
|
||||||
selectedImage: opt?.selectedImage || undefined,
|
selectedImage: opt?.selectedImage || undefined,
|
||||||
}),
|
}),
|
||||||
|
{ indexDeleteQrCodeBank: deletesStatusQrCodeBankImag.value },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
|
@ -211,7 +245,7 @@ async function submit(opt?: { selectedImage: string }) {
|
||||||
...payload,
|
...payload,
|
||||||
code: formData.value.group || '',
|
code: formData.value.group || '',
|
||||||
},
|
},
|
||||||
onCreateImageList.value,
|
imageListOnCreate.value,
|
||||||
);
|
);
|
||||||
|
|
||||||
await fetchData($q.screen.xs);
|
await fetchData($q.screen.xs);
|
||||||
|
|
@ -251,6 +285,8 @@ async function fetchData(mobileFetch?: boolean) {
|
||||||
: statusFilter.value === 'statusACTIVE'
|
: statusFilter.value === 'statusACTIVE'
|
||||||
? 'ACTIVE'
|
? 'ACTIVE'
|
||||||
: 'INACTIVE',
|
: 'INACTIVE',
|
||||||
|
startDate: pageState.searchDate[0],
|
||||||
|
endDate: pageState.searchDate[1],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
|
@ -320,7 +356,7 @@ onMounted(async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => [pageState.inputSearch, statusFilter.value],
|
() => [pageState.inputSearch, statusFilter.value, pageState.searchDate],
|
||||||
() => {
|
() => {
|
||||||
page.value = 1;
|
page.value = 1;
|
||||||
data.value = [];
|
data.value = [];
|
||||||
|
|
@ -403,26 +439,44 @@ watch(
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<q-icon name="mdi-magnify" />
|
<q-icon name="mdi-magnify" />
|
||||||
</template>
|
</template>
|
||||||
<template v-if="$q.screen.lt.md" v-slot:append>
|
<template v-slot:append>
|
||||||
<span class="row">
|
<q-separator vertical inset class="q-mr-xs" />
|
||||||
<q-separator vertical />
|
<AdvanceSearch
|
||||||
<q-btn
|
v-model="pageState.searchDate"
|
||||||
icon="mdi-filter-variant"
|
:active="$q.screen.lt.md && statusFilter !== 'all'"
|
||||||
unelevated
|
>
|
||||||
class="q-ml-sm"
|
<div
|
||||||
padding="4px"
|
v-if="$q.screen.lt.md"
|
||||||
size="sm"
|
class="q-mt-sm text-weight-medium"
|
||||||
rounded
|
>
|
||||||
@click="refFilter?.showPopup"
|
{{ $t('general.status') }}
|
||||||
|
</div>
|
||||||
|
<q-select
|
||||||
|
v-if="$q.screen.lt.md"
|
||||||
|
v-model="statusFilter"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
option-value="value"
|
||||||
|
option-label="label"
|
||||||
|
map-options
|
||||||
|
emit-value
|
||||||
|
:for="'field-select-status'"
|
||||||
|
:options="[
|
||||||
|
{ label: $t('general.all'), value: 'all' },
|
||||||
|
{ label: $t('general.active'), value: 'statusACTIVE' },
|
||||||
|
{
|
||||||
|
label: $t('general.inactive'),
|
||||||
|
value: 'statusINACTIVE',
|
||||||
|
},
|
||||||
|
]"
|
||||||
/>
|
/>
|
||||||
</span>
|
</AdvanceSearch>
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<div class="row col-md-5 justify-end" style="white-space: nowrap">
|
<div class="row col-md-5 justify-end" style="white-space: nowrap">
|
||||||
<q-select
|
<q-select
|
||||||
v-show="$q.screen.gt.sm"
|
v-if="$q.screen.gt.sm"
|
||||||
ref="refFilter"
|
|
||||||
v-model="statusFilter"
|
v-model="statusFilter"
|
||||||
outlined
|
outlined
|
||||||
dense
|
dense
|
||||||
|
|
@ -922,7 +976,9 @@ watch(
|
||||||
v-model="pageState.addModal"
|
v-model="pageState.addModal"
|
||||||
v-model:drawer-model="pageState.viewDrawer"
|
v-model:drawer-model="pageState.viewDrawer"
|
||||||
v-model:data="formData"
|
v-model:data="formData"
|
||||||
v-model:on-create-image-list="onCreateImageList"
|
v-model:form-bank-book="formData.bank"
|
||||||
|
v-model:image-list-on-create="imageListOnCreate"
|
||||||
|
v-model:deletes-status-qr-code-bank-imag="deletesStatusQrCodeBankImag"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import { useRequestList } from 'src/stores/request-list';
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
step: Step;
|
step: Step;
|
||||||
|
requestWorkId: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const requestListStore = useRequestList();
|
const requestListStore = useRequestList();
|
||||||
|
|
@ -116,7 +117,7 @@ function assignToForm() {
|
||||||
<FormGroupHead>
|
<FormGroupHead>
|
||||||
{{ $t('quotation.templateForm') }}
|
{{ $t('quotation.templateForm') }}
|
||||||
</FormGroupHead>
|
</FormGroupHead>
|
||||||
<FormIssue :readonly="!state.isEdit" />
|
<FormIssue :request-work-id="requestWorkId" :readonly="!state.isEdit" />
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</q-expansion-item>
|
</q-expansion-item>
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,49 @@
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { MainButton } from 'components/button';
|
import { MainButton } from 'components/button';
|
||||||
import SelectInput from 'src/components/shared/SelectInput.vue';
|
import SelectInput from 'src/components/shared/SelectInput.vue';
|
||||||
|
import { onMounted } from 'vue';
|
||||||
|
import { api } from 'src/boot/axios';
|
||||||
|
import { baseUrl } from 'src/stores/utils';
|
||||||
|
|
||||||
defineProps<{
|
const prop = defineProps<{
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
|
requestWorkId: string;
|
||||||
}>();
|
}>();
|
||||||
const templateForm = defineModel<string>();
|
const templateForm = defineModel<string>();
|
||||||
const templateFormOption = ref<
|
const templateFormOption = ref<
|
||||||
{ label: string; labelEN: string; value: string }[]
|
{ label: string; labelEN: string; value: string }[]
|
||||||
>([]);
|
>([]);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const { data: docTemplate, status } = await api.get<string[]>(
|
||||||
|
'/doc-template',
|
||||||
|
{ params: { templateGroup: 'Request' } },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (status < 400) {
|
||||||
|
templateFormOption.value = docTemplate.map((v) => ({
|
||||||
|
label: v,
|
||||||
|
labelEN: v,
|
||||||
|
value: v,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function formDownload() {
|
||||||
|
const res = await fetch(
|
||||||
|
baseUrl +
|
||||||
|
'/doc-template/' +
|
||||||
|
templateForm.value +
|
||||||
|
`?templateGroup=Request&data=request-work&dataId=${prop.requestWorkId}`,
|
||||||
|
);
|
||||||
|
const blob = await res.blob();
|
||||||
|
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.download = templateForm.value;
|
||||||
|
a.href = window.URL.createObjectURL(blob);
|
||||||
|
a.click();
|
||||||
|
a.remove();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -24,7 +59,6 @@ const templateFormOption = ref<
|
||||||
<SelectInput
|
<SelectInput
|
||||||
id="quotation-branch"
|
id="quotation-branch"
|
||||||
style="grid-column: span 2"
|
style="grid-column: span 2"
|
||||||
incremental
|
|
||||||
v-model="templateForm"
|
v-model="templateForm"
|
||||||
class="full-width"
|
class="full-width"
|
||||||
:readonly
|
:readonly
|
||||||
|
|
@ -32,21 +66,14 @@ const templateFormOption = ref<
|
||||||
:label="$t('quotation.templateForm')"
|
:label="$t('quotation.templateForm')"
|
||||||
:option-label="$i18n.locale === 'eng' ? 'labelEN' : 'label'"
|
:option-label="$i18n.locale === 'eng' ? 'labelEN' : 'label'"
|
||||||
/>
|
/>
|
||||||
<MainButton
|
|
||||||
outlined
|
|
||||||
icon="mdi-play-box-outline"
|
|
||||||
color="207 96% 32%"
|
|
||||||
class="full-width"
|
|
||||||
style="grid-column: span 1"
|
|
||||||
>
|
|
||||||
{{ $t('general.view', { msg: $t('general.example') }) }}
|
|
||||||
</MainButton>
|
|
||||||
<MainButton
|
<MainButton
|
||||||
solid
|
solid
|
||||||
icon="mdi-pencil-outline"
|
icon="mdi-pencil-outline"
|
||||||
color="207 96% 32%"
|
color="207 96% 32%"
|
||||||
class="full-width"
|
class="full-width"
|
||||||
style="grid-column: span 1"
|
style="grid-column: span 1"
|
||||||
|
:disabled="!templateForm"
|
||||||
|
@click="formDownload"
|
||||||
>
|
>
|
||||||
{{ $t('general.designForm') }}
|
{{ $t('general.designForm') }}
|
||||||
</MainButton>
|
</MainButton>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
import { computed, onMounted, reactive, ref, watch } from 'vue';
|
import { computed, onMounted, reactive, ref, watch } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { QSelect, useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
|
|
||||||
// NOTE: Components
|
// NOTE: Components
|
||||||
import StatCardComponent from 'src/components/StatCardComponent.vue';
|
import StatCardComponent from 'src/components/StatCardComponent.vue';
|
||||||
|
|
@ -24,6 +24,9 @@ import { RequestData, RequestDataStatus } from 'src/stores/request-list/types';
|
||||||
import { dialogWarningClose } from 'src/stores/utils';
|
import { dialogWarningClose } from 'src/stores/utils';
|
||||||
import { CancelButton, SaveButton } from 'src/components/button';
|
import { CancelButton, SaveButton } from 'src/components/button';
|
||||||
import { getRole } from 'src/services/keycloak';
|
import { getRole } from 'src/services/keycloak';
|
||||||
|
import FloatingActionButton from 'src/components/FloatingActionButton.vue';
|
||||||
|
import RequestListAction from './RequestListAction .vue';
|
||||||
|
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const navigatorStore = useNavigator();
|
const navigatorStore = useNavigator();
|
||||||
|
|
@ -32,7 +35,7 @@ const requestListStore = useRequestList();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { data, stats, page, pageMax, pageSize } = storeToRefs(requestListStore);
|
const { data, stats, page, pageMax, pageSize } = storeToRefs(requestListStore);
|
||||||
|
|
||||||
const refFilter = ref<InstanceType<typeof QSelect>>();
|
const requestListActionData = ref<RequestData[]>();
|
||||||
|
|
||||||
// NOTE: Variable
|
// NOTE: Variable
|
||||||
const pageState = reactive({
|
const pageState = reactive({
|
||||||
|
|
@ -45,6 +48,8 @@ const pageState = reactive({
|
||||||
rejectCancelDialog: false,
|
rejectCancelDialog: false,
|
||||||
rejectCancelReason: '',
|
rejectCancelReason: '',
|
||||||
requestId: '',
|
requestId: '',
|
||||||
|
requestListActionDialog: false,
|
||||||
|
searchDate: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const fieldSelectedOption = computed(() => {
|
const fieldSelectedOption = computed(() => {
|
||||||
|
|
@ -60,6 +65,8 @@ async function fetchList(opts?: { rotateFlowId?: boolean }) {
|
||||||
query: pageState.inputSearch,
|
query: pageState.inputSearch,
|
||||||
page: page.value,
|
page: page.value,
|
||||||
pageSize: pageSize.value,
|
pageSize: pageSize.value,
|
||||||
|
startDate: pageState.searchDate[0],
|
||||||
|
endDate: pageState.searchDate[1],
|
||||||
requestDataStatus:
|
requestDataStatus:
|
||||||
pageState.statusFilter === 'None' ? undefined : pageState.statusFilter,
|
pageState.statusFilter === 'None' ? undefined : pageState.statusFilter,
|
||||||
// responsibleOnly: true,
|
// responsibleOnly: true,
|
||||||
|
|
@ -131,6 +138,32 @@ async function submitRejectCancel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function openRequestListDialog() {
|
||||||
|
const ret = await requestListStore.getRequestDataList({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 999,
|
||||||
|
incomplete: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
requestListActionData.value = ret.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pageState.requestListActionDialog = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitRequestListAction(data: {
|
||||||
|
form: { responsibleUserLocal: boolean; responsibleUserId: string };
|
||||||
|
selected: RequestData[];
|
||||||
|
}) {
|
||||||
|
const res = await requestListStore.updateMessenger(
|
||||||
|
data.selected.map((v) => v.id),
|
||||||
|
data.form.responsibleUserId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res) pageState.requestListActionDialog = false;
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
pageState.gridView = $q.screen.lt.md ? true : false;
|
pageState.gridView = $q.screen.lt.md ? true : false;
|
||||||
navigatorStore.current.title = 'requestList.title';
|
navigatorStore.current.title = 'requestList.title';
|
||||||
|
|
@ -140,13 +173,27 @@ onMounted(async () => {
|
||||||
await fetchList({ rotateFlowId: true });
|
await fetchList({ rotateFlowId: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
watch([() => pageState.inputSearch, () => pageState.statusFilter], () => {
|
watch(
|
||||||
|
[
|
||||||
|
() => pageState.inputSearch,
|
||||||
|
() => pageState.statusFilter,
|
||||||
|
() => pageState.searchDate,
|
||||||
|
],
|
||||||
|
() => {
|
||||||
page.value = 1;
|
page.value = 1;
|
||||||
data.value = [];
|
data.value = [];
|
||||||
fetchList({ rotateFlowId: true });
|
fetchList({ rotateFlowId: true });
|
||||||
});
|
},
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
<FloatingActionButton
|
||||||
|
hide-icon
|
||||||
|
style="z-index: 999"
|
||||||
|
icon="mdi-account-outline"
|
||||||
|
@click.stop="openRequestListDialog"
|
||||||
|
></FloatingActionButton>
|
||||||
|
|
||||||
<div class="column full-height no-wrap">
|
<div class="column full-height no-wrap">
|
||||||
<!-- SEC: stat -->
|
<!-- SEC: stat -->
|
||||||
<section class="text-body-2 q-mb-xs flex items-center">
|
<section class="text-body-2 q-mb-xs flex items-center">
|
||||||
|
|
@ -239,26 +286,62 @@ watch([() => pageState.inputSearch, () => pageState.statusFilter], () => {
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<q-icon name="mdi-magnify" />
|
<q-icon name="mdi-magnify" />
|
||||||
</template>
|
</template>
|
||||||
<template v-if="$q.screen.lt.md" v-slot:append>
|
<template v-slot:append>
|
||||||
<span class="row">
|
<q-separator vertical inset class="q-mr-xs" />
|
||||||
<q-separator vertical />
|
<AdvanceSearch
|
||||||
<q-btn
|
v-model="pageState.searchDate"
|
||||||
icon="mdi-filter-variant"
|
:active="$q.screen.lt.md && pageState.statusFilter !== 'None'"
|
||||||
unelevated
|
>
|
||||||
class="q-ml-sm"
|
<div
|
||||||
padding="4px"
|
v-if="$q.screen.lt.md"
|
||||||
size="sm"
|
class="q-mt-sm text-weight-medium"
|
||||||
rounded
|
>
|
||||||
@click="refFilter?.showPopup"
|
{{ $t('general.status') }}
|
||||||
|
</div>
|
||||||
|
<q-select
|
||||||
|
v-if="$q.screen.lt.md"
|
||||||
|
v-model="pageState.statusFilter"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
option-value="value"
|
||||||
|
option-label="label"
|
||||||
|
map-options
|
||||||
|
emit-value
|
||||||
|
:for="'field-select-status'"
|
||||||
|
:options="[
|
||||||
|
{
|
||||||
|
label: $t('general.all'),
|
||||||
|
value: 'None',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('requestList.status.Pending'),
|
||||||
|
value: RequestDataStatus.Pending,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('requestList.status.Ready'),
|
||||||
|
value: RequestDataStatus.Ready,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('requestList.status.InProgress'),
|
||||||
|
value: RequestDataStatus.InProgress,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('requestList.status.Completed'),
|
||||||
|
value: RequestDataStatus.Completed,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('requestList.status.Canceled'),
|
||||||
|
value: RequestDataStatus.Canceled,
|
||||||
|
},
|
||||||
|
]"
|
||||||
/>
|
/>
|
||||||
</span>
|
</AdvanceSearch>
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<div class="row col-md-5" style="white-space: nowrap">
|
<div class="row col-md-5" style="white-space: nowrap">
|
||||||
<q-select
|
<q-select
|
||||||
v-show="$q.screen.gt.sm"
|
v-if="$q.screen.gt.sm"
|
||||||
ref="refFilter"
|
|
||||||
v-model="pageState.statusFilter"
|
v-model="pageState.statusFilter"
|
||||||
outlined
|
outlined
|
||||||
dense
|
dense
|
||||||
|
|
@ -477,7 +560,7 @@ watch([() => pageState.inputSearch, () => pageState.statusFilter], () => {
|
||||||
@click="() => (pageState.rejectCancelDialog = false)"
|
@click="() => (pageState.rejectCancelDialog = false)"
|
||||||
/>
|
/>
|
||||||
<SaveButton
|
<SaveButton
|
||||||
label="ยืนยัน"
|
:label="$t('general.confirm')"
|
||||||
class="q-ml-sm"
|
class="q-ml-sm"
|
||||||
icon="mdi-check"
|
icon="mdi-check"
|
||||||
solid
|
solid
|
||||||
|
|
@ -485,6 +568,13 @@ watch([() => pageState.inputSearch, () => pageState.statusFilter], () => {
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</DialogFormContainer>
|
</DialogFormContainer>
|
||||||
|
|
||||||
|
<RequestListAction
|
||||||
|
v-if="requestListActionData"
|
||||||
|
v-model="pageState.requestListActionDialog"
|
||||||
|
:request-list="requestListActionData"
|
||||||
|
@submit="submitRequestListAction"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style></style>
|
<style></style>
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ const props = defineProps<{
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
step: Step;
|
step: Step;
|
||||||
responsibleAreaDistrictId?: string;
|
responsibleAreaDistrictId?: string;
|
||||||
|
defaultMessenger?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|
@ -85,7 +86,8 @@ function assignToForm() {
|
||||||
companyDuty: attributesForm.value.companyDuty ?? false,
|
companyDuty: attributesForm.value.companyDuty ?? false,
|
||||||
companyDutyCost: attributesForm.value.companyDutyCost ?? 30,
|
companyDutyCost: attributesForm.value.companyDutyCost ?? 30,
|
||||||
responsibleUserLocal: attributesForm.value.responsibleUserLocal ?? true,
|
responsibleUserLocal: attributesForm.value.responsibleUserLocal ?? true,
|
||||||
responsibleUserId: attributesForm.value.responsibleUserId ?? '',
|
responsibleUserId:
|
||||||
|
attributesForm.value.responsibleUserId || props.defaultMessenger,
|
||||||
individualDuty: attributesForm.value.individualDuty ?? false,
|
individualDuty: attributesForm.value.individualDuty ?? false,
|
||||||
individualDutyCost: attributesForm.value.individualDutyCost ?? 10,
|
individualDutyCost: attributesForm.value.individualDutyCost ?? 10,
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
238
src/pages/08_request-list/RequestListAction .vue
Normal file
238
src/pages/08_request-list/RequestListAction .vue
Normal file
|
|
@ -0,0 +1,238 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref, watch } from 'vue';
|
||||||
|
import { RequestData } from 'src/stores/request-list';
|
||||||
|
import { DialogContainer, DialogHeader } from 'src/components/dialog';
|
||||||
|
import {
|
||||||
|
BackButton,
|
||||||
|
CancelButton,
|
||||||
|
MainButton,
|
||||||
|
SaveButton,
|
||||||
|
} from 'src/components/button';
|
||||||
|
import FormResponsibleUser from './FormResponsibleUser.vue';
|
||||||
|
import FormGroupHead from './FormGroupHead.vue';
|
||||||
|
import TableRequestList from './TableRequestList.vue';
|
||||||
|
import { column } from './constants';
|
||||||
|
import useAddressStore from 'src/stores/address';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
requestList: RequestData[];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
(
|
||||||
|
e: 'submit',
|
||||||
|
data: {
|
||||||
|
form: { responsibleUserLocal: boolean; responsibleUserId: string };
|
||||||
|
selected: RequestData[];
|
||||||
|
},
|
||||||
|
): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
enum Step {
|
||||||
|
RequestList = 1,
|
||||||
|
Configure = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = defineModel<boolean>({ default: false });
|
||||||
|
const step = ref<Step>(Step.RequestList);
|
||||||
|
const selected = ref<RequestData[]>([]);
|
||||||
|
const listSameArea = ref<string[]>([]);
|
||||||
|
const form = reactive({
|
||||||
|
responsibleUserLocal: false,
|
||||||
|
responsibleUserId: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
step.value = Step.RequestList;
|
||||||
|
selected.value = [];
|
||||||
|
form.responsibleUserLocal = false;
|
||||||
|
form.responsibleUserId = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function prev() {
|
||||||
|
step.value = Step.RequestList;
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => selected.value,
|
||||||
|
async () => {
|
||||||
|
if (selected.value.length === 1) {
|
||||||
|
const districtId = selected.value[0].quotation.customerBranch.districtId;
|
||||||
|
const ret = await useAddressStore().listSameOfficeArea(districtId);
|
||||||
|
if (ret) listSameArea.value = ret;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<DialogContainer v-model="open" :onOpen="reset">
|
||||||
|
<template #header>
|
||||||
|
<DialogHeader :title="$t('requestList.action.title')" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="surface-0 q-pa-md">
|
||||||
|
<div class="stepper-wrapper">
|
||||||
|
<div class="stepper">
|
||||||
|
<template
|
||||||
|
v-for="(label, i) in [
|
||||||
|
$t('menu.product'),
|
||||||
|
$t('requestList.action.configure'),
|
||||||
|
]"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
|
<span class="step" :class="{ ['step__active']: step > i }">
|
||||||
|
<span class="step-outer"><span class="step-inner" /></span>
|
||||||
|
<span class="step-label">{{ label }}</span>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="step-connector"
|
||||||
|
:class="{ ['step-connector__active']: step > i + 1 }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="surface-1 q-pa-md col full-width scroll">
|
||||||
|
<TableRequestList
|
||||||
|
v-if="step === Step.RequestList"
|
||||||
|
v-model:selected="selected"
|
||||||
|
hide-action
|
||||||
|
hide-view
|
||||||
|
checkable
|
||||||
|
:list-same-area="listSameArea"
|
||||||
|
:columns="column"
|
||||||
|
:rows="requestList"
|
||||||
|
:visible-columns="[...column.map((col) => col.name)]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<template v-if="step === Step.Configure">
|
||||||
|
<q-expansion-item
|
||||||
|
dense
|
||||||
|
class="overflow-hidden bordered full-width"
|
||||||
|
switch-toggle-side
|
||||||
|
style="border-radius: var(--radius-2)"
|
||||||
|
expand-icon="mdi-chevron-down-circle"
|
||||||
|
header-class="surface-1 q-py-sm text-medium text-body1"
|
||||||
|
default-opened
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<span>
|
||||||
|
{{ $t('requestList.employeeMessenger') }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<FormGroupHead>
|
||||||
|
{{
|
||||||
|
$t('general.select', { msg: $t('requestList.employeeMessenger') })
|
||||||
|
}}
|
||||||
|
</FormGroupHead>
|
||||||
|
|
||||||
|
<FormResponsibleUser
|
||||||
|
:district-id="listSameArea[0]"
|
||||||
|
v-model:responsible-user-id="form.responsibleUserId"
|
||||||
|
v-model:responsible-user-local="form.responsibleUserLocal"
|
||||||
|
/>
|
||||||
|
</q-expansion-item>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div class="q-gutter-x-xs q-ml-auto">
|
||||||
|
<CancelButton
|
||||||
|
v-if="step === Step.RequestList"
|
||||||
|
id="btn-cancel"
|
||||||
|
outlined
|
||||||
|
@click="
|
||||||
|
reset();
|
||||||
|
open = false;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<BackButton
|
||||||
|
v-if="step === Step.Configure"
|
||||||
|
id="btn-back"
|
||||||
|
outlined
|
||||||
|
@click="prev"
|
||||||
|
/>
|
||||||
|
<MainButton
|
||||||
|
icon="mdi-check"
|
||||||
|
color="207 96% 32%"
|
||||||
|
solid
|
||||||
|
id="btn-next"
|
||||||
|
v-if="step === Step.RequestList"
|
||||||
|
@click="step = Step.Configure"
|
||||||
|
>
|
||||||
|
{{ $t('general.next') }}
|
||||||
|
</MainButton>
|
||||||
|
<SaveButton
|
||||||
|
v-if="step === Step.Configure"
|
||||||
|
id="btn-save"
|
||||||
|
solid
|
||||||
|
@click="$emit('submit', { form, selected })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</DialogContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.stepper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
margin-inline: 25%;
|
||||||
|
|
||||||
|
& > .step {
|
||||||
|
--__color: var(--gray-5);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
gap: 0.25rem;
|
||||||
|
|
||||||
|
&.step__active {
|
||||||
|
--__color: var(--brand-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .step-label {
|
||||||
|
position: absolute;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--__color);
|
||||||
|
white-space: nowrap;
|
||||||
|
top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .step-outer {
|
||||||
|
display: inline-flex;
|
||||||
|
border: 2px solid var(--__color);
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
& > .step-inner {
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--__color);
|
||||||
|
width: 0.7rem;
|
||||||
|
height: 0.7rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .step-connector {
|
||||||
|
display: block;
|
||||||
|
border-bottom: 2px solid var(--gray-5);
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
&.step-connector__active {
|
||||||
|
border-color: var(--brand-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -54,6 +54,7 @@ import { Invoice } from 'src/stores/payment/types';
|
||||||
import { CreatedBy } from 'src/stores/types';
|
import { CreatedBy } from 'src/stores/types';
|
||||||
import { getUserId } from 'src/services/keycloak';
|
import { getUserId } from 'src/services/keycloak';
|
||||||
import { QuotationFull } from 'src/stores/quotations/types';
|
import { QuotationFull } from 'src/stores/quotations/types';
|
||||||
|
import useUserStore from 'src/stores/user';
|
||||||
|
|
||||||
const { locale, t } = useI18n();
|
const { locale, t } = useI18n();
|
||||||
|
|
||||||
|
|
@ -62,7 +63,9 @@ const route = useRoute();
|
||||||
const optionStore = useOptionStore();
|
const optionStore = useOptionStore();
|
||||||
const requestListStore = useRequestList();
|
const requestListStore = useRequestList();
|
||||||
const flowTemplateStore = useWorkflowTemplate();
|
const flowTemplateStore = useWorkflowTemplate();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const currentUserGroup = ref<string[]>([]);
|
||||||
const workList = ref<RequestWork[]>([]);
|
const workList = ref<RequestWork[]>([]);
|
||||||
const statusFile = ref<Attributes>({
|
const statusFile = ref<Attributes>({
|
||||||
customer: {},
|
customer: {},
|
||||||
|
|
@ -158,6 +161,10 @@ onMounted(async () => {
|
||||||
initTheme();
|
initTheme();
|
||||||
initLang();
|
initLang();
|
||||||
|
|
||||||
|
const result = await userStore.fetchUserGroup();
|
||||||
|
|
||||||
|
currentUserGroup.value = result.map((v) => v.name);
|
||||||
|
|
||||||
// get data
|
// get data
|
||||||
await getData();
|
await getData();
|
||||||
});
|
});
|
||||||
|
|
@ -283,26 +290,38 @@ async function triggerViewFile(opt: {
|
||||||
if (!opt.download) window.open(url, '_blank');
|
if (!opt.download) window.open(url, '_blank');
|
||||||
}
|
}
|
||||||
|
|
||||||
const responsiblePersonList = computed(() => {
|
const responsibleList = computed(() => {
|
||||||
const temp = workList.value?.reduce<Record<string, CreatedBy[]>>(
|
const temp = workList.value?.reduce<
|
||||||
(acc, curr: RequestWork) => {
|
Record<string, { user: CreatedBy[]; group: string[] }>
|
||||||
|
>((acc, curr: RequestWork) => {
|
||||||
curr.productService.service?.workflow?.step.forEach((v) => {
|
curr.productService.service?.workflow?.step.forEach((v) => {
|
||||||
const key = v.order.toString();
|
const key = v.order.toString();
|
||||||
|
const responsibleGroup = (
|
||||||
|
v.responsibleGroup as unknown as { group: string }[]
|
||||||
|
).map((v) => v.group);
|
||||||
|
|
||||||
if (!acc[key]) acc[key] = v.responsiblePerson.map((v) => v.user);
|
if (!acc[key]) {
|
||||||
|
acc[key] = {
|
||||||
|
user: v.responsiblePerson.map((v) => v.user),
|
||||||
|
group: responsibleGroup,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const current = acc[key];
|
const current = acc[key];
|
||||||
|
|
||||||
v.responsiblePerson.forEach((lhs) => {
|
v.responsiblePerson.forEach((lhs) => {
|
||||||
if (current.find((rhs) => rhs.id === lhs.userId)) return;
|
if (current.user.find((rhs) => rhs.id === lhs.userId)) return;
|
||||||
current.push(lhs.user);
|
current.user.push(lhs.user);
|
||||||
|
});
|
||||||
|
|
||||||
|
responsibleGroup.forEach((lhs) => {
|
||||||
|
if (current.group.find((rhs) => rhs === lhs)) return;
|
||||||
|
current.group.push(lhs);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
},
|
}, {});
|
||||||
{},
|
|
||||||
);
|
|
||||||
|
|
||||||
return temp || {};
|
return temp || {};
|
||||||
});
|
});
|
||||||
|
|
@ -438,6 +457,24 @@ async function submitRejectCancel() {
|
||||||
pageState.rejectCancelDialog = false;
|
pageState.rejectCancelDialog = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toCustomer(customer: RequestData['quotation']['customerBranch']) {
|
||||||
|
const url = new URL(
|
||||||
|
`/customer-management?tab=customer&id=${customer.customerId}`,
|
||||||
|
window.location.origin,
|
||||||
|
);
|
||||||
|
|
||||||
|
window.open(url.toString(), '_blank');
|
||||||
|
}
|
||||||
|
|
||||||
|
function toEmployee(employee: RequestData['employee']) {
|
||||||
|
const url = new URL(
|
||||||
|
`/customer-management?tab=employee&id=${employee.id}`,
|
||||||
|
window.location.origin,
|
||||||
|
);
|
||||||
|
|
||||||
|
window.open(url.toString(), '_blank');
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="column surface-0 fullscreen" v-if="data">
|
<div class="column surface-0 fullscreen" v-if="data">
|
||||||
|
|
@ -478,11 +515,11 @@ async function submitRejectCancel() {
|
||||||
<span class="app-text-muted">{{ $t('flow.responsiblePerson') }}</span>
|
<span class="app-text-muted">{{ $t('flow.responsiblePerson') }}</span>
|
||||||
<span>
|
<span>
|
||||||
<template
|
<template
|
||||||
v-if="responsiblePersonList[pageState.currentStep]?.length"
|
v-if="responsibleList[pageState.currentStep]?.user.length"
|
||||||
>
|
>
|
||||||
<AvatarGroup
|
<AvatarGroup
|
||||||
:data="
|
:data="[
|
||||||
(responsiblePersonList[pageState.currentStep] || []).map(
|
...(responsibleList[pageState.currentStep].user || []).map(
|
||||||
(v) => ({
|
(v) => ({
|
||||||
name:
|
name:
|
||||||
$i18n.locale === 'eng'
|
$i18n.locale === 'eng'
|
||||||
|
|
@ -494,8 +531,12 @@ async function submitRejectCancel() {
|
||||||
: `/no-img-female.png`
|
: `/no-img-female.png`
|
||||||
: `${baseUrl}/user/${v.id}/profile-image/${v.selectedImage}`,
|
: `${baseUrl}/user/${v.id}/profile-image/${v.selectedImage}`,
|
||||||
}),
|
}),
|
||||||
)
|
),
|
||||||
"
|
...responsibleList[pageState.currentStep].group.map((g) => ({
|
||||||
|
name: `${$t('general.group')} ${g}`,
|
||||||
|
imgUrl: '/img-group.png',
|
||||||
|
})),
|
||||||
|
]"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>-</template>
|
<template v-else>-</template>
|
||||||
|
|
@ -701,6 +742,7 @@ async function submitRejectCancel() {
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<DataDisplay
|
<DataDisplay
|
||||||
|
clickable
|
||||||
class="col"
|
class="col"
|
||||||
icon="mdi-account-settings-outline"
|
icon="mdi-account-settings-outline"
|
||||||
:label="$t('customer.employer')"
|
:label="$t('customer.employer')"
|
||||||
|
|
@ -710,8 +752,10 @@ async function submitRejectCancel() {
|
||||||
noCode: true,
|
noCode: true,
|
||||||
}) || '-'
|
}) || '-'
|
||||||
"
|
"
|
||||||
|
@label-click="toCustomer(data.quotation.customerBranch)"
|
||||||
/>
|
/>
|
||||||
<DataDisplay
|
<DataDisplay
|
||||||
|
clickable
|
||||||
class="col"
|
class="col"
|
||||||
icon="mdi-account-settings-outline"
|
icon="mdi-account-settings-outline"
|
||||||
:label="$t('customer.employee')"
|
:label="$t('customer.employee')"
|
||||||
|
|
@ -720,6 +764,7 @@ async function submitRejectCancel() {
|
||||||
locale: $i18n.locale,
|
locale: $i18n.locale,
|
||||||
}) || '-'
|
}) || '-'
|
||||||
"
|
"
|
||||||
|
@label-click="toEmployee(data.employee)"
|
||||||
/>
|
/>
|
||||||
<DataDisplay
|
<DataDisplay
|
||||||
class="col"
|
class="col"
|
||||||
|
|
@ -776,11 +821,15 @@ async function submitRejectCancel() {
|
||||||
:cancel="data.requestDataStatus === RequestDataStatus.Canceled"
|
:cancel="data.requestDataStatus === RequestDataStatus.Canceled"
|
||||||
:readonly="
|
:readonly="
|
||||||
data.requestDataStatus === RequestDataStatus.Canceled ||
|
data.requestDataStatus === RequestDataStatus.Canceled ||
|
||||||
(responsiblePersonList &&
|
(responsibleList &&
|
||||||
!!responsiblePersonList[pageState.currentStep]?.length &&
|
!responsibleList[pageState.currentStep]?.user.find(
|
||||||
!responsiblePersonList[pageState.currentStep]?.find(
|
|
||||||
(v) => v.id === getUserId(),
|
(v) => v.id === getUserId(),
|
||||||
))
|
) &&
|
||||||
|
!responsibleList[pageState.currentStep]?.group.some((v) =>
|
||||||
|
currentUserGroup.includes(v),
|
||||||
|
)) ||
|
||||||
|
(!!responsibleList[pageState.currentStep]?.user?.length &&
|
||||||
|
!!responsibleList[pageState.currentStep]?.user?.length)
|
||||||
"
|
"
|
||||||
:order-able="value._messengerExpansion"
|
:order-able="value._messengerExpansion"
|
||||||
:installment-info="getInstallmentInfo()"
|
:installment-info="getInstallmentInfo()"
|
||||||
|
|
@ -873,6 +922,11 @@ async function submitRejectCancel() {
|
||||||
:readonly="
|
:readonly="
|
||||||
data.requestDataStatus === RequestDataStatus.Canceled
|
data.requestDataStatus === RequestDataStatus.Canceled
|
||||||
"
|
"
|
||||||
|
:default-messenger="
|
||||||
|
value.stepStatus[pageState.currentStep - 1]
|
||||||
|
? undefined
|
||||||
|
: data.defaultMessengerId
|
||||||
|
"
|
||||||
:step="{
|
:step="{
|
||||||
step: pageState.currentStep,
|
step: pageState.currentStep,
|
||||||
requestWorkId: value.id || '',
|
requestWorkId: value.id || '',
|
||||||
|
|
@ -906,6 +960,7 @@ async function submitRejectCancel() {
|
||||||
/>
|
/>
|
||||||
<FormExpansion
|
<FormExpansion
|
||||||
v-if="value._formExpansion"
|
v-if="value._formExpansion"
|
||||||
|
:request-work-id="value.id"
|
||||||
:readonly="
|
:readonly="
|
||||||
data.requestDataStatus === RequestDataStatus.Canceled
|
data.requestDataStatus === RequestDataStatus.Canceled
|
||||||
"
|
"
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import useOptionStore from 'src/stores/options';
|
||||||
|
|
||||||
import KebabAction from 'src/components/shared/KebabAction.vue';
|
import KebabAction from 'src/components/shared/KebabAction.vue';
|
||||||
import { CreatedBy } from 'src/stores/types';
|
import { CreatedBy } from 'src/stores/types';
|
||||||
|
import { dateFormatJS } from 'src/utils/datetime';
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
|
@ -21,6 +22,9 @@ const props = withDefaults(
|
||||||
grid?: boolean;
|
grid?: boolean;
|
||||||
visibleColumns?: string[];
|
visibleColumns?: string[];
|
||||||
hideAction?: boolean;
|
hideAction?: boolean;
|
||||||
|
hideView?: boolean;
|
||||||
|
checkable?: boolean;
|
||||||
|
listSameArea?: string[];
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
row: () => [],
|
row: () => [],
|
||||||
|
|
@ -36,9 +40,16 @@ defineEmits<{
|
||||||
(e: 'rejectCancel', data: RequestData): void;
|
(e: 'rejectCancel', data: RequestData): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
function responsiblePerson(quotation: QuotationFull): CreatedBy[] | undefined {
|
const selected = defineModel<RequestData[]>('selected');
|
||||||
|
|
||||||
|
function responsiblePerson(quotation: QuotationFull) {
|
||||||
const productServiceList = quotation.productServiceList;
|
const productServiceList = quotation.productServiceList;
|
||||||
const tempPerson: CreatedBy[] = [];
|
const tempPerson: CreatedBy[] = [];
|
||||||
|
const tempGroup: {
|
||||||
|
group: string;
|
||||||
|
id: string;
|
||||||
|
workflowTemplateStepId: string;
|
||||||
|
}[] = [];
|
||||||
|
|
||||||
for (const v of productServiceList) {
|
for (const v of productServiceList) {
|
||||||
const tempStep = v.service?.workflow?.step;
|
const tempStep = v.service?.workflow?.step;
|
||||||
|
|
@ -49,7 +60,17 @@ function responsiblePerson(quotation: QuotationFull): CreatedBy[] | undefined {
|
||||||
tempPerson.push(rhs.user);
|
tempPerson.push(rhs.user);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return tempPerson;
|
tempStep.forEach((lhs) => {
|
||||||
|
const newGroup = lhs.responsibleGroup as unknown as {
|
||||||
|
group: string;
|
||||||
|
id: string;
|
||||||
|
workflowTemplateStepId: string;
|
||||||
|
}[];
|
||||||
|
for (const rhs of newGroup) {
|
||||||
|
tempGroup.push(rhs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { user: tempPerson, group: tempGroup };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,10 +113,39 @@ function getEmployeeName(
|
||||||
return (
|
return (
|
||||||
{
|
{
|
||||||
['eng']: `${useOptionStore().mapOption(employee?.namePrefix || '')} ${employee?.firstNameEN} ${employee?.lastNameEN}`,
|
['eng']: `${useOptionStore().mapOption(employee?.namePrefix || '')} ${employee?.firstNameEN} ${employee?.lastNameEN}`,
|
||||||
['tha']: `${useOptionStore().mapOption(employee?.namePrefix || '')} ${employee?.firstName} ${employee?.lastName}`,
|
['tha']: `${useOptionStore().mapOption(employee?.namePrefix || '')} ${employee?.firstName || employee?.firstNameEN} ${employee?.lastName || employee?.lastNameEN}`,
|
||||||
}[opts?.locale || 'eng'] || '-'
|
}[opts?.locale || 'eng'] || '-'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toCustomer(customer: RequestData['quotation']['customerBranch']) {
|
||||||
|
const url = new URL(
|
||||||
|
`/customer-management?tab=customer&id=${customer.customerId}`,
|
||||||
|
window.location.origin,
|
||||||
|
);
|
||||||
|
|
||||||
|
window.open(url.toString(), '_blank');
|
||||||
|
}
|
||||||
|
|
||||||
|
function toEmployee(employee: RequestData['employee']) {
|
||||||
|
const url = new URL(
|
||||||
|
`/customer-management?tab=employee&id=${employee.id}`,
|
||||||
|
window.location.origin,
|
||||||
|
);
|
||||||
|
|
||||||
|
window.open(url.toString(), '_blank');
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCheckAll() {
|
||||||
|
const filteredRows = props.rows.filter((row) =>
|
||||||
|
props.listSameArea?.includes(row.quotation.customerBranch.districtId),
|
||||||
|
);
|
||||||
|
if (selected.value.length === filteredRows.length) {
|
||||||
|
selected.value = [];
|
||||||
|
} else {
|
||||||
|
selected.value = filteredRows;
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<q-table
|
<q-table
|
||||||
|
|
@ -106,12 +156,36 @@ function getEmployeeName(
|
||||||
card-container-class="q-col-gutter-sm"
|
card-container-class="q-col-gutter-sm"
|
||||||
:rows-per-page-options="[0]"
|
:rows-per-page-options="[0]"
|
||||||
class="full-width"
|
class="full-width"
|
||||||
|
selection="multiple"
|
||||||
|
v-model:selected="selected"
|
||||||
|
:selected-rows-label="
|
||||||
|
(n) =>
|
||||||
|
$t('general.selected', {
|
||||||
|
number: n,
|
||||||
|
msg: $t('general.list'),
|
||||||
|
})
|
||||||
|
"
|
||||||
|
:no-data-label="$t('general.noDataTable')"
|
||||||
>
|
>
|
||||||
<template v-slot:header="props">
|
<template v-slot:header="props">
|
||||||
<q-tr
|
<q-tr
|
||||||
style="background-color: hsla(var(--info-bg) / 0.07)"
|
style="background-color: hsla(var(--info-bg) / 0.07)"
|
||||||
:props="props"
|
:props="props"
|
||||||
>
|
>
|
||||||
|
<q-th v-if="checkable">
|
||||||
|
<q-checkbox
|
||||||
|
v-if="selected.length > 0"
|
||||||
|
:model-value="
|
||||||
|
selected.length ===
|
||||||
|
rows.filter((row) =>
|
||||||
|
listSameArea?.includes(row.quotation.customerBranch.districtId),
|
||||||
|
).length
|
||||||
|
"
|
||||||
|
size="sm"
|
||||||
|
@click="handleCheckAll"
|
||||||
|
/>
|
||||||
|
<div v-else style="width: 35px; height: 35px"></div>
|
||||||
|
</q-th>
|
||||||
<q-th v-for="col in props.cols" :key="col.name" :props="props">
|
<q-th v-for="col in props.cols" :key="col.name" :props="props">
|
||||||
{{ col.label && $t(col.label) }}
|
{{ col.label && $t(col.label) }}
|
||||||
</q-th>
|
</q-th>
|
||||||
|
|
@ -125,9 +199,30 @@ function getEmployeeName(
|
||||||
} & Omit<Parameters<QTableSlots['body']>[0], 'row'>"
|
} & Omit<Parameters<QTableSlots['body']>[0], 'row'>"
|
||||||
>
|
>
|
||||||
<q-tr
|
<q-tr
|
||||||
:class="{ urgent: props.row.quotation.urgent, dark: $q.dark.isActive }"
|
:class="{
|
||||||
|
urgent: props.row.quotation.urgent,
|
||||||
|
dark: $q.dark.isActive,
|
||||||
|
'disabled-row':
|
||||||
|
selected &&
|
||||||
|
selected.length > 0 &&
|
||||||
|
!listSameArea.includes(
|
||||||
|
props.row.quotation.customerBranch.districtId,
|
||||||
|
),
|
||||||
|
}"
|
||||||
class="text-center"
|
class="text-center"
|
||||||
>
|
>
|
||||||
|
<q-td v-if="checkable">
|
||||||
|
<q-checkbox
|
||||||
|
:disable="
|
||||||
|
selected.length > 0 &&
|
||||||
|
!listSameArea.includes(
|
||||||
|
props.row.quotation.customerBranch.districtId,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
v-model="props.selected"
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
</q-td>
|
||||||
<q-td v-if="visibleColumns.includes('order')">
|
<q-td v-if="visibleColumns.includes('order')">
|
||||||
{{ props.rowIndex + 1 }}
|
{{ props.rowIndex + 1 }}
|
||||||
</q-td>
|
</q-td>
|
||||||
|
|
@ -138,21 +233,50 @@ function getEmployeeName(
|
||||||
</div>
|
</div>
|
||||||
</q-td>
|
</q-td>
|
||||||
<q-td v-if="visibleColumns.includes('employer')" class="text-left">
|
<q-td v-if="visibleColumns.includes('employer')" class="text-left">
|
||||||
|
<span
|
||||||
|
class="link"
|
||||||
|
@click="toCustomer(props.row.quotation.customerBranch)"
|
||||||
|
>
|
||||||
{{
|
{{
|
||||||
getCustomerName(props.row, {
|
getCustomerName(props.row, {
|
||||||
noCode: true,
|
noCode: true,
|
||||||
locale: $i18n.locale,
|
locale: $i18n.locale,
|
||||||
}) || '-'
|
}) || '-'
|
||||||
}}
|
}}
|
||||||
|
</span>
|
||||||
</q-td>
|
</q-td>
|
||||||
<q-td v-if="visibleColumns.includes('employee')" class="text-left">
|
<q-td v-if="visibleColumns.includes('employee')" class="text-left">
|
||||||
|
<span class="link" @click="toEmployee(props.row.employee)">
|
||||||
{{ getEmployeeName(props.row, { locale: $i18n.locale }) || '-' }}
|
{{ getEmployeeName(props.row, { locale: $i18n.locale }) || '-' }}
|
||||||
|
</span>
|
||||||
</q-td>
|
</q-td>
|
||||||
|
|
||||||
|
<q-td
|
||||||
|
v-if="visibleColumns.includes('employeePassport')"
|
||||||
|
class="text-left"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
props.row.employee.employeePassport.length !== 0
|
||||||
|
? props.row.employee.employeePassport[0].number
|
||||||
|
: '-'
|
||||||
|
}}
|
||||||
|
</q-td>
|
||||||
|
<q-td v-if="visibleColumns.includes('dataOffice')" class="text-left">
|
||||||
|
{{
|
||||||
|
$i18n.locale === 'eng'
|
||||||
|
? props.row.dataOffice.nameEN
|
||||||
|
: props.row.dataOffice.name
|
||||||
|
}}
|
||||||
|
</q-td>
|
||||||
|
<q-td v-if="visibleColumns.includes('createdAt')" class="text-left">
|
||||||
|
{{ dateFormatJS({ date: props.row.createdAt }) }}
|
||||||
|
</q-td>
|
||||||
|
|
||||||
<q-td v-if="visibleColumns.includes('quotationCode')">
|
<q-td v-if="visibleColumns.includes('quotationCode')">
|
||||||
{{ props.row.quotation.code || '-' }}
|
{{ props.row.quotation.code || '-' }}
|
||||||
</q-td>
|
</q-td>
|
||||||
<q-td v-if="visibleColumns.includes('responsiblePerson')">
|
<q-td v-if="visibleColumns.includes('responsiblePerson')">
|
||||||
<AvatarGroup
|
<!-- <AvatarGroup
|
||||||
:data="
|
:data="
|
||||||
responsiblePerson(props.row.quotation)?.map((v) => {
|
responsiblePerson(props.row.quotation)?.map((v) => {
|
||||||
return {
|
return {
|
||||||
|
|
@ -168,7 +292,26 @@ function getEmployeeName(
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
/>
|
/> -->
|
||||||
|
<AvatarGroup
|
||||||
|
:data="[
|
||||||
|
...responsiblePerson(props.row.quotation).user.map((v) => ({
|
||||||
|
name:
|
||||||
|
$i18n.locale === 'eng'
|
||||||
|
? `${v.firstNameEN} ${v.lastNameEN}`
|
||||||
|
: `${v.firstName} ${v.lastName}`,
|
||||||
|
imgUrl: !v.selectedImage
|
||||||
|
? v.gender === 'male'
|
||||||
|
? `/no-img-man.png`
|
||||||
|
: `/no-img-female.png`
|
||||||
|
: `${baseUrl}/user/${v.id}/profile-image/${v.selectedImage}`,
|
||||||
|
})),
|
||||||
|
...responsiblePerson(props.row.quotation).group.map((g) => ({
|
||||||
|
name: `${$t('general.group')} ${g.group}`,
|
||||||
|
imgUrl: '/img-group.png',
|
||||||
|
})),
|
||||||
|
]"
|
||||||
|
></AvatarGroup>
|
||||||
</q-td>
|
</q-td>
|
||||||
<q-td v-if="visibleColumns.includes('status')">
|
<q-td v-if="visibleColumns.includes('status')">
|
||||||
<BadgeComponent
|
<BadgeComponent
|
||||||
|
|
@ -215,6 +358,7 @@ function getEmployeeName(
|
||||||
</q-td>
|
</q-td>
|
||||||
<q-td class="text-right">
|
<q-td class="text-right">
|
||||||
<q-btn
|
<q-btn
|
||||||
|
v-if="!hideView"
|
||||||
:id="`btn-eye-${props.row.code}`"
|
:id="`btn-eye-${props.row.code}`"
|
||||||
icon="mdi-eye-outline"
|
icon="mdi-eye-outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|
@ -313,10 +457,14 @@ function getEmployeeName(
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
<AvatarGroup
|
<AvatarGroup
|
||||||
v-if="(responsiblePerson(props.row.quotation) ?? []).length > 0"
|
v-if="
|
||||||
:data="
|
(responsiblePerson(props.row.quotation).user ?? []).length >
|
||||||
responsiblePerson(props.row.quotation)?.map((v) => {
|
0 ||
|
||||||
return {
|
(responsiblePerson(props.row.quotation).group ?? []).length >
|
||||||
|
0
|
||||||
|
"
|
||||||
|
:data="[
|
||||||
|
...responsiblePerson(props.row.quotation).user.map((v) => ({
|
||||||
name:
|
name:
|
||||||
$i18n.locale === 'eng'
|
$i18n.locale === 'eng'
|
||||||
? `${v.firstNameEN} ${v.lastNameEN}`
|
? `${v.firstNameEN} ${v.lastNameEN}`
|
||||||
|
|
@ -326,9 +474,12 @@ function getEmployeeName(
|
||||||
? `/no-img-man.png`
|
? `/no-img-man.png`
|
||||||
: `/no-img-female.png`
|
: `/no-img-female.png`
|
||||||
: `${baseUrl}/user/${v.id}/profile-image/${v.selectedImage}`,
|
: `${baseUrl}/user/${v.id}/profile-image/${v.selectedImage}`,
|
||||||
};
|
})),
|
||||||
})
|
...responsiblePerson(props.row.quotation).group.map((g) => ({
|
||||||
"
|
name: `${$t('general.group')} ${g.group}`,
|
||||||
|
imgUrl: '/img-group.png',
|
||||||
|
})),
|
||||||
|
]"
|
||||||
/>
|
/>
|
||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -406,4 +557,15 @@ function getEmployeeName(
|
||||||
background: var(--red-8);
|
background: var(--red-8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
color: hsl(var(--info-bg));
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled-row {
|
||||||
|
opacity: 0.3;
|
||||||
|
filter: grayscale(1);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,24 @@ export const column = [
|
||||||
label: 'customer.employee',
|
label: 'customer.employee',
|
||||||
field: 'employee',
|
field: 'employee',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'employeePassport',
|
||||||
|
align: 'center',
|
||||||
|
label: 'customerEmployee.form.passportNo',
|
||||||
|
field: 'employeePassport',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'dataOffice',
|
||||||
|
align: 'center',
|
||||||
|
label: 'requestList.dataOffice',
|
||||||
|
field: 'dataOffice',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'createdAt',
|
||||||
|
align: 'center',
|
||||||
|
label: 'general.createdAt',
|
||||||
|
field: 'createdAt',
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'quotationCode',
|
name: 'quotationCode',
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import useFlowStore from 'src/stores/flow';
|
||||||
import { pageTabs, column, pageTabsReceive } from './constants';
|
import { pageTabs, column, pageTabsReceive } from './constants';
|
||||||
import { dialogWarningClose, isRoleInclude } from 'src/stores/utils';
|
import { dialogWarningClose, isRoleInclude } from 'src/stores/utils';
|
||||||
import { PaginationResult } from 'src/types';
|
import { PaginationResult } from 'src/types';
|
||||||
|
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
|
|
@ -48,6 +49,7 @@ const pageState = reactive({
|
||||||
isMessenger: isRoleInclude(['messenger']),
|
isMessenger: isRoleInclude(['messenger']),
|
||||||
receiveDialog: false,
|
receiveDialog: false,
|
||||||
isReceiveScan: false,
|
isReceiveScan: false,
|
||||||
|
searchDate: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const taskOrderList = ref<TaskOrder[]>([]);
|
const taskOrderList = ref<TaskOrder[]>([]);
|
||||||
|
|
@ -69,6 +71,8 @@ async function fetchTaskOrderList(opts?: { page?: number; pageSize?: number }) {
|
||||||
pageSize: opts?.pageSize || pageSize.value,
|
pageSize: opts?.pageSize || pageSize.value,
|
||||||
query: pageState.inputSearch === '' ? undefined : pageState.inputSearch,
|
query: pageState.inputSearch === '' ? undefined : pageState.inputSearch,
|
||||||
userTaskStatus: pageState.currentTab as UserTaskStatus,
|
userTaskStatus: pageState.currentTab as UserTaskStatus,
|
||||||
|
startDate: pageState.searchDate[0],
|
||||||
|
endDate: pageState.searchDate[1],
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res = await taskOrderStore.getTaskOrderList({
|
res = await taskOrderStore.getTaskOrderList({
|
||||||
|
|
@ -76,6 +80,8 @@ async function fetchTaskOrderList(opts?: { page?: number; pageSize?: number }) {
|
||||||
pageSize: opts?.pageSize || pageSize.value,
|
pageSize: opts?.pageSize || pageSize.value,
|
||||||
query: pageState.inputSearch === '' ? undefined : pageState.inputSearch,
|
query: pageState.inputSearch === '' ? undefined : pageState.inputSearch,
|
||||||
taskOrderStatus: pageState.currentTab as TaskOrderStatus | undefined,
|
taskOrderStatus: pageState.currentTab as TaskOrderStatus | undefined,
|
||||||
|
startDate: pageState.searchDate[0],
|
||||||
|
endDate: pageState.searchDate[1],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (res) {
|
if (res) {
|
||||||
|
|
@ -157,6 +163,7 @@ watch(
|
||||||
() => pageState.inputSearch,
|
() => pageState.inputSearch,
|
||||||
() => pageSize.value,
|
() => pageSize.value,
|
||||||
() => pageState.statusFilter,
|
() => pageState.statusFilter,
|
||||||
|
() => pageState.searchDate,
|
||||||
],
|
],
|
||||||
() => {
|
() => {
|
||||||
fetchTaskOrderList();
|
fetchTaskOrderList();
|
||||||
|
|
@ -299,6 +306,10 @@ watch(
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<q-icon name="mdi-magnify" />
|
<q-icon name="mdi-magnify" />
|
||||||
</template>
|
</template>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-separator vertical inset class="q-mr-xs" />
|
||||||
|
<AdvanceSearch v-model="pageState.searchDate" />
|
||||||
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<div class="row col-md-5 justify-end" style="white-space: nowrap">
|
<div class="row col-md-5 justify-end" style="white-space: nowrap">
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,12 @@ const emit = defineEmits<{
|
||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-caption app-text-muted">
|
<div class="text-caption app-text-muted">
|
||||||
{{ props.row.code || '-' }}
|
{{
|
||||||
|
(props.row.taskOrderStatus === TaskOrderStatus.Complete &&
|
||||||
|
props.row.codeProductReceived
|
||||||
|
? props.row.codeProductReceived
|
||||||
|
: props.row.code) || '-'
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
</q-td>
|
</q-td>
|
||||||
<q-td v-if="visibleColumns.includes('issueBranch')">
|
<q-td v-if="visibleColumns.includes('issueBranch')">
|
||||||
|
|
|
||||||
|
|
@ -227,6 +227,12 @@ export const productColumn = [
|
||||||
label: 'taskOrder.productList',
|
label: 'taskOrder.productList',
|
||||||
field: 'productList',
|
field: 'productList',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'status',
|
||||||
|
align: 'center',
|
||||||
|
label: 'general.status',
|
||||||
|
field: 'status',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'amountOfEmployee',
|
name: 'amountOfEmployee',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
|
|
|
||||||
|
|
@ -294,7 +294,7 @@ function closeAble() {
|
||||||
:branch="branch"
|
:branch="branch"
|
||||||
:institution="data.institution"
|
:institution="data.institution"
|
||||||
:details="{
|
:details="{
|
||||||
code: data.code,
|
code: data.codeProductReceived ?? data.code,
|
||||||
name: data.taskName,
|
name: data.taskName,
|
||||||
contactName: data.contactName,
|
contactName: data.contactName,
|
||||||
contactTel: data.contactTel,
|
contactTel: data.contactTel,
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ import { baseUrl, formatNumberDecimal, commaInput } from 'src/stores/utils';
|
||||||
import { precisionRound } from 'src/utils/arithmetic';
|
import { precisionRound } from 'src/utils/arithmetic';
|
||||||
import { useConfigStore } from 'stores/config';
|
import { useConfigStore } from 'stores/config';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
import BadgeComponent from 'src/components/BadgeComponent.vue';
|
||||||
|
import { TaskStatus } from 'src/stores/task-order/types';
|
||||||
|
|
||||||
const currentBtnOpen = ref<boolean[]>([]);
|
const currentBtnOpen = ref<boolean[]>([]);
|
||||||
const configStore = useConfigStore();
|
const configStore = useConfigStore();
|
||||||
|
|
@ -30,7 +32,10 @@ const props = defineProps<{
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
agentPrice?: boolean;
|
agentPrice?: boolean;
|
||||||
taskList: {
|
taskList: {
|
||||||
product: RequestWork['productService']['product'];
|
product: RequestWork['productService']['product'] & {
|
||||||
|
taskStatus?: TaskStatus;
|
||||||
|
totalNotStatusComplete?: number;
|
||||||
|
};
|
||||||
list: RequestWork[];
|
list: RequestWork[];
|
||||||
}[];
|
}[];
|
||||||
creditNote?: boolean;
|
creditNote?: boolean;
|
||||||
|
|
@ -111,6 +116,26 @@ function calcPrice(
|
||||||
|
|
||||||
return precisionRound(priceNoVat * amount + rawVatTotal);
|
return precisionRound(priceNoVat * amount + rawVatTotal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function taskOrderStatus(value: TaskStatus) {
|
||||||
|
if ([TaskStatus.Pending].includes(value)) {
|
||||||
|
return '--blue-6-hsl';
|
||||||
|
}
|
||||||
|
if ([TaskStatus.InProgress, TaskStatus.Validate].includes(value)) {
|
||||||
|
return '--orange-5-hsl';
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
[
|
||||||
|
TaskStatus.Canceled,
|
||||||
|
TaskStatus.Restart,
|
||||||
|
TaskStatus.Redo,
|
||||||
|
TaskStatus.Failed,
|
||||||
|
].includes(value)
|
||||||
|
) {
|
||||||
|
return '--red-5-hsl';
|
||||||
|
}
|
||||||
|
return '--green-8-hsl';
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<q-expansion-item
|
<q-expansion-item
|
||||||
|
|
@ -144,7 +169,8 @@ function calcPrice(
|
||||||
(v) =>
|
(v) =>
|
||||||
v.name !== 'discount' &&
|
v.name !== 'discount' &&
|
||||||
v.name !== 'priceBeforeVat' &&
|
v.name !== 'priceBeforeVat' &&
|
||||||
v.name !== 'vat',
|
v.name !== 'vat' &&
|
||||||
|
v.name !== 'status',
|
||||||
)
|
)
|
||||||
: productColumn
|
: productColumn
|
||||||
"
|
"
|
||||||
|
|
@ -173,7 +199,10 @@ function calcPrice(
|
||||||
<template
|
<template
|
||||||
v-slot:body="props: {
|
v-slot:body="props: {
|
||||||
row: {
|
row: {
|
||||||
product: RequestWork['productService']['product'];
|
product: RequestWork['productService']['product'] & {
|
||||||
|
taskStatus?: TaskStatus;
|
||||||
|
totalNotStatusComplete?: number;
|
||||||
|
};
|
||||||
list: RequestWork[];
|
list: RequestWork[];
|
||||||
};
|
};
|
||||||
} & Omit<Parameters<QTableSlots['body']>[0], 'row'>"
|
} & Omit<Parameters<QTableSlots['body']>[0], 'row'>"
|
||||||
|
|
@ -203,6 +232,14 @@ function calcPrice(
|
||||||
</q-avatar>
|
</q-avatar>
|
||||||
{{ props.row.product.name }}
|
{{ props.row.product.name }}
|
||||||
</q-td>
|
</q-td>
|
||||||
|
<q-td class="text-left" v-if="!creditNote">
|
||||||
|
<BadgeComponent
|
||||||
|
hide-icon
|
||||||
|
:hsla-color="taskOrderStatus(props.row.product.taskStatus)"
|
||||||
|
:title="`${$t(`taskOrder.status.${props.row.product.taskStatus}`)} ${!!props.row.product.totalNotStatusComplete ? $t('general.totalPeople', { meg: props.row.product.totalNotStatusComplete }) : ''}`"
|
||||||
|
/>
|
||||||
|
</q-td>
|
||||||
|
|
||||||
<q-td>
|
<q-td>
|
||||||
{{ props.row.list.length }}
|
{{ props.row.list.length }}
|
||||||
</q-td>
|
</q-td>
|
||||||
|
|
|
||||||
|
|
@ -280,7 +280,10 @@ let taskListGroup = computed(() => {
|
||||||
|
|
||||||
const cacheData = currentFormData.value.taskList.reduce<
|
const cacheData = currentFormData.value.taskList.reduce<
|
||||||
{
|
{
|
||||||
product: RequestWork['productService']['product'];
|
product: RequestWork['productService']['product'] & {
|
||||||
|
taskStatus?: TaskStatus;
|
||||||
|
totalNotStatusComplete?: number;
|
||||||
|
};
|
||||||
list: (RequestWork & {
|
list: (RequestWork & {
|
||||||
_template?: {
|
_template?: {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -289,15 +292,15 @@ let taskListGroup = computed(() => {
|
||||||
step: number;
|
step: number;
|
||||||
responsibleInstitution: (string | { group: string })[];
|
responsibleInstitution: (string | { group: string })[];
|
||||||
} | null;
|
} | null;
|
||||||
|
taskStatus?: TaskStatus;
|
||||||
|
failedComment?: string;
|
||||||
|
failedType?: string;
|
||||||
})[];
|
})[];
|
||||||
}[]
|
}[]
|
||||||
>((acc, curr) => {
|
>((acc, curr) => {
|
||||||
if (
|
const isNotComplete =
|
||||||
fullTaskOrder.value?.taskOrderStatus === TaskOrderStatus.Complete &&
|
fullTaskOrder.value?.taskOrderStatus === TaskOrderStatus.Complete &&
|
||||||
curr.taskStatus !== TaskStatus.Complete
|
curr.taskStatus !== TaskStatus.Complete;
|
||||||
) {
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
const task = curr.requestWorkStep;
|
const task = curr.requestWorkStep;
|
||||||
const step = curr.step;
|
const step = curr.step;
|
||||||
|
|
@ -308,9 +311,18 @@ let taskListGroup = computed(() => {
|
||||||
let exist = acc.find(
|
let exist = acc.find(
|
||||||
(item) => task.requestWork.productService.productId == item.product.id,
|
(item) => task.requestWork.productService.productId == item.product.id,
|
||||||
);
|
);
|
||||||
const record = Object.assign(task.requestWork, {
|
|
||||||
|
const record = Object.assign(
|
||||||
|
{
|
||||||
|
...task.requestWork,
|
||||||
|
taskStatus: curr.taskStatus,
|
||||||
|
failedComment: curr.failedComment || '',
|
||||||
|
failedType: curr.failedType || '',
|
||||||
|
},
|
||||||
|
{
|
||||||
_template: getTemplateData(task.requestWork, step),
|
_template: getTemplateData(task.requestWork, step),
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const template = getTemplateData(task.requestWork, step);
|
const template = getTemplateData(task.requestWork, step);
|
||||||
|
|
||||||
|
|
@ -323,10 +335,18 @@ let taskListGroup = computed(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exist) {
|
if (exist) {
|
||||||
exist.list.push(task.requestWork);
|
exist.list.push(record);
|
||||||
|
if (isNotComplete) {
|
||||||
|
exist.product.totalNotStatusComplete =
|
||||||
|
(exist.product.totalNotStatusComplete || undefined) + 1;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
acc.push({
|
acc.push({
|
||||||
product: task.requestWork.productService.product,
|
product: {
|
||||||
|
...task.requestWork.productService.product,
|
||||||
|
taskStatus: curr.taskStatus || TaskStatus.Pending,
|
||||||
|
totalNotStatusComplete: isNotComplete ? 1 : undefined,
|
||||||
|
},
|
||||||
list: [record],
|
list: [record],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -897,9 +917,14 @@ watch(
|
||||||
v-model:registered-branch-id="currentFormData.registeredBranchId"
|
v-model:registered-branch-id="currentFormData.registeredBranchId"
|
||||||
v-model:institution-id="currentFormData.institutionId"
|
v-model:institution-id="currentFormData.institutionId"
|
||||||
v-model:task-name="currentFormData.taskName"
|
v-model:task-name="currentFormData.taskName"
|
||||||
v-model:code="currentFormData.code"
|
|
||||||
v-model:contact-name="currentFormData.contactName"
|
v-model:contact-name="currentFormData.contactName"
|
||||||
v-model:contact-tel="currentFormData.contactTel"
|
v-model:contact-tel="currentFormData.contactTel"
|
||||||
|
:code="
|
||||||
|
view === TaskOrderStatus.Complete &&
|
||||||
|
currentFormData.codeProductReceived
|
||||||
|
? currentFormData.codeProductReceived
|
||||||
|
: currentFormData.code
|
||||||
|
"
|
||||||
:task-list-group="
|
:task-list-group="
|
||||||
taskListGroup.length === 0 && state.mode === 'create'
|
taskListGroup.length === 0 && state.mode === 'create'
|
||||||
"
|
"
|
||||||
|
|
@ -985,6 +1010,7 @@ watch(
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<!-- TODO: blind remark, urgent -->
|
<!-- TODO: blind remark, urgent -->
|
||||||
|
{{ console.log(taskListGroup) }}
|
||||||
<RemarkExpansion
|
<RemarkExpansion
|
||||||
v-if="
|
v-if="
|
||||||
view === TaskOrderStatus.Pending ||
|
view === TaskOrderStatus.Pending ||
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// NOTE: Library
|
// NOTE: Library
|
||||||
import { computed, onMounted, reactive, ref, watch } from 'vue';
|
import { computed, onMounted, reactive, ref, watch } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { QSelect, useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
|
|
||||||
// NOTE: Components
|
// NOTE: Components
|
||||||
import StatCardComponent from 'src/components/StatCardComponent.vue';
|
import StatCardComponent from 'src/components/StatCardComponent.vue';
|
||||||
|
|
@ -18,13 +18,13 @@ import { columns, hslaColors } from './constants';
|
||||||
import useFlowStore from 'src/stores/flow';
|
import useFlowStore from 'src/stores/flow';
|
||||||
import { useInvoice } from 'src/stores/payment';
|
import { useInvoice } from 'src/stores/payment';
|
||||||
import { Invoice, PaymentDataStatus } from 'src/stores/payment/types';
|
import { Invoice, PaymentDataStatus } from 'src/stores/payment/types';
|
||||||
|
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const navigatorStore = useNavigator();
|
const navigatorStore = useNavigator();
|
||||||
const flowStore = useFlowStore();
|
const flowStore = useFlowStore();
|
||||||
const invoiceStore = useInvoice();
|
const invoiceStore = useInvoice();
|
||||||
const { data, stats, page, pageMax, pageSize } = storeToRefs(invoiceStore);
|
const { data, stats, page, pageMax, pageSize } = storeToRefs(invoiceStore);
|
||||||
const refFilter = ref<InstanceType<typeof QSelect>>();
|
|
||||||
|
|
||||||
// NOTE: Variable
|
// NOTE: Variable
|
||||||
const pageState = reactive({
|
const pageState = reactive({
|
||||||
|
|
@ -34,6 +34,7 @@ const pageState = reactive({
|
||||||
fieldSelected: [...columns.map((v) => v.name)],
|
fieldSelected: [...columns.map((v) => v.name)],
|
||||||
gridView: false,
|
gridView: false,
|
||||||
total: 0,
|
total: 0,
|
||||||
|
searchDate: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const fieldSelectedOption = computed(() => {
|
const fieldSelectedOption = computed(() => {
|
||||||
|
|
@ -56,6 +57,8 @@ async function fetchList(opts?: { rotateFlowId?: boolean }) {
|
||||||
: undefined,
|
: undefined,
|
||||||
quotationOnly: true,
|
quotationOnly: true,
|
||||||
debitNoteOnly: false,
|
debitNoteOnly: false,
|
||||||
|
startDate: pageState.searchDate[0],
|
||||||
|
endDate: pageState.searchDate[1],
|
||||||
});
|
});
|
||||||
if (ret) {
|
if (ret) {
|
||||||
data.value = $q.screen.xs ? [...data.value, ...ret.result] : ret.result;
|
data.value = $q.screen.xs ? [...data.value, ...ret.result] : ret.result;
|
||||||
|
|
@ -89,8 +92,6 @@ function triggerView(opts: { quotationId: string }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function viewDocExample(quotationId: string, codeInvoice: string) {
|
function viewDocExample(quotationId: string, codeInvoice: string) {
|
||||||
console.log(codeInvoice);
|
|
||||||
|
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'quotation-preview',
|
'quotation-preview',
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
|
|
@ -124,6 +125,7 @@ watch(
|
||||||
() => pageState.inputSearch,
|
() => pageState.inputSearch,
|
||||||
() => pageState.statusFilter,
|
() => pageState.statusFilter,
|
||||||
() => pageSize.value,
|
() => pageSize.value,
|
||||||
|
() => pageState.searchDate,
|
||||||
],
|
],
|
||||||
() => {
|
() => {
|
||||||
page.value = 1;
|
page.value = 1;
|
||||||
|
|
@ -207,26 +209,50 @@ watch(
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<q-icon name="mdi-magnify" />
|
<q-icon name="mdi-magnify" />
|
||||||
</template>
|
</template>
|
||||||
<template v-if="$q.screen.lt.md" v-slot:append>
|
<template v-slot:append>
|
||||||
<span class="row">
|
<q-separator vertical inset class="q-mr-xs" />
|
||||||
<q-separator vertical />
|
<AdvanceSearch
|
||||||
<q-btn
|
v-model="pageState.searchDate"
|
||||||
icon="mdi-filter-variant"
|
:active="$q.screen.lt.md && pageState.statusFilter !== 'None'"
|
||||||
unelevated
|
>
|
||||||
class="q-ml-sm"
|
<div
|
||||||
padding="4px"
|
v-if="$q.screen.lt.md"
|
||||||
size="sm"
|
class="q-mt-sm text-weight-medium"
|
||||||
rounded
|
>
|
||||||
@click="refFilter?.showPopup"
|
{{ $t('general.status') }}
|
||||||
|
</div>
|
||||||
|
<q-select
|
||||||
|
v-if="$q.screen.lt.md"
|
||||||
|
v-model="pageState.statusFilter"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
option-value="value"
|
||||||
|
option-label="label"
|
||||||
|
map-options
|
||||||
|
emit-value
|
||||||
|
:for="'field-select-status'"
|
||||||
|
:options="[
|
||||||
|
{
|
||||||
|
label: $t('general.all'),
|
||||||
|
value: 'None',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('invoice.status.PaymentWait'),
|
||||||
|
value: PaymentDataStatus.Wait,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('invoice.status.PaymentSuccess'),
|
||||||
|
value: PaymentDataStatus.Success,
|
||||||
|
},
|
||||||
|
]"
|
||||||
/>
|
/>
|
||||||
</span>
|
</AdvanceSearch>
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<div class="row col-md-5" style="white-space: nowrap">
|
<div class="row col-md-5" style="white-space: nowrap">
|
||||||
<q-select
|
<q-select
|
||||||
v-show="$q.screen.gt.sm"
|
v-if="$q.screen.gt.sm"
|
||||||
ref="refFilter"
|
|
||||||
v-model="pageState.statusFilter"
|
v-model="pageState.statusFilter"
|
||||||
outlined
|
outlined
|
||||||
dense
|
dense
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import { pageTabs, columns, hslaColors } from './constants';
|
||||||
import { CreditNoteStatus, useCreditNote } from 'src/stores/credit-note';
|
import { CreditNoteStatus, useCreditNote } from 'src/stores/credit-note';
|
||||||
import TableCreditNote from './TableCreditNote.vue';
|
import TableCreditNote from './TableCreditNote.vue';
|
||||||
import { dialogWarningClose } from 'src/stores/utils';
|
import { dialogWarningClose } from 'src/stores/utils';
|
||||||
|
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
@ -46,6 +47,7 @@ const pageState = reactive({
|
||||||
total: 0,
|
total: 0,
|
||||||
|
|
||||||
creditDialog: false,
|
creditDialog: false,
|
||||||
|
searchDate: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const fieldSelectedOption = computed(() => {
|
const fieldSelectedOption = computed(() => {
|
||||||
|
|
@ -64,6 +66,8 @@ async function getList(opts?: { page?: number; pageSize?: number }) {
|
||||||
pageSize: opts?.pageSize || pageSize.value,
|
pageSize: opts?.pageSize || pageSize.value,
|
||||||
query: pageState.inputSearch === '' ? undefined : pageState.inputSearch,
|
query: pageState.inputSearch === '' ? undefined : pageState.inputSearch,
|
||||||
creditNoteStatus: pageState.currentTab as CreditNoteStatus | undefined,
|
creditNoteStatus: pageState.currentTab as CreditNoteStatus | undefined,
|
||||||
|
startDate: pageState.searchDate[0],
|
||||||
|
endDate: pageState.searchDate[1],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
|
|
@ -133,6 +137,7 @@ watch(
|
||||||
() => pageState.inputSearch,
|
() => pageState.inputSearch,
|
||||||
() => pageSize.value,
|
() => pageSize.value,
|
||||||
() => pageState.statusFilter,
|
() => pageState.statusFilter,
|
||||||
|
() => pageState.searchDate,
|
||||||
],
|
],
|
||||||
() => {
|
() => {
|
||||||
getList();
|
getList();
|
||||||
|
|
@ -228,6 +233,10 @@ watch(
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<q-icon name="mdi-magnify" />
|
<q-icon name="mdi-magnify" />
|
||||||
</template>
|
</template>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-separator vertical inset class="q-mr-xs" />
|
||||||
|
<AdvanceSearch v-model="pageState.searchDate" />
|
||||||
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<div class="row col-md-5 justify-end" style="white-space: nowrap">
|
<div class="row col-md-5 justify-end" style="white-space: nowrap">
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { storeToRefs } from 'pinia';
|
||||||
import { QTableSlots } from 'quasar';
|
import { QTableSlots } from 'quasar';
|
||||||
import { CreditNote, useCreditNote } from 'src/stores/credit-note';
|
import { CreditNote, useCreditNote } from 'src/stores/credit-note';
|
||||||
|
|
||||||
import { columns } from './constants.ts';
|
import { columns } from './constants';
|
||||||
import KebabAction from 'src/components/shared/KebabAction.vue';
|
import KebabAction from 'src/components/shared/KebabAction.vue';
|
||||||
|
|
||||||
const creditNote = useCreditNote();
|
const creditNote = useCreditNote();
|
||||||
|
|
|
||||||
|
|
@ -248,6 +248,7 @@ function calcPricePerUnit(product: RequestWork['productService']['product']) {
|
||||||
function calcPrice(
|
function calcPrice(
|
||||||
product: RequestWork['productService']['product'],
|
product: RequestWork['productService']['product'],
|
||||||
amount: number,
|
amount: number,
|
||||||
|
vat: number = 0,
|
||||||
) {
|
) {
|
||||||
const pricePerUnit = agentPrice.value ? product.agentPrice : product.price;
|
const pricePerUnit = agentPrice.value ? product.agentPrice : product.price;
|
||||||
|
|
||||||
|
|
@ -256,7 +257,8 @@ function calcPrice(
|
||||||
: pricePerUnit;
|
: pricePerUnit;
|
||||||
const priceDiscountNoVat = priceNoVat * amount - 0;
|
const priceDiscountNoVat = priceNoVat * amount - 0;
|
||||||
|
|
||||||
const rawVatTotal = priceDiscountNoVat * (config.value?.vat || 0.07);
|
const rawVatTotal =
|
||||||
|
vat === 0 ? 0 : priceDiscountNoVat * (config.value?.vat || 0.07);
|
||||||
|
|
||||||
return precisionRound(priceNoVat * amount + rawVatTotal);
|
return precisionRound(priceNoVat * amount + rawVatTotal);
|
||||||
}
|
}
|
||||||
|
|
@ -346,7 +348,7 @@ function closeAble() {
|
||||||
<td style="text-align: center">
|
<td style="text-align: center">
|
||||||
{{
|
{{
|
||||||
formatNumberDecimal(
|
formatNumberDecimal(
|
||||||
calcPrice(v.product.product, v.list.length),
|
calcPrice(v.product.product, v.list.length, v.product.vat),
|
||||||
2,
|
2,
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
|
@ -431,7 +433,7 @@ function closeAble() {
|
||||||
class="column set-width bg-color full-height"
|
class="column set-width bg-color full-height"
|
||||||
style="padding: 12px"
|
style="padding: 12px"
|
||||||
>
|
>
|
||||||
({{ ThaiBahtText(summaryPrice.finalPrice) }})
|
({{ ThaiBahtText(precisionRound(summaryPrice.finalPrice)) }})
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="row text-right border-5 items-center"
|
class="row text-right border-5 items-center"
|
||||||
|
|
@ -494,7 +496,7 @@ function closeAble() {
|
||||||
details?.worker.map(
|
details?.worker.map(
|
||||||
(v, i) =>
|
(v, i) =>
|
||||||
`${i + 1}. ` +
|
`${i + 1}. ` +
|
||||||
`${v.namePrefix}. ${v.firstNameEN} ${v.lastNameEN}`.toUpperCase(),
|
`${v.namePrefix}. ${v.firstNameEN ? `${v.firstNameEN} ${v.lastNameEN}` : `${v.firstName} ${v.lastName}`} `.toUpperCase(),
|
||||||
) || [],
|
) || [],
|
||||||
},
|
},
|
||||||
}) || '-'
|
}) || '-'
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import { pageTabs, columns, hslaColors } from './constants';
|
||||||
import { DebitNoteStatus, useDebitNote } from 'src/stores/debit-note';
|
import { DebitNoteStatus, useDebitNote } from 'src/stores/debit-note';
|
||||||
import { dialogWarningClose } from 'src/stores/utils';
|
import { dialogWarningClose } from 'src/stores/utils';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
|
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
@ -46,6 +47,7 @@ const pageState = reactive({
|
||||||
total: 0,
|
total: 0,
|
||||||
|
|
||||||
debitDialog: false,
|
debitDialog: false,
|
||||||
|
searchDate: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const fieldSelectedOption = computed(() => {
|
const fieldSelectedOption = computed(() => {
|
||||||
|
|
@ -68,6 +70,8 @@ async function getList(opts?: { page?: number; pageSize?: number }) {
|
||||||
? undefined
|
? undefined
|
||||||
: pageState.currentTab) as DebitNoteStatus,
|
: pageState.currentTab) as DebitNoteStatus,
|
||||||
includeRegisteredBranch: true,
|
includeRegisteredBranch: true,
|
||||||
|
startDate: pageState.searchDate[0],
|
||||||
|
endDate: pageState.searchDate[1],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
|
|
@ -149,6 +153,7 @@ watch(
|
||||||
() => pageState.inputSearch,
|
() => pageState.inputSearch,
|
||||||
() => pageSize.value,
|
() => pageSize.value,
|
||||||
() => pageState.statusFilter,
|
() => pageState.statusFilter,
|
||||||
|
() => pageState.searchDate,
|
||||||
],
|
],
|
||||||
() => getList(),
|
() => getList(),
|
||||||
);
|
);
|
||||||
|
|
@ -256,6 +261,10 @@ watch(
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<q-icon name="mdi-magnify" />
|
<q-icon name="mdi-magnify" />
|
||||||
</template>
|
</template>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-separator vertical inset class="q-mr-xs" />
|
||||||
|
<AdvanceSearch v-model="pageState.searchDate" />
|
||||||
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<div class="row col-md-5 justify-end" style="white-space: nowrap">
|
<div class="row col-md-5 justify-end" style="white-space: nowrap">
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { storeToRefs } from 'pinia';
|
||||||
import { QTableSlots } from 'quasar';
|
import { QTableSlots } from 'quasar';
|
||||||
import { DebitNote, useDebitNote } from 'src/stores/debit-note';
|
import { DebitNote, useDebitNote } from 'src/stores/debit-note';
|
||||||
|
|
||||||
import { columns } from './constants.ts';
|
import { columns } from './constants';
|
||||||
import KebabAction from 'src/components/shared/KebabAction.vue';
|
import KebabAction from 'src/components/shared/KebabAction.vue';
|
||||||
|
|
||||||
const debitNote = useDebitNote();
|
const debitNote = useDebitNote();
|
||||||
|
|
|
||||||
|
|
@ -501,7 +501,7 @@ function print() {
|
||||||
details?.worker.map(
|
details?.worker.map(
|
||||||
(v, i) =>
|
(v, i) =>
|
||||||
`${i + 1}. ` +
|
`${i + 1}. ` +
|
||||||
`${v.namePrefix}. ${v.firstNameEN} ${v.lastNameEN}`.toUpperCase(),
|
`${v.namePrefix}. ${v.firstNameEN ? `${v.firstNameEN} ${v.lastNameEN}` : `${v.firstName} ${v.lastName}`} `.toUpperCase(),
|
||||||
) || [],
|
) || [],
|
||||||
},
|
},
|
||||||
}) || '-'
|
}) || '-'
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,8 @@ import { columns, hslaColors } from './constants';
|
||||||
import useFlowStore from 'src/stores/flow';
|
import useFlowStore from 'src/stores/flow';
|
||||||
import { usePayment, useReceipt } from 'src/stores/payment';
|
import { usePayment, useReceipt } from 'src/stores/payment';
|
||||||
import { PaymentDataStatus } from 'src/stores/payment/types';
|
import { PaymentDataStatus } from 'src/stores/payment/types';
|
||||||
import { QSelect, useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
|
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const navigatorStore = useNavigator();
|
const navigatorStore = useNavigator();
|
||||||
|
|
@ -26,7 +27,6 @@ const receiptStore = useReceipt();
|
||||||
const { data, page, pageMax, pageSize } = storeToRefs(receiptStore);
|
const { data, page, pageMax, pageSize } = storeToRefs(receiptStore);
|
||||||
|
|
||||||
// NOTE: Variable
|
// NOTE: Variable
|
||||||
const refFilter = ref<InstanceType<typeof QSelect>>();
|
|
||||||
|
|
||||||
const pageState = reactive({
|
const pageState = reactive({
|
||||||
hideStat: false,
|
hideStat: false,
|
||||||
|
|
@ -35,6 +35,7 @@ const pageState = reactive({
|
||||||
fieldSelected: [...columns.map((v) => v.name)],
|
fieldSelected: [...columns.map((v) => v.name)],
|
||||||
gridView: false,
|
gridView: false,
|
||||||
total: 0,
|
total: 0,
|
||||||
|
searchDate: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const fieldSelectedOption = computed(() => {
|
const fieldSelectedOption = computed(() => {
|
||||||
|
|
@ -49,6 +50,8 @@ async function fetchList(opts?: { rotateFlowId?: boolean }) {
|
||||||
page: page.value,
|
page: page.value,
|
||||||
pageSize: pageSize.value,
|
pageSize: pageSize.value,
|
||||||
query: pageState.inputSearch,
|
query: pageState.inputSearch,
|
||||||
|
startDate: pageState.searchDate[0],
|
||||||
|
endDate: pageState.searchDate[1],
|
||||||
});
|
});
|
||||||
if (ret) {
|
if (ret) {
|
||||||
data.value = $q.screen.xs ? [...data.value, ...ret.result] : ret.result;
|
data.value = $q.screen.xs ? [...data.value, ...ret.result] : ret.result;
|
||||||
|
|
@ -95,6 +98,7 @@ watch(
|
||||||
() => pageState.inputSearch,
|
() => pageState.inputSearch,
|
||||||
() => pageState.statusFilter,
|
() => pageState.statusFilter,
|
||||||
() => pageSize.value,
|
() => pageSize.value,
|
||||||
|
() => pageState.searchDate,
|
||||||
],
|
],
|
||||||
() => {
|
() => {
|
||||||
page.value = 1;
|
page.value = 1;
|
||||||
|
|
@ -172,25 +176,43 @@ watch(
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<q-icon name="mdi-magnify" />
|
<q-icon name="mdi-magnify" />
|
||||||
</template>
|
</template>
|
||||||
<template v-if="$q.screen.lt.md" v-slot:append>
|
<template v-slot:append>
|
||||||
<span class="row">
|
<q-separator vertical inset class="q-mr-xs" />
|
||||||
<q-separator vertical />
|
<AdvanceSearch
|
||||||
<q-btn
|
v-model="pageState.searchDate"
|
||||||
icon="mdi-filter-variant"
|
:active="$q.screen.lt.md && pageState.statusFilter !== 'None'"
|
||||||
unelevated
|
>
|
||||||
class="q-ml-sm"
|
<div
|
||||||
padding="4px"
|
v-if="$q.screen.lt.md"
|
||||||
size="sm"
|
class="q-mt-sm text-weight-medium"
|
||||||
rounded
|
>
|
||||||
@click="refFilter?.showPopup"
|
{{ $t('general.status') }}
|
||||||
|
</div>
|
||||||
|
<q-select
|
||||||
|
v-if="$q.screen.lt.md"
|
||||||
|
ref="refFilter"
|
||||||
|
v-model="pageState.statusFilter"
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
option-value="value"
|
||||||
|
option-label="label"
|
||||||
|
map-options
|
||||||
|
emit-value
|
||||||
|
:for="'field-select-status'"
|
||||||
|
:options="[
|
||||||
|
{
|
||||||
|
label: $t('general.all'),
|
||||||
|
value: 'None',
|
||||||
|
},
|
||||||
|
]"
|
||||||
/>
|
/>
|
||||||
</span>
|
</AdvanceSearch>
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<div class="row col-md-5" style="white-space: nowrap">
|
<div class="row col-md-5" style="white-space: nowrap">
|
||||||
<q-select
|
<q-select
|
||||||
v-show="$q.screen.gt.sm"
|
v-if="$q.screen.gt.sm"
|
||||||
ref="refFilter"
|
ref="refFilter"
|
||||||
v-model="pageState.statusFilter"
|
v-model="pageState.statusFilter"
|
||||||
outlined
|
outlined
|
||||||
|
|
|
||||||
9
src/quasar.d.ts
vendored
9
src/quasar.d.ts
vendored
|
|
@ -1,9 +0,0 @@
|
||||||
/* eslint-disable */
|
|
||||||
|
|
||||||
// Forces TS to apply `@quasar/app-vite` augmentations of `quasar` package
|
|
||||||
// Removing this would break `quasar/wrappers` imports as those typings are declared
|
|
||||||
// into `@quasar/app-vite`
|
|
||||||
// As a side effect, since `@quasar/app-vite` reference `quasar` to augment it,
|
|
||||||
// this declaration also apply `quasar` own
|
|
||||||
// augmentations (eg. adds `$q` into Vue component context)
|
|
||||||
/// <reference types="@quasar/app-vite" />
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { route } from 'quasar/wrappers';
|
import { defineRouter } from '#q-app/wrappers';
|
||||||
import {
|
import {
|
||||||
createMemoryHistory,
|
createMemoryHistory,
|
||||||
createRouter,
|
createRouter,
|
||||||
|
|
@ -17,7 +17,7 @@ import routes from './routes';
|
||||||
* with the Router instance.
|
* with the Router instance.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default route(function (/* { store, ssrContext } */) {
|
export default defineRouter(function (/* { store, ssrContext } */) {
|
||||||
const createHistory = process.env.SERVER
|
const createHistory = process.env.SERVER
|
||||||
? createMemoryHistory
|
? createMemoryHistory
|
||||||
: process.env.VUE_ROUTER_MODE === 'history'
|
: process.env.VUE_ROUTER_MODE === 'history'
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,16 @@ const routes: RouteRecordRaw[] = [
|
||||||
name: 'ManualView',
|
name: 'ManualView',
|
||||||
component: () => import('pages/00_manual/ViewPage.vue'),
|
component: () => import('pages/00_manual/ViewPage.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/troubleshooting',
|
||||||
|
name: 'Troubleshooting',
|
||||||
|
component: () => import('pages/00_manual/MainPage.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/troubleshooting/:category/:page',
|
||||||
|
name: 'TroubleshootingView',
|
||||||
|
component: () => import('pages/00_manual/ViewPage.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
10
src/shims-vue.d.ts
vendored
10
src/shims-vue.d.ts
vendored
|
|
@ -1,10 +0,0 @@
|
||||||
/* eslint-disable */
|
|
||||||
|
|
||||||
/// <reference types="vite/client" />
|
|
||||||
|
|
||||||
// Mocks all files ending in `.vue` showing them as plain Vue instances
|
|
||||||
declare module '*.vue' {
|
|
||||||
import type { DefineComponent } from 'vue';
|
|
||||||
const component: DefineComponent<{}, {}, any>;
|
|
||||||
export default component;
|
|
||||||
}
|
|
||||||
|
|
@ -102,12 +102,25 @@ const useAddressStore = defineStore('api-address', () => {
|
||||||
return subDistrict.value[districtId];
|
return subDistrict.value[districtId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function listSameOfficeArea(districtId: string) {
|
||||||
|
const res = await api.post<string[]>(
|
||||||
|
`/employment-office/list-same-office-area`,
|
||||||
|
{ districtId: districtId },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res) return false;
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fetchOffice,
|
fetchOffice,
|
||||||
fetchOfficeById,
|
fetchOfficeById,
|
||||||
fetchProvince,
|
fetchProvince,
|
||||||
fetchDistrictByProvinceId,
|
fetchDistrictByProvinceId,
|
||||||
fetchSubDistrictByProvinceId,
|
fetchSubDistrictByProvinceId,
|
||||||
|
|
||||||
|
listSameOfficeArea,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,8 @@ const useBranchStore = defineStore('api-branch', () => {
|
||||||
withHead?: boolean;
|
withHead?: boolean;
|
||||||
activeOnly?: boolean;
|
activeOnly?: boolean;
|
||||||
headOfficeId?: string;
|
headOfficeId?: string;
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string;
|
||||||
},
|
},
|
||||||
Data extends Pagination<Branch[]>,
|
Data extends Pagination<Branch[]>,
|
||||||
>(opts?: Options): Promise<Data | false> {
|
>(opts?: Options): Promise<Data | false> {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { api } from 'src/boot/axios';
|
import { api } from 'src/boot/axios';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
import type { AppConfig } from './types';
|
||||||
|
|
||||||
export const useConfigStore = defineStore('config-store', () => {
|
export const useConfigStore = defineStore('config-store', () => {
|
||||||
const data = ref<AppConfig>();
|
const data = ref<AppConfig>();
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
type AppConfig = {
|
export type AppConfig = {
|
||||||
vat: number;
|
vat: number;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,17 @@ import {
|
||||||
CreditNoteStatus as Status,
|
CreditNoteStatus as Status,
|
||||||
CreditNotePayload as Payload,
|
CreditNotePayload as Payload,
|
||||||
CreditNotePaybackStatus,
|
CreditNotePaybackStatus,
|
||||||
} from './types.ts';
|
} from './types';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
import { api } from 'src/boot/axios.ts';
|
import { api } from 'src/boot/axios';
|
||||||
import { PaginationResult } from 'src/types.ts';
|
import { PaginationResult } from 'src/types';
|
||||||
import { manageAttachment, manageFile } from '../utils/index.ts';
|
import { manageAttachment, manageFile } from '../utils';
|
||||||
|
|
||||||
const ENDPOINT = 'credit-note';
|
const ENDPOINT = 'credit-note';
|
||||||
|
|
||||||
export * from './types.ts';
|
export * from './types';
|
||||||
|
|
||||||
export async function getCreditNoteStats() {
|
export async function getCreditNoteStats() {
|
||||||
const res = await api.get<Record<Status, number>>(`/${ENDPOINT}/stats`);
|
const res = await api.get<Record<Status, number>>(`/${ENDPOINT}/stats`);
|
||||||
|
|
@ -28,6 +28,8 @@ export async function getCreditNoteList(params?: {
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
query?: string;
|
query?: string;
|
||||||
creditNoteStatus?: Status;
|
creditNoteStatus?: Status;
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string;
|
||||||
}) {
|
}) {
|
||||||
const res = await api.get<PaginationResult<Data>>(`/${ENDPOINT}`, {
|
const res = await api.get<PaginationResult<Data>>(`/${ENDPOINT}`, {
|
||||||
params,
|
params,
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,8 @@ const useCustomerStore = defineStore('api-customer', () => {
|
||||||
includeBranch?: boolean;
|
includeBranch?: boolean;
|
||||||
status?: 'CREATED' | 'ACTIVE' | 'INACTIVE';
|
status?: 'CREATED' | 'ACTIVE' | 'INACTIVE';
|
||||||
customerType?: CustomerType;
|
customerType?: CustomerType;
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string;
|
||||||
},
|
},
|
||||||
Data extends Pagination<
|
Data extends Pagination<
|
||||||
(Customer &
|
(Customer &
|
||||||
|
|
@ -500,6 +502,6 @@ const useCustomerStore = defineStore('api-customer', () => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
export * from './types.ts';
|
export * from './types';
|
||||||
|
|
||||||
export default useCustomerStore;
|
export default useCustomerStore;
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,17 @@ import {
|
||||||
DebitNote as Data,
|
DebitNote as Data,
|
||||||
DebitNoteStatus as Status,
|
DebitNoteStatus as Status,
|
||||||
DebitNotePayload as Payload,
|
DebitNotePayload as Payload,
|
||||||
} from './types.ts';
|
} from './types';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
import { api } from 'src/boot/axios.ts';
|
import { api } from 'src/boot/axios';
|
||||||
import { PaginationResult } from 'src/types.ts';
|
import { PaginationResult } from 'src/types';
|
||||||
import { manageAttachment, manageFile } from '../utils/index.ts';
|
import { manageAttachment, manageFile } from '../utils';
|
||||||
|
|
||||||
const ENDPOINT = 'debit-note';
|
const ENDPOINT = 'debit-note';
|
||||||
|
|
||||||
export * from './types.ts';
|
export * from './types';
|
||||||
|
|
||||||
export async function getDebitNoteStats() {
|
export async function getDebitNoteStats() {
|
||||||
const res = await api.get<Record<Status, number>>(`/${ENDPOINT}/stats`);
|
const res = await api.get<Record<Status, number>>(`/${ENDPOINT}/stats`);
|
||||||
|
|
@ -28,6 +28,8 @@ export async function getDebitNoteList(params?: {
|
||||||
query?: string;
|
query?: string;
|
||||||
status?: Status;
|
status?: Status;
|
||||||
includeRegisteredBranch?: boolean;
|
includeRegisteredBranch?: boolean;
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string;
|
||||||
}) {
|
}) {
|
||||||
const res = await api.get<PaginationResult<Data>>(`/${ENDPOINT}`, {
|
const res = await api.get<PaginationResult<Data>>(`/${ENDPOINT}`, {
|
||||||
params,
|
params,
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ const useEmployeeStore = defineStore('api-employee', () => {
|
||||||
customerId?: string;
|
customerId?: string;
|
||||||
customerBranchId?: string;
|
customerBranchId?: string;
|
||||||
activeOnly?: boolean;
|
activeOnly?: boolean;
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string;
|
||||||
payload?: { passport?: string[] };
|
payload?: { passport?: string[] };
|
||||||
}) {
|
}) {
|
||||||
const { payload, ...params } = opts || {};
|
const { payload, ...params } = opts || {};
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,9 @@ export const useInstitution = defineStore('institution-store', () => {
|
||||||
group?: string;
|
group?: string;
|
||||||
status?: Status;
|
status?: Status;
|
||||||
payload?: { group?: string[] };
|
payload?: { group?: string[] };
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string;
|
||||||
|
activeOnly?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { payload, ...params } = opts || {};
|
const { payload, ...params } = opts || {};
|
||||||
|
|
||||||
|
|
@ -72,18 +75,67 @@ export const useInstitution = defineStore('institution-store', () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (res.data.bank && data.bank.length > 0) {
|
||||||
|
for (let i = 0; i < data.bank?.length; i++) {
|
||||||
|
if (data.bank[i].bankQr) {
|
||||||
|
await api
|
||||||
|
.put(
|
||||||
|
`/institution/${res.data.id}/bank-qr/${res.data.bank[i].id}`,
|
||||||
|
data.bank[i].bankQr,
|
||||||
|
{
|
||||||
|
headers: { 'Content-Type': data.bank[i].bankQr?.type },
|
||||||
|
onUploadProgress: (e) => console.log(e),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.catch((e) => console.error(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (res.status < 400) {
|
if (res.status < 400) {
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function editInstitution(data: InstitutionPayload & { id: string }) {
|
async function editInstitution(
|
||||||
|
data: InstitutionPayload & { id: string },
|
||||||
|
opts?: { indexDeleteQrCodeBank?: number[] },
|
||||||
|
) {
|
||||||
const res = await api.put(`/institution/${data.id}`, {
|
const res = await api.put(`/institution/${data.id}`, {
|
||||||
...data,
|
...data,
|
||||||
id: undefined,
|
id: undefined,
|
||||||
group: undefined,
|
group: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!!res.data.bank && !!data.bank.length) {
|
||||||
|
for (let i = 0; i < data.bank?.length; i++) {
|
||||||
|
if (data.bank[i].bankQr) {
|
||||||
|
console.log(i);
|
||||||
|
console.log(data.bank[i].bankQr);
|
||||||
|
await api
|
||||||
|
.put(
|
||||||
|
`/institution/${res.data.id}/bank-qr/${res.data.bank[i].id}`,
|
||||||
|
data.bank[i].bankQr,
|
||||||
|
{
|
||||||
|
headers: { 'Content-Type': data.bank[i].bankQr?.type },
|
||||||
|
onUploadProgress: (e) => console.log(e),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.catch((e) => console.error(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.indexDeleteQrCodeBank && opts.indexDeleteQrCodeBank.length > 0) {
|
||||||
|
console.log('delete');
|
||||||
|
opts.indexDeleteQrCodeBank.forEach(async (i) => {
|
||||||
|
await api
|
||||||
|
.delete(`/institution/${res.data.id}/bank-qr/${res.data.bank[i].id}`)
|
||||||
|
.catch((e) => console.error(e));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (res.status < 400) {
|
if (res.status < 400) {
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { District, Province, SubDistrict } from '../address';
|
import { District, Province, SubDistrict } from '../address';
|
||||||
|
import { BankBook } from '../branch/types';
|
||||||
import { Status } from '../types';
|
import { Status } from '../types';
|
||||||
|
|
||||||
export type Institution = {
|
export type Institution = {
|
||||||
|
|
@ -25,6 +26,11 @@ export type Institution = {
|
||||||
districtId: string;
|
districtId: string;
|
||||||
provinceId: string;
|
provinceId: string;
|
||||||
status: Status;
|
status: Status;
|
||||||
|
|
||||||
|
contactName?: string | null;
|
||||||
|
contactEmail?: string | null;
|
||||||
|
contactTel?: string | null;
|
||||||
|
bank: BankBook[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InstitutionPayload = {
|
export type InstitutionPayload = {
|
||||||
|
|
@ -34,6 +40,11 @@ export type InstitutionPayload = {
|
||||||
group?: string;
|
group?: string;
|
||||||
selectedImage?: string | null;
|
selectedImage?: string | null;
|
||||||
|
|
||||||
|
contactName?: string | null;
|
||||||
|
contactEmail?: string | null;
|
||||||
|
contactTel?: string | null;
|
||||||
|
bank: BankBook[];
|
||||||
|
|
||||||
addressEN: string;
|
addressEN: string;
|
||||||
address: string;
|
address: string;
|
||||||
soi?: string | null;
|
soi?: string | null;
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,11 @@ import { getToken } from 'src/services/keycloak';
|
||||||
import { Manual } from './types';
|
import { Manual } from './types';
|
||||||
import { baseUrl } from '../utils';
|
import { baseUrl } from '../utils';
|
||||||
|
|
||||||
const ENDPOINT = 'manual';
|
const MANUAL_ENDPOINT = 'manual';
|
||||||
|
const TROUBLESHOOTING_ENDPOINT = 'troubleshooting';
|
||||||
|
|
||||||
export async function getManual() {
|
export async function getManual() {
|
||||||
const res = await api.get<Manual[]>(`/${ENDPOINT}`);
|
const res = await api.get<Manual[]>(`/${MANUAL_ENDPOINT}`);
|
||||||
if (res.status < 400) {
|
if (res.status < 400) {
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
|
|
@ -20,7 +21,28 @@ export async function getManualByPage(opt: {
|
||||||
pageName: string;
|
pageName: string;
|
||||||
}) {
|
}) {
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`${baseUrl}/${ENDPOINT}/${opt.category}/page/${opt.pageName}`,
|
`${baseUrl}/${MANUAL_ENDPOINT}/${opt.category}/page/${opt.pageName}`,
|
||||||
|
);
|
||||||
|
if (res.status < 400) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getTroubleshooting() {
|
||||||
|
const res = await api.get<Manual[]>(`/${TROUBLESHOOTING_ENDPOINT}`);
|
||||||
|
if (res.status < 400) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getTroubleshootingByPage(opt: {
|
||||||
|
category: string;
|
||||||
|
pageName: string;
|
||||||
|
}) {
|
||||||
|
const res = await fetch(
|
||||||
|
`${baseUrl}/${TROUBLESHOOTING_ENDPOINT}/${opt.category}/page/${opt.pageName}`,
|
||||||
);
|
);
|
||||||
if (res.status < 400) {
|
if (res.status < 400) {
|
||||||
return res;
|
return res;
|
||||||
|
|
@ -30,11 +52,15 @@ export async function getManualByPage(opt: {
|
||||||
|
|
||||||
export const useManualStore = defineStore('manual-store', () => {
|
export const useManualStore = defineStore('manual-store', () => {
|
||||||
const dataManual = ref<Manual[]>([]);
|
const dataManual = ref<Manual[]>([]);
|
||||||
|
const dataTroubleshooting = ref<Manual[]>([]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getManual,
|
getManual,
|
||||||
getManualByPage,
|
getManualByPage,
|
||||||
|
getTroubleshooting,
|
||||||
|
getTroubleshootingByPage,
|
||||||
|
|
||||||
dataManual,
|
dataManual,
|
||||||
|
dataTroubleshooting,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ const useOptionStore = defineStore('optionStore', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
rawOption,
|
||||||
globalOption,
|
globalOption,
|
||||||
mapOption,
|
mapOption,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue