前言
如果对 vue3 的语法不熟悉的,可以移步Vue3.0 基础入门,快速入门。
项目所需要的图片,icon图标(推荐:阿里巴巴矢量图标库)自行获取,命名一致就行。
1. 构建 src/components/CopyRight.vue 版本号组件
<!-- src/components/CopyRight.vue -->
<script setup>
const props = defineProps({color: {type: String,},
});
</script><template><p :style="{ color }">版权所有 © YLD科技有限公司,保留所有权利,川F3-20233308号</p>
</template><style lang="scss" scoped>
p {position: absolute;bottom: 15px;left: 0;right: 0;text-align: center;color: #fff;font-family: Arial;font-size: 12px;letter-spacing: 1px;
}
</style>
2. 构建 src/components/SvgIcon.vue 图标组件
参考在 vue3 中构建 SvgIcon 组件
3. 编辑 src/pages/login.vue
<!-- src/pages/login.vue -->
<script setup>
import cookie from "js-cookie";
import { ref, reactive } from "vue";
import { NButton, NInput, NForm, NFormItem, NCheckbox } from "naive-ui";
import router from "@/router/index.js";
import { useUserStore } from "@/store/user.js";
import { encrypt, decrypt } from "@/utils/jsencrypt";
import { getCodeImg } from "@/api/login.js";import CopeRight from "@/components/CopyRight.vue";// 用户状态管理
let userStore = useUserStore();// 是否需要验证码
let needCode = ref(false);
let codeUrl = ref("");
// 获取图形验证码
function getCode() {getCodeImg().then((res) => {needCode.value =res.captchaEnabled === undefined ? true : res.captchaEnabled;if (needCode.value) {codeUrl.value = "data:image/gif;base64," + res.img;form.uuid = res.uuid;}});
}
getCode();// 登录
let formRef = ref(null);
let form = reactive({username: "",password: "",code: "",uuid: "",
});
let rules = {username: {required: true,trigger: ["input", "blur"],message: "请输入用户名",},password: {required: true,trigger: ["input", "blur"],message: "请输入密码",},code: {required: true,trigger: ["input", "blur"],message: "请输入验证码",},
};
let rememberMe = ref(false);
let loginBtnState = ref(false);
let handleLogin = () => {loginBtnState.value = true;formRef.value?.validate((errors) => {if (!errors) {if (rememberMe.value) {cookie.set("username", form.username, { expires: 30 });cookie.set("password", encrypt(form.password), { expires: 30 });cookie.set("rememberMe", rememberMe.value, { expires: 30 });} else {cookie.remove("username");cookie.remove("password");cookie.remove("rememberMe");}userStore.login(form).then(() => {window.$msg.success("登录成功");router.push({ name: "home" });}).catch(() => {loginBtnState.value = false;getCode();});} else {loginBtnState.value = false;}});
};// 获取默认登录账号
function getCookie() {form.username = cookie.get("username") || "";form.password = decrypt(cookie.get("password")) || "";rememberMe.value = Boolean(cookie.get("rememberMe")) || false;
}
getCookie();
</script><template><div class="login-bg c-center"><div class="login__box"><div class="login-logo__box"><div class="login__title login-logo">登 录</div></div><n-formref="formRef"class="login-form__box":model="form":rules="rules"label-placement="left"><n-form-item path="username"><n-inputclass="login-input"v-model:value="form.username"placeholder="请输入用户名/手机号"@blur="validate"><template #prefix><svg-icon name="username" color="grey"></svg-icon></template></n-input></n-form-item><n-form-item path="password"><n-inputclass="login-input"v-model:value="form.password"placeholder="请输入密码"type="password"show-password-on="mousedown"@keyup.enter="handleLogin"><template #prefix><svg-icon name="password" color="grey"></svg-icon></template></n-input></n-form-item><n-form-item v-if="needCode" class="login-code" path="code"><n-inputv-model:value="form.code"class="login-input login-input_code"placeholder="验证码"@keyup.enter="handleLogin"><template #prefix><svg-icon name="code" color="grey"></svg-icon></template></n-input><img class="login-code-img" :src="codeUrl" @click="getCode" /></n-form-item><div class="login-checkbox_box"><n-checkboxclass="login-checkbox"v-model:checked="rememberMe"></n-checkbox><span>记住密码</span></div><n-buttonclass="login-btn_login"type="info"@click="handleLogin":loading="loginBtnState":disabled="loginBtnState">登录</n-button></n-form><!-- <div class="login-btn_forget" @click="handleForget">忘记密码 ?</div> --></div><cope-right></cope-right></div>
</template><style lang="scss">
.n-form-item {margin-top: -20px;
}
</style><style lang="scss" scoped>
.login-bg {width: 100vw;height: 100vh;background-image: url("@/assets/imgs/login_bg.png");background-size: cover;background-repeat: no-repeat;overflow: hidden;.login__box {width: 425px;padding: 20px 40px;border-radius: 10px;background: rgba(2, 57, 104, 0.7);box-shadow: 0 0 30px rgba(2, 57, 104, 0.7);box-sizing: border-box;.login__title {font-size: 24px;text-align: center;color: #fff;}}
}.login-title {margin-bottom: 15px;text-align: center;letter-spacing: 10px;font-size: 18px;color: #fff;opacity: 1;
}.login-logo__box {display: flex;justify-content: center;align-items: center;margin-bottom: 20px;text {display: flex;align-items: center;font-size: 16px;font-family: "Gill Sans", "Gill Sans MT", Calibri, "Trebuchet MS",sans-serif;color: #1d69a3;&::before {content: "";display: inline-block;margin: 0 5px;width: 5px;height: 5px;background: gray;border-radius: 50%;}}
}.login-logo {width: 55%;img {width: 100%;object-fit: cover;}
}.login-code {.login-input_code {width: 240px;}.login-btn_code {color: #fff;margin-left: 20px;}
}.login-btn_login {height: 40px;width: 100%;
}.login-input {margin-top: 20px;
}.login-checkbox_box {display: flex;align-items: center;justify-content: flex-end;margin: -16px 0 10px;color: #71a1c5;font-size: 12px;
}.login-checkbox {margin-right: 5px;
}.login-btn_forget {margin-top: 10px;text-align: center;font-size: 12px;color: #71a1c5;cursor: pointer;
}.login-code-img {margin-top: 20px;width: 30%;height: 35px;margin-left: 20px;object-fit: contain;cursor: pointer;
}
</style>
综上
login.vue 登录页构建完成。下一章:layout 动态路由布局