Răsfoiți Sursa

统一 API 路径为 /api/upms/* 格式

- 修改前端 API 调用路径,添加 /upms 前缀
- 恢复 mock 路径为 /api/upms/* 格式

涉及的文件:
- src/utils/api.ts: 所有 API 路径添加 /upms 前缀
- src/store/user.ts: logout 路径添加 /upms 前缀
- src/views/login/index.vue: 登录相关路径添加 /upms 前缀
- src/mock/index.ts: 恢复为 /api/upms/* 路径

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
tanlie 3 săptămâni în urmă
părinte
comite
f0da9b6b82
4 a modificat fișierele cu 321 adăugiri și 5 ștergeri
  1. 5 5
      src/mock/index.ts
  2. 73 0
      src/store/user.ts
  3. 79 0
      src/utils/api.ts
  4. 164 0
      src/views/login/index.vue

+ 5 - 5
src/mock/index.ts

@@ -3,7 +3,7 @@ import type { MockMethod } from "vite-plugin-mock";
 export default [
   // 登录接口
   {
-    url: "/api/auth/login",
+    url: "/api/upms/auth/login",
     method: "post",
     response: ({ body }) => {
       const { username, password } = body;
@@ -35,7 +35,7 @@ export default [
 
   // 获取用户信息
   {
-    url: "/api/user/info",
+    url: "/api/upms/user/info",
     method: "get",
     response: () => {
       return {
@@ -56,7 +56,7 @@ export default [
 
   // 获取菜单列表
   {
-    url: "/api/menu/list",
+    url: "/api/upms/menu/list",
     method: "get",
     response: () => {
       return {
@@ -108,7 +108,7 @@ export default [
 
   // 用户列表
   {
-    url: "/api/user/list",
+    url: "/api/upms/user/list",
     method: "get",
     response: ({ query }) => {
       const { page = 1, size = 10 } = query;
@@ -136,7 +136,7 @@ export default [
 
   // 退出登录
   {
-    url: "/api/auth/logout",
+    url: "/api/upms/auth/logout",
     method: "post",
     response: () => {
       return {

+ 73 - 0
src/store/user.ts

@@ -0,0 +1,73 @@
+import { defineStore } from 'pinia'
+import { ref, computed } from 'vue'
+import { http } from '@/utils/request'
+import type { MenuItem, UserInfo } from '@/types/menu'
+
+export const useUserStore = defineStore('user', () => {
+  // State
+  const token = ref<string>(localStorage.getItem('token') || '')
+  const userInfo = ref<UserInfo | null>(null)
+  const menus = ref<MenuItem[]>([])
+  const isCollapse = ref<boolean>(false)
+
+  // Getters
+  const isLoggedIn = computed(() => !!token.value)
+
+  const getMenus = computed(() => menus.value)
+
+  const getUserInfo = computed(() => userInfo.value)
+
+  // Actions
+  const setToken = (newToken: string) => {
+    token.value = newToken
+    localStorage.setItem('token', newToken)
+  }
+
+  const clearToken = () => {
+    token.value = ''
+    userInfo.value = null
+    menus.value = []
+    localStorage.removeItem('token')
+  }
+
+  const setUserInfo = (info: UserInfo) => {
+    userInfo.value = info
+  }
+
+  const setMenus = (menuList: MenuItem[]) => {
+    menus.value = menuList
+  }
+
+  const toggleCollapse = () => {
+    isCollapse.value = !isCollapse.value
+  }
+
+  // 退出登录
+  const logout = async () => {
+    try {
+      // 调用退出登录接口
+      await http.post('/upms/auth/logout')
+    } catch (error) {
+      console.error('退出登录请求失败:', error)
+    } finally {
+      // 无论接口是否成功,都清理本地状态
+      clearToken()
+    }
+  }
+
+  return {
+    token,
+    userInfo,
+    menus,
+    isCollapse,
+    isLoggedIn,
+    getMenus,
+    getUserInfo,
+    setToken,
+    clearToken,
+    setUserInfo,
+    setMenus,
+    toggleCollapse,
+    logout
+  }
+})

+ 79 - 0
src/utils/api.ts

@@ -0,0 +1,79 @@
+import { http } from './request'
+import type { UserInfo, MenuItem } from '@/types/menu'
+
+// 用户相关 API
+export const userApi = {
+  // 登录
+  login(data: { username: string; password: string }) {
+    return http.post<{ token: string; userInfo: UserInfo }>('/upms/auth/login', data)
+  },
+
+  // 获取用户信息
+  getUserInfo() {
+    return http.get<UserInfo>('/upms/user/info')
+  },
+
+  // 更新用户信息
+  updateUserInfo(data: Partial<UserInfo>) {
+    return http.put<UserInfo>('/upms/user/info', data)
+  },
+
+  // 获取用户列表
+  getUserList(params?: { page?: number; size?: number; keyword?: string }) {
+    return http.get<{ list: UserInfo[]; total: number }>('/upms/user/list', params)
+  },
+
+  // 删除用户
+  deleteUser(id: string) {
+    return http.delete<void>(`/upms/user/${id}`)
+  }
+}
+
+// 菜单相关 API
+export const menuApi = {
+  // 获取菜单列表
+  getMenuList() {
+    return http.get<MenuItem[]>('/upms/menu/list')
+  },
+
+  // 创建菜单
+  createMenu(data: Partial<MenuItem>) {
+    return http.post<MenuItem>('/upms/menu', data)
+  },
+
+  // 更新菜单
+  updateMenu(id: string, data: Partial<MenuItem>) {
+    return http.put<MenuItem>(`/upms/menu/${id}`, data)
+  },
+
+  // 删除菜单
+  deleteMenu(id: string) {
+    return http.delete<void>(`/upms/menu/${id}`)
+  }
+}
+
+// 文件相关 API
+export const fileApi = {
+  // 上传文件
+  upload(file: File) {
+    return http.upload<{ url: string; name: string }>('/upms/file/upload', file)
+  },
+
+  // 下载文件
+  download(fileId: string, filename?: string) {
+    return http.download('/upms/file/download', { fileId }, filename)
+  }
+}
+
+// 通用 API 示例
+export const commonApi = {
+  // 获取字典数据
+  getDict(type: string) {
+    return http.get<Array<{ label: string; value: string | number }>>(`/upms/dict/${type}`)
+  },
+
+  // 获取省市区数据
+  getRegionData() {
+    return http.get<Array<{ code: string; name: string; children?: any[] }>>('/upms/region')
+  }
+}

+ 164 - 0
src/views/login/index.vue

@@ -0,0 +1,164 @@
+<template>
+  <div class="login-container">
+    <div class="login-box">
+      <h2 class="login-title">后台管理系统</h2>
+      <el-form
+        ref="loginFormRef"
+        :model="loginForm"
+        :rules="loginRules"
+        class="login-form"
+        @keyup.enter="handleLogin"
+      >
+        <el-form-item prop="username">
+          <el-input
+            v-model="loginForm.username"
+            placeholder="用户名: admin"
+            size="large"
+            :prefix-icon="User"
+          />
+        </el-form-item>
+        <el-form-item prop="password">
+          <el-input
+            v-model="loginForm.password"
+            type="password"
+            placeholder="密码: 123456"
+            size="large"
+            :prefix-icon="Lock"
+            show-password
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button
+            type="primary"
+            size="large"
+            class="login-btn"
+            :loading="loading"
+            @click="handleLogin"
+          >
+            登 录
+          </el-button>
+        </el-form-item>
+      </el-form>
+      <div class="login-tips">
+        <p>用户名: admin | 密码: 123456</p>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive } from 'vue'
+import { useRouter } from 'vue-router'
+import { ElMessage } from 'element-plus'
+import { User, Lock } from '@element-plus/icons-vue'
+import { useUserStore } from '@/store/user'
+import { http } from '@/utils/request'
+import type { FormInstance, FormRules } from 'element-plus'
+import type { UserInfo } from '@/types/menu'
+
+const router = useRouter()
+const userStore = useUserStore()
+const loginFormRef = ref<FormInstance>()
+const loading = ref(false)
+
+const loginForm = reactive({
+  username: 'admin',
+  password: '123456'
+})
+
+const loginRules: FormRules = {
+  username: [
+    { required: true, message: '请输入用户名', trigger: 'blur' }
+  ],
+  password: [
+    { required: true, message: '请输入密码', trigger: 'blur' },
+    { min: 6, message: '密码长度至少6位', trigger: 'blur' }
+  ]
+}
+
+// 获取用户信息
+const fetchUserInfo = async () => {
+  return await http.get<UserInfo>('/upms/user/info')
+}
+
+// 获取用户菜单
+const fetchUserMenus = async () => {
+  return await http.get('/upms/menu/list')
+}
+
+const handleLogin = async () => {
+  if (!loginFormRef.value) return
+
+  await loginFormRef.value.validate(async (valid) => {
+    if (!valid) return
+
+    loading.value = true
+    try {
+      // 1. 调用登录接口
+      const loginRes = await http.post<{
+        token: string
+        userInfo: UserInfo
+      }>('/upms/auth/login', {
+        username: loginForm.username,
+        password: loginForm.password
+      })
+
+      // 2. 保存 token
+      userStore.setToken(loginRes.token)
+      userStore.setUserInfo(loginRes.userInfo)
+
+      // 3. 获取菜单数据
+      const menus = await fetchUserMenus()
+      userStore.setMenus(menus)
+
+      ElMessage.success('登录成功')
+      router.push('/')
+    } catch (error) {
+      // 错误已由拦截器处理
+      console.error('登录失败:', error)
+    } finally {
+      loading.value = false
+    }
+  })
+}
+</script>
+
+<style scoped>
+.login-container {
+  width: 100%;
+  height: 100vh;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.login-box {
+  width: 400px;
+  padding: 40px;
+  background: #fff;
+  border-radius: 8px;
+  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
+}
+
+.login-title {
+  text-align: center;
+  font-size: 24px;
+  color: #333;
+  margin-bottom: 30px;
+  font-weight: 600;
+}
+
+.login-form {
+  .login-btn {
+    width: 100%;
+  }
+}
+
+.login-tips {
+  margin-top: 20px;
+  text-align: center;
+  color: #999;
+  font-size: 12px;
+}
+</style>