fix(04): product service (unfinish)

This commit is contained in:
puriphatt 2024-08-14 14:38:26 +07:00
parent 485e0b82b2
commit 7cfc1ddee5
4 changed files with 342 additions and 165 deletions

View file

@ -107,7 +107,7 @@ const branchFilter = selectFilterOptionRefMod(
/>
</div>
<div v-if="service" class="col-9 row q-col-gutter-md">
<div v-if="service" class="col-12 row q-col-gutter-sm">
<q-input
lazy-rules="ondemand"
id="input-service-code"

View file

@ -18,46 +18,50 @@ defineEmits<{
}>();
</script>
<template>
<div class="col-3 app-text-muted">
{{ $t(`properties`) }}
<div>
<div class="full-width full-height">
<div
class="text-weight-bold text-body1 flex items-center justify-between q-px-md q-py-sm"
style="background: hsla(var(--info-bg) / 0.1)"
>
{{ $t(`serviceProperties`) }}
<q-btn
:disable="readonly"
dense
flat
unelevated
outline
class="q-mt-sm q-px-sm"
color="primary"
class="q-px-sm"
color="info"
@click="$emit('serviceProperties')"
>
<Icon
icon="basil:settings-adjust-solid"
width="24px"
class="q-mr-sm"
style="color: var(--brand-1)"
style="color: hsl(var(--info-bg))"
/>
{{ $t('properties') }}
{{ $t('serviceProperties') }}
</q-btn>
</div>
</div>
<div class="col-9 row">
<div
v-if="serviceAttributes.additional.length > 0"
class="col q-gutter-sm row items-center"
>
<div class="col-12 row q-px-md q-py-xs items-center surface-1 scroll">
<div
v-for="(p, index) in serviceAttributes.additional"
:key="index"
class="bordered q-px-sm surface-3"
style="border-radius: 6px"
v-if="serviceAttributes.additional.length > 0"
class="row items-center q-gutter-sm items-center"
>
{{ optionStore.mapOption(p.fieldName ?? '') }}
<div
v-for="(p, index) in serviceAttributes.additional"
:key="index"
class="bordered q-px-sm surface-3"
style="border-radius: 6px"
>
{{ optionStore.mapOption(p.fieldName ?? '') }}
</div>
</div>
</div>
<div v-else class="col flex items-center app-text-muted">
{{ $t('noProperties') }}
<div v-else class="col flex items-center app-text-muted">
{{ $t('noProperties') }}
</div>
</div>
</div>
</template>

View file

@ -3,14 +3,15 @@ import { ref } from 'vue';
import ToggleButton from './button/ToggleButton.vue';
defineProps<{
icon?: string;
fallbackCover?: string;
color?: string;
img?: string | null;
bgColor?: string;
icon?: string;
title?: string;
caption?: string;
color?: string;
bgColor?: string;
toggleTitle?: string;
fallbackImg?: string;
fallbackCover?: string;
hideFade?: boolean;
hideActive?: boolean;
@ -67,12 +68,6 @@ const showOverlay = ref(false);
class="surface-1"
style="border-radius: 50%; border: 4px solid var(--surface-1)"
>
<!-- <div
class="avatar__status"
style="z-index: 2"
:style="`${active ? 'background-color: hsla(var(--positive-bg) / 1)' : 'background-color: hsla(var(--text-mute) / 1)'}`"
></div> -->
<q-avatar
size="6rem"
font-size="3rem"
@ -89,7 +84,26 @@ const showOverlay = ref(false);
>
<q-img id="profile-view" v-if="img" :src="img" :ratio="1">
<template #error>
<q-img
v-if="fallbackImg"
:src="fallbackImg"
:ratio="1"
style="background-color: transparent"
>
<template #error>
<div
class="full-width full-height flex items-center justify-center"
style="background-color: transparent"
:style="{
color: `${color || 'white'}`,
}"
>
<q-icon :name="icon || 'mdi-account'" />
</div>
</template>
</q-img>
<div
v-else
class="full-width full-height flex items-center justify-center"
style="background-color: transparent"
:style="{

View file

@ -12,7 +12,6 @@ import ProductCardComponent from 'components/04_product-service/ProductCardCompo
import StatCard from 'components/StatCardComponent.vue';
import DrawerInfo from 'components/DrawerInfo.vue';
import BasicInformation from 'components/04_product-service/BasicInformation.vue';
import FormDialog from 'components/FormDialog.vue';
import TooltipComponent from 'components/TooltipComponent.vue';
import ButtonAddComponent from 'components/ButtonAddCompoent.vue';
import BasicInfoProduct from 'components/04_product-service/BasicInfoProduct.vue';
@ -32,6 +31,7 @@ import DialogForm from 'components/DialogForm.vue';
import ProfileBanner from 'components/ProfileBanner.vue';
import SideMenu from 'components/SideMenu.vue';
import ImageUploadDialog from 'components/ImageUploadDialog.vue';
import ToggleButton from 'src/components/button/ToggleButton.vue';
import useFlowStore from 'stores/flow';
import useMyBranchStore from 'stores/my-branch';
@ -134,7 +134,7 @@ const stat = ref<
color: 'orange',
},
{
icon: 'mdi-folder',
icon: 'mdi-shopping',
count: 0,
label: 'product',
mode: 'product',
@ -185,6 +185,7 @@ const treeProductTypeAndGroup = computed(() => {
return tempValue;
});
const profileFileImg = ref<File | null>(null);
const refImageUpload = ref<InstanceType<typeof ImageUploadDialog>>();
const inputSearch = ref('');
const inputSearchProductAndService = ref('');
@ -394,7 +395,7 @@ const workNameRef = ref();
const selectProduct = ref<ProductList[]>([]);
const currentWorkIndex = ref<number>(0);
const currentServiceTab = ref('serviceInformation');
const currentServiceTab = ref(1);
const propertiesDialog = ref<boolean>(false);
const totalProduct = ref<number>(0);
@ -964,6 +965,7 @@ const prevProduct = ref<ProductCreate>({
function assignFormDataProduct(data: ProductList) {
statusToggle.value = data.status === 'INACTIVE' ? false : true;
profileUrl.value = `${baseUrl.value}/product/${data?.id}/image`;
profileSubmit.value = true;
prevProduct.value = {
@ -1089,8 +1091,9 @@ async function submitService() {
async function submitProduct() {
formDataProduct.value.productTypeId = currentIdType.value;
if (profileSubmit.value) {
formDataProduct.value.image = imageProduct.value;
if (profileFileImg.value) {
formDataProduct.value.image = profileFileImg.value;
// formDataProduct.value.image = imageProduct.value;
}
if (dialogProduct.value) {
@ -1282,6 +1285,18 @@ function handleHold(node: ProductGroup) {
};
}
async function handleImageUpload(file: File | null, url: string | null) {
// if (!infoProductEdit.value && !dialogProductEdit.value) {
// infoProductEdit.value = true;
// await submitProduct();
// infoProductEdit.value = false;
// }
// if (infoProductEdit.value && !dialogProductEdit.value) {
// await submitProduct();
// }
// imageDialog.value = false;
}
onMounted(async () => {
utilsStore.currentTitle.title = 'mainProductTitle';
utilsStore.currentTitle.path = [
@ -1310,6 +1325,7 @@ watch(productMode, () => {
const tmp: typeof utilsStore.currentTitle.path = [
{
text: 'manage',
i18n: true,
handler: () => {
productMode.value = 'group';
expandedTree.value = [];
@ -1322,6 +1338,7 @@ watch(productMode, () => {
if (productMode.value === 'type' || productMode.value === 'service') {
tmp.push({
text: 'productGroup',
i18n: true,
argsi18n: { name: pathGroupName.value },
handler: () => {
productMode.value = 'type';
@ -1332,7 +1349,11 @@ watch(productMode, () => {
});
}
if (productMode.value === 'service') {
tmp.push({ text: 'productType', argsi18n: { name: pathTypeName.value } });
tmp.push({
text: 'productType',
i18n: true,
argsi18n: { name: pathTypeName.value },
});
}
utilsStore.currentTitle.path = tmp;
@ -1435,7 +1456,7 @@ watch(
clearFormGroup();
clearFormService();
await fetchListOfOptionBranch();
currentServiceTab = 'serviceInformation';
currentServiceTab = 1;
dialogService = true;
}
"
@ -1447,7 +1468,7 @@ watch(
{{ $t('dataSum') }}
<q-btn
class="q-ml-xs"
icon="mdi-pin"
icon="mdi-pin-outline"
color="primary"
size="sm"
flat
@ -2349,16 +2370,14 @@ watch(
<q-item dense>
<q-item-section class="q-py-sm">
<div class="q-pa-sm surface-2 rounded">
<q-toggle
color="positive"
<div
class="q-pa-sm surface-2 rounded flex items-center"
>
<ToggleButton
two-way
:id="`view-detail-btn-${props.row.name}-status`"
dense
size="sm"
:label="
:model-value="
props.row.status !== 'INACTIVE'
? $t('switchOnLabel')
: $t('switchOffLabel')
"
@click="
async () => {
@ -2368,10 +2387,14 @@ watch(
);
}
"
:model-value="
props.row.status !== 'INACTIVE'
"
/>
<span class="q-pl-md">
{{
props.row.status !== 'INACTIVE'
? $t('switchOnLabel')
: $t('switchOffLabel')
}}
</span>
</div>
</q-item-section>
</q-item>
@ -2896,15 +2919,27 @@ watch(
class="table__icon"
:class="`icon-color-${productAndServiceTab === 'product' ? 'green' : 'orange'}`"
>
<q-icon
size="md"
style="scale: 0.8"
:name="
productAndServiceTab === 'product'
? 'mdi-folder'
: 'mdi-server-network'
"
/>
<q-avatar size="md">
<q-img
class="text-center"
:ratio="1"
:src="`
${props.row.imageUrl ?? null}
`"
>
<template #error>
<q-icon
size="sm"
:name="
productAndServiceTab === 'product'
? 'mdi-shopping'
: 'mdi-server-network'
"
style="color: var(--teal-10); top: 10%"
/>
</template>
</q-img>
</q-avatar>
</div>
</div>
<div class="col">
@ -3170,16 +3205,15 @@ watch(
<q-item dense>
<q-item-section class="q-py-sm">
<div class="q-pa-sm surface-2 rounded">
<q-toggle
color="positive"
<div
class="q-pa-sm surface-2 rounded flex items-center"
>
<ToggleButton
two-way
:id="`view-detail-btn-${props.row.name}-status`"
dense
size="sm"
:label="
props.row.status !== 'INACTIVE'
? $t('switchOnLabel')
: $t('switchOffLabel')
:model-value="
props.row.status === 'CREATED' ||
props.row.status === 'ACTIVE'
"
@click="
() => {
@ -3190,11 +3224,14 @@ watch(
);
}
"
:model-value="
props.row.status === 'CREATED' ||
props.row.status === 'ACTIVE'
"
/>
<span class="q-pl-md">
{{
props.row.status !== 'INACTIVE'
? $t('switchOnLabel')
: $t('switchOffLabel')
}}
</span>
</div>
</q-item-section>
</q-item>
@ -3351,8 +3388,9 @@ watch(
hideFade
hideActive
useToggle
:img="`/images/product-service-${productMode}-avatar-add${productMode === 'type' ? ($q.dark.isActive ? '-d' : '-l') : ''}.png`"
:toggleTitle="$t('formDialogTitleUseStatus')"
:icon="productMode === 'group' ? 'mdi-folder' : 'mdi-folder-table'"
:icon="productMode === 'group' ? 'mdi-folder-plus' : 'mdi-folder-table'"
:title="formDataGroup.name"
:caption="formDataGroup.code"
:menu="[
@ -3428,9 +3466,7 @@ watch(
</div>
</div>
</DialogForm>
<DrawerInfo
:hideAction="!currentStatusProduct"
ref="formDialogRef"
v-model:drawerOpen="drawerInfo"
:title="
@ -3559,7 +3595,7 @@ watch(
</InfoForm>
</DrawerInfo>
<FormDialog
<DialogForm
v-model:modal="dialogProductServiceType"
:title="'สร้างสินค้าและบริการ'"
no-footer
@ -3585,9 +3621,9 @@ watch(
/>
</div>
</template>
</FormDialog>
</DialogForm>
<FormDialog
<DialogForm
v-model:modal="dialogTotalProduct"
noAddress
noAppBox
@ -3704,7 +3740,7 @@ watch(
</AppBox>
</div>
</div>
</FormDialog>
</DialogForm>
<!-- Add Product -->
<DialogForm
@ -3737,7 +3773,7 @@ watch(
useToggle
hideActive
:toggleTitle="$t('formDialogTitleUseStatus')"
img="/images/product-avatar.png"
:img="profileUrl || '/images/product-avatar-add.png'"
fallbackCover="/images/product-banner.png"
bgColor="#ebf1ee"
:menu="[
@ -3747,6 +3783,8 @@ watch(
bgColor: 'var(--surface-1)',
},
]"
@view="imageDialog = true"
@edit="refImageUpload && refImageUpload.browse()"
/>
</div>
@ -3773,7 +3811,7 @@ watch(
>
<span class="full-width q-py-sm" style="padding-inline: 20px">
{{
v === 0
v === 1
? $t('formDialogTitleInformation')
: $t('priceInformation')
}}
@ -3810,7 +3848,7 @@ watch(
</DialogForm>
<!-- edit product -->
<FormDialog
<DialogForm
:edit="!(formDataProduct.status === 'INACTIVE')"
:isEdit="infoProductEdit"
v-model:modal="dialogProductEdit"
@ -3836,12 +3874,98 @@ watch(
"
:close="
() => {
infoProductEdit = false;
dialogProductEdit = false;
flowStore.rotate();
}
"
>
<template #prepend>
<div class="q-mx-lg q-mt-lg">
<ProfileBanner
active
hideFade
useToggle
hideActive
icon="mdi-shopping"
fallbackImg="/images/product-avatar.png"
color="var(--teal-10)"
:readonly="!infoProductEdit"
:toggleTitle="$t('formDialogTitleUseStatus')"
:img="profileUrl || '/images/product-avatar.png'"
fallbackCover="/images/product-banner.png"
bgColor="#ebf1ee"
:menu="[
{
icon: 'mdi-office-building-outline',
color: 'hsl(var(--info-bg))',
bgColor: 'var(--surface-1)',
},
]"
@view="imageDialog = true"
@edit="refImageUpload && refImageUpload.browse()"
/>
</div>
<div
class="col surface-1 q-ma-lg rounded bordered scroll row relative-position"
id="group-form"
>
<div
class="col"
style="height: 100%; max-height: 100; overflow-y: auto"
v-if="$q.screen.gt.sm"
>
<div class="q-py-md q-pl-md q-pr-sm">
<q-item
v-for="v in 2"
:key="v"
dense
clickable
class="no-padding items-center rounded full-width"
:class="{ 'q-mt-xs': v > 1 }"
active-class="product-form-active"
:active="productTab === v"
@click="productTab = v"
>
<span class="full-width q-py-sm" style="padding-inline: 20px">
{{
v === 1
? $t('formDialogTitleInformation')
: $t('priceInformation')
}}
</span>
</q-item>
</div>
</div>
<div
class="col-12 col-md-10 q-py-md q-pr-md q-pl-sm"
id="customer-form-content"
style="height: 100%; max-height: 100%; overflow-y: auto"
>
<BasicInfoProduct
v-if="productTab === 1"
:readonly="!infoProductEdit"
v-model:options-branch="branchOption"
v-model:registered-branch-id="formDataProduct.registeredBranchId"
v-model:detail="formDataProduct.detail"
v-model:remark="formDataProduct.remark"
v-model:name="formDataProduct.name"
v-model:code="formDataProduct.code"
v-model:process="formDataProduct.process"
dense
separator
/>
<PriceDataComponent
v-if="productTab === 2"
:readonly="!infoProductEdit"
v-model:price="formDataProduct.price"
v-model:agent-price="formDataProduct.agentPrice"
v-model:service-charge="formDataProduct.serviceCharge"
dense
/>
</div>
</div>
<!-- <template #prepend>
<ProfileUpload
prefix-id="form-dialog-product"
isProduct
@ -3876,18 +4000,16 @@ watch(
v-model:service-charge="formDataProduct.serviceCharge"
dense
/>
</template>
</FormDialog>
</template> -->
</DialogForm>
<!-- add service -->
<FormDialog
<DialogForm
no-address
no-app-box
height="95vh"
:title="$t('addService')"
v-model:modal="dialogService"
:tabs-list="serviceTab"
v-model:current-tab="currentServiceTab"
:submit="
() => {
submitService();
@ -3901,8 +4023,8 @@ watch(
}
"
>
<template #prepend>
<ProfileUpload
<!-- :tabs-list="serviceTab" -->
<!-- <ProfileUpload
prefix-id="form-dialog-service"
isService
v-model:url-profile="profileUrl"
@ -3910,15 +4032,62 @@ watch(
v-model:profile-submit="profileSubmit"
@cancel-file="inputFile.value = ''"
@input-file="inputFile.click()"
/> -->
<div class="q-mx-lg q-mt-lg">
<ProfileBanner
hideFade
useToggle
hideActive
:toggleTitle="$t('formDialogTitleUseStatus')"
:img="profileUrl || '/images/product-avatar-add.png'"
fallbackCover="/images/product-banner.png"
bgColor="#ebf1ee"
:menu="[
{
icon: 'mdi-office-building-outline',
color: 'hsl(var(--info-bg))',
bgColor: 'var(--surface-1)',
},
]"
@view="imageDialog = true"
@edit="refImageUpload && refImageUpload.browse()"
/>
</template>
</div>
<div
v-if="currentServiceTab === 'serviceInformation'"
class="col-md-10 col-sm-12"
class="col surface-1 q-mx-lg q-mt-lg q-mb-md rounded bordered scroll row relative-position"
id="group-form"
>
<div class="surface-1 rounded bordered q-pa-lg full-width row">
<div
class="col"
style="height: 100%; max-height: 100; overflow-y: auto"
v-if="$q.screen.gt.sm"
>
<div class="q-py-md q-pl-md q-pr-sm">
<q-item
v-for="v in 2"
:key="v"
dense
clickable
class="no-padding items-center rounded full-width"
:class="{ 'q-mt-xs': v > 1 }"
active-class="product-form-active"
:active="currentServiceTab === v"
@click="currentServiceTab = v"
>
<span class="full-width q-py-sm" style="padding-inline: 20px">
{{ v === 1 ? $t('serviceInformation') : $t('workInformation') }}
</span>
</q-item>
</div>
</div>
<div
class="col-12 col-md-10 q-py-md q-pr-md q-pl-sm"
id="customer-form-content"
style="height: 100%; max-height: 100%; overflow-y: auto"
>
<BasicInformation
v-if="currentServiceTab === 1"
dense
service
v-model:options-branch="branchOption"
@ -3929,52 +4098,51 @@ watch(
v-model:service-description="formDataProductService.detail"
v-model:service-name-th="formDataProductService.name"
/>
</div>
<div class="surface-1 rounded bordered q-mt-md q-pa-lg row">
<FormServiceProperties
v-model:service-attributes="formDataProductService.attributes"
@service-properties="
<FormServiceWork
v-if="currentServiceTab === 2"
v-model:work-items="workItems"
dense
@addProduct="
async (index) => {
await fetchListOfProductIsAdd(currentIdType);
currentWorkIndex = index;
dialogTotalProduct = true;
}
"
@manage-work-name="
() => {
tempValueProperties = formDataProductService.attributes;
openPropertiesDialog('service');
manageWorkNameDialog = true;
}
"
@work-properties="
(index) => {
currentWorkIndex = index;
tempValueProperties = workItems[index].attributes;
openPropertiesDialog('work');
}
"
/>
</div>
</div>
<div
v-if="currentServiceTab === 'workInformation'"
class="surface-1 rounded bordered col-md-10 col-sm-12 q-pa-lg"
class="col-2 surface-1 q-mx-lg q-mb-lg rounded bordered row"
v-if="currentServiceTab === 1"
>
<FormServiceWork
v-model:work-items="workItems"
dense
@addProduct="
async (index) => {
await fetchListOfProductIsAdd(currentIdType);
currentWorkIndex = index;
dialogTotalProduct = true;
}
"
@manage-work-name="
<FormServiceProperties
v-model:service-attributes="formDataProductService.attributes"
@service-properties="
() => {
manageWorkNameDialog = true;
}
"
@work-properties="
(index) => {
currentWorkIndex = index;
tempValueProperties = workItems[index].attributes;
openPropertiesDialog('work');
tempValueProperties = formDataProductService.attributes;
openPropertiesDialog('service');
}
"
/>
</div>
</FormDialog>
</DialogForm>
<!-- service properties -->
<FormDialog
<DialogForm
no-address
no-app-box
height="75vh"
@ -4003,10 +4171,10 @@ watch(
v-model:properties-option="propertiesOption"
v-model:form-service-properties="tempValueProperties"
/>
</FormDialog>
</DialogForm>
<!-- manage work name -->
<FormDialog
<DialogForm
no-address
no-app-box
no-footer
@ -4023,19 +4191,17 @@ watch(
@edit="editWork"
@add="createWork"
/>
</FormDialog>
</DialogForm>
<!-- edit service -->
<FormDialog
<DialogForm
no-address
:no-app-box="currentServiceTab !== 'workInformation'"
:edit="!(formDataProductService.status === 'INACTIVE')"
height="95vh"
:isEdit="infoServiceEdit"
:title="$t('service')"
:tabs-list="serviceTab"
v-model:modal="dialogServiceEdit"
v-model:current-tab="currentServiceTab"
:submit="
() => {
submitService();
@ -4076,14 +4242,11 @@ watch(
/>
</template>
<div
v-if="currentServiceTab === 'serviceInformation'"
class="col-md-10 col-sm-12"
>
<div v-if="currentServiceTab === 1" class="col-md-10 col-sm-12">
<div class="surface-1 rounded bordered q-pa-lg full-width row">
<BasicInformation
:readonly="!infoServiceEdit"
v-if="currentServiceTab === 'serviceInformation'"
v-if="currentServiceTab === 1"
dense
service
v-model:options-branch="branchOption"
@ -4113,7 +4276,7 @@ watch(
<FormServiceWork
:readonly="!infoServiceEdit"
v-model:work-items="workItems"
v-if="currentServiceTab === 'workInformation'"
v-if="currentServiceTab === 2"
dense
@addProduct="
async (index) => {
@ -4132,7 +4295,7 @@ watch(
"
/>
</template>
</FormDialog>
</DialogForm>
<q-dialog v-model="holdDialog" position="bottom">
<div class="surface-1 full-width rounded column q-pb-md">
@ -4248,12 +4411,11 @@ watch(
<q-item clickable v-ripple>
<q-item-section avatar>
<q-toggle
color="positive"
dense
<ToggleButton
two-way
:id="`view-detail-btn-${currentNode.name}-status`"
size="sm"
@click.stop="
:model-value="currentNode.status !== 'INACTIVE'"
@click="
async () => {
if (!currentNode) return;
if (currentNode.type === 'type') {
@ -4278,7 +4440,6 @@ watch(
}
}
"
:model-value="currentNode.status !== 'INACTIVE'"
/>
</q-item-section>
<q-item-section>
@ -4297,31 +4458,22 @@ watch(
ref="refImageUpload"
v-model:dialogState="imageDialog"
v-model:file="profileFileImg"
v-model:image-url="profileUrl"
:hidden-footer="!infoProductEdit"
clearButton
@save="handleImageUpload"
>
<template #error>
<div class="full-height full-width" style="background: white">
<div
class="full-height full-width flex justify-center items-center"
:style="`background: hsla(var(${
productMode === 'group'
? '--pink-6'
: $q.dark.isActive
? '--violet-10'
: '--violet-11'
}-hsl)/0.1)`"
style="background: #ebf1ee"
>
<q-icon
size="15rem"
:name="productMode === 'group' ? 'mdi-folder' : 'mdi-folder-table'"
:style="`color: hsla(var(${
productMode === 'group'
? '--pink-6'
: $q.dark.isActive
? '--violet-10'
: '--violet-11'
}-hsl)/1)`"
></q-icon>
<q-img
src="/images/product-avatar.png"
fit="contain"
style="height: 100%"
/>
</div>
</div>
</template>
@ -4386,7 +4538,6 @@ watch(
color: hsla(var(--_color) / 1);
border-radius: 50%;
padding: var(--size-1);
position: relative;
transform: rotate(45deg);
@ -4407,6 +4558,14 @@ watch(
transform: rotate(-45deg);
color: hsla(var(--_branch-card-bg) / 1);
}
&:deep(.q-img) {
transform: rotate(-45deg);
&:deep(.q-icon) {
transform: rotate(0deg);
}
}
}
.tags {