ต่อ socket กับserver

This commit is contained in:
Kittapath 2024-02-16 14:31:05 +07:00 committed by puri-ph4tt
parent 940c2c4592
commit fc369de729
4 changed files with 317 additions and 277 deletions

63
package-lock.json generated
View file

@ -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,

View file

@ -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, }
};

View file

@ -1,26 +1,19 @@
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`,
supportMessage: (id: string, pageSize: number = 999, page: number = 1) => supportMessage: (id: string, pageSize: number = 999, page: number = 1) => `${support}/issue/${id}/message?pageSize=${pageSize}&page=${page}`,
`${support}/issue/${id}/message?pageSize=${pageSize}&page=${page}`,
supportIssueUserId: (userId: string) => `${support}/issue?userId=${userId}`, supportIssueUserId: (userId: string) => `${support}/issue?userId=${userId}`,
supportIssue: (pageSize: number = 999, page: number = 1) => supportIssue: (pageSize: number = 999, page: number = 1) => `${support}/issue?pageSize=${pageSize}&page=${page}`,
`${support}/issue?pageSize=${pageSize}&page=${page}`,
supportSearchIssue: ( supportSearchIssue: (search: string, pageSize: number = 999, page: number = 1) => `${support}/issue?pageSize=${pageSize}&page=${page}&search=${search}`,
search: string,
pageSize: number = 999,
page: number = 1
) => `${support}/issue?pageSize=${pageSize}&page=${page}&search=${search}`,
supportIssueCategory: `${support}/issue-category`, supportIssueCategory: `${support}/issue-category`,
supportNewIssue: `${support}/issue`, supportNewIssue: `${support}/issue`,
supportSocket: `${socket}`, supportSocket: `${support}`,
}; }

View file

@ -1,239 +1,238 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia"
import { ref } from "vue"; import { ref } from "vue"
import { io } from "socket.io-client"; import { io } from "socket.io-client"
import { useQuasar } from "quasar"; import { useQuasar } from "quasar"
import http from "@/plugins/http"; import http from "@/plugins/http"
import config from "@/app.config"; import config from "@/app.config"
import type { import type {
SupportMessageResponse, SupportMessageResponse,
SupportIssueResponse, SupportIssueResponse,
SupportIssueCategoryResponse, SupportIssueCategoryResponse,
SupportMessageStatusResponse, SupportMessageStatusResponse,
SupportUserStatus, SupportUserStatus,
SupportMessageStatus, SupportMessageStatus,
} from "@/modules/00_support/interface/index/Main"; } from "@/modules/00_support/interface/index/Main"
import keycloak from "@/plugins/keycloak"; import keycloak from "@/plugins/keycloak"
import { useCounterMixin } from "@/stores/mixin"; 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 openChat = 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[]>([])
const issue = ref<SupportIssueResponse>(); const issue = ref<SupportIssueResponse>()
const issueCategory = ref<SupportIssueCategoryResponse>(); const issueCategory = ref<SupportIssueCategoryResponse>()
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 currentTitle = ref<string>(""); const currentTitle = ref<string>("")
const items = ref<any>([{}, {}, {}, {}, {}, {}, {}]); const items = ref<any>([{}, {}, {}, {}, {}, {}, {}])
const scrollContainer = ref(); const scrollContainer = ref()
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[]) => {
userStatus.value = data; userStatus.value = data
}); })
socket.on("online", (r) => { socket.on("online", r => {
userStatus.value.push({ userStatus.value.push({
socketId: r.socketId, socketId: r.socketId,
userId: r.userId, userId: r.userId,
name: r.name, name: r.name,
role: r.role, role: r.role,
}); })
// console.log("online: ", userStatus.value); // console.log("online: ", userStatus.value);
}); })
socket.on("offline", (socketId: string) => { socket.on("offline", (socketId: string) => {
if (socketId === socket.id) return; if (socketId === socket.id) return
userStatus.value = userStatus.value.filter((v) => v.socketId !== socketId); userStatus.value = userStatus.value.filter(v => v.socketId !== socketId)
// console.log("offline: ", userStatus.value); // console.log("offline: ", userStatus.value);
}); })
socket.on("notify-message", (r) => { socket.on("notify-message", r => {
if (issue.value) { if (issue.value) {
issue.value.result = issue.value.result.map((v) => { issue.value.result = issue.value.result.map(v => {
if (v.id === r.issueId) { if (v.id === r.issueId) {
v.unreadCount++; v.unreadCount++
v.lastMessage = r.content; v.lastMessage = r.content
} }
return v; return v
}); })
} }
}); })
socket.on("message", (r) => { socket.on("message", r => {
message.value?.result.message.push(r); message.value?.result.message.push(r)
if (issue.value) { if (issue.value) {
issue.value.result = issue.value.result.map((v) => { issue.value.result = issue.value.result.map(v => {
if (v.id === r.issueId) v.lastMessage = r.content; if (v.id === r.issueId) v.lastMessage = r.content
return v; return v
}); })
} }
socket.emit("mark-read", { issueId: currentIssue.value }); socket.emit("mark-read", { issueId: currentIssue.value })
scrollToEnd(); scrollToEnd()
// console.log(r.id); // console.log(r.id);
// console.log(message.value?.result.message); // console.log(message.value?.result.message);
}); })
socket.on("read", (r: SupportMessageStatus) => { socket.on("read", (r: SupportMessageStatus) => {
if (messageStatus.value) { if (messageStatus.value) {
messageStatus.value.result = messageStatus.value.result.map((v) => { messageStatus.value.result = messageStatus.value.result.map(v => {
if (v.fromUserId !== r.fromUserId) return r; if (v.fromUserId !== r.fromUserId) return r
return v; return v
}); })
} }
// console.log("event(read):", messageStatus.value); // console.log("event(read):", messageStatus.value);
}); })
function scrollToEnd(position: Number = 1) { function scrollToEnd(position: Number = 1) {
setTimeout(() => { setTimeout(() => {
scrollContainer.value?.setScrollPercentage("vertical", position); scrollContainer.value?.setScrollPercentage("vertical", position)
}, 150); }, 150)
} }
function sendMessage(content: string, to: string) { function sendMessage(content: string, to: string) {
// console.log(content); // console.log(content);
// console.log(to); // console.log(to);
if (content.trim() !== "") { if (content.trim() !== "") {
socket.emit("message", { to, content }); socket.emit("message", { to, content })
scrollToEnd(); scrollToEnd()
} }
} }
async function fetchMessageStatus(issueId: string) { async function fetchMessageStatus(issueId: string) {
showLoader(); showLoader()
const res = await http const res = await http
.get(config.API.supportMessageStatus(issueId)) .get(config.API.supportMessageStatus(issueId))
.catch((err) => { .catch(err => {
messageError($q, err); messageError($q, err)
}) })
.finally(() => { .finally(() => {
hideLoader(); hideLoader()
}); })
if (res && res.data) { if (res && res.data) {
messageStatus.value = res.data; messageStatus.value = res.data
// console.log(messageStatus.value); // console.log(messageStatus.value);
} }
} }
async function fetchMessage(issueId: string) { async function fetchMessage(issueId: string) {
showLoader(); showLoader()
const res = await http const res = await http
.get(config.API.supportMessage(issueId)) .get(config.API.supportMessage(issueId))
.catch((err) => { .catch(err => {
messageError($q, err); messageError($q, err)
}) })
.finally(() => { .finally(() => {
hideLoader(); hideLoader()
}); })
if (res && res.data) { if (res && res.data) {
message.value = await res.data; message.value = await res.data
message.value?.result.message.reverse(); message.value?.result.message.reverse()
socket.emit("join-issue", { issueId }); socket.emit("join-issue", { issueId })
socket.emit("mark-read", { issueId }); socket.emit("mark-read", { issueId })
scrollToEnd(); scrollToEnd()
} }
} }
async function fetchIssue() { async function fetchIssue() {
if (!userId.value) return; if (!userId.value) return
showLoader(); showLoader()
const res = await http const res = await http
.get(config.API.supportIssueUserId(userId.value)) .get(config.API.supportIssueUserId(userId.value))
.catch((err) => { .catch(err => {
messageError($q, err); messageError($q, err)
}) })
.finally(() => { .finally(() => {
hideLoader(); hideLoader()
}); })
if (res && res.data) { if (res && res.data) {
issue.value = res.data; issue.value = res.data
// console.log(JSON.stringify(res.data, null, 2)); // console.log(JSON.stringify(res.data, null, 2));
} }
} }
async function fetchIssueCategory() { async function fetchIssueCategory() {
showLoader(); showLoader()
const res = await http const res = await http
.get(config.API.supportIssueCategory) .get(config.API.supportIssueCategory)
.catch((err) => { .catch(err => {
messageError($q, err); messageError($q, err)
}) })
.finally(() => { .finally(() => {
hideLoader(); hideLoader()
}); })
if (res && res.data) { if (res && res.data) {
issueCategory.value = res.data; issueCategory.value = res.data
// console.log(JSON.stringify(res.data, null, 2)); // console.log(JSON.stringify(res.data, null, 2));
} }
} }
async function newIssue(title: string, categoryId: string) { async function newIssue(title: string, categoryId: string) {
showLoader(); showLoader()
const requestBody = { const requestBody = {
title: title, title: title,
categoryId: categoryId, categoryId: categoryId,
}; }
const res = await http const res = await http
.post(config.API.supportNewIssue, requestBody) .post(config.API.supportNewIssue, requestBody)
.catch((err) => { .catch(err => {
messageError($q, err); messageError($q, err)
}) })
.finally(() => { .finally(() => {
hideLoader(); hideLoader()
}); })
if (res) { if (res) {
fetchIssue(); fetchIssue()
// console.log(JSON.stringify(res.data, null, 2)); // console.log(JSON.stringify(res.data, null, 2));
} }
} }
async function searchIssue(searchData: string) { async function searchIssue(searchData: string) {
const res = await http const res = await http.get(config.API.supportSearchIssue(searchData)).catch(err => {
.get(config.API.supportSearchIssue(searchData)) messageError($q, err)
.catch((err) => { })
messageError($q, err); if (res && res.data) {
}); issue.value = res.data
if (res && res.data) { // console.log(issue.value);
issue.value = res.data; }
// console.log(issue.value); }
}
}
return { return {
userId, userId,
issue, issue,
issueCategory, issueCategory,
message, message,
messageStatus, messageStatus,
userStatus, userStatus,
currentIssue, currentIssue,
currentTitle, currentTitle,
fetchIssue, fetchIssue,
fetchIssueCategory, fetchIssueCategory,
fetchMessageStatus, fetchMessageStatus,
fetchMessage, fetchMessage,
sendMessage, sendMessage,
newIssue, newIssue,
searchIssue, searchIssue,
openChat, openChat,
items, items,
socket, socket,
scrollContainer, scrollContainer,
icon, icon,
}; }
}); })