mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-06-07 18:47:44 +08:00
feat: create base input component and update pages
This commit is contained in:
63
open-isle-cli/src/components/BaseInput.vue
Normal file
63
open-isle-cli/src/components/BaseInput.vue
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<div class="base-input">
|
||||||
|
<i v-if="icon" :class="['base-input-icon', icon]"></i>
|
||||||
|
<component
|
||||||
|
:is="textarea ? 'textarea' : 'input'"
|
||||||
|
class="base-input-text"
|
||||||
|
:type="type"
|
||||||
|
v-model="innerValue"
|
||||||
|
v-bind="$attrs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'BaseInput',
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: {
|
||||||
|
modelValue: { type: [String, Number], default: '' },
|
||||||
|
icon: { type: String, default: '' },
|
||||||
|
type: { type: String, default: 'text' },
|
||||||
|
textarea: { type: Boolean, default: false }
|
||||||
|
},
|
||||||
|
emits: ['update:modelValue'],
|
||||||
|
computed: {
|
||||||
|
innerValue: {
|
||||||
|
get() {
|
||||||
|
return this.modelValue
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.$emit('update:modelValue', val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.base-input {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: calc(100% - 40px);
|
||||||
|
padding: 15px 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.base-input-icon {
|
||||||
|
opacity: 0.5;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.base-input-text {
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -8,25 +8,18 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="email-login-page-content">
|
<div class="email-login-page-content">
|
||||||
<div class="login-page-input">
|
<BaseInput
|
||||||
<i class="login-page-input-icon fas fa-envelope"></i>
|
icon="fas fa-envelope"
|
||||||
<input
|
v-model="username"
|
||||||
class="login-page-input-text"
|
placeholder="邮箱/用户名"
|
||||||
v-model="username"
|
/>
|
||||||
type="text"
|
|
||||||
placeholder="邮箱/用户名"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="login-page-input">
|
<BaseInput
|
||||||
<i class="login-page-input-icon fas fa-lock"></i>
|
icon="fas fa-lock"
|
||||||
<input
|
v-model="password"
|
||||||
class="login-page-input-text"
|
type="password"
|
||||||
v-model="password"
|
placeholder="密码"
|
||||||
type="password"
|
/>
|
||||||
placeholder="密码"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div v-if="!isWaitingForLogin" class="login-page-button-primary" @click="submitLogin">
|
<div v-if="!isWaitingForLogin" class="login-page-button-primary" @click="submitLogin">
|
||||||
@@ -58,8 +51,10 @@
|
|||||||
import { API_BASE_URL, toast } from '../main'
|
import { API_BASE_URL, toast } from '../main'
|
||||||
import { setToken } from '../utils/auth'
|
import { setToken } from '../utils/auth'
|
||||||
import { googleSignIn } from '../utils/google'
|
import { googleSignIn } from '../utils/google'
|
||||||
|
import BaseInput from '../components/BaseInput.vue'
|
||||||
export default {
|
export default {
|
||||||
name: 'LoginPageView',
|
name: 'LoginPageView',
|
||||||
|
components: { BaseInput },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
username: '',
|
username: '',
|
||||||
|
|||||||
@@ -3,16 +3,26 @@
|
|||||||
<div class="settings-title">设置</div>
|
<div class="settings-title">设置</div>
|
||||||
<div class="profile-section">
|
<div class="profile-section">
|
||||||
<div class="avatar-row">
|
<div class="avatar-row">
|
||||||
<img :src="avatar" class="avatar-preview" />
|
<!-- label 充当点击区域,内部隐藏 input -->
|
||||||
<input type="file" @change="onAvatarChange" />
|
<label class="avatar-container">
|
||||||
|
<img :src="avatar" class="avatar-preview" />
|
||||||
|
<!-- 半透明蒙层:hover 时出现 -->
|
||||||
|
<div class="avatar-overlay">更换头像</div>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
class="avatar-input"
|
||||||
|
accept="image/*"
|
||||||
|
@change="onAvatarChange"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label>用户名</label>
|
<label>用户名</label>
|
||||||
<input v-model="username" type="text" />
|
<BaseInput v-model="username" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label>自我介绍</label>
|
<label>自我介绍</label>
|
||||||
<textarea v-model="introduction" rows="3" />
|
<BaseInput v-model="introduction" textarea rows="3" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="role === 'ADMIN'" class="admin-section">
|
<div v-if="role === 'ADMIN'" class="admin-section">
|
||||||
@@ -42,8 +52,10 @@
|
|||||||
<script>
|
<script>
|
||||||
import { API_BASE_URL, toast } from '../main'
|
import { API_BASE_URL, toast } from '../main'
|
||||||
import { getToken, fetchCurrentUser } from '../utils/auth'
|
import { getToken, fetchCurrentUser } from '../utils/auth'
|
||||||
|
import BaseInput from '../components/BaseInput.vue'
|
||||||
export default {
|
export default {
|
||||||
name: 'SettingsPageView',
|
name: 'SettingsPageView',
|
||||||
|
components: { BaseInput },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
username: '',
|
username: '',
|
||||||
@@ -163,4 +175,36 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 容器:定位 + 光标 */
|
||||||
|
.avatar-container {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 隐藏默认文件选择按钮 */
|
||||||
|
.avatar-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 蒙层:初始透明,hover 时渐显 */
|
||||||
|
.avatar-overlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
border-radius: 40px;
|
||||||
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hover 触发 */
|
||||||
|
.avatar-container:hover .avatar-overlay {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -8,51 +8,36 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="emailStep === 0" class="email-signup-page-content">
|
<div v-if="emailStep === 0" class="email-signup-page-content">
|
||||||
<div class="signup-page-input">
|
<BaseInput
|
||||||
<i class="signup-page-input-icon fas fa-envelope"></i>
|
icon="fas fa-envelope"
|
||||||
<input
|
v-model="email"
|
||||||
class="signup-page-input-text"
|
@input="emailError = ''"
|
||||||
v-model="email"
|
placeholder="邮箱"
|
||||||
@input="emailError = ''"
|
/>
|
||||||
type="text"
|
|
||||||
placeholder="邮箱"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div v-if="emailError" class="error-message">{{ emailError }}</div>
|
<div v-if="emailError" class="error-message">{{ emailError }}</div>
|
||||||
|
|
||||||
<div class="signup-page-input">
|
<BaseInput
|
||||||
<i class="signup-page-input-icon fas fa-user"></i>
|
icon="fas fa-user"
|
||||||
<input
|
v-model="username"
|
||||||
class="signup-page-input-text"
|
@input="usernameError = ''"
|
||||||
v-model="username"
|
placeholder="用户名"
|
||||||
@input="usernameError = ''"
|
/>
|
||||||
type="text"
|
|
||||||
placeholder="用户名"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div v-if="usernameError" class="error-message">{{ usernameError }}</div>
|
<div v-if="usernameError" class="error-message">{{ usernameError }}</div>
|
||||||
|
|
||||||
<div class="signup-page-input">
|
<BaseInput
|
||||||
<i class="signup-page-input-icon fas fa-lock"></i>
|
icon="fas fa-lock"
|
||||||
<input
|
v-model="password"
|
||||||
class="signup-page-input-text"
|
@input="passwordError = ''"
|
||||||
v-model="password"
|
type="password"
|
||||||
@input="passwordError = ''"
|
placeholder="密码"
|
||||||
type="password"
|
/>
|
||||||
placeholder="密码"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div v-if="passwordError" class="error-message">{{ passwordError }}</div>
|
<div v-if="passwordError" class="error-message">{{ passwordError }}</div>
|
||||||
|
|
||||||
<div class="signup-page-input">
|
<BaseInput
|
||||||
<i class="signup-page-input-icon fas fa-user"></i>
|
icon="fas fa-user"
|
||||||
<input
|
v-model="nickname"
|
||||||
class="signup-page-input-text"
|
placeholder="昵称 (可选)"
|
||||||
v-model="nickname"
|
/>
|
||||||
type="text"
|
|
||||||
placeholder="昵称 (可选)"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="!isWaitingForEmailSent" class="signup-page-button-primary" @click="sendVerification">
|
<div v-if="!isWaitingForEmailSent" class="signup-page-button-primary" @click="sendVerification">
|
||||||
<div class="signup-page-button-text">验证邮箱</div>
|
<div class="signup-page-button-text">验证邮箱</div>
|
||||||
@@ -69,15 +54,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="emailStep === 1" class="email-signup-page-content">
|
<div v-if="emailStep === 1" class="email-signup-page-content">
|
||||||
<div class="signup-page-input">
|
<BaseInput
|
||||||
<i class="signup-page-input-icon fas fa-envelope"></i>
|
icon="fas fa-envelope"
|
||||||
<input
|
v-model="code"
|
||||||
class="signup-page-input-text"
|
placeholder="邮箱验证码"
|
||||||
v-model="code"
|
/>
|
||||||
type="text"
|
|
||||||
placeholder="邮箱验证码"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div v-if="!isWaitingForEmailVerified" class="signup-page-button-primary" @click="verifyCode">
|
<div v-if="!isWaitingForEmailVerified" class="signup-page-button-primary" @click="verifyCode">
|
||||||
<div class="signup-page-button-text">注册</div>
|
<div class="signup-page-button-text">注册</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -102,8 +83,10 @@
|
|||||||
<script>
|
<script>
|
||||||
import { API_BASE_URL, toast } from '../main'
|
import { API_BASE_URL, toast } from '../main'
|
||||||
import { googleSignIn } from '../utils/google'
|
import { googleSignIn } from '../utils/google'
|
||||||
|
import BaseInput from '../components/BaseInput.vue'
|
||||||
export default {
|
export default {
|
||||||
name: 'SignupPageView',
|
name: 'SignupPageView',
|
||||||
|
components: { BaseInput },
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user