Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 

508 rindas
16 KiB

  1. <template>
  2. <Layout>
  3. <BRow>
  4. <BCol sm="12">
  5. <BCard no-body>
  6. <BCardHeader>
  7. <h5>ویرایش تخفیف</h5>
  8. </BCardHeader>
  9. <BCardBody>
  10. <BRow class="g-3">
  11. <BCol md="6">
  12. <div class="form-group">
  13. <label class="form-label">عنوان</label>
  14. <input
  15. type="text"
  16. v-model="title"
  17. class="form-control"
  18. placeholder="عنوان تخفیف"
  19. :class="{ 'is-invalid': errors.title }"
  20. @input="clearError('title')"
  21. />
  22. </div>
  23. <small v-if="errors.title" class="text-danger">
  24. {{ errors.title }}
  25. </small>
  26. </BCol>
  27. <!-- Second Input Field (Slug) -->
  28. <BCol md="6">
  29. <div class="form-group">
  30. <label class="form-label">نوع تخفیف</label>
  31. <select
  32. v-model="discountType"
  33. class="form-control"
  34. :class="{ 'is-invalid': errors.discountType }"
  35. @change="clearError('discountType')"
  36. placeholder="انتخاب نوع اعمال تخفیف"
  37. >
  38. <option value="percentage">درصدی</option>
  39. <option value="const">مبلغ ثابت</option>
  40. </select>
  41. </div>
  42. <small v-if="errors.discountType" class="text-danger">
  43. {{ errors.discountType }}
  44. </small>
  45. </BCol>
  46. <BCol md="6">
  47. <div class="form-group">
  48. <label class="form-label">مقدار تخفیف</label>
  49. <input
  50. v-model="amount"
  51. class="form-control"
  52. type="number"
  53. placeholder="مقدار تخفیف"
  54. :class="{ 'is-invalid': errors.amount }"
  55. @input="clearError('amount')"
  56. />
  57. </div>
  58. <small v-if="errors.amount" class="text-danger">
  59. {{ errors.amount }}
  60. </small>
  61. </BCol>
  62. <BCol md="6">
  63. <div class="form-group">
  64. <label class="form-label">حداقل میزان سفارش</label>
  65. <input
  66. v-model="minOrder"
  67. class="form-control"
  68. type="number"
  69. placeholder="میزان سفارش"
  70. :class="{ 'is-invalid': errors.minOrder }"
  71. @input="clearError('minOrder')"
  72. />
  73. </div>
  74. <small v-if="errors.minOrder" class="text-danger">
  75. {{ errors.minOrder }}
  76. </small>
  77. </BCol>
  78. <BCol md="6">
  79. <div class="form-group">
  80. <label class="form-label">حداکثر میزان اسفاده</label>
  81. <input
  82. v-model="maxUsage"
  83. type="number"
  84. class="form-control"
  85. placeholder="حداکثر میزان استفاده"
  86. :class="{ 'is-invalid': errors.maxUsage }"
  87. @input="clearError('maxUsage')"
  88. />
  89. </div>
  90. <small v-if="errors.maxUsage" class="text-danger">
  91. {{ errors.maxUsage }}
  92. </small>
  93. </BCol>
  94. <BCol md="6">
  95. <div class="form-group">
  96. <label class="form-label">اعمال تخفیف بر</label>
  97. <select
  98. v-model="whichPart"
  99. class="form-control"
  100. :class="{ 'is-invalid': errors.whichPart }"
  101. @select="clearError('whichPart')"
  102. placeholder="انتخاب محل اعمل تخفیف"
  103. >
  104. <option value="cat">دسته</option>
  105. <option value="product">محصول</option>
  106. <option value="all">همه</option>
  107. </select>
  108. </div>
  109. <small v-if="errors.discountType" class="text-danger">
  110. {{ errors.discountType }}
  111. </small>
  112. </BCol>
  113. <BCol v-if="whichPart === 'cat'" md="6">
  114. <div class="form-group">
  115. <label class="form-label">دسته</label>
  116. <VueSelect
  117. style="--vs-min-height: 48px; --vs-border-radius: 8px"
  118. :isLoading="categorySelectorLoader"
  119. v-model="selectedCat"
  120. :options="formattedCategories"
  121. placeholder="دسته ای را انتخاب کنید"
  122. />
  123. </div>
  124. <small v-if="errors.selectedCat" class="text-danger">
  125. {{ errors.selectedCat }}
  126. </small>
  127. </BCol>
  128. <BCol v-if="whichPart === 'product'" md="6">
  129. <div class="form-group">
  130. <label class="form-label">محصول</label>
  131. <VueSelect
  132. style="--vs-min-height: 48px; --vs-border-radius: 8px"
  133. :isLoading="categorySelectorLoader"
  134. v-model="selectedProduct"
  135. :options="formattedProducts"
  136. placeholder="محصولی را انتخاب کنید "
  137. />
  138. </div>
  139. <small v-if="errors.selectedProduct" class="text-danger">
  140. {{ errors.selectedProduct }}
  141. </small>
  142. </BCol>
  143. <BCol md="6">
  144. <div class="form-group">
  145. <label class="form-label"> تاریخ اعمال تخفیف </label>
  146. <DatePicker
  147. :format="'jYYYY/jMM/jDD HH:mm:ss'"
  148. type="datetime"
  149. v-model="startDate"
  150. @input="handleStartDateInput"
  151. ></DatePicker>
  152. </div>
  153. <small v-if="errors.startDate" class="text-danger">
  154. {{ errors.startDate }}
  155. </small>
  156. </BCol>
  157. <BCol md="6">
  158. <div class="form-group">
  159. <label class="form-label"> تاریخ انقضای تخفیف </label>
  160. <DatePicker
  161. :format="'jYYYY/jMM/jDD HH:mm:ss'"
  162. type="datetime"
  163. v-model="expire"
  164. @input="handleExpireDateInput"
  165. ></DatePicker>
  166. </div>
  167. <small v-if="errors.expire" class="text-danger">
  168. {{ errors.expire }}
  169. </small>
  170. </BCol>
  171. </BRow>
  172. </BCardBody>
  173. <BCardFooter>
  174. <div class="d-flex justify-content-center">
  175. <div class="text-center">
  176. <button
  177. type="submit"
  178. class="btn btn-primary"
  179. @click.prevent="submitForm"
  180. :disabled="loading"
  181. >
  182. <span v-if="loading">
  183. <i class="fa fa-spinner fa-spin"></i> ویرایش...
  184. </span>
  185. <span v-else>ویرایش</span>
  186. </button>
  187. </div>
  188. </div>
  189. </BCardFooter>
  190. </BCard>
  191. </BCol>
  192. </BRow>
  193. </Layout>
  194. </template>
  195. <script>
  196. import VueSelect from "vue3-select-component";
  197. import moment from "jalali-moment";
  198. import { useRoute } from "vue-router";
  199. import { toast } from "vue3-toastify";
  200. import "vue3-toastify/dist/index.css";
  201. import ApiServiece from "@/services/ApiService";
  202. import { ref, onMounted, computed } from "vue";
  203. import Layout from "@/layout/custom.vue";
  204. import DatePicker from "vue3-persian-datetime-picker";
  205. export default {
  206. name: "SAMPLE-PAGE",
  207. components: {
  208. Layout,
  209. DatePicker,
  210. VueSelect,
  211. },
  212. setup() {
  213. const route = useRoute();
  214. const title = ref();
  215. const discountType = ref();
  216. const amount = ref();
  217. const minOrder = ref();
  218. const selectedCat = ref();
  219. const selectedProduct = ref();
  220. const startDate = ref();
  221. const expire = ref();
  222. const maxUsage = ref();
  223. const whichPart = ref();
  224. const loading = ref(false);
  225. const categories = ref([]);
  226. const errors = ref({});
  227. const products = ref();
  228. const discount = ref();
  229. const formattedCategories = computed(() =>
  230. Array.isArray(categories.value)
  231. ? categories.value.map((category) => ({
  232. value: category.id,
  233. label: category.title,
  234. }))
  235. : []
  236. );
  237. const formattedProducts = computed(() =>
  238. Array.isArray(products.value)
  239. ? products.value.map((product) => ({
  240. value: product.id,
  241. label: product.title,
  242. }))
  243. : []
  244. );
  245. const getCats = () => {
  246. ApiServiece.get(`admin/categories`)
  247. .then((resp) => {
  248. categories.value = resp.data.data;
  249. })
  250. .catch((err) => {
  251. console.log(err);
  252. });
  253. };
  254. const getProduct = () => {
  255. ApiServiece.get(`admin/products`)
  256. .then((resp) => {
  257. products.value = resp.data.data;
  258. })
  259. .catch((err) => {
  260. console.log(err);
  261. });
  262. };
  263. const getDiscount = () => {
  264. ApiServiece.get(`admin/discounts/${route.params.id}`)
  265. .then((resp) => {
  266. discount.value = resp.data.data;
  267. title.value = discount.value.title;
  268. discountType.value = discount.value.type;
  269. amount.value = discount.value.amount;
  270. minOrder.value = discount.value.min_order;
  271. selectedCat.value = discount.value.category_id;
  272. if (discount.value.category_id) {
  273. whichPart.value = "cat";
  274. }
  275. if (!discount.value.category_id && !discount.value.product_id) {
  276. whichPart.value = "all";
  277. }
  278. selectedProduct.value = discount.value.product_id;
  279. if (discount.value.product_id) {
  280. whichPart.value = "product";
  281. }
  282. if (discount?.value.starts_at) {
  283. startDate.value = moment(
  284. discount?.value.starts_at,
  285. "YYYY-MM-DD HH:mm:ss"
  286. ).format("jYYYY/jMM/jDD HH:mm:ss");
  287. }
  288. if (discount?.value.expires_at) {
  289. expire.value = moment(
  290. discount?.value.expires_at,
  291. "YYYY-MM-DD HH:mm:ss"
  292. ).format("jYYYY/jMM/jDD HH:mm:ss");
  293. }
  294. maxUsage.value = discount.value.max_usage;
  295. })
  296. .catch((err) => {
  297. console.log(err);
  298. });
  299. };
  300. const handleStartDateInput = () => {
  301. if (startDate.value) {
  302. startDate.value = moment(
  303. startDate.value,
  304. "jYYYY/jMM/jDD HH:mm:ss"
  305. ).format("YYYY-MM-DD HH:mm:ss");
  306. } else {
  307. clearError("expire");
  308. }
  309. };
  310. const handleExpireDateInput = () => {
  311. if (expire.value) {
  312. expire.value = moment(expire.value, "jYYYY/jMM/jDD HH:mm:ss").format(
  313. "YYYY-MM-DD HH:mm:ss"
  314. );
  315. } else {
  316. clearError("expire");
  317. }
  318. };
  319. const validateForm = () => {
  320. errors.value = {};
  321. if (!title.value) errors.value.title = "وارد کردن عنوان تخفیف الزامی است";
  322. if (!discountType.value)
  323. errors.value.discountType = "وارد کردن حالت تخفیف الزامی است";
  324. if (!amount.value)
  325. errors.value.amount = "وارد کردن مقدار تخفیف الزامی می باشد";
  326. if (!selectedCat.value && whichPart.value === "cat")
  327. errors.value.selectedCat = "انتخاب دسته برای تخفیف الزامی می باشد";
  328. if (!selectedProduct.value && whichPart.value === "product")
  329. errors.value.selectedProduct = "انتخاب محصول برای تخفیف الزامی می باشد";
  330. if (!startDate.value)
  331. errors.value.startDate = "انتخاب تاریخ اعمال تخفیف الزامی می باشد ";
  332. if (!whichPart.value)
  333. errors.value.whichPart = "مشخص کنید تخفیف بر چه بخشی اعمال شود";
  334. return Object.keys(errors.value).length === 0;
  335. };
  336. const clearError = (field) => {
  337. errors.value[field] = "";
  338. };
  339. onMounted(() => {
  340. getCats();
  341. getProduct();
  342. getDiscount();
  343. });
  344. const submitForm = () => {
  345. if (!validateForm()) {
  346. toast.error("لطفا فیلد های لازم را وارد نمایید", {
  347. position: "top-right",
  348. autoClose: 1000,
  349. });
  350. return;
  351. }
  352. loading.value = true;
  353. const formData = new FormData();
  354. formData.append("title", title.value);
  355. formData.append("type", discountType.value);
  356. formData.append("amount", amount.value);
  357. formData.append("min_order", minOrder.value);
  358. if (whichPart.value === "cat") {
  359. formData.append("category_id", selectedCat.value);
  360. }
  361. if (whichPart.value === "product") {
  362. formData.append("product_id", selectedProduct.value);
  363. }
  364. if (startDate.value) {
  365. const georgianDate = moment(
  366. startDate.value,
  367. "jYYYY/jMM/jDD HH:mm:ss"
  368. ).format("YYYY/MM/DD HH:mm:ss");
  369. formData.append("starts_at", georgianDate);
  370. }
  371. if (expire.value) {
  372. const georgianDate = moment(
  373. expire.value,
  374. "jYYYY/jMM/jDD HH:mm:ss"
  375. ).format("YYYY/MM/DD HH:mm:ss");
  376. formData.append("expires_at", georgianDate);
  377. }
  378. formData.append("max_usage", maxUsage.value);
  379. ApiServiece.post(`/admin/discounts`, formData)
  380. .then((resp) => {
  381. loading.value = false;
  382. toast.success("!تخفیف با موفقیت ویرایش شد", {
  383. position: "top-right",
  384. autoClose: 1000,
  385. });
  386. console.log(resp);
  387. })
  388. .catch((error) => {
  389. loading.value = false;
  390. console.log(error.response.message);
  391. toast.error(`${error.response.data.message}`, {
  392. position: "top-right",
  393. autoClose: 1000,
  394. });
  395. });
  396. };
  397. return {
  398. categories,
  399. errors,
  400. title,
  401. products,
  402. discountType,
  403. amount,
  404. minOrder,
  405. selectedCat,
  406. selectedProduct,
  407. startDate,
  408. expire,
  409. maxUsage,
  410. submitForm,
  411. handleStartDateInput,
  412. handleExpireDateInput,
  413. whichPart,
  414. clearError,
  415. loading,
  416. formattedCategories,
  417. formattedProducts,
  418. };
  419. },
  420. };
  421. </script>
  422. <style scoped>
  423. .ql-editor {
  424. direction: rtl;
  425. text-align: right;
  426. }
  427. .ql-editor::before {
  428. content: attr(placeholder);
  429. direction: rtl !important;
  430. text-align: right;
  431. }
  432. .Image-Preview {
  433. min-width: 200px;
  434. max-height: 200px;
  435. min-height: 200px;
  436. max-width: 200px;
  437. object-fit: cover;
  438. border-radius: 8px;
  439. border: 1px solid #ddd;
  440. }
  441. .delete-btn {
  442. display: flex;
  443. align-items: center;
  444. padding: 3px;
  445. font-size: 10px;
  446. background-color: #e74c3c;
  447. color: white;
  448. border: none;
  449. border-radius: 5px;
  450. cursor: pointer;
  451. transition: background-color 0.3s ease, transform 0.2s ease;
  452. gap: 8px;
  453. margin-right: 200px;
  454. }
  455. .delete-btn:hover {
  456. background-color: #c0392b;
  457. transform: scale(1.05);
  458. }
  459. .delete-btn:active {
  460. background-color: #a93226;
  461. }
  462. .delete-btn:focus {
  463. outline: none;
  464. }
  465. </style>