refactor: เพิ่ม list ของ กลุ่ม สินค้า และ บริการ

This commit is contained in:
Net 2024-07-09 14:38:22 +07:00
parent bf5b868d31
commit 41a833418e

View file

@ -204,6 +204,39 @@ const serviceTab = [
const hideStat = ref(false); const hideStat = ref(false);
const tbColumn = { const tbColumn = {
groupAndType: [
{
name: 'name',
align:'left',
label: 'name',
field: 'name',
},
{
name: 'detail',
align:'left',
label: 'detail',
field: 'detail',
},
{
name: 'formDialogInputRemark',
align:'left',
label: 'formDialogInputRemark',
field: 'remark',
},
{
name: 'createdAt',
align:'left',
label: 'createdAt',
field: 'createdAt',
}
],
product: [ product: [
{ name: 'productName', align: 'left', label: 'productName', field: 'name' }, { name: 'productName', align: 'left', label: 'productName', field: 'name' },
{ {
@ -241,11 +274,17 @@ const tbColumn = {
}, },
], ],
} satisfies { } satisfies {
groupAndType: QTableProps['columns'];
product: QTableProps['columns']; product: QTableProps['columns'];
service: QTableProps['columns']; service: QTableProps['columns'];
}; };
const tbControl = reactive({ const tbControl = reactive({
groupAndType: {
fieldDisplay: ['name', 'detail', 'formDialogInputRemark', 'createdAt'],
fieldSelected: ['name', 'detail', 'formDialogInputRemark', 'createdAt'],
},
product: { product: {
fieldDisplay: [ fieldDisplay: [
'productName', 'productName',
@ -1235,88 +1274,544 @@ watch(inputSearchProductAndService, async () => {
<!-- group/type --> <!-- group/type -->
<div <div
v-if="productMode === 'group' || productMode === 'type'" v-if="productMode === 'group' || productMode === 'type'"
class="surface-1 col bordered rounded column no-wrap" class="surface-2 col bordered rounded column no-wrap"
> >
<div class="row surface-2 bordered-b q-px-md q-py-sm items-center"> <div class="row q-px-md q-py-sm items-center">
<div v-if="productMode === 'type'" class="text-h6 text-weight-bold">
{{ $t('productAndServiceType') }}
</div>
<div
v-else-if="productMode === 'group'"
class="text-h6 text-weight-bold"
>
{{ $t('productAndServiceAll') }}
</div>
<div <div
v-if="productMode === 'group' || productMode === 'type'" v-if="productMode === 'group' || productMode === 'type'"
class="row q-ml-auto" class="row full-width justify-between"
> >
<q-input <q-input
style="width: 300px" style="width: 300px"
outlined outlined
dense dense
unelavated
:label="$t('search')" :label="$t('search')"
class="q-mr-sm" class="q-mr-md"
:bg-color="$q.dark.isActive ? 'dark' : 'white'" :bg-color="$q.dark.isActive ? 'dark' : 'white'"
v-model="inputSearch" v-model="inputSearch"
debounce="500" debounce="250"
> >
<template v-slot:prepend> <template v-slot:prepend>
<q-icon name="mdi-magnify" /> <q-icon name="mdi-magnify" />
</template> </template>
</q-input> </q-input>
<q-btn
icon="mdi-tune-vertical-variant" <div class="row" style="gap: var(--size-2)">
size="sm" <q-btn
:color="$q.dark.isActive ? 'dark' : 'white'" icon="mdi-tune-vertical-variant"
:text-color="$q.dark.isActive ? 'white' : 'dark'" size="sm"
class="bordered rounded" :color="$q.dark.isActive ? 'dark' : 'white'"
unelevated :text-color="$q.dark.isActive ? 'white' : 'dark'"
> class="bordered rounded"
<q-menu class="bordered"> unelevated
<q-list v-close-popup dense> >
<q-item <q-menu class="bordered">
clickable <q-list v-close-popup dense>
class="flex items-center" <q-item
@click=" clickable
() => { class="flex items-center"
currentStatus = 'All'; @click="
} () => {
" currentStatus = 'All';
> }
{{ $t('all') }} "
</q-item> >
<q-item {{ $t('all') }}
clickable </q-item>
class="flex items-center" <q-item
@click=" clickable
() => { class="flex items-center"
currentStatus = 'ACTIVE'; @click="
} () => {
" currentStatus = 'ACTIVE';
> }
{{ $t('statusACTIVE') }} "
</q-item> >
<q-item {{ $t('statusACTIVE') }}
clickable </q-item>
class="flex items-center" <q-item
@click=" clickable
() => { class="flex items-center"
currentStatus = 'INACTIVE'; @click="
} () => {
" currentStatus = 'INACTIVE';
> }
{{ $t('statusINACTIVE') }} "
</q-item> >
</q-list> {{ $t('statusINACTIVE') }}
</q-menu> </q-item>
</q-btn> </q-list>
</q-menu>
</q-btn>
<q-select
v-if="modeView === false"
id="select-field"
for="select-field"
:options=" tbControl.groupAndType.fieldDisplay.map(x => ({ label: $t(x), value: x })) "
:display-value="$t('displayField')"
v-model="tbControl.groupAndType.fieldSelected"
option-label="label"
option-value="value"
map-options
emit-value
outlined
multiple
dense
/>
<q-btn-toggle
v-model="modeView"
id="btn-mode"
dense
class="no-shadow bordered rounded surface-1"
: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
? modeView
? '#C9D3DB '
: '#787B7C'
: modeView
? '#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
? modeView === false
? '#C9D3DB'
: '#787B7C'
: modeView === false
? '#787B7C'
: '#C9D3DB',
}"
/>
</template>
</q-btn-toggle>
</div>
</div> </div>
</div> </div>
<div style="height: 100%" class="q-pa-md scroll"> <div style="height: 100%" class="scroll">
<div class="row q-col-gutter-md col"> <div class="row col full-width full-height">
<div <q-table
flat
bordered
:grid="modeView"
:rows="
(productMode === 'type' ? productType : productGroup) || []
"
:columns="tbColumn.groupAndType"
class="full-width"
card-container-class="q-gutter-md "
row-key="name"
:rows-per-page-options="[0]"
hide-pagination
:visible-columns="tbControl.groupAndType.fieldSelected"
>
<template v-slot:header="props">
<q-tr class="surface-2" :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
{{ $t(col.label) }}
</q-th>
<q-th auto-width />
</q-tr>
</template>
<template v-slot:body="props">
<q-tr
:class="{
'app-text-muted': props.row.status === 'INACTIVE',
'status-active': props.row.status !== 'INACTIVE',
'status-inactive': props.row.status === 'INACTIVE',
}"
:props="props"
@click="async ()=>{
if (productMode === 'type') {
pathTypeName = props.row.name;
currentIdType = props.row.id;
productMode = 'service';
productAndServiceTab = 'service';
await fetchListOfProduct();
await fetchListOfService();
await fetchListOfProductAndService();
flowStore.rotate();
}
if (productMode === 'group') {
pathGroupName = props.row.name;
currentId = props.row.id;
productMode = 'type';
await fetchListType();
flowStore.rotate();
}
}"
>
<q-td v-if="tbControl.groupAndType.fieldSelected.includes('name')">
<div class="row items-center">
<div
style="
width: 50px;
display: flex;
margin-bottom: var(--size-2);
"
>
<div :class="`icon-color-${productMode ==='group' ? 'pink' : 'purple'}`" class="branch-card__icon">
<q-avatar size="md" icon="mdi-folder">
</q-avatar>
</div>
</div>
<div class="col">
<div class="col">
{{ props.row.name }}
</div>
<div class="col app-text-muted">
{{ props.row.code || '-' }}
</div>
</div>
</div>
</q-td>
<q-td v-if="tbControl.groupAndType.fieldSelected.includes('detail')">
{{ props.row.detail || '-' }}
</q-td>
<q-td v-if="tbControl.groupAndType.fieldSelected.includes('formDialogInputRemark')">
{{ props.row.remark || '-' }}
</q-td>
<q-td v-if="tbControl.groupAndType.fieldSelected.includes('createdAt')">
88585
</q-td>
<q-td>
<q-btn
icon="mdi-eye-outline"
size="sm"
dense
round
flat
@click.stop="
() => {
if (productMode === 'type') {
currentStatusProduct =
props.row.status === 'INACTIVE';
clearFormGroup();
currentIdType = props.row.id;
assignFormDataGroup(props.row);
isEdit = false;
drawerInfo = true;
}
if (productMode === 'group') {
currentStatusProduct = props.row.status === 'INACTIVE';
clearFormGroup();
assignFormDataGroup(props.row);
isEdit = false;
currentId = props.row.id;
drawerInfo = true;
}
}
"
/>
<q-btn
icon="mdi-dots-vertical"
size="sm"
dense
round
flat
@click.stop
:key="props.row.id"
>
<q-menu class="bordered">
<q-list v-close-popup>
<q-item
:id="`view-detail-btn-${props.row.name}-view`"
@click.stop="
() => {
if (productMode === 'type') {
currentStatusProduct =
props.row.status === 'INACTIVE';
clearFormGroup();
currentIdType = props.row.id;
assignFormDataGroup(props.row);
isEdit = false;
drawerInfo = true;
}
if (productMode === 'group') {
currentStatusProduct = props.row.status === 'INACTIVE';
clearFormGroup();
assignFormDataGroup(props.row);
isEdit = false;
currentId = props.row.id;
drawerInfo = true;
}
}
"
clickable
dense
class="row q-py-sm"
style="white-space: nowrap"
>
<q-icon
name="mdi-eye-outline"
class="col-3"
size="xs"
style="color: hsl(var(--green-6-hsl))"
/>
<span class="col-9 q-px-md flex items-center">
{{ $t('viewDetail') }}
</span>
</q-item>
<q-item
:id="`view-detail-btn-${props.row.name}-edit`"
clickable
dense
class="row q-py-sm"
style="white-space: nowrap"
@click="
() => {
if (productMode === 'type') {
clearFormGroup();
currentIdType = props.row.id;
assignFormDataGroup(props.row);
isEdit = true;
drawerInfo = true;
}
if (productMode === 'group') {
clearFormGroup();
assignFormDataGroup(props.row);
isEdit = true;
currentId = props.row.id;
drawerInfo = true;
}
}
"
>
<q-icon
name="mdi-pencil-outline"
class="col-3"
size="xs"
style="color: hsl(var(--cyan-6-hsl))"
/>
<span class="col-9 q-px-md flex items-center">
{{ $t('edit') }}
</span>
</q-item>
<q-item
:id="`view-detail-btn-${props.row.name}-delete`"
dense
:clickable="props.row.status === 'CREATED'"
class="row"
:class="{
'surface-3': props.row.status !== 'CREATED',
'app-text-muted': props.row.status !== 'CREATED',
}"
style="white-space: nowrap"
@click="()=>{
if (productMode === 'type') {
deleteProductById(props.row.id);
}
if (productMode === 'group') {
deleteProductById(props.row.id)
}
}"
>
<q-icon
name="mdi-trash-can-outline"
size="xs"
class="col-3"
:class="{
'app-text-negative':
props.row.status === 'CREATED',
}"
/>
<span class="col-9 q-px-md flex items-center">
{{ $t('delete') }}
</span>
</q-item>
<q-item dense>
<q-item-section class="q-py-sm">
<div class="q-pa-sm surface-2 rounded">
<q-toggle
:id="`view-detail-btn-${props.row.name}-status`"
dense
size="sm"
:label="
props.row.status !== 'INACTIVE'
? $t('switchOnLabel')
: $t('switchOffLabel')
"
@click="
async () => {
if (productMode === 'type') {
toggleStatusType(props.row.id, props.row.status);
}
if (productMode === 'group') {
toggleStatusGroup(props.row.id, props.row.status);
}
}
"
:model-value="props.row.status !== 'INACTIVE'"
/>
</div>
</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</q-td>
</q-tr>
</template>
<template v-slot:item="props">
<div class="col-3">
<ProductCardComponent
:isType="productMode === 'type'"
:count-product="props.row._count.product"
:count-type="props.row._count.type"
:count-service="props.row._count.service"
:title="props.row.name"
:subtitle="props.row.code"
:date="new Date(props.row.updatedAt)"
:status="props.row.status"
:id="props.row.id"
:isDisabled="props.row.status === 'INACTIVE'"
:color="
{
type: $q.dark.isActive
? 'var(--purple-7-hsl)'
: 'var(--violet-11-hsl)',
group: 'var(--pink-6-hsl)',
}[productMode] || 'var(--pink-6-hsl)'
"
@toggleStatus="
if (productMode === 'type') {
toggleStatusType(props.row.id, props.row.status);
}
if (productMode === 'group') {
toggleStatusGroup(props.row.id, props.row.status);
}
"
@viewCard="
() => {
if (productMode === 'type') {
currentStatusProduct =
props.row.status === 'INACTIVE';
clearFormGroup();
currentIdType = props.row.id;
assignFormDataGroup(props.row);
isEdit = false;
drawerInfo = true;
}
if (productMode === 'group') {
currentStatusProduct = props.row.status === 'INACTIVE';
clearFormGroup();
assignFormDataGroup(props.row);
isEdit = false;
currentId = props.row.id;
drawerInfo = true;
}
}
"
@updateCard="
() => {
if (productMode === 'type') {
clearFormGroup();
currentIdType = props.row.id;
assignFormDataGroup(props.row);
isEdit = true;
drawerInfo = true;
}
if (productMode === 'group') {
clearFormGroup();
assignFormDataGroup(props.row);
isEdit = true;
currentId = props.row.id;
drawerInfo = true;
}
}
"
@deleteCard="
() => {
if (productMode === 'type') {
deleteProductById(props.row.id);
}
if (productMode === 'group') {
deleteProductById(props.row.id)
}
}
"
@on-click="
async () => {
if (productMode === 'type') {
pathTypeName = props.row.name;
currentIdType = props.row.id;
productMode = 'service';
productAndServiceTab = 'service';
await fetchListOfProduct();
await fetchListOfService();
await fetchListOfProductAndService();
flowStore.rotate();
}
if (productMode === 'group') {
pathGroupName = props.row.name;
currentId = props.row.id;
productMode = 'type';
await fetchListType();
flowStore.rotate();
}
}
"
/>
</div>
</template>
</q-table>
<!-- <div
:class="`${$q.screen.gt.sm ? 'col-3' : $q.screen.gt.xs ? 'col-6' : 'col-12'}`" :class="`${$q.screen.gt.sm ? 'col-3' : $q.screen.gt.xs ? 'col-6' : 'col-12'}`"
v-for="(v, i) in productMode === 'type' v-for="(v, i) in productMode === 'type'
? productType ? productType
@ -1424,7 +1919,7 @@ watch(inputSearchProductAndService, async () => {
} }
" "
/> />
</div> </div> -->
</div> </div>
</div> </div>
@ -2758,4 +3253,74 @@ watch(inputSearchProductAndService, async () => {
cursor: pointer; cursor: pointer;
text-decoration: underline; text-decoration: underline;
} }
.status-active {
--_branch-status-color: var(--green-6-hsl);
}
.status-inactive {
--_branch-status-color: var(--red-4-hsl);
--_branch-badge-bg: var(--red-4-hsl);
filter: grayscale(0.5);
opacity: 0.5;
}
.icon-color-purple {
--_color: var(--violet-11-hsl);
}
.icon-color-pink {
--_color: var(--pink-6-hsl);
}
.icon-color-orange {
--_color: var(--orange-5-hsl);
}
.icon-color-green {
--_color: var(--teal-10-hsl);
}
.dark .icon-color-purple {
--_color: var(--violet-10-hsl);
}
.dark .icon-color-green {
--_color: var(--teal-8-hsl);
}
.dark .icon-color-orange {
--_color: var(--orange-6-hsl);
}
.branch-card__icon {
background-color: hsla(var(--_color) / 0.15);
color: hsla(var(--_color) / 1);
border-radius: 50%;
padding: var(--size-1);
position: relative;
transform: rotate(45deg);
&::after {
content: ' ';
display: block;
block-size: 0.5rem;
aspect-ratio: 1;
position: absolute;
border-radius: 50%;
right: -0.1rem;
top: calc(50% - 0.25rem);
bottom: calc(50% - 0.25rem);
background-color: hsla(var(--_branch-status-color) / 1);
}
& :deep(.q-avatar) {
transform: rotate(-45deg);
color: hsla(var(--_branch-card-bg) / 1);
}
}
</style> </style>
toggleStatusGroup(props.row