選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 

463 行
12 KiB

  1. <script>
  2. import Layout from "@/layout/custom.vue";
  3. import ApiServiece from "@/services/ApiService";
  4. import moment from "jalali-moment";
  5. import { onMounted, ref, watch, computed } from "vue";
  6. import { toast } from "vue3-toastify";
  7. import "vue3-toastify/dist/index.css";
  8. import Swal from "sweetalert2";
  9. import addAttribute from "@/components/modals/attribute/addAttribute.vue";
  10. import editAttribute from "@/components/modals/attribute/editAttribute.vue";
  11. export default {
  12. name: "BORDER",
  13. components: {
  14. Layout,
  15. addAttribute,
  16. editAttribute,
  17. },
  18. setup() {
  19. const searchPage = ref();
  20. const currentPage = ref(1);
  21. const totalPages = ref(1);
  22. const paginate = ref(20);
  23. const page = ref(1);
  24. const attributeValues = ref();
  25. const filterLoading = ref(false);
  26. const searchQuery = ref("");
  27. const attributes = ref();
  28. const attributeTitle = ref();
  29. const attributeId = ref();
  30. const attributeCode = ref();
  31. let searchTimeout = null;
  32. const convertToJalali = (date) => {
  33. return moment(date, "YYYY-MM-DD HH:mm:ss")
  34. .locale("fa")
  35. .format("YYYY/MM/DD");
  36. };
  37. const handleSearchChange = () => {
  38. clearTimeout(searchTimeout);
  39. searchTimeout = setTimeout(() => {
  40. getAttributes();
  41. page.value = 1;
  42. }, 500);
  43. };
  44. watch(searchQuery, () => {
  45. handleSearchChange();
  46. });
  47. const getAttributes = () => {
  48. filterLoading.value = true;
  49. ApiServiece.get(
  50. `admin/attribute-values?attribute_id=1&title=${encodeURIComponent(
  51. searchQuery.value || ""
  52. )}&code=${encodeURIComponent(searchQuery.value || "")}
  53. &paginate=${paginate.value || 10}&page=${page.value || 1}
  54. `
  55. )
  56. .then((resp) => {
  57. filterLoading.value = false;
  58. console.log(resp.data);
  59. attributes.value = resp.data.data.data;
  60. currentPage.value = resp.data.data.current_page;
  61. totalPages.value = resp.data.data.last_page;
  62. console.log(attributes.value);
  63. })
  64. .catch(() => {
  65. filterLoading.value = false;
  66. });
  67. };
  68. const handleAttributeUpdated = () => {
  69. getAttributes();
  70. };
  71. const nextPage = () => {
  72. if (currentPage.value < totalPages.value) {
  73. page.value++;
  74. getAttributes();
  75. }
  76. };
  77. const prevPage = () => {
  78. if (currentPage.value > 1) {
  79. page.value--;
  80. getAttributes();
  81. }
  82. };
  83. function handlePageInput() {
  84. if (searchPage.value < 1) {
  85. searchPage.value = 1;
  86. } else if (searchPage.value > totalPages.value) {
  87. searchPage.value = totalPages.value;
  88. }
  89. if (searchPage.value >= 1 && searchPage.value <= totalPages.value) {
  90. page.value = searchPage.value;
  91. }
  92. }
  93. const visiblePages = computed(() => {
  94. const pages = [];
  95. if (totalPages.value <= 5) {
  96. for (let i = 1; i <= totalPages.value; i++) {
  97. pages.push(i);
  98. }
  99. } else {
  100. let start = currentPage.value - 2;
  101. let end = currentPage.value + 2;
  102. if (start < 1) start = 1;
  103. if (end > totalPages.value) end = totalPages.value;
  104. for (let i = start; i <= end; i++) {
  105. pages.push(i);
  106. }
  107. }
  108. return pages;
  109. });
  110. watch(page, () => {
  111. getAttributes();
  112. });
  113. const deleteAttribute = (id, title) => {
  114. Swal.fire({
  115. text: `می خواهید رنگ ${title} را حذف کنید ؟`,
  116. icon: "warning",
  117. showCancelButton: true,
  118. confirmButtonColor: "#3085d6",
  119. cancelButtonColor: "#d33",
  120. confirmButtonText: "بله!",
  121. cancelButtonText: "خیر",
  122. }).then((result) => {
  123. if (result.isConfirmed) {
  124. ApiServiece.delete(`admin/attribute-values/${id}`)
  125. .then(() => {
  126. toast.success("!ویژگی با موفقیت حذف شد", {
  127. position: "top-right",
  128. autoClose: 3000,
  129. });
  130. attributes.value = attributes.value.filter(
  131. (attribute) => attribute.id !== id
  132. );
  133. })
  134. .catch((err) => {
  135. console.log(err);
  136. toast.error("!مشکلی در حذف کردن ویژگی پیش آمد", {
  137. position: "top-right",
  138. autoClose: 3000,
  139. });
  140. });
  141. }
  142. });
  143. };
  144. const editModalData = (id, title, code) => {
  145. attributeId.value = id;
  146. attributeTitle.value = title;
  147. attributeCode.value = code;
  148. };
  149. const getAttributeValues = () => {
  150. ApiServiece.get(`admin/attributes`).then((resp) => {
  151. console.log(resp);
  152. attributeValues.value = resp.data.data;
  153. });
  154. };
  155. onMounted(() => {
  156. getAttributes();
  157. getAttributeValues();
  158. });
  159. return {
  160. attributes,
  161. convertToJalali,
  162. handleAttributeUpdated,
  163. editModalData,
  164. deleteAttribute,
  165. searchQuery,
  166. filterLoading,
  167. attributeId,
  168. attributeCode,
  169. attributeTitle,
  170. attributeValues,
  171. searchPage,
  172. currentPage,
  173. totalPages,
  174. paginate,
  175. page,
  176. prevPage,
  177. nextPage,
  178. handlePageInput,
  179. visiblePages,
  180. };
  181. },
  182. };
  183. </script>
  184. <template>
  185. <Layout>
  186. <BRow>
  187. <div class="col-md-12">
  188. <div class="card shadow-sm border-0 rounded">
  189. <div
  190. class="card-header d-flex justify-content-between align-items-center p-3"
  191. dir="rtl"
  192. >
  193. <div class="d-flex align-items-center">
  194. <input
  195. v-model="searchQuery"
  196. type="text"
  197. placeholder="جستجو..."
  198. class="form-control form-control-sm d-inline-block me-2"
  199. style="width: 250px; border-radius: 15px"
  200. />
  201. </div>
  202. <button
  203. data-bs-toggle="modal"
  204. data-bs-target="#addAttribute"
  205. class="btn btn-light text-primary btn-sm px-3"
  206. >
  207. افزودن رنگ
  208. </button>
  209. </div>
  210. <div v-if="!filterLoading" class="card-body table-border-style p-0">
  211. <div class="table-responsive">
  212. <table class="table table-hover table-bordered m-0" dir="rtl">
  213. <thead class="table-light">
  214. <tr>
  215. <th>نام</th>
  216. <th>رنگ</th>
  217. <th>کد رنگ</th>
  218. <th>تاریخ ایجاد</th>
  219. <th>عملیات</th>
  220. </tr>
  221. </thead>
  222. <tbody>
  223. <tr v-for="attribute in attributes" :key="attribute.id">
  224. <td>{{ attribute.title }}</td>
  225. <td
  226. :style="{
  227. backgroundColor: attribute.code,
  228. textAlign: 'center',
  229. }"
  230. ></td>
  231. <td>{{ attribute.code }}</td>
  232. <td>{{ convertToJalali(attribute?.created_at) }}</td>
  233. <td>
  234. <!-- <button
  235. @click="
  236. editModalData(
  237. attribute?.id,
  238. attribute?.title,
  239. attribute.code
  240. )
  241. "
  242. data-bs-toggle="modal"
  243. data-bs-target="#editAttribute"
  244. class="btn btn-sm btn-outline-warning me-1"
  245. >
  246. ویرایش
  247. </button> -->
  248. <button
  249. @click="deleteAttribute(attribute.id, attribute.title)"
  250. class="btn btn-sm btn-outline-danger"
  251. >
  252. حذف
  253. </button>
  254. </td>
  255. </tr>
  256. </tbody>
  257. </table>
  258. </div>
  259. </div>
  260. <div
  261. v-else
  262. class="filter-loader card table-card user-profile-list"
  263. ></div>
  264. </div>
  265. </div>
  266. <addAttribute
  267. :attributeValues="attributeValues"
  268. @attribute-updated="handleAttributeUpdated()"
  269. />
  270. <editAttribute
  271. :id="attributeId"
  272. :title="attributeTitle"
  273. :code="attributeCode"
  274. :attributeValues="attributeValues"
  275. @attribute-updated="handleAttributeUpdated()"
  276. />
  277. </BRow>
  278. <BRow>
  279. <BCol sm="12">
  280. <div class="d-flex justify-content-center">
  281. <nav aria-label="Page navigation">
  282. <ul class="pagination">
  283. <li class="page-item" :class="{ disabled: currentPage === 1 }">
  284. <span class="page-link" @click="prevPage">قبلی</span>
  285. </li>
  286. <li v-if="currentPage > 2" class="page-item" @click="page = 1">
  287. <a class="page-link" href="javascript:void(0)">1</a>
  288. </li>
  289. <li v-if="currentPage > 3" class="page-item" disabled>
  290. <span class="page-link">...</span>
  291. </li>
  292. <li
  293. v-for="n in visiblePages"
  294. :key="n"
  295. class="page-item"
  296. :class="{ active: currentPage === n }"
  297. >
  298. <a
  299. class="page-link"
  300. href="javascript:void(0)"
  301. @click="page = n"
  302. >
  303. {{ n }}
  304. </a>
  305. </li>
  306. <li
  307. v-if="currentPage < totalPages - 2"
  308. class="page-item"
  309. disabled
  310. >
  311. <span class="page-link">...</span>
  312. </li>
  313. <li
  314. v-if="currentPage < totalPages - 1"
  315. class="page-item"
  316. @click="page = totalPages"
  317. >
  318. <a class="page-link" href="javascript:void(0)">{{
  319. totalPages
  320. }}</a>
  321. </li>
  322. <li
  323. class="page-item"
  324. :class="{ disabled: currentPage === totalPages }"
  325. >
  326. <span class="page-link" @click="nextPage">بعدی</span>
  327. </li>
  328. </ul>
  329. </nav>
  330. </div>
  331. </BCol>
  332. <BCol sm="4">
  333. <div class="ms-0 search-number">
  334. <input
  335. v-model="searchPage"
  336. type="text"
  337. class="form-control"
  338. placeholder="برو به صفحه"
  339. :max="totalPages"
  340. min="1"
  341. @input="handlePageInput"
  342. />
  343. </div>
  344. </BCol>
  345. </BRow>
  346. </Layout>
  347. </template>
  348. <style scoped>
  349. .card {
  350. transition: transform 0.3s ease;
  351. }
  352. .card:hover {
  353. transform: translateY(-3px);
  354. }
  355. .table th,
  356. .table td {
  357. vertical-align: middle;
  358. text-align: center;
  359. }
  360. .filter-loader {
  361. border: 4px solid rgba(0, 123, 255, 0.3);
  362. border-top: 4px solid #007bff;
  363. border-radius: 50%;
  364. width: 40px;
  365. height: 40px;
  366. animation: spin 1s linear infinite;
  367. margin: 20px auto;
  368. }
  369. .Brand-Image {
  370. width: 100px;
  371. height: 100px;
  372. object-fit: cover;
  373. border-radius: 8px;
  374. border: 1px solid #ddd;
  375. }
  376. .subject-box {
  377. padding: 8px 14px;
  378. background: linear-gradient(135deg, #fff3e0, #ffe0b2);
  379. color: #ef6c00;
  380. font-weight: 600;
  381. border-radius: 10px;
  382. box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.1);
  383. display: inline-flex;
  384. align-items: center;
  385. gap: 8px;
  386. transition: transform 0.2s ease, box-shadow 0.2s ease;
  387. }
  388. .subject-box i {
  389. color: #ef6c00;
  390. font-size: 1rem;
  391. }
  392. .subject-box:hover {
  393. transform: translateY(-2px);
  394. box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.15);
  395. }
  396. .search-number {
  397. display: flex;
  398. align-items: center;
  399. }
  400. .search-number input {
  401. width: 150px;
  402. padding: 0.5rem;
  403. font-size: 1rem;
  404. border-radius: 0.375rem;
  405. margin-bottom: 7px;
  406. border: 1px solid #ced4da;
  407. transition: border-color 0.3s ease, box-shadow 0.3s ease;
  408. }
  409. .search-number input:focus {
  410. border-color: #007bff;
  411. box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.25);
  412. }
  413. .search-number input::placeholder {
  414. color: #6c757d;
  415. }
  416. .search-number input:disabled {
  417. background-color: #f8f9fa;
  418. cursor: not-allowed;
  419. }
  420. .pagination {
  421. display: flex;
  422. flex-wrap: wrap;
  423. gap: 5px;
  424. }
  425. .page-item {
  426. flex: 0 0 auto;
  427. }
  428. .page-link {
  429. cursor: pointer;
  430. user-select: none;
  431. }
  432. </style>