refactor: add new components
This commit is contained in:
parent
d012bb4d70
commit
18d0aca1de
2 changed files with 271 additions and 0 deletions
51
src/components/shared/ToggleView.vue
Normal file
51
src/components/shared/ToggleView.vue
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
const model = defineModel<boolean>('model');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<q-btn-toggle
|
||||||
|
id="btn-mode"
|
||||||
|
v-model="model"
|
||||||
|
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
|
||||||
|
name="mdi-view-grid-outline"
|
||||||
|
size="16px"
|
||||||
|
class="q-px-sm q-py-xs rounded"
|
||||||
|
:style="{
|
||||||
|
color: $q.dark.isActive
|
||||||
|
? model
|
||||||
|
? '#C9D3DB '
|
||||||
|
: '#787B7C'
|
||||||
|
: model
|
||||||
|
? '#787B7C'
|
||||||
|
: '#C9D3DB',
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-slot:list>
|
||||||
|
<q-icon
|
||||||
|
name="mdi-format-list-bulleted"
|
||||||
|
class="q-px-sm q-py-xs rounded"
|
||||||
|
size="16px"
|
||||||
|
:style="{
|
||||||
|
color: $q.dark.isActive
|
||||||
|
? model === false
|
||||||
|
? '#C9D3DB'
|
||||||
|
: '#787B7C'
|
||||||
|
: model === false
|
||||||
|
? '#787B7C'
|
||||||
|
: '#C9D3DB',
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</q-btn-toggle>
|
||||||
|
</template>
|
||||||
220
src/components/shared/table/TableWorker.vue
Normal file
220
src/components/shared/table/TableWorker.vue
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import { Lang } from 'src/utils/ui';
|
||||||
|
|
||||||
|
import { QTableSlots, QTableColumn } from 'quasar';
|
||||||
|
import { Employee } from 'src/stores/employee/types';
|
||||||
|
|
||||||
|
import { calculateAge, dateFormatJS } from 'src/utils/datetime';
|
||||||
|
import useOptionStore from 'stores/options';
|
||||||
|
|
||||||
|
const selected = defineModel<Employee[]>('selected');
|
||||||
|
|
||||||
|
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;
|
||||||
|
const { locale } = useI18n();
|
||||||
|
const optionStore = useOptionStore();
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
(e: 'create'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
type ExclusiveProps = {
|
||||||
|
grid?: boolean;
|
||||||
|
disabledWorkerId?: string[];
|
||||||
|
rows: Employee[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
name: '#check',
|
||||||
|
align: 'left',
|
||||||
|
label: '',
|
||||||
|
field: (_: any) => '#check',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'order',
|
||||||
|
align: 'center',
|
||||||
|
label: 'general.order',
|
||||||
|
field: (e: Employee & { _index: number }) => e._index + 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'foreignRefNo',
|
||||||
|
align: 'center',
|
||||||
|
label: 'quotation.foreignRefNo',
|
||||||
|
field: (v: Employee) =>
|
||||||
|
v.employeePassport !== undefined
|
||||||
|
? v.employeePassport[0]?.number || '-'
|
||||||
|
: '-',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'employeeName',
|
||||||
|
align: 'left',
|
||||||
|
label: 'quotation.employeeName',
|
||||||
|
field: (v: Employee) =>
|
||||||
|
locale.value === Lang.English
|
||||||
|
? `${v.firstNameEN} ${v.lastNameEN}`
|
||||||
|
: `${v.firstName} ${v.lastName}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'birthDate',
|
||||||
|
align: 'left',
|
||||||
|
label: 'general.birthDate',
|
||||||
|
field: (v: Employee) => dateFormatJS({ date: v.dateOfBirth }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'age',
|
||||||
|
align: 'left',
|
||||||
|
label: 'general.age',
|
||||||
|
field: (v: Employee) => calculateAge(v.dateOfBirth),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'nationality',
|
||||||
|
align: 'left',
|
||||||
|
label: 'general.nationality',
|
||||||
|
field: (v: Employee) => optionStore.mapOption(v.nationality),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'documentExpireDate',
|
||||||
|
align: 'left',
|
||||||
|
label: 'quotation.documentExpireDate',
|
||||||
|
field: (v: Employee) =>
|
||||||
|
v.employeePassport !== undefined &&
|
||||||
|
v.employeePassport[0]?.expireDate !== undefined
|
||||||
|
? dateFormatJS({ date: v.employeePassport[0]?.expireDate })
|
||||||
|
: '-',
|
||||||
|
},
|
||||||
|
] satisfies QTableColumn[];
|
||||||
|
|
||||||
|
const props = defineProps<ExclusiveProps>();
|
||||||
|
|
||||||
|
function getEmployeeImageUrl(item: Employee) {
|
||||||
|
if (item.selectedImage) {
|
||||||
|
return `${API_BASE_URL}/employee/${item.id}/image/${item.selectedImage}`;
|
||||||
|
}
|
||||||
|
// NOTE: static image
|
||||||
|
return `/images/employee-avatar-${item.gender}.png`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUpdate() {
|
||||||
|
if (selected.value?.length === 0) {
|
||||||
|
selected.value = props.rows?.filter(
|
||||||
|
(v) => !props.disabledWorkerId?.includes(v.id),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
selected.value = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectedIndex(item: Employee) {
|
||||||
|
return selected.value?.findIndex((v) => v.id === item.id);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<q-table
|
||||||
|
v-model:selected="selected"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
:rows="
|
||||||
|
rows.map((data, i) => ({
|
||||||
|
...data,
|
||||||
|
_index: i,
|
||||||
|
}))
|
||||||
|
"
|
||||||
|
:grid
|
||||||
|
:columns
|
||||||
|
hide-bottom
|
||||||
|
bordered
|
||||||
|
flat
|
||||||
|
hide-pagination
|
||||||
|
selection="multiple"
|
||||||
|
card-container-class="q-col-gutter-sm"
|
||||||
|
class="full-width"
|
||||||
|
row-key="id"
|
||||||
|
>
|
||||||
|
<template v-slot:header="props">
|
||||||
|
<q-tr
|
||||||
|
style="background-color: hsla(var(--info-bg) / 0.07)"
|
||||||
|
:props="props"
|
||||||
|
>
|
||||||
|
<q-th v-for="col in columns" :key="col.name" :props="props">
|
||||||
|
<template v-if="!col.name.startsWith('#')">
|
||||||
|
{{ $t(col.label) }}
|
||||||
|
</template>
|
||||||
|
<template v-if="col.name === '#check'">
|
||||||
|
<q-checkbox
|
||||||
|
v-model="props.selected"
|
||||||
|
@update:model-value="(v) => handleUpdate()"
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</q-th>
|
||||||
|
</q-tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template
|
||||||
|
v-slot:body="props: {
|
||||||
|
row: Employee & { _index: number };
|
||||||
|
} & Omit<Parameters<QTableSlots['body']>[0], 'row'>"
|
||||||
|
>
|
||||||
|
<q-tr
|
||||||
|
:class="{
|
||||||
|
dark: $q.dark.isActive,
|
||||||
|
'selectable-item__disabled': disabledWorkerId?.some(
|
||||||
|
(id) => id === props.row.id,
|
||||||
|
),
|
||||||
|
}"
|
||||||
|
class="text-center"
|
||||||
|
>
|
||||||
|
<q-td v-for="col in columns" :align="col.align" :key="col.name">
|
||||||
|
<!-- NOTE: custom column will starts with # -->
|
||||||
|
<template v-if="!col.name.startsWith('#')">
|
||||||
|
<q-avatar
|
||||||
|
v-if="col.name === 'employeeName'"
|
||||||
|
class="q-mr-sm"
|
||||||
|
size="md"
|
||||||
|
>
|
||||||
|
<q-img
|
||||||
|
:src="getEmployeeImageUrl(props.row)"
|
||||||
|
:ratio="1"
|
||||||
|
class="text-center"
|
||||||
|
/>
|
||||||
|
</q-avatar>
|
||||||
|
<span>
|
||||||
|
{{
|
||||||
|
typeof col.field === 'string'
|
||||||
|
? props.row[col.field as keyof Employee]
|
||||||
|
: col.field(props.row)
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template
|
||||||
|
v-if="
|
||||||
|
col.name === '#check' &&
|
||||||
|
!disabledWorkerId?.some((id) => id === props.row.id)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<q-checkbox v-model="props.selected" size="sm" />
|
||||||
|
</template>
|
||||||
|
</q-td>
|
||||||
|
</q-tr>
|
||||||
|
</template>
|
||||||
|
<template v-slot:item="props: { row: Employee; rowIndex: number }">
|
||||||
|
<slot
|
||||||
|
name="grid"
|
||||||
|
:index="props.rowIndex"
|
||||||
|
:item="{ ...props.row, _selectedIndex: selectedIndex(props.row) }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.selectable-item__disabled {
|
||||||
|
filter: grayscale(1);
|
||||||
|
opacity: 0.5;
|
||||||
|
|
||||||
|
& :deep(*) {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue