Merge branch 'phatt-dev' into develop
This commit is contained in:
commit
e6d623074d
8 changed files with 280 additions and 193 deletions
63
package-lock.json
generated
63
package-lock.json
generated
|
|
@ -23,6 +23,7 @@
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"pinia": "^2.0.29",
|
"pinia": "^2.0.29",
|
||||||
"quasar": "^2.11.1",
|
"quasar": "^2.11.1",
|
||||||
|
"socket.io-client": "^4.7.4",
|
||||||
"structure-chart": "^0.0.9",
|
"structure-chart": "^0.0.9",
|
||||||
"vue": "^3.4.15",
|
"vue": "^3.4.15",
|
||||||
"vue-router": "^4.1.6",
|
"vue-router": "^4.1.6",
|
||||||
|
|
@ -1191,6 +1192,11 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/@socket.io/component-emitter": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
|
||||||
|
},
|
||||||
"node_modules/@tato30/vue-pdf": {
|
"node_modules/@tato30/vue-pdf": {
|
||||||
"version": "1.6.2",
|
"version": "1.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/@tato30/vue-pdf/-/vue-pdf-1.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/@tato30/vue-pdf/-/vue-pdf-1.6.2.tgz",
|
||||||
|
|
@ -2894,7 +2900,6 @@
|
||||||
},
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "2.1.2"
|
"ms": "2.1.2"
|
||||||
|
|
@ -3056,6 +3061,26 @@
|
||||||
"once": "^1.4.0"
|
"once": "^1.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/engine.io-client": {
|
||||||
|
"version": "6.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz",
|
||||||
|
"integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.1",
|
||||||
|
"engine.io-parser": "~5.2.1",
|
||||||
|
"ws": "~8.11.0",
|
||||||
|
"xmlhttprequest-ssl": "~2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/engine.io-parser": {
|
||||||
|
"version": "5.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz",
|
||||||
|
"integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/enquirer": {
|
"node_modules/enquirer": {
|
||||||
"version": "2.3.6",
|
"version": "2.3.6",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
|
@ -5456,7 +5481,6 @@
|
||||||
},
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/muggle-string": {
|
"node_modules/muggle-string": {
|
||||||
|
|
@ -6702,6 +6726,32 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/socket.io-client": {
|
||||||
|
"version": "4.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.4.tgz",
|
||||||
|
"integrity": "sha512-wh+OkeF0rAVCrABWQBaEjLfb7DVPotMbu0cgWgyR0v6eA4EoVnAwcIeIbcdTE3GT/H3kbdLl7OoH2+asoDRIIg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.2",
|
||||||
|
"engine.io-client": "~6.5.2",
|
||||||
|
"socket.io-parser": "~4.2.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io-parser": {
|
||||||
|
"version": "4.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||||
|
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/source-map": {
|
"node_modules/source-map": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
|
@ -7804,7 +7854,6 @@
|
||||||
},
|
},
|
||||||
"node_modules/ws": {
|
"node_modules/ws": {
|
||||||
"version": "8.11.0",
|
"version": "8.11.0",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
|
|
@ -7835,6 +7884,14 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/xmlhttprequest-ssl": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yallist": {
|
"node_modules/yallist": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
|
|
||||||
|
|
@ -1,53 +1,44 @@
|
||||||
/**config api */
|
/**config api */
|
||||||
import { ref } from "vue";
|
import { ref } from "vue"
|
||||||
|
|
||||||
const env = ref<string>(process.env.NODE_ENV || "development");
|
const env = ref<string>(process.env.NODE_ENV || "development")
|
||||||
export const apiUrlConfig = import.meta.env.VITE_API_URI_CONFIG;
|
export const apiUrlConfig = import.meta.env.VITE_API_URI_CONFIG
|
||||||
export const apiUrlConfigPublish = import.meta.env.VITE_API_PUBLISH_URL;
|
export const apiUrlConfigPublish = import.meta.env.VITE_API_PUBLISH_URL
|
||||||
|
|
||||||
// if (process.env.VUE_APP_TEST) {
|
// if (process.env.VUE_APP_TEST) {
|
||||||
// env = "test";
|
// env = "test";
|
||||||
// }
|
// }
|
||||||
const config = ref<any>({
|
const config = ref<any>({
|
||||||
development: {
|
development: {
|
||||||
// API_URI: "https://localhost:7260/api",
|
// API_URI: "https://localhost:7260/api",
|
||||||
API_URI: "https://bma-ehr.frappet.synology.me/api/v1",
|
API_URI: "https://bma-ehr.frappet.synology.me/api/v1",
|
||||||
API_URL_SUPPORT: "http://192.168.1.10:3000/api/v1/support",
|
API_URL_SUPPORT: "https://bma-ehr.frappet.synology.me/api/v1/support",
|
||||||
API_SUPPORT_SOCKET: "http://192.168.1.10:3000/",
|
MEET_URI: "meet.frappet.com",
|
||||||
MEET_URI: "meet.frappet.com",
|
LINK_EVALUATE_PUBLISH: "https://bma-ehr-publish.frappet.synology.me",
|
||||||
LINK_EVALUATE_PUBLISH: "https://bma-ehr-publish.frappet.synology.me",
|
},
|
||||||
},
|
test: {
|
||||||
test: {
|
API_URI: "http://localhost:5010/api/v1",
|
||||||
API_URI: "http://localhost:5010/api/v1",
|
MEET_URI: "meet.frappet.com",
|
||||||
MEET_URI: "meet.frappet.com",
|
},
|
||||||
},
|
production: {
|
||||||
production: {
|
API_URI: apiUrlConfig,
|
||||||
API_URI: apiUrlConfig,
|
API_URL_SUPPORT: `${apiUrlConfig}/support`,
|
||||||
API_URL_SUPPORT: "http://192.168.1.10:3000/api/v1/support",
|
API_URI_ORG_TREE: "https://s3cluster.frappet.com/bma-ehr-fpt/organization/strueture/tree_20230707_115124.json",
|
||||||
API_SUPPORT_SOCKET: "http://192.168.1.10:3000/",
|
MEET_URI: "meet.frappet.com",
|
||||||
API_URI_ORG_TREE:
|
LINK_EVALUATE_PUBLISH: apiUrlConfigPublish,
|
||||||
"https://s3cluster.frappet.com/bma-ehr-fpt/organization/strueture/tree_20230707_115124.json",
|
},
|
||||||
MEET_URI: "meet.frappet.com",
|
})
|
||||||
LINK_EVALUATE_PUBLISH: apiUrlConfigPublish,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const API_URI = ref<string>(config.value[env.value].API_URI);
|
const API_URI = ref<string>(config.value[env.value].API_URI)
|
||||||
const API_URL_SUPPORT = ref<string>(config.value[env.value].API_URL_SUPPORT);
|
const API_URL_SUPPORT = ref<string>(config.value[env.value].API_URL_SUPPORT)
|
||||||
const API_SUPPORT_SOCKET = ref<string>(
|
const MEET_URI = ref<string>(config.value[env.value].MEET_URI)
|
||||||
config.value[env.value].API_SUPPORT_SOCKET
|
const LINK_EVALUATE_PUBLISH = ref<string>(config.value[env.value].LINK_EVALUATE_PUBLISH)
|
||||||
);
|
|
||||||
const MEET_URI = ref<string>(config.value[env.value].MEET_URI);
|
|
||||||
const LINK_EVALUATE_PUBLISH = ref<string>(
|
|
||||||
config.value[env.value].LINK_EVALUATE_PUBLISH
|
|
||||||
);
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
env: env.value,
|
env: env.value,
|
||||||
config: config.value,
|
config: config.value,
|
||||||
API_URI: API_URI.value,
|
API_URI: API_URI.value,
|
||||||
API_URL_SUPPORT: API_URL_SUPPORT.value,
|
API_URL_SUPPORT: API_URL_SUPPORT.value,
|
||||||
API_SUPPORT_SOCKET: API_SUPPORT_SOCKET.value,
|
MEET_URI: MEET_URI.value,
|
||||||
MEET_URI: MEET_URI.value,
|
LINK_EVALUATE_PUBLISH: LINK_EVALUATE_PUBLISH.value,
|
||||||
LINK_EVALUATE_PUBLISH: LINK_EVALUATE_PUBLISH.value,
|
}
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import env from "../index";
|
import env from "../index";
|
||||||
|
|
||||||
const support = `${env.API_URL_SUPPORT}`;
|
const support = `${env.API_URL_SUPPORT}`;
|
||||||
const socket = `${env.API_SUPPORT_SOCKET}`;
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
supportMessageStatus: (id: string) => `${support}/issue/${id}/message-status`,
|
supportMessageStatus: (id: string) => `${support}/issue/${id}/message-status`,
|
||||||
|
|
@ -22,5 +21,5 @@ export default {
|
||||||
|
|
||||||
supportIssueCategory: `${support}/issue-category`,
|
supportIssueCategory: `${support}/issue-category`,
|
||||||
supportNewIssue: `${support}/issue`,
|
supportNewIssue: `${support}/issue`,
|
||||||
supportSocket: `${socket}`,
|
supportSocket: `${new URL(support).origin}`,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ function dateChat(timestamp: string) {
|
||||||
const diff = moment().diff(parsedTimestamp);
|
const diff = moment().diff(parsedTimestamp);
|
||||||
|
|
||||||
if (diff < 86400000) {
|
if (diff < 86400000) {
|
||||||
const formattedDate = parsedTimestamp.format("HH:mm:") + " น.";
|
const formattedDate = parsedTimestamp.format("HH:mm") + " น.";
|
||||||
return formattedDate;
|
return formattedDate;
|
||||||
} else {
|
} else {
|
||||||
const beYear = parsedTimestamp.year() + 543;
|
const beYear = parsedTimestamp.year() + 543;
|
||||||
|
|
@ -37,11 +37,14 @@ function dateChat(timestamp: string) {
|
||||||
<span> {{ dateChat(item.createdAt) }} </span>
|
<span> {{ dateChat(item.createdAt) }} </span>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:avatar>
|
<template v-slot:avatar>
|
||||||
<img
|
<q-img
|
||||||
v-if="item.fromUserId === store.userId"
|
v-if="item.fromUserId === store.userId"
|
||||||
class="q-message-avatar q-message-avatar--sent"
|
src="@/assets/avatar_user.jpg"
|
||||||
src="https://cdn.quasar.dev/img/avatar4.jpg"
|
class="q-ml-sm"
|
||||||
|
spinner-color="white"
|
||||||
|
style="height: 42px; max-width: 42px; border-radius: 50%"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="q-mr-sm"
|
class="q-mr-sm"
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
import { useSupportStore } from "@/modules/00_support/store/Main";
|
import { useSupportStore } from "@/modules/00_support/store/Main";
|
||||||
|
import { useCounterMixin } from "@/stores/mixin";
|
||||||
import ChatMessage from "@/modules/00_support/components/ChatMessage.vue";
|
import ChatMessage from "@/modules/00_support/components/ChatMessage.vue";
|
||||||
|
|
||||||
const store = useSupportStore();
|
const store = useSupportStore();
|
||||||
const content = ref<string>("");
|
const content = ref<string>("");
|
||||||
|
const { date2Thai } = useCounterMixin();
|
||||||
const { scrollContainer } = storeToRefs(store);
|
const { scrollContainer } = storeToRefs(store);
|
||||||
|
|
||||||
function getOnlineStatus(option: "icon" | "status") {
|
function getOnlineStatus(option: "icon" | "status") {
|
||||||
|
|
@ -13,20 +15,26 @@ function getOnlineStatus(option: "icon" | "status") {
|
||||||
if (option === "icon") return isAdmin ? "green" : "grey";
|
if (option === "icon") return isAdmin ? "green" : "grey";
|
||||||
if (option === "status") return isAdmin ? "ออนไลน์" : "ออฟไลน์";
|
if (option === "status") return isAdmin ? "ออนไลน์" : "ออฟไลน์";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const thaiOptions: Intl.DateTimeFormatOptions = {
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="row q-py-md q-px-lg justify-between items-center"
|
class="row q-py-md q-px-lg justify-between items-center"
|
||||||
v-if="store.currentTitle && store.currentTitle.length > 0"
|
v-if="store.currentTitle && store.currentTitle.length > 0"
|
||||||
|
style="flex-wrap: nowrap"
|
||||||
>
|
>
|
||||||
<div class="col row items-center">
|
<div class="row items-center" style="flex-wrap: nowrap">
|
||||||
<q-btn
|
<q-btn
|
||||||
flat
|
flat
|
||||||
padding="none"
|
padding="none"
|
||||||
class="q-mr-md"
|
class="q-mr-md"
|
||||||
icon="mdi-chevron-left"
|
icon="mdi-chevron-left"
|
||||||
@click="store.isOpen = false"
|
@click="store.openChat = false"
|
||||||
v-if="!$q.screen.gt.xs"
|
v-if="!$q.screen.gt.xs"
|
||||||
/>
|
/>
|
||||||
<div style="border-radius: 50%; border: 1px solid teal">
|
<div style="border-radius: 50%; border: 1px solid teal">
|
||||||
|
|
@ -34,8 +42,8 @@ function getOnlineStatus(option: "icon" | "status") {
|
||||||
<q-icon :name="store.icon" size="24px" color="teal" />
|
<q-icon :name="store.icon" size="24px" color="teal" />
|
||||||
</q-avatar>
|
</q-avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="col column q-ml-md">
|
<div class="column q-ml-md">
|
||||||
<span class="col text-weight-bold">
|
<span class="text-weight-bold ellipsis-2-lines">
|
||||||
{{ store.currentTitle }}
|
{{ store.currentTitle }}
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
|
|
@ -56,7 +64,21 @@ function getOnlineStatus(option: "icon" | "status") {
|
||||||
name="mdi-information-outline"
|
name="mdi-information-outline"
|
||||||
size="24px"
|
size="24px"
|
||||||
color="grey"
|
color="grey"
|
||||||
/>
|
>
|
||||||
|
<q-tooltip class="column">
|
||||||
|
<span>{{ store.currentTitle }}</span>
|
||||||
|
<span>
|
||||||
|
{{ date2Thai(store.currentIssueDate) }}
|
||||||
|
{{
|
||||||
|
new Date(store.currentIssueDate).toLocaleTimeString(
|
||||||
|
"th-TH",
|
||||||
|
thaiOptions
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
น. <q-space />
|
||||||
|
</span>
|
||||||
|
</q-tooltip>
|
||||||
|
</q-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<q-separator />
|
<q-separator />
|
||||||
|
|
@ -74,7 +96,10 @@ function getOnlineStatus(option: "icon" | "status") {
|
||||||
</q-scroll-area>
|
</q-scroll-area>
|
||||||
|
|
||||||
<q-separator />
|
<q-separator />
|
||||||
<div class="row q-py-md q-px-lg justify-between items-center q-gutter-x-lg">
|
<div
|
||||||
|
class="row q-py-md q-px-lg justify-between items-center q-gutter-x-lg"
|
||||||
|
style="flex-wrap: nowrap"
|
||||||
|
>
|
||||||
<!-- <div>
|
<!-- <div>
|
||||||
<q-btn flat disable class="col-1">
|
<q-btn flat disable class="col-1">
|
||||||
<q-icon
|
<q-icon
|
||||||
|
|
@ -118,8 +143,10 @@ function getOnlineStatus(option: "icon" | "status") {
|
||||||
flat
|
flat
|
||||||
class="col-2"
|
class="col-2"
|
||||||
style="color: #009789"
|
style="color: #009789"
|
||||||
label="ส่งข้อความ"
|
>
|
||||||
/>
|
<span v-if="$q.screen.gt.xs">ส่งข้อความ</span>
|
||||||
|
<q-icon v-else name="mdi-send" size="xs" color="primary" />
|
||||||
|
</q-btn>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,27 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import { useSupportStore } from "@/modules/00_support/store/Main";
|
import { useSupportStore } from "@/modules/00_support/store/Main";
|
||||||
|
import DialogHeader from "@/components/DialogHeader.vue";
|
||||||
|
|
||||||
const store = useSupportStore();
|
const store = useSupportStore();
|
||||||
|
|
||||||
const isOpen = ref<boolean>(false);
|
|
||||||
const title = ref<string>("");
|
const title = ref<string>("");
|
||||||
const titleRef = ref();
|
const titleRef = ref();
|
||||||
const categoryId = ref<string>("");
|
const categoryId = ref<string>("");
|
||||||
const categoryRef = ref();
|
const categoryRef = ref();
|
||||||
const options = ref<{ label: string; value: string }[]>([]);
|
const options = ref<{ label: string; value: string }[]>([]);
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modal: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
clickClose: {
|
||||||
|
type: Function,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await store.fetchIssueCategory();
|
await store.fetchIssueCategory();
|
||||||
if (store.issueCategory) {
|
if (store.issueCategory) {
|
||||||
|
|
@ -25,109 +36,77 @@ async function onSubmit() {
|
||||||
titleRef.value.validate();
|
titleRef.value.validate();
|
||||||
categoryRef.value.validate();
|
categoryRef.value.validate();
|
||||||
if (titleRef.value.hasError || categoryRef.value.hasError) return "";
|
if (titleRef.value.hasError || categoryRef.value.hasError) return "";
|
||||||
await store.newIssue(title.value, categoryId.value);
|
await store.newIssue(title.value, categoryId.value).then(() => {
|
||||||
isOpen.value = false;
|
props.clickClose();
|
||||||
|
title.value = "";
|
||||||
|
categoryId.value = "";
|
||||||
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<q-dialog v-model="props.modal" persistent>
|
||||||
@click="isOpen = true"
|
<q-card style="width: 40vw; max-width: 40vw">
|
||||||
class="col-10 row items-center q-py-sm q-px-md new"
|
<q-form ref="myForm" @submit.prevent.stop="onSubmit">
|
||||||
>
|
<DialogHeader tittle="แจ้งปัญหา" :close="clickClose" />
|
||||||
<div class="noactive-avatar">
|
<q-separator />
|
||||||
<div class="new-avatar">
|
<q-card-section class="q-pa-sm bg-grey-1">
|
||||||
<q-icon name="mdi-plus" size="24px" color="primary" />
|
<div class="row col-12 q-col-gutter-sm">
|
||||||
</div>
|
<div class="col-xs-12">
|
||||||
</div>
|
<div class="col-12 row q-py-sm items-center q-col-gutter-sm">
|
||||||
|
<q-select
|
||||||
<div class="col column q-ml-md">
|
outlined
|
||||||
<span class="col text-grey"> เพิ่มเรื่องปรึกษาใหม่ </span>
|
dense
|
||||||
</div>
|
hide-bottom-space
|
||||||
</div>
|
emit-value
|
||||||
|
map-options
|
||||||
<!-- Dialog -->
|
lazy-rules
|
||||||
<q-dialog v-model="isOpen">
|
ref="categoryRef"
|
||||||
<q-card class="q-pa-md" style="width: 100%">
|
class="full-width inputgreen cursor-pointer"
|
||||||
<q-card-section class="row items-center">
|
option-value="value"
|
||||||
<div class="text-h6">เพิ่มเรื่องปรึกษาใหม่</div>
|
option-label="label"
|
||||||
<q-space />
|
label="หมวดหมู่"
|
||||||
<q-btn
|
v-model="categoryId"
|
||||||
icon="close"
|
:options="options"
|
||||||
unelevated
|
:rules="[
|
||||||
round
|
(val) =>
|
||||||
dense
|
(val !== null && val !== '') || 'กรุณาเลือหมวดหมู่',
|
||||||
v-close-popup
|
]"
|
||||||
style="color: #ff8080; background-color: #ffdede"
|
/>
|
||||||
/>
|
<q-input
|
||||||
</q-card-section>
|
type="textarea"
|
||||||
|
ref="titleRef"
|
||||||
<form @submit.prevent.stop="onSubmit">
|
outlined
|
||||||
<q-card-section class="q-gutter-x-md row">
|
dense
|
||||||
<q-select
|
hide-bottom-space
|
||||||
ref="categoryRef"
|
lazy-rules
|
||||||
class="col-3"
|
class="full-width inputgreen cursor-pointer"
|
||||||
outlined
|
label="รายละเอียด"
|
||||||
dense
|
v-model="title"
|
||||||
emit-value
|
:rules="[
|
||||||
map-options
|
(val) => (val && val.length > 0) || 'กรุณากรอกชื่อเรื่อง',
|
||||||
v-model="categoryId"
|
]"
|
||||||
:options="options"
|
/>
|
||||||
option-value="value"
|
</div>
|
||||||
option-label="label"
|
</div>
|
||||||
label="หมวดหมู่"
|
</div>
|
||||||
lazy-rules
|
|
||||||
:rules="[
|
|
||||||
(val) => (val !== null && val !== '') || 'กรุณาเลือหมวดหมู่',
|
|
||||||
]"
|
|
||||||
/>
|
|
||||||
<q-input
|
|
||||||
ref="titleRef"
|
|
||||||
class="col-grow"
|
|
||||||
outlined
|
|
||||||
dense
|
|
||||||
v-model="title"
|
|
||||||
label="ชื่อเรื่อง"
|
|
||||||
lazy-rules
|
|
||||||
:rules="[
|
|
||||||
(val) => (val && val.length > 0) || 'กรุณาเพิ่มชื่อเรื่อง',
|
|
||||||
]"
|
|
||||||
/>
|
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
<q-separator />
|
||||||
<q-card-actions align="right">
|
<q-card-actions align="right">
|
||||||
<q-btn flat v-close-popup label="ยกเลิก" text-color="primary" />
|
|
||||||
<q-btn
|
<q-btn
|
||||||
|
dense
|
||||||
|
unelevated
|
||||||
|
label="แจ้งปัญหา"
|
||||||
|
color="public"
|
||||||
type="submit"
|
type="submit"
|
||||||
label="บันทึก"
|
class="q-px-md"
|
||||||
color="primary"
|
v-close-popup
|
||||||
text-color="white"
|
>
|
||||||
/>
|
<!-- icon="mdi-content-save-outline" -->
|
||||||
|
<q-tooltip>แจ้งปัญหา</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</form>
|
</q-form>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.new {
|
|
||||||
transition: 0.1s ease all;
|
|
||||||
opacity: 1;
|
|
||||||
cursor: pointer;
|
|
||||||
border-left: 5px solid transparent;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #ebf9f7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.new-avatar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 2px dashed #9e9e9e;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import { useCounterMixin } from "@/stores/mixin";
|
||||||
export const useSupportStore = defineStore("supportServiceStore", () => {
|
export const useSupportStore = defineStore("supportServiceStore", () => {
|
||||||
const { showLoader, hideLoader, messageError } = useCounterMixin();
|
const { showLoader, hideLoader, messageError } = useCounterMixin();
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const isOpen = ref<boolean>(false);
|
const openChat = ref<boolean>(false);
|
||||||
const icon = ref<string>("mdi-account-check");
|
const icon = ref<string>("mdi-account-check");
|
||||||
const userId = ref<string | undefined>(keycloak.subject);
|
const userId = ref<string | undefined>(keycloak.subject);
|
||||||
const userStatus = ref<SupportUserStatus[]>([]);
|
const userStatus = ref<SupportUserStatus[]>([]);
|
||||||
|
|
@ -27,6 +27,7 @@ export const useSupportStore = defineStore("supportServiceStore", () => {
|
||||||
const messageStatus = ref<SupportMessageStatusResponse>();
|
const messageStatus = ref<SupportMessageStatusResponse>();
|
||||||
const message = ref<SupportMessageResponse>();
|
const message = ref<SupportMessageResponse>();
|
||||||
const currentIssue = ref<string>("");
|
const currentIssue = ref<string>("");
|
||||||
|
const currentIssueDate = ref<any>("");
|
||||||
const currentTitle = ref<string>("");
|
const currentTitle = ref<string>("");
|
||||||
const items = ref<any>([{}, {}, {}, {}, {}, {}, {}]);
|
const items = ref<any>([{}, {}, {}, {}, {}, {}, {}]);
|
||||||
const scrollContainer = ref();
|
const scrollContainer = ref();
|
||||||
|
|
@ -34,6 +35,7 @@ export const useSupportStore = defineStore("supportServiceStore", () => {
|
||||||
const socket = io(config.API.supportSocket, {
|
const socket = io(config.API.supportSocket, {
|
||||||
auth: { token: keycloak.token },
|
auth: { token: keycloak.token },
|
||||||
autoConnect: false,
|
autoConnect: false,
|
||||||
|
path: "/api/v1/support/socket/",
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("users", (data: SupportUserStatus[]) => {
|
socket.on("users", (data: SupportUserStatus[]) => {
|
||||||
|
|
@ -63,6 +65,9 @@ export const useSupportStore = defineStore("supportServiceStore", () => {
|
||||||
v.unreadCount++;
|
v.unreadCount++;
|
||||||
v.lastMessage = r.content;
|
v.lastMessage = r.content;
|
||||||
}
|
}
|
||||||
|
if (currentIssue.value === r.issueId) {
|
||||||
|
v.unreadCount = 0;
|
||||||
|
}
|
||||||
return v;
|
return v;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -102,8 +107,10 @@ export const useSupportStore = defineStore("supportServiceStore", () => {
|
||||||
function sendMessage(content: string, to: string) {
|
function sendMessage(content: string, to: string) {
|
||||||
// console.log(content);
|
// console.log(content);
|
||||||
// console.log(to);
|
// console.log(to);
|
||||||
socket.emit("message", { to, content });
|
if (content.trim() !== "") {
|
||||||
scrollToEnd();
|
socket.emit("message", { to, content });
|
||||||
|
scrollToEnd();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchMessageStatus(issueId: string) {
|
async function fetchMessageStatus(issueId: string) {
|
||||||
|
|
@ -220,6 +227,7 @@ export const useSupportStore = defineStore("supportServiceStore", () => {
|
||||||
messageStatus,
|
messageStatus,
|
||||||
userStatus,
|
userStatus,
|
||||||
currentIssue,
|
currentIssue,
|
||||||
|
currentIssueDate,
|
||||||
currentTitle,
|
currentTitle,
|
||||||
fetchIssue,
|
fetchIssue,
|
||||||
fetchIssueCategory,
|
fetchIssueCategory,
|
||||||
|
|
@ -228,7 +236,7 @@ export const useSupportStore = defineStore("supportServiceStore", () => {
|
||||||
sendMessage,
|
sendMessage,
|
||||||
newIssue,
|
newIssue,
|
||||||
searchIssue,
|
searchIssue,
|
||||||
isOpen,
|
openChat,
|
||||||
items,
|
items,
|
||||||
socket,
|
socket,
|
||||||
scrollContainer,
|
scrollContainer,
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import moment from "moment";
|
||||||
|
|
||||||
const store = useSupportStore();
|
const store = useSupportStore();
|
||||||
const searchData = ref<string>("");
|
const searchData = ref<string>("");
|
||||||
|
const newIssueModal = ref<boolean>(false);
|
||||||
|
|
||||||
function dateIssue(timestamp: string): string {
|
function dateIssue(timestamp: string): string {
|
||||||
const parsedTimestamp = moment(timestamp);
|
const parsedTimestamp = moment(timestamp);
|
||||||
|
|
@ -44,14 +45,16 @@ watch(searchData, () => {
|
||||||
<template>
|
<template>
|
||||||
<div class="col-12 row justify-center">
|
<div class="col-12 row justify-center">
|
||||||
<div class="col-xs-12 col-sm-12 col-md-11">
|
<div class="col-xs-12 col-sm-12 col-md-11">
|
||||||
<div class="toptitle text-white col-12 row items-center">ถาม - ตอบ</div>
|
<div class="toptitle text-white col-12 row items-center">
|
||||||
|
แจ้งปัญหาการใช้งาน
|
||||||
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<q-card bordered>
|
<q-card bordered>
|
||||||
<div class="row">
|
<div class="row" style="flex-wrap: nowrap">
|
||||||
<!-- Left -->
|
<!-- Left -->
|
||||||
<div v-if="!store.isOpen" class="col-xs-12 col-sm-4 col-md-3">
|
<div v-if="!store.openChat || $q.screen.gt.xs" class="col-xs-12 col-sm-4 col-md-4">
|
||||||
<!-- New -->
|
<!-- New -->
|
||||||
<div class="q-pt-md bg-white">
|
<div class="q-pt-md">
|
||||||
<div class="q-px-md q-pb-md">
|
<div class="q-px-md q-pb-md">
|
||||||
<q-input
|
<q-input
|
||||||
rounded
|
rounded
|
||||||
|
|
@ -63,35 +66,29 @@ watch(searchData, () => {
|
||||||
id="inputSearch"
|
id="inputSearch"
|
||||||
@keydown.enter.prevent="store.searchIssue(searchData)"
|
@keydown.enter.prevent="store.searchIssue(searchData)"
|
||||||
>
|
>
|
||||||
<template v-slot:prepend
|
<template v-slot:prepend>
|
||||||
><q-icon name="mdi-magnify" class="pointer" />
|
<q-icon name="mdi-magnify" class="pointer" />
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
</div>
|
</div>
|
||||||
<q-separator inset />
|
<q-separator inset />
|
||||||
<div>
|
<div>
|
||||||
<new-issue />
|
<div
|
||||||
|
@click="newIssueModal = true"
|
||||||
|
class="col-10 row items-center q-py-sm q-px-md new"
|
||||||
|
>
|
||||||
|
<div class="no-active-avatar">
|
||||||
|
<div class="new-avatar">
|
||||||
|
<q-icon name="mdi-plus" size="24px" color="primary" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col column q-ml-md">
|
||||||
|
<span class="col text-grey"> แจ้งปัญหา </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<q-separator inset />
|
<q-separator inset />
|
||||||
<!-- No Issue -->
|
|
||||||
<div
|
|
||||||
v-if="store.issue?.result.length === 0"
|
|
||||||
class="col-10 row items-center q-pa-md new"
|
|
||||||
>
|
|
||||||
<div class="noactive-avatar">
|
|
||||||
<q-avatar
|
|
||||||
color="transparent"
|
|
||||||
text-color="white"
|
|
||||||
size="40px"
|
|
||||||
>
|
|
||||||
<q-icon name="mdi-close" size="24px" color="negative" />
|
|
||||||
</q-avatar>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col column q-ml-md">
|
|
||||||
<span class="col text-grey"> ไม่พบเรื่องปรึกษา </span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- List Issue -->
|
<!-- List Issue -->
|
||||||
|
|
@ -105,9 +102,10 @@ watch(searchData, () => {
|
||||||
<div
|
<div
|
||||||
@click="
|
@click="
|
||||||
async () => {
|
async () => {
|
||||||
$q.screen.gt.xs ? '' : (store.isOpen = true);
|
$q.screen.gt.xs ? '' : (store.openChat = true);
|
||||||
store.currentIssue = item.id;
|
store.currentIssue = item.id;
|
||||||
store.currentTitle = item.title;
|
store.currentTitle = item.title;
|
||||||
|
store.currentIssueDate = item.createdAt
|
||||||
store.issue
|
store.issue
|
||||||
? (store.issue.result = store.issue.result.map(
|
? (store.issue.result = store.issue.result.map(
|
||||||
(v) => {
|
(v) => {
|
||||||
|
|
@ -123,10 +121,10 @@ watch(searchData, () => {
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
:class="{ active: store.currentIssue === item.id }"
|
:class="{ active: store.currentIssue === item.id }"
|
||||||
class="noactive row q-py-md justify-between items-center q-px-md"
|
class="no-active row q-py-md justify-between items-center q-px-md"
|
||||||
>
|
>
|
||||||
<div class="col-9 row items-center">
|
<div class="col-9 row items-center">
|
||||||
<div class="noactive-avatar">
|
<div class="no-active-avatar">
|
||||||
<q-avatar color="grey-2" text-color="white" size="40px">
|
<q-avatar color="grey-2" text-color="white" size="40px">
|
||||||
<q-icon
|
<q-icon
|
||||||
:name="store.icon"
|
:name="store.icon"
|
||||||
|
|
@ -187,18 +185,22 @@ watch(searchData, () => {
|
||||||
<q-separator vertical />
|
<q-separator vertical />
|
||||||
|
|
||||||
<!-- Right -->
|
<!-- Right -->
|
||||||
<div v-if="$q.screen.gt.xs || store.isOpen" class="col-grow">
|
<div v-if="$q.screen.gt.xs || store.openChat" style="width: 100%">
|
||||||
<form-chat v-if="store.currentIssue.length !== 0" />
|
<form-chat v-if="store.currentIssue.length !== 0" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-card>
|
</q-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<new-issue
|
||||||
|
:modal="newIssueModal"
|
||||||
|
:click-close="() => (newIssueModal = false)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.noactive {
|
.no-active {
|
||||||
transition: 0.1s ease all;
|
transition: 0.1s ease all;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
@ -207,7 +209,7 @@ watch(searchData, () => {
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #ebf9f7;
|
background: #ebf9f7;
|
||||||
|
|
||||||
.noactive-avatar {
|
.no-active-avatar {
|
||||||
transition: 0.1s ease all;
|
transition: 0.1s ease all;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 1px solid $primary;
|
border: 1px solid $primary;
|
||||||
|
|
@ -215,7 +217,7 @@ watch(searchData, () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.noactive-avatar {
|
.no-active-avatar {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 1px solid #f5f4f4;
|
border: 1px solid #f5f4f4;
|
||||||
}
|
}
|
||||||
|
|
@ -224,13 +226,34 @@ watch(searchData, () => {
|
||||||
background: #ebf9f7;
|
background: #ebf9f7;
|
||||||
border-left: 5px solid $primary;
|
border-left: 5px solid $primary;
|
||||||
& {
|
& {
|
||||||
.noactive-avatar {
|
.no-active-avatar {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 1px solid $primary;
|
border: 1px solid $primary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.new {
|
||||||
|
transition: 0.1s ease all;
|
||||||
|
opacity: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
border-left: 5px solid transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #ebf9f7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-avatar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px dashed #9e9e9e;
|
||||||
|
}
|
||||||
|
|
||||||
.line-ellipsis {
|
.line-ellipsis {
|
||||||
display: block;
|
display: block;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue