GUI chat
This commit is contained in:
parent
f25a439804
commit
0a98d96d38
9 changed files with 6862 additions and 9 deletions
6498
pnpm-lock.yaml
generated
Normal file
6498
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load diff
10
src/api/00_support/api.support.ts
Normal file
10
src/api/00_support/api.support.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import env from "../index";
|
||||||
|
|
||||||
|
export const supportIssue = `${env.API_SUPPORT_URI}/issue`;
|
||||||
|
export const supportMessage = (id: string) =>
|
||||||
|
`${env.API_SUPPORT_URI}/issue/${id}/message`;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
supportIssue,
|
||||||
|
supportMessage,
|
||||||
|
};
|
||||||
|
|
@ -35,6 +35,7 @@ const config = ref<any>({
|
||||||
LINK_EVALUATE_PUBLISH: "https://bma-ehr-publish.frappet.synology.me",
|
LINK_EVALUATE_PUBLISH: "https://bma-ehr-publish.frappet.synology.me",
|
||||||
API_REPORT_TEMPLATE_URI:
|
API_REPORT_TEMPLATE_URI:
|
||||||
"https://report-server.frappet.synology.me/api/v1/report-template",
|
"https://report-server.frappet.synology.me/api/v1/report-template",
|
||||||
|
API_SUPPORT_URI: "http://192.168.1.10:3000/api/support/v1",
|
||||||
},
|
},
|
||||||
test: {
|
test: {
|
||||||
API_URI: "http://localhost:5010/api/v1",
|
API_URI: "http://localhost:5010/api/v1",
|
||||||
|
|
@ -62,6 +63,7 @@ const config = ref<any>({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const API_SUPPORT_URI = ref<string>(config.value[env.value].API_SUPPORT_URI);
|
||||||
const API_URI = ref<string>(config.value[env.value].API_URI);
|
const API_URI = ref<string>(config.value[env.value].API_URI);
|
||||||
const API_CANDIDATE_URI = ref<string>(
|
const API_CANDIDATE_URI = ref<string>(
|
||||||
config.value[env.value].API_CANDIDATE_URI
|
config.value[env.value].API_CANDIDATE_URI
|
||||||
|
|
@ -112,4 +114,5 @@ export default {
|
||||||
API_REPORT2_URI: API_REPORT2_URI.value,
|
API_REPORT2_URI: API_REPORT2_URI.value,
|
||||||
LINK_EVALUATE_PUBLISH: LINK_EVALUATE_PUBLISH.value,
|
LINK_EVALUATE_PUBLISH: LINK_EVALUATE_PUBLISH.value,
|
||||||
API_REPORT_TEMPLATE_URI: API_REPORT_TEMPLATE_URI.value,
|
API_REPORT_TEMPLATE_URI: API_REPORT_TEMPLATE_URI.value,
|
||||||
|
API_SUPPORT_URI: API_SUPPORT_URI.value,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,8 @@ import evaluate from "./api/12_evaluatePersonal/api.evaluate";
|
||||||
/** API โครงสร้างอัตรากำลัง*/
|
/** API โครงสร้างอัตรากำลัง*/
|
||||||
import organization from "./api/02_organizational/api.organization";
|
import organization from "./api/02_organizational/api.organization";
|
||||||
|
|
||||||
|
import support from "./api/00_support/api.support";
|
||||||
|
|
||||||
/** API เงินเดือน/ค่าจ้าง*/
|
/** API เงินเดือน/ค่าจ้าง*/
|
||||||
import salary from "./api/13_salary/api.salary";
|
import salary from "./api/13_salary/api.salary";
|
||||||
|
|
||||||
|
|
@ -120,6 +122,9 @@ const API = {
|
||||||
/**evaluate*/
|
/**evaluate*/
|
||||||
...evaluate,
|
...evaluate,
|
||||||
|
|
||||||
|
/** support */
|
||||||
|
...support,
|
||||||
|
|
||||||
...salary,
|
...salary,
|
||||||
...KPI,
|
...KPI,
|
||||||
...development,
|
...development,
|
||||||
|
|
|
||||||
184
src/modules/00_support/components/FormChat.vue
Normal file
184
src/modules/00_support/components/FormChat.vue
Normal file
|
|
@ -0,0 +1,184 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from "vue";
|
||||||
|
import { useSupportStore } from "@/modules/00_support/store/Main.ts";
|
||||||
|
import MessageChat from "@/modules/00_support/components/MessageChat.vue";
|
||||||
|
|
||||||
|
const store = useSupportStore();
|
||||||
|
const currentIssue = ref<string>("");
|
||||||
|
const content = ref<string>("");
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
store.fetchIssue();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<p class="text-h6 text-weight-medium">ถาม - ตอบ</p>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="i1 bg-white align-center">
|
||||||
|
<q-toolbar>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input dense rounded outlined label="ค้นหาข้อความ...">
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<q-icon name="search" />
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-item-section>
|
||||||
|
</q-toolbar>
|
||||||
|
</div>
|
||||||
|
<div class="i2 bg-white align-center">
|
||||||
|
<q-toolbar>
|
||||||
|
<q-avatar>
|
||||||
|
<img src="https://cdn.quasar.dev/img/avatar3.jpg" />
|
||||||
|
</q-avatar>
|
||||||
|
|
||||||
|
<q-item-section class="q-pl-sm">
|
||||||
|
<q-item-label>สุวิมล คงสวัสดิ์</q-item-label>
|
||||||
|
<q-item-label caption>
|
||||||
|
<q-icon name="noise_control_off" color="positive" />
|
||||||
|
ออนไลน์
|
||||||
|
</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
<q-space />
|
||||||
|
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
icon="o_videocam"
|
||||||
|
class="q-mr-xs"
|
||||||
|
text-color="primary"
|
||||||
|
/>
|
||||||
|
<q-btn flat round dense icon="o_info" text-color="grey" />
|
||||||
|
</q-toolbar>
|
||||||
|
</div>
|
||||||
|
<div class="i3 bg-white">
|
||||||
|
<q-list class="text-primary">
|
||||||
|
<div v-for="data in store.issue?.result">
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
v-ripple
|
||||||
|
:active="currentIssue === data.id"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
currentIssue = data.id;
|
||||||
|
store.fetchMessage(data.id);
|
||||||
|
}
|
||||||
|
"
|
||||||
|
active-class="my-menu-link"
|
||||||
|
>
|
||||||
|
<q-avatar>
|
||||||
|
<img src="https://cdn.quasar.dev/img/avatar1.jpg" />
|
||||||
|
</q-avatar>
|
||||||
|
|
||||||
|
<q-item-section class="q-pl-sm">
|
||||||
|
<q-item-label>{{ data.title }}</q-item-label>
|
||||||
|
<q-item-label caption>
|
||||||
|
{{ data.lastMessage }}
|
||||||
|
</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-separator inset />
|
||||||
|
</div>
|
||||||
|
</q-list>
|
||||||
|
</div>
|
||||||
|
<div class="i4 bg-grey-3 q-pa-sm" style="overflow-y: auto">
|
||||||
|
<!-- <div class="i4 bg-grey-3 q-pa-sm"> -->
|
||||||
|
<message-chat v-if="currentIssue" />
|
||||||
|
</div>
|
||||||
|
<div class="i5 bg-white container-input align-center">
|
||||||
|
<div class="input-file">
|
||||||
|
<q-btn flat>
|
||||||
|
<q-icon name="attach_file" style="transform: rotate(-125deg)" />
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-chat">
|
||||||
|
<q-input
|
||||||
|
dense
|
||||||
|
class="q-pa-xs"
|
||||||
|
standout="bg-teal text-white"
|
||||||
|
label="Aa"
|
||||||
|
v-model="content"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-chat">
|
||||||
|
<q-btn
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
store.sendMessage(content, currentIssue);
|
||||||
|
}
|
||||||
|
"
|
||||||
|
flat
|
||||||
|
color="primary"
|
||||||
|
label="ส่งข้อความ"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.align-center {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas:
|
||||||
|
"search toolbar"
|
||||||
|
"menu chat"
|
||||||
|
"menu input";
|
||||||
|
grid-template-rows: 60px 400px 60px;
|
||||||
|
grid-template-columns: 400px 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-input {
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas: "file input-chat btn";
|
||||||
|
grid-template-rows: 1fr;
|
||||||
|
grid-template-columns: 60px 1fr 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-file {
|
||||||
|
grid-area: file;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-chat {
|
||||||
|
grid-area: input-chat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-chat {
|
||||||
|
grid-area: btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
.i1 {
|
||||||
|
grid-area: search;
|
||||||
|
}
|
||||||
|
.i2 {
|
||||||
|
grid-area: toolbar;
|
||||||
|
}
|
||||||
|
.i3 {
|
||||||
|
grid-area: menu;
|
||||||
|
}
|
||||||
|
.i4 {
|
||||||
|
grid-area: chat;
|
||||||
|
}
|
||||||
|
.i5 {
|
||||||
|
grid-area: input;
|
||||||
|
}
|
||||||
|
.container-1 {
|
||||||
|
display: grid;
|
||||||
|
/* grid-template-columns: 1fr 2fr; */
|
||||||
|
grid: 50px 450px / 1fr 2fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-2 {
|
||||||
|
display: grid;
|
||||||
|
/* grid-template-columns: 1fr 2fr; */
|
||||||
|
grid: 50px 450px / 1fr 2fr;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
29
src/modules/00_support/components/MessageChat.vue
Normal file
29
src/modules/00_support/components/MessageChat.vue
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import "moment/dist/locale/th";
|
||||||
|
import moment from "moment";
|
||||||
|
import { useSupportStore } from "@/modules/00_support/store/Main";
|
||||||
|
import { ref, onMounted } from "vue";
|
||||||
|
const store = useSupportStore();
|
||||||
|
moment.locale("th");
|
||||||
|
console.log(moment.locale());
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<q-infinite-scroll reverse>
|
||||||
|
<div class="bg-grey-3 q-pb-sm">
|
||||||
|
<q-chat-message label="Sunday, 19th" />
|
||||||
|
<q-chat-message
|
||||||
|
v-if="store.message"
|
||||||
|
v-for="data in store.message?.result.message.reverse()"
|
||||||
|
:id="data.id"
|
||||||
|
avatar="https://cdn.quasar.dev/img/avatar4.jpg"
|
||||||
|
:text="[data.content]"
|
||||||
|
:bg-color="data.fromUserId === store.userId ? 'primary' : 'white'"
|
||||||
|
:text-color="data.fromUserId === store.userId ? 'white' : 'black'"
|
||||||
|
:sent="data.fromUserId === store.userId"
|
||||||
|
:stamp="moment(data.createdAt).fromNow()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-infinite-scroll>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
|
@ -1,8 +1,54 @@
|
||||||
interface DataOption {
|
export interface SupportIssueResponse {
|
||||||
|
result: SupportIssue[];
|
||||||
|
page: number;
|
||||||
|
pageSize: number;
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SupportIssue {
|
||||||
|
id: string;
|
||||||
|
createdByUserId: string;
|
||||||
|
createdByUserName: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
title: string;
|
||||||
|
status: string;
|
||||||
|
category: SupportIssueCategory;
|
||||||
|
unreadCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SupportIssueCategory {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type {
|
export interface SupportMessageResponse {
|
||||||
DataOption,
|
result: SupportIssueWithMessage;
|
||||||
};
|
page: number;
|
||||||
|
pageSize: number;
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SupportIssueWithMessage {
|
||||||
|
id: string;
|
||||||
|
createdByUserId: string;
|
||||||
|
createdByUserName: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
title: string;
|
||||||
|
status: string;
|
||||||
|
message: SupportIssueMessage[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SupportIssueMessage {
|
||||||
|
id: string;
|
||||||
|
fromUserId: string;
|
||||||
|
fromUserName: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
content: string;
|
||||||
|
read: boolean;
|
||||||
|
issueId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type { SupportMessageResponse, SupportIssueResponse };
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,86 @@
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
import { io } from "socket.io-client";
|
||||||
|
import http from "@/plugins/http";
|
||||||
|
import config from "@/app.config";
|
||||||
|
import { useCounterMixin } from "@/stores/mixin";
|
||||||
|
import type {
|
||||||
|
SupportMessageResponse,
|
||||||
|
SupportIssueResponse,
|
||||||
|
} from "@/modules/00_support/interface/index/Main";
|
||||||
|
import keycloak from "@/plugins/keycloak";
|
||||||
|
|
||||||
export const useEvalutuonStore = defineStore("supportServiceStore", () => {
|
export const useSupportStore = defineStore("supportServiceStore", () => {
|
||||||
const index = ref<number>(0);
|
const socket = io("http://192.168.1.10:3000/", {
|
||||||
|
auth: { token: keycloak.token },
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("users", (users) => {
|
||||||
|
// console.log(users);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("message", (r) => {
|
||||||
|
console.log(r);
|
||||||
|
});
|
||||||
|
const { showLoader, hideLoader } = useCounterMixin();
|
||||||
|
|
||||||
|
const userId = ref<string>(keycloak.subject);
|
||||||
|
const issue = ref<SupportIssueResponse>();
|
||||||
|
const message = ref<SupportMessageResponse>();
|
||||||
|
const items = ref<string>([{}, {}, {}, {}, {}, {}, {}]);
|
||||||
|
const scrollTargetRef = ref();
|
||||||
|
|
||||||
|
function sendMessage(content: string, to: string) {
|
||||||
|
console.log(content);
|
||||||
|
console.log(to);
|
||||||
|
socket.emit("message", { to, content });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchMessage(issueId: string) {
|
||||||
|
showLoader();
|
||||||
|
const res = await http
|
||||||
|
.get(config.API.supportMessage(issueId))
|
||||||
|
.catch((err) => {
|
||||||
|
messageError($q, err);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hideLoader();
|
||||||
|
});
|
||||||
|
if (res && res.data) {
|
||||||
|
message.value = res.data;
|
||||||
|
|
||||||
|
console.log(res.data);
|
||||||
|
socket.emit("join-issue", { issueId });
|
||||||
|
socket.emit("mark-read", { issueId });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchIssue() {
|
||||||
|
showLoader();
|
||||||
|
|
||||||
|
const res = await http
|
||||||
|
.get(config.API.supportIssue)
|
||||||
|
.catch((err) => {
|
||||||
|
messageError($q, err);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hideLoader();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res && res.data) {
|
||||||
|
issue.value = res.data;
|
||||||
|
console.log(JSON.stringify(res.data, null, -2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
index,
|
userId,
|
||||||
|
issue,
|
||||||
|
message,
|
||||||
|
fetchIssue,
|
||||||
|
fetchMessage,
|
||||||
|
sendMessage,
|
||||||
|
scrollTargetRef,
|
||||||
|
items,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
<script setup lang="ts"></script>
|
<script setup lang="ts">
|
||||||
|
import FormChat from "@/modules/00_support/components/FormChat.vue";
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>support user</div>
|
<form-Chat />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue