超詳細的前后端實戰項目(Spring系列加上vue3)(一步步實現+源碼)前端篇(一)

最近想著一步步搭建一個前后端項目,將每一步詳細的做出來。(如果有不足或者建議,也希望大佬們指出哦)

前端初始化

1.根據vue腳手架創建vue項目

這里可以用很多方法創建vue項目,大家看著創建吧,只要能創建出來就可以了(用的vue3)

這里我加了router和vuex,emm,就不用ts了(感覺還是js用的更好一點)

OK,項目創建完畢,即可開始寫前端代碼了。

2.安裝依賴:

? ? ? ? 1.挑一個自己喜歡的組件庫引用一手,這里我選用arco?design(也沒必要只用Element-ui,可以試試其他的)

npm install --save-dev @arco-design/web-vue

OKOK,開始下載

安裝好了之后,我們還要安裝一些必要的依賴,比如axios,sass這些,emmmm,目前就安裝這幾個吧。

npm install axios

npm install sass -D

然后根據組件庫官網的操作進行引入

import { createApp } from 'vue'
import ArcoVue from '@arco-design/web-vue';
import App from './App.vue';
import '@arco-design/web-vue/dist/arco.css';const app = createApp(App);
app.use(ArcoVue);
app.mount('#app');

改造一下main.js中的代碼

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ArcoVue from '@arco-design/web-vue';
import '@arco-design/web-vue/dist/arco.css';createApp(App).use(store).use(ArcoVue).use(router).mount('#app')

注意注意引入組件庫之后,可以找個組件看看能不能使用該組件庫,以防萬一;

吶,這里我們就去官網看看

很好,就是你了,表單布局,我們點擊其下方代碼,將其放入App.vue試試能不能用

好嘞,隨機找一串串代碼放到App.vue中試試(不要試著從圖片中照抄我的哈,自己去官網找一個試試Arco Design Vue)咱,主打一個快速。

嚯,出現了!那么組件庫是能用噠!

好,第二步也就完成了。

這里我建議每做幾步就開啟調試一下。

3.調整目錄,初始化項目:

? ? ? ? 1.清空components(把HelloWorld.vue刪了)

? ? ? ? 2.新建目錄api,utils,views

? ? ? ? 3.把App.vue中用不到的代碼都給刪了,留一個? <router-view />就好了,然后,把router的index.js的代碼全給注釋了(emmm,直接刪了感覺太麻煩,注釋了后面寫起來好做參照)

目前的目錄結構就是這樣了;

(App.vue就變成這樣啦)

4.定義請求工具:

大家不要一開始寫前端就直奔優美的”可跳轉“頁面,這邊先整體規定一個向后端發請求的工具request.js

? ? ? ? 在utils目錄下創建request.js文件:

import axios from 'axios';//導入axios
//定義前綴
const URL = 'http://localhost:8080';
const instance = axios.create({URL})//來一個響應攔截器
instance.interceptors.response.use(result=>{if(result.data.code == 0){return result.data;}//如果不為0則表示失敗alert(result.data.message||'出現錯誤')return Promise.reject(err);//異步的狀態轉化成失敗的狀態},err=>{alert('服務異常');return Promise.reject(err);//異步的狀態轉化成失敗的狀態}
)
export default instance;

解釋一下這段代碼:引入發請求的axios,然后定義一個開發時期的請求必有前綴(這里直接寫http://localhost:8080其實是有問題的,對于后面出現的跨域問題....emmmm,其實前端后端都有方式解決,看情況吧,后面再講),做一個相應攔截器,對結果進行判斷,然后導出

(這里需要提示一下統一相應有哪些(不然可能會不太好理解))

后端那一塊統一相應數據:code:0表示成功,1表示失敗;msg: 提示信息;data:數據

前端代碼編寫開始!!

OK啊各位,初始化完成我們便要開始編寫代碼了。

首先,我們先明確一波步驟:

????????確定布局? --> 填充各部分代碼 -->? 后端插入

(如果涉及與后端的交互,遵循以下步驟

????????1.搭建頁面(html,css)-> 2.綁定數據與事件?-> 3.調用后端接口)

好嘞,熟悉了嗎各位,不熟練也沒事,我們先找一個練練手。

先從組件庫這里找到布局:

這布局,看似普通,實則帥的雅痞,就它了。引入代碼

回到目錄component下,創建一個新的文件GlobalPage.vue

(眾所周知的小技巧:<vue>可快速構建vue文件)

<template><a-layout style="height: 400px;"><a-layout-header>Header</a-layout-header><a-layout><a-layout-sider theme="dark">Sider</a-layout-sider><a-layout-content>Content</a-layout-content></a-layout><a-layout-footer>Footer</a-layout-footer></a-layout>
</template><script>
export default {}
</script><style scoped>
.layout-demo :deep(.arco-layout-header),
.layout-demo :deep(.arco-layout-footer),
.layout-demo :deep(.arco-layout-sider-children),
.layout-demo :deep(.arco-layout-content) {display: flex;flex-direction: column;justify-content: center;color: var(--color-white);font-size: 16px;font-stretch: condensed;text-align: center;
}.layout-demo :deep(.arco-layout-header),
.layout-demo :deep(.arco-layout-footer) {height: 64px;background-color: var(--color-primary-light-4);
}.layout-demo :deep(.arco-layout-sider) {width: 206px;background-color: var(--color-primary-light-3);
}.layout-demo :deep(.arco-layout-content) {background-color: rgb(var(--arcoblue-6));
}
</style>

然后我們改一下index.js文件(將GlobalPage引入)(要是看著那些注解感覺不美觀的話就直接刪了)

import { createRouter, createWebHashHistory } from 'vue-router'
import MainPage from '../components/GlobalPage.vue'const routes = [// {//   path: '/',//   name: 'home',//   component: HomeView// },// {//   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(/* webpackChunkName: "about" */ '../views/AboutView.vue')// }{path: '/',name: 'mainPage',component: MainPage}
]const router = createRouter({history: createWebHashHistory(),routes
})export default router

運行一手看看(記得每次有一些大改就要看看情況,改多再看就會很麻煩,bug也不好排查)

運行出來就是這么個界面

搭建Header和Footer代碼

先把頭部和尾部的代碼解決了。

先來尾部吧,我覺得這個比較簡單(哈)

一般來說,網站的底部都是一些網站運營的介紹什么的(大多都是哈,也不一定全是)

這里我就以大家都愛的4399為例(大多都是哈,也不一定全是,哈哈哈)

emmm,這里我們可以把Footer這一塊封裝為一個組件,因為雖然定義的GlobalPage屬于是全局的頁面,但萬一之后會跳轉到其他的頁面呢,這里我覺得封裝成組件更好一些(看起來也更輕巧一些)

在views目錄下創建GlobalFooter.vue

<template><div id="header">由Heath ceTide 創作的網站 XXX 有限公司</div>
</template><script>
export default {};
</script><style>
#header {font-family: "Satisfy", cursive;font-size: 16px;color: rgb(102, 149, 164);margin-right: 20px;
}
</style>

差不多了,將其替換掉GlobalPage的header

<template><a-layout style="height: 400px;"><a-layout-header>Header</a-layout-header><a-layout><a-layout-sider theme="dark">Sider</a-layout-sider><a-layout-content>Content</a-layout-content></a-layout><a-layout-footer><global-footer></global-footer></a-layout-footer></a-layout>
</template>
<script>
import GlobalFooter from '../views/GlobalFooter.vue'
export default {components: { GlobalFooter },}
</script>

展示出了樣式就行了,(之后可以美化以下),好,然后我們開始整Header,

這一塊導航欄,我覺得吧,還是用組件比較合適(倒不是不會整,畢竟也麻煩,不用自己手敲的絕不自己手敲代碼)

就選這個深色模式導航啦,新建GlobalHeader.vue文件把代碼用CV大法放進去

<template><div class="menu-demo"><a-menu mode="horizontal" theme="dark" :default-selected-keys="['1']"><a-menu-item key="0" :style="{ padding: 0, marginRight: '38px' }" disabled><div:style="{width: '80px',height: '30px',background: 'var(--color-fill-3)',cursor: 'text',}"/></a-menu-item><a-menu-item key="1">Home</a-menu-item><a-menu-item key="2">Solution</a-menu-item><a-menu-item key="3">Cloud Service</a-menu-item><a-menu-item key="4">Cooperation</a-menu-item></a-menu></div>
</template>
<style scoped>
.menu-demo {box-sizing: border-box;width: 100%;background-color: var(--color-neutral-2);
}
</style>
<template><a-layout style="height: 400px"><a-layout-header><global-header></global-header></a-layout-header><a-layout><a-layout-sider theme="dark">Sider</a-layout-sider><a-layout-content>Content</a-layout-content></a-layout><a-layout-footer><global-footer class="footer"></global-footer></a-layout-footer></a-layout>
</template><script>
import GlobalFooter from "../views/GlobalFooter.vue";
import GlobalHeader from "../views/GlobalHeader.vue";
export default {components: { GlobalFooter, GlobalHeader },
};
</script><style scoped>
.layout-demo :deep(.arco-layout-header),
.layout-demo :deep(.arco-layout-footer),
.layout-demo :deep(.arco-layout-sider-children),
.layout-demo :deep(.arco-layout-content) {display: flex;flex-direction: column;justify-content: center;color: var(--color-white);font-size: 16px;font-stretch: condensed;text-align: center;
}.layout-demo :deep(.arco-layout-header),
.layout-demo :deep(.arco-layout-footer) {height: 64px;background-color: var(--color-primary-light-4);
}.layout-demo :deep(.arco-layout-sider) {width: 206px;background-color: var(--color-primary-light-3);
}.layout-demo :deep(.arco-layout-content) {background-color: rgb(var(--arcoblue-6));
}.footer {background-color: aliceblue;padding: 16px;position: sticky;bottom: 0;left: 0;right: 0;text-align: center;letter-spacing: 3px;
}
</style>

其實這個空的區域可以加一個logo或者其他什么的

吶,就像這樣

OK,目前的頁面就是這個樣子了;

搭建搜索頁面

好的,下面整一個全局的搜索框頁面吧

創建一個SearchPage.vue 文件

從組件庫中找到一個合適的搜索框

<!--SearchPage.vue代碼-->
<template><a-space direction="vertical" size="large"><a-input-search:style="{ width: '320px' }"placeholder="Please enter something"button-text="Search"search-button/></a-space>
</template><script>
export default {};
</script><style>
</style>

將新組件放到GlobalPage中(暫時的,之后還是要處理路由的)

<template><a-layout style="height: 400px"><a-layout-header><global-header></global-header></a-layout-header><a-layout><a-layout-sider theme="dark">Sider</a-layout-sider><a-layout-content><search-page></search-page></a-layout-content></a-layout><a-layout-footer><global-footer class="footer"></global-footer></a-layout-footer></a-layout>
</template><script>
import GlobalFooter from "../views/GlobalFooter.vue";
import GlobalHeader from "../views/GlobalHeader.vue";
import SearchPage from '../views/SearchPage.vue';
export default {components: { GlobalFooter, GlobalHeader, SearchPage },
};
</script>

現在搜索框就出來了,(這里最右邊加了一點點小細節(不重要)之后會把代碼發出來)

這里其實可以加一個搜索框等待效果

<template><div id="search"><a-space direction="vertical" size="large"><a-input-search:style="{ width: '320px' }"placeholder="Please enter something"button-text="Search"search-button/></a-space><a-space direction="vertical" size="large"><a-input-search:style="{ width: '320px' }"placeholder="Please enter something"button-text="Search"search-buttonloading/></a-space></div>
</template><script>
export default {};
</script><style>
</style>

這里其實是復制了兩個搜索框代碼,不過另外一個加上了loading屬性,這樣就能表現出等待中的效果

吶吶吶,就是這樣,然后我們再用v-if定義一下它的顯示和隱藏就可以了

ok,演示一下:

先設置script 為setup,然后引入ref

<script setup>
import {ref} from 'vue';

?還記得之前說的三部曲嗎:?1.搭建頁面(html,css)-> 2.綁定數據與實踐?-> 3.調用后端接口)

那我們以及寫好了搜索框了,也該進行數據綁定了(這里暫時不進行后端接口調用,(畢竟還沒開發))

對于一個組件的顯示和隱藏我們直接寫

<script setup>
import {ref} from 'vue';
const isSearch = ref(false)

然后呢,兩個搜索框綁定該屬性(一個是isSearch,一個是!isSearch),這樣就能保持一個顯示,一個隱藏了。那么,如何實現轉換呢,平時在搜索的時候,當我們點擊了搜索按鈕的時候就會出現轉換效果,如果網比較好的話,估計也就直接跳轉了,(如果網不好的話,搜索框轉圈轉了一會兒就會恢復原樣)

那么,我們定義一個函數,在正常情況下點擊搜索的時候觸發,轉換為搜索中的樣式,然后過幾秒就恢復原狀。

那么開始定義吧;

<script setup>
import {ref} from 'vue';
const isSearch = ref(false)const onSearch = () => {isSearch.value = true;setTimeout(() => {isSearch.value = false;}, 5000);
};
</script>

這里定義了一個函數,點擊之后,isSearch改變,等到五秒之后,值恢復。來試試吧

<template><div id="search"><a-space direction="vertical" size="large" v-if="!isSearch"><a-input-search:style="{ width: '320px' }"placeholder="Please enter something"button-text="Search"search-button@click=onSearch/></a-space><a-space direction="vertical" size="large" v-if="isSearch"><a-input-search:style="{ width: '320px' }"placeholder="Please enter something"button-text="Search"search-buttonloading/></a-space></div>
</template><script setup>
import {ref} from 'vue';
const isSearch = ref(false)const onSearch = () => {isSearch.value = true;setTimeout(() => {isSearch.value = false;}, 5000);
};
</script>

成功了,那再來完善一下

有搜索框,當然要有顯示搜索內容的區域,

再去組件庫看看有沒有什么合適的

就用這個吧,

<template><div id="searchPage"><div class="search"><a-space direction="vertical" size="large" v-if="!isSearch"><a-input-search:style="{ width: '620px' }"placeholder="Please enter something"button-text="Search"search-button@click="onSearch"/></a-space><a-space direction="vertical" size="large" v-if="isSearch"><a-input-search:style="{ width: '620px' }"placeholder="Please enter something"button-text="Search"search-buttonloading/></a-space></div><a-space direction="vertical" size="large"><a-radio-group v-model="type" type="button"><a-radio value="line">Line</a-radio><a-radio value="card">Card</a-radio><a-radio value="card-gutter">Card Gutter</a-radio><a-radio value="text">Text</a-radio><a-radio value="rounded">Rounded</a-radio><a-radio value="capsule">Capsule</a-radio></a-radio-group><a-radio-group v-model="size" type="button"><a-radio value="mini">Mini</a-radio><a-radio value="small">Small</a-radio><a-radio value="medium">Medium</a-radio><a-radio value="large">Large</a-radio></a-radio-group><a-tabs :type="type" :size="size"><a-tab-pane key="1" title="Tab 1"> Content of Tab Panel 1 </a-tab-pane><a-tab-pane key="2" title="Tab 2"> Content of Tab Panel 2 </a-tab-pane><a-tab-pane key="3" title="Tab 3"> Content of Tab Panel 3 </a-tab-pane></a-tabs></a-space></div>
</template><script setup>
import { ref } from "vue";
const isSearch = ref(false);const onSearch = () => {isSearch.value = true;setTimeout(() => {isSearch.value = false;}, 5000);
};
</script><style scoped>
.search{margin: 30px auto;}
</style>

添加代碼并做一些優化

目前就是這個樣子了。

emmm,感覺不太對,好像有點快了,要不還是做一個登錄注冊頁面吧。。。

搭建登錄注冊頁面

創建LoginPage.vue

這一塊我覺得還挺容易的,先給個示例代碼吧

<template><div class="container"><div class="login-box"><!-- Login Form --><form action=""><h2>Login</h2><div class="input-box"><span class="icon"><i class="fa-solid fa-envelope"></i></span><input type="email" required placeholder="Email" /></div><div class="input-box"><span class="icon"><i class="fa-solid fa-lock"></i></span><input type="password" placeholder="Password" required /></div><div class="remember-forget"><label><input type="checkbox" />Remember Me</label><a href="#">Forgot Password?</a></div><button type="submit">Login</button><div class="register-link"><a href="#">Don't have an account? Create Now!</a></div></form></div></div>
</template><script>
export default {}
</script><style scoped>
* {margin: 0;padding: 0;box-sizing: border-box;font-family: "Poppins", sans-serif;
}
.container {width: 100%;height: 100vh;background: url("../assets/background.jpg") no-repeat;background-size: cover;background-position: center;display: flex;justify-content: center;align-items: center;
}.container .login-box {position: relative;width: 390px;height: 420px;background-color: transparent;border: 2px solid rgba(255, 255, 255, 0.5);border-radius: 20px;display: flex;justify-content: center;align-items: center;backdrop-filter: blur(15px);
}.login-box h2 {font-size: 28px;color: #fff;text-align: center;
}.login-box .input-box {position: relative;width: 310px;margin: 30px 0;border-bottom: 2px solid #fff;
}.input-box input {width: 100%;height: 50px;background: transparent;border: none;outline: none;font-size: 16px;color: #fff;padding: 0 35px 0 5px;
}.input-box input::placeholder {color: #f9f9f9;
}.input-box .icon {position: absolute;right: 8px;color: #fff;font-size: 18px;line-height: 50px;
}.login-box .remember-forget {margin: -15px 0 15px;font-size: 15px;color: #fff;display: flex;justify-content: space-between;
}.remember-forget label input {margin-right: 3px;
}.login-box button {width: 100%;height: 40px;background: #fff;border: none;outline: none;border-radius: 40px;cursor: pointer;font-size: 16px;color: #000;transition: all 0.5s;
}.login-box button:hover {background: #1f73c9;color: #fff;
}.login-box .register-link {font-size: 15px;color: #fff;text-align: center;margin: 20px 0 10px;
}.remember-forget a,
.register-link a {color: #fff;text-decoration: none;
}.remember-forget a:hover,
.register-link a:hover {text-decoration: underline;
}/* Responsive Design */
@media (max-width: 460px) {.container .login-box {width: 350px;}.login-box .input-box {width: 290px;}
}@media (max-width: 360px) {.container .login-box {width: 100%;height: 100vh;border: none;}
}
</style>

這一塊代碼是之前找到的一個登錄頁代碼,我覺著就還挺好

不過吧,我決定還是不用這個代碼,去組件庫上找找吧(主要還是要加深一下組件庫的使用)

好了就用這個

小改一下吧;

