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 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: [
{ name: 'productName', align: 'left', label: 'productName', field: 'name' },
{
@ -241,11 +274,17 @@ const tbColumn = {
},
],
} satisfies {
groupAndType: QTableProps['columns'];
product: QTableProps['columns'];
service: QTableProps['columns'];
};
const tbControl = reactive({
groupAndType: {
fieldDisplay: ['name', 'detail', 'formDialogInputRemark', 'createdAt'],
fieldSelected: ['name', 'detail', 'formDialogInputRemark', 'createdAt'],
},
product: {
fieldDisplay: [
'productName',
@ -1235,88 +1274,544 @@ watch(inputSearchProductAndService, async () => {
<!-- group/type -->
<div
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 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 class="row q-px-md q-py-sm items-center">
<div
v-if="productMode === 'group' || productMode === 'type'"
class="row q-ml-auto"
class="row full-width justify-between"
>
<q-input
style="width: 300px"
outlined
dense
unelavated
:label="$t('search')"
class="q-mr-sm"
class="q-mr-md"
:bg-color="$q.dark.isActive ? 'dark' : 'white'"
v-model="inputSearch"
debounce="500"
debounce="250"
>
<template v-slot:prepend>
<q-icon name="mdi-magnify" />
</template>
</q-input>
<q-btn
icon="mdi-tune-vertical-variant"
size="sm"
:color="$q.dark.isActive ? 'dark' : 'white'"
:text-color="$q.dark.isActive ? 'white' : 'dark'"
class="bordered rounded"
unelevated
>
<q-menu class="bordered">
<q-list v-close-popup dense>
<q-item
clickable
class="flex items-center"
@click="
() => {
currentStatus = 'All';
}
"
>
{{ $t('all') }}
</q-item>
<q-item
clickable
class="flex items-center"
@click="
() => {
currentStatus = 'ACTIVE';
}
"
>
{{ $t('statusACTIVE') }}
</q-item>
<q-item
clickable
class="flex items-center"
@click="
() => {
currentStatus = 'INACTIVE';
}
"
>
{{ $t('statusINACTIVE') }}
</q-item>
</q-list>
</q-menu>
</q-btn>
<div class="row" style="gap: var(--size-2)">
<q-btn
icon="mdi-tune-vertical-variant"
size="sm"
:color="$q.dark.isActive ? 'dark' : 'white'"
:text-color="$q.dark.isActive ? 'white' : 'dark'"
class="bordered rounded"
unelevated
>
<q-menu class="bordered">
<q-list v-close-popup dense>
<q-item
clickable
class="flex items-center"
@click="
() => {
currentStatus = 'All';
}
"
>
{{ $t('all') }}
</q-item>
<q-item
clickable
class="flex items-center"
@click="
() => {
currentStatus = 'ACTIVE';
}
"
>
{{ $t('statusACTIVE') }}
</q-item>
<q-item
clickable
class="flex items-center"
@click="
() => {
currentStatus = 'INACTIVE';
}
"
>
{{ $t('statusINACTIVE') }}
</q-item>
</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 style="height: 100%" class="q-pa-md scroll">
<div class="row q-col-gutter-md col">
<div
<div style="height: 100%" class="scroll">
<div class="row col full-width full-height">
<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'}`"
v-for="(v, i) in productMode === 'type'
? productType
@ -1424,7 +1919,7 @@ watch(inputSearchProductAndService, async () => {
}
"
/>
</div>
</div> -->
</div>
</div>
@ -2758,4 +3253,74 @@ watch(inputSearchProductAndService, async () => {
cursor: pointer;
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>
toggleStatusGroup(props.row