refactor(04): service new img dialog

This commit is contained in:
puriphatt 2024-09-12 11:22:11 +07:00
parent f1168c781f
commit 21ac39d538
4 changed files with 296 additions and 92 deletions

View file

@ -146,7 +146,7 @@ withDefaults(
style="background-color: transparent"
loading="lazy"
:src="
`${baseUrl}/${data?.type === 'service' ? 'service' : 'product'}/${data?.id}/image`.concat(
`${baseUrl}/${data?.type}/${data?.id}/image/${data?.selectedImage}`.concat(
noTimeImg ? '' : `?ts=${Date.now()}`,
)
"

View file

@ -490,6 +490,15 @@ const branchOption = ref<{ id: string; name: string }[]>([]);
const currentStatus = ref<Status | 'All'>('All');
// img
const isImageEdit = ref<boolean>(false);
const refreshImageState = ref(false);
const imageList = ref<{ selectedImage: string; list: string[] }>();
const onCreateImageList = ref<{
selectedImage: string;
list: { url: string; imgFile: File | null; name: string }[];
}>({ selectedImage: '', list: [] });
async function searchProduct(isAdd: boolean = true) {
const res = await fetchListProduct({
query: inputSearchProductAndService.value,
@ -869,9 +878,10 @@ async function assignFormDataProductService(id: string) {
const res = await fetchListServiceById(id);
if (res) {
await fetchImageList(res.id, res.selectedImage || '', 'service');
serviceTab.value = 1;
statusToggle.value = res.status === 'INACTIVE' ? false : true;
profileUrl.value = res.imageUrl;
profileUrl.value = `${baseUrl.value}/service/${res.id}/image/${res.selectedImage}`;
profileSubmit.value = true;
currentService.value = JSON.parse(JSON.stringify(res));
@ -884,6 +894,7 @@ async function assignFormDataProductService(id: string) {
work: [],
status: res.status,
productGroupId: res.productGroupId,
selectedImage: res.selectedImage,
};
formDataProductService.value = { ...prevService.value };
@ -932,12 +943,13 @@ const prevProduct = ref<ProductCreate>({
image: undefined,
});
function assignFormDataProduct(data: ProductList) {
async function assignFormDataProduct(data: ProductList) {
productTab.value = 1;
statusToggle.value = data.status === 'INACTIVE' ? false : true;
profileUrl.value = `${baseUrl.value}/product/${data?.id}/image`;
profileUrl.value = `${baseUrl.value}/product/${data?.id}/image/${data?.selectedImage}`;
profileSubmit.value = true;
await fetchImageList(data.id, data.selectedImage || '', 'product');
// profileSubmit.value = true;
prevProduct.value = {
productGroupId: data.productGroupId,
@ -953,8 +965,8 @@ function assignFormDataProduct(data: ProductList) {
status: data.status,
expenseType: data.expenseType,
vatIncluded: data.vatIncluded,
selectedImage: data.selectedImage,
};
formDataProduct.value = { ...prevProduct.value };
}
@ -1028,7 +1040,7 @@ function assignFormDataProductServiceCreate() {
});
}
async function submitService() {
async function submitService(notClose = false) {
assignFormDataProductServiceCreate();
formDataProductService.value.productGroupId = currentIdGrop.value;
@ -1045,7 +1057,10 @@ async function submitService() {
return;
}
const res = await createService(formDataProductService.value);
const res = await createService(
formDataProductService.value,
onCreateImageList.value,
);
if (res) {
allStat.value[1].count = allStat.value[1].count + 1;
stat.value[1].count = stat.value[1].count + 1;
@ -1063,15 +1078,16 @@ async function submitService() {
});
}
if (!notClose) clearFormService();
if (productAndServiceTab.value === 'service') {
await fetchListOfService();
}
flowStore.rotate();
clearFormService();
}
async function submitProduct() {
async function submitProduct(notClose = false) {
formDataProduct.value.productGroupId = currentIdGrop.value;
if (profileFileImg.value) {
formDataProduct.value.image = profileFileImg.value;
@ -1085,7 +1101,10 @@ async function submitProduct() {
productTab.value = 1;
return;
}
const res = await createProduct(formDataProduct.value);
const res = await createProduct(
formDataProduct.value,
onCreateImageList.value,
);
if (res) {
allStat.value[2].count = allStat.value[2].count + 1;
@ -1102,7 +1121,7 @@ async function submitProduct() {
});
}
totalProduct.value = totalProduct.value + 1;
clearFormProduct();
if (!notClose) clearFormProduct();
if (productAndServiceTab.value === 'product') {
await fetchListOfProduct();
@ -1234,12 +1253,14 @@ async function alternativeFetch() {
}
}
function cloneData() {
function cloneServiceData() {
if (!currentService.value) return;
const currentSelectedImage = formDataProductService.value.selectedImage;
formDataProductService.value = {
...prevService.value,
attributes: JSON.parse(JSON.stringify(currentService.value.attributes)),
};
formDataProductService.value.selectedImage = currentSelectedImage;
workItems.value = currentService.value.work.map((item) => {
return {
@ -1317,6 +1338,19 @@ function handleHold(node: ProductGroup & { type: string }) {
currentNode.value = node;
}
async function fetchImageList(
id: string,
selectedName: string,
type: 'product' | 'service',
) {
const res = await productServiceStore.fetchImageListById(id, type);
imageList.value = {
selectedImage: selectedName,
list: res.map((n: string) => `${type}/${id}/image/${n}`),
};
return res;
}
onMounted(async () => {
utilsStore.currentTitle.title = 'productService.title';
utilsStore.currentTitle.path = [
@ -1442,6 +1476,13 @@ watch(productMode, async () => {
await calculateStats({ type: 'product' });
}
});
watch(
() => profileFileImg.value,
() => {
if (profileFileImg.value !== null) isImageEdit.value = true;
},
);
</script>
<template>
@ -2430,9 +2471,7 @@ watch(productMode, async () => {
<q-img
class="text-center"
:ratio="1"
:src="`
${props.row.imageUrl ?? null}
`"
:src="`${baseUrl}/${productAndServiceTab}/${props.row.id}/image/${props.row.selectedImage}`"
>
<template #error>
<q-icon
@ -3336,6 +3375,7 @@ watch(productMode, async () => {
:close="
() => {
dialogProduct = false;
onCreateImageList = { selectedImage: '', list: [] };
flowStore.rotate();
}
"
@ -3356,8 +3396,13 @@ watch(productMode, async () => {
bgColor: 'var(--surface-1)',
},
]"
@view="imageDialog = true"
@edit="refImageUpload && refImageUpload.browse()"
@view="
() => {
imageDialog = true;
isImageEdit = false;
}
"
@edit="imageDialog = isImageEdit = true"
v-model:toggle-status="formDataProduct.status"
@update:toggle-status="
() => {
@ -3449,15 +3494,6 @@ watch(productMode, async () => {
v-model:modal="dialogProductEdit"
noAddress
:title="$t('productService.product.title')"
:editData="() => (infoProductEdit = true)"
:undo="
() => {
formDataProduct = { ...prevProduct };
infoProductEdit = false;
flowStore.rotate();
}
"
:deleteData="() => deleteProductConfirm()"
:submit="() => submitProduct()"
:close="
() => {
@ -3478,9 +3514,12 @@ watch(productMode, async () => {
icon="mdi-shopping-outline"
fallbackImg="/images/product-avatar.png"
color="var(--teal-10)"
:readonly="!infoProductEdit"
:toggleTitle="$t('status.title')"
:img="profileUrl || '/images/product-avatar.png'"
:img="
`${baseUrl}/product/${currentIdProduct}/image/${formDataProduct.selectedImage}`.concat(
refreshImageState ? `?ts=${Date.now()}` : '',
) || '/images/product-avatar.png'
"
fallbackCover="/images/product-banner.png"
:bgColor="`hsla(var(--teal-${$q.dark.isActive ? '8' : '10'}-hsl)/0.15)`"
:menu="[
@ -3491,8 +3530,13 @@ watch(productMode, async () => {
},
]"
v-model:toggle-status="formDataProduct.status"
@view="imageDialog = true"
@edit="refImageUpload && refImageUpload.browse()"
@view="
() => {
imageDialog = true;
isImageEdit = false;
}
"
@edit="imageDialog = isImageEdit = true"
@update:toggle-status="
async () => {
if (formDataProduct.status)
@ -3639,6 +3683,7 @@ watch(productMode, async () => {
() => {
clearFormService();
dialogService = false;
onCreateImageList = { selectedImage: '', list: [] };
flowStore.rotate();
}
"
@ -3664,8 +3709,13 @@ watch(productMode, async () => {
bgColor: 'var(--surface-1)',
},
]"
@view="imageDialog = true"
@edit="refImageUpload && refImageUpload.browse()"
@view="
() => {
imageDialog = true;
isImageEdit = false;
}
"
@edit="imageDialog = isImageEdit = true"
v-model:toggle-status="formDataProductService.status"
@update:toggle-status="
() => {
@ -3879,20 +3929,6 @@ watch(productMode, async () => {
dialogServiceEdit = false;
}
"
:edit-data="
() => {
infoServiceEdit = true;
}
"
:undo="
() => {
infoServiceEdit = false;
cloneData();
statusToggle = prevService.status === 'INACTIVE' ? false : true;
flowStore.rotate();
}
"
:delete-data="() => deleteServiceConfirm()"
>
<div class="q-mx-lg q-mt-lg">
<ProfileBanner
@ -3901,9 +3937,13 @@ watch(productMode, async () => {
:title="formDataProductService.name"
:caption="formDataProductService.code"
:active="formDataProductService.status !== 'INACTIVE'"
:readonly="!infoServiceEdit"
:toggleTitle="$t('status.title')"
:img="profileUrl || '/images/service-avatar.png'"
:img="
`${baseUrl}/service/${currentIdService}/image/${formDataProductService.selectedImage}`.concat(
refreshImageState ? `?ts=${Date.now()}` : '',
) || '/images/service-avatar.png'
"
fallbackImg="/images/service-avatar.png"
fallbackCover="/images/service-banner.png"
:bgColor="`hsla(var(--orange-${$q.dark.isActive ? '6' : '5'}-hsl)/0.15)`"
:menu="[
@ -3919,8 +3959,13 @@ watch(productMode, async () => {
},
]"
v-model:toggle-status="formDataProductService.status"
@view="imageDialog = true"
@edit="refImageUpload && refImageUpload.browse()"
@view="
() => {
imageDialog = true;
isImageEdit = false;
}
"
@edit="imageDialog = isImageEdit = true"
@update:toggle-status="
async () => {
if (formDataProductService.status)
@ -3965,7 +4010,7 @@ watch(productMode, async () => {
@click="
() => {
infoServiceEdit = false;
cloneData();
cloneServiceData();
statusToggle = prevService.status === 'INACTIVE' ? false : true;
flowStore.rotate();
}
@ -4225,10 +4270,91 @@ watch(productMode, async () => {
v-model:dialogState="imageDialog"
v-model:file="profileFileImg"
v-model:image-url="profileUrl as string"
:hidden-footer="!profileUrl && !dialogService && !dialogProduct"
clearButton
v-model:data-list="imageList"
v-model:on-create-data-list="onCreateImageList"
:hidden-footer="!isImageEdit"
:on-create="dialogProduct || dialogService"
:change-disabled="!actionDisplay"
@add-image="
async (v) => {
if (!v) return;
if (!currentIdProduct) return;
const res = await productServiceStore.addImageList(
v,
currentIdProduct,
Date.now(),
dialogProductEdit ? 'product' : 'service',
);
await fetchImageList(
currentIdProduct,
res,
dialogProductEdit ? 'product' : 'service',
);
}
"
@remove-image="
async (v) => {
if (!v) return;
if (!currentIdProduct && !currentIdService) return;
const name = v.split('/').pop() || '';
const type = dialogProductEdit ? 'product' : 'service';
await productServiceStore.deleteImageByName(
dialogProductEdit ? currentIdProduct : currentIdService,
name,
type,
);
await fetchImageList(
dialogProductEdit ? currentIdProduct : currentIdService,
name,
type,
);
}
"
@submit="
async (v) => {
if (dialogProduct || dialogService) {
profileUrl = v;
imageDialog = false;
} else {
const type = dialogProductEdit ? 'product' : 'service';
const id = dialogProductEdit ? currentIdProduct : currentIdService;
refreshImageState = true;
type === 'product'
? (formDataProduct.selectedImage = v)
: (formDataProductService.selectedImage = v);
imageList ? (imageList.selectedImage = v) : '';
profileUrl = `${baseUrl}/${type}/${id}/image/${v}`;
if (type === 'product') {
const { selectedImage, ...data } = prevProduct;
formDataProduct = {
selectedImage: formDataProduct.selectedImage,
...data,
};
await submitProduct(true);
} else {
cloneServiceData();
await submitService(true);
}
imageDialog = false;
refreshImageState = false;
infoProductEdit = false;
infoServiceEdit = false;
}
}
"
>
<template #title>
<span
v-if="!dialogProduct || !dialogService"
class="justify-center flex text-bold"
>
{{ $t('general.image') }}
{{
dialogProductEdit ? formDataProduct.name : formDataProductService.name
}}
</span>
</template>
<template #error>
<div class="full-height full-width" style="background: var(--surface-1)">
<div

View file

@ -185,22 +185,32 @@ const useProductServiceStore = defineStore('api-product-service', () => {
return false;
}
async function createProduct(data: ProductCreate) {
async function createProduct(
data: ProductCreate,
imgList: {
selectedImage: string;
list: { url: string; imgFile: File | null; name: string }[];
},
) {
const { image, status, ...payload } = data;
const res = await api.post<ProductCreate & { imageUploadUrl: string }>(
'/product',
{
...payload,
},
);
image &&
(await axios
.put(res.data.imageUploadUrl, image, {
headers: { 'Content-Type': image.type },
onUploadProgress: (e) => console.log(e),
})
.catch((e) => console.error(e)));
const res = await api.post<ProductCreate>('/product', {
...payload,
selectedImage: imgList.selectedImage || '',
});
if (imgList.list.length > 0 && res.data.id) {
for (let index = 0; index < imgList.list.length; index++) {
const imgFile = imgList.list[index].imgFile;
if (imgFile)
await addImageList(
imgFile,
res.data.id,
imgList.list[index].name,
'product',
);
}
}
if (!res) return false;
@ -234,14 +244,6 @@ const useProductServiceStore = defineStore('api-product-service', () => {
},
);
image &&
(await axios
.put(res.data.imageUploadUrl, image, {
headers: { 'Content-Type': image.type },
onUploadProgress: (e) => console.log(e),
})
.catch((e) => console.error(e)));
if (res.status === 200) {
return res.data;
}
@ -314,23 +316,35 @@ const useProductServiceStore = defineStore('api-product-service', () => {
return false;
}
async function createService(data: ServiceCreate) {
async function createService(
data: ServiceCreate,
imgList: {
selectedImage: string;
list: { url: string; imgFile: File | null; name: string }[];
},
) {
const { image, status, ...payload } = data;
const res = await api.post<ServiceCreate & { imageUploadUrl: string }>(
'/service',
{
...payload,
selectedImage: imgList.selectedImage || '',
},
);
image &&
(await axios
.put(res.data.imageUploadUrl, image, {
headers: { 'Content-Type': image.type },
onUploadProgress: (e) => console.log(e),
})
.catch((e) => console.error(e)));
if (imgList.list.length > 0 && res.data.id) {
for (let index = 0; index < imgList.list.length; index++) {
const imgFile = imgList.list[index].imgFile;
if (imgFile)
await addImageList(
imgFile,
res.data.id,
imgList.list[index].name,
'service',
);
}
}
if (!res) return false;
@ -397,14 +411,6 @@ const useProductServiceStore = defineStore('api-product-service', () => {
},
);
image &&
(await axios
.put(res.data.imageUploadUrl, image, {
headers: { 'Content-Type': image.type },
onUploadProgress: (e) => console.log(e),
})
.catch((e) => console.error(e)));
if (!res) return false;
if (res.status === 200) {
@ -554,6 +560,67 @@ const useProductServiceStore = defineStore('api-product-service', () => {
}
}
async function fetchImageListById(
id: string,
type: 'product' | 'service',
flow?: {
sessionId?: string;
refTransactionId?: string;
transactionId?: string;
},
) {
const res = await api.get(`/${type}/${id}/image`, {
headers: {
'X-Session-Id': flow?.sessionId,
'X-Rtid': flow?.refTransactionId || flowStore.rtid,
'X-Tid': flow?.transactionId,
},
});
if (!res) return false;
if (res.status === 200) return res.data;
if (res.status === 204) return null;
return false;
}
async function addImageList(
file: File,
userId: string,
name: string,
type: 'product' | 'service',
) {
await api
.put(`/${type}/${userId}/image/${name}`, file, {
headers: { 'Content-Type': file.type },
onUploadProgress: (e) => console.log(e),
})
.catch((e) => console.error(e));
return name;
}
async function deleteImageByName(
id: string,
name: string,
type: 'product' | 'service',
flow?: {
sessionId?: string;
refTransactionId?: string;
transactionId?: string;
},
) {
const res = await api.delete(`/${type}/${id}/image/${name}`, {
headers: {
'X-Session-Id': flow?.sessionId,
'X-Rtid': flow?.refTransactionId || flowStore.rtid,
'X-Tid': flow?.transactionId,
},
});
if (!res) return false;
}
return {
workNameItems,
@ -587,6 +654,10 @@ const useProductServiceStore = defineStore('api-product-service', () => {
fetchListProductByIdWork,
fetchListOfWork,
fetchImageListById,
addImageList,
deleteImageByName,
};
});

View file

@ -10,6 +10,7 @@ export interface TreeProduct {
}
export interface Service {
selectedImage?: string;
productGroupId: string;
updatedAt: string;
updatedBy: UpdatedBy;
@ -47,6 +48,7 @@ export interface Work {
}
export interface ServiceCreate {
id?: string;
work: {
attributes: Attributes;
productId: string[];
@ -59,6 +61,7 @@ export interface ServiceCreate {
image?: File;
status?: Status;
productGroupId: string;
selectedImage?: string;
}
export interface Attributes {
@ -131,6 +134,7 @@ export interface WorkItems {
}
export interface ProductList {
selectedImage?: string;
expenseType: string;
vatIncluded: boolean;
remark: string;
@ -152,6 +156,8 @@ export interface ProductList {
}
export interface ProductCreate {
id?: string;
selectedImage?: string;
expenseType: string;
vatIncluded: boolean;
productGroupId: string;
@ -168,6 +174,7 @@ export interface ProductCreate {
}
export interface ProductUpdate {
selectedImage?: string;
productGroupId: string;
remark: string;
serviceCharge: number;