938 lines
36 KiB
Vue
938 lines
36 KiB
Vue
<script lang="ts" setup>
|
|
import { onMounted, ref, watch } from 'vue';
|
|
import { Icon } from '@iconify/vue/dist/iconify.js';
|
|
import { moveItemUp, moveItemDown, deleteItem } from 'src/stores/utils';
|
|
import { useI18n } from 'vue-i18n';
|
|
|
|
import useUserStore from 'src/stores/user';
|
|
import useOptionStore from 'src/stores/options';
|
|
import { useWorkflowTemplate } from 'src/stores/workflow-template';
|
|
import { baseUrl } from 'stores/utils';
|
|
import { getRole } from 'src/services/keycloak';
|
|
import {
|
|
WorkflowUserInTable,
|
|
WorkflowTemplatePayload,
|
|
WorkFlowPayloadStep,
|
|
Group,
|
|
} from 'src/stores/workflow-template/types';
|
|
import { User } from 'src/stores/user/types';
|
|
|
|
import SelectMenuWithSearch from '../shared/SelectMenuWithSearch.vue';
|
|
import ToggleButton from 'src/components/button/ToggleButton.vue';
|
|
import NoData from '../NoData.vue';
|
|
import SelectBranch from '../shared/select/SelectBranch.vue';
|
|
import AddButton from '../button/AddButton.vue';
|
|
import { QField } from 'quasar';
|
|
|
|
defineProps<{
|
|
readonly?: boolean;
|
|
onDrawer?: boolean;
|
|
}>();
|
|
|
|
const { t } = useI18n();
|
|
const userStore = useUserStore();
|
|
const optionStore = useOptionStore();
|
|
const workflowStore = useWorkflowTemplate();
|
|
|
|
const userInTable = defineModel<WorkflowUserInTable[]>('userInTable', {
|
|
default: [],
|
|
});
|
|
const registerBranchId = defineModel('registerBranchId', { default: '' });
|
|
const flowData = defineModel<WorkflowTemplatePayload>('flowData', {
|
|
required: true,
|
|
default: {
|
|
status: 'CREATED',
|
|
name: '',
|
|
step: [],
|
|
},
|
|
});
|
|
|
|
let objectOptions = [
|
|
...(optionStore.globalOption?.agenciesType || []),
|
|
{ label: t('flow.customer'), value: 'customer' },
|
|
{ label: t('flow.officer'), value: 'officer' },
|
|
];
|
|
const options = ref(objectOptions);
|
|
const role = ref<string[]>([]);
|
|
const userList = ref<User[]>([]);
|
|
const groupList = ref<Group[]>([]);
|
|
const responsiblePersonSearch = ref('');
|
|
const responsibleMenu = ref(false);
|
|
|
|
async function getUserList(opts?: { query: string }) {
|
|
const resUser = await userStore.fetchList({
|
|
query: !!opts?.query ? opts.query : undefined,
|
|
});
|
|
if (resUser) userList.value = resUser.result;
|
|
}
|
|
|
|
async function getGroupList() {
|
|
const resGroup = await workflowStore.getGroupList();
|
|
if (resGroup) groupList.value = resGroup;
|
|
}
|
|
|
|
function selectResponsiblePerson(stepIndex: number, responsiblePerson: User) {
|
|
const currStep = flowData.value.step[stepIndex];
|
|
const existPersonIndex = currStep.responsiblePersonId?.findIndex(
|
|
(p) => p === responsiblePerson.id,
|
|
);
|
|
|
|
if (existPersonIndex === -1) {
|
|
currStep.responsiblePersonId?.push(responsiblePerson.id);
|
|
|
|
if (!userInTable.value[stepIndex]) {
|
|
userInTable.value[stepIndex] = {
|
|
name: flowData.value.step[stepIndex].name,
|
|
responsiblePerson: [],
|
|
responsibleGroup: [],
|
|
};
|
|
}
|
|
|
|
userInTable.value[stepIndex]?.responsiblePerson.push({
|
|
id: responsiblePerson.id,
|
|
selectedImage: responsiblePerson.selectedImage,
|
|
gender: responsiblePerson.gender,
|
|
namePrefix: responsiblePerson.namePrefix,
|
|
firstName: responsiblePerson.firstName,
|
|
lastName: responsiblePerson.lastName,
|
|
firstNameEN: responsiblePerson.firstNameEN,
|
|
lastNameEN: responsiblePerson.lastNameEN,
|
|
code: responsiblePerson.code,
|
|
});
|
|
} else {
|
|
currStep.responsiblePersonId?.splice(Number(existPersonIndex), 1);
|
|
userInTable.value[stepIndex]?.responsiblePerson.splice(
|
|
Number(existPersonIndex),
|
|
1,
|
|
);
|
|
}
|
|
}
|
|
|
|
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(
|
|
val: Record<string, unknown>,
|
|
responsibleInstitution?: string[],
|
|
) {
|
|
if (!responsibleInstitution) return;
|
|
const existIndex = responsibleInstitution.findIndex((p) => p === val.value);
|
|
if (existIndex === -1) {
|
|
responsibleInstitution.push(val.value as string);
|
|
} else {
|
|
responsibleInstitution.splice(Number(existIndex), 1);
|
|
}
|
|
}
|
|
|
|
function optionSearch(val: string | null) {
|
|
if (val === '') {
|
|
options.value = objectOptions;
|
|
return;
|
|
}
|
|
|
|
const needle = val ? val.toLowerCase() : '';
|
|
options.value = objectOptions.filter(
|
|
(v: { label: string }) => v.label.toLowerCase().indexOf(needle) > -1,
|
|
);
|
|
}
|
|
|
|
defineEmits<{
|
|
(e: 'moveUp'): void;
|
|
(e: 'addStep'): void;
|
|
(e: 'moveDown'): void;
|
|
(e: 'changeStatus'): void;
|
|
(e: 'triggerProperties', step: WorkFlowPayloadStep): void;
|
|
}>();
|
|
|
|
watch(
|
|
responsiblePersonSearch,
|
|
async () => await getUserList({ query: responsiblePersonSearch.value }),
|
|
);
|
|
|
|
onMounted(async () => {
|
|
role.value = getRole() || [];
|
|
await getUserList();
|
|
await getGroupList();
|
|
await userStore.fetchHqOption();
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div class="row col-12">
|
|
<section
|
|
:id="`form-flow-template-${onDrawer ? 'drawer' : 'dialog'}`"
|
|
class="col-12 q-pb-sm text-weight-bold text-body1 row items-center"
|
|
>
|
|
<q-icon
|
|
flat
|
|
size="xs"
|
|
class="q-pa-sm rounded q-mr-xs"
|
|
color="info"
|
|
name="mdi-cogs"
|
|
style="background-color: var(--surface-3)"
|
|
/>
|
|
{{ $t(`general.name`, { msg: $t('flow.title') }) }}
|
|
<span
|
|
class="row items-center text-weight-regular text-body2"
|
|
:class="{ 'q-ml-lg': $q.screen.gt.xs, 'q-mt-sm': $q.screen.lt.sm }"
|
|
>
|
|
<ToggleButton
|
|
class="q-mr-sm"
|
|
two-way
|
|
:model-value="flowData.status !== 'INACTIVE'"
|
|
@click="
|
|
() => {
|
|
onDrawer
|
|
? $emit('changeStatus')
|
|
: flowData.status !== 'INACTIVE'
|
|
? (flowData.status = 'INACTIVE')
|
|
: (flowData.status = 'CREATED');
|
|
}
|
|
"
|
|
/>
|
|
{{ $t('status.title') }}
|
|
</span>
|
|
</section>
|
|
|
|
<section class="col-12 row q-col-gutter-sm">
|
|
<SelectBranch
|
|
v-if="role.includes('system')"
|
|
:label="$t('branch.form.code')"
|
|
:readonly
|
|
code-only
|
|
v-model:value="registerBranchId"
|
|
class="col-md-3 col-12"
|
|
required
|
|
/>
|
|
<q-input
|
|
:readonly
|
|
bg-color="transparent"
|
|
outlined
|
|
dense
|
|
class="col"
|
|
for="input-flow-template-name"
|
|
v-model="flowData.name"
|
|
hide-bottom-space
|
|
:label="$t(`general.name`, { msg: $t('flow.step') })"
|
|
:rules="[(val: string) => !!val || $t('form.error.required')]"
|
|
/>
|
|
</section>
|
|
|
|
<!-- SEC: Step -->
|
|
<section
|
|
:id="`form-flow-step-${onDrawer ? 'drawer' : 'dialog'}`"
|
|
class="col-12 q-pb-sm q-pt-xl text-weight-bold text-body1 row items-center"
|
|
>
|
|
<q-icon
|
|
flat
|
|
size="xs"
|
|
class="q-pa-sm rounded q-mr-xs"
|
|
color="info"
|
|
name="mdi-note-edit-outline"
|
|
style="background-color: var(--surface-3)"
|
|
/>
|
|
{{ $t(`flow.processStep`) }}
|
|
<AddButton
|
|
v-if="!readonly && $q.screen.lt.md"
|
|
id="btn-add-work"
|
|
icon-only
|
|
class="q-ml-sm"
|
|
@click="$emit('addStep')"
|
|
/>
|
|
</section>
|
|
|
|
<section
|
|
v-if="flowData.step.length === 0"
|
|
class="col-12 surface-2 rounded bordered column items-center justify-center q-pa-xl"
|
|
>
|
|
<NoData class="col" />
|
|
</section>
|
|
|
|
<section v-else class="col-12 q-gutter-y-md">
|
|
<template v-for="(step, index) in flowData.step" :key="index">
|
|
<div class="bordered rounded">
|
|
<q-expansion-item
|
|
for="item-up"
|
|
id="item-up"
|
|
dense
|
|
switch-toggle-side
|
|
default-opened
|
|
expand-icon="mdi-chevron-down-circle"
|
|
header-class="expansion-rounded surface-2"
|
|
header-style="border-top-left-radius: var(--radius-2); border-top-right-radius: var(--radius-2)"
|
|
>
|
|
<template v-slot:header>
|
|
<div class="column full-width" @keyup.stop @click.stop>
|
|
<div class="row items-center q-py-sm full-width">
|
|
<q-btn
|
|
v-if="!readonly"
|
|
:id="`btn-work-up-product-${step.name || index}-${onDrawer ? 'drawer' : 'dialog'}`"
|
|
:for="`btn-work-up-product-${step.name || index}-${onDrawer ? 'drawer' : 'dialog'}`"
|
|
icon="mdi-arrow-up"
|
|
dense
|
|
flat
|
|
round
|
|
:disable="index === 0"
|
|
style="color: hsl(var(--text-mute-2))"
|
|
@click.stop="
|
|
moveItemUp(flowData.step, index);
|
|
moveItemUp(userInTable, index);
|
|
"
|
|
/>
|
|
<q-btn
|
|
v-if="!readonly"
|
|
:id="`btn-work-down-product-${step.name || index}-${onDrawer ? 'drawer' : 'dialog'}`"
|
|
:for="`btn-work-down-product-${step.name || index}-${onDrawer ? 'drawer' : 'dialog'}`"
|
|
icon="mdi-arrow-down"
|
|
dense
|
|
flat
|
|
round
|
|
class="q-mx-sm"
|
|
:disable="index === flowData.step.length - 1"
|
|
style="color: hsl(var(--text-mute-2))"
|
|
@click.stop="
|
|
moveItemDown(flowData.step, index);
|
|
moveItemDown(userInTable, index);
|
|
"
|
|
/>
|
|
<q-input
|
|
:bg-color="readonly ? 'transparent' : ''"
|
|
:prefix="`${$t('flow.stepNo')} ${index + 1}: `"
|
|
dense
|
|
outlined
|
|
:readonly
|
|
:id="`input-flow-step-name-${index}-${onDrawer ? 'drawer' : 'dialog'}`"
|
|
:for="`input-flow-step-name-${index}-${onDrawer ? 'drawer' : 'dialog'}`"
|
|
class="col q-ml-md"
|
|
:placeholder="$t('general.no', { msg: $t('flow.step') })"
|
|
v-model="step.name"
|
|
hide-bottom-space
|
|
:rules="[
|
|
(val: string) => !!val || $t('form.error.required'),
|
|
]"
|
|
/>
|
|
<!-- <div
|
|
:for="`select-work-name-${index + 1}`"
|
|
class="col q-py-sm q-px-md"
|
|
style="background-color: var(--surface-1); z-index: 2"
|
|
@click="() => (readonly ? '' : fetchListOfWork())"
|
|
>
|
|
<span class="text-body2" style="color: var(--foreground)">
|
|
{{ $t('productService.service.work') }} {{ index + 1 }} :
|
|
<span class="app-text-muted-2">
|
|
{{
|
|
workName
|
|
? workName
|
|
: $t('productService.service.workName')
|
|
}}
|
|
</span>
|
|
</span>
|
|
<q-menu
|
|
v-if="!readonly"
|
|
fit
|
|
ref="refMenu"
|
|
self="top left"
|
|
anchor="bottom left"
|
|
>
|
|
<q-item>
|
|
<div
|
|
class="full-width flex items-center justify-between"
|
|
>
|
|
{{ $t('productService.service.workName') }}
|
|
<q-btn
|
|
dense
|
|
unelevated
|
|
class="bordered q-px-sm text-capitalize"
|
|
style="
|
|
border-radius: var(--radius-2);
|
|
color: hsl(var(--info-bg));
|
|
"
|
|
@click.stop="
|
|
() => {
|
|
refMenu.hide();
|
|
$emit('manageWorkName');
|
|
}
|
|
"
|
|
>
|
|
<q-icon name="mdi-cog" size="xs" class="q-mr-sm" />
|
|
{{ $t('general.manage') }}
|
|
</q-btn>
|
|
</div>
|
|
</q-item>
|
|
<q-item
|
|
@click="workName = item.name"
|
|
clickable
|
|
v-for="(item, index) in workNameItems"
|
|
:key="index"
|
|
>
|
|
<div class="full-width flex items-center">
|
|
<q-icon
|
|
v-if="workName === item.name"
|
|
name="mdi-checkbox-marked"
|
|
size="xs"
|
|
color="primary"
|
|
class="q-mr-sm"
|
|
/>
|
|
<q-icon
|
|
v-else
|
|
name="mdi-checkbox-blank-outline"
|
|
size="xs"
|
|
style="color: hsl(var(--text-mute))"
|
|
class="q-mr-sm"
|
|
/>
|
|
{{ item.name }}
|
|
</div>
|
|
</q-item>
|
|
</q-menu>
|
|
</div> -->
|
|
<q-btn
|
|
v-if="!readonly"
|
|
:id="`btn-delete-work-${step.name || index}-${onDrawer ? 'drawer' : 'dialog'}`"
|
|
icon="mdi-trash-can-outline"
|
|
dense
|
|
flat
|
|
round
|
|
padding="0"
|
|
class="q-ml-md"
|
|
color="negative"
|
|
@click="deleteItem(flowData.step, index)"
|
|
>
|
|
<q-tooltip>{{ $t('general.delete') }}</q-tooltip>
|
|
</q-btn>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<section class="q-px-md surface-2 q-py-sm">
|
|
<div
|
|
:class="{ 'surface-1 rounded bordered': readonly }"
|
|
style="border: 1px solid transparent"
|
|
>
|
|
<div class="row q-col-gutter-sm">
|
|
<q-input
|
|
:for="`textarea-detail-${index}-${onDrawer ? 'drawer' : 'dialog'}`"
|
|
:bg-color="readonly ? 'transparent' : ''"
|
|
:readonly
|
|
class="col-md-6 col-12"
|
|
type="textarea"
|
|
dense
|
|
outlined
|
|
:label="$t('general.detail')"
|
|
:model-value="!readonly ? step.detail : step.detail || '-'"
|
|
@update:model-value="
|
|
(v) => (step.detail = v?.toString() || '')
|
|
"
|
|
/>
|
|
<div class="col-md-6 col-12">
|
|
<div
|
|
class="surface-1 rounded bordered full-height"
|
|
style="padding-inline: 12px"
|
|
:style="readonly ? 'border: 1px solid transparent;' : ''"
|
|
>
|
|
<section
|
|
class="row items-center q-pt-xs justify-between relative-position"
|
|
>
|
|
<div
|
|
class="app-text-muted-2"
|
|
:style="
|
|
step.attributes?.properties &&
|
|
step.attributes?.properties.length > 0
|
|
? 'font-size: 10px'
|
|
: ''
|
|
"
|
|
>
|
|
{{ $t('general.properties') }}
|
|
</div>
|
|
<q-btn
|
|
v-if="!readonly"
|
|
:id="`btn-add-work-product-${index}-${onDrawer ? 'drawer' : 'dialog'}`"
|
|
class="text-capitalize rounded absolute-top-right"
|
|
flat
|
|
dense
|
|
padding="4px 8px"
|
|
style="color: hsl(var(--info-bg)); top: 4px"
|
|
@click.stop="$emit('triggerProperties', step)"
|
|
>
|
|
<Icon
|
|
icon="basil:settings-adjust-solid"
|
|
width="20.08px"
|
|
style="color: hsl(var(--info-bg))"
|
|
/>
|
|
</q-btn>
|
|
</section>
|
|
<section class="row q-gutter-sm q-pb-sm scroll">
|
|
<span
|
|
v-for="(att, i) in step.attributes?.properties"
|
|
:key="i"
|
|
class="surface-2 bordered rounded q-px-xs"
|
|
>
|
|
{{
|
|
optionStore.mapOption(
|
|
att.fieldName ?? '',
|
|
'propertiesField',
|
|
)
|
|
}}
|
|
</span>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- RESPONSIBLE-PERSON -->
|
|
<q-field
|
|
v-if="step.responsiblePersonId"
|
|
:for="`select-responsible-person-${index}-${onDrawer ? 'drawer' : 'dialog'}`"
|
|
:bg-color="readonly ? 'transparent' : ''"
|
|
:readonly
|
|
outlined
|
|
:stack-label="
|
|
userInTable[index]?.responsiblePerson.length > 0 ||
|
|
userInTable[index]?.responsibleGroup.length > 0
|
|
"
|
|
:label="$t('flow.responsiblePerson')"
|
|
dense
|
|
class="col-md-6 col-12"
|
|
:class="{ 'cursor-pointer': !readonly }"
|
|
>
|
|
<template #control>
|
|
<q-item
|
|
dense
|
|
class="items-center full-width no-padding"
|
|
v-for="person in userInTable[
|
|
index
|
|
]?.responsiblePerson.filter((p) =>
|
|
step.responsiblePersonId.includes(p.id),
|
|
)"
|
|
:key="person.id"
|
|
>
|
|
<q-avatar class="q-ml-sm" size="md">
|
|
<q-img
|
|
class="text-center"
|
|
:ratio="1"
|
|
:src="`${baseUrl}/user/${person.id}/profile-image/${person.selectedImage}`"
|
|
>
|
|
<template #error>
|
|
<div
|
|
class="no-padding full-width full-height flex items-center justify-center"
|
|
:style="`${person.gender ? 'background: white' : 'background: linear-gradient(135deg,rgba(43, 137, 223, 1) 0%, rgba(230, 51, 81, 1) 100%);'}`"
|
|
>
|
|
<q-img
|
|
v-if="person.gender"
|
|
:src="
|
|
person.gender === 'male'
|
|
? '/no-img-man.png'
|
|
: '/no-img-female.png'
|
|
"
|
|
/>
|
|
<q-icon
|
|
v-else
|
|
size="sm"
|
|
name="mdi-account-outline"
|
|
style="color: white"
|
|
/>
|
|
</div>
|
|
</template>
|
|
</q-img>
|
|
</q-avatar>
|
|
<div
|
|
class="column q-pl-md"
|
|
style="color: var(--foreground)"
|
|
>
|
|
<span>
|
|
{{
|
|
`${optionStore.mapOption(person.namePrefix || '')} ${
|
|
$i18n.locale === 'eng'
|
|
? person.firstNameEN
|
|
: person.firstName
|
|
} ${
|
|
$i18n.locale === 'eng'
|
|
? person.lastNameEN
|
|
: person.lastName
|
|
}`
|
|
}}
|
|
</span>
|
|
<span class="text-caption app-text-muted">
|
|
{{ person.code }}
|
|
</span>
|
|
</div>
|
|
</q-item>
|
|
|
|
<div
|
|
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-item>
|
|
<q-input
|
|
:for="`input-search-${index}-${onDrawer ? 'drawer' : 'dialog'}`"
|
|
outlined
|
|
dense
|
|
:label="$t('general.search')"
|
|
class="col responsible-search"
|
|
:bg-color="$q.dark.isActive ? 'dark' : 'white'"
|
|
v-model="responsiblePersonSearch"
|
|
debounce="200"
|
|
>
|
|
<template #prepend>
|
|
<q-icon name="mdi-magnify" />
|
|
</template>
|
|
</q-input>
|
|
</q-item>
|
|
<span class="text-caption app-text-muted-2 q-px-md">
|
|
{{ $t('general.people') }}
|
|
</span>
|
|
|
|
<q-item
|
|
v-if="userList.length === 0"
|
|
class="app-text-muted q-px-lg"
|
|
>
|
|
{{ $t('general.noData') }}
|
|
</q-item>
|
|
<q-item
|
|
v-else
|
|
v-for="(person, i) in userList"
|
|
dense
|
|
:key="i"
|
|
clickable
|
|
class="column"
|
|
@click.stop="selectResponsiblePerson(index, person)"
|
|
>
|
|
<div
|
|
class="row items-center no-wrap"
|
|
:id="`select-responsible-person-${index}-${person.firstName || person.firstNameEN}-${onDrawer ? 'drawer' : 'dialog'}`"
|
|
>
|
|
<q-checkbox
|
|
size="xs"
|
|
:model-value="
|
|
step.responsiblePersonId.includes(person.id)
|
|
"
|
|
@click.stop="
|
|
selectResponsiblePerson(index, person)
|
|
"
|
|
/>
|
|
<q-avatar class="q-ml-sm" size="md">
|
|
<q-img
|
|
class="text-center"
|
|
:ratio="1"
|
|
:src="`${baseUrl}/user/${person.id}/profile-image/${person.selectedImage}`"
|
|
>
|
|
<template #error>
|
|
<div
|
|
class="no-padding full-width full-height flex items-center justify-center"
|
|
:style="`${person.gender ? 'background: white' : 'background: linear-gradient(135deg,rgba(43, 137, 223, 1) 0%, rgba(230, 51, 81, 1) 100%);'}`"
|
|
>
|
|
<q-img
|
|
v-if="person.gender"
|
|
:src="
|
|
person.gender === 'male'
|
|
? '/no-img-man.png'
|
|
: '/no-img-female.png'
|
|
"
|
|
/>
|
|
<q-icon
|
|
v-else
|
|
size="sm"
|
|
name="mdi-account-outline"
|
|
style="color: white"
|
|
/>
|
|
</div>
|
|
</template>
|
|
</q-img>
|
|
</q-avatar>
|
|
<div class="column q-pl-md">
|
|
<span>
|
|
{{
|
|
`${optionStore.mapOption(person.namePrefix || '')} ${
|
|
$i18n.locale === 'eng'
|
|
? person.firstNameEN
|
|
: person.firstName
|
|
} ${
|
|
$i18n.locale === 'eng'
|
|
? person.lastNameEN
|
|
: person.lastName
|
|
}`
|
|
}}
|
|
</span>
|
|
<span class="text-caption app-text-muted">
|
|
{{ person.code }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</q-item>
|
|
|
|
<span class="text-caption app-text-muted-2 q-px-md">
|
|
{{ $t('personnel.MESSENGER') }}
|
|
</span>
|
|
<q-item
|
|
dense
|
|
clickable
|
|
@click="step.messengerByArea = !step.messengerByArea"
|
|
class="column"
|
|
>
|
|
<div class="row items-center">
|
|
<q-checkbox
|
|
:id="`select-area-${index}`"
|
|
v-model="step.messengerByArea"
|
|
size="xs"
|
|
></q-checkbox>
|
|
<div class="column q-pl-md">
|
|
<span>{{ $t('general.byArea') }}</span>
|
|
</div>
|
|
</div>
|
|
</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-menu>
|
|
</q-field>
|
|
|
|
<!-- RESPONSIBLE-AGENCIES, RESPONSIBLE-INSTITUTION -->
|
|
<q-select
|
|
behavior="menu"
|
|
:for="`select-responsible-institution-${index}-${onDrawer ? 'drawer' : 'dialog'}`"
|
|
:bg-color="readonly ? 'transparent' : ''"
|
|
:readonly
|
|
outlined
|
|
dense
|
|
v-model="step.responsibleInstitution"
|
|
multiple
|
|
:options="options"
|
|
hide-bottom-space
|
|
option-label="label"
|
|
option-value="value"
|
|
emit-value
|
|
:label="
|
|
$t('general.select', { msg: $t('general.agencies') })
|
|
"
|
|
class="col-md-6 col-12"
|
|
:hide-dropdown-icon="readonly"
|
|
>
|
|
<template v-slot:selected-item="scope">
|
|
<q-chip
|
|
dense
|
|
:removable="!readonly"
|
|
@remove="scope.removeAtIndex(scope.index)"
|
|
>
|
|
<span class="text-caption">
|
|
{{
|
|
scope.opt === 'customer'
|
|
? $t('flow.customer')
|
|
: scope.opt === 'officer'
|
|
? $t('flow.officer')
|
|
: optionStore.mapOption(
|
|
scope.opt,
|
|
'agenciesType',
|
|
)
|
|
}}
|
|
</span>
|
|
</q-chip>
|
|
</template>
|
|
<template v-slot:option></template>
|
|
|
|
<SelectMenuWithSearch
|
|
v-if="!readonly"
|
|
:title="
|
|
$t('general.select', { msg: $t('general.agencies') })
|
|
"
|
|
:option="options"
|
|
:separator-index="[9]"
|
|
width="353.66px"
|
|
@search="(v) => optionSearch(v as string)"
|
|
@select="
|
|
(v) => selectItem(v, step.responsibleInstitution)
|
|
"
|
|
@before-show="
|
|
() => {
|
|
objectOptions = [
|
|
...(optionStore.globalOption?.agenciesType || []),
|
|
{ label: t('flow.customer'), value: 'customer' },
|
|
{ label: t('flow.officer'), value: 'officer' },
|
|
];
|
|
options = objectOptions;
|
|
}
|
|
"
|
|
>
|
|
<template #prepend>
|
|
<q-separator></q-separator>
|
|
</template>
|
|
<template
|
|
#option="{ opt }"
|
|
v-if="step.responsibleInstitution"
|
|
>
|
|
<q-checkbox
|
|
:id="`select-responsible-institution-${index}-${opt.value}-${onDrawer ? 'drawer' : 'dialog'}`"
|
|
:model-value="
|
|
step.responsibleInstitution.some(
|
|
(v: string) => v === opt.value,
|
|
)
|
|
"
|
|
class="q-pr-sm"
|
|
size="xs"
|
|
@click="selectItem(opt, step.responsibleInstitution)"
|
|
/>
|
|
<span
|
|
:class="{
|
|
'app-text-info': step.responsibleInstitution.some(
|
|
(v: string) => v === opt.value,
|
|
),
|
|
}"
|
|
>
|
|
{{ opt.label }}
|
|
</span>
|
|
</template>
|
|
</SelectMenuWithSearch>
|
|
</q-select>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</q-expansion-item>
|
|
</div>
|
|
</template>
|
|
</section>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
:deep(.responsible-search .q-field__control) {
|
|
height: 36px;
|
|
font-size: 12px;
|
|
}
|
|
|
|
:deep(
|
|
.q-item__section.column.q-item__section--side.justify-center.q-item__section--avatar.q-focusable.relative-position.cursor-pointer
|
|
) {
|
|
justify-content: start !important;
|
|
padding-right: 8px !important;
|
|
padding-top: 16px;
|
|
min-width: 0px;
|
|
}
|
|
|
|
:deep(i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon) {
|
|
color: hsl(var(--text-mute));
|
|
}
|
|
|
|
:deep(
|
|
i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon.q-expansion-item__toggle-icon--rotated
|
|
) {
|
|
color: var(--brand-1);
|
|
}
|
|
|
|
:deep(
|
|
.q-item.q-item-type.row.no-wrap.q-item--dense.q-item--clickable.q-link.cursor-pointer.q-focusable.q-hoverable.expansion-rounded.surface-2
|
|
.q-focus-helper
|
|
) {
|
|
visibility: hidden;
|
|
}
|
|
|
|
:deep(.q-dialog.fullscreen.no-pointer-events.q-dialog--modal) {
|
|
visibility: hidden;
|
|
}
|
|
|
|
.transition-rotate {
|
|
transition: transform 0.3s ease;
|
|
}
|
|
.rotated {
|
|
transform: rotate(180deg);
|
|
}
|
|
</style>
|