This commit is contained in:
aaron 2025-04-16 22:45:07 +08:00
parent 50f7272230
commit 613aca84cb
7 changed files with 171 additions and 5 deletions

View File

@ -8,7 +8,11 @@
"build": "vite build",
"serve": "vite preview"
},
"keywords": ["vue", "meida", "fashion"],
"keywords": [
"vue",
"meida",
"fashion"
],
"author": "",
"license": "ISC",
"dependencies": {
@ -16,6 +20,7 @@
"axios": "^1.8.4",
"vite": "^6.3.0",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
"vue-router": "^4.5.0",
"vuex": "^4.1.0"
}
}

View File

@ -1,9 +1,11 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './assets/main.css'
const app = createApp(App)
app.use(router)
app.use(store)
app.mount('#app')

View File

@ -1,5 +1,6 @@
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import store from '../store'
const routes = [
{
@ -7,10 +8,23 @@ const routes = [
name: 'home',
component: Home
},
{
path: '/password',
name: 'password',
component: () => import('../views/PasswordProtection.vue')
},
{
path: '/tryon-history',
name: 'tryon-history',
component: () => import('../views/TryonHistory.vue')
component: () => import('../views/TryonHistory.vue'),
meta: { requiresAuth: true },
beforeEnter: (to, from, next) => {
if (store.getters.isAuthenticated) {
next()
} else {
next('/password')
}
}
}
]

30
src/store/index.js Normal file
View File

@ -0,0 +1,30 @@
import { createStore } from 'vuex'
const store = createStore({
state: {
isAuthenticated: false
},
mutations: {
setAuthenticated(state, value) {
state.isAuthenticated = value
}
},
actions: {
authenticate({ commit }, password) {
return new Promise((resolve, reject) => {
if (password === '147258') {
commit('setAuthenticated', true)
resolve(true)
} else {
commit('setAuthenticated', false)
reject(new Error('密码错误'))
}
})
}
},
getters: {
isAuthenticated: state => state.isAuthenticated
}
})
export default store

View File

@ -3,7 +3,7 @@
<div class="hero">
<h1>欢迎使用美搭</h1>
<p>您的个人时尚穿搭助手</p>
<router-link to="/tryon-history" class="btn btn-primary">查看穿搭历史</router-link>
<router-link to="/password" class="btn btn-primary">查看穿搭历史</router-link>
</div>
<div class="features mt-20">

View File

@ -0,0 +1,115 @@
<template>
<div class="password-protection">
<div class="password-form card">
<h2>访问受限</h2>
<p>请输入密码查看穿搭历史</p>
<div class="input-group">
<input
type="password"
v-model="password"
placeholder="请输入密码"
@keyup.enter="validatePassword"
/>
<button
class="btn btn-primary"
@click="validatePassword"
:disabled="loading"
>
{{ loading ? '验证中...' : '确认' }}
</button>
</div>
<div v-if="errorMessage" class="error-message">
{{ errorMessage }}
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
const router = useRouter()
const store = useStore()
const password = ref('')
const errorMessage = ref('')
const loading = ref(false)
const validatePassword = async () => {
if (!password.value) {
errorMessage.value = '请输入密码'
return
}
loading.value = true
errorMessage.value = ''
try {
await store.dispatch('authenticate', password.value)
router.push('/tryon-history')
} catch (error) {
errorMessage.value = error.message
} finally {
loading.value = false
}
}
</script>
<style scoped>
.password-protection {
display: flex;
justify-content: center;
align-items: center;
min-height: 70vh;
padding: 20px;
}
.password-form {
width: 100%;
max-width: 400px;
padding: 30px;
text-align: center;
}
.password-form h2 {
margin-bottom: 10px;
color: #fe2c55;
}
.password-form p {
margin-bottom: 25px;
color: #bbb;
}
.input-group {
display: flex;
margin-bottom: 15px;
}
.input-group input {
flex: 1;
padding: 10px 15px;
border: 1px solid #333;
border-radius: 4px 0 0 4px;
background-color: #1a1a1a;
color: #f0f0f0;
}
.input-group input:focus {
outline: none;
border-color: #fe2c55;
}
.input-group button {
border-radius: 0 4px 4px 0;
}
.error-message {
color: #fe2c55;
margin-top: 10px;
}
</style>