| @@ -1,4 +1,4 @@ | |||||
| <!doctype html><html lang=""><head><meta charset="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="viewport" content="width=device-width,initial-scale=1"/><link rel="icon" id="favicon" href="/favicon.svg"/><link rel="stylesheet" href="/fonts/vazir.css"/><script defer="defer" src="https://bazarce.liara.run/script.js" data-website-id="7baabdd5-3224-41c1-9267-d2a1abd29d01"></script><title>NovinPlast</title><script defer="defer" src="/js/chunk-vendors.e75a4c30.js"></script><script defer="defer" src="/js/app.deaabe6f.js"></script><link href="/css/chunk-vendors.8ada15f4.css" rel="stylesheet"><link href="/css/app.fed18238.css" rel="stylesheet"></head><body lang><noscript><strong>We're sorry but NovinPlast doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script>document.addEventListener("DOMContentLoaded", function () { | |||||
| <!doctype html><html lang=""><head><meta charset="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="viewport" content="width=device-width,initial-scale=1"/><link rel="icon" id="favicon" href="/favicon.svg"/><link rel="stylesheet" href="/fonts/vazir.css"/><script defer="defer" src="https://bazarce.liara.run/script.js" data-website-id="7baabdd5-3224-41c1-9267-d2a1abd29d01"></script><title>NovinPlast</title><script defer="defer" src="/js/chunk-vendors.e75a4c30.js"></script><script defer="defer" src="/js/app.f350e69a.js"></script><link href="/css/chunk-vendors.8ada15f4.css" rel="stylesheet"><link href="/css/app.fed18238.css" rel="stylesheet"></head><body lang><noscript><strong>We're sorry but NovinPlast doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script>document.addEventListener("DOMContentLoaded", function () { | |||||
| const faviconUrl = localStorage.getItem("logo"); | const faviconUrl = localStorage.getItem("logo"); | ||||
| if (faviconUrl) { | if (faviconUrl) { | ||||
| const faviconLink = document.getElementById("favicon"); | const faviconLink = document.getElementById("favicon"); | ||||
| @@ -397,7 +397,7 @@ export default { | |||||
| :class="{ active: this.$route.path === '/approvedOrders' }" | :class="{ active: this.$route.path === '/approvedOrders' }" | ||||
| > | > | ||||
| <router-link to="/approvedOrders" class="pc-link"> | <router-link to="/approvedOrders" class="pc-link"> | ||||
| <span class="pc-mtext">آیتم های تایید شده</span></router-link | |||||
| <span class="pc-mtext">آیتم های تایید شده(عمده)</span></router-link | |||||
| > | > | ||||
| </li> | </li> | ||||
| </ul> | </ul> | ||||
| @@ -107,7 +107,7 @@ | |||||
| class="form-control" | class="form-control" | ||||
| placeholder="انتخاب کنید" | placeholder="انتخاب کنید" | ||||
| > | > | ||||
| <option :value="null"> | |||||
| <option :value="''"> | |||||
| ندارد | ندارد | ||||
| </option> | </option> | ||||
| <option | <option | ||||
| @@ -289,9 +289,7 @@ export default { | |||||
| formData.append("description", description.value); | formData.append("description", description.value); | ||||
| formData.append("image", image.value); | formData.append("image", image.value); | ||||
| formData.append("icon", selectedIcon.value); | formData.append("icon", selectedIcon.value); | ||||
| if (selectedPaernt.value) { | |||||
| formData.append("parent_id", selectedPaernt.value); | |||||
| } | |||||
| formData.append("parent_id", selectedPaernt.value ?? ''); | |||||
| ApiServiece.post(`admin/categories`, formData, { | ApiServiece.post(`admin/categories`, formData, { | ||||
| headers: { | headers: { | ||||
| "content-type": "multipart", | "content-type": "multipart", | ||||
| @@ -100,7 +100,7 @@ | |||||
| class="form-control" | class="form-control" | ||||
| placeholder="انتخاب کنید" | placeholder="انتخاب کنید" | ||||
| > | > | ||||
| <option :value="null"> | |||||
| <option :value="''"> | |||||
| ندارد | ندارد | ||||
| </option> | </option> | ||||
| <option | <option | ||||
| @@ -289,8 +289,9 @@ export default { | |||||
| watch( | watch( | ||||
| () => props.parent, | () => props.parent, | ||||
| (newVal) => (localParent.value = newVal) | |||||
| ); | |||||
| (newVal) => { | |||||
| localParent.value = newVal ?? '' | |||||
| },{ immediate: true }); | |||||
| watch( | watch( | ||||
| () => props.allParents, | () => props.allParents, | ||||
| @@ -338,9 +339,9 @@ export default { | |||||
| if (image.value) { | if (image.value) { | ||||
| formData.append("image", image.value); | formData.append("image", image.value); | ||||
| } | } | ||||
| if (localParent.value) { | |||||
| formData.append("parent_id", localParent.value); | |||||
| } | |||||
| formData.append("parent_id", localParent.value ?? ''); | |||||
| formData.append("_method", "put"); | formData.append("_method", "put"); | ||||
| ApiServiece.post(`admin/categories/${localId.value}`, formData, { | ApiServiece.post(`admin/categories/${localId.value}`, formData, { | ||||
| @@ -176,7 +176,7 @@ export default { | |||||
| Array.isArray(localCat.value) | Array.isArray(localCat.value) | ||||
| ? localCat.value.map((category) => ({ | ? localCat.value.map((category) => ({ | ||||
| value: category.id, | value: category.id, | ||||
| label: category.title, | |||||
| label: category.title || '', | |||||
| })) | })) | ||||
| : [] | : [] | ||||
| ); | ); | ||||
| @@ -202,9 +202,11 @@ export default { | |||||
| } | } | ||||
| loading.value = true; | loading.value = true; | ||||
| const formData = new FormData(); | const formData = new FormData(); | ||||
| if (localCatId.value) { | |||||
| formData.append("category_id", localCatId.value); | |||||
| } | |||||
| formData.append("category_id", localCatId.value ?? '' + | |||||
| '' + | |||||
| ''); | |||||
| formData.append("title", localTitle.value); | formData.append("title", localTitle.value); | ||||
| ApiServiece.put(`admin/attributes/${localId.value}`, formData) | ApiServiece.put(`admin/attributes/${localId.value}`, formData) | ||||
| @@ -48,12 +48,18 @@ export const actions = { | |||||
| if (!data) { | if (!data) { | ||||
| throw new Error("شماره موبایل یا رمز عبور اشتباه است"); | throw new Error("شماره موبایل یا رمز عبور اشتباه است"); | ||||
| } | } | ||||
| if (data.data.user.role !== "admin") { | |||||
| if ( | |||||
| data?.data?.user?.role === "admin" | |||||
| || | |||||
| data?.data?.user?.role === "operator" | |||||
| ) { | |||||
| commit("SET_TOKEN", data.data.token); | |||||
| localStorage.setItem("token", data.data.token); | |||||
| } else { | |||||
| throw new Error("شماره موبایل یا رمز عبور اشتباه است"); | throw new Error("شماره موبایل یا رمز عبور اشتباه است"); | ||||
| } | } | ||||
| commit("SET_TOKEN", data.data.token); | |||||
| localStorage.setItem("token", data.data.token); | |||||
| } catch (error) { | } catch (error) { | ||||
| console.error("Login failed:", error.response?.data || error.message); | console.error("Login failed:", error.response?.data || error.message); | ||||
| throw new Error( | throw new Error( | ||||
| @@ -78,21 +84,23 @@ export const actions = { | |||||
| ); | ); | ||||
| if (response.data.success) { | if (response.data.success) { | ||||
| const token = response.data.data.token; | |||||
| const user = response.data.data.user; | |||||
| const token = response?.data?.data?.token; | |||||
| const user = response?.data?.data?.user; | |||||
| if (user?.role === "admin" || user?.role === "operator") { | |||||
| localStorage.setItem("token", token); | |||||
| if (user.role !== "admin") { | |||||
| commit("SET_TOKEN", token); | |||||
| commit("SET_USER", user); | |||||
| return response.data; | |||||
| } else { | |||||
| throw new Error("شماره موبایل یا رمز عبور نامعتبر است"); | throw new Error("شماره موبایل یا رمز عبور نامعتبر است"); | ||||
| } | } | ||||
| localStorage.setItem("token", token); | |||||
| console.log(token) | |||||
| commit("SET_TOKEN", token); | |||||
| commit("SET_USER", user); | |||||
| return response.data; | |||||
| } else { | } else { | ||||
| const errorMsg = response.data.message || "خطا در تایید کد"; | const errorMsg = response.data.message || "خطا در تایید کد"; | ||||
| console.log(errorMsg, 'errorMsg') | |||||
| throw new Error(errorMsg); | throw new Error(errorMsg); | ||||
| } | } | ||||
| } catch (error) { | } catch (error) { | ||||
| @@ -153,6 +153,7 @@ export default { | |||||
| const url = window.URL.createObjectURL(excelBlob); | const url = window.URL.createObjectURL(excelBlob); | ||||
| const link = document.createElement("a"); | const link = document.createElement("a"); | ||||
| link.href = url; | link.href = url; | ||||
| link.setAttribute("download", "order-items-export.xlsx"); | link.setAttribute("download", "order-items-export.xlsx"); | ||||
| document.body.appendChild(link); | document.body.appendChild(link); | ||||
| @@ -160,7 +161,9 @@ export default { | |||||
| link.click(); | link.click(); | ||||
| window.URL.revokeObjectURL(url); | window.URL.revokeObjectURL(url); | ||||
| document.body.removeChild(link); | document.body.removeChild(link); | ||||
| isLoading.value = false; | isLoading.value = false; | ||||
| }) | }) | ||||
| .catch((err) => { | .catch((err) => { | ||||
| @@ -441,7 +444,7 @@ export default { | |||||
| <!-- Operations --> | <!-- Operations --> | ||||
| <td> | <td> | ||||
| <router-link | <router-link | ||||
| :to="`/singleOrder/${order?.id}`" | |||||
| :to="`/singleOrder/${order?.order_id}`" | |||||
| class="btn btn-sm btn-outline-primary me-1" | class="btn btn-sm btn-outline-primary me-1" | ||||
| > | > | ||||
| مشاهده و ویرایش | مشاهده و ویرایش | ||||
| @@ -23,6 +23,7 @@ export default { | |||||
| const searchQuery = ref(""); | const searchQuery = ref(""); | ||||
| const orders = ref(); | const orders = ref(); | ||||
| const panel = ref(""); | const panel = ref(""); | ||||
| const loadingId = ref(null); | |||||
| const convertToJalali = (date) => { | const convertToJalali = (date) => { | ||||
| if (!date) return | if (!date) return | ||||
| @@ -32,30 +33,60 @@ export default { | |||||
| .format("HH:mm:ss YYYY/MM/DD "); | .format("HH:mm:ss YYYY/MM/DD "); | ||||
| }; | }; | ||||
| const loadingId = ref(null); | |||||
| const getExport = (id) => { | const getExport = (id) => { | ||||
| loadingId.value = id; | loadingId.value = id; | ||||
| //ApiServiece.post(`admin/orders/${id}/export`, {}, { responseType: "blob" }) | |||||
| ApiServiece({ | |||||
| method: 'POST', | |||||
| url: `admin/orders/${id}/export`, | |||||
| config: { responseType: "blob" } | |||||
| }).then((resp) => { | |||||
| const excelBlob = resp.data; | |||||
| const url = window.URL.createObjectURL(excelBlob); | |||||
| ApiServiece.post( | |||||
| `admin/orders/${id}/export`, | |||||
| {}, | |||||
| { responseType: "blob" } | |||||
| ).then((response) => { | |||||
| const blob = new Blob([response.data], { type: 'application/pdf' }); | |||||
| const link = document.createElement("a"); | |||||
| link.href = url; | |||||
| link.setAttribute("download", "order-items-export.xlsx"); | |||||
| document.body.appendChild(link); | |||||
| const url = window.URL.createObjectURL(blob); | |||||
| link.click(); | |||||
| const link = document.createElement('a'); | |||||
| window.URL.revokeObjectURL(url); | |||||
| document.body.removeChild(link); | |||||
| loadingId.value = null; | |||||
| link.href = url; | |||||
| const contentDisposition = response.headers['content-disposition']; | |||||
| let fileName = 'order-export.pdf'; | |||||
| if (contentDisposition) { | |||||
| const fileNameMatch = contentDisposition.match(/filename="?(.+)"?/); | |||||
| if (fileNameMatch && fileNameMatch.length > 1) { | |||||
| fileName = fileNameMatch[1]; | |||||
| } | |||||
| } | |||||
| link.setAttribute('download', fileName); | |||||
| document.body.appendChild(link); | |||||
| // Trigger download | |||||
| link.click(); | |||||
| // Clean up | |||||
| window.URL.revokeObjectURL(url); | |||||
| document.body.removeChild(link); | |||||
| loadingId.value = null; | |||||
| // const excelBlob = resp.data; | |||||
| // const url = window.URL.createObjectURL(excelBlob); | |||||
| // | |||||
| // const link = document.createElement("a"); | |||||
| // link.href = url; | |||||
| // link.setAttribute("download", "order-items-export.pdf"); | |||||
| // document.body.appendChild(link); | |||||
| // | |||||
| // link.click(); | |||||
| // | |||||
| // window.URL.revokeObjectURL(url); | |||||
| // document.body.removeChild(link); | |||||
| // loadingId.value = null; | |||||
| }) | }) | ||||
| .catch((err) => { | .catch((err) => { | ||||
| console.error(err, "export err"); | console.error(err, "export err"); | ||||
| @@ -419,7 +450,7 @@ export default { | |||||
| ></a | ></a | ||||
| > | > | ||||
| </li> | </li> | ||||
| <li> | |||||
| <li v-if="order?.user?.role === 'wholesale'"> | |||||
| <a | <a | ||||
| class="dropdown-item d-flex justify-content-center align-items-center" | class="dropdown-item d-flex justify-content-center align-items-center" | ||||
| @click="changeStatus(order?.id, 'approved')" | @click="changeStatus(order?.id, 'approved')" | ||||
| @@ -30,17 +30,28 @@ export default { | |||||
| }; | }; | ||||
| const getStatusLabel = (status) => { | const getStatusLabel = (status) => { | ||||
| const statusLabels = { | |||||
| waiting: "در انتظار", | |||||
| paid: "پرداختشده", | |||||
| un_paid: "پرداختنشده", | |||||
| approved: "تأییدشده", | |||||
| processing: "در حال پردازش", | |||||
| shipping: "در حال ارسال", | |||||
| delivered: "تحویلشده", | |||||
| canceled: "لغوشده", | |||||
| }; | |||||
| return statusLabels[status] || "نامشخص"; | |||||
| switch (status) { | |||||
| case 'waiting': | |||||
| return 'در انتظار'; | |||||
| case 'paid': | |||||
| return 'پرداخت شده'; | |||||
| case 'un_paid': | |||||
| return 'پرداخت نشده'; | |||||
| case 'approved': | |||||
| return 'تایید شده'; | |||||
| case 'processing': | |||||
| return 'در حال پردازش'; | |||||
| case 'shipping': | |||||
| return 'در حال ارسال'; | |||||
| case 'delivered': | |||||
| return 'تحویل شده'; | |||||
| case 'canceled': | |||||
| return 'لغو شده'; | |||||
| case 'in_cart': | |||||
| return 'در سبد خرید'; | |||||
| default: | |||||
| return 'وضعیت نامشخص'; | |||||
| } | |||||
| }; | }; | ||||
| const formatDate = (date) => { | const formatDate = (date) => { | ||||
| @@ -112,6 +123,17 @@ export default { | |||||
| return numeral(price).format("0,0") | return numeral(price).format("0,0") | ||||
| }; | }; | ||||
| const amountDiscount = (item) => { | |||||
| if (item?.discount_id) { | |||||
| if (item?.discount?.type === 'percentage') | |||||
| return formatPrice(item?.price * (Number(item?.discount?.amount) / 100)) + 'تومان'; | |||||
| return 0 | |||||
| } | |||||
| return 0 | |||||
| } | |||||
| onMounted(() => { | onMounted(() => { | ||||
| getOrder(); | getOrder(); | ||||
| }); | }); | ||||
| @@ -125,6 +147,7 @@ export default { | |||||
| updateNote, | updateNote, | ||||
| updateStatus, | updateStatus, | ||||
| formatPrice, | formatPrice, | ||||
| amountDiscount, | |||||
| }; | }; | ||||
| }, | }, | ||||
| }; | }; | ||||
| @@ -178,7 +201,8 @@ export default { | |||||
| </BCol> | </BCol> | ||||
| <BCol md="6"> | <BCol md="6"> | ||||
| <strong class="me-2">تخفیف:</strong> | <strong class="me-2">تخفیف:</strong> | ||||
| <span>{{ formatPrice(order?.discount?.amount) + (order?.discount?.type === 'percentage' ? ' درصد' : 'تومان') }}</span> | |||||
| <span>{{ ((order?.order_items?.reduce((sum, item) => sum + Number(item?.price), 0) + Number(order?.shipping_price)) - order?.total_price).toLocaleString() }} تومان</span> | |||||
| <span v-if="order?.discount?.amount">({{ formatPrice(order?.discount?.amount) + (order?.discount?.type === 'percentage' ? ' درصد' : 'تومان') }})</span> | |||||
| </BCol> | </BCol> | ||||
| </BRow> | </BRow> | ||||
| @@ -191,17 +215,21 @@ export default { | |||||
| <BCol md="6"> | <BCol md="6"> | ||||
| <strong class="me-2">وضعیت سفارش:</strong> | <strong class="me-2">وضعیت سفارش:</strong> | ||||
| <span>{{ order?.status || "-" }}</span> | |||||
| <span>{{ getStatusLabel(order?.status) || "-" }}</span> | |||||
| </BCol> | </BCol> | ||||
| </BRow> | </BRow> | ||||
| <BRow class="mb-3"> | <BRow class="mb-3"> | ||||
| <BCol md="6"> | <BCol md="6"> | ||||
| <strong class="me-2">قیمت کل:</strong> | |||||
| <strong class="me-2">مبلغ پرداختی:</strong> | |||||
| <span>{{ order?.total_price ? Number(order?.total_price).toLocaleString() + 'تومان' : '0' }}</span> | <span>{{ order?.total_price ? Number(order?.total_price).toLocaleString() + 'تومان' : '0' }}</span> | ||||
| </BCol> | </BCol> | ||||
| </BRow> | |||||
| <BCol md="6"> | |||||
| <strong class="me-2">مبلغ کل:</strong> | |||||
| <span>{{ order?.order_items?.reduce((sum, item) => sum + Number(item?.price), 0) + Number(order?.shipping_price) }}</span> | |||||
| </BCol> | |||||
| </BRow> | |||||
| </div> | </div> | ||||
| <!-- Order Details and Items Here --> | <!-- Order Details and Items Here --> | ||||
| @@ -217,7 +245,7 @@ export default { | |||||
| { key: 'title', label: 'عنوان محصول' }, | { key: 'title', label: 'عنوان محصول' }, | ||||
| { key: 'color', label: 'رنگ محصول' }, | { key: 'color', label: 'رنگ محصول' }, | ||||
| { key: 'count', label: 'تعداد در خواستی ' }, | { key: 'count', label: 'تعداد در خواستی ' }, | ||||
| { key: 'price', label: 'قیمت' }, | |||||
| { key: 'price', label: 'قیمت واحد' }, | |||||
| { key: 'discount', label: 'مبلغ تخفیف' }, | { key: 'discount', label: 'مبلغ تخفیف' }, | ||||
| { key: 'created_at', label: 'تاریخ ثبت' }, | { key: 'created_at', label: 'تاریخ ثبت' }, | ||||
| { key: 'edit_count', label: 'تعداد فرستاده شده' }, | { key: 'edit_count', label: 'تعداد فرستاده شده' }, | ||||
| @@ -227,10 +255,11 @@ export default { | |||||
| > | > | ||||
| <!-- Price formatting --> | <!-- Price formatting --> | ||||
| <template #cell(price)="data"> | <template #cell(price)="data"> | ||||
| {{ formatPrice(data?.item?.product?.retail_price) }} تومان | |||||
| {{ formatPrice(data?.item?.price / data?.item?.count) }} تومان | |||||
| </template> | </template> | ||||
| <template #cell(discount)="data"> | <template #cell(discount)="data"> | ||||
| {{ formatPrice(data?.item?.price) }} تومان | |||||
| <!-- {{ formatPrice(data?.item?.price) }} تومان--> | |||||
| {{ amountDiscount(data?.item) }} | |||||
| </template> | </template> | ||||
| <template #cell(title)="data"> | <template #cell(title)="data"> | ||||