ProfileView.vue 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. <template>
  2. <IonPage>
  3. <IonHeader>
  4. <IonToolbar>
  5. <IonButtons slot="start">
  6. <IonBackButton text="" />
  7. </IonButtons>
  8. <IonTitle>Profile</IonTitle>
  9. </IonToolbar>
  10. </IonHeader>
  11. <IonContent>
  12. <div class="flex flex-col items-center mt-8">
  13. <div class="w-[72px] h-[72px] relative">
  14. <img
  15. class="w-[72px] h-[72px] object-cover rounded-full"
  16. :src="avatar"
  17. alt="avatar"
  18. />
  19. <div
  20. class="absolute bottom-0 right-0 rounded-full bg-white text-black w-[20px] h-[20px] flex justify-center items-center"
  21. >
  22. <IonIcon class="text-sm" :icon="cameraOutline" />
  23. </div>
  24. <input
  25. type="file"
  26. accept="image/*"
  27. class="absolute w-full h-full opacity-0 bottom-0 right-0"
  28. @change="onChooseFile"
  29. />
  30. </div>
  31. <div class="mt-4 text-xs text-neutral-500">
  32. Current Logged In Email
  33. </div>
  34. <div class="text-sm">{{ user?.email }}</div>
  35. <div class="w-[250px]">
  36. <input
  37. v-model="username"
  38. placeholder="username"
  39. class="w-full h-10 mt-6 rounded-md px-4 bg-neutral-800 active:border-2 focus:border-2 border-prim focus:border-prim outline-none"
  40. />
  41. <button
  42. class="mt-6 w-full h-10 bg-prim text-white rounded-md disabled:bg-opacity-50 disabled:text-opacity-50"
  43. :disabled="disabled"
  44. @click="save"
  45. >
  46. Save
  47. </button>
  48. </div>
  49. </div>
  50. </IonContent>
  51. </IonPage>
  52. </template>
  53. <script setup lang="ts">
  54. import { computed, ref, watch } from "vue";
  55. import {
  56. IonPage,
  57. IonContent,
  58. IonHeader,
  59. IonToolbar,
  60. IonTitle,
  61. IonLabel,
  62. IonButtons,
  63. IonBackButton,
  64. IonIcon,
  65. } from "@ionic/vue";
  66. import { useUserStore } from "@/store/user";
  67. import { storeToRefs } from "pinia";
  68. import IconAvatar from "@/assets/icon_avatar.png";
  69. import { cameraOutline } from "ionicons/icons";
  70. import http from "@/plugins/http";
  71. import toast from "@/plugins/toast";
  72. const userStore = useUserStore();
  73. const { user } = storeToRefs(userStore);
  74. const { getUser } = userStore;
  75. const username = ref("");
  76. const avatar = ref("");
  77. username.value = user.value?.username || "";
  78. avatar.value = user.value?.avatar || IconAvatar;
  79. const disabled = computed(() => {
  80. return username.value === user.value?.username;
  81. });
  82. async function onChooseFile(e: any) {
  83. console.log("onChooseFile", e);
  84. if (e.target.files.length > 0) {
  85. const file = e.target.files[0];
  86. console.log("file", file);
  87. const formData = new FormData();
  88. formData.append("file", file);
  89. const { url } = await http.post("/files/upload", formData);
  90. avatar.value = url;
  91. await http.put(`/users/${user.value?.id}`, { avatar: url });
  92. getUser();
  93. }
  94. }
  95. async function save() {
  96. try {
  97. await http.put(`/users/${user.value?.id}`, {
  98. username: username.value,
  99. });
  100. getUser();
  101. toast("Saved", { type: "success" });
  102. } catch (error: any) {
  103. console.error(error.error[0]?.message);
  104. }
  105. }
  106. </script>
  107. <style lang="less" scoped></style>