Login.tsx 7.51 KB
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { App as AntApp, Button } from "antd";
import { UserOutlined, LockOutlined, DownOutlined } from "@ant-design/icons";
import { login } from "@/api/auth";
import { useAppDispatch } from "@/store";
import { loginSucceeded } from "@/store/authSlice";
import { COMPANIES } from "@/utils/data";

export default function Login() {
  const navigate = useNavigate();
  const dispatch = useAppDispatch();
  const { message } = AntApp.useApp();

  const [user, setUser] = useState("admin");
  const [pass, setPass] = useState("666666");
  const [companyId, setCompanyId] = useState("std");
  const [companyOpen, setCompanyOpen] = useState(false);
  const [submitting, setSubmitting] = useState(false);

  const company = COMPANIES.find((c) => c.id === companyId);

  const submit = async (e?: React.FormEvent) => {
    e?.preventDefault();
    if (!user || !pass) return;
    setSubmitting(true);
    try {
      const data = await login(user, pass, companyId);
      dispatch(
        loginSucceeded({
          token: data.accessToken,
          user: data.user,
          companyId,
          companyName: company?.name ?? companyId,
        })
      );
      message.success("登录成功");
      navigate("/home", { replace: true });
    } catch {
      // message.error already shown by interceptor
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <div
      style={{
        position: "fixed",
        inset: 0,
        background:
          "radial-gradient(ellipse at center, #424b60 0%, #2e3645 70%, #262d3a 100%)",
        display: "flex",
        flexDirection: "column",
        alignItems: "center",
        justifyContent: "center",
        fontFamily: "inherit",
      }}
    >
      <div style={{ width: 320, marginTop: -40 }}>
        <div
          style={{
            display: "flex",
            flexDirection: "column",
            alignItems: "center",
            marginBottom: 28,
          }}
        >
          <div style={{ marginBottom: 10 }}>
            <svg width="76" height="76" viewBox="0 0 76 76" fill="none">
              <rect x="8" y="8" width="34" height="34" stroke="rgba(255,255,255,0.95)" strokeWidth="2" />
              <rect x="22" y="22" width="34" height="34" stroke="rgba(255,255,255,0.85)" strokeWidth="2" />
              <rect x="36" y="36" width="32" height="32" stroke="rgba(255,255,255,0.7)" strokeWidth="2" />
              <circle cx="38" cy="38" r="4" stroke="#fff" strokeWidth="1.5" />
              <path d="M38 32v12M32 38h12" stroke="#fff" strokeWidth="1" />
            </svg>
          </div>
          <div
            style={{
              color: "#fff",
              fontSize: 22,
              fontWeight: 600,
              letterSpacing: 4,
              marginBottom: 2,
            }}
          >
            XLY-ERP
          </div>
          <div style={{ color: "rgba(255,255,255,0.55)", fontSize: 11, letterSpacing: 2 }}>
            印刷制造管理平台
          </div>
        </div>

        <form onSubmit={submit}>
          <div style={fieldWrap}>
            <span style={fieldIcon}>
              <UserOutlined style={{ color: "rgba(0,0,0,0.5)", fontSize: 14 }} />
            </span>
            <span style={fieldDivider} />
            <input
              value={user}
              onChange={(e) => setUser(e.target.value)}
              placeholder="请输入用户名"
              style={inputStyle}
            />
          </div>
          <div style={fieldWrap}>
            <span style={fieldIcon}>
              <LockOutlined style={{ color: "rgba(0,0,0,0.5)", fontSize: 14 }} />
            </span>
            <span style={fieldDivider} />
            <input
              type="password"
              value={pass}
              onChange={(e) => setPass(e.target.value)}
              placeholder="请输入密码"
              style={inputStyle}
            />
          </div>
          <div style={{ position: "relative", marginBottom: 14 }}>
            <button
              type="button"
              onClick={() => setCompanyOpen((v) => !v)}
              style={{
                ...fieldWrap,
                width: "100%",
                cursor: "pointer",
                textAlign: "left",
                paddingRight: 12,
              }}
            >
              <span style={{ flex: 1, color: "#1a2332", fontSize: 13, paddingLeft: 12 }}>
                {company?.name ?? "请选择公司名"}
              </span>
              <span style={{ color: "rgba(0,0,0,0.5)", display: "inline-flex", paddingRight: 4 }}>
                <DownOutlined
                  style={{
                    fontSize: 12,
                    transform: companyOpen ? "rotate(180deg)" : "none",
                    transition: "transform 0.15s",
                  }}
                />
              </span>
            </button>
            {companyOpen && (
              <div
                style={{
                  position: "absolute",
                  top: "calc(100% + 2px)",
                  left: 0,
                  right: 0,
                  background: "rgba(60,68,82,0.98)",
                  border: "1px solid rgba(255,255,255,0.1)",
                  zIndex: 10,
                }}
              >
                {COMPANIES.map((c, i) => {
                  const sel = c.id === companyId;
                  return (
                    <div
                      key={c.id}
                      onClick={() => {
                        setCompanyId(c.id);
                        setCompanyOpen(false);
                      }}
                      style={{
                        padding: "9px 12px",
                        color: "#fff",
                        fontSize: 13,
                        cursor: "pointer",
                        background: sel ? "var(--accent)" : "transparent",
                        borderTop: i === 0 ? "none" : "1px solid rgba(255,255,255,0.04)",
                      }}
                    >
                      {c.name}
                    </div>
                  );
                })}
              </div>
            )}
          </div>
          <Button
            type="primary"
            htmlType="submit"
            loading={submitting}
            block
            style={{
              height: 38,
              fontSize: 14,
              letterSpacing: 8,
              borderRadius: 0,
            }}
          >
            {submitting ? "登 录 中" : "登 录"}
          </Button>
        </form>
      </div>

      <div
        style={{
          position: "absolute",
          bottom: 24,
          left: 0,
          right: 0,
          textAlign: "center",
          color: "rgba(255,255,255,0.3)",
          fontSize: 11,
          letterSpacing: 1,
        }}
      >
        XLY 软件 · 印刷制造管理平台 · 版权所有 © 2017–2026
      </div>
    </div>
  );
}

const fieldWrap: React.CSSProperties = {
  display: "flex",
  alignItems: "center",
  height: 38,
  background: "rgba(255,255,255,0.95)",
  border: "1px solid rgba(255,255,255,0.15)",
  marginBottom: 10,
};
const fieldIcon: React.CSSProperties = {
  width: 36,
  display: "inline-flex",
  alignItems: "center",
  justifyContent: "center",
};
const fieldDivider: React.CSSProperties = {
  width: 1,
  height: 18,
  background: "rgba(0,0,0,0.12)",
};
const inputStyle: React.CSSProperties = {
  flex: 1,
  height: "100%",
  border: "none",
  background: "transparent",
  paddingLeft: 10,
  paddingRight: 10,
  fontSize: 13,
  color: "#1a2332",
  outline: "none",
  fontFamily: "inherit",
};