Browse Source

feat: 完成登陆模块功能

曾坤森 3 months ago
parent
commit
fd885bc24a

+ 2 - 1
.env.development

@@ -1 +1,2 @@
-VITE_API_BASE_URL= 'http://localhost:8080'
+VITE_NODE_ENV= development
+VITE_API_BASE_URL= 'http://localhost:5173/'

+ 2 - 0
.env.production

@@ -0,0 +1,2 @@
+VITE_NODE_ENV= production
+VITE_API_BASE_URL= 'http://192.168.1.28:5000/'

+ 8 - 0
config/vite.config.dev.ts

@@ -9,6 +9,14 @@ export default mergeConfig(
       fs: {
         strict: true,
       },
+      proxy: {
+        '/api': {
+          // 代理路径前缀
+          target: 'http://192.168.1.28:5000/', // 目标服务器地址
+          changeOrigin: true, // 修改请求头中的 Origin
+          rewrite: path => path.replace(/^\/api/, ''), // 重写路径(可选)
+        },
+      },
     },
     plugins: [],
   },

+ 1 - 1
index.html

@@ -4,7 +4,7 @@
     <meta charset="UTF-8" />
     <link rel="shortcut icon" type="image/x-icon" href="https://unpkg.byted-static.com/latest/byted/arco-config/assets/favicon.ico">
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>Arco Design Pro</title>
+    <title>Smms Web</title>
   </head>
   <body>
     <div id="app"></div>

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "site-web",
-  "description": "Arco Design Pro for Vue",
+  "description": "Smms Web",
   "version": "1.0.0",
   "type": "module",
   "private": true,

+ 22 - 43
src/api/interceptor.ts

@@ -8,18 +8,11 @@ import { Message, Modal } from '@arco-design/web-vue';
 import { useUserStore } from '@/store';
 import { getToken } from '@/utils/auth';
 
-export interface HttpResponse<T = unknown> {
-  status: number;
-  msg: string;
-  code: number;
-  data: T;
-}
+const instance = axios.create({
+  baseURL: import.meta.env.VITE_API_BASE_URL,
+});
 
