index.vue 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. <script setup lang="ts">
  2. import { computed, ref, watch } from 'vue';
  3. import { VbenButton } from '@vben/common-ui';
  4. import { useUserStore } from '@vben/stores';
  5. import { $t } from '@/locales';
  6. import {
  7. Dropdown,
  8. Empty,
  9. Input,
  10. message,
  11. Modal,
  12. Pagination,
  13. Select,
  14. } from 'antdv-next';
  15. import {
  16. deleteApplicationApi,
  17. getMyApplicationListApi,
  18. getPartnersApi,
  19. type UserApi,
  20. } from '#/api';
  21. import ApplicationModal from './application-modal.vue';
  22. const applicationMenu = ref<Array<{ label: string; value: any }>>([]);
  23. const modalOpen = ref(false);
  24. const modalMode = ref<'add' | 'edit'>('add');
  25. const currentApplication = ref<any>(null);
  26. const userStore = useUserStore();
  27. const isLogin = computed(() => !!userStore.userInfo);
  28. const applicationList = ref<UserApi.ApplicationModel[]>([]);
  29. const loading = ref(false);
  30. const searchParams = ref({
  31. applicationName: '',
  32. applicationId: '',
  33. cooperatePartner: '',
  34. activateNow: 'Yes',
  35. currentPage: 1,
  36. pageSize: 16,
  37. });
  38. const totalPage = ref(0);
  39. const totalCount = ref(0);
  40. function handleSearch() {
  41. searchParams.value.currentPage = 1;
  42. fetchApplicationList();
  43. }
  44. function handleAddNew() {
  45. modalMode.value = 'add';
  46. currentApplication.value = null;
  47. modalOpen.value = true;
  48. }
  49. function handleEdit(item: any) {
  50. modalMode.value = 'edit';
  51. currentApplication.value = item;
  52. modalOpen.value = true;
  53. }
  54. function handleModalSave() {
  55. modalOpen.value = false;
  56. fetchApplicationList();
  57. }
  58. function handleMenuClick({ key }: { key: string }, item: any) {
  59. if (key === 'Edit') {
  60. handleEdit(item);
  61. } else if (key === 'Remove') {
  62. Modal.confirm({
  63. title: 'Are you sure delete this task?',
  64. content: 'Some descriptions',
  65. okText: 'Yes',
  66. okType: 'danger',
  67. cancelText: 'No',
  68. onOk() {
  69. deleteApplication(item);
  70. },
  71. onCancel() {},
  72. });
  73. }
  74. }
  75. function handleBack() {
  76. window.history.back();
  77. }
  78. async function fetchApplicationList() {
  79. if (!isLogin.value) {
  80. return;
  81. }
  82. try {
  83. loading.value = true;
  84. const result = await getMyApplicationListApi({
  85. currentPage: searchParams.value.currentPage,
  86. pageSize: searchParams.value.pageSize,
  87. orderByProperty: 'name',
  88. Ascending: true,
  89. filters: [
  90. {
  91. name: 'isEnable',
  92. value: searchParams.value.activateNow === 'Yes' ? '1' : '0',
  93. },
  94. {
  95. name: 'name',
  96. value: searchParams.value.applicationName,
  97. },
  98. {
  99. name: 'partnerInfoId',
  100. value: searchParams.value.cooperatePartner,
  101. },
  102. {
  103. name: 'code',
  104. value: searchParams.value.applicationId,
  105. },
  106. ],
  107. });
  108. if (result?.result?.model) {
  109. applicationList.value = result.result.model;
  110. totalPage.value = result.result.totalPages;
  111. totalCount.value = result.result.totalCount;
  112. }
  113. } catch (error) {
  114. console.error('获取应用列表失败:', error);
  115. } finally {
  116. loading.value = false;
  117. }
  118. }
  119. async function fetchPartners() {
  120. if (!isLogin.value) {
  121. return;
  122. }
  123. try {
  124. loading.value = true;
  125. const result = await getPartnersApi();
  126. if (result?.result) {
  127. applicationMenu.value = result.result.map(
  128. (item: { id: any; name: any }) => ({
  129. value: item.id,
  130. label: item.name,
  131. }),
  132. );
  133. }
  134. } catch (error) {
  135. console.error('获取合作伙伴失败:', error);
  136. } finally {
  137. loading.value = false;
  138. }
  139. }
  140. async function deleteApplication(item: any) {
  141. if (!isLogin.value) {
  142. return;
  143. }
  144. try {
  145. loading.value = true;
  146. const result = await deleteApplicationApi({
  147. id: item.id,
  148. });
  149. if (result?.result) {
  150. fetchApplicationList();
  151. message.success('删除成功!');
  152. }
  153. } catch (error) {
  154. console.error('删除应用失败:', error);
  155. } finally {
  156. loading.value = false;
  157. }
  158. }
  159. watch(
  160. () => isLogin.value,
  161. (newValue) => {
  162. if (newValue) {
  163. fetchApplicationList();
  164. fetchPartners();
  165. }
  166. },
  167. { immediate: true },
  168. );
  169. </script>
  170. <template>
  171. <div class="p-5">
  172. <div class="mb-4 flex items-center justify-between">
  173. <div class="text-sm text-[#462424] text-gray-500">
  174. Dashboard / Application Management
  175. </div>
  176. <div
  177. class="cursor-pointer text-[16px] font-bold text-[#462424]"
  178. @click="handleBack"
  179. >
  180. Back
  181. </div>
  182. </div>
  183. <div class="mb-[21px] mt-[30px] text-[26px] font-bold text-[#462424]">
  184. {{ $t('homeModule.applicationManagement') }}
  185. </div>
  186. <div class="mb-4 flex flex-wrap items-center gap-4">
  187. <div class="flex flex-col gap-1">
  188. <label class="text-[11px] text-[#000]">Application Name:</label>
  189. <Input
  190. v-model:value="searchParams.applicationName"
  191. class="h-[42px] w-[147px] rounded-[11px] border-[#707070]"
  192. placeholder=""
  193. />
  194. </div>
  195. <div class="flex flex-col gap-1">
  196. <label class="text-[11px] text-[#000]">Application ID:</label>
  197. <Input
  198. v-model:value="searchParams.applicationId"
  199. class="h-[42px] w-[89px] rounded-[11px] border-[#707070]"
  200. placeholder=""
  201. />
  202. </div>
  203. <div class="flex flex-col gap-1">
  204. <label class="text-[11px] text-[#000]">Cooperate Partner:</label>
  205. <Select
  206. v-model:value="searchParams.cooperatePartner"
  207. :options="applicationMenu"
  208. class="h-[42px] w-[168px] rounded-[11px] border-[#707070] text-[12px]"
  209. placeholder="Choose Coope Partner"
  210. />
  211. </div>
  212. <div class="ml-[86px] flex flex-col gap-1">
  213. <label class="text-[11px] text-[#000]">Activate Now:</label>
  214. <div class="flex h-[42px] items-center gap-2">
  215. <label
  216. class="flex cursor-pointer items-center gap-2 text-sm"
  217. @click="searchParams.activateNow = 'Yes'"
  218. >
  219. <div
  220. class="flex h-[20px] w-[20px] items-center justify-center rounded-[5px] border-[1px] border-[#707070]"
  221. >
  222. <div
  223. v-if="searchParams.activateNow === 'Yes'"
  224. class="h-[14px] w-[14px] flex-shrink-0 rounded-[3px] bg-[#462424]"
  225. ></div>
  226. </div>
  227. Yes
  228. </label>
  229. <label
  230. class="flex cursor-pointer items-center gap-2 text-sm"
  231. @click="searchParams.activateNow = 'No'"
  232. >
  233. <div
  234. class="flex h-[20px] w-[20px] items-center justify-center rounded-[5px] border-[1px] border-[#707070]"
  235. >
  236. <div
  237. v-if="searchParams.activateNow === 'No'"
  238. class="h-[14px] w-[14px] flex-shrink-0 rounded-[3px] bg-[#462424]"
  239. ></div>
  240. </div>
  241. No
  242. </label>
  243. </div>
  244. </div>
  245. <div class="ml-auto flex items-center gap-2">
  246. <VbenButton
  247. class="flex h-[42px] w-[102px] items-center gap-[7px] rounded-[25px] border-[#000] bg-transparent text-sm font-medium"
  248. variant="outline"
  249. @click="handleSearch"
  250. >
  251. <img
  252. alt=""
  253. class="h-[21.5px] w-[21.5px] cursor-pointer"
  254. src="@/assets/image/search.png"
  255. />
  256. Search
  257. </VbenButton>
  258. <VbenButton
  259. class="flex h-[42px] w-[133px] items-center gap-[7px] rounded-[25px] text-sm font-medium text-white"
  260. style="background: linear-gradient(to right, #8b0046, #460023)"
  261. @click="handleAddNew"
  262. >
  263. <img
  264. alt=""
  265. class="h-[21.5px] w-[21.5px] cursor-pointer"
  266. src="@/assets/image/new.png"
  267. />
  268. Add New
  269. </VbenButton>
  270. </div>
  271. </div>
  272. <div v-if="loading" class="py-8 text-center">加载中...</div>
  273. <div
  274. v-else-if="applicationList.length === 0"
  275. class="mt-[100px] py-8 text-center text-gray-500"
  276. >
  277. <Empty />
  278. </div>
  279. <div v-else class="mb-[76px] mt-[38px] flex flex-wrap gap-[18px]">
  280. <div
  281. v-for="item in applicationList"
  282. :key="item.id"
  283. class="flex h-[78px] cursor-pointer items-center gap-[25px] rounded-[11px] bg-[#fff] px-[20px] shadow-md"
  284. >
  285. <img
  286. :src="`/File/Download?fileId=${item.imgPhotoFileId}`"
  287. alt=""
  288. class="h-[48px] w-auto object-contain"
  289. />
  290. <Dropdown
  291. :menu="{
  292. items: [
  293. { key: 'Edit', label: 'Edit' },
  294. { key: 'Remove', label: 'Remove' },
  295. ],
  296. }"
  297. placement="bottom"
  298. @menu-click="(info: any) => handleMenuClick(info, item)"
  299. >
  300. <div class="flex cursor-pointer items-center gap-2">
  301. <img
  302. alt=""
  303. class="h-[19px] w-[19px] cursor-pointer"
  304. src="@/assets/image/more-tow.png"
  305. />
  306. </div>
  307. </Dropdown>
  308. </div>
  309. </div>
  310. <div v-if="totalCount > 16" class="list-pagination">
  311. <Pagination
  312. v-model:current="searchParams.currentPage"
  313. :show-size-changer="false"
  314. :total="totalCount"
  315. />
  316. </div>
  317. <ApplicationModal
  318. v-model:open="modalOpen"
  319. :application-data="currentApplication"
  320. :mode="modalMode"
  321. @save="handleModalSave"
  322. />
  323. </div>
  324. </template>
  325. <style lang="scss">
  326. .ant-pagination-item-active {
  327. color: #7a003d !important;
  328. background-color: #f8f2f5 !important;
  329. border-color: transparent !important;
  330. a {
  331. color: #c48da8 !important;
  332. }
  333. }
  334. </style>