| @@ -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"); | |||
| if (faviconUrl) { | |||
| const faviconLink = document.getElementById("favicon"); | |||
| @@ -397,7 +397,7 @@ export default { | |||
| :class="{ active: this.$route.path === '/approvedOrders' }" | |||
| > | |||
| <router-link to="/approvedOrders" class="pc-link"> | |||
| <span class="pc-mtext">آیتم های تایید شده</span></router-link | |||
| <span class="pc-mtext">آیتم های تایید شده(عمده)</span></router-link | |||
| > | |||
| </li> | |||
| </ul> | |||
| @@ -107,7 +107,7 @@ | |||
| class="form-control" | |||
| placeholder="انتخاب کنید" | |||
| > | |||
| <option :value="null"> | |||
| <option :value="''"> | |||
| ندارد | |||
| </option> | |||
| <option | |||
| @@ -289,9 +289,7 @@ export default { | |||
| formData.append("description", description.value); | |||
| formData.append("image", image.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, { | |||
| headers: { | |||
| "content-type": "multipart", | |||
| @@ -100,7 +100,7 @@ | |||
| class="form-control" | |||
| placeholder="انتخاب کنید" | |||
| > | |||
| <option :value="null"> | |||
| <option :value="''"> | |||
| ندارد | |||
| </option> | |||
| <option | |||
| @@ -289,8 +289,9 @@ export default { | |||
| watch( | |||
| () => props.parent, | |||
| (newVal) => (localParent.value = newVal) | |||
| ); | |||
| (newVal) => { | |||
| localParent.value = newVal ?? '' | |||
| },{ immediate: true }); | |||
| watch( | |||
| () => props.allParents, | |||
| @@ -338,9 +339,9 @@ export default { | |||
| if (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"); | |||
| ApiServiece.post(`admin/categories/${localId.value}`, formData, { | |||
| @@ -176,7 +176,7 @@ export default { | |||
| Array.isArray(localCat.value) | |||
| ? localCat.value.map((category) => ({ | |||
| value: category.id, | |||
| label: category.title, | |||
| label: category.title || '', | |||
| })) | |||
| : [] | |||
| ); | |||
| @@ -202,9 +202,11 @@ export default { | |||
| } | |||
| loading.value = true; | |||
| const formData = new FormData(); | |||
| if (localCatId.value) { | |||
| formData.append("category_id", localCatId.value); | |||
| } | |||
| formData.append("category_id", localCatId.value ?? '' + | |||
| '' + | |||
| ''); | |||
| formData.append("title", localTitle.value); | |||
| ApiServiece.put(`admin/attributes/${localId.value}`, formData) | |||
| @@ -48,12 +48,18 @@ export const actions = { | |||
| if (!data) { | |||
| 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("شماره موبایل یا رمز عبور اشتباه است"); | |||
| } | |||
| commit("SET_TOKEN", data.data.token); | |||
| localStorage.setItem("token", data.data.token); | |||
| } catch (error) { | |||
| console.error("Login failed:", error.response?.data || error.message); | |||
| throw new Error( | |||
| @@ -78,21 +84,23 @@ export const actions = { | |||
| ); | |||
| 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("شماره موبایل یا رمز عبور نامعتبر است"); | |||
| } | |||
| localStorage.setItem("token", token); | |||
| console.log(token) | |||
| commit("SET_TOKEN", token); | |||
| commit("SET_USER", user); | |||
| return response.data; | |||
| } else { | |||
| const errorMsg = response.data.message || "خطا در تایید کد"; | |||
| console.log(errorMsg, 'errorMsg') | |||
| throw new Error(errorMsg); | |||
| } | |||
| } catch (error) { | |||
| @@ -153,6 +153,7 @@ export default { | |||
| const url = window.URL.createObjectURL(excelBlob); | |||
| const link = document.createElement("a"); | |||
| link.href = url; | |||
| link.setAttribute("download", "order-items-export.xlsx"); | |||
| document.body.appendChild(link); | |||
| @@ -160,7 +161,9 @@ export default { | |||
| link.click(); | |||
| window.URL.revokeObjectURL(url); | |||
| document.body.removeChild(link); | |||
| isLoading.value = false; | |||
| }) | |||
| .catch((err) => { | |||
| @@ -441,7 +444,7 @@ export default { | |||
| <!-- Operations --> | |||
| <td> | |||
| <router-link | |||
| :to="`/singleOrder/${order?.id}`" | |||
| :to="`/singleOrder/${order?.order_id}`" | |||
| class="btn btn-sm btn-outline-primary me-1" | |||
| > | |||
| مشاهده و ویرایش | |||
| @@ -23,6 +23,7 @@ export default { | |||
| const searchQuery = ref(""); | |||
| const orders = ref(); | |||
| const panel = ref(""); | |||
| const loadingId = ref(null); | |||
| const convertToJalali = (date) => { | |||
| if (!date) return | |||
| @@ -32,30 +33,60 @@ export default { | |||
| .format("HH:mm:ss YYYY/MM/DD "); | |||
| }; | |||
| const loadingId = ref(null); | |||
| const getExport = (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) => { | |||
| console.error(err, "export err"); | |||
| @@ -419,7 +450,7 @@ export default { | |||
| ></a | |||
| > | |||
| </li> | |||
| <li> | |||
| <li v-if="order?.user?.role === 'wholesale'"> | |||
| <a | |||
| class="dropdown-item d-flex justify-content-center align-items-center" | |||
| @click="changeStatus(order?.id, 'approved')" | |||
| @@ -30,17 +30,28 @@ export default { | |||
| }; | |||
| 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) => { | |||
| @@ -112,6 +123,17 @@ export default { | |||
| 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(() => { | |||
| getOrder(); | |||
| }); | |||
| @@ -125,6 +147,7 @@ export default { | |||
| updateNote, | |||
| updateStatus, | |||
| formatPrice, | |||
| amountDiscount, | |||
| }; | |||
| }, | |||
| }; | |||
| @@ -178,7 +201,8 @@ export default { | |||
| </BCol> | |||
| <BCol md="6"> | |||
| <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> | |||
| </BRow> | |||
| @@ -191,17 +215,21 @@ export default { | |||
| <BCol md="6"> | |||
| <strong class="me-2">وضعیت سفارش:</strong> | |||
| <span>{{ order?.status || "-" }}</span> | |||
| <span>{{ getStatusLabel(order?.status) || "-" }}</span> | |||
| </BCol> | |||
| </BRow> | |||
| <BRow class="mb-3"> | |||
| <BCol md="6"> | |||
| <strong class="me-2">قیمت کل:</strong> | |||
| <strong class="me-2">مبلغ پرداختی:</strong> | |||
| <span>{{ order?.total_price ? Number(order?.total_price).toLocaleString() + 'تومان' : '0' }}</span> | |||
| </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> | |||
| <!-- Order Details and Items Here --> | |||
| @@ -217,7 +245,7 @@ export default { | |||
| { key: 'title', label: 'عنوان محصول' }, | |||
| { key: 'color', label: 'رنگ محصول' }, | |||
| { key: 'count', label: 'تعداد در خواستی ' }, | |||
| { key: 'price', label: 'قیمت' }, | |||
| { key: 'price', label: 'قیمت واحد' }, | |||
| { key: 'discount', label: 'مبلغ تخفیف' }, | |||
| { key: 'created_at', label: 'تاریخ ثبت' }, | |||
| { key: 'edit_count', label: 'تعداد فرستاده شده' }, | |||
| @@ -227,10 +255,11 @@ export default { | |||
| > | |||
| <!-- Price formatting --> | |||
| <template #cell(price)="data"> | |||
| {{ formatPrice(data?.item?.product?.retail_price) }} تومان | |||
| {{ formatPrice(data?.item?.price / data?.item?.count) }} تومان | |||
| </template> | |||
| <template #cell(discount)="data"> | |||
| {{ formatPrice(data?.item?.price) }} تومان | |||
| <!-- {{ formatPrice(data?.item?.price) }} تومان--> | |||
| {{ amountDiscount(data?.item) }} | |||
| </template> | |||
| <template #cell(title)="data"> | |||