feat: update responsibleGroup type to string array and initialize in workflow steps
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 7s

This commit is contained in:
puriphatt 2025-04-24 14:31:08 +07:00
parent 28395b4f80
commit 56a63185a1
5 changed files with 215 additions and 84 deletions

View file

@ -6,12 +6,14 @@ import { useI18n } from 'vue-i18n';
import useUserStore from 'src/stores/user'; import useUserStore from 'src/stores/user';
import useOptionStore from 'src/stores/options'; import useOptionStore from 'src/stores/options';
import { useWorkflowTemplate } from 'src/stores/workflow-template';
import { baseUrl } from 'stores/utils'; import { baseUrl } from 'stores/utils';
import { getRole } from 'src/services/keycloak'; import { getRole } from 'src/services/keycloak';
import { import {
WorkflowUserInTable, WorkflowUserInTable,
WorkflowTemplatePayload, WorkflowTemplatePayload,
WorkFlowPayloadStep, WorkFlowPayloadStep,
Group,
} from 'src/stores/workflow-template/types'; } from 'src/stores/workflow-template/types';
import { User } from 'src/stores/user/types'; import { User } from 'src/stores/user/types';
@ -20,6 +22,7 @@ import ToggleButton from 'src/components/button/ToggleButton.vue';
import NoData from '../NoData.vue'; import NoData from '../NoData.vue';
import SelectBranch from '../shared/select/SelectBranch.vue'; import SelectBranch from '../shared/select/SelectBranch.vue';
import AddButton from '../button/AddButton.vue'; import AddButton from '../button/AddButton.vue';
import { QField } from 'quasar';
defineProps<{ defineProps<{
readonly?: boolean; readonly?: boolean;
@ -29,6 +32,7 @@ defineProps<{
const { t } = useI18n(); const { t } = useI18n();
const userStore = useUserStore(); const userStore = useUserStore();
const optionStore = useOptionStore(); const optionStore = useOptionStore();
const workflowStore = useWorkflowTemplate();
const userInTable = defineModel<WorkflowUserInTable[]>('userInTable', { const userInTable = defineModel<WorkflowUserInTable[]>('userInTable', {
default: [], default: [],
@ -51,7 +55,9 @@ let objectOptions = [
const options = ref(objectOptions); const options = ref(objectOptions);
const role = ref<string[]>([]); const role = ref<string[]>([]);
const userList = ref<User[]>([]); const userList = ref<User[]>([]);
const groupList = ref<Group[]>([]);
const responsiblePersonSearch = ref(''); const responsiblePersonSearch = ref('');
const responsibleMenu = ref(false);
async function getUserList(opts?: { query: string }) { async function getUserList(opts?: { query: string }) {
const resUser = await userStore.fetchList({ const resUser = await userStore.fetchList({
@ -60,10 +66,10 @@ async function getUserList(opts?: { query: string }) {
if (resUser) userList.value = resUser.result; if (resUser) userList.value = resUser.result;
} }
// async function getUserById(responsiblePersonId: string) { async function getGroupList() {
// const resUser = await userStore.fetchById(responsiblePersonId); const resGroup = await workflowStore.getGroupList();
// if (resUser) userInTable.value.push(resUser); if (resGroup) groupList.value = resGroup;
// } }
function selectResponsiblePerson(stepIndex: number, responsiblePerson: User) { function selectResponsiblePerson(stepIndex: number, responsiblePerson: User) {
const currStep = flowData.value.step[stepIndex]; const currStep = flowData.value.step[stepIndex];
@ -78,6 +84,7 @@ function selectResponsiblePerson(stepIndex: number, responsiblePerson: User) {
userInTable.value[stepIndex] = { userInTable.value[stepIndex] = {
name: flowData.value.step[stepIndex].name, name: flowData.value.step[stepIndex].name,
responsiblePerson: [], responsiblePerson: [],
responsibleGroup: [],
}; };
} }
@ -101,6 +108,33 @@ function selectResponsiblePerson(stepIndex: number, responsiblePerson: User) {
} }
} }
function selectResponsibleGroup(stepIndex: number, responsibleGroup: string) {
const currStep = flowData.value.step[stepIndex];
const existGroupIndex = currStep.responsibleGroup?.findIndex(
(p) => p === responsibleGroup,
);
if (existGroupIndex === -1) {
currStep.responsibleGroup?.push(responsibleGroup);
if (!userInTable.value[stepIndex]) {
userInTable.value[stepIndex] = {
name: flowData.value.step[stepIndex].name,
responsiblePerson: [],
responsibleGroup: [],
};
}
userInTable.value[stepIndex]?.responsibleGroup.push(responsibleGroup);
} else {
currStep.responsibleGroup?.splice(Number(existGroupIndex), 1);
userInTable.value[stepIndex]?.responsibleGroup.splice(
Number(existGroupIndex),
1,
);
}
}
function selectItem( function selectItem(
val: Record<string, unknown>, val: Record<string, unknown>,
responsibleInstitution?: string[], responsibleInstitution?: string[],
@ -142,6 +176,7 @@ watch(
onMounted(async () => { onMounted(async () => {
role.value = getRole() || []; role.value = getRole() || [];
await getUserList(); await getUserList();
await getGroupList();
await userStore.fetchHqOption(); await userStore.fetchHqOption();
}); });
</script> </script>
@ -467,33 +502,29 @@ onMounted(async () => {
</div> </div>
<!-- RESPONSIBLE-PERSON --> <!-- RESPONSIBLE-PERSON -->
<q-select <q-field
v-if="step.responsiblePersonId" v-if="step.responsiblePersonId"
behavior="menu"
:for="`select-responsible-person-${index}-${onDrawer ? 'drawer' : 'dialog'}`" :for="`select-responsible-person-${index}-${onDrawer ? 'drawer' : 'dialog'}`"
:bg-color="readonly ? 'transparent' : ''" :bg-color="readonly ? 'transparent' : ''"
:readonly :readonly
outlined outlined
dense :stack-label="
v-model="step.responsiblePersonId" userInTable[index]?.responsiblePerson.length > 0 ||
multiple userInTable[index]?.responsibleGroup.length > 0
:options="[1, 2, 3]" "
hide-bottom-space
option-label="label"
option-value="value"
emit-value
:label="$t('flow.responsiblePerson')" :label="$t('flow.responsiblePerson')"
dense
class="col-md-6 col-12" class="col-md-6 col-12"
:hide-dropdown-icon="readonly" :class="{ 'cursor-pointer': !readonly }"
> >
<template v-slot:selected-item="scope"> <template #control>
<div class="column full-width"> <q-item
<div dense
class="row items-center no-wrap" class="items-center full-width no-padding"
v-for="person in userInTable[ v-for="person in userInTable[
index index
]?.responsiblePerson.filter( ]?.responsiblePerson.filter((p) =>
(p) => p.id === scope.opt, step.responsiblePersonId.includes(p.id),
)" )"
:key="person.id" :key="person.id"
> >
@ -547,12 +578,52 @@ onMounted(async () => {
{{ person.code }} {{ person.code }}
</span> </span>
</div> </div>
</div> </q-item>
</div>
</template>
<template v-slot:option></template> <div
<q-menu v-if="!readonly" :offset="[0, 4]"> v-if="step.responsibleGroup.length > 0"
class="full-width app-text-muted text-weight-medium"
style="font-size: 10px"
>
{{ $t('general.group') }}
</div>
<q-item
class="items-center full-width no-padding"
v-for="group in userInTable[
index
]?.responsibleGroup.filter((g) =>
step.responsibleGroup.includes(g),
)"
:key="group"
dense
>
<q-avatar class="q-ml-sm" size="md">
<q-img
class="text-center"
:ratio="1"
:src="`/img-group.png`"
/>
</q-avatar>
<span class="q-pl-md">
{{ group }}
</span>
</q-item>
</template>
<template #append>
<q-icon
name="mdi-menu-down"
:class="{ rotated: responsibleMenu }"
class="transition-rotate"
/>
</template>
<q-menu
v-if="!readonly"
no-focus
no-refocus
:offset="[0, 4]"
@before-show="() => (responsibleMenu = true)"
@before-hide="() => (responsibleMenu = false)"
>
<q-list> <q-list>
<q-item> <q-item>
<q-input <q-input
@ -581,6 +652,7 @@ onMounted(async () => {
{{ $t('general.noData') }} {{ $t('general.noData') }}
</q-item> </q-item>
<q-item <q-item
v-else
v-for="(person, i) in userList" v-for="(person, i) in userList"
dense dense
:key="i" :key="i"
@ -655,6 +727,7 @@ onMounted(async () => {
{{ $t('personnel.MESSENGER') }} {{ $t('personnel.MESSENGER') }}
</span> </span>
<q-item <q-item
dense
clickable clickable
@click="step.messengerByArea = !step.messengerByArea" @click="step.messengerByArea = !step.messengerByArea"
class="column" class="column"
@ -670,9 +743,49 @@ onMounted(async () => {
</div> </div>
</div> </div>
</q-item> </q-item>
<span class="text-caption app-text-muted-2 q-px-md">
{{ $t('general.group') }}
</span>
<q-item
v-if="groupList.length === 0"
class="app-text-muted q-px-lg"
>
{{ $t('general.noData') }}
</q-item>
<q-item
v-else
v-for="(group, i) in groupList"
dense
clickable
@click="selectResponsibleGroup(index, group.name)"
class="column"
>
<div class="row items-center">
<q-checkbox
size="xs"
:model-value="
step.responsibleGroup.includes(group.name)
"
@click.stop="
selectResponsibleGroup(index, group.name)
"
/>
<q-avatar class="q-ml-sm" size="md">
<q-img
class="text-center"
:ratio="1"
:src="`/img-group.png`"
/>
</q-avatar>
<div class="column q-pl-md">
<span>{{ group.name }}</span>
</div>
</div>
</q-item>
</q-list> </q-list>
</q-menu> </q-menu>
</q-select> </q-field>
<!-- RESPONSIBLE-AGENCIES, RESPONSIBLE-INSTITUTION --> <!-- RESPONSIBLE-AGENCIES, RESPONSIBLE-INSTITUTION -->
<q-select <q-select
@ -815,4 +928,11 @@ onMounted(async () => {
:deep(.q-dialog.fullscreen.no-pointer-events.q-dialog--modal) { :deep(.q-dialog.fullscreen.no-pointer-events.q-dialog--modal) {
visibility: hidden; visibility: hidden;
} }
.transition-rotate {
transition: transform 0.3s ease;
}
.rotated {
transform: rotate(180deg);
}
</style> </style>

View file

@ -60,6 +60,7 @@ async function addStep() {
flowData.value.step.push({ flowData.value.step.push({
responsibleInstitution: [], responsibleInstitution: [],
responsiblePersonId: [], responsiblePersonId: [],
responsibleGroup: [],
value: [], value: [],
detail: '', detail: '',
name: '', name: '',
@ -166,6 +167,7 @@ function triggerPropertiesDialog(step: WorkFlowPayloadStep) {
id="flow-form-dialog" id="flow-form-dialog"
> >
<FormFlow <FormFlow
v-model:user-in-table="userInTable"
v-model:flow-data="flowData" v-model:flow-data="flowData"
v-model:register-branch-id="registerBranchId" v-model:register-branch-id="registerBranchId"
@trigger-properties="triggerPropertiesDialog" @trigger-properties="triggerPropertiesDialog"

View file

@ -103,6 +103,7 @@ const columns = [
function triggerDialog(type: 'add' | 'edit' | 'view') { function triggerDialog(type: 'add' | 'edit' | 'view') {
if (type === 'add') { if (type === 'add') {
registeredBranchId.value = ''; registeredBranchId.value = '';
userInTable.value = [];
formDataWorkflow.value = { formDataWorkflow.value = {
status: 'CREATED', status: 'CREATED',
name: '', name: '',
@ -207,7 +208,7 @@ async function submit() {
...formDataWorkflow.value, ...formDataWorkflow.value,
}); });
} else { } else {
await workflowStore.creatWorkflowTemplate({ await workflowStore.createWorkflowTemplate({
registeredBranchId: registeredBranchId.value, registeredBranchId: registeredBranchId.value,
...formDataWorkflow.value, ...formDataWorkflow.value,
}); });
@ -223,7 +224,11 @@ function assignFormData(workflowData: WorkflowTemplate) {
status: workflowData.status, status: workflowData.status,
name: workflowData.name, name: workflowData.name,
step: workflowData.step.map((s, i) => { step: workflowData.step.map((s, i) => {
userInTable.value[i] = { name: s.name, responsiblePerson: [] }; userInTable.value[i] = {
name: s.name,
responsiblePerson: [],
responsibleGroup: [],
};
s.responsiblePerson.forEach((p) => { s.responsiblePerson.forEach((p) => {
userInTable.value[i].responsiblePerson.push({ userInTable.value[i].responsiblePerson.push({
id: p.user.id, id: p.user.id,
@ -237,12 +242,16 @@ function assignFormData(workflowData: WorkflowTemplate) {
code: p.user.code, code: p.user.code,
}); });
}); });
s.responsibleGroup.forEach((g) => {
userInTable.value[i].responsibleGroup.push(g);
});
return { return {
id: s.id, id: s.id,
name: s.name, name: s.name,
detail: s.detail, detail: s.detail,
messengerByArea: s.messengerByArea || false, messengerByArea: s.messengerByArea || false,
value: s.value.length > 0 ? JSON.parse(JSON.stringify(s.value)) : [], value: s.value.length > 0 ? JSON.parse(JSON.stringify(s.value)) : [],
responsibleGroup: s.responsibleGroup.map((g) => g),
responsiblePersonId: s.responsiblePerson.map((p) => p.userId), responsiblePersonId: s.responsiblePerson.map((p) => p.userId),
responsibleInstitution: JSON.parse( responsibleInstitution: JSON.parse(
JSON.stringify(s.responsibleInstitution), JSON.stringify(s.responsibleInstitution),

View file

@ -2,7 +2,7 @@ import { ref } from 'vue';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { api } from 'src/boot/axios'; import { api } from 'src/boot/axios';
import { PaginationResult } from 'src/types'; import { PaginationResult } from 'src/types';
import { WorkflowTemplate, WorkflowTemplatePayload, Group } from './types'; import { Group, WorkflowTemplate, WorkflowTemplatePayload } from './types';
import { Status } from '../types'; import { Status } from '../types';
export const useWorkflowTemplate = defineStore('workflow-store', () => { export const useWorkflowTemplate = defineStore('workflow-store', () => {

View file

@ -50,7 +50,7 @@ export type WorkflowTemplatePayload = {
export type WorkflowUserInTable = { export type WorkflowUserInTable = {
name: string; name: string;
responsibleGroup: Group[]; responsibleGroup: string[];
responsiblePerson: { responsiblePerson: {
id: string; id: string;
selectedImage?: string; selectedImage?: string;