增加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="忘记密码" />