<template><div class="container"><div class="login-box"><a-form:model="form":style="{ width: '600px' }"@submit="handleSubmit"class="input-box"><h2 style="margin-bottom: 60px">Login</h2><a-form-itemfield="name"tooltip="Please enter username"label="賬號"class="element"><a-inputv-model="form.name"placeholder="please enter your username..."style="margin-right: 40px"/></a-form-item><a-form-item field="post" label="密碼" class="element"><a-inputv-model="form.post"placeholder="please enter your password..."style="margin-right: 40px"/></a-form-item><a-form-item field="isRead"><a-checkbox v-model="form.isRead">I have read the manual</a-checkbox></a-form-item><a-form-item><a-button html-type="submit" class="input-box">Submit</a-button></a-form-item><div class="register-link"><a href="#">Don't have an account? Create Now!</a></div></a-form></div></div>
</template><script></script><style scoped>
* {margin: 0;padding: 0;box-sizing: border-box;font-family: "Poppins", sans-serif;display: flex;justify-content: center;
}
.container {width: 100%;height: 100vh;background: url("../assets/background.jpg") no-repeat;background-size: cover;background-position: center;display: flex;justify-content: center;align-items: center;
}.container .login-box {position: relative;width: 500px;height: 580px;background-color: transparent;border: 2px solid rgba(255, 255, 255, 0.5);border-radius: 20px;display: flex;justify-content: center;align-items: center;backdrop-filter: blur(15px);
}.login-box h2 {font-size: 28px;color: #fff;text-align: center;
}.login-box .input-box {position: relative;width: 310px;margin: 30px 0;
}.input-box input {width: 80%;height: 60px;background: transparent;border: none;outline: none;font-size: 16px;color: #fff;padding: 0 2px 0 5px;
}.input-box input::placeholder {color: #f9f9f9;
}.input-box .icon {position: absolute;right: 8px;color: #fff;font-size: 16px;line-height: 25px;
}.login-box .remember-forget {margin: -15px 0 15px;font-size: 15px;color: #fff;display: flex;justify-content: space-between;
}.remember-forget label input {margin-right: 30px;
}.login-box button {width: 100%;height: 40px;background: #fff;border: none;outline: none;border-radius: 40px;cursor: pointer;font-size: 16px;color: #000;transition: all 0.5s;
}.login-box button:hover {background: #1f73c9;color: #fff;
}.login-box .register-link {font-size: 15px;color: #fff;text-align: center;margin: 5px 0 5px;
}.remember-forget a,
.register-link a {color: #fff;text-decoration: none;
}.remember-forget a:hover,
.register-link a:hover {text-decoration: underline;
}/* Responsive Design */
@media (max-width: 460px) {.container .login-box {width: 350px;}.login-box .input-box {width: 290px;}
}@media (max-width: 360px) {.container .login-box {width: 100%;height: 100vh;border: none;}
}
.element {margin: 20px 0;
}
</style>

emmm,這代碼可謂一坨(發出來也需要些勇氣啊。。)

大家如果有自己的寫的規范代碼就用自己的(hhh)

那么,我們開始前面的三部曲:搭建頁面以及完成,可以開始綁定數據與事件了。

<script setup>
import {ref} from 'vue';
const loginData = ref({username: '',password: '',isRead: false,
})

先引入ref,然后創建loginData,然后v-model綁定數據

<template><div class="container"><div class="login-box"><a-form:model="loginData":style="{ width: '600px' }"@submit="handleSubmit"class="input-box"><h2 style="margin-bottom: 60px">Login</h2><a-form-itemfield="name"tooltip="Please enter username"label="賬號"class="element":rules= "rules"><a-inputv-model="loginData.username"placeholder="please enter your username..."style="margin-right: 40px"/></a-form-item><a-form-item field="post" label="密碼" class="element" :rules= "rules"><a-inputv-model="loginData.password"placeholder="please enter your password..."style="margin-right: 40px"/></a-form-item><a-form-item field="isRead"><a-checkbox v-model="loginData.isRead">I have read the manual</a-checkbox></a-form-item><a-form-item><a-button html-type="submit" class="input-box">Submit</a-button></a-form-item><div class="register-link"><a href="#">Don't have an account? Create Now!</a></div></a-form></div></div>
</template>
<script setup>
import {ref} from 'vue';
const loginData = ref({username: '',password: '',isRead: false,
})
const rules = [{validator: (value, cb) => {return new Promise(resolve => {window.setTimeout(() => {if (value !== ' ') {cb('content not empty')}resolve()}, 1000)})}}];</script><style scoped>
* {margin: 0;padding: 0;box-sizing: border-box;font-family: "Poppins", sans-serif;display: flex;justify-content: center;
}
.container {width: 100%;height: 100vh;background: url("../assets/background.jpg") no-repeat;background-size: cover;background-position: center;display: flex;justify-content: center;align-items: center;
}.container .login-box {position: relative;width: 500px;height: 580px;background-color: transparent;border: 2px solid rgba(255, 255, 255, 0.5);border-radius: 20px;display: flex;justify-content: center;align-items: center;backdrop-filter: blur(15px);
}.login-box h2 {font-size: 28px;color: #fff;text-align: center;
}.login-box .input-box {position: relative;width: 310px;margin: 30px 0;
}.input-box input {width: 80%;height: 60px;background: transparent;border: none;outline: none;font-size: 16px;color: #fff;padding: 0 2px 0 5px;
}.input-box input::placeholder {color: #f9f9f9;
}.input-box .icon {position: absolute;right: 8px;color: #fff;font-size: 16px;line-height: 25px;
}.login-box .remember-forget {margin: -15px 0 15px;font-size: 15px;color: #fff;display: flex;justify-content: space-between;
}.remember-forget label input {margin-right: 30px;
}.login-box button {width: 100%;height: 40px;background: #fff;border: none;outline: none;border-radius: 40px;cursor: pointer;font-size: 16px;color: #000;transition: all 0.5s;
}.login-box button:hover {background: #1f73c9;color: #fff;
}.login-box .register-link {font-size: 15px;color: #fff;text-align: center;margin: 5px 0 5px;
}.remember-forget a,
.register-link a {color: #fff;text-decoration: none;
}.remember-forget a:hover,
.register-link a:hover {text-decoration: underline;
}/* Responsive Design */
@media (max-width: 460px) {.container .login-box {width: 350px;}.login-box .input-box {width: 290px;}
}@media (max-width: 360px) {.container .login-box {width: 100%;height: 100vh;border: none;}
}
.element {margin: 20px 0;
}
</style>

