update หน้า support user (แยก component)
This commit is contained in:
parent
666fb8e4d9
commit
a17625c13c
4 changed files with 437 additions and 124 deletions
47
src/modules/00_support/components/ChatMessage.vue
Normal file
47
src/modules/00_support/components/ChatMessage.vue
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<script setup lang="ts">
|
||||
import "moment/dist/locale/th";
|
||||
import moment from "moment";
|
||||
import { useSupportStore } from "@/modules/00_support/store/Main";
|
||||
|
||||
const store = useSupportStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="col-grow q-pt-md q-pb-sm">
|
||||
<div v-for="(data, index) in store.message?.result.message">
|
||||
<q-item-label>
|
||||
<q-chat-message
|
||||
ref="myElement"
|
||||
:key="index"
|
||||
:id="data.id"
|
||||
avatar="https://cdn.quasar.dev/img/avatar4.jpg"
|
||||
:text="[data.content]"
|
||||
:bg-color="data.fromUserId === store.userId ? 'teal' : 'white'"
|
||||
:text-color="data.fromUserId === store.userId ? 'white' : 'black'"
|
||||
:sent="data.fromUserId === store.userId"
|
||||
:stamp="moment(data.createdAt).fromNow()"
|
||||
/>
|
||||
</q-item-label>
|
||||
|
||||
<q-item-label
|
||||
v-if="data.fromUserId === store.userId"
|
||||
class="flex"
|
||||
caption
|
||||
>
|
||||
<q-space />
|
||||
<div
|
||||
v-if="
|
||||
store.messageStatus?.result.some(
|
||||
(v) =>
|
||||
new Date(v.lastAccessDate).getTime() >=
|
||||
new Date(data.createdAt).getTime() &&
|
||||
index + 1 === store.message?.result.message.length
|
||||
)
|
||||
"
|
||||
>
|
||||
อ่านเเล้ว
|
||||
</div>
|
||||
</q-item-label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
117
src/modules/00_support/components/FormChat.vue
Normal file
117
src/modules/00_support/components/FormChat.vue
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { useSupportStore } from "@/modules/00_support/store/Main";
|
||||
import ChatMessage from "@/modules/00_support/components/ChatMessage.vue";
|
||||
|
||||
const store = useSupportStore();
|
||||
const content = ref<string>("");
|
||||
|
||||
function getOnlineStatus(option: "icon" | "status") {
|
||||
const isAdmin = store.userStatus.some((u) => u.role.includes("admin"));
|
||||
if (option === "icon") return isAdmin ? "green" : "grey";
|
||||
if (option === "status") return isAdmin ? "ออนไลน์" : "ออฟไลน์";
|
||||
}
|
||||
defineProps({
|
||||
icon: String,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="row q-py-md q-px-lg justify-between items-center"
|
||||
v-if="store.currentTitle && store.currentTitle.length > 0"
|
||||
>
|
||||
<div class="col row">
|
||||
<div style="border-radius: 50%; border: 1px solid teal">
|
||||
<q-avatar color="teal-1" text-color="white" size="40px">
|
||||
<q-icon :name="icon" size="24px" color="teal" />
|
||||
</q-avatar>
|
||||
</div>
|
||||
<div class="col column q-ml-md">
|
||||
<span class="col text-weight-bold">
|
||||
{{ store.currentTitle }}
|
||||
</span>
|
||||
<span>
|
||||
<q-icon
|
||||
name="mdi-circle"
|
||||
size="10px"
|
||||
:color="getOnlineStatus('icon')"
|
||||
/>
|
||||
{{ getOnlineStatus("status") }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-2 text-right q-gutter-x-md">
|
||||
<q-icon class="col" name="mdi-video-outline" size="24px" color="teal" />
|
||||
<q-icon
|
||||
class="col"
|
||||
name="mdi-information-outline"
|
||||
size="24px"
|
||||
color="grey"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<q-separator />
|
||||
|
||||
<q-scroll-area
|
||||
style="background-color: #f5f4f4"
|
||||
:style="{
|
||||
height: $q.screen.gt.md ? '600px' : '450px',
|
||||
}"
|
||||
>
|
||||
<div class="q-px-xl row col-12 justify-between">
|
||||
<chat-message />
|
||||
</div>
|
||||
</q-scroll-area>
|
||||
|
||||
<q-separator />
|
||||
<div class="row q-py-md q-px-lg justify-between items-center q-gutter-x-lg">
|
||||
<div>
|
||||
<q-btn flat class="col-1">
|
||||
<q-icon
|
||||
name="mdi-paperclip"
|
||||
size="20px"
|
||||
style="transform: rotate(-125deg)"
|
||||
color="grey"
|
||||
/>
|
||||
</q-btn>
|
||||
</div>
|
||||
|
||||
<div class="col-grow">
|
||||
<q-input
|
||||
@keydown.enter.prevent="
|
||||
() => {
|
||||
if (store.currentIssue) {
|
||||
store.sendMessage(content, store.currentIssue);
|
||||
content = '';
|
||||
}
|
||||
}
|
||||
"
|
||||
outlined
|
||||
dense
|
||||
placeholder="Aa"
|
||||
v-model="content"
|
||||
id="message"
|
||||
>
|
||||
</q-input>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<q-btn
|
||||
@click="
|
||||
() => {
|
||||
if (store.currentIssue) {
|
||||
store.sendMessage(content, store.currentIssue);
|
||||
content = '';
|
||||
}
|
||||
}
|
||||
"
|
||||
flat
|
||||
class="col-2"
|
||||
style="color: #009789"
|
||||
label="ส่งข้อความ"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
114
src/modules/00_support/components/NewIssue.vue
Normal file
114
src/modules/00_support/components/NewIssue.vue
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useSupportStore } from "@/modules/00_support/store/Main";
|
||||
|
||||
const store = useSupportStore();
|
||||
|
||||
const isOpen = ref<boolean>(false);
|
||||
const title = ref<string>("");
|
||||
const categoryId = ref<string>("");
|
||||
|
||||
const options = ref<{ label: string; value: string }[]>([]);
|
||||
|
||||
onMounted(async () => {
|
||||
await store.fetchIssueCategory();
|
||||
if (store.issueCategory) {
|
||||
options.value = store.issueCategory?.result.map((v) => ({
|
||||
label: v.name,
|
||||
value: v.id,
|
||||
}));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div @click="isOpen = true" class="col-10 row items-center q-pa-md new">
|
||||
<div class="noactive-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>
|
||||
|
||||
<!-- Dialog -->
|
||||
<q-dialog v-model="isOpen">
|
||||
<q-card class="q-pa-md" style="width: 100%">
|
||||
<q-card-section class="row items-center">
|
||||
<div class="text-h6">เพิ่มเรื่องปรึกษาใหม่</div>
|
||||
<q-space />
|
||||
<q-btn
|
||||
icon="close"
|
||||
unelevated
|
||||
round
|
||||
dense
|
||||
v-close-popup
|
||||
style="color: #ff8080; background-color: #ffdede"
|
||||
/>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-gutter-x-md row">
|
||||
<q-select
|
||||
class="col-3"
|
||||
outlined
|
||||
dense
|
||||
emit-value
|
||||
map-options
|
||||
v-model="categoryId"
|
||||
:options="options"
|
||||
option-value="value"
|
||||
option-label="label"
|
||||
label="หมวดหมู่"
|
||||
/>
|
||||
<q-input
|
||||
class="col-grow"
|
||||
outlined
|
||||
dense
|
||||
v-model="title"
|
||||
label="ชื่อเรื่อง"
|
||||
/>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right">
|
||||
<q-btn flat v-close-popup label="ยกเลิก" text-color="primary" />
|
||||
<q-btn
|
||||
@click="
|
||||
async () => {
|
||||
await store.newIssue(title, categoryId);
|
||||
isOpen = false;
|
||||
}
|
||||
"
|
||||
label="บันทึก"
|
||||
color="primary"
|
||||
text-color="white"
|
||||
/>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</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>
|
||||
|
|
@ -1,30 +1,22 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { onMounted, onUnmounted, ref } from "vue";
|
||||
import { useSupportStore } from "@/modules/00_support/store/Main";
|
||||
import FormChat from "@/modules/00_support/components/FormChat.vue";
|
||||
import NewIssue from "@/modules/00_support/components/NewIssue.vue";
|
||||
|
||||
const search = ref("");
|
||||
const store = useSupportStore();
|
||||
|
||||
const items = ref<any>([
|
||||
{
|
||||
icon: "mdi-account-settings-outline",
|
||||
title: "ข้อมูลหลัก",
|
||||
},
|
||||
{
|
||||
icon: "mdi-account-group-outline",
|
||||
title: "โครงสร้างอัตรากำลัง",
|
||||
},
|
||||
{
|
||||
icon: "mdi-file-account-outline",
|
||||
title: "ทะเบียนประวัติ",
|
||||
},
|
||||
{
|
||||
icon: "mdi-magnify",
|
||||
title: "สรรหา",
|
||||
},
|
||||
{
|
||||
icon: "mdi-account-check",
|
||||
title: "บรรจุแต่งตั้ง",
|
||||
},
|
||||
]);
|
||||
const search = ref<string>("");
|
||||
const icon = ref<string>("mdi-account-check");
|
||||
|
||||
onUnmounted(async () => {
|
||||
store.socket.disconnect();
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
store.socket.connect();
|
||||
store.fetchIssue();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -35,117 +27,118 @@ const items = ref<any>([
|
|||
<q-card bordered>
|
||||
<div class="row">
|
||||
<!-- Left -->
|
||||
<div class="col-3">
|
||||
<div class="q-px-md q-py-md">
|
||||
<q-input
|
||||
rounded
|
||||
outlined
|
||||
dense
|
||||
placeholder="ค้นหา"
|
||||
debounce="300"
|
||||
v-model="search"
|
||||
id="inputSearch"
|
||||
>
|
||||
<template v-slot:prepend
|
||||
><q-icon name="mdi-magnify" class="pointer" />
|
||||
</template>
|
||||
</q-input>
|
||||
<div class="col-xs-12 col-sm-4 col-md-3">
|
||||
<div class="q-pt-md bg-white">
|
||||
<div class="q-px-md q-pb-md">
|
||||
<q-input
|
||||
rounded
|
||||
outlined
|
||||
dense
|
||||
placeholder="ค้นหา"
|
||||
debounce="300"
|
||||
v-model="search"
|
||||
id="inputSearch"
|
||||
>
|
||||
<template v-slot:prepend
|
||||
><q-icon name="mdi-magnify" class="pointer" />
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
<q-separator inset />
|
||||
<div>
|
||||
<new-issue />
|
||||
</div>
|
||||
<q-separator inset />
|
||||
</div>
|
||||
|
||||
<div v-for="(item, j) in items">
|
||||
<div
|
||||
class="noactive row q-py-md justify-between items-center q-px-md"
|
||||
>
|
||||
<div class="col-11 row items-center">
|
||||
<div class="noactive-avatar">
|
||||
<q-avatar color="grey-2" text-color="white" size="40px">
|
||||
<q-icon :name="item.icon" size="24px" color="teal" />
|
||||
</q-avatar>
|
||||
<!-- List Issue -->
|
||||
<div
|
||||
class="scrollable-content"
|
||||
:style="{
|
||||
'max-height': $q.screen.gt.md ? '600px' : '450px',
|
||||
}"
|
||||
>
|
||||
<div v-for="(item, index) in store.issue?.result" :key="index">
|
||||
<div
|
||||
@click="
|
||||
async () => {
|
||||
store.currentIssue = item.id;
|
||||
store.currentTitle = item.title;
|
||||
store.issue
|
||||
? (store.issue.result = store.issue.result.map(
|
||||
(v) => {
|
||||
if (v.id === item.id) {
|
||||
v.unreadCount = 0;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
))
|
||||
: '';
|
||||
await store.fetchMessageStatus(item.id);
|
||||
await store.fetchMessage(item.id);
|
||||
}
|
||||
"
|
||||
:class="{ active: store.currentIssue === item.id }"
|
||||
class="noactive row q-py-md justify-between items-center q-px-md"
|
||||
>
|
||||
<div class="col-10 row items-center">
|
||||
<div class="noactive-avatar">
|
||||
<q-avatar color="grey-2" text-color="white" size="40px">
|
||||
<q-icon :name="icon" size="24px" color="primary" />
|
||||
</q-avatar>
|
||||
</div>
|
||||
<div class="col column q-ml-md">
|
||||
<span class="col text-weight-bold line-ellipsis">
|
||||
{{ item.title }}
|
||||
</span>
|
||||
<span
|
||||
class="col text-caption line-ellipsis"
|
||||
:class="{
|
||||
'text-weight-bold': item.unreadCount > 0,
|
||||
'text-grey-8': item.unreadCount > 0,
|
||||
'text-grey': item.unreadCount === 0,
|
||||
}"
|
||||
>{{ item.lastMessage }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col column q-ml-md">
|
||||
<span class="col text-weight-bold">
|
||||
{{ item.title }}
|
||||
</span>
|
||||
<!-- <span class="col">ขอสอบถาม</span> -->
|
||||
<div class="col-2 items-center text-right">
|
||||
<q-icon
|
||||
v-if="item.lastMessage?.length === 0"
|
||||
name="mdi-send"
|
||||
size="xs"
|
||||
color="primary"
|
||||
/>
|
||||
<div v-else class="column">
|
||||
<span class="col text-caption text-grey">20 s</span>
|
||||
<div class="col">
|
||||
<q-badge
|
||||
v-if="item.unreadCount > 0"
|
||||
rounded
|
||||
color="negative"
|
||||
text-color="white"
|
||||
:label="item.unreadCount"
|
||||
/>
|
||||
<q-icon
|
||||
v-else
|
||||
name="mdi-check-all"
|
||||
size="xs"
|
||||
color="grey"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-1">
|
||||
<q-icon name="mdi-send" size="xs" color="teal" />
|
||||
</div>
|
||||
<q-separator inset />
|
||||
</div>
|
||||
<q-separator v-if="j < items.length - 1" inset />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<q-separator vertical />
|
||||
|
||||
<!-- Right -->
|
||||
<div class="col-grow">
|
||||
<div class="row q-py-md q-px-lg justify-between items-center">
|
||||
<div class="col row">
|
||||
<div style="border-radius: 50%; border: 1px solid teal">
|
||||
<q-avatar color="teal-1" text-color="white" size="40px">
|
||||
<q-icon :name="items[0].icon" size="24px" color="teal" />
|
||||
</q-avatar>
|
||||
</div>
|
||||
<div class="col column q-ml-md">
|
||||
<span class="col text-weight-bold">
|
||||
{{ items[0].title }}
|
||||
</span>
|
||||
<span>
|
||||
<q-icon name="mdi-circle" size="10px" color="green" />
|
||||
ออนไลน์
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-2 text-right q-gutter-x-md">
|
||||
<q-icon
|
||||
class="col"
|
||||
name="mdi-video-outline"
|
||||
size="24px"
|
||||
color="teal"
|
||||
/>
|
||||
<q-icon
|
||||
class="col"
|
||||
name="mdi-information-outline"
|
||||
size="24px"
|
||||
color="grey"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<q-separator />
|
||||
<div class="row" style="background-color: #f5f4f4; height: 400px;">asas</div>
|
||||
<q-separator />
|
||||
<div class="row q-py-md q-px-lg justify-between items-center">
|
||||
<div class="col-1 text-left">
|
||||
<q-btn flat>
|
||||
<q-icon
|
||||
name="mdi-paperclip"
|
||||
size="20px"
|
||||
style="transform: rotate(-125deg)"
|
||||
color="grey"
|
||||
/>
|
||||
</q-btn>
|
||||
</div>
|
||||
|
||||
<div class="col-grow">
|
||||
<q-input
|
||||
outlined
|
||||
dense
|
||||
placeholder="Aa"
|
||||
v-model="search"
|
||||
id="message"
|
||||
>
|
||||
</q-input>
|
||||
</div>
|
||||
|
||||
<div class="col-2">
|
||||
<q-btn flat style="color: #009789" label="ส่งข้อความ" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="$q.screen.gt.xs" class="col-grow">
|
||||
<form-chat v-if="store.currentIssue.length !== 0" :icon="icon" />
|
||||
</div>
|
||||
</div>
|
||||
</q-card>
|
||||
|
|
@ -156,14 +149,16 @@ const items = ref<any>([
|
|||
|
||||
<style scoped lang="scss">
|
||||
.noactive {
|
||||
transition: 0.1s ease;
|
||||
transition: 0.1s ease all;
|
||||
opacity: 1;
|
||||
cursor: pointer;
|
||||
border-left: 5px solid transparent;
|
||||
|
||||
&:hover {
|
||||
background: #ebf9f7;
|
||||
|
||||
.noactive-avatar {
|
||||
transition: 0.1s ease all;
|
||||
border-radius: 50%;
|
||||
border: 1px solid teal;
|
||||
}
|
||||
|
|
@ -174,4 +169,44 @@ const items = ref<any>([
|
|||
border-radius: 50%;
|
||||
border: 1px solid #f5f4f4;
|
||||
}
|
||||
|
||||
.active {
|
||||
background: #ebf9f7;
|
||||
border-left: 5px solid #03a898;
|
||||
& {
|
||||
.noactive-avatar {
|
||||
border-radius: 50%;
|
||||
border: 1px solid #03a898;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.line-ellipsis {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
.scrollable-content {
|
||||
overflow: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.scrollable-content::-webkit-scrollbar {
|
||||
width: 0.3em;
|
||||
}
|
||||
|
||||
.scrollable-content::-webkit-scrollbar-track {
|
||||
background-color: #f5f4f4;
|
||||
}
|
||||
|
||||
.scrollable-content::-webkit-scrollbar-thumb {
|
||||
background-color: #c5c3c2;
|
||||
}
|
||||
|
||||
.scrollable-content::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #7a7b7b;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue