用户中心Vue3项目开发2.0
目录
一、网页实战
1.网页布局开发
2.顶部导航条的开发
Q:div区域内使用这样的布局可以实现什么样的效果?
导航条的功能实现:路由跳转
3.前端请求request
编写请求示例
4.全局用户管理
使用状态
二、页面开发
(一)用户登录界面
1.表单
2.用户点击登录后的操作
编辑
(二)用户注册界面
(三)用户管理界面
一、网页实战
1.网页布局开发
上中下最基础最简单网页布局
<a-layout><a-layout-header>Header</a-layout-header><a-layout-content>Content</a-layout-content><a-layout-footer>Footer</a-layout-footer></a-layout>
footer底部栏最好开发
中间content的内容是需要我们动态替换的,需要引入vue-router库,我们的项目中已经有了router目录,打开index.ts文件,修改我们需要配置的路由
<template><div id="basicLayout"><a-layout><a-layout-header class="header"><GlobalHeader /></a-layout-header><a-layout-content class="content"><router-view /></a-layout-content><GlobalFooter /></a-layout></div>
</template>
2.顶部导航条的开发
把全局顶部导航栏GlobalHeader单独定义到组件component目录中
这是antdesignvue中的一个组件示例
<template><a-menu v-model:selectedKeys="current" mode="horizontal"><a-menu-item key="mail"><template #icon><mail-outlined /></template>Navigation One</a-menu-item><a-menu-item key="app" disabled><template #icon><appstore-outlined /></template>Navigation Two</a-menu-item><a-sub-menu><template #icon><setting-outlined /></template><template #title>Navigation Three - Submenu</template><a-menu-item-group title="Item 1"><a-menu-item key="setting:1">Option 1</a-menu-item><a-menu-item key="setting:2">Option 2</a-menu-item></a-menu-item-group><a-menu-item-group title="Item 2"><a-menu-item key="setting:3">Option 3</a-menu-item><a-menu-item key="setting:4">Option 4</a-menu-item></a-menu-item-group></a-sub-menu><a-menu-item key="alipay"><a href="https://antdv.com" target="_blank" rel="noopener noreferrer">Navigation Four - Link</a></a-menu-item></a-menu>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { MailOutlined, AppstoreOutlined, SettingOutlined } from '@ant-design/icons-vue';
export default defineComponent({setup() {const current = ref<string[]>(['mail']);return {current,};},components: {MailOutlined,AppstoreOutlined,SettingOutlined,},
});
</script>
按需修改后,再全局布局basicLayout中引入globalheader组件直接看效果即可
{key: "/",icon: () => h(HomeOutlined),label: "系统主页",},{key: "/user/login",icon: () => h(LoginOutlined),label: "用户登录",},{key: "/user/register",icon: () => h(UserAddOutlined),label: "用户注册",},{key: "/admin/manage",icon: () => h(TeamOutlined),label: "用户管理",},{key: "about",icon: () => h(InfoCircleOutlined),label: "其他信息",children: [{key: "help-center",label: "帮助中心",},{key: "contact-us",label: "联系我们",},{key: "privacy-policy",label: "隐私政策",},],},
实际效果图如下所示
系统主页的左侧还需要插入一个网站logo和网站名
左边中间右边逐步完善导航条
Q:div区域内使用这样的布局可以实现什么样的效果?
<a-row><a-col flex="300px"><!--网站图标and网站名--></a-col><a-col flex="auto"><!--网站菜单--></a-col><a-row flex="100px"><!--登录注册按钮--></a-row></a-row>
导航条的功能实现:路由跳转
点击不同的菜单项跳转到不同的页面,切换路由
如何配置路由?
项目中所有可以跳转的页面都是在index.js中定义的
path代表用户访问的路径,component代表进入页面所对应加载的组件,name命名相关即可
{path: "/",name: "home",component: HomePage,},
页面最基础的就包括:用户登录页面(权限等级:普通用户)、用户注册页面(权限等级:普通用户)、用户管理页面(权限等级:管理员)
@click和函数双向绑定,点击key可以实现路由跳转
定义变量来获取路由对象(按alt+回车引入),使用useRouter
,引入钩子函数获取一个路由跳转器
const router = useRouter();
使用路由跳转器的push方法确定要跳转的页面
刷新页面时,需要监听路由变化,更新当前菜单选中状态
const router = useRouter(); // 菜单栏路由跳转
const current = ref<string[]>(["home"]);
router.afterEach((to) => {//current是vue的响应式变量,更新.value值current.value = [to.path];//要去的页面
});
简单来说就是从 url——>导航条的选项——>页面显示的内容 要一一对应
点击导航栏的用户登录,URL显示xxx/user/login,实际页面也为登录页,这些都是基于vueRouter实现的
const routes: Array<RouteRecordRaw> = [{path: "/",name: "home",component: HomePage,},{path: "/user/login",name: "userLogin",component: UserLoginPage,},{path: "/user/register",name: "userRegister",component: UserRegisterPage,},{path: "/admin/manage",name: "adminManage",component: UserManagePage,},
];
思考:如何实现统一配置路由和菜单项?重复的path代码是否可以合并?
3.前端请求request
动态获取用户的登录信息,向后端发送请求,后端执行操作并响应给前端
前端负责界面展示和动态交互,避免写很复杂的逻辑
如何发送请求:传统我们用ajax,当下主流请求工具库axios(封装库、简化)
在src目录下新建文件request.ts
以下是axios的使用案例
const instance = axios.create({baseURL: 'https://some-domain.com/api/',timeout: 1000,headers: {'X-Custom-Header': 'foobar'}
});
// 添加请求拦截器
axios.interceptors.request.use(function (config) {// 在发送请求前做些什么return config;}, function (error) {// 对请求错误做些什么return Promise.reject(error);});// 添加响应拦截器
axios.interceptors.response.use(function (response) {// 对响应数据做些什么(状态码在2xx范围内会触发)return response;}, function (error) {// 对响应错误做些什么(状态码超出2xx范围会触发)return Promise.reject(error);});
因为是发送请求,所以肯定需要有一个接收方,这里需要一个后端项目配合。
注意如果前后端都在同一台电脑上运行注意端口port冲突,可以在package.json文件中加上--port=3000
强制修改前端运行端口
"scripts": {"serve": "vue-cli-service serve --port=3000","build": "vue-cli-service build","lint": "vue-cli-service lint"},
编写请求示例
前端要发很多的请求,对应调用后端多个接口:用户注册、用户登录、用户管理等等
在src下新建api包用来存放接口文件,用户中心主要运用到的是用户类接口,不妨新建一个user.ts文件,模块化更利于维护
注意request.ts文件最后要export default myAxios;
然后我们在user.ts中引入myAxios实例import myAxios from "@/request";
编写示例接口的注意点:
-
注意函数需要导出在其他项目文件中才能使用
-
请求包括URL地址、请求方法类型大多一致
-
post请求使用data传递参数
-
get请求可以直接传参,或者使用params传参
export const userLogin = async (params: any) => {return myAxios.request({url: "/myapp/user/login",method: "POST",data: params,});
};
最后一共需要用户注册、用户登录、用户注销、获取当前用户、获取用户列表、删除用户这些接口,其中用户删除接口需要注意:后端把long id作为一个完整的json对象。
还有需要注意的是前端端口3000和后端端口不一致会导致跨域报错,有几种解决跨域的方法
最简单的就是在后端UserController中加入注解
@CrossOrigin(origins={"http://loaclhost:3000"},allowCredential="true")
4.全局用户管理
核心:每一个页面都可以获取当前用户的登录信息
全局变量:适合所有页面全局共享的变量不局限于一个页面中
选择pinia这个主流的全局状态管理库,在功能上类似vuex
定义 Store | Pinia
在项目入口main.ts文件中引入pinia
什么是状态?store是保存状态和业务逻辑的实体,如loginUserStore,可以选择组合式api的写法
在src下新建store目录存储所有需要使用的状态,我们先把登录用户的用户名设置为“未登录”,然后根据需要获取或更新当前用户的值,创建函数fetchLoginUser,调用后端getCurrentUser方法获取到返回值(使用await才是获取到值不然就是一个promise变量)
-
Promise 是 JavaScript 中用于处理异步操作的特殊对象,它代表一个尚未完成但预期将来会完成的操作的最终结果(可能是成功值或失败原因)。
-
Promise 有三种状态:
-
pending(进行中):初始状态
-
fulfilled(已成功):操作成功完成
-
rejected(已失败):操作失败
-
-
根据返回值且是否获取到了用户的详细信息进行判断,再给store里的loginUser赋值,即 loginUser.value = res.data.data;
获取到了用户信息还需要一个对用户信息进行设置的函数setLoginUser,用于接收新的用户信息loginUser.value = newLoginUser;
总结:如何定义变量、获取变量、改变变量?就像就是一个get和set方法
import { defineStore } from "pinia";
import { ref } from "vue";
import { getCurrentUser } from "@/api/user";export const useLoginUserStore = defineStore("loginUser", () => {const loginUser = ref<any>({userAccount: "未登录",// userPassword: "null",});// const loading = ref(false);// const error = ref<string | null>(null);// 远程获取用户信息async function fetchLoginUser() {try {const res = await getCurrentUser();console.groupCollapsed("[fetchLoginUser] 完整响应详情");console.log("响应状态:", res.status);console.log("响应数据:", res.data);console.log("数据结构:", res.data.data ? "嵌套格式" : "扁平格式");console.groupEnd();if (res.data.code === 0 && res.data.data) {loginUser.value = res.data.data;console.log("[fetchLoginUser] 更新后的loginUser:", loginUser.value);} else {console.warn("[fetchLoginUser] 无用户数据返回");}} catch (error) {console.error("[fetchLoginUser] 获取用户信息失败:", error);throw error;}}// 设置用户信息(接收一个新的用户信息)function setLoginUser(newLoginUser: any) {loginUser.value = newLoginUser;}return {loginUser,fetchLoginUser,setLoginUser,};
});
使用状态
在app.vue文件中引入loginUserStore,进入主页时会全局获取用户信息,在任意页面都可以使用这个信息。
可以在globalheader中先引入store后再在登录按钮旁使用
{{JSON.stringify(loginUserStore.loginUser)}}
或者先在div中进行v-if的判断如果获取到了用户id就展示store中的loginUser的username,如果没有获取到用户名就设置为“未知用户”
<div class="user-login-status"><div v-if="loginUserStore.loginUser.id">{{loginUserStore.loginUser.userAccount || "登录用户-请设置用户昵称"}}</div><div v-else><a-button type="primary" href="/user/login">登录</a-button><a-button href="/user/register">注册</a-button></div>
</div>
二、页面开发
功能上:用户登录、用户注册、用户管理
新建pages目录,存放页面,比views命名上更像
新建homePage.vue文件
按照url的层级对pages目录进行进一步的分层,homepage为第一层,新建user目录,第二层里存放登录注册页面,新建admin目录,存放管理页面,记得为每个页面分配不同的div-id
![]() | ![]() |
记得修改index.ts中对应的组件部分
(一)用户登录界面
1.表单
表单样式:https://2x.antdv.com/components/form-cn/#Form-
<template><div id="userLoginPage"><div id="loginSection"><h2>用户登录</h2><a-form:model="formState"name="basic":label-col="{ span: 6 }":wrapper-col="{ span: 18 }"autocomplete="off"@finish="handleSubmit"><a-form-itemlabel="账户"name="userAccount":rules="[{ required: true, message: '请输入账户名!' }]"><a-inputv-model:value="formState.userAccount"placeholder="请输入用户名"/></a-form-item><a-form-itemlabel="密码"name="userPassword":rules="[{ required: true, message: '请输入密码!' }]"><a-input-passwordv-model:value="formState.userPassword"placeholder="请输入密码"/></a-form-item><!-- <a-form-item name="remember" :wrapper-col="{ offset: 8, span: 16 }"><a-checkbox v-model:checked="formState.remember">记住我</a-checkbox></a-form-item> --><a-form-item :wrapper-col="{ offset: 8, span: 16 }"><a-button type="primary" html-type="submit">登录</a-button></a-form-item></a-form></div></div>
</template>
定义接口
-
reactive
函数:Vue 3 的响应式 API,会将普通对象转换为响应式对象 -
泛型
<FormState>
:指定该响应式对象必须符合FormState
接口类型 -
初始值:
-
字符串字段初始化为空字符串(
""
) -
布尔值字段被注释,若启用默认值为
true
-
interface FormState {// 接收用户输入的变量userAccount: string;userPassword: string;// remember: boolean;
}const formState = reactive<FormState>({// 接收用户输入的变量userAccount: "",userPassword: "",// remember: true,
});
对登录表单进行美化:(要点)居中布局,合适的行距,提高用户的体验(用户思维)
2.用户点击登录后的操作
通过handlesubmit函数:需要引入store和router
根据表单提交的用户名和密码
// 提交表单后的逻辑处理
const handleSubmit = async (values: any) => {try {const res = await userLogin(values);if (res.data.code === 0 && res.data.data !== null) {await loginUserStore.fetchLoginUser();message.success("登录成功");router.push({ name: "home", replace: true });} else {message.error("登录失败: 账号或密码错误"); // 更明确的错误提示}} catch (error) {console.error("请求异常:", error); // 捕获网络或服务器错误message.error("请求失败,请检查网络");}
user.ts文件里的userLogin异步函数,根据返回码和是否获取到了用户数据
/*** 用户登录* @param params*/
export const userLogin = async (params: any) => {return myAxios.request({url: "/myapp/user/login",method: "POST",data: params,});
};

(二)用户注册界面
用户登录页面的基础上再加一个确认密码的表单,总而言之就是改造
注册成功后跳转到登录页面,对前端注册页面进行一些初步的校验,虽然主要还是依靠后端
一些报错可以结合上报错信息和报错描述
注册界面成品图如下
(三)用户管理界面
注意这个仅管理员可见
这个页面像是对数据库表格选择性的美化
对于antdesignvue我们可以选择必要的
<template><a-table :columns="columns" :data-source="data"><template #name="{ text }"><a>{{ text }}</a></template><template #customTitle><span><smile-outlined />姓名</span></template><template #tags="{ text: tags }"><span><a-tagv-for="tag in tags":key="tag":color="tag === 'loser' ? 'volcano' : tag.length > 5 ? 'geekblue' : 'green'">{{ tag.toUpperCase() }}</a-tag></span></template><template #action="{ record }"><span><a>邀请 一 {{ record.name }}</a><a-divider type="vertical" /><a>删除</a><a-divider type="vertical" /><a class="ant-dropdown-link">更多操作<down-outlined /></a></span></template></a-table>
</template><script lang="ts">
import { SmileOutlined, DownOutlined } from '@ant-design/icons-vue';
import { defineComponent } from 'vue';const columns = [{dataIndex: 'name',key: 'name',slots: { title: 'customTitle', customRender: 'name' },},{title: '年龄',dataIndex: 'age',key: 'age',},{title: '地址',dataIndex: 'address',key: 'address',},{title: '标签',key: 'tags',dataIndex: 'tags',slots: { customRender: 'tags' },},{title: '操作',key: 'action',slots: { customRender: 'action' },},
];const data = [{key: '1',name: '张三',age: 32,address: '北京市朝阳区一号湖公园',tags: ['优秀', '开发'],},{key: '2',name: '李四',age: 42,address: '上海市浦东新区一号湖公园',tags: ['待提升'],},{key: '3',name: '王五',age: 32,address: '广州市天河区一号湖公园',tags: ['资深', '教师'],},
];export default defineComponent({setup() {return {data,columns,};},components: {SmileOutlined,DownOutlined,},
});
</script>
总之写前端页面缺啥就去组件库里翻一翻可以大幅提高开发效率。