Просмотр исходного кода

Merge branch 'feature_first_pages' of Web/contentCooper into master

jihuaqiang 2 недель назад
Родитель
Сommit
3d33b95c4d

+ 1 - 0
package.json

@@ -17,6 +17,7 @@
     "react": "^18.3.1",
     "react-dom": "^18.3.1",
     "react-router-dom": "^6.23.1",
+    "vite-plugin-svgr": "^4.3.0",
     "zustand": "^4.5.2"
   },
   "devDependencies": {

+ 1 - 1
src/components/layout/sidebar.tsx

@@ -8,7 +8,7 @@ const { Sider } = Layout;
 const getMenuItems = (routes: AdminRouterItem[]): any[] => {
   return routes.map(itm => {
     if (!itm.meta) return null
-    let children = null
+    let children: any[] = []
     if (itm.children) children = getMenuItems(itm.children)
     return children ? {
       ...itm.meta,

+ 12 - 5
src/router/index.tsx

@@ -4,6 +4,7 @@ import {
 } from "react-router-dom";
 import App from "../App";
 import { MenuItemType } from "antd/es/menu/interface";
+import LoginPage from "@src/views/login/login";
 
 
 export type AdminRouterItem = RouteObject & {
@@ -40,8 +41,14 @@ export const routes: AdminRouterItem[] = [
   ...await loadRouteModules()
 ]
 
-export default createBrowserRouter([{
-  path: "/",
-  element: <App />,
-  children: routes,
-}])
+export default createBrowserRouter([
+  {
+    path: "/",
+    element: <App />,
+    children: routes,
+  },
+  {
+    path: "/login",
+    element: <LoginPage />,
+  }
+])

+ 109 - 0
src/views/login/login.module.css

@@ -0,0 +1,109 @@
+.loginContainer {
+  display: flex;
+  min-height: 100vh;
+  background-color: #e8f0fe;
+  background-image: linear-gradient(to bottom, #e8f0fe 60%, #c9dbfd);
+  position: relative;
+  overflow: hidden;
+}
+
+.leftSection {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  padding-left: 10%;
+}
+
+.platformTitle {
+  font-size: 2.2rem;
+  font-weight: bold;
+  margin-bottom: 1.5rem;
+  color: #333;
+}
+
+.illustration {
+  max-width: 500px;
+  position: relative;
+}
+
+.rightSection {
+  flex: 1;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 2rem;
+}
+
+.loginBox {
+  background-color: white;
+  border-radius: 10px;
+  padding: 2rem;
+  width: 100%;
+  max-width: 450px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+}
+
+.tabsContainer :global(.ant-tabs-nav::before) {
+  border-bottom: none !important;
+}
+
+.tabsContainer :global(.ant-tabs-tab) {
+  padding: 12px 16px;
+  font-size: 16px;
+}
+
+.tabsContainer :global(.ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn) {
+  color: #1890ff;
+  font-weight: 500;
+}
+
+.phonePrefix {
+  width: 80px;
+  text-align: center;
+  color: #555;
+}
+
+.phoneInput {
+  width: 100%;
+  border-radius: 4px;
+  font-size: 16px;
+}
+
+.codeRow {
+  display: flex;
+  gap: 12px;
+}
+
+.codeInput {
+  flex: 1;
+}
+
+.getCodeBtn {
+  width: 120px;
+  background-color: #f5f5f5;
+  border: none;
+  color: #1890ff;
+  font-weight: 500;
+}
+
+.loginBtn {
+  height: 48px;
+  font-size: 16px;
+  font-weight: 500;
+  margin-top: 16px;
+  background-color: #1890ff;
+}
+
+.decorationBottom {
+  position: absolute;
+  bottom: 0;
+  right: 0;
+  width: 25%;
+}
+
+@media (max-width: 992px) {
+  .leftSection {
+    display: none;
+  }
+} 

+ 214 - 0
src/views/login/login.tsx

@@ -0,0 +1,214 @@
+import React, { useState } from 'react';
+import { Form, Input, Button, Tabs, message } from 'antd';
+import { LockOutlined, UserOutlined } from '@ant-design/icons';
+import styles from './login.module.css';
+import { sendCode, loginBySendCode, login } from '../../http/sso';
+
+const LoginPage: React.FC = () => {
+  const [form] = Form.useForm();
+  const [loading, setLoading] = useState(false);
+  const [sendingCode, setSendingCode] = useState(false);
+  const [countdown, setCountdown] = useState(0);
+  const [accountForm] = Form.useForm();
+  const [accountLoading, setAccountLoading] = useState(false);
+
+  const handlePhoneLogin = async (values: any) => {
+    try {
+      setLoading(true);
+      const { phone, code } = values;
+      const success = await loginBySendCode(phone, code);
+      if (success) {
+        // Redirect will be handled by the login function
+      }
+    } catch (error) {
+      message.error('登录失败,请重试');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleSendCode = async () => {
+    try {
+      const phone = form.getFieldValue('phone');
+      if (!phone) {
+        message.error('请输入手机号');
+        return;
+      }
+      if (!/^1[3-9]\d{9}$/.test(phone)) {
+        message.error('请输入正确的手机号');
+        return;
+      }
+
+      setSendingCode(true);
+      const success = await sendCode(phone);
+      if (success) {
+        let count = 60;
+        setCountdown(count);
+        const timer = setInterval(() => {
+          count -= 1;
+          setCountdown(count);
+          if (count <= 0) {
+            clearInterval(timer);
+            setCountdown(0);
+          }
+        }, 1000);
+      }
+    } catch (error) {
+      message.error('发送验证码失败,请重试');
+    } finally {
+      setSendingCode(false);
+    }
+  };
+
+  const handleAccountLogin = async (values: any) => {
+    try {
+      setAccountLoading(true);
+      const { account, password } = values;
+      const success = await login(account, password);
+      if (success) {
+        // Redirect will be handled by the login function
+      }
+    } catch (error) {
+      message.error('登录失败,请重试');
+    } finally {
+      setAccountLoading(false);
+    }
+  };
+
+  return (
+    <div className={styles.loginContainer}>
+      <div className={styles.leftSection}>
+        <div className={styles.platformTitle}>票圈内容合作平台</div>
+        <div className={styles.illustration}>
+          {/* You can add an illustration image here */}
+        </div>
+      </div>
+      
+      <div className={styles.rightSection}>
+        <div className={styles.loginBox}>
+          <h1 className={styles.platformTitle}>票圈内容合作平台</h1>
+          
+          <div className={styles.tabsContainer}>
+            <Tabs 
+              defaultActiveKey="phone"
+              centered
+              items={[
+                {
+                  key: 'phone',
+                  label: '验证码登录',
+                  children: (
+                    <Form
+                      form={form}
+                      name="phone_login"
+                      onFinish={handlePhoneLogin}
+                      layout="vertical"
+                      size="large"
+                    >
+                      <Form.Item
+                        name="phone"
+                        rules={[
+                          { required: true, message: '请输入手机号' },
+                          { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号' }
+                        ]}
+                      >
+                        <Input 
+                          addonBefore={<div className={styles.phonePrefix}>+86</div>}
+                          placeholder="手机号"
+                          className={styles.phoneInput}
+                        />
+                      </Form.Item>
+
+                      <Form.Item
+                        name="code"
+                        rules={[{ required: true, message: '请输入验证码' }]}
+                      >
+                        <div className={styles.codeRow}>
+                          <Input 
+                            prefix={<LockOutlined />}
+                            placeholder="验证码"
+                            className={styles.codeInput}
+                          />
+                          <Button 
+                            onClick={handleSendCode}
+                            disabled={countdown > 0}
+                            loading={sendingCode}
+                            className={styles.getCodeBtn}
+                          >
+                            {countdown > 0 ? `${countdown}s` : '获取验证码'}
+                          </Button>
+                        </div>
+                      </Form.Item>
+
+                      <Form.Item>
+                        <Button 
+                          type="primary" 
+                          htmlType="submit" 
+                          block 
+                          loading={loading}
+                          className={styles.loginBtn}
+                        >
+                          登录
+                        </Button>
+                      </Form.Item>
+                    </Form>
+                  ),
+                },
+                {
+                  key: 'account',
+                  label: '账号登录',
+                  children: (
+                    <Form
+                      form={accountForm}
+                      name="account_login"
+                      onFinish={handleAccountLogin}
+                      layout="vertical"
+                      size="large"
+                    >
+                      <Form.Item
+                        name="account"
+                        rules={[{ required: true, message: '请输入账号' }]}
+                      >
+                        <Input 
+                          prefix={<UserOutlined />}
+                          placeholder="账号"
+                        />
+                      </Form.Item>
+
+                      <Form.Item
+                        name="password"
+                        rules={[{ required: true, message: '请输入密码' }]}
+                      >
+                        <Input.Password 
+                          prefix={<LockOutlined />}
+                          placeholder="密码"
+                        />
+                      </Form.Item>
+
+                      <Form.Item>
+                        <Button 
+                          type="primary" 
+                          htmlType="submit" 
+                          block 
+                          loading={accountLoading}
+                          className={styles.loginBtn}
+                        >
+                          登录
+                        </Button>
+                      </Form.Item>
+                    </Form>
+                  ),
+                }
+              ]}
+            />
+          </div>
+        </div>
+      </div>
+      
+      <div className={styles.decorationBottom}>
+        {/* You can add a decoration image here */}
+      </div>
+    </div>
+  );
+};
+
+export default LoginPage;

+ 12 - 0
src/views/test/index.tsx

@@ -0,0 +1,12 @@
+import React from 'react';
+
+const TestPage: React.FC = () => {
+  return (
+    <div>
+      <h1>Test Page</h1>
+    </div>
+  );
+};
+
+export default TestPage;
+

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
tsconfig.node.tsbuildinfo


+ 2 - 0
vite.config.d.ts

@@ -0,0 +1,2 @@
+declare const _default: import("vite").UserConfig;
+export default _default;

+ 31 - 0
vite.config.js

@@ -0,0 +1,31 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react-swc';
+import path, { resolve } from 'path';
+import { fileURLToPath } from 'node:url';
+var __filename = fileURLToPath(import.meta.url);
+var __dirname = path.dirname(__filename);
+// https://vitejs.dev/config/
+export default defineConfig({
+    plugins: [react()],
+    build: {
+        target: "esnext",
+    },
+    resolve: {
+        alias: {
+            '@src': resolve(__dirname, 'src'),
+            '@assets': resolve(__dirname, 'assets'),
+            '@': resolve(__dirname, '.'),
+        }
+    },
+    server: {
+        host: '0.0.0.0',
+        port: 3305,
+        proxy: {
+            '/api': {
+                target: 'https://testadmin.piaoquantv.com/',
+                changeOrigin: true,
+                rewrite: function (path) { return path.replace(/^\/api/, ''); },
+            }
+        }
+    }
+});

Разница между файлами не показана из-за своего большого размера
+ 535 - 221
yarn.lock


Некоторые файлы не были показаны из-за большого количества измененных файлов