refactor: restructure
This commit is contained in:
parent
7a81991c79
commit
11ff03ec50
8 changed files with 16 additions and 9 deletions
|
|
@ -0,0 +1,482 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
|
||||
import useCustomerStore from 'stores/customer';
|
||||
import useFlowStore from 'stores/flow';
|
||||
import useOptionStore from 'stores/options';
|
||||
|
||||
import { Status } from 'stores/types';
|
||||
import { CustomerBranch, CustomerType } from 'stores/customer/types';
|
||||
|
||||
import BranchCardCustomer from 'components/03_customer-management/BranchCardCustomer.vue';
|
||||
import PaginationComponent from 'components/PaginationComponent.vue';
|
||||
import NoData from 'components/NoData.vue';
|
||||
import { QTableProps } from 'quasar';
|
||||
|
||||
const flowStore = useFlowStore();
|
||||
|
||||
const userCustomer = useCustomerStore();
|
||||
const { fetchListCustomeBranch } = userCustomer;
|
||||
|
||||
const inputSearch = ref<string>('');
|
||||
|
||||
const branch = defineModel<CustomerBranch[]>('branch');
|
||||
const currentCustomerName = defineModel<string>('currentCustomerName');
|
||||
const modeView = defineModel<boolean>('modeView');
|
||||
|
||||
const currentCustomerUrlImage = defineModel<string | null>(
|
||||
'currentCustomerUrlImage',
|
||||
);
|
||||
|
||||
const currentStatus = ref<Status | 'All'>('All');
|
||||
const totalBranch = ref(0);
|
||||
|
||||
const currentPageBranch = ref<number>(1);
|
||||
const maxPageBranch = ref<number>(1);
|
||||
const pageSizeBranch = ref<number>(30);
|
||||
|
||||
const prop = withDefaults(
|
||||
defineProps<{
|
||||
color?: 'purple' | 'green';
|
||||
|
||||
customerId: string;
|
||||
customerType: CustomerType;
|
||||
}>(),
|
||||
{
|
||||
color: 'green',
|
||||
},
|
||||
);
|
||||
|
||||
defineEmits<{
|
||||
(e: 'back'): void;
|
||||
(e: 'viewDetail', branch: CustomerBranch, index: number): void;
|
||||
(e: 'dialog'): void;
|
||||
}>();
|
||||
|
||||
onMounted(async () => {
|
||||
await fetchList();
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{
|
||||
name: 'branchLabelName',
|
||||
align: 'left',
|
||||
label: 'office',
|
||||
field: 'name',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
name: 'branchLabelAddress',
|
||||
align: 'left',
|
||||
label: 'address',
|
||||
field: 'address',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
name: 'branchLabelTel',
|
||||
align: 'left',
|
||||
label: 'formDialogInputTelephone',
|
||||
field: 'telephoneNo',
|
||||
},
|
||||
] satisfies QTableProps['columns'];
|
||||
|
||||
const fieldDisplay = ref<
|
||||
(
|
||||
| 'branchName'
|
||||
| 'customerBranchFormTab'
|
||||
| 'address'
|
||||
| 'telephone'
|
||||
| 'businessTypePure'
|
||||
| 'totalEmployee'
|
||||
)[]
|
||||
>([
|
||||
'branchName',
|
||||
'customerBranchFormTab',
|
||||
'address',
|
||||
'telephone',
|
||||
'businessTypePure',
|
||||
'totalEmployee',
|
||||
]);
|
||||
const branchFieldSelected = ref<
|
||||
(
|
||||
| 'customerBranchFormTab'
|
||||
| 'branchName'
|
||||
| 'address'
|
||||
| 'telephone'
|
||||
| 'businessTypePure'
|
||||
| 'totalEmployee'
|
||||
)[]
|
||||
>(fieldDisplay.value);
|
||||
|
||||
async function fetchList() {
|
||||
const result = await fetchListCustomeBranch({
|
||||
customerId: prop.customerId,
|
||||
query: !!inputSearch.value ? inputSearch.value : undefined,
|
||||
page: currentPageBranch.value,
|
||||
pageSize: pageSizeBranch.value,
|
||||
includeCustomer: true,
|
||||
status:
|
||||
currentStatus.value === 'All'
|
||||
? undefined
|
||||
: currentStatus.value === 'ACTIVE'
|
||||
? 'ACTIVE'
|
||||
: 'INACTIVE',
|
||||
});
|
||||
if (result) {
|
||||
totalBranch.value = result.total;
|
||||
maxPageBranch.value = Math.ceil(result.total / pageSizeBranch.value);
|
||||
branch.value = result.result;
|
||||
}
|
||||
}
|
||||
|
||||
watch([inputSearch, currentStatus], async () => {
|
||||
await fetchList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="row justify-between bordered-b surface-3 full-width"
|
||||
style="z-index: 1"
|
||||
>
|
||||
<div class="row items-center col-12 col-md">
|
||||
<q-btn
|
||||
round
|
||||
icon="mdi-arrow-left"
|
||||
flat
|
||||
dense
|
||||
@click="$emit('back')"
|
||||
class="q-mr-md"
|
||||
/>
|
||||
|
||||
<q-card-section
|
||||
class="q-pa-sm q-pt-md q-mr-md q-mb-sm"
|
||||
:class="{
|
||||
color__purple: customerType === 'CORP',
|
||||
color__green: customerType === 'PERS',
|
||||
}"
|
||||
style="border-radius: 0 0 40px 40px; position: relative"
|
||||
>
|
||||
<q-avatar no-padding size="50px">
|
||||
<img :src="currentCustomerUrlImage ?? '/no-profile.png'" />
|
||||
</q-avatar>
|
||||
</q-card-section>
|
||||
<div>
|
||||
<div>{{ currentCustomerName }}</div>
|
||||
<div class="row items-center">
|
||||
<i
|
||||
class="isax isax-frame5"
|
||||
style="font-size: 1rem; color: var(--stone-6)"
|
||||
/>
|
||||
<div class="q-px-sm">
|
||||
{{ '10' }}
|
||||
</div>
|
||||
<q-icon name="mdi-home" size="16px" style="color: var(--stone-6)" />
|
||||
<div class="q-px-sm">
|
||||
{{ '2' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="row items-center justify-end q-px-md col-12 col-sm q-py-sm no-wrap"
|
||||
>
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
outlined
|
||||
dense
|
||||
class="col-6"
|
||||
:label="$t('search')"
|
||||
:bg-color="$q.dark.isActive ? 'dark' : 'white'"
|
||||
v-model="inputSearch"
|
||||
debounce="500"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="mdi-magnify" />
|
||||
</template>
|
||||
</q-input>
|
||||
|
||||
<q-select
|
||||
lazy-rules="ondemand"
|
||||
id="select-status"
|
||||
for="select-status"
|
||||
v-model="currentStatus"
|
||||
outlined
|
||||
dense
|
||||
option-value="value"
|
||||
option-label="label"
|
||||
class="col q-pl-sm"
|
||||
map-options
|
||||
emit-value
|
||||
:hide-dropdown-icon="$q.screen.lt.sm"
|
||||
:options="[
|
||||
{ label: $t('all'), value: 'All' },
|
||||
{ label: $t('statusACTIVE'), value: 'ACTIVE' },
|
||||
{ label: $t('statusINACTIVE'), value: 'INACTIVE' },
|
||||
]"
|
||||
></q-select>
|
||||
|
||||
<q-btn-toggle
|
||||
id="btn-mode"
|
||||
v-model="modeView"
|
||||
dense
|
||||
class="no-shadow bordered rounded surface-1 q-ml-sm"
|
||||
:toggle-color="$q.dark.isActive ? 'grey-9' : 'grey-2'"
|
||||
size="xs"
|
||||
:options="[
|
||||
{ value: true, slot: 'folder' },
|
||||
{ value: false, slot: 'list' },
|
||||
]"
|
||||
>
|
||||
<template v-slot:folder>
|
||||
<q-icon
|
||||
id="icon-mode-grid"
|
||||
name="mdi-view-grid-outline"
|
||||
size="16px"
|
||||
class="q-px-sm q-py-sm rounded"
|
||||
:style="{
|
||||
color: $q.dark.isActive
|
||||
? modeView
|
||||
? '#C9D3DB'
|
||||
: '#787B7C'
|
||||
: modeView
|
||||
? '#787B7C'
|
||||
: '#C9D3DB',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template v-slot:list>
|
||||
<q-icon
|
||||
id="icon-mode-list"
|
||||
name="mdi-format-list-bulleted"
|
||||
class="q-px-sm q-py-xs rounded"
|
||||
size="16px"
|
||||
:style="{
|
||||
color: $q.dark.isActive
|
||||
? modeView === true
|
||||
? '#C9D3DB'
|
||||
: '#787B7C'
|
||||
: modeView === false
|
||||
? '#787B7C'
|
||||
: '#C9D3DB',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
</q-btn-toggle>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="row q-pa-md col scroll full-width surface-2"
|
||||
:class="{
|
||||
'justify-center': totalBranch === 0,
|
||||
'items-center': totalBranch === 0,
|
||||
}"
|
||||
>
|
||||
<NoData v-if="totalBranch === 0" :not-found="!!inputSearch" />
|
||||
|
||||
<div style="width: 100%" v-else>
|
||||
<q-table
|
||||
v-if="branch"
|
||||
flat
|
||||
class="full-width"
|
||||
bordered
|
||||
:rows-per-page-options="[0]"
|
||||
:rows="branch"
|
||||
:columns="columns"
|
||||
:grid="modeView"
|
||||
card-container-class="row q-col-gutter-md"
|
||||
row-key="name"
|
||||
hide-pagination
|
||||
:visible-columns="branchFieldSelected"
|
||||
>
|
||||
<template #header>
|
||||
<q-tr style="background-color: hsla(var(--info-bg) / 0.07)">
|
||||
<q-th
|
||||
v-for="(v, i) in fieldDisplay"
|
||||
:key="v"
|
||||
:class="{ 'text-left': i === 0 }"
|
||||
>
|
||||
{{ $t(v) }}
|
||||
</q-th>
|
||||
<q-th auto-width />
|
||||
</q-tr>
|
||||
</template>
|
||||
<template #body="props">
|
||||
<q-tr
|
||||
:class="{
|
||||
'app-text-muted': props.row.status === 'INACTIVE',
|
||||
'cursor-pointer': props.row._count?.branch !== 0,
|
||||
}"
|
||||
:props="props"
|
||||
@click="$emit('viewDetail', props.row, props.rowIndex)"
|
||||
>
|
||||
<q-td v-if="branchFieldSelected.includes('branchName')">
|
||||
<div class="row items-center no-wrap">
|
||||
<div
|
||||
:class="{
|
||||
'status-active': props.row.status !== 'INACTIVE',
|
||||
'status-inactive': props.row.status === 'INACTIVE',
|
||||
}"
|
||||
class="q-mr-md"
|
||||
style="display: flex; margin-bottom: var(--size-2)"
|
||||
>
|
||||
<div
|
||||
class="branch-card__icon"
|
||||
:class="{ 'branch-card__dark': $q.dark.isActive }"
|
||||
>
|
||||
<q-icon
|
||||
size="md"
|
||||
style="scale: 0.8"
|
||||
name="mdi-office-building-outline"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col" style="min-width: fit-content">
|
||||
<div class="col">
|
||||
{{
|
||||
$i18n.locale === 'en-US'
|
||||
? props.row.nameEN
|
||||
: props.row.name
|
||||
}}
|
||||
</div>
|
||||
<div class="col app-text-muted">
|
||||
{{ props.row.code }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-td>
|
||||
<q-td
|
||||
v-if="branchFieldSelected.includes('customerBranchFormTab')"
|
||||
class="text-center"
|
||||
>
|
||||
{{ props.row.branchNo || '-' }}
|
||||
</q-td>
|
||||
<q-td
|
||||
v-if="branchFieldSelected.includes('address')"
|
||||
class="text-center"
|
||||
>
|
||||
{{
|
||||
$i18n.locale === 'en-US'
|
||||
? `${props.row.addressEN || ''} ${props.row.subDistrict?.nameEN || ''} ${props.row.district?.nameEN || ''} ${props.row.province?.nameEN || ''}`
|
||||
: `${props.row.address || ''} ${props.row.subDistrict?.name || ''} ${props.row.district?.name || ''} ${props.row.province?.name || ''}`
|
||||
}}
|
||||
</q-td>
|
||||
<q-td
|
||||
v-if="branchFieldSelected.includes('telephone')"
|
||||
class="text-center"
|
||||
>
|
||||
{{ props.row.telephoneNo || '-' }}
|
||||
</q-td>
|
||||
<q-td
|
||||
v-if="branchFieldSelected.includes('businessTypePure')"
|
||||
class="text-center"
|
||||
>
|
||||
{{ useOptionStore().mapOption(props.row.bussinessType) || '-' }}
|
||||
</q-td>
|
||||
<q-td
|
||||
v-if="branchFieldSelected.includes('totalEmployee')"
|
||||
class="text-center"
|
||||
>
|
||||
{{ props.row._count?.employee }}
|
||||
</q-td>
|
||||
<q-td>
|
||||
<q-btn
|
||||
icon="mdi-eye-outline"
|
||||
:id="`btn-eye-${props.row.customerName}`"
|
||||
size="sm"
|
||||
dense
|
||||
round
|
||||
flat
|
||||
@click="$emit('viewDetail', props.row, props.rowIndex)"
|
||||
/>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</template>
|
||||
<template #item="props">
|
||||
<div class="col-12 col-md-6">
|
||||
<BranchCardCustomer
|
||||
class="surface-1"
|
||||
:inactive="props.row.status === 'INACTIVE'"
|
||||
:color="customerType === 'CORP' ? 'corp' : 'pers'"
|
||||
:badgeField="['status']"
|
||||
:data="{
|
||||
customerBranchFormTab: props.row.branchNo,
|
||||
branchName: props.row.name,
|
||||
address:
|
||||
$i18n.locale === 'en-US'
|
||||
? `${props.row.addressEN || ''} ${props.row.subDistrict?.nameEN || ''} ${props.row.district?.nameEN || ''} ${props.row.province?.nameEN || ''}`
|
||||
: `${props.row.address || ''} ${props.row.subDistrict?.name || ''} ${props.row.district?.name || ''} ${props.row.province?.name || ''}`,
|
||||
telephone: props.row.telephoneNo,
|
||||
businessTypePure: useOptionStore().mapOption(
|
||||
props.row.bussinessType,
|
||||
),
|
||||
totalEmployee: props.row._count?.employee,
|
||||
}"
|
||||
:visible-columns="branchFieldSelected"
|
||||
@view-detail="$emit('viewDetail', props.row, props.rowIndex)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</q-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row justify-between items-center q-px-md q-py-sm surface-2">
|
||||
<div class="col"></div>
|
||||
<div class="app-text-muted col text-center">
|
||||
{{
|
||||
$t('recordsPage', {
|
||||
resultcurrentPage: branch?.length,
|
||||
total: totalBranch,
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
<div class="col-4 flex justify-end">
|
||||
<PaginationComponent
|
||||
v-model:current-page="currentPageBranch"
|
||||
v-model:max-page="maxPageBranch"
|
||||
:fetch-data="
|
||||
async () => {
|
||||
await fetchList();
|
||||
flowStore.rotate();
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.branch-card__icon {
|
||||
--_branch-card-bg: var(--pink-6-hsl);
|
||||
|
||||
background-color: hsla(var(--_branch-card-bg) / 0.15);
|
||||
border-radius: 50%;
|
||||
padding: var(--size-1);
|
||||
position: relative;
|
||||
transform: rotate(45deg);
|
||||
|
||||
&.branch-card__dark {
|
||||
--_branch-card-bg: var(--pink-5-hsl);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
block-size: 0.5rem;
|
||||
aspect-ratio: 1;
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
right: -0.25rem;
|
||||
top: calc(50% - 0.25rem);
|
||||
bottom: calc(50% - 0.25rem);
|
||||
background-color: hsla(var(--_branch-status-color) / 1);
|
||||
}
|
||||
|
||||
& :deep(.q-icon) {
|
||||
transform: rotate(-45deg);
|
||||
color: hsla(var(--_branch-card-bg) / 1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { QSelect } from 'quasar';
|
||||
import { selectFilterOptionRefMod } from 'stores/utils';
|
||||
import { getRole } from 'src/services/keycloak';
|
||||
import { onMounted } from 'vue';
|
||||
import {
|
||||
SaveButton,
|
||||
EditButton,
|
||||
CancelButton,
|
||||
DeleteButton,
|
||||
} from 'src/components/button';
|
||||
|
||||
defineProps<{
|
||||
prefixId?: string;
|
||||
outlined?: boolean;
|
||||
readonly?: boolean;
|
||||
create?: boolean;
|
||||
actionDisabled?: boolean;
|
||||
customerType?: 'CORP' | 'PERS';
|
||||
}>();
|
||||
defineEmits<{
|
||||
(e: 'save'): void;
|
||||
(e: 'edit'): void;
|
||||
(e: 'delete'): void;
|
||||
(e: 'cancel'): void;
|
||||
}>();
|
||||
|
||||
const personName = defineModel<string>('personName', { required: true });
|
||||
const customerName = defineModel<string>('customerName', { required: true });
|
||||
const customerNameEN = defineModel<string>('customerNameEn', {
|
||||
required: true,
|
||||
});
|
||||
const taxNo = defineModel<string | null>('taxNo');
|
||||
const registeredBranchId = defineModel<string>('registeredBranchId', {
|
||||
required: true,
|
||||
});
|
||||
|
||||
const branchOptions = defineModel<{ id: string; name: string }[]>(
|
||||
'branchOptions',
|
||||
{ default: [] },
|
||||
);
|
||||
const filteredBranchOptions = ref<Record<string, unknown>[]>([]);
|
||||
|
||||
let branchFilter: (
|
||||
value: string,
|
||||
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void,
|
||||
) => void;
|
||||
|
||||
onMounted(() => {
|
||||
branchFilter = selectFilterOptionRefMod(
|
||||
branchOptions,
|
||||
filteredBranchOptions,
|
||||
'name',
|
||||
);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => branchOptions.value,
|
||||
() => {
|
||||
branchFilter = selectFilterOptionRefMod(
|
||||
branchOptions,
|
||||
filteredBranchOptions,
|
||||
'name',
|
||||
);
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-12 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-office-building-outline"
|
||||
style="background-color: var(--surface-3)"
|
||||
/>
|
||||
<span>{{ $t('customer.form.group.basicInfo') }}</span>
|
||||
<EditButton
|
||||
v-if="readonly && !create"
|
||||
type="button"
|
||||
@click="$emit('edit')"
|
||||
class="q-ml-auto"
|
||||
:disabled="actionDisabled"
|
||||
/>
|
||||
<DeleteButton
|
||||
v-if="readonly && !create"
|
||||
@click="$emit('delete')"
|
||||
type="button"
|
||||
class="q-ml-sm"
|
||||
:disabled="actionDisabled"
|
||||
/>
|
||||
<SaveButton
|
||||
v-if="!readonly"
|
||||
@click="$emit('save')"
|
||||
class="q-ml-auto"
|
||||
:disabled="actionDisabled"
|
||||
/>
|
||||
<CancelButton
|
||||
v-if="!readonly && !create"
|
||||
type="button"
|
||||
class="q-ml-sm"
|
||||
:disabled="actionDisabled"
|
||||
@click="$emit('cancel')"
|
||||
/>
|
||||
</div>
|
||||
<q-select
|
||||
outlined
|
||||
clearable
|
||||
use-input
|
||||
fill-input
|
||||
emit-value
|
||||
map-options
|
||||
hide-selected
|
||||
hide-bottom-space
|
||||
dense
|
||||
class="col-12 col-md-4"
|
||||
option-value="id"
|
||||
input-debounce="0"
|
||||
option-label="name"
|
||||
lazy-rules="ondemand"
|
||||
v-model="registeredBranchId"
|
||||
:readonly="readonly"
|
||||
:options="filteredBranchOptions"
|
||||
:hide-dropdown-icon="readonly"
|
||||
:label="$t('registeredBranch')"
|
||||
:for="`${prefixId}-input-source-nationality`"
|
||||
:rules="[
|
||||
(val) => {
|
||||
const roles = getRole() || [];
|
||||
return (
|
||||
['admin', 'system', 'head_of_admin'].some((v) =>
|
||||
roles.includes(v),
|
||||
) ||
|
||||
!!val ||
|
||||
$t('form.error.required')
|
||||
);
|
||||
},
|
||||
]"
|
||||
@filter="branchFilter"
|
||||
>
|
||||
<template v-slot:no-option>
|
||||
<q-item>
|
||||
<q-item-section class="text-grey">
|
||||
{{ $t('noResults') }}
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
dense
|
||||
outlined
|
||||
hide-bottom-space
|
||||
:label="$t('customer.form.customerName')"
|
||||
v-model="customerName"
|
||||
class="col-12 col-md-4"
|
||||
:readonly="readonly"
|
||||
:for="`${prefixId}-input-company-name`"
|
||||
/>
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
dense
|
||||
outlined
|
||||
:readonly="readonly"
|
||||
hide-bottom-space
|
||||
:label="$t('customer.form.customerNameEN')"
|
||||
v-model="customerNameEN"
|
||||
:for="`${prefixId}-input-company-name-en`"
|
||||
class="col-12 col-md-4"
|
||||
/>
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
dense
|
||||
outlined
|
||||
:readonly="readonly"
|
||||
hide-bottom-space
|
||||
v-model="personName"
|
||||
:label="$t('customer.form.personName')"
|
||||
:for="`${prefixId}-input-person-name`"
|
||||
:rules="[(v: string) => !!v || $t('form.error.required')]"
|
||||
class="col-12 col-md-4"
|
||||
/>
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
dense
|
||||
outlined
|
||||
v-if="customerType === 'PERS'"
|
||||
:readonly="readonly"
|
||||
hide-bottom-space
|
||||
:label="$t('customer.form.taxIdentificationNumber')"
|
||||
v-model="taxNo"
|
||||
:for="`${prefixId}-input-tax-no`"
|
||||
class="col-12 col-md-4"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,291 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import FormAddress from 'src/components/02_personnel-management/FormAddress.vue';
|
||||
import FormBusiness from './EmployerFormBusiness.vue';
|
||||
import FormContact from './EmployerFormContact.vue';
|
||||
import { CustomerCreate } from 'src/stores/customer/types';
|
||||
import { dateFormat, parseAndFormatDate } from 'src/utils/datetime';
|
||||
import { EditButton } from 'src/components/button';
|
||||
import DeleteButton from 'src/components/button/DeleteButton.vue';
|
||||
import SaveButton from 'src/components/button/SaveButton.vue';
|
||||
import CancelButton from 'src/components/button/CancelButton.vue';
|
||||
|
||||
const item = defineModel<NonNullable<CustomerCreate['customerBranch']>[number]>(
|
||||
'customerBranch',
|
||||
{
|
||||
required: true,
|
||||
default: [],
|
||||
},
|
||||
);
|
||||
const tab = ref('main');
|
||||
|
||||
defineEmits<{
|
||||
(e: 'edit'): void;
|
||||
(e: 'save'): void;
|
||||
(e: 'cancel'): void;
|
||||
(e: 'delete'): void;
|
||||
}>();
|
||||
|
||||
defineProps<{
|
||||
readonly?: boolean;
|
||||
prefixId?: string;
|
||||
actionDisabled?: boolean;
|
||||
customerType?: 'CORP' | 'PERS';
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span
|
||||
class="col-12 text-weight-bold row items-center q-mb-sm"
|
||||
:style="{ opacity: actionDisabled ? '.5' : undefined }"
|
||||
:id="`form-branch-customer-no-${item.branchNo}`"
|
||||
>
|
||||
{{ $t('customer.form.branch.title', { name: item.branchNo || 0 }) }}
|
||||
<EditButton
|
||||
v-if="readonly"
|
||||
@click="$emit('edit')"
|
||||
class="q-ml-auto"
|
||||
type="button"
|
||||
:disabled="actionDisabled"
|
||||
/>
|
||||
<DeleteButton
|
||||
v-if="readonly"
|
||||
@click="$emit('delete')"
|
||||
class="q-ml-sm"
|
||||
type="button"
|
||||
:disabled="actionDisabled"
|
||||
/>
|
||||
<SaveButton
|
||||
v-if="!readonly"
|
||||
@click="$emit('save')"
|
||||
class="q-ml-auto"
|
||||
type="submit"
|
||||
:disabled="actionDisabled"
|
||||
/>
|
||||
<CancelButton
|
||||
v-if="!readonly"
|
||||
@click="$emit('cancel')"
|
||||
class="q-ml-sm"
|
||||
type="button"
|
||||
:disabled="actionDisabled"
|
||||
/>
|
||||
</span>
|
||||
|
||||
<div class="col-12" :style="{ opacity: actionDisabled ? '.5' : undefined }">
|
||||
<div class="rounded bordered">
|
||||
<q-tabs
|
||||
v-model="tab"
|
||||
dense
|
||||
align="left"
|
||||
class="bordered-b"
|
||||
active-color="primary"
|
||||
no-caps
|
||||
>
|
||||
<q-tab name="main" label="Main"></q-tab>
|
||||
<q-tab name="address" label="Address"></q-tab>
|
||||
<q-tab name="business" label="Business"></q-tab>
|
||||
<q-tab name="contact" label="Contact"></q-tab>
|
||||
</q-tabs>
|
||||
<q-tab-panels v-model="tab">
|
||||
<q-tab-panel name="main">
|
||||
<div class="row q-col-gutter-md">
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
dense
|
||||
outlined
|
||||
:readonly="readonly"
|
||||
hide-bottom-space
|
||||
:label="$t('customerBranch.form.no')"
|
||||
v-model="item.branchNo"
|
||||
:for="`${prefixId}-input-branch-branch-no`"
|
||||
type="number"
|
||||
class="col-12 col-md-2"
|
||||
/>
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
dense
|
||||
outlined
|
||||
readonly
|
||||
hide-bottom-space
|
||||
:label="$t('customerBranch.form.code')"
|
||||
:model-value="item.code || '-'"
|
||||
:for="`${prefixId}-input-branch-branch-code`"
|
||||
class="col-12 col-md-4"
|
||||
/>
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
dense
|
||||
outlined
|
||||
:readonly="readonly"
|
||||
hide-bottom-space
|
||||
:label="$t('customerBranch.form.taxNo')"
|
||||
v-model="item.taxNo"
|
||||
:for="`${prefixId}-input-branch-tax-no`"
|
||||
type="number"
|
||||
class="col-12 col-md-4"
|
||||
/>
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
dense
|
||||
outlined
|
||||
:readonly="readonly"
|
||||
hide-bottom-space
|
||||
:label="$t('customerBranch.form.name')"
|
||||
v-model="item.name"
|
||||
:for="`${prefixId}-input-branch-tax-no`"
|
||||
class="col-12 col-md-6"
|
||||
/>
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
dense
|
||||
outlined
|
||||
:readonly="readonly"
|
||||
hide-bottom-space
|
||||
:label="$t('customerBranch.form.nameEN')"
|
||||
v-model="item.nameEN"
|
||||
:for="`${prefixId}-input-branch-tax-no`"
|
||||
class="col-12 col-md-6"
|
||||
/>
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
dense
|
||||
outlined
|
||||
v-if="customerType === 'CORP'"
|
||||
:readonly="readonly"
|
||||
hide-bottom-space
|
||||
:label="$t('customerBranch.form.registerName')"
|
||||
v-model="item.registerName"
|
||||
:for="`${prefixId}-input-branch-register-name`"
|
||||
class="col-12 col-md-4"
|
||||
/>
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
dense
|
||||
outlined
|
||||
:readonly="readonly"
|
||||
hide-bottom-space
|
||||
v-if="customerType === 'CORP'"
|
||||
:label="$t('customerBranch.form.authorizedCapital')"
|
||||
v-model="item.authorizedCapital"
|
||||
:for="`${prefixId}-input-branch-authorized-capital`"
|
||||
class="col-12 col-md-4"
|
||||
/>
|
||||
<VueDatePicker
|
||||
:id="`${prefixId}-picker-date-register-date`"
|
||||
:teleport="true"
|
||||
utc
|
||||
v-if="customerType === 'CORP'"
|
||||
auto-apply
|
||||
v-model="item.registerDate"
|
||||
:dark="$q.dark.isActive"
|
||||
:locale="$i18n.locale === 'th-th' ? 'th' : 'en'"
|
||||
:enableTimePicker="false"
|
||||
:disabled="readonly"
|
||||
class="col-md-4 col-12"
|
||||
>
|
||||
<template #year="{ value }">
|
||||
{{ $i18n.locale === 'th-th' ? value + 543 : value }}
|
||||
</template>
|
||||
<template #year-overlay-value="{ value }">
|
||||
{{ $i18n.locale === 'th-th' ? value + 543 : value }}
|
||||
</template>
|
||||
<template #trigger>
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
:for="`${prefixId}-input-start-date`"
|
||||
:id="`${prefixId}-input-start-date`"
|
||||
:label="$t('customerBranch.form.registerDate')"
|
||||
dense
|
||||
outlined
|
||||
:readonly="readonly"
|
||||
placeholder="DD/MM/YYYY"
|
||||
:mask="readonly ? '' : '##/##/####'"
|
||||
:model-value="
|
||||
item.registerDate
|
||||
? readonly
|
||||
? dateFormat(item.registerDate)
|
||||
: dateFormat(item.registerDate, false, false, true)
|
||||
: undefined
|
||||
"
|
||||
@update:model-value="
|
||||
(v) => {
|
||||
if (readonly && v && v.toString().length === 10) {
|
||||
const _date = parseAndFormatDate(v, $i18n.locale);
|
||||
if (_date) {
|
||||
item.registerDate?.setDate(_date.getDate());
|
||||
item.registerDate?.setMonth(_date.getMonth());
|
||||
item.registerDate?.setFullYear(_date.getFullYear());
|
||||
} else {
|
||||
item.registerDate = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon
|
||||
size="xs"
|
||||
name="mdi-calendar-blank-outline"
|
||||
class="cursor-pointer"
|
||||
color="primary"
|
||||
/>
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<q-icon
|
||||
v-if="!item.registerDate && readonly"
|
||||
name="mdi-close-circle"
|
||||
class="cursor-pointer app-text-muted"
|
||||
size="sm"
|
||||
@click="item.registerDate = null"
|
||||
/>
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
</VueDatePicker>
|
||||
</div>
|
||||
</q-tab-panel>
|
||||
<q-tab-panel name="address">
|
||||
<FormAddress
|
||||
:prefix-id="prefixId || 'employer'"
|
||||
hide-title
|
||||
dense
|
||||
:readonly="readonly"
|
||||
outlined
|
||||
:title="$t('form.address')"
|
||||
v-model:address="item.address"
|
||||
v-model:addressEN="item.addressEN"
|
||||
v-model:province-id="item.provinceId"
|
||||
v-model:district-id="item.districtId"
|
||||
v-model:sub-district-id="item.subDistrictId"
|
||||
:addressTitle="$t('form.address')"
|
||||
:addressTitleEN="$t('form.address', { suffix: '(EN)' })"
|
||||
/>
|
||||
</q-tab-panel>
|
||||
<q-tab-panel name="business">
|
||||
<FormBusiness
|
||||
dense
|
||||
outlined
|
||||
:prefix-id="prefixId || 'employer'"
|
||||
:readonly="readonly"
|
||||
v-model:employment-office="item.employmentOffice"
|
||||
v-model:bussiness-type="item.bussinessType"
|
||||
v-model:bussiness-type-en="item.bussinessTypeEN"
|
||||
v-model:job-position="item.jobPosition"
|
||||
v-model:job-position-en="item.jobPositionEN"
|
||||
v-model:job-description="item.jobDescription"
|
||||
v-model:sale-employee="item.saleEmployee"
|
||||
v-model:pay-date="item.payDate"
|
||||
v-model:wage-rate="item.wageRate"
|
||||
/>
|
||||
</q-tab-panel>
|
||||
<q-tab-panel name="contact">
|
||||
<FormContact
|
||||
:readonly="readonly"
|
||||
v-model:email="item.email"
|
||||
v-model:telephone="item.telephoneNo"
|
||||
/>
|
||||
</q-tab-panel>
|
||||
</q-tab-panels>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,286 @@
|
|||
<script setup lang="ts">
|
||||
import { selectFilterOptionRefMod } from 'src/stores/utils';
|
||||
import { dateFormat, parseAndFormatDate } from 'src/utils/datetime';
|
||||
import { onMounted, watch } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const { locale } = useI18n({ useScope: 'global' });
|
||||
|
||||
const employmentOffice = defineModel<string>('employmentOffice');
|
||||
const bussinessType = defineModel<string>('bussinessType');
|
||||
const jobPosition = defineModel<string>('jobPosition');
|
||||
|
||||
const bussinessTypeEN = defineModel<string>('bussinessTypeEn');
|
||||
const jobPositionEN = defineModel<string>('jobPositionEn');
|
||||
|
||||
const jobDescription = defineModel<string>('jobDescription');
|
||||
|
||||
const payDate = defineModel<Date | null | string>('payDate');
|
||||
const wageRate = defineModel<number>('wageRate');
|
||||
|
||||
const saleEmployee = defineModel<string>('saleEmployee');
|
||||
|
||||
const rawOption = ref();
|
||||
|
||||
const typeBusinessOption = ref([]);
|
||||
const jobPositionOption = ref([]);
|
||||
|
||||
defineProps<{
|
||||
title?: string;
|
||||
dense?: boolean;
|
||||
outlined?: boolean;
|
||||
readonly?: boolean;
|
||||
prefixId: string;
|
||||
}>();
|
||||
|
||||
onMounted(async () => {
|
||||
const resultOption = await fetch('/option/option.json');
|
||||
rawOption.value = await resultOption.json();
|
||||
|
||||
if (locale.value === 'en-US') {
|
||||
typeBusinessOption.value = rawOption.value.eng.businessType;
|
||||
jobPositionOption.value = rawOption.value.eng.position;
|
||||
}
|
||||
if (locale.value === 'th-th') {
|
||||
typeBusinessOption.value = rawOption.value.tha.businessType;
|
||||
jobPositionOption.value = rawOption.value.tha.position;
|
||||
}
|
||||
});
|
||||
watch(typeBusinessOption, () => {
|
||||
typeBusinessFilter = selectFilterOptionRefMod(
|
||||
typeBusinessOption,
|
||||
typeBusinessOptions,
|
||||
'label',
|
||||
);
|
||||
});
|
||||
|
||||
watch(jobPositionOption, () => {
|
||||
jobPositionFilter = selectFilterOptionRefMod(
|
||||
jobPositionOption,
|
||||
jobPositionOptions,
|
||||
'label',
|
||||
);
|
||||
});
|
||||
|
||||
const typeBusinessOptions = ref<Record<string, unknown>[]>([]);
|
||||
let typeBusinessFilter = selectFilterOptionRefMod(
|
||||
typeBusinessOption,
|
||||
typeBusinessOptions,
|
||||
'label',
|
||||
);
|
||||
|
||||
const jobPositionOptions = ref<Record<string, unknown>[]>([]);
|
||||
let jobPositionFilter = selectFilterOptionRefMod(
|
||||
jobPositionOption,
|
||||
jobPositionOptions,
|
||||
'label',
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<div class="col-12 row q-col-gutter-sm">
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
:for="`${prefixId}-input-employment-office`"
|
||||
:id="`${prefixId}-input-employment-office`"
|
||||
:dense="dense"
|
||||
outlined
|
||||
:readonly="readonly"
|
||||
hide-bottom-space
|
||||
class="col-12"
|
||||
:label="$t('inputCustomerAddress')"
|
||||
v-model="employmentOffice"
|
||||
/>
|
||||
|
||||
<q-select
|
||||
outlined
|
||||
clearable
|
||||
use-input
|
||||
fill-input
|
||||
emit-value
|
||||
map-options
|
||||
hide-selected
|
||||
hide-bottom-space
|
||||
input-debounce="0"
|
||||
option-value="value"
|
||||
option-label="label"
|
||||
lazy-rules="ondemand"
|
||||
v-model="bussinessType"
|
||||
class="col-md-6 col-12"
|
||||
:dense="dense"
|
||||
:readonly="readonly"
|
||||
:label="$t('businessType')"
|
||||
:options="typeBusinessOptions"
|
||||
:for="`${prefixId}-select-business-type`"
|
||||
@filter="typeBusinessFilter"
|
||||
>
|
||||
<template v-slot:no-option>
|
||||
<q-item>
|
||||
<q-item-section class="text-grey">
|
||||
{{ $t('noResults') }}
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
:for="`${prefixId}-input-bussiness-type-en`"
|
||||
:id="`${prefixId}-input-bussiness-type-en`"
|
||||
:dense="dense"
|
||||
outlined
|
||||
:readonly="readonly"
|
||||
hide-bottom-space
|
||||
class="col-md-6 col-12"
|
||||
:label="$t('businessTypeEN')"
|
||||
v-model="bussinessTypeEN"
|
||||
/>
|
||||
|
||||
<q-select
|
||||
outlined
|
||||
clearable
|
||||
use-input
|
||||
fill-input
|
||||
emit-value
|
||||
map-options
|
||||
hide-selected
|
||||
hide-bottom-space
|
||||
input-debounce="0"
|
||||
option-value="value"
|
||||
option-label="label"
|
||||
lazy-rules="ondemand"
|
||||
v-model="jobPosition"
|
||||
class="col-md-6 col-12"
|
||||
:dense="dense"
|
||||
:readonly="readonly"
|
||||
:label="$t('jobPosition')"
|
||||
:options="jobPositionOptions"
|
||||
:for="`${prefixId}-select-job-position`"
|
||||
@filter="jobPositionFilter"
|
||||
>
|
||||
<template v-slot:no-option>
|
||||
<q-item>
|
||||
<q-item-section class="text-grey">
|
||||
{{ $t('noResults') }}
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
:for="`${prefixId}-input-job-position-en`"
|
||||
:id="`${prefixId}-input-job-position-en`"
|
||||
:dense="dense"
|
||||
outlined
|
||||
:readonly="readonly"
|
||||
hide-bottom-space
|
||||
class="col-md-6 col-12"
|
||||
:label="$t('jobPositionEN')"
|
||||
v-model="jobPositionEN"
|
||||
/>
|
||||
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
:for="`${prefixId}-input-job-description`"
|
||||
:id="`${prefixId}-input-job-description`"
|
||||
:dense="dense"
|
||||
outlined
|
||||
:readonly="readonly"
|
||||
hide-bottom-space
|
||||
class="col-md-6 col-12"
|
||||
:label="$t('jobDescription')"
|
||||
v-model="jobDescription"
|
||||
/>
|
||||
|
||||
<VueDatePicker
|
||||
:id="`${prefixId}-date-picker-start-date`"
|
||||
:teleport="true"
|
||||
utc
|
||||
autoApply
|
||||
v-model="payDate"
|
||||
:dark="$q.dark.isActive"
|
||||
:locale="$i18n.locale === 'th-th' ? 'th' : 'en'"
|
||||
:enableTimePicker="false"
|
||||
:disabled="readonly"
|
||||
class="col-md-3 col-12"
|
||||
>
|
||||
<template #year="{ value }">
|
||||
{{ $i18n.locale === 'th-th' ? value + 543 : value }}
|
||||
</template>
|
||||
<template #year-overlay-value="{ value }">
|
||||
{{ $i18n.locale === 'th-th' ? value + 543 : value }}
|
||||
</template>
|
||||
<template #trigger>
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
:for="`${prefixId}-input-start-date`"
|
||||
:id="`${prefixId}-input-start-date`"
|
||||
:label="$t('payDay')"
|
||||
:dense="dense"
|
||||
outlined
|
||||
:readonly="readonly"
|
||||
placeholder="DD/MM/YYYY"
|
||||
:mask="readonly ? '' : '##/##/####'"
|
||||
:model-value="
|
||||
payDate
|
||||
? readonly
|
||||
? dateFormat(payDate)
|
||||
: dateFormat(payDate, false, false, true)
|
||||
: undefined
|
||||
"
|
||||
@update:model-value="
|
||||
(v) => {
|
||||
if (v && v.toString().length === 10) {
|
||||
payDate = parseAndFormatDate(v, locale);
|
||||
}
|
||||
}
|
||||
"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon
|
||||
size="xs"
|
||||
name="mdi-calendar-blank-outline"
|
||||
class="cursor-pointer"
|
||||
color="primary"
|
||||
/>
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<q-icon
|
||||
v-if="payDate && !readonly"
|
||||
name="mdi-close-circle"
|
||||
class="cursor-pointer app-text-muted"
|
||||
size="sm"
|
||||
@click="payDate = undefined"
|
||||
/>
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
</VueDatePicker>
|
||||
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
:for="`${prefixId}-input-pay-rate`"
|
||||
:id="`${prefixId}-input-pay-rate`"
|
||||
:dense="dense"
|
||||
outlined
|
||||
:readonly="readonly"
|
||||
hide-bottom-space
|
||||
class="col-md-3 col-6"
|
||||
:label="$t('payRate')"
|
||||
v-model="wageRate"
|
||||
/>
|
||||
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
:for="`${prefixId}-input-sales-person`"
|
||||
:id="`${prefixId}-input-sales-person`"
|
||||
:dense="dense"
|
||||
outlined
|
||||
:readonly="readonly"
|
||||
hide-bottom-space
|
||||
class="col-6"
|
||||
:label="$t('salesPerson')"
|
||||
v-model="saleEmployee"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<script lang="ts" setup>
|
||||
defineProps<{
|
||||
readonly?: boolean;
|
||||
prefixId?: string;
|
||||
}>();
|
||||
const mail = defineModel<string>('mail');
|
||||
const telephone = defineModel<string>('telephone');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="col-md-9 col-12 row q-col-gutter-md">
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
:for="`${prefixId}-input-mail`"
|
||||
:id="`${prefixId}-input-mail`"
|
||||
dense
|
||||
outlined
|
||||
:readonly="readonly"
|
||||
hide-bottom-space
|
||||
class="col-md-6 col-12"
|
||||
:label="$t('formDialogInputEmail')"
|
||||
v-model="mail"
|
||||
/>
|
||||
<q-input
|
||||
lazy-rules="ondemand"
|
||||
:for="`${prefixId}-input-telephone`"
|
||||
:id="`${prefixId}-input-telephone`"
|
||||
dense
|
||||
outlined
|
||||
:readonly="readonly"
|
||||
hide-bottom-space
|
||||
class="col-md-6 col-12"
|
||||
:label="$t('formDialogInputTelephone')"
|
||||
v-model="telephone"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
Loading…
Add table
Add a link
Reference in a new issue