feat: midterm shit done

This commit is contained in:
Tony Yang
2025-04-15 03:59:33 +08:00
parent f093df29a1
commit f7ee02586b
34 changed files with 1460 additions and 197 deletions
+102
View File
@@ -0,0 +1,102 @@
<script setup>
import { ref, computed, defineProps } from 'vue';
import { useRouter } from 'vue-router';
import { uploadAvatar } from '../../lib/api';
import { useAuthStore } from '../../stores/auth';
const props = defineProps({
profile: {
type: Object,
required: true
}
});
const authStore = useAuthStore();
const router = useRouter();
const jwt = authStore.jwt;
const avatarFile = ref(null);
const avatarError = ref('');
const onFileChange = (event) => {
avatarFile.value = event.target.files[0];
};
const onSubmit = async () => {
avatarError.value = '';
if (!avatarFile.value) {
avatarError.value = 'Avatar is required.';
return;
}
if (avatarFile.value.size > 2 * 1024 * 1024) {
avatarError.value = 'Avatar must be less than 2MB.';
return;
}
if (avatarFile.value.type !== 'image/jpeg' && avatarFile.value.type !== 'image/png') {
avatarError.value = 'Avatar must be a JPG or PNG image.';
return;
}
try {
await uploadAvatar(avatarFile.value, jwt);
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">Update Profile</legend>
<div class="ts-wrap is-vertical">
<div class="ts-control">
<div class="label">Username</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">
<div class="label">Avatar</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">Avatar must be a JPG or PNG image less than 2MB.</div>
</div>
</div>
</div>
<div class="ts-wrap has-top-spaced is-end-aligned">
<button class="ts-button" type="submit">Update</button>
</div>
</fieldset>
</form>
</template>
<style scoped>
.ts-error {
color: red;
font-size: 0.8em;
}
</style>