feat:issue
This commit is contained in:
parent
0078dff1af
commit
d558f826f6
4 changed files with 451 additions and 0 deletions
|
|
@ -194,4 +194,6 @@ export default {
|
|||
workflowCommanderSign: `${workflow}/commander/sign`,
|
||||
|
||||
orgAssistance: (id: string) => `${orgProfile}/assistance/${id}`,
|
||||
|
||||
orgIssues: `${organization}/issues`,
|
||||
};
|
||||
|
|
|
|||
369
src/components/Dialogs/DialogDebug.vue
Normal file
369
src/components/Dialogs/DialogDebug.vue
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, reactive, ref } from "vue";
|
||||
import { useQuasar } from "quasar";
|
||||
import axios from "axios";
|
||||
import { storeToRefs } from "pinia";
|
||||
|
||||
import { useCounterMixin } from "@/stores/mixin";
|
||||
import { usePositionKeycloakStore } from "@/stores/positionKeycloak";
|
||||
import { useMenuDataStore } from "@/stores/menuList";
|
||||
import http from "@/plugins/http";
|
||||
import config from "@/app.config";
|
||||
|
||||
import DialogHeader from "@/components/DialogHeader.vue";
|
||||
|
||||
const $q = useQuasar();
|
||||
const store = usePositionKeycloakStore();
|
||||
const { dataPositionKeycloak } = storeToRefs(store);
|
||||
const { findOrgName } = store;
|
||||
|
||||
const { menuList } = storeToRefs(useMenuDataStore());
|
||||
const { dialogConfirm, showLoader, hideLoader, messageError, success } =
|
||||
useCounterMixin();
|
||||
|
||||
const modal = defineModel<boolean>("modal", {
|
||||
default: false,
|
||||
});
|
||||
|
||||
const title = computed(() => "แจ้งปัญหาการใช้งานระบบ");
|
||||
const orgName = computed(() => findOrgName(dataPositionKeycloak.value) || "");
|
||||
const optionData = computed(() => {
|
||||
return menuList.value.map((menu) => ({
|
||||
label: menu.sysName,
|
||||
value: menu.sysName,
|
||||
disable: menu?.children?.length === 0 ? false : true,
|
||||
children: menu?.children?.map((subMenu) => ({
|
||||
label: subMenu.sysName,
|
||||
value: `${menu.sysName}/${subMenu.sysName}`,
|
||||
})),
|
||||
}));
|
||||
});
|
||||
|
||||
const menuSelect = ref();
|
||||
const optionsMenu = ref(optionData.value);
|
||||
const formData = reactive({
|
||||
title: "",
|
||||
description: "",
|
||||
system: "mgt",
|
||||
fileAttachments: [] as File[],
|
||||
menu: "",
|
||||
org: orgName.value,
|
||||
});
|
||||
|
||||
/** ฟังก์ชันบันทึกข้อมูล */
|
||||
function onSubmit() {
|
||||
dialogConfirm($q, async () => {
|
||||
try {
|
||||
showLoader();
|
||||
const payload = {
|
||||
title: formData.title,
|
||||
description: formData.description,
|
||||
system: formData.system,
|
||||
menu: formData.menu,
|
||||
org: formData.org,
|
||||
};
|
||||
|
||||
const res = await http.post(config.API.orgIssues, payload);
|
||||
|
||||
const issueCode = res.data.result.codeIssue;
|
||||
await uploadProfile(issueCode);
|
||||
success($q, "บันทึกข้อมูลเรียบร้อย");
|
||||
|
||||
onClose();
|
||||
} catch (error) {
|
||||
messageError($q, error);
|
||||
} finally {
|
||||
hideLoader();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ฟังก์ชันเพิ่มไฟล์
|
||||
* @param files ไฟล์ที่ต้องการเพิ่ม
|
||||
*/
|
||||
async function onAddfile(files: any) {
|
||||
files.forEach((file: any) => {
|
||||
formData.fileAttachments.push(file);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ฟังก์ชันลบไฟล์
|
||||
* @param files ไฟล์ที่ต้องการลบ
|
||||
*/
|
||||
async function onRemoveFile(files: any) {
|
||||
files.forEach((file: any) => {
|
||||
const index = formData.fileAttachments.findIndex(
|
||||
(x: any) => x.__key == file.__key
|
||||
);
|
||||
if (index > -1) {
|
||||
formData.fileAttachments.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ฟังก์ชันสร้าง url อัปโหลดไฟล์
|
||||
* @param code รหัส issue
|
||||
*/
|
||||
async function uploadProfile(code: string) {
|
||||
if (formData.fileAttachments.length === 0) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const fileName = formData.fileAttachments.map((file) => ({
|
||||
fileName: file.name,
|
||||
}));
|
||||
const res = await http.post(
|
||||
config.API.file("issueAttachments", "documents", code),
|
||||
{
|
||||
replace: false,
|
||||
fileList: fileName,
|
||||
}
|
||||
);
|
||||
|
||||
for (const file of formData.fileAttachments) {
|
||||
const fileInfo = res.data[file.name];
|
||||
if (fileInfo && fileInfo.uploadUrl) {
|
||||
await uploadFileDoc(fileInfo.uploadUrl, file);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
messageError($q, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ฟังก์ชันอัปโหลดไฟล์เอกสาร
|
||||
* @param uploadUrl ลิงก์อัปโหลดไฟล์
|
||||
* @param file ไฟล์ที่ต้องการอัปโหลด
|
||||
*/
|
||||
async function uploadFileDoc(uploadUrl: string, file: any) {
|
||||
try {
|
||||
await axios.put(uploadUrl, file, {
|
||||
headers: {
|
||||
"Content-Type": file.type,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
messageError($q, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ฟังก์ชันกรองข้อมูลใน select
|
||||
* @param val ค่าที่กรอง
|
||||
* @param update ฟังก์ชันอัปเดตค่าหลังกรอง
|
||||
*/
|
||||
function filterSelector(val: string, update: Function) {
|
||||
update(() => {
|
||||
if (!val) {
|
||||
optionsMenu.value = optionData.value;
|
||||
return;
|
||||
}
|
||||
optionsMenu.value = optionData.value
|
||||
.map((parent: any) => {
|
||||
const matchParent = parent.label.indexOf(val) > -1;
|
||||
|
||||
const filteredChildren = (parent.children || []).filter(
|
||||
(child: any) => child.label.indexOf(val) > -1
|
||||
);
|
||||
if (matchParent) {
|
||||
return { ...parent };
|
||||
} else if (filteredChildren.length > 0) {
|
||||
return { ...parent, children: filteredChildren };
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
.filter((item: any) => item !== null);
|
||||
});
|
||||
}
|
||||
|
||||
/** ฟังก์ชันปิด dialog และรีเซ็ตข้อมูล */
|
||||
function onClose() {
|
||||
modal.value = false;
|
||||
formData.menu = "";
|
||||
formData.title = "";
|
||||
formData.description = "";
|
||||
formData.fileAttachments = [];
|
||||
formData.org = orgName.value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-dialog v-model="modal" persistent>
|
||||
<q-card style="width: 700px; max-width: 80vw">
|
||||
<q-form greedy @submit.prevent @validation-success="onSubmit">
|
||||
<DialogHeader :tittle="title" :close="onClose" />
|
||||
<q-separator />
|
||||
|
||||
<q-card-section>
|
||||
<div class="row col q-col-gutter-md">
|
||||
<div class="col-12">
|
||||
<q-select
|
||||
ref="menuSelect"
|
||||
dense
|
||||
outlined
|
||||
label="ระบบ"
|
||||
v-model="formData.menu"
|
||||
:options="optionsMenu"
|
||||
class="inputgreen"
|
||||
:rules="[ (val: string) => !!val || 'กรุณาเลือกระบบ' ]"
|
||||
hide-bottom-space
|
||||
emit-value
|
||||
map-options
|
||||
use-input
|
||||
@filter="(inputValue: string,
|
||||
doneFn: Function) => filterSelector(inputValue, doneFn,
|
||||
)"
|
||||
>
|
||||
<template v-slot:option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-list v-if="scope.opt.children">
|
||||
<q-item
|
||||
v-for="child in scope.opt.children"
|
||||
:key="child.value"
|
||||
clickable
|
||||
@click.stop="
|
||||
formData.menu = child.value;
|
||||
menuSelect.hidePopup();
|
||||
"
|
||||
>
|
||||
<q-item-section class="q-ml-md">
|
||||
{{ child.label }}
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<q-input
|
||||
dense
|
||||
outlined
|
||||
label="หัวข้อปัญหา"
|
||||
v-model="formData.title"
|
||||
class="inputgreen"
|
||||
:rules="[ (val: string) => !!val || 'กรุณากรอกหัวข้อปัญหา' ]"
|
||||
hide-bottom-space
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<q-input
|
||||
dense
|
||||
outlined
|
||||
type="textarea"
|
||||
label="รายละเอียดปัญหา"
|
||||
v-model="formData.description"
|
||||
class="inputgreen"
|
||||
:rules="[ (val: string) => !!val || 'กรุณากรอกรายละเอียดปัญหา' ]"
|
||||
hide-bottom-space
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<q-uploader
|
||||
color="gray"
|
||||
type="file"
|
||||
flat
|
||||
ref="uploader"
|
||||
class="full-width"
|
||||
text-color="dark"
|
||||
:max-size="10000000"
|
||||
accept=".jpg,.png,.pdf,.csv,.doc"
|
||||
bordered
|
||||
label="[ไฟล์ jpg,png,pdf,csv,doc ขนาดไม่เกิน 10MB]"
|
||||
multiple
|
||||
@added="onAddfile"
|
||||
@removed="onRemoveFile"
|
||||
>
|
||||
<template v-slot:header="scope">
|
||||
<div
|
||||
class="row no-wrap items-center q-pa-sm q-gutter-xs text-white"
|
||||
>
|
||||
<q-btn
|
||||
v-if="scope.queuedFiles.length > 0"
|
||||
icon="clear_all"
|
||||
@click="scope.removeQueuedFiles"
|
||||
round
|
||||
dense
|
||||
flat
|
||||
>
|
||||
<q-tooltip>ลบทั้งหมด</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
v-if="scope.uploadedFiles.length > 0"
|
||||
icon="done_all"
|
||||
@click="scope.removeUploadedFiles"
|
||||
round
|
||||
dense
|
||||
flat
|
||||
>
|
||||
<q-tooltip>ลบไฟล์ที่อัปโหลด</q-tooltip>
|
||||
</q-btn>
|
||||
<q-spinner
|
||||
v-if="scope.isUploading"
|
||||
class="q-uploader__spinner"
|
||||
/>
|
||||
<div class="col">
|
||||
<div class="q-uploader__title">
|
||||
{{ "[ไฟล์ jpg,png,pdf,csv,doc ขนาดไม่เกิน 10MB]" }}
|
||||
</div>
|
||||
<div class="q-uploader__subtitle">
|
||||
{{ scope.uploadSizeLabel }}
|
||||
/
|
||||
{{ scope.uploadProgressLabel }}
|
||||
</div>
|
||||
</div>
|
||||
<q-btn
|
||||
v-if="scope.canAddFiles"
|
||||
type="a"
|
||||
icon="add_box"
|
||||
@click="scope.pickFiles"
|
||||
round
|
||||
dense
|
||||
flat
|
||||
>
|
||||
<q-uploader-add-trigger />
|
||||
<q-tooltip>เลือกไฟล์</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
v-if="scope.isUploading"
|
||||
icon="clear"
|
||||
@click="scope.abort"
|
||||
round
|
||||
dense
|
||||
flat
|
||||
>
|
||||
<q-tooltip>ยกเลิกการอัปโหลด</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</template>
|
||||
</q-uploader>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-separator />
|
||||
<q-card-actions align="right">
|
||||
<q-btn
|
||||
type="submit"
|
||||
for="#submitForm"
|
||||
class="q-px-md items-center"
|
||||
color="public"
|
||||
label="บันทึก"
|
||||
>
|
||||
<q-tooltip>บันทึกข้อมูล</q-tooltip>
|
||||
</q-btn>
|
||||
</q-card-actions>
|
||||
</q-form>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
60
src/stores/positionKeycloak.ts
Normal file
60
src/stores/positionKeycloak.ts
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import { ref } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
export const usePositionKeycloakStore = defineStore("positionKeycloak", () => {
|
||||
const dataPositionKeycloak = ref<any>(null);
|
||||
|
||||
function setPositionKeycloak(data: any) {
|
||||
dataPositionKeycloak.value = data;
|
||||
}
|
||||
|
||||
function findOrgName(obj: any) {
|
||||
if (obj) {
|
||||
let name =
|
||||
obj.child4 != null &&
|
||||
obj.child4 !== "" &&
|
||||
obj.child3 != null &&
|
||||
obj.child3 !== ""
|
||||
? obj.child4 + (obj.child3 ? "/" : "")
|
||||
: obj.child4 != null && obj.child4 !== ""
|
||||
? obj.child4
|
||||
: "";
|
||||
|
||||
name +=
|
||||
obj.child3 != null &&
|
||||
obj.child3 !== "" &&
|
||||
obj.child2 != null &&
|
||||
obj.child2 !== ""
|
||||
? obj.child3 + (obj.child2 ? "/" : "")
|
||||
: obj.child3 != null && obj.child3 !== ""
|
||||
? obj.child3
|
||||
: "";
|
||||
|
||||
name +=
|
||||
obj.child2 != null &&
|
||||
obj.child2 !== "" &&
|
||||
obj.child1 != null &&
|
||||
obj.child1 !== ""
|
||||
? obj.child2 + (obj.child1 ? "/" : "")
|
||||
: obj.child2 != null && obj.child2 !== ""
|
||||
? obj.child2
|
||||
: "";
|
||||
|
||||
name +=
|
||||
obj.child1 != null &&
|
||||
obj.child1 !== "" &&
|
||||
obj.root != null &&
|
||||
obj.root !== ""
|
||||
? obj.child1 + (obj.root ? "/" : "")
|
||||
: obj.child1 != null && obj.child1 !== ""
|
||||
? obj.child1
|
||||
: "";
|
||||
name += obj.root != null && obj.root !== "" ? obj.root : "";
|
||||
return name == "" ? "-" : name;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
return { dataPositionKeycloak, setPositionKeycloak, findOrgName };
|
||||
});
|
||||
|
|
@ -6,6 +6,7 @@ import { storeToRefs } from "pinia";
|
|||
import { scroll, useQuasar } from "quasar";
|
||||
import { useCounterMixin } from "@/stores/mixin";
|
||||
import { useMenuDataStore } from "@/stores/menuList";
|
||||
import { usePositionKeycloakStore } from "@/stores/positionKeycloak";
|
||||
import {
|
||||
tokenParsed,
|
||||
logout,
|
||||
|
|
@ -26,6 +27,7 @@ import type {
|
|||
import { tabList, tabListPlacement } from "../interface/request/main/main";
|
||||
|
||||
import LoginLinkage from "@/components/LoginLinkage.vue";
|
||||
import DialogDebug from "@/components/Dialogs/DialogDebug.vue";
|
||||
|
||||
// landing page config url
|
||||
const configParam = {
|
||||
|
|
@ -63,6 +65,7 @@ const modalLoginLinkage = ref<boolean>(false); //เข้าสู่ระบ
|
|||
|
||||
// landing page redirect
|
||||
const landingPageUrl = ref<string>(configParam.landingPageUrl);
|
||||
const modalDebug = ref<boolean>(false);
|
||||
|
||||
async function fetchmsgNoread() {
|
||||
await http
|
||||
|
|
@ -524,6 +527,7 @@ async function fetchKeycloakPosition() {
|
|||
.get(config.API.keycloakPosition())
|
||||
.then(async (res) => {
|
||||
const data = await res.data.result;
|
||||
usePositionKeycloakStore().setPositionKeycloak(data);
|
||||
if (data.avatarName) {
|
||||
await getImg(data.profileId, data.avatarName);
|
||||
} else {
|
||||
|
|
@ -783,6 +787,21 @@ function onViewDetailNoti(url: string) {
|
|||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item clickable @click="modalDebug = true">
|
||||
<q-item-section avatar>
|
||||
<q-avatar
|
||||
color="warning"
|
||||
text-color="white"
|
||||
icon="mdi-bug"
|
||||
size="24px"
|
||||
font-size="14px"
|
||||
/>
|
||||
</q-item-section>
|
||||
<q-item-section class="q-py-sm">
|
||||
แจ้งปัญหาการใช้งานระบบ
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item clickable @click="modalLoginLinkage = true">
|
||||
<q-item-section avatar>
|
||||
<q-avatar
|
||||
|
|
@ -1216,6 +1235,7 @@ function onViewDetailNoti(url: string) {
|
|||
</q-page-container>
|
||||
<full-loader :visibility="loader" />
|
||||
<LoginLinkage v-model:modal="modalLoginLinkage" />
|
||||
<DialogDebug v-model:modal="modalDebug" />
|
||||
</q-layout>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue