Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 

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