這里可以添加一個校驗規則,

在組件庫這里找一下表單的校驗規則

<script setup>
import {ref} from 'vue';
const loginData = ref({username: '',password: '',isRead: false,
})
const rules = [{validator: (value, cb) => {return new Promise(resolve => {window.setTimeout(() => {if (value === ' ') {cb('content not empty')}resolve()}, 1000)})}}];</script>

隨便定義了一個,大家可以自己定義看看,(emmm,這里我感覺element-ui的校驗規則更好一丟丟)。

好了,既然定義好了登錄界面,就可以試試向后端發起請求了。

這里開始在api文件夾下面創建一個文件user.js然后編寫一個登錄接口函數

import request from '../utils/request'//創建一個調用登錄接口函數
export const userLoginService = (loginData) =>{//用urlSearchParams完成傳遞const params = new URLSearchParams()for(let key in loginData){params.append(key,loginData[key]);}return request.post('/user/login',params);
}

定義之后就去登錄頁調用

        <a-form-item><a-button html-type="submit" class="input-box" @click="login">Submit</a-button></a-form-item>

在submit提交這里加上@click="login"

然后定義方法:(獲取信息,如果msg提示信息不為0,則顯示消息,為0就表示登陸成功)

const login = async()=>{let result = await userLoginService(loginData.value);alert(result.msg?result.msg:'登錄成功');
}

不過,這里用瀏覽器默認的alert感覺不太美觀啊,畢竟都用組件庫了,

消息類型:就決定是你了!

引入一手修改代碼,

<script setup>
import { h } from 'vue';
import { IconExclamationCircleFill } from '@arco-design/web-vue/es/icon';
import { ref } from "vue";
import {userLoginService} from '../api/user'
const loginData = ref({username: "",password: "",isRead: false,
});const rules = [{validator: (value, cb) => {return new Promise((resolve) => {window.setTimeout(() => {if (value !== " ") {cb("content not empty");}resolve();}, 1000);});},},
];
const renderIcon = () => h(IconExclamationCircleFill);
const login = async()=>{let result = await userLoginService(loginData.value);renderIcon(result.msg?result.msg:'登錄成功');
}</script>

好,感覺這樣也就差不多了,下面展示一下登錄頁代碼

<template><div class="container"><div class="login-box"><a-form:model="loginData":style="{ width: '600px' }"@submit="handleSubmit"class="input-box"><h2 style="margin-bottom: 60px">Login</h2><a-form-itemfield="name"tooltip="Please enter username"label="賬號"class="element":rules="rules"><a-inputv-model="loginData.username"placeholder="please enter your username..."style="margin-right: 40px"/></a-form-item><a-form-item field="post" label="密碼" class="element" :rules="rules"><a-inputv-model="loginData.password"placeholder="please enter your password..."style="margin-right: 40px"/></a-form-item><a-form-item field="isRead"><a-checkbox v-model="loginData.isRead">I have read the manual</a-checkbox></a-form-item><a-form-item><a-button html-type="submit" class="input-box" @click="login">Submit</a-button></a-form-item><div class="register-link"><a href="#">Don't have an account? Create Now!</a></div></a-form></div></div>
</template>
<script setup>
import { h } from 'vue';
import { IconExclamationCircleFill } from '@arco-design/web-vue/es/icon';
import { ref } from "vue";
import {userLoginService} from '../api/user'
const loginData = ref({username: "",password: "",isRead: false,
});const rules = [{validator: (value, cb) => {return new Promise((resolve) => {window.setTimeout(() => {if (value !== " ") {cb("content not empty");}resolve();}, 1000);});},},
];
const renderIcon = () => h(IconExclamationCircleFill);
const login = async()=>{let result = await userLoginService(loginData.value);renderIcon(result.msg?result.msg:'登錄成功');
}</script><style scoped>
* {margin: 0;padding: 0;box-sizing: border-box;font-family: "Poppins", sans-serif;display: flex;justify-content: center;
}
.container {width: 100%;height: 100vh;background: url("../assets/background.jpg") no-repeat;background-size: cover;background-position: center;display: flex;justify-content: center;align-items: center;
}.container .login-box {position: relative;width: 500px;height: 580px;background-color: transparent;border: 2px solid rgba(255, 255, 255, 0.5);border-radius: 20px;display: flex;justify-content: center;align-items: center;backdrop-filter: blur(15px);
}.login-box h2 {font-size: 28px;color: #fff;text-align: center;
}.login-box .input-box {position: relative;width: 310px;margin: 30px 0;
}.input-box input {width: 80%;height: 60px;background: transparent;border: none;outline: none;font-size: 16px;color: #fff;padding: 0 2px 0 5px;
}.input-box input::placeholder {color: #f9f9f9;
}.input-box .icon {position: absolute;right: 8px;color: #fff;font-size: 16px;line-height: 25px;
}.login-box .remember-forget {margin: -15px 0 15px;font-size: 15px;color: #fff;display: flex;justify-content: space-between;
}.remember-forget label input {margin-right: 30px;
}.login-box button {width: 100%;height: 40px;background: #fff;border: none;outline: none;border-radius: 40px;cursor: pointer;font-size: 16px;color: #000;transition: all 0.5s;
}.login-box button:hover {background: #1f73c9;color: #fff;
}.login-box .register-link {font-size: 15px;color: #fff;text-align: center;margin: 5px 0 5px;
}.remember-forget a,
.register-link a {color: #fff;text-decoration: none;
}.remember-forget a:hover,
.register-link a:hover {text-decoration: underline;
}/* Responsive Design */
@media (max-width: 460px) {.container .login-box {width: 350px;}.login-box .input-box {width: 290px;}
}@media (max-width: 360px) {.container .login-box {width: 100%;height: 100vh;border: none;}
}
.element {margin: 20px 0;
}
</style>

