change HomeView.vue

add LoginView.vue
add db.json
change App.vue
This commit is contained in:
LingandRX 2024-11-03 23:08:31 +08:00
parent ca0cac99c8
commit 2cc365f2cc
8 changed files with 271 additions and 122 deletions

10
db.json Normal file
View File

@ -0,0 +1,10 @@
{
"users": [
{
"id": 1,
"username": "test",
"password": "test",
"token": "123456"
}
]
}

View File

@ -24,7 +24,7 @@
"eslint": "^8.57.0",
"eslint-plugin-vue": "^9.23.0",
"prettier": "^3.2.5",
"vite": "^5.3.1",
"vite": "5.4.6",
"vite-plugin-vue-devtools": "^7.3.1"
}
}

View File

@ -1,28 +1,5 @@
<script setup lang="js">
import { RouterView } from 'vue-router'
import { h, ref } from 'vue'
import { AppstoreOutlined, MailOutlined } from '@ant-design/icons-vue'
import CustomCard from '@/components/CustomCard.vue'
const current = ref(['mail'])
const items = ref([
{
key: 'home',
icon: () => h(MailOutlined),
label: h('a', { href: '/' }, '首页'),
title: '首页'
},
{
key: 'about',
icon: () => h(AppstoreOutlined),
label: h('a', { href: '/about' }, '关于'),
title: '关于'
}
])
</script>
import CustomCard from '@/components/CustomCard.vue'
<template>
<header>
<header v-if="isLogin">
<div class="wrapper">
<nav>
<a-menu v-model:selectedKeys="current" mode="horizontal" :items="items" />
@ -35,17 +12,54 @@ import CustomCard from '@/components/CustomCard.vue'
<RouterView />
</article>
<aside>
<CustomCard :style="{margin: 'auto', padding: '10px'}" />
<CustomCard :style="{ margin: 'auto' }" />
</aside>
</main>
</template>
<script setup lang="js">
import { RouterView } from 'vue-router'
import { h, ref, onMounted } from 'vue'
import { AppstoreOutlined } from '@ant-design/icons-vue'
import CustomCard from '@/components/CustomCard.vue'
const isLogin = ref(false)
const current = ref(['mail'])
const items = ref([
{
key: 'home',
icon: () => h(AppstoreOutlined),
label: h('a', { href: '/' }, '首页'),
title: '首页'
},
{
key: 'about',
icon: () => h(AppstoreOutlined),
label: h('a', { href: '/about' }, '关于'),
title: '关于'
}
])
onMounted(() => {
isLogin.value = !!localStorage.getItem('token')
})
</script>
<style scoped>
article {
flex: 5;
border: 2px solid #ccc;
border-radius: 8px;
padding: 16px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
aside {
flex: 2;
display: flex;
border: 2px solid #ccc;
border-radius: 8px;
padding: 16px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
</style>

View File

@ -4,16 +4,15 @@ import { CustomCard } from '@/components/CustomCard.js'
const customCard = ref(new CustomCard())
</script>
<template>
<div class="card">
<div class="avatar">
<img :src="customCard.avatar" alt="avatar" height="100px" width="100px" />
<img :src="customCard.avatar" alt="avatar" />
</div>
<div class="name">
<h2>{{ customCard.name }}</h2>
</div>
<div class="content">
<p>{{ customCard.content }}</p>
</div>
@ -24,35 +23,29 @@ const customCard = ref(new CustomCard())
.card {
display: flex;
flex-direction: column;
text-align: center;
justify-content: center;
align-items: center;
text-align: center;
height: 100%;
width: 100%;
box-sizing: border-box;
}
.avatar {
width: 50%;
img {
border-radius: 50%;
}
}
.name {
width: 50%;
}
.content {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
.avatar img {
border-radius: 50%;
}
@media screen and (max-width: 800px) {
.card {
width: 160px;
}
}
@media screen and (min-width: 800px) {
.card {
width: 200px;
}
.name,
.content {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@ -1,9 +1,7 @@
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
const routes = [
{
path: '/',
name: 'home',
@ -12,12 +10,33 @@ const router = createRouter({
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue')
},
{
path: '/login',
name: 'login',
component: () => import('../views/LoginView.vue'),
meta: {
requiresAuth: false
}
}
]
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: routes
})
router.beforeEach((to, from, next) => {
console.log('beforeEach', to, from)
const token = localStorage.getItem('token')
if (to.matched.some((record) => record.meta.requiresAuth === false)) {
next()
} else if (!token) {
next({ name: 'login' })
} else {
next()
}
})
export default router

View File

@ -3,68 +3,58 @@ const cardStyle = {
width: '200px'
}
const moduleList_1 = [
{
title: 'Gitea',
url: 'http://10.120.20.137:3000/'
},
{
title: 'Pve',
url: 'https://10.120.20.15:8006/'
class Module {
/**
* 模块
* @param {string} title
* @param {string} url
*/
constructor(title, url) {
this.title = title
this.url = url
}
}
]
const moduleList_2 = [
{
title: 'zero tier',
url: 'https://zerotier.yulinling.asia/'
},
{
title: 'gitea',
url: 'https://gitea.yulinling.asia/'
class ManagerModule {
/**
* 模块管理
* @param {string} name
* @param {Module[]} moduleList
*/
constructor(name, moduleList) {
this.name = name
this.moduleList = moduleList
}
}
const manageModuleList = [
new ManagerModule('Zero tier', [
new Module('Pve', 'https://10.18.80.15:8006/'),
new Module('fnOS', 'http://10.18.80.124:8000/')
]),
new ManagerModule('云服务器', [
new Module('zero tier', 'https://zerotier.yulinling.asia/'),
new Module('gitea', 'https://gitea.yulinling.asia/')
])
]
</script>
<template>
<div>
<h2>Zero tier</h2>
<div v-for="(item, itemIndex) in manageModuleList" :key="itemIndex">
<h2>{{ item.name }}</h2>
<ul>
<a-card
v-for="(item, index) in moduleList_1"
:key="index"
:style="cardStyle"
:body-style="{ padding: 0, overflow: 'hidden' }"
>
<div v-for="(module, index) in item.moduleList" :key="index">
<a-card :style="cardStyle">
<a-flex justify="space-between">
<a-flex vertical align="flex-end" justify="space-between" :style="{ padding: '32px' }">
<a-typography>
<a-typography-title :level="3"> {{ item.title }}</a-typography-title>
<a-typography-title :level="3"> {{ module.title }}</a-typography-title>
</a-typography>
<a-button type="primary" :href="item.url" target="_blank">Get Start</a-button>
<a-button type="primary" :href="module.url" target="_blank">Get Start</a-button>
</a-flex>
</a-flex>
</a-card>
</ul>
</div>
<div>
<h2>aliyun</h2>
<ul>
<a-card
v-for="(item, index) in moduleList_2"
:key="index"
:style="cardStyle"
:body-style="{ padding: 0, overflow: 'hidden' }"
>
<a-flex justify="space-between">
<a-flex vertical align="flex-end" justify="space-between" :style="{ padding: '32px' }">
<a-typography>
<a-typography-title :level="3"> {{ item.title }}</a-typography-title>
</a-typography>
<a-button type="primary" :href="item.url" target="_blank">Get Start</a-button>
</a-flex>
</a-flex>
</a-card>
</ul>
</div>
</template>

123
src/views/LoginView.vue Normal file
View File

@ -0,0 +1,123 @@
<template>
<div class="login-container">
<div v-if="!isLogin">
<a-form
:model="formState"
name="basic"
:label-col="labelCol"
:wrapper-col="wrapperCol"
autocomplete="off"
@finish="onFinish"
@finishFailed="onFinishFailed"
>
<a-form-item
label="Username"
name="username"
:rules="[{ required: true, message: 'Please input your username!' }]"
>
<a-input v-model:value="formState.username">
<template #prefix>
<UserOutlined class="site-form-item-icon" />
</template>
</a-input>
</a-form-item>
<a-form-item
label="Password"
name="password"
:rules="[{ required: true, message: 'Please input your password!' }]"
>
<a-input-password v-model:value="formState.password">
<template #prefix>
<LockOutlined class="site-form-item-icon" />
</template>
</a-input-password>
</a-form-item>
<a-form-item name="remember" :wrapper-col="{ offset: 8, span: 16 }">
<a-checkbox v-model:checked="formState.remember">Remember me</a-checkbox>
</a-form-item>
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
<a-button type="primary" html-type="submit">Submit</a-button>
</a-form-item>
</a-form>
</div>
<div v-else>
<p>Welcome, you are logged in!</p>
</div>
</div>
</template>
<script setup>
import { reactive, ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { UserOutlined, LockOutlined } from '@ant-design/icons-vue'
const formState = reactive({
username: '',
password: '',
remember: true
})
const labelCol = {
style: {
width: '200px'
}
}
const wrapperCol = {
span: 12
}
const isLogin = ref(false)
const router = useRouter()
onMounted(() => {
isLogin.value = !!localStorage.getItem('token')
})
const removeLoginRoute = () => {
const loginRoute = router.getRoutes().find((route) => route.name === 'login')
console.log('loginRoute:', loginRoute)
if (loginRoute) {
console.log(loginRoute.name)
router.removeRoute(loginRoute.name)
}
}
const onFinish = async (values) => {
try {
const response = await fetch('http://localhost:3000/users', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
const users = await response.json()
const user = users.find(
(user) => user.username === values.username && user.password === values.password
)
if (user && user.token) {
localStorage.setItem('token', user.token)
isLogin.value = true
removeLoginRoute()
await router.push('/')
} else {
alert('Invalid username or password')
}
} catch (error) {
console.error('Error:', error)
}
}
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo)
}
</script>
<style scoped>
.login-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
</style>