增加docker配置,美化了一下登录界面

Change-Id: I36f376e001f2718e5ededdd94c4cbc968f7ed5a2
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..d604a03
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,6 @@
+# .dockerignore
+node_modules
+npm-debug.log
+yarn-error.log
+dist
+build
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..2cdc894
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,14 @@
+# 构建阶段
+FROM node:16 as build
+WORKDIR /app
+COPY package*.json ./
+RUN npm install
+COPY . .
+RUN npm run build
+
+# 生产阶段
+FROM nginx:alpine
+COPY --from=build /app/dist  /usr/share/nginx/html
+COPY nginx.conf /etc/nginx/conf.d/default.conf
+EXPOSE 8000
+CMD ["nginx", "-g", "daemon off;"]
\ No newline at end of file
diff --git a/nginx.conf b/nginx.conf
new file mode 100644
index 0000000..577d11a
--- /dev/null
+++ b/nginx.conf
@@ -0,0 +1,24 @@
+upstream backend {
+    server backend:8080;  # 保持不变(Docker Compose中服务名即容器间通信域名)
+}
+
+server {
+    listen 8000;  # 修改为Docker Compose中映射的前端端口(与docker-compose.yml保持一致)
+    server_name _;  # 允许所有域名访问
+
+    # 静态资源处理
+    location / {
+        root /usr/share/nginx/html;
+        index index.html;
+        try_files $uri $uri/ /index.html;  # 适配SPA路由(如React/Vue)
+    }
+
+    # 反向代理API请求到后端
+    location /api/ {
+        proxy_pass http://backend/;  # 代理路径与后端API前缀匹配
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+    }
+}
\ No newline at end of file
diff --git a/src/pages/User/Login/index.tsx b/src/pages/User/Login/index.tsx
index a18f785..233cb3d 100644
--- a/src/pages/User/Login/index.tsx
+++ b/src/pages/User/Login/index.tsx
@@ -167,7 +167,18 @@
   }, []);
 
   return (
-    <div className={containerClassName}>
+    <div
+      className={containerClassName}
+      style={{
+        backgroundImage:
+          "url('https://images.unsplash.com/photo-1462331940025-496dfbfc7564?auto=format&fit=crop&w=1500&q=80')",
+        backgroundSize: 'cover',
+        backgroundPosition: 'center',
+        minHeight: '100vh',
+        position: 'relative',
+        overflow: 'hidden',
+      }}
+    >
       <Helmet>
         <title>
           {intl.formatMessage({
@@ -178,31 +189,86 @@
         </title>
       </Helmet>
       <Lang />
+      {/* 星空粒子特效层 */}
+      <div
+        style={{
+          position: 'absolute',
+          inset: 0,
+          zIndex: 0,
+          pointerEvents: 'none',
+          background: 'radial-gradient(ellipse at bottom, #1b2735 0%, #090a0f 100%)',
+        }}
+      >
+        {/* 可以集成粒子库如 tsParticles 或者简单用 CSS 动画 */}
+        <svg width="100%" height="100%">
+          {[...Array(60)].map((_, i) => (
+            <circle
+              key={i}
+              cx={Math.random() * 1600}
+              cy={Math.random() * 900}
+              r={Math.random() * 1.5 + 0.5}
+              fill="#fff"
+              opacity={Math.random() * 0.8 + 0.2}
+            />
+          ))}
+        </svg>
+      </div>
       <div
         style={{
           flex: '1',
           padding: '32px 0',
+          display: 'flex',
+          alignItems: 'center',
+          justifyContent: 'center',
+          minHeight: '100vh',
+          position: 'relative',
+          zIndex: 1,
         }}
       >
         <LoginForm
           contentStyle={{
-            minWidth: 280,
-            maxWidth: '75vw',
+            minWidth: 320,
+            maxWidth: 400,
+            background: 'rgba(25, 34, 54, 0.92)',
+            borderRadius: 16,
+            boxShadow: '0 8px 32px 0 rgba(31, 38, 135, 0.37)',
+            border: '1px solid rgba(255,255,255,0.18)',
+            padding: '32px 32px 24px 32px',
+            color: '#fff',
           }}
-          logo={<img alt="logo" src="/logo.svg" />}
-          title="Ant Design"
-          subTitle={intl.formatMessage({ id: 'pages.layouts.userLayout.title' })}
+          // logo={
+          //   <img
+          //     alt="logo"
+          //     src="/planet-logo.svg"
+          //     style={{
+          //       width: 64,
+          //       height: 64,
+          //       filter: 'drop-shadow(0 0 8px #fff8) drop-shadow(0 0 16px #6cf)',
+          //       marginBottom: 8,
+          //     }}
+          //   />
+          // }
+          title={
+            <span style={{ color: '#fff', fontWeight: 700, fontSize: 28, letterSpacing: 2 }}>
+              PT星球
+            </span>
+          }
+          subTitle={
+            <span style={{ color: '#b3c7f9', fontSize: 16 }}>
+              探索你的专属星球,畅享PT世界
+            </span>
+          }
           initialValues={{
             autoLogin: true,
           }}
-          actions={[
-            <FormattedMessage
-              key="loginWith"
-              id="pages.login.loginWith"
-              defaultMessage="其他登录方式"
-            />,
-            <ActionIcons key="icons" />,
-          ]}
+          // actions={[
+          //   <FormattedMessage
+          //     key="loginWith"
+          //     id="pages.login.loginWith"
+          //     defaultMessage="其他登录方式"
+          //   />,
+          //   <ActionIcons key="icons" />,
+          // ]}
           onFinish={async (values) => {
             await handleSubmit(values as API.LoginParams);
           }}
@@ -214,19 +280,28 @@
             items={[
               {
                 key: 'account',
-                label: intl.formatMessage({
-                  id: 'pages.login.accountLogin.tab',
-                  defaultMessage: '账户密码登录',
-                }),
+                label: (
+                  <span style={{ color: '#fff' }}>
+                    {intl.formatMessage({
+                      id: 'pages.login.accountLogin.tab',
+                      defaultMessage: '账户密码登录',
+                    })}
+                  </span>
+                ),
               },
               {
                 key: 'mobile',
-                label: intl.formatMessage({
-                  id: 'pages.login.phoneLogin.tab',
-                  defaultMessage: '手机号登录',
-                }),
+                label: (
+                  <span style={{ color: '#fff' }}>
+                    {intl.formatMessage({
+                      id: 'pages.login.phoneLogin.tab',
+                      defaultMessage: '手机号登录',
+                    })}
+                  </span>
+                ),
               },
             ]}
+            style={{ marginBottom: 24 }}
           />
 
           {code !== 200 && loginType === 'account' && (
@@ -244,7 +319,8 @@
                 initialValue="admin"
                 fieldProps={{
                   size: 'large',
-                  prefix: <UserOutlined />,
+                  prefix: <UserOutlined style={{ color: '#6cf' }} />,
+                  style: { background: 'rgba(255,255,255,0.08)', color: '#fff' },
                 }}
                 placeholder={intl.formatMessage({
                   id: 'pages.login.username.placeholder',
@@ -267,7 +343,8 @@
                 initialValue="admin123"
                 fieldProps={{
                   size: 'large',
-                  prefix: <LockOutlined />,
+                  prefix: <LockOutlined style={{ color: '#6cf' }} />,
+                  style: { background: 'rgba(255,255,255,0.08)', color: '#fff' },
                 }}
                 placeholder={intl.formatMessage({
                   id: 'pages.login.password.placeholder',
@@ -290,6 +367,8 @@
                   <ProFormText
                     style={{
                       float: 'right',
+                      background: 'rgba(255,255,255,0.08)',
+                      color: '#fff',
                     }}
                     name="code"
                     placeholder={intl.formatMessage({
@@ -319,6 +398,9 @@
                       cursor: 'pointer',
                       paddingLeft: '10px',
                       width: '100px',
+                      borderRadius: 8,
+                      boxShadow: '0 0 8px #6cf8',
+                      background: '#fff',
                     }}
                     preview={false}
                     onClick={() => getCaptchaCode()}
@@ -328,13 +410,16 @@
             </>
           )}
 
-          {code !== 200 && loginType === 'mobile' && <LoginMessage content="验证码错误" />}
+          {code !== 200 && loginType === 'mobile' && (
+            <LoginMessage content="验证码错误" />
+          )}
           {type === 'mobile' && (
             <>
               <ProFormText
                 fieldProps={{
                   size: 'large',
-                  prefix: <MobileOutlined />,
+                  prefix: <MobileOutlined style={{ color: '#6cf' }} />,
+                  style: { background: 'rgba(255,255,255,0.08)', color: '#fff' },
                 }}
                 name="mobile"
                 placeholder={intl.formatMessage({
@@ -365,7 +450,8 @@
               <ProFormCaptcha
                 fieldProps={{
                   size: 'large',
-                  prefix: <LockOutlined />,
+                  prefix: <LockOutlined style={{ color: '#6cf' }} />,
+                  style: { background: 'rgba(255,255,255,0.08)', color: '#fff' },
                 }}
                 captchaProps={{
                   size: 'large',
@@ -413,14 +499,23 @@
           <div
             style={{
               marginBottom: 24,
+              color: '#b3c7f9',
             }}
           >
-            <ProFormCheckbox noStyle name="autoLogin">
-              <FormattedMessage id="pages.login.rememberMe" defaultMessage="自动登录" />
+            <ProFormCheckbox>
+              <a
+                style={{
+                  float: 'right',
+                  color: '#fff',
+                  fontSize: 14,
+                }}>
+              <FormattedMessage id="pages.login.rememberMe"  defaultMessage="自动登录" />
+              </a>
             </ProFormCheckbox>
             <a
               style={{
                 float: 'right',
+                color: '#6cf',
               }}
             >
               <FormattedMessage id="pages.login.forgotPassword" defaultMessage="忘记密码" />