ระบบจัดการประเภทของปัญหา
This commit is contained in:
parent
c6b9236a04
commit
b4c4325cd1
11 changed files with 813 additions and 77 deletions
|
|
@ -1,14 +1,22 @@
|
||||||
import env from "../index";
|
import env from "../index";
|
||||||
|
|
||||||
export const supportIssue = `${env.API_SUPPORT_URI}/issue`;
|
export const supportIssue = `${env.API_SUPPORT_URI}/issue`;
|
||||||
|
export const supportCategory = `${env.API_SUPPORT_URI}/Issue-category`;
|
||||||
|
export const supportCategoryAction = (id: string) =>
|
||||||
|
`${env.API_SUPPORT_URI}/Issue-category/${id}`;
|
||||||
|
export const supportIssueChangeStatus = (id: string) =>
|
||||||
|
`${env.API_SUPPORT_URI}/issue/${id}`;
|
||||||
export const supportMessage = (id: string) =>
|
export const supportMessage = (id: string) =>
|
||||||
`${env.API_SUPPORT_URI}/issue/${id}/message`;
|
`${env.API_SUPPORT_URI}/issue/${id}/message`;
|
||||||
|
|
||||||
export const supportMessageStatus = (id: string) =>
|
export const supportMessageStatus = (id: string) =>
|
||||||
`${env.API_SUPPORT_URI}/issue/${id}/message-status?issueId`;
|
`${env.API_SUPPORT_URI}/issue/${id}/message-status`;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
supportIssue,
|
supportIssue,
|
||||||
|
supportCategory,
|
||||||
supportMessage,
|
supportMessage,
|
||||||
supportMessageStatus,
|
supportMessageStatus,
|
||||||
|
supportIssueChangeStatus,
|
||||||
|
supportCategoryAction,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
122
src/modules/00_support/components/DialogStatus.vue
Normal file
122
src/modules/00_support/components/DialogStatus.vue
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useSupportStore } from "@/modules/00_support/store/Main";
|
||||||
|
const store = useSupportStore();
|
||||||
|
const open = ref<boolean>(false);
|
||||||
|
const statusIssue: ("new" | "ongoing" | "resolved")[] = [
|
||||||
|
"new",
|
||||||
|
"ongoing",
|
||||||
|
"resolved",
|
||||||
|
];
|
||||||
|
|
||||||
|
const status = ref<"new" | "ongoing" | "resolved">(store.correntStatusIssue);
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
issueId: String,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<q-btn icon="more_vert" dense flat size="11px" @click.stop>
|
||||||
|
<q-menu transition-show="jump-down" transition-hide="jump-up">
|
||||||
|
<q-list style="min-width: 100px">
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
v-close-popup
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
console.log('เเก้ไข ' + issueId);
|
||||||
|
open = true;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<q-item-section>
|
||||||
|
<div class="row items-center white">
|
||||||
|
<q-icon name="o_edit" color="positive" />
|
||||||
|
<span class="q-ml-sm">แก้ไขสถานะ</span>
|
||||||
|
</div></q-item-section
|
||||||
|
>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-menu>
|
||||||
|
</q-btn>
|
||||||
|
|
||||||
|
<q-dialog v-model="open" persistent>
|
||||||
|
<q-card>
|
||||||
|
<q-card-section class="row items-center">
|
||||||
|
<div class="flex items-center" style="flex-wrap: nowrap">
|
||||||
|
<div>
|
||||||
|
<h6 class="q-my-none">แก้ไขสถานะ</h6>
|
||||||
|
|
||||||
|
<q-btn-dropdown
|
||||||
|
v-if="store.currentIssue"
|
||||||
|
:label="
|
||||||
|
store.correntStatusIssue == 'new'
|
||||||
|
? 'ปัญหาใหม่'
|
||||||
|
: store.correntStatusIssue == 'ongoing'
|
||||||
|
? 'กำลังดำเนินการ'
|
||||||
|
: 'เสร็จสิ้น'
|
||||||
|
"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<q-list v-for="itemStatusIssue in statusIssue">
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
v-close-popup
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
status = itemStatusIssue;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>{{
|
||||||
|
itemStatusIssue == "new"
|
||||||
|
? "ปัญหาใหม่"
|
||||||
|
: itemStatusIssue == "ongoing"
|
||||||
|
? "กำลังดำเนินการ"
|
||||||
|
: "เสร็จสิ้น"
|
||||||
|
}}</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-btn-dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-card-actions align="right">
|
||||||
|
<q-btn
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
console.log('ยกเลิกการลบ');
|
||||||
|
}
|
||||||
|
"
|
||||||
|
label="ยกเลิก"
|
||||||
|
flat
|
||||||
|
v-close-popup
|
||||||
|
id="dialogDeleteClose"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-btn
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
store.ChangeStatusIssue(
|
||||||
|
store.currentIssue,
|
||||||
|
store.currentTitle,
|
||||||
|
store.currentCategoryId,
|
||||||
|
status
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"
|
||||||
|
color="primary"
|
||||||
|
v-close-popup
|
||||||
|
label="ยืนยัน"
|
||||||
|
id="dialogDeleteConfirm"
|
||||||
|
/>
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import "moment/dist/locale/th";
|
import "moment/dist/locale/th";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
import DialogStatus from "@/modules/00_support/components/DialogStatus.vue";
|
||||||
import { ref, onMounted } from "vue";
|
import { ref, onMounted } 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";
|
||||||
|
|
@ -15,11 +16,11 @@ const { scrollContainer } = storeToRefs(store);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await store.fetchIssue();
|
await store.fetchIssue();
|
||||||
totalPageIssue.value = Math.ceil(store.currentTotalIssue || 0 / 6);
|
totalPageIssue.value = Math.ceil((store.currentTotalIssue || 0) / 6);
|
||||||
});
|
});
|
||||||
|
|
||||||
const onLoad = (async (_: any, done: any) => {
|
const onLoad = (async (_: any, done: any) => {
|
||||||
const totalPages = Math.ceil(store.currentTotalMessage || 0 / 30);
|
const totalPages = Math.ceil((store.currentTotalMessage || 1) / 30);
|
||||||
if (store.currentPage && totalPages > store.currentPage) {
|
if (store.currentPage && totalPages > store.currentPage) {
|
||||||
await store.loadMessage(store.currentPage + 1);
|
await store.loadMessage(store.currentPage + 1);
|
||||||
done();
|
done();
|
||||||
|
|
@ -28,7 +29,15 @@ const onLoad = (async (_: any, done: any) => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<p class="text-h6 text-weight-medium">ถาม - ตอบ</p>
|
<div class="flex q-mt-md">
|
||||||
|
<p class="text-h6 text-weight-medium align-center">ถาม - ตอบ</p>
|
||||||
|
<q-space />
|
||||||
|
<p>
|
||||||
|
<router-link to="/category"
|
||||||
|
><q-btn dense size="16px">จัดการประเภทของปัญหา</q-btn></router-link
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="i1 bg-white align-center">
|
<div class="i1 bg-white align-center">
|
||||||
|
|
@ -54,66 +63,154 @@ const onLoad = (async (_: any, done: any) => {
|
||||||
<q-avatar>
|
<q-avatar>
|
||||||
<img src="https://cdn.quasar.dev/img/avatar3.jpg" />
|
<img src="https://cdn.quasar.dev/img/avatar3.jpg" />
|
||||||
</q-avatar>
|
</q-avatar>
|
||||||
<q-item-section class="q-pl-sm">
|
<q-item-section v-if="store.currentIssue" class="q-pl-sm">
|
||||||
<q-item-label>{{ store.currentTitle }}</q-item-label>
|
<q-item-label
|
||||||
|
>{{ store.currentTitle }}
|
||||||
|
<q-badge color="blue">
|
||||||
|
{{ store.currentCategory }}
|
||||||
|
</q-badge></q-item-label
|
||||||
|
>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
|
|
||||||
<q-space />
|
<q-space />
|
||||||
|
|
||||||
|
<div v-if="store.currentIssue">
|
||||||
|
<q-badge
|
||||||
|
color="white"
|
||||||
|
text-color="black"
|
||||||
|
:label="
|
||||||
|
store.correntStatusIssue == 'new'
|
||||||
|
? 'ปัญหาใหม่'
|
||||||
|
: store.correntStatusIssue == 'ongoing'
|
||||||
|
? 'กำลังดำเนินการ'
|
||||||
|
: 'เสร็จสิ้น'
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<dialog-status />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <q-btn-dropdown
|
||||||
|
v-if="store.currentIssue"
|
||||||
|
:label="
|
||||||
|
store.correntStatusIssue == 'new'
|
||||||
|
? 'ปัญหาใหม่'
|
||||||
|
: store.correntStatusIssue == 'ongoing'
|
||||||
|
? 'กำลังดำเนินการ'
|
||||||
|
: 'เสร็จสิ้น'
|
||||||
|
"
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
color="positive"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
|
||||||
|
<q-list v-for="itemStatusIssue in statusIssue">
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
v-close-popup
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
store.ChangeStatusIssue(
|
||||||
|
store.currentIssue,
|
||||||
|
store.currentTitle,
|
||||||
|
store.currentCategoryId,
|
||||||
|
itemStatusIssue
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>{{
|
||||||
|
itemStatusIssue == "new"
|
||||||
|
? "ปัญหาใหม่"
|
||||||
|
: itemStatusIssue == "ongoing"
|
||||||
|
? "กำลังดำเนินการ"
|
||||||
|
: "เสร็จสิ้น"
|
||||||
|
}}</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-btn-dropdown> -->
|
||||||
|
|
||||||
<q-btn flat round dense icon="o_info" text-color="grey" />
|
<q-btn flat round dense icon="o_info" text-color="grey" />
|
||||||
</q-toolbar>
|
</q-toolbar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="i3 bg-white">
|
<div class="i3 bg-white">
|
||||||
<q-list class="text-primary">
|
<div v-for="(item, index) in store.issue?.result" :key="index">
|
||||||
<div v-for="data in store.issue?.result">
|
<div
|
||||||
<q-item
|
@click="
|
||||||
clickable
|
async () => {
|
||||||
v-ripple
|
store.currentIssue = item.id;
|
||||||
:active="store.currentIssue === data.id"
|
store.currentTitle = item.title;
|
||||||
@click="
|
store.correntStatusIssue = item.status;
|
||||||
store.currentIssue = data.id;
|
store.currentCategory = item.category.name;
|
||||||
store.currentTitle = data.title;
|
store.issue
|
||||||
store.fetchMessage(data.id);
|
? (store.issue.result = store.issue.result.map((v) => {
|
||||||
store.fetchMessageStatus(data.id);
|
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-sm 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">
|
||||||
|
<img src="https://cdn.quasar.dev/img/avatar1.jpg" />
|
||||||
|
</q-avatar>
|
||||||
|
</div>
|
||||||
|
<div class="col column q-ml-md">
|
||||||
|
<span class="text-long col text-weight-bold line-ellipsis">
|
||||||
|
{{ item.title }}
|
||||||
|
</span>
|
||||||
|
|
||||||
if (store.issue) {
|
<span
|
||||||
store.issue.result = store.issue.result.map((v) => {
|
class="col text-caption line-ellipsis"
|
||||||
if (v.id === data.id) v.unreadCount = 0;
|
:class="{
|
||||||
return v;
|
'text-weight-bold': item.unreadCount > 0,
|
||||||
});
|
'text-grey-8': item.unreadCount > 0,
|
||||||
}
|
'text-grey': item.unreadCount === 0,
|
||||||
"
|
}"
|
||||||
active-class="my-menu-link"
|
>{{ item.lastMessage }}</span
|
||||||
>
|
>
|
||||||
<q-avatar>
|
</div>
|
||||||
<img src="https://cdn.quasar.dev/img/avatar1.jpg" />
|
<div></div>
|
||||||
</q-avatar>
|
</div>
|
||||||
<q-item-section class="q-pl-sm">
|
|
||||||
<q-item-label>{{ data.title }}</q-item-label>
|
<div class="col-2 items-center text-right">
|
||||||
<q-item-label class="flex" caption>
|
<q-icon
|
||||||
{{ data.lastMessage }}
|
v-if="item.lastMessage?.length === 0"
|
||||||
<q-space />
|
name="mdi-send"
|
||||||
<q-icon
|
size="xs"
|
||||||
v-if="data.lastMessage?.length === 0"
|
color="primary"
|
||||||
color="green"
|
/>
|
||||||
size="18px"
|
|
||||||
name="mdi-send"
|
<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"
|
||||||
/>
|
/>
|
||||||
<div v-else>
|
<q-icon v-else name="mdi-check-all" size="xs" color="grey" />
|
||||||
<q-badge
|
</div>
|
||||||
v-if="data.unreadCount > 0"
|
</div>
|
||||||
class=""
|
</div>
|
||||||
rounded
|
|
||||||
color="red"
|
|
||||||
:label="data.unreadCount"
|
|
||||||
/>
|
|
||||||
<q-icon v-else size="18px" name="done_all" />
|
|
||||||
</div>
|
|
||||||
</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-separator inset />
|
|
||||||
</div>
|
</div>
|
||||||
</q-list>
|
|
||||||
|
<q-separator inset />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="i4 bg-grey-3" v-if="store.currentIssue">
|
<div class="i4 bg-grey-3" v-if="store.currentIssue">
|
||||||
|
|
@ -198,29 +295,35 @@ const onLoad = (async (_: any, done: any) => {
|
||||||
</div> -->
|
</div> -->
|
||||||
<div class="input-chat">
|
<div class="input-chat">
|
||||||
<q-input
|
<q-input
|
||||||
dense
|
|
||||||
class="q-pa-xs"
|
|
||||||
standout="bg-teal text-white"
|
|
||||||
label="Aa"
|
|
||||||
v-model="content"
|
|
||||||
@keydown.enter.prevent="
|
@keydown.enter.prevent="
|
||||||
() => {
|
() => {
|
||||||
store.sendMessage(content, store.currentIssue);
|
if (store.currentIssue) {
|
||||||
content = '';
|
store.sendMessage(content, store.currentIssue);
|
||||||
|
content = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
/>
|
outlined
|
||||||
|
dense
|
||||||
|
placeholder="Aa"
|
||||||
|
v-model="content"
|
||||||
|
id="message"
|
||||||
|
>
|
||||||
|
</q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-chat">
|
<div class="btn-chat">
|
||||||
<q-btn
|
<q-btn
|
||||||
@click="
|
@click="
|
||||||
() => {
|
() => {
|
||||||
store.sendMessage(content, store.currentIssue);
|
if (store.currentIssue) {
|
||||||
content = '';
|
store.sendMessage(content, store.currentIssue);
|
||||||
|
content = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
flat
|
flat
|
||||||
color="primary"
|
class="col-2"
|
||||||
|
style="color: #009789"
|
||||||
label="ส่งข้อความ"
|
label="ส่งข้อความ"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -229,9 +332,24 @@ const onLoad = (async (_: any, done: any) => {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.scroll {
|
.text-long {
|
||||||
overflow-y: auto;
|
width: 100px;
|
||||||
|
display: inline-block;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
.active {
|
||||||
|
background: #ebf9f7;
|
||||||
|
border-left: 5px solid #03a898;
|
||||||
|
& {
|
||||||
|
.noactive-avatar {
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid #03a898;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.align-center {
|
.align-center {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
122
src/modules/00_support/components/ItemAction.vue
Normal file
122
src/modules/00_support/components/ItemAction.vue
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useSupportStore } from "@/modules/00_support/store/Main";
|
||||||
|
const store = useSupportStore();
|
||||||
|
const open = ref<boolean>(false);
|
||||||
|
const statusIssue: ("new" | "ongoing" | "resolved")[] = [
|
||||||
|
"new",
|
||||||
|
"ongoing",
|
||||||
|
"resolved",
|
||||||
|
];
|
||||||
|
|
||||||
|
const status = ref<"new" | "ongoing" | "resolved">(store.correntStatusIssue);
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
issueId: String,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<q-btn icon="more_vert" dense flat size="11px" @click.stop>
|
||||||
|
<q-menu transition-show="jump-down" transition-hide="jump-up">
|
||||||
|
<q-list style="min-width: 100px">
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
v-close-popup
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
console.log('เเก้ไข ' + issueId);
|
||||||
|
open = true;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<q-item-section>
|
||||||
|
<div class="row items-center white">
|
||||||
|
<q-icon name="o_edit" color="positive" />
|
||||||
|
<span class="q-ml-sm">แก้ไขสถานะ</span>
|
||||||
|
</div></q-item-section
|
||||||
|
>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-menu>
|
||||||
|
</q-btn>
|
||||||
|
|
||||||
|
<q-dialog v-model="open" persistent>
|
||||||
|
<q-card>
|
||||||
|
<q-card-section class="row items-center">
|
||||||
|
<div class="flex items-center" style="flex-wrap: nowrap">
|
||||||
|
<div>
|
||||||
|
<h6 class="q-my-none">แก้ไขสถานะ</h6>
|
||||||
|
|
||||||
|
<q-btn-dropdown
|
||||||
|
v-if="store.currentIssue"
|
||||||
|
:label="
|
||||||
|
store.correntStatusIssue == 'new'
|
||||||
|
? 'ปัญหาใหม่'
|
||||||
|
: store.correntStatusIssue == 'ongoing'
|
||||||
|
? 'กำลังดำเนินการ'
|
||||||
|
: 'เสร็จสิ้น'
|
||||||
|
"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<q-list v-for="itemStatusIssue in statusIssue">
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
v-close-popup
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
status = itemStatusIssue;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>{{
|
||||||
|
itemStatusIssue == "new"
|
||||||
|
? "ปัญหาใหม่"
|
||||||
|
: itemStatusIssue == "ongoing"
|
||||||
|
? "กำลังดำเนินการ"
|
||||||
|
: "เสร็จสิ้น"
|
||||||
|
}}</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-btn-dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-card-actions align="right">
|
||||||
|
<q-btn
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
console.log('ยกเลิกการลบ');
|
||||||
|
}
|
||||||
|
"
|
||||||
|
label="ยกเลิก"
|
||||||
|
flat
|
||||||
|
v-close-popup
|
||||||
|
id="dialogDeleteClose"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-btn
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
store.ChangeStatusIssue(
|
||||||
|
store.currentIssue,
|
||||||
|
store.currentTitle,
|
||||||
|
store.currentCategoryId,
|
||||||
|
status
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"
|
||||||
|
color="primary"
|
||||||
|
v-close-popup
|
||||||
|
label="ยืนยัน"
|
||||||
|
id="dialogDeleteConfirm"
|
||||||
|
/>
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
103
src/modules/00_support/components/category/DialogCategory.vue
Normal file
103
src/modules/00_support/components/category/DialogCategory.vue
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useSupportStore } from "@/modules/00_support/store/Main";
|
||||||
|
|
||||||
|
const store = useSupportStore();
|
||||||
|
|
||||||
|
const [open] = defineModel<Boolean>();
|
||||||
|
const input = defineModel<string>("input");
|
||||||
|
const prop = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
open: boolean;
|
||||||
|
status: string;
|
||||||
|
category: string;
|
||||||
|
input: string;
|
||||||
|
}>(),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
function controlAction(
|
||||||
|
categoryId: string = "",
|
||||||
|
statusAction?: string,
|
||||||
|
name: string = ""
|
||||||
|
) {
|
||||||
|
if (statusAction == "add") store.newCategory(name);
|
||||||
|
if (statusAction == "edit") store.editCategory(categoryId, name);
|
||||||
|
if (statusAction == "delete") store.deleteCategory(categoryId);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<q-dialog v-model="prop.open" persistent>
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="flex items-center" style="flex-wrap: nowrap">
|
||||||
|
<div class="q-pa-sm">
|
||||||
|
<h6 v-if="prop.status == 'add'" class="q-my-none">
|
||||||
|
เพิ่มประเภทของปัญหา
|
||||||
|
</h6>
|
||||||
|
<h6 v-if="prop.status == 'edit'" class="q-my-none">
|
||||||
|
แก้ไขประเภทของปัญหา
|
||||||
|
</h6>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="prop.status == 'delete'"
|
||||||
|
style="border-radius: 50%"
|
||||||
|
class="q-pa-sm"
|
||||||
|
>
|
||||||
|
<q-icon
|
||||||
|
name="mdi-trash-can-outline"
|
||||||
|
color="negative"
|
||||||
|
size="2.5rem"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="prop.status == 'delete'">
|
||||||
|
<h6 class="q-my-none">ยืนยันการลบข้อมูล</h6>
|
||||||
|
<p class="q-my-none">ต้องการยืนยันการลบข้อมูลนี้หรือไม่</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-card-section
|
||||||
|
style="width: 400px"
|
||||||
|
class="items-center"
|
||||||
|
v-if="prop.status !== 'delete'"
|
||||||
|
>
|
||||||
|
<div style="flex-wrap: ">
|
||||||
|
<div>
|
||||||
|
<q-input dense outlined v-model="input" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-card-actions align="right">
|
||||||
|
<q-btn
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
open = false;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
label="ยกเลิก"
|
||||||
|
flat
|
||||||
|
v-close-popup
|
||||||
|
id="dialogDeleteClose"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-btn
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
controlAction(prop.category, prop.status, input);
|
||||||
|
open = false;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
:color="prop.status == 'delete' ? 'negative' : 'primary'"
|
||||||
|
v-close-popup
|
||||||
|
label="ยืนยัน"
|
||||||
|
id="dialogDeleteConfirm"
|
||||||
|
/>
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
92
src/modules/00_support/components/category/TableCategory.vue
Normal file
92
src/modules/00_support/components/category/TableCategory.vue
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useSupportStore } from "@/modules/00_support/store/Main";
|
||||||
|
import DialogCategory from "@/modules/00_support/components/category/DialogCategory.vue";
|
||||||
|
import { onMounted, ref } from "vue";
|
||||||
|
const store = useSupportStore();
|
||||||
|
const open = ref<boolean>(false);
|
||||||
|
const currentName = ref<string>("");
|
||||||
|
const status = ref<"add" | "edit" | "delete">("add");
|
||||||
|
const categoryId = ref<string>("");
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await store.fetchCategory();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex q-my-md">
|
||||||
|
<p class="text-h6 text-weight-medium align-center">จัดการประเภคของปัญหา</p>
|
||||||
|
<q-space />
|
||||||
|
<q-btn
|
||||||
|
dense
|
||||||
|
size="16px"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
open = true;
|
||||||
|
status = 'add';
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>เพิ่มประเภทของปัญหา</q-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<q-table
|
||||||
|
:rows="store.rowsCategory?.result"
|
||||||
|
:columns="store.columnsCategory"
|
||||||
|
row-key="name"
|
||||||
|
>
|
||||||
|
<template v-slot:body-cell-actions="data">
|
||||||
|
<q-td>
|
||||||
|
<div>
|
||||||
|
<q-btn
|
||||||
|
class="q-ma-sm"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
id="listViewFolderEdit"
|
||||||
|
icon="o_edit"
|
||||||
|
color="positive"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
open = true;
|
||||||
|
status = 'edit';
|
||||||
|
categoryId = data.row.id;
|
||||||
|
currentName = data.row.name;
|
||||||
|
|
||||||
|
console.log(currentName);
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-btn
|
||||||
|
class="q-ma-sm"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
id="listViewFolderDelete"
|
||||||
|
color="negative"
|
||||||
|
icon="mdi-trash-can-outline"
|
||||||
|
:data-testid="data.row.name"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
open = true;
|
||||||
|
status = 'delete';
|
||||||
|
categoryId = data.row.id;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<dialog-category
|
||||||
|
v-model.open="open"
|
||||||
|
:open="open"
|
||||||
|
:status="status"
|
||||||
|
:category="categoryId"
|
||||||
|
:input="currentName"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
|
@ -30,7 +30,7 @@ export interface SupportIssue {
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
title: string;
|
title: string;
|
||||||
status: string;
|
status: "new" | "ongoing" | "resolved";
|
||||||
category: SupportIssueCategory;
|
category: SupportIssueCategory;
|
||||||
unreadCount: number;
|
unreadCount: number;
|
||||||
lastMessage: string;
|
lastMessage: string;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
const supportMain = () => import("@/modules/00_support/views/MainPage.vue");
|
const supportMain = () => import("@/modules/00_support/views/MainPage.vue");
|
||||||
|
const supportCategory = () =>
|
||||||
|
import("@/modules/00_support/views/ManageCategory.vue");
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
|
|
@ -11,4 +13,14 @@ export default [
|
||||||
Role: "evaluate",
|
Role: "evaluate",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/category",
|
||||||
|
name: "supportCategory",
|
||||||
|
component: supportCategory,
|
||||||
|
meta: {
|
||||||
|
Auth: true,
|
||||||
|
Key: [1.1],
|
||||||
|
Role: "evaluate",
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,10 @@ import type {
|
||||||
SupportIssueResponse,
|
SupportIssueResponse,
|
||||||
SupportStatusUser,
|
SupportStatusUser,
|
||||||
SupportMessageStatus,
|
SupportMessageStatus,
|
||||||
|
SupportIssueCategory,
|
||||||
} 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 { useQuasar } from "quasar";
|
import { useQuasar, type QTableProps } from "quasar";
|
||||||
|
|
||||||
export const useSupportStore = defineStore("supportServiceStore", () => {
|
export const useSupportStore = defineStore("supportServiceStore", () => {
|
||||||
const { showLoader, hideLoader, messageError } = useCounterMixin();
|
const { showLoader, hideLoader, messageError } = useCounterMixin();
|
||||||
|
|
@ -24,11 +25,40 @@ export const useSupportStore = defineStore("supportServiceStore", () => {
|
||||||
const statusUser = ref<SupportStatusUser[]>([]);
|
const statusUser = ref<SupportStatusUser[]>([]);
|
||||||
const currentIssue = ref<string>("");
|
const currentIssue = ref<string>("");
|
||||||
const currentTitle = ref<string>("");
|
const currentTitle = ref<string>("");
|
||||||
|
const currentCategoryId = ref<string>("");
|
||||||
|
const correntStatusIssue = ref<"new" | "ongoing" | "resolved">("new");
|
||||||
const currentTotalMessage = ref<number>();
|
const currentTotalMessage = ref<number>();
|
||||||
const currentPage = ref<number>();
|
const currentPage = ref<number>();
|
||||||
const scrollContainer = ref();
|
const scrollContainer = ref();
|
||||||
const currentPageIssue = ref<number>();
|
const currentPageIssue = ref<number>();
|
||||||
const currentTotalIssue = ref<number>();
|
const currentTotalIssue = ref<number>();
|
||||||
|
const currentCategory = ref<string>();
|
||||||
|
|
||||||
|
const columnsCategory = [
|
||||||
|
{
|
||||||
|
name: "id",
|
||||||
|
label: "id",
|
||||||
|
align: "center",
|
||||||
|
field: "id",
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
align: "center",
|
||||||
|
label: "name",
|
||||||
|
field: "name",
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "actions",
|
||||||
|
align: "center",
|
||||||
|
label: "",
|
||||||
|
field: "",
|
||||||
|
},
|
||||||
|
] satisfies QTableProps["columns"];
|
||||||
|
|
||||||
|
const rowsCategory = ref<SupportIssueCategory>();
|
||||||
|
|
||||||
function scrollToEnd(position: Number = 1) {
|
function scrollToEnd(position: Number = 1) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scrollContainer.value?.setScrollPercentage("vertical", position);
|
scrollContainer.value?.setScrollPercentage("vertical", position);
|
||||||
|
|
@ -146,10 +176,37 @@ export const useSupportStore = defineStore("supportServiceStore", () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function ChangeStatusIssue(
|
||||||
|
issueId: string,
|
||||||
|
title: string,
|
||||||
|
categoryId: string,
|
||||||
|
status: "new" | "ongoing" | "resolved"
|
||||||
|
) {
|
||||||
|
showLoader();
|
||||||
|
const requestBody = {
|
||||||
|
title: title,
|
||||||
|
categoryId: categoryId,
|
||||||
|
status: status,
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await http
|
||||||
|
.patch(config.API.supportIssueChangeStatus(issueId), requestBody)
|
||||||
|
.catch((err) => {
|
||||||
|
messageError($q, err);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hideLoader();
|
||||||
|
});
|
||||||
|
if (res) {
|
||||||
|
fetchIssue();
|
||||||
|
correntStatusIssue.value = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchIssue(page: number = 1) {
|
async function fetchIssue(page: number = 1) {
|
||||||
showLoader();
|
showLoader();
|
||||||
const res = await http
|
const res = await http
|
||||||
.get(`${config.API.supportIssue}?page=${page}&&pageSize=6`)
|
.get(`${config.API.supportIssue}?page=${page}&pageSize=6`)
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
messageError($q, err);
|
messageError($q, err);
|
||||||
})
|
})
|
||||||
|
|
@ -159,29 +216,110 @@ export const useSupportStore = defineStore("supportServiceStore", () => {
|
||||||
|
|
||||||
if (res && res.data) {
|
if (res && res.data) {
|
||||||
issue.value = res.data;
|
issue.value = res.data;
|
||||||
|
|
||||||
currentPageIssue.value = res.data.page;
|
currentPageIssue.value = res.data.page;
|
||||||
currentTotalIssue.value = res.data.total;
|
currentTotalIssue.value = res.data.total;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function newCategory(name: string) {
|
||||||
|
showLoader();
|
||||||
|
const res = await http
|
||||||
|
.post(config.API.supportCategory, {
|
||||||
|
name: name,
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
messageError($q, err);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hideLoader();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
fetchCategory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchCategory() {
|
||||||
|
showLoader();
|
||||||
|
const res = await http
|
||||||
|
.get(config.API.supportCategory)
|
||||||
|
.catch((err) => {
|
||||||
|
messageError($q, err);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
messageError($q, err);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hideLoader();
|
||||||
|
});
|
||||||
|
if (res && res.data) {
|
||||||
|
rowsCategory.value = res.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteCategory(CategoryId: string) {
|
||||||
|
showLoader();
|
||||||
|
const res = await http
|
||||||
|
.delete(config.API.supportCategoryAction(CategoryId))
|
||||||
|
.catch((err) => {
|
||||||
|
messageError($q, err);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hideLoader();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
fetchCategory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function editCategory(CategoryId: string, name: string) {
|
||||||
|
showLoader();
|
||||||
|
const res = await http
|
||||||
|
.patch(config.API.supportCategoryAction(CategoryId), {
|
||||||
|
name: name,
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
messageError($q, err);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hideLoader();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
fetchCategory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
userId,
|
userId,
|
||||||
issue,
|
issue,
|
||||||
message,
|
message,
|
||||||
fetchIssue,
|
|
||||||
fetchMessage,
|
|
||||||
fetchMessageStatus,
|
|
||||||
sendMessage,
|
|
||||||
scrollToEnd,
|
|
||||||
scrollContainer,
|
scrollContainer,
|
||||||
currentIssue,
|
currentIssue,
|
||||||
currentTitle,
|
currentTitle,
|
||||||
socket,
|
socket,
|
||||||
messageStatus,
|
messageStatus,
|
||||||
loadMessage,
|
|
||||||
currentTotalMessage,
|
currentTotalMessage,
|
||||||
currentPage,
|
currentPage,
|
||||||
currentPageIssue,
|
currentPageIssue,
|
||||||
currentTotalIssue,
|
currentTotalIssue,
|
||||||
|
currentCategoryId,
|
||||||
|
correntStatusIssue,
|
||||||
|
currentCategory,
|
||||||
|
rowsCategory,
|
||||||
|
columnsCategory,
|
||||||
|
fetchIssue,
|
||||||
|
fetchMessage,
|
||||||
|
fetchMessageStatus,
|
||||||
|
sendMessage,
|
||||||
|
scrollToEnd,
|
||||||
|
loadMessage,
|
||||||
|
ChangeStatusIssue,
|
||||||
|
newCategory,
|
||||||
|
fetchCategory,
|
||||||
|
deleteCategory,
|
||||||
|
editCategory,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import FormChat from "@/modules/00_support/components/FormChat.vue";
|
import FormChat from "@/modules/00_support/components/FormChat.vue";
|
||||||
import { useSupportStore } from "@/modules/00_support/store/Main.ts";
|
import { useSupportStore } from "@/modules/00_support/store/Main";
|
||||||
import { onMounted, onUnmounted, ref } from "vue";
|
import { onMounted, onUnmounted, ref } from "vue";
|
||||||
const store = useSupportStore();
|
const store = useSupportStore();
|
||||||
|
|
||||||
|
|
|
||||||
21
src/modules/00_support/views/ManageCategory.vue
Normal file
21
src/modules/00_support/views/ManageCategory.vue
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useSupportStore } from "@/modules/00_support/store/Main";
|
||||||
|
import TableCategory from "@/modules/00_support/components/category/TableCategory.vue";
|
||||||
|
import { onMounted, onUnmounted, ref } from "vue";
|
||||||
|
|
||||||
|
const store = useSupportStore();
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
store.socket.connect();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(async () => {
|
||||||
|
store.socket.disconnect();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<table-category />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue