Files
ntu-awd-website/src/components/Profile/ProfileForm.vue
T
2025-04-17 02:04:35 +08:00

135 lines
3.7 KiB
Vue

<script setup>
import { ref, computed, defineProps, useTemplateRef } from 'vue';
import { useRouter } from 'vue-router';
import { uploadAvatar } from '../../lib/api';
import CAPTCHA from '../CAPTCHA.vue';
import { useAuthStore } from '../../stores/auth';
const props = defineProps({
profile: {
type: Object,
required: true
}
});
const authStore = useAuthStore();
const router = useRouter();
const avatarFile = ref(null);
const avatarError = ref('');
const captcha = useTemplateRef('captcha');
const captchaResponse = ref(null);
const onFileChange = (event) => {
avatarFile.value = event.target.files[0];
};
const captchaVerified = computed(() => {
return captchaResponse.value !== null;
});
const handleCaptchaVerified = (response) => {
captchaResponse.value = response;
};
const onSubmit = async () => {
avatarError.value = '';
if (!avatarFile.value) {
avatarError.value = '必須選擇新頭貼。';
return;
}
if (!captchaVerified) {
avatarError.value = '請完成驗證。';
return;
}
if (avatarFile.value.size > 1 * 1024 * 1024) {
avatarError.value = '頭貼檔案需小於 1MB。';
return;
}
if (avatarFile.value.type !== 'image/jpeg' && avatarFile.value.type !== 'image/png') {
avatarError.value = '頭貼必須是 JPG 或 PNG 格式。';
return;
}
try {
await uploadAvatar(avatarFile.value, authStore.jwt, captchaResponse.value);
alert('Avatar uploaded successfully!');
// After successful upload, reload this page
router.go(0);
} catch (error) {
alert("Avatar upload failed: " + error.message);
}
};
const avatarUrl = computed(() => {
if (avatarFile.value) {
return URL.createObjectURL(avatarFile.value);
}
return props.profile?.avatar ? import.meta.env.VITE_R2_BASE_URL + props.profile.avatar : '';
});
</script>
<template>
<form @submit.prevent="onSubmit">
<fieldset class="ts-fieldset">
<legend class="ts-legend">更新個人檔案</legend>
<div class="ts-wrap is-vertical">
<div class="ts-control is-wide">
<div class="label">使用者名稱</div>
<div class="content is-fluid">
<div class="ts-input">
<input type="text" name="username" placeholder="Username" :value="props.profile?.username" readonly disabled />
</div>
</div>
</div>
<div class="ts-control is-wide">
<div class="label">頭貼</div>
<div class="content is-fluid">
<div v-if="avatarUrl">
<img :src="avatarUrl" alt="Avatar" style="max-width: 100px; max-height: 100px;" />
</div>
<div class="ts-file" :class="{'is-negative': avatarError}">
<input type="file" accept="image/jpeg, image/png" @change="onFileChange" />
</div>
<div class="ts-text is-small is-negative" v-if="avatarError">{{ avatarError }}</div>
<div class="ts-text is-small">頭貼必須是 JPG PNG 格式檔案需小於 1MB</div>
</div>
</div>
</div>
<CAPTCHA
ref="captcha"
:hcaptchaSitekey="$hcaptchaSitekey"
:recaptchaSitekey="$recaptchaSitekey"
:turnstileSitekey="$turnstileSitekey"
@captchaVerified="handleCaptchaVerified"
/>
<div class="ts-wrap has-top-spaced is-end-aligned">
<button class="ts-button" :disabled="!(avatarFile && avatarError === '' && captchaVerified)" type="submit">更新</button>
</div>
</fieldset>
</form>
</template>
<style scoped>
.ts-error {
color: red;
font-size: 0.8em;
}
@media screen and (max-width: 768px) {
.ts-control {
--label-width: 80px;
}
.ts-control .content.is-fluid {
max-width: 100%;
}
}
</style>