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

558 行
15 KiB

  1. <script>
  2. import Layout from "@/layout/custom.vue";
  3. import { onMounted, ref, watch, computed } from "vue";
  4. import Swal from "sweetalert2";
  5. import ApiServiece from "@/services/ApiService";
  6. import moment from "jalali-moment";
  7. import editUser from "@/components/modals/editUser.vue";
  8. import addUser from "@/components/modals/addUser.vue";
  9. import { toast } from "vue3-toastify";
  10. import "vue3-toastify/dist/index.css";
  11. export default {
  12. name: "SAMPLE-PAGE",
  13. components: {
  14. Layout,
  15. editUser,
  16. addUser,
  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 searchQuery = ref("");
  25. const users = ref([]);
  26. const userName = ref();
  27. const userMobile = ref();
  28. const userId = ref();
  29. const userRole = ref();
  30. const convertToJalali = (date) => {
  31. return moment(date, "YYYY-MM-DD HH:mm:ss")
  32. .locale("fa")
  33. .format("YYYY/MM/DD");
  34. };
  35. const getUsers = () => {
  36. ApiServiece.get(
  37. `admin/users?name=${searchQuery.value || ""}&paginate=${
  38. paginate.value || 10
  39. }&page=${page.value || 1}`
  40. ).then((resp) => {
  41. console.log(resp.data.data);
  42. users.value = resp.data.data.data;
  43. currentPage.value = resp.data.data.current_page;
  44. totalPages.value = resp.data.data.last_page;
  45. });
  46. };
  47. const nextPage = () => {
  48. if (currentPage.value < totalPages.value) {
  49. page.value++;
  50. getUsers();
  51. }
  52. };
  53. const prevPage = () => {
  54. if (currentPage.value > 1) {
  55. page.value--;
  56. getUsers();
  57. }
  58. };
  59. const visiblePages = computed(() => {
  60. const pages = [];
  61. if (totalPages.value <= 5) {
  62. for (let i = 1; i <= totalPages.value; i++) {
  63. pages.push(i);
  64. }
  65. } else {
  66. let start = currentPage.value - 2;
  67. let end = currentPage.value + 2;
  68. if (start < 1) start = 1;
  69. if (end > totalPages.value) end = totalPages.value;
  70. for (let i = start; i <= end; i++) {
  71. pages.push(i);
  72. }
  73. }
  74. return pages;
  75. });
  76. watch(searchQuery, () => {
  77. getUsers();
  78. });
  79. watch(page, () => {
  80. getUsers();
  81. });
  82. function handlePageInput() {
  83. if (searchPage.value < 1) {
  84. searchPage.value = 1;
  85. } else if (searchPage.value > totalPages.value) {
  86. searchPage.value = totalPages.value;
  87. }
  88. if (searchPage.value >= 1 && searchPage.value <= totalPages.value) {
  89. page.value = searchPage.value;
  90. }
  91. }
  92. const modalData = (id, name, mobile, role) => {
  93. userName.value = name;
  94. userMobile.value = mobile;
  95. userId.value = id;
  96. userRole.value = role;
  97. };
  98. const handleUserUpdated = () => {
  99. getUsers();
  100. };
  101. const blockUser = (id) => {
  102. Swal.fire({
  103. text: "آیا میخواهید این کاربر را مسدود کنید؟",
  104. icon: "warning",
  105. showCancelButton: true,
  106. confirmButtonText: "بله",
  107. cancelButtonText: "خیر",
  108. confirmButtonColor: "#3085d6",
  109. cancelButtonColor: "#d33",
  110. }).then((result) => {
  111. if (result.isConfirmed) {
  112. ApiServiece.delete(`admin/users/${id}`)
  113. .then(() => {
  114. toast.success("!کاربر با موفقیت بلاک شد", {
  115. position: "top-right",
  116. autoClose: 1000,
  117. });
  118. })
  119. .then(() => {
  120. getUsers();
  121. })
  122. .catch((err) => {
  123. console.log(err);
  124. toast.error("در مسدود کردن کاربر مشکلی پیش آمد", {
  125. position: "top-right",
  126. autoClose: 1000,
  127. });
  128. });
  129. }
  130. });
  131. };
  132. const unBlockUser = (id) => {
  133. Swal.fire({
  134. text: "آیا میخواهید این کاربر را فعال نمایید؟",
  135. icon: "warning",
  136. showCancelButton: true,
  137. confirmButtonText: "بله",
  138. cancelButtonText: "خیر",
  139. confirmButtonColor: "#3085d6",
  140. cancelButtonColor: "#d33",
  141. }).then((result) => {
  142. if (result.isConfirmed) {
  143. ApiServiece.put(`admin/users/${id}/restore`)
  144. .then(() => {
  145. toast.success("!کاربر با موفقیت فعال شد", {
  146. position: "top-right",
  147. autoClose: 1000,
  148. });
  149. })
  150. .then(() => {
  151. getUsers();
  152. })
  153. .catch((err) => {
  154. console.log(err);
  155. toast.error("در فعال کردن کاربر مشکلی پیش آمد", {
  156. position: "top-right",
  157. autoClose: 1000,
  158. });
  159. });
  160. }
  161. });
  162. };
  163. onMounted(() => {
  164. getUsers();
  165. });
  166. return {
  167. users,
  168. convertToJalali,
  169. searchQuery,
  170. modalData,
  171. userName,
  172. userMobile,
  173. userId,
  174. handleUserUpdated,
  175. blockUser,
  176. unBlockUser,
  177. userRole,
  178. handlePageInput,
  179. nextPage,
  180. paginate,
  181. totalPages,
  182. currentPage,
  183. prevPage,
  184. visiblePages,
  185. page,
  186. searchPage,
  187. };
  188. },
  189. };
  190. </script>
  191. <template>
  192. <Layout>
  193. <BRow>
  194. <div class="col-sm-12">
  195. <div class="card table-card user-profile-list">
  196. <div class="card-body">
  197. <div class="filter-container">
  198. <div class="search-filters">
  199. <input
  200. type="text"
  201. class="form-control search-input"
  202. placeholder="جستجو بر اساس نام کاربر"
  203. id="search-users"
  204. v-model="searchQuery"
  205. />
  206. <!-- <div class="dropdown">
  207. <button
  208. class="btn btn-dropdown"
  209. type="button"
  210. id="filtersDropdown"
  211. data-bs-toggle="dropdown"
  212. aria-expanded="false"
  213. >
  214. فیلترها
  215. </button>
  216. <ul class="dropdown-menu" aria-labelledby="filtersDropdown">
  217. <li><a class="dropdown-item" href="#">همه کاربران</a></li>
  218. <li><a class="dropdown-item" href="#">فقط مدیران</a></li>
  219. <li><a class="dropdown-item" href="#">فقط مشتریان</a></li>
  220. <li><a class="dropdown-item" href="#">فعال</a></li>
  221. <li><a class="dropdown-item" href="#">بلاک</a></li>
  222. </ul>
  223. </div> -->
  224. <button
  225. data-bs-toggle="modal"
  226. data-bs-target="#addUser"
  227. class="btn btn-add-user me-3"
  228. >
  229. افزودن کاربر
  230. </button>
  231. </div>
  232. </div>
  233. <div class="table-responsive">
  234. <table class="table table-hover" id="pc-dt-simple">
  235. <thead>
  236. <tr>
  237. <th>نام</th>
  238. <th>موبایل</th>
  239. <th>نقش</th>
  240. <th>تاریخ ایجاد</th>
  241. <th>وضعیت</th>
  242. </tr>
  243. </thead>
  244. <tbody>
  245. <tr v-for="user in users" :key="user.id">
  246. <td>
  247. <div class="d-inline-block align-middle">
  248. <img
  249. src="@/assets/images/user/avatar-1.jpg"
  250. alt="user image"
  251. class="img-radius align-top m-r-15"
  252. style="width: 40px"
  253. />
  254. <div class="d-inline-block users-names">
  255. <h6 v-if="user.name" class="m-b-0">
  256. {{ user?.name }}
  257. </h6>
  258. <h6 v-else class="m-b-0">بی نام</h6>
  259. </div>
  260. </div>
  261. </td>
  262. <td>{{ user?.mobile }}</td>
  263. <td v-if="user.role === 'admin'">مدیر</td>
  264. <td v-if="user.role === 'client'">مشتری</td>
  265. <td v-if="user.role === 'operator'">اپراتور</td>
  266. <td>{{ convertToJalali(user.created_at) }}</td>
  267. <td>
  268. <span
  269. v-if="!user.deleted_at"
  270. class="badge bg-light-success"
  271. >فعال</span
  272. >
  273. <span v-else class="badge bg-light-danger">بلاک</span>
  274. <div class="overlay-edit">
  275. <ul class="list-inline mb-0">
  276. <li class="list-inline-item m-0">
  277. <a
  278. @click="
  279. modalData(
  280. user.id,
  281. user.name,
  282. user.mobile,
  283. user.role
  284. )
  285. "
  286. data-bs-toggle="modal"
  287. data-bs-target="#editUser"
  288. href="#"
  289. class="avtar avtar-s btn btn-primary"
  290. >
  291. <i class="ti ti-pencil f-18"></i>
  292. </a>
  293. </li>
  294. <li class="list-inline-item m-0">
  295. <a
  296. v-if="!user.deleted_at"
  297. @click="blockUser(user.id)"
  298. href="#"
  299. class="avtar avtar-s btn bg-white btn-link-danger"
  300. >
  301. <i class="ti ti-lock f-18"></i>
  302. </a>
  303. <a
  304. @click="unBlockUser(user.id)"
  305. v-else
  306. href="#"
  307. class="avtar avtar-s btn bg-white btn-link-success"
  308. >
  309. <i class="fa fa-unlock-alt f-18"></i>
  310. </a>
  311. </li>
  312. </ul>
  313. </div>
  314. </td>
  315. </tr>
  316. </tbody>
  317. </table>
  318. </div>
  319. </div>
  320. </div>
  321. </div>
  322. <editUser
  323. @user-updated="handleUserUpdated"
  324. :name="userName"
  325. :mobile="userMobile"
  326. :id="userId"
  327. :role="userRole"
  328. />
  329. <addUser @user-updated="handleUserUpdated" />
  330. </BRow>
  331. <BRow>
  332. <BCol sm="12">
  333. <div class="d-flex justify-content-center">
  334. <nav aria-label="Page navigation">
  335. <ul class="pagination">
  336. <li class="page-item" :class="{ disabled: currentPage === 1 }">
  337. <span class="page-link" @click="prevPage">قبلی</span>
  338. </li>
  339. <li v-if="currentPage > 2" class="page-item" @click="page = 1">
  340. <a class="page-link" href="javascript:void(0)">1</a>
  341. </li>
  342. <li v-if="currentPage > 3" class="page-item" disabled>
  343. <span class="page-link">...</span>
  344. </li>
  345. <li
  346. v-for="n in visiblePages"
  347. :key="n"
  348. class="page-item"
  349. :class="{ active: currentPage === n }"
  350. >
  351. <a
  352. class="page-link"
  353. href="javascript:void(0)"
  354. @click="page = n"
  355. >
  356. {{ n }}
  357. </a>
  358. </li>
  359. <li
  360. v-if="currentPage < totalPages - 2"
  361. class="page-item"
  362. disabled
  363. >
  364. <span class="page-link">...</span>
  365. </li>
  366. <li
  367. v-if="currentPage < totalPages - 1"
  368. class="page-item"
  369. @click="page = totalPages"
  370. >
  371. <a class="page-link" href="javascript:void(0)">{{
  372. totalPages
  373. }}</a>
  374. </li>
  375. <li
  376. class="page-item"
  377. :class="{ disabled: currentPage === totalPages }"
  378. >
  379. <span class="page-link" @click="nextPage">بعدی</span>
  380. </li>
  381. </ul>
  382. </nav>
  383. </div>
  384. </BCol>
  385. <BCol sm="4">
  386. <div class="ms-0 search-number">
  387. <input
  388. v-model="searchPage"
  389. type="text"
  390. class="form-control"
  391. placeholder="برو به صفحه"
  392. :max="totalPages"
  393. min="1"
  394. @input="handlePageInput"
  395. />
  396. </div>
  397. </BCol>
  398. </BRow>
  399. </Layout>
  400. </template>
  401. <style scoped>
  402. .users-names {
  403. margin-top: 7px;
  404. }
  405. .filter-container {
  406. display: flex;
  407. justify-content: space-between;
  408. align-items: center;
  409. margin-bottom: 24px;
  410. padding: 16px;
  411. background-color: #fafafa;
  412. border-radius: 8px;
  413. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  414. }
  415. .search-filters {
  416. display: flex;
  417. align-items: center;
  418. }
  419. .search-input {
  420. width: 250px;
  421. padding: 10px 16px;
  422. font-size: 14px;
  423. border: 1px solid #ccc;
  424. border-radius: 8px;
  425. margin-right: 16px;
  426. transition: all 0.3s ease;
  427. }
  428. .search-input:focus {
  429. outline: none;
  430. border-color: #007bff;
  431. box-shadow: 0 0 8px rgba(0, 123, 255, 0.3);
  432. }
  433. .btn-dropdown {
  434. padding: 10px 16px;
  435. font-size: 14px;
  436. border: 1px solid #ccc;
  437. border-radius: 8px;
  438. background-color: white;
  439. transition: all 0.3s ease;
  440. }
  441. .btn-dropdown:hover {
  442. background-color: #f1f1f1;
  443. border-color: #007bff;
  444. }
  445. .dropdown-menu {
  446. border-radius: 8px;
  447. }
  448. .dropdown-item {
  449. padding: 12px 16px;
  450. font-size: 14px;
  451. color: #333;
  452. transition: background-color 0.3s ease;
  453. }
  454. .dropdown {
  455. margin-right: 5px;
  456. }
  457. .dropdown-item:hover {
  458. background-color: #f1f1f1;
  459. }
  460. .btn-add-user {
  461. display: flex;
  462. align-items: center;
  463. padding: 10px 20px;
  464. background-color: #007bff;
  465. color: white;
  466. font-size: 14px;
  467. border-radius: 8px;
  468. border: none;
  469. transition: all 0.3s ease;
  470. }
  471. .btn-add-user i {
  472. margin-right: 8px;
  473. }
  474. .btn-add-user:hover {
  475. background-color: #0056b3;
  476. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  477. }
  478. .search-number {
  479. display: flex;
  480. align-items: center;
  481. }
  482. .search-number input {
  483. width: 150px;
  484. padding: 0.5rem;
  485. font-size: 1rem;
  486. border-radius: 0.375rem;
  487. margin-bottom: 7px;
  488. border: 1px solid #ced4da;
  489. transition: border-color 0.3s ease, box-shadow 0.3s ease;
  490. }
  491. .search-number input:focus {
  492. border-color: #007bff;
  493. box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.25);
  494. }
  495. .search-number input::placeholder {
  496. color: #6c757d;
  497. }
  498. .search-number input:disabled {
  499. background-color: #f8f9fa;
  500. cursor: not-allowed;
  501. }
  502. .pagination {
  503. display: flex;
  504. flex-wrap: wrap;
  505. gap: 5px;
  506. }
  507. .page-item {
  508. flex: 0 0 auto;
  509. }
  510. .page-link {
  511. cursor: pointer;
  512. user-select: none;
  513. }
  514. </style>