好了,今天的前端就忙碌到這里吧(下一篇文章再見)。

總結一下:1.完成了前端初始化4步,2.完成了全局頁面的布局,3.完成了搜索頁的代碼,和登錄頁的代碼。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/13775.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/13775.shtml
英文地址,請注明出處:http://en.pswp.cn/web/13775.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

k8s 部署mqtt簡介

在Kubernetes&#xff08;K8s&#xff09;中部署MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;服務通常涉及以下幾個步驟&#xff1a; 選擇MQTT Broker MQTT Broker是MQTT消息傳遞的中間件。流行的MQTT Broker包括Mosquitto, HiveMQ, EMQ X等。你需要選擇一…

機器學習面試問題總結 | 貝葉斯網絡

本文給大家帶來的百面算法工程師是機器學習中貝葉斯網路面試總結&#xff0c;文章內總結了常見的提問問題&#xff0c;旨在為廣大學子模擬出更貼合實際的面試問答場景。在這篇文章中&#xff0c;我們還將介紹一些常見的面試問題&#xff0c;并提供參考的回答及其理論基礎&#…

K8S Secret管理之SealedSecrets

1 關于K8S Secret 我們通常將應用程序使用的密碼、API密鑰保存在K8S Secret中&#xff0c;然后應用去引用。對于這些敏感信息&#xff0c;安全性是至關重要的&#xff0c;而傳統的存儲方式可能會導致密鑰在存儲、傳輸或使用過程中受到威脅&#xff0c;例如在git中明文存儲密碼…

FreeRTOS_事件組_學習筆記

事件組 原文鏈接 事件組是一個整數&#xff0c;其中的高8位留給內核&#xff0c;只能用其他位來表示時間 每一位代表一個事件&#xff0c;且每個時間的含義由程序員決定 1為發生&#xff0c;0為未發生 一個/多個任務或ISR都能讀寫這些位 可以等待某一位&#xff0c;也可以等待…

如何理解合約中的引用類型(3)——Mapping

映射&#xff08;mapping&#xff09; 聲明形式&#xff1a;mapping(key type > value type)keytype可以是除枚舉外的幾乎任何基本類型&#xff0c;包括bytes和string&#xff0c;不包括用戶自定義的復雜類型-合約&#xff0c;枚舉&#xff0c;結構&#xff0c;映射value t…

簡單美觀易上手的 Docker Compose 可視化管理器 Dockge

本文首發于只抄博客&#xff0c;歡迎點擊原文鏈接了解更多內容。 前言 Dockge 是 Uptime Kuma 作者的新作品&#xff0c;因此 UI 風格與 Uptime Kuma 基本一致&#xff0c;如果你正在使用 Uptime Kuma 的話&#xff0c;那么 Dockge 的 UI 設計應該也不會讓你失望。Dockge 主打…

智慧校園為高校帶來哪些價值

在21世紀的教育圖景中&#xff0c;"智慧"不再僅僅是一個科技名詞&#xff0c;它已成為衡量教育現代化水平的重要標志。智慧校園&#xff0c;這一融合了物聯網、大數據、云計算等先進技術的教育新形態&#xff0c;正逐步成為高校轉型升級的關鍵驅動力。本文將從多個維…

note-網絡是怎樣連接的2 協議棧和網卡

助記提要 協議棧的結構協議棧創建連接的實際過程協議棧發送數據包的2個判斷依據TCP確認數據收到的原理斷開連接的過程路由表和ARPMAC地址的分配MAC模塊的工作通過電信號讀取數據的原理網卡和協議棧接收包的過程ICMPUDP協議的適用場景 2章 用電信號傳輸TCP/IP數據 探索協議棧和…

