1. 项目整体架构
前端 (Vue.js + Element UI) 后端 (Spring Boot/Node.js等)
┌─────────────────┐ ┌──────────────────┐
│ Vue Router │◄───────►│ Controller │
│ Vuex Store │◄───────►│ Service │
│ Axios Request │◄───────►│ DAO/Mapper │
│ Components │ │ Database │
└─────────────────┘ └──────────────────┘
2. 用户登录流程
前端流程
// 1. 用户在登录页面输入用户名密码
// views/login/index.vue
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
this.$store.dispatch('user/login', this.loginForm)
.then(() => {
this.$router.push({ path: this.redirect || '/' })
this.loading = false
})
.catch(() => {
this.loading = false
})
}
})
}
// 2. 调用 store action
// store/modules/user.js
login({ commit }, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
login({ username: username.trim(), password: password }).then(response => {
const { data } = response
commit('SET_TOKEN', data.token)
setToken(data.token) // 存储到 Cookie
resolve()
}).catch(error => {
reject(error)
})
})
}
// 3. API 调用
// api/user.js
export function login(data) {
return request({
url: '/user/login',
method: 'post',
data
})
}
后端流程 (以 Spring Boot 为例)
// 1. Controller 接收请求
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/login")
public Result login(@RequestBody LoginRequest loginRequest) {
String token = userService.login(loginRequest);
return Result.success().put("token", token);
}
}
// 2. Service 处理业务逻辑
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public String login(LoginRequest loginRequest) {
// 验证用户名密码
User user = userMapper.selectByUsername(loginRequest.getUsername());
if (user == null || !passwordEncoder.matches(loginRequest.getPassword(), user.getPassword())) {
throw new RuntimeException("用户名或密码错误");
}
// 生成 JWT Token
String token = JwtUtils.generateToken(user.getId(), user.getUsername());
return token;
}
}
3. 带 Token 的业务请求流程
前端请求流程
// 1. 页面组件发起请求
// views/dashboard/index.vue
async getList() {
this.listLoading = true
try {
const response = await getList(this.listQuery)
this.list = response.data.items
} catch (error) {
console.error(error)
} finally {
this.listLoading = false
}
}
// 2. API 定义
// api/table.js
export function getList(params) {
return request({
url: '/table/list',
method: 'get',
params
})
}
// 3. Axios 拦截器自动添加 Token
// utils/request.js
service.interceptors.request.use(config => {
if (store.getters.token) {
config.headers['Authorization'] = `Bearer ${getToken()}`
}
return config
})
后端处理流程
// 1. Controller 接收带 Token 的请求
@RestController
@RequestMapping("/table")
public class TableController {
@Autowired
private TableService tableService;
@GetMapping("/list")
public Result list(@RequestParam Map<String, Object> params) {
PageUtils page = tableService.queryPage(params);
return Result.success().put("data", page);
}
}
// 2. 拦截器验证 Token
@Component
public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (StringUtils.isBlank(token)) {
throw new UnauthorizedException("未提供认证令牌");
}
// 验证 Token
Claims claims = JwtUtils.parseToken(token.replace("Bearer ", ""));
Long userId = claims.get("userId", Long.class);
// 将用户信息存入 ThreadLocal
UserContext.setUserId(userId);
return true;
}
}
// 3. Service 执行业务逻辑
@Service
public class TableServiceImpl implements TableService {
@Autowired
private TableMapper tableMapper;
@Override
public PageUtils queryPage(Map<String, Object> params) {
// 从 ThreadLocal 获取用户 ID
Long userId = UserContext.getUserId();
// 查询数据库
Query query = new Query(params);
List<TableEntity> list = tableMapper.queryPage(query);
int total = tableMapper.queryTotal(query);
PageUtils page = new PageUtils(list, total, query.getLimit(), query.getPage());
return page;
}
}
4. 完整的时序图
用户操作 → Vue组件 → Vuex Action → API Service → Axios → 网络传输
→ 后端Controller → Service → DAO → 数据库
↓
响应数据 ← 后端Response ← 业务逻辑处理 ← 数据查询 ← 数据库结果
↓
Vue组件更新 ← Vuex Mutation ← API Response ← Axios响应 ← 网络传输
5. 错误处理流程
前端错误处理
// utils/request.js 响应拦截器
service.interceptors.response.use(
response => {
const res = response.data
if (res.code !== 20000) {
Message({
message: res.message || 'Error',
type: 'error',
duration: 5 * 1000
})
// Token 过期处理
if (res.code === 50014) {
MessageBox.confirm('Token已过期,请重新登录', '确认退出', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
}
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
},
error => {
// HTTP 状态码错误处理
if (error.response.status === 401) {
// 未授权,跳转到登录页
store.dispatch('user/resetToken').then(() => {
location.reload()
})
}
Message.error(error.message)
return Promise.reject(error)
}
)
后端统一响应格式
// 统一响应对象
@Data
public class Result<T> implements Serializable {
private static final long serialVersionUID = 1L;
private Integer code;
private String msg;
private T data;
public static <T> Result<T> success() {
return success(null);
}
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(20000);
result.setMsg("成功");
result.setData(data);
return result;
}
public static <T> Result<T> error(String msg) {
return error(50000, msg);
}
public static <T> Result<T> error(Integer code, String msg) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMsg(msg);
return result;
}
}
6. 权限控制流程
前端路由守卫
// permission.js
router.beforeEach(async(to, from, next) => {
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
next({ path: '/' })
} else {
const hasRoles = store.getters.roles && store.getters.roles.length > 0
if (hasRoles) {
next()
} else {
try {
const { roles } = await store.dispatch('user/getInfo')
// 根据角色生成可访问路由
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
router.addRoutes(accessRoutes)
next({ …to, replace: true })
} catch (error) {
await store.dispatch('user/resetToken')
next(`/login?redirect=${to.path}`)
}
}
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
next(`/login?redirect=${to.path}`)
}
}
})
后端权限验证
// 方法级别的权限注解
@RestController
@RequestMapping("/system/user")
public class SysUserController {
@RequiresPermissions("system:user:list")
@GetMapping("/list")
public Result list(@RequestParam Map<String, Object> params) {
PageUtils page = userService.queryPage(params);
return Result.success().put("data", page);
}
@RequiresPermissions("system:user:add")
@PostMapping("/save")
public Result save(@RequestBody User user) {
userService.save(user);
return Result.success();
}
}
7. 文件上传流程
前端上传
// 使用 el-upload 组件
<el-upload
:action="uploadUrl"
:headers="headers"
:show-file-list="false"
:on-success="handleSuccess"
>
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
// 设置请求头
computed: {
headers() {
return {
'Authorization': 'Bearer ' + getToken()
}
}
}
后端处理
@RestController
@RequestMapping("/common/upload")
public class CommonController {
@PostMapping("/file")
public Result uploadFile(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return Result.error("上传文件不能为空");
}
try {
// 保存文件
String fileName = fileService.upload(file);
Map<String, Object> data = new HashMap<>();
data.put("fileName", fileName);
data.put("url", fileService.getFileUrl(fileName));
return Result.success().put("data", data);
} catch (Exception e) {
return Result.error(e.getMessage());
}
}
}
这个完整的流程展示了从用户操作到最终数据显示的全过程,包括认证、授权、业务逻辑处理和错误处理等各个环节。


