135 lines
3.7 KiB
Vue
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>
|