BUUCTF靶場 [reverse]easyre、reverse1、reverse2

工具&#xff1a; DIE&#xff1a;下載&#xff1a;https://download.csdn.net/download/m0_73981089/89334360 IDA&#xff1a;下載&#xff1a;https://hex-rays.com/ida-free/ 新手小白勇闖逆向區&#xff01;&#xff01;&#xff01; [reverse]easyre 首先查殼&#xf…

如何理解HTML語義化

如何理解HTML語義化 HTML語義化&#xff0c;簡單來說&#xff0c;就是使用HTML標簽來清晰地表達頁面內容的結構和意義&#xff0c;而不僅僅是作為布局的容器。它強調使用具有明確含義的HTML標簽來描述頁面元素&#xff0c;而不是僅僅依賴CSS來實現頁面的外觀和布局。 理解HTM…

android 權限相關定義解釋

1 PID &#xff1a;程序一運行系統就會自動分配給進程一個獨一無二的PID&#xff0c;內部線程可以共享 該pid的分配的內存數據 2 UID&#xff1a;User Identifier,UID在linux中就是用戶的ID&#xff0c;表明時哪個用戶運行了這個程序&#xff0c;主要用于權限的管理。內部共享各…

【oracle003】圖片轉為字節、base64編碼等形式批量插入oracle數據庫并查詢

1.熟悉、梳理、總結下Oracle相關知識體系 2.歡迎批評指正&#xff0c;跪謝一鍵三連&#xff01; 資源下載&#xff1a; oci.dll、oraocci11.dll、oraociei11.dll3個資源文件資源下載&#xff1a; Instant Client Setup.exe資源下載&#xff1a; oci.dll、oraocci11.dll、oraoc…

Vue ref訪問這些元素或組件的實例

可以在任意DOM元素上使用ref屬性&#xff0c;然后在Vue實例中通過this.$refs訪問這個DOM元素 1.訪問子組件實例&#xff0c;可以調用方法或者獲取值 <template><div><input type"text" ref"myInput" /><button click"focusInp…

ubuntu手動替換源后,更新源時提示“倉庫.... jammy Release“ 沒有Release文件

問題如圖所示&#xff0c;由于問題不好定位&#xff0c;我就從替換源&#xff0c;以及解決錯誤提示這兩個步驟&#xff0c;來解決其中可能存在的問題。 1、替換源 這一步驟&#xff0c;網上的資料可以搜到很多&#xff0c;我跟著做了之后&#xff0c;總會冒出來各種各樣的小問…

皮影戲藝術品3D沉浸式展館提供非同一般的趣味體驗

引領藝術展示的未來&#xff0c;深圳華銳視點融合多年的web3d項目制作經驗&#xff0c;傾力打造3D虛擬藝術品展館在線編輯平臺&#xff0c;為您提供一個超越時空限制的線上藝術展示平臺。 一、極致視覺盛宴 我們擁有領先的美術團隊&#xff0c;運用先進的web3D開發技術&#xf…

可能是最適合PS的AI插件,設計師大救星!StartAI初體驗!不是恰飯

這款軟件雖然有一些功能需要完善&#xff0c;比如&#xff1a; 1&#xff1a;生成圖片產品海報&#xff0c;會出現圖隨意出現&#xff0c;跟設計圖起沖突&#xff0c;需要PS才可以正常使用它。 2&#xff1a;即使開會員也需要排隊生成。 3: 還有&#xff0c;會員沒有更加好用的…

vue3結合element-plus之如何優雅的使用表單組件

背景 在日常開發中,我們會經常使用 element-ui 或者是 antdesign 或者是 element-plus 等組件庫實現搜索功能 這里就需要用到相關表單組件 下面就以 element-plus 為例,我們實現一個搜索功能的組件,并且隨著屏幕尺寸的變化,其布局也會跟隨變化 最終大致效果如下: 這里…

詳解VLSM技術

在現代網絡設計中&#xff0c;如何高效地分配和管理IP地址是一個關鍵問題。傳統的子網劃分方法雖然簡單&#xff0c;但在實際應用中常常導致IP地址的浪費。為了應對這一問題&#xff0c;VLSM&#xff08;Variable Length Subnet Mask&#xff0c;可變長子網掩碼&#xff09;技術…

C++:set和map的底層封裝模擬實現

目錄 底層對比&#xff1a; 底層紅黑樹結構和set、map&#xff1a; 底層模擬&#xff1a; 傳值調用&#xff1a; 迭代器&#xff1a; operator &#xff08;&#xff09; find函數 operator&#xff08;&#xff09; 、仿函數 set和map的仿函數 &#xff1a; 圖解&a…

地平線-旭日X3派(RDK X3)上手基本操作

0. 環境 - win10筆記本 - RDK X3 1.0&#xff08;地平線旭日X3派&#xff0c;后來改名為代號RDK X3&#xff09; 1. 下載資料 https://developer.horizon.ai/resource 主要下載鏡像 http://sunrise.horizon.cc/downloads/os_images/2.1.0/release/ 下載得到了 ubuntu-prei…