Skip to content

登录页静态布局

• 完成登录页面的静态布局,不接入接口逻辑 • 掌握大背景图 + 登录卡片这种经典后台登录页结构 • 理解 CSS 背景图铺满方案:cover / contain / 固定尺寸 • 学会用绝对定位 + transform 实现登录卡片垂直居中 • 熟悉 Ant Design 5 的 Form、Input、Button 基础用法 • 了解 Less 嵌套语法与 box-sizing 在项目中的实际作用

新建 global.less

  • styles/global.less
less
/* ============================== 全局样式 ============================== */

/* 统一全局盒模型为 border-box:元素宽高 = content + padding + border,避免布局计算异常 */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html,
body,
#root {
  width: 100%;
  height: 100%;
}

body {
  font-family:
    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
    Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
  -webkit-font-smoothing: antialiased;
}

main.tsx 引入全局global.less

ts

import { createRoot } from 'react-dom/client';
import App from './App.tsx';
import './styles/global.less';
createRoot(document.getElementById('root')!).render(<App />);

login页面的index.less

  • 新建 styles/login/index.less
less
/* ============================== 登录页样式 ============================== */

// 背景图地址(内网部署时可替换为本地静态资源)
@login-bg-url: "https://jsopy.oss-cn-beijing.aliyuncs.com/IMAGES/banner/0.jpg";

.login-page {
  position: relative;
  width: 100%;
  height: 100vh;
  overflow: hidden;

  /* ---------- CSS 背景图铺满页面的三种核心方案 ----------
   * 方案一:cover(生产环境采用)
   *   等比缩放至完全覆盖容器,可能裁剪部分图像,但无拉伸、无留白、无失真,
   *   适配任意屏幕尺寸。
   *
   * 方案二:contain(验证用,未采用)
   *   background-size: contain;
   *   等比缩放至完整显示整张图,屏幕比例与图片不一致时两侧/上下会留白。
   *
   * 方案三:固定尺寸(验证用,未采用)
   *   background-size: 1920px 1080px;
   *   背景图按固定像素铺放,屏幕大于该尺寸时背景无法铺满,会出现空白区域。
   * ------------------------------------------------------ */
  background-image: url(@login-bg-url);
  background-repeat: no-repeat;
  background-position: center center;
  background-size: cover;

  // 背景遮罩层:提升卡片可读性
  .login-mask {
    position: absolute;
    inset: 0;
    background: linear-gradient(
      135deg,
      rgba(8, 18, 38, 0.55) 0%,
      rgba(8, 18, 38, 0.25) 50%,
      rgba(8, 18, 38, 0.55) 100%
    );
  }

  // 登录卡片:绝对定位 + transform 垂直水平居中
  .login-card {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%); // 兼容主流桌面端浏览器的居中方案
    width: 400px;
    max-width: calc(100vw - 48px); // 小屏幕下不溢出
    padding: 40px 40px 32px;
    background: rgba(255, 255, 255, 0.96);
    border-radius: 12px;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.28);
    backdrop-filter: blur(6px);

    .login-header {
      margin-bottom: 32px;
      text-align: center;

      .login-title {
        margin-bottom: 8px;
        font-size: 24px;
        font-weight: 600;
        color: #1f2d3d;
        letter-spacing: 2px;
      }

      .login-subtitle {
        font-size: 14px;
        color: #8a97a8;
      }
    }

    .login-form {
      .login-submit-btn {
        height: 44px;
        font-size: 16px;
        letter-spacing: 4px;
      }
    }
  }
}

login页面布局

tsx
import { Button, Form, Input } from "antd";
import { LockOutlined, UserOutlined } from "@ant-design/icons";
import "@/styles/login/index.less";

/** 登录表单数据类型 */
interface LoginFormValues {
  username: string;
  password: string;
}

function LoginPage() {
  // 控制表单用的。可以获取表单的内容和校验结果
  const [form] = Form.useForm<LoginFormValues>();

  /** 提交登录(当前仅做前端校验,不接入后端接口) */
  const handleSubmit = (values: LoginFormValues) => {
    // console.log('登录表单校验通过:', values);
    //写几个form 方法
    form.validateFields().then(() => {
      console.log("登录表单校验通过77777:", values);

      // 获取单个字段值
      // const username = form.getFieldValue("username");
      // console.log("username:", username);

      // 表单赋值
      // form.setFieldsValue({
      //   username: "admin",
      //   password: "123456",
      // });
      //重置表单
      // form.resetFields();
    });
  };

  return (
    <div className="login-page">
      <div className="login-mask" />

      <div className="login-card">
        <div className="login-header">
          <h1 className="login-title">后台管理系统</h1>
          <p className="login-subtitle">欢迎登录,请使用您的账号</p>
        </div>

        <Form<LoginFormValues>
          form={form}
          className="login-form"
          size="large"
          onFinish={handleSubmit}
        >
          <Form.Item
            name="username"
            rules={[
              { required: true, message: "请输入用户名" },
              { min: 3, max: 20, message: "用户名长度需在 3-20 个字符之间" },
            ]}
          >
            <Input prefix={<UserOutlined />} placeholder="用户名" allowClear />
          </Form.Item>

          <Form.Item
            name="password"
            rules={[
              { required: true, message: "请输入密码" },
              { min: 6, max: 20, message: "密码长度需在 6-20 个字符之间" },
            ]}
          >
            <Input.Password prefix={<LockOutlined />} placeholder="密码" />
          </Form.Item>

          <Form.Item>
            <Button
              type="primary"
              htmlType="submit"
              block
              className="login-submit-btn"
            >
              登 录
            </Button>
          </Form.Item>
        </Form>
      </div>
    </div>
  );
}

export default LoginPage;