-if (import.meta.env.VITE_API_BASE_URL) {
-  axios.defaults.baseURL = import.meta.env.VITE_API_BASE_URL;
-}
-
-axios.interceptors.request.use(
+instance.interceptors.request.use(
   (config: InternalAxiosRequestConfig) => {
     // let each request carry token
     // this example using the JWT token
@@ -34,47 +27,32 @@ axios.interceptors.request.use(
     }
     return config;
   },
-  (error) => {
+  error => {
     // do something
     return Promise.reject(error);
   }
 );
 // 响应拦截器修复版本
-axios.interceptors.response.use(
-  (response: AxiosResponse<HttpResponse>): any => {
+instance.interceptors.response.use(
+  (response: AxiosResponse) => {
     const res = response.data;
-
-    if (res.code !== 20000) {
-      Message.error({
-        content: res.msg || 'Error',
-        duration: 5 * 1000,
+    if (response.status === 401) {
+      Modal.error({
+        title: 'Confirm logout',
+        content:
+          'You have been logged out, you can cancel to stay on this page, or log in again',
+        okText: 'Re-Login',
+        async onOk() {
+          const userStore = useUserStore();
+          await userStore.logout();
+          window.location.reload();
+        },
       });
-
-      if (
-        [50008, 50012, 50014].includes(res.code) &&
-        response.config.url !== '/api/user/info'
-      ) {
-        Modal.error({
-          title: 'Confirm logout',
-          content:
-            'You have been logged out, you can cancel to stay on this page, or log in again',
-          okText: 'Re-Login',
-          async onOk() {
-            const userStore = useUserStore();
-            await userStore.logout();
-            window.location.reload();
-          },
-        });
-      }
-
-      // 修复点:仍需返回 AxiosResponse 类型,不能直接返回 res
-      return Promise.reject(new Error(res.msg || 'Error'));
     }
-
-    // 修复点:返回完整的 response,而不是 response.data
-    return res;
+    console.log('resaaaa', response);
+    return response;
   },
-  (error) => {
+  error => {
     Message.error({
       content: error.message || 'Request Error',
       duration: 5 * 1000,
@@ -82,3 +60,4 @@ axios.interceptors.response.use(
     return Promise.reject(error);
   }
 );
+export default instance;

+ 19 - 3
src/api/user.ts

@@ -1,17 +1,33 @@
 import axios from 'axios';
 import type { RouteRecordNormalized } from 'vue-router';
 import { UserState } from '@/store/modules/user/types';
+import instance from './interceptor';
 
 export interface LoginData {
   username: string;
   password: string;
 }
 
-export interface LoginRes {
+export interface LogoutRes {
   token: string;
 }
-export function login(data: LoginData) {
-  return axios.post<LoginRes>('/api/user/login', data);
+export interface Data {
+  id: number;
+  name: string;
+  password: string;
+  desc: string;
+  privilege: number;
+}
+
+export interface LoginRes {
+  success: boolean;
+  data: Data;
+  message: string;
+  code: string;
+}
+export async function login(params: object): Promise<LoginRes> {
+  const res = await instance.post('/api/Author/Login', params);
+  return res.data;
 }
 
 export function logout() {

+ 0 - 73
src/api/visualization.ts

@@ -1,73 +0,0 @@
-import axios from 'axios';
-import { GeneralChart } from '@/types/global';
-
-export interface ChartDataRecord {
-  x: string;
-  y: number;
-  name: string;
-}
-export interface DataChainGrowth {
-  quota: string;
-}
-
-export interface DataChainGrowthRes {
-  count: number;
-  growth: number;
-  chartData: {
-    xAxis: string[];
-    data: { name: string; value: number[] };
-  };
-}
-export function queryDataChainGrowth(data: DataChainGrowth) {
-  return axios.post<DataChainGrowthRes>('/api/data-chain-growth', data);
-}
-
-export interface PopularAuthorRes {
-  list: {
-    ranking: number;
-    author: string;
-    contentCount: number;
-    clickCount: number;
-  }[];
-}
-
-export function queryPopularAuthor() {
-  return axios.get<PopularAuthorRes>('/api/popular-author/list');
-}
-
-export interface ContentPublishRecord {
-  x: string[];
-  y: number[];
-  name: string;
-}
-
-export function queryContentPublish() {
-  return axios.get<ContentPublishRecord[]>('/api/content-publish');
-}
-
-export function queryContentPeriodAnalysis() {
-  return axios.post<GeneralChart>('/api/content-period-analysis');
-}
-
-export interface PublicOpinionAnalysis {
-  quota: string;
-}
-export interface PublicOpinionAnalysisRes {
-  count: number;
-  growth: number;
-  chartData: ChartDataRecord[];
-}
-export function queryPublicOpinionAnalysis(data: DataChainGrowth) {
-  return axios.post<PublicOpinionAnalysisRes>(
-    '/api/public-opinion-analysis',
-    data
-  );
-}
-export interface DataOverviewRes {
-  xAxis: string[];
-  data: Array<{ name: string; value: number[]; count: number }>;
-}
-
-export function queryDataOverview() {
-  return axios.post<DataOverviewRes>('/api/data-overview');
-}

+ 2 - 3
src/hooks/request.ts

@@ -1,6 +1,5 @@
 import { ref, UnwrapRef } from 'vue';
 import { AxiosResponse } from 'axios';
-import { HttpResponse } from '@/api/interceptor';
 import useLoading from './loading';
 
 // use to fetch list
@@ -9,14 +8,14 @@ import useLoading from './loading';
 // example: useRequest(api.bind(null, {}))
 
 export default function useRequest<T>(
-  api: () => Promise<AxiosResponse<HttpResponse>>,
+  api: () => Promise<AxiosResponse<T>>,
   defaultValue = [] as unknown as T,
   isLoading = true
 ) {
   const { loading, setLoading } = useLoading(isLoading);
   const response = ref<T>(defaultValue);
   api()
-    .then((res) => {
+    .then(res => {
       response.value = res.data as unknown as UnwrapRef<T>;
     })
     .finally(() => {

+ 1 - 1
src/layout/default-layout.vue

@@ -28,7 +28,7 @@
                 :heading="5"
                 v-if="!collapsed"
               >
-                Arco Pro
+                Smms Web
               </a-typography-title>
             </a-space>
           </div>

+ 8 - 3
src/store/modules/user/index.ts

@@ -38,7 +38,7 @@ const useUserStore = defineStore('user', {
 
   actions: {
     switchRoles() {
-      return new Promise((resolve) => {
+      return new Promise(resolve => {
         this.role = this.role === 'user' ? 'admin' : 'user';
         resolve(this.role);
       });
@@ -63,8 +63,13 @@ const useUserStore = defineStore('user', {
     // Login
     async login(loginForm: LoginData) {
       try {
-        const res = await userLogin(loginForm);
-        setToken(res.data.token);
+        const params = {
+          name: loginForm.username,
+          password: loginForm.password,
+        };
+        const res = await userLogin(params);
+        setToken('test-token');
+        return res;
       } catch (err) {
         clearToken();
         throw err;

+ 75 - 71
src/views/login/components/login-form.vue

@@ -64,45 +64,46 @@
 </template>
 
 <script lang="ts" setup>
-  import { ref, reactive } from 'vue';
-  import { useRouter } from 'vue-router';
-  import { Message } from '@arco-design/web-vue';
-  import { ValidatedError } from '@arco-design/web-vue/es/form/interface';
-  import { useI18n } from 'vue-i18n';
-  import { useStorage } from '@vueuse/core';
-  import { useUserStore } from '@/store';
-  import useLoading from '@/hooks/loading';
-  import type { LoginData } from '@/api/user';
+import { ref, reactive } from 'vue';
+import { useRouter } from 'vue-router';
+import { Message } from '@arco-design/web-vue';
+import { ValidatedError } from '@arco-design/web-vue/es/form/interface';
+import { useI18n } from 'vue-i18n';
+import { useStorage } from '@vueuse/core';
+import { useUserStore } from '@/store';
+import useLoading from '@/hooks/loading';
+import type { LoginData } from '@/api/user';
 
-  const router = useRouter();
-  const { t } = useI18n();
-  const errorMessage = ref('');
-  const { loading, setLoading } = useLoading();
-  const userStore = useUserStore();
+const router = useRouter();
+const { t } = useI18n();
+const errorMessage = ref('');
+const { loading, setLoading } = useLoading();
+const userStore = useUserStore();
 
-  const loginConfig = useStorage('login-config', {
-    rememberPassword: true,
-    username: 'admin', // 演示默认值
-    password: 'admin', // demo default value
-  });
-  const userInfo = reactive({
-    username: loginConfig.value.username,
-    password: loginConfig.value.password,
-  });
+const loginConfig = useStorage('login-config', {
+  rememberPassword: true,
+  username: 'admin', // 演示默认值
+  password: 'admin', // demo default value
+});
+const userInfo = reactive({
+  username: loginConfig.value.username,
+  password: loginConfig.value.password,
+});
 
-  const handleSubmit = async ({
-    errors,
-    values,
-  }: {
-    errors: Record<string, ValidatedError> | undefined;
-    values: Record<string, any>;
-  }) => {
-    if (loading.value) return;
-    if (!errors) {
-      setLoading(true);
-      try {
-        await userStore.login(values as LoginData);
-        const { redirect, ...othersQuery } = router.currentRoute.value.query;
+const handleSubmit = async ({
+  errors,
+  values,
+}: {
+  errors: Record<string, ValidatedError> | undefined;
+  values: Record<string, any>;
+}) => {
+  if (loading.value) return;
+  if (!errors) {
+    setLoading(true);
+    try {
+      const res = await userStore.login(values as LoginData);
+      const { redirect, ...othersQuery } = router.currentRoute.value.query;
+      if (res.success) {
         router.push({
           name: (redirect as string) || 'Workplace',
           query: {
@@ -116,50 +117,53 @@
         // The actual production environment requires encrypted storage.
         loginConfig.value.username = rememberPassword ? username : '';
         loginConfig.value.password = rememberPassword ? password : '';
-      } catch (err) {
-        errorMessage.value = (err as Error).message;
-      } finally {
-        setLoading(false);
+      } else {
+        Message.error(res.message);
       }
+    } catch (err) {
+      errorMessage.value = (err as Error).message;
+    } finally {
+      setLoading(false);
     }
-  };
-  const setRememberPassword = (value: boolean) => {
-    loginConfig.value.rememberPassword = value;
-  };
+  }
+};
+const setRememberPassword = (value: boolean) => {
+  loginConfig.value.rememberPassword = value;
+};
 </script>
 
 <style lang="less" scoped>
-  .login-form {
-    &-wrapper {
-      width: 320px;
-    }
+.login-form {
+  &-wrapper {
+    width: 320px;
+  }
 
-    &-title {
-      color: var(--color-text-1);
-      font-weight: 500;
-      font-size: 24px;
-      line-height: 32px;
-    }
+  &-title {
+    color: var(--color-text-1);
+    font-weight: 500;
+    font-size: 24px;
+    line-height: 32px;
+  }
 
-    &-sub-title {
-      color: var(--color-text-3);
-      font-size: 16px;
-      line-height: 24px;
-    }
+  &-sub-title {
+    color: var(--color-text-3);
+    font-size: 16px;
+    line-height: 24px;
+  }
 
-    &-error-msg {
-      height: 32px;
-      color: rgb(var(--red-6));
-      line-height: 32px;
-    }
+  &-error-msg {
+    height: 32px;
+    color: rgb(var(--red-6));
+    line-height: 32px;
+  }
 
-    &-password-actions {
-      display: flex;
-      justify-content: space-between;
-    }
+  &-password-actions {
+    display: flex;
+    justify-content: space-between;
+  }
 
-    &-register-btn {
-      color: var(--color-text-3) !important;
-    }
+  &-register-btn {
+    color: var(--color-text-3) !important;
   }
+}
 </style>

+ 48 - 48
src/views/login/index.vue

@@ -1,13 +1,13 @@
 <template>
   <div class="container">
-    <div class="logo">
+    <!-- <div class="logo">
       <img
         alt="logo"
         src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/dfdba5317c0c20ce20e64fac803d52bc.svg~tplv-49unhts6dw-image.image"
       />
-      <div class="logo-text">Arco Design Pro</div>
+      <div class="logo-text">Smms Web</div>
     </div>
-    <LoginBanner />
+    <LoginBanner /> -->
     <div class="content">
       <div class="content-inner">
         <LoginForm />
@@ -20,62 +20,62 @@
 </template>
 
 <script lang="ts" setup>
-  import Footer from '@/components/footer/index.vue';
-  import LoginBanner from './components/banner.vue';
-  import LoginForm from './components/login-form.vue';
+import Footer from '@/components/footer/index.vue';
+import LoginBanner from './components/banner.vue';
+import LoginForm from './components/login-form.vue';
 </script>
 
 <style lang="less" scoped>
-  .container {
-    display: flex;
-    height: 100vh;
+.container {
+  display: flex;
+  height: 100vh;
 
-    .banner {
-      width: 550px;
-      background: linear-gradient(163.85deg, #1d2129 0%, #00308f 100%);
-    }
+  .banner {
+    width: 550px;
+    background: linear-gradient(163.85deg, #1d2129 0%, #00308f 100%);
+  }
 
-    .content {
-      position: relative;
-      display: flex;
-      flex: 1;
-      align-items: center;
-      justify-content: center;
-      padding-bottom: 40px;
-    }
+  .content {
+    position: relative;
+    display: flex;
+    flex: 1;
+    align-items: center;
+    justify-content: center;
+    padding-bottom: 40px;
+  }
 
-    .footer {
-      position: absolute;
-      right: 0;
-      bottom: 0;
-      width: 100%;
-    }
+  .footer {
+    position: absolute;
+    right: 0;
+    bottom: 0;
+    width: 100%;
   }
+}
 
-  .logo {
-    position: fixed;
-    top: 24px;
-    left: 22px;
-    z-index: 1;
-    display: inline-flex;
-    align-items: center;
+.logo {
+  position: fixed;
+  top: 24px;
+  left: 22px;
+  z-index: 1;
+  display: inline-flex;
+  align-items: center;
 
-    &-text {
-      margin-right: 4px;
-      margin-left: 4px;
-      color: var(--color-fill-1);
-      font-size: 20px;
-    }
+  &-text {
+    margin-right: 4px;
+    margin-left: 4px;
+    color: var(--color-fill-1);
+    font-size: 20px;
   }
+}
 </style>
 
 <style lang="less" scoped>
-  // responsive
-  @media (max-width: @screen-lg) {
-    .container {
-      .banner {
-        width: 25%;
-      }
-    }
-  }
+// responsive
+// @media (max-width: @screen-lg) {
+//   .container {
+//     .banner {
+//       width: 25%;
+//     }
+//   }
+// }
 </style>

+ 1 - 1
src/views/login/locale/en-US.ts

@@ -1,5 +1,5 @@
 export default {
-  'login.form.title': 'Login to Arco Design Pro',
+  'login.form.title': 'Login to Smms Web',
   'login.form.userName.errMsg': 'Username cannot be empty',
   'login.form.password.errMsg': 'Password cannot be empty',
   'login.form.login.errMsg': 'Login error, refresh and try again',

+ 1 - 1
src/views/login/locale/zh-CN.ts

@@ -1,5 +1,5 @@
 export default {
-  'login.form.title': '登录 Arco Design Pro',
+  'login.form.title': '登录 Smms Web',
   'login.form.userName.errMsg': '用户名不能为空',
   'login.form.password.errMsg': '密码不能为空',
   'login.form.login.errMsg': '登录出错,轻刷新重试',