Merge "update nginx"
diff --git a/nginx.conf b/nginx.conf
index 8673316..90e9a4c 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -1,7 +1,3 @@
-upstream backend {
- server backend:5004; # 保持不变(Docker Compose中服务名即容器间通信域名)
-}
-
server {
listen 3004; # 修改为Docker Compose中映射的前端端口(与docker-compose.yml保持一致)
@@ -16,7 +12,7 @@
# 反向代理API请求到后端
location /api/ {
- proxy_pass http://backend/; # 代理路径与后端API前缀匹配
+ proxy_pass http://thunderhub-backend:5004; # 代理路径与后端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;
diff --git a/package.json b/package.json
index 4625852..9cf008a 100644
--- a/package.json
+++ b/package.json
@@ -68,6 +68,7 @@
"antd-style": "^3.6.2",
"classnames": "^2.5.1",
"dayjs": "^1.11.13",
+ "dva": "^3.0.0-alpha.1",
"fabric": "^6.4.0",
"highlight.js": "^11.10.0",
"lodash": "^4.17.21",
diff --git a/src/api/system/register.js b/src/api/system/register.js
new file mode 100644
index 0000000..64adc05
--- /dev/null
+++ b/src/api/system/register.js
@@ -0,0 +1,10 @@
+import request from '@/utils/request'
+
+// 用户注册
+export function register(data) {
+ return request({
+ url: '/register',
+ method: 'post',
+ data: data
+ })
+}
\ No newline at end of file
diff --git a/src/pages/register/index.jsx b/src/pages/register/index.jsx
new file mode 100644
index 0000000..a91d63b
--- /dev/null
+++ b/src/pages/register/index.jsx
@@ -0,0 +1,115 @@
+import React, { useState } from 'react'
+import { connect } from 'dva'
+import { history } from 'umi'
+import { Form, Input, Button, message } from 'antd'
+import { UserOutlined, LockOutlined } from '@ant-design/icons'
+import styles from './index.less'
+
+const RegisterPage = ({ dispatch, register }) => {
+ const [form] = Form.useForm()
+
+ const onFinish = values => {
+ dispatch({
+ type: 'register/submit',
+ payload: values
+ }).then(() => {
+ if (!register.error) {
+ message.success('注册成功')
+ history.push('/user/login')
+ } else {
+ message.error(register.error)
+ }
+ })
+ }
+
+ return (
+ <div className={styles.container}>
+ <div className={styles.content}>
+ <div className={styles.top}>
+ <div className={styles.header}>
+ <span className={styles.title}>用户注册</span>
+ </div>
+ </div>
+
+ <div className={styles.main}>
+ <Form
+ form={form}
+ name="register"
+ onFinish={onFinish}
+ scrollToFirstError
+ >
+ <Form.Item
+ name="username"
+ rules={[
+ { required: true, message: '请输入用户名!' },
+ { min: 4, message: '用户名至少4个字符' },
+ { max: 20, message: '用户名最多20个字符' },
+ { pattern: /^[a-zA-Z0-9_]+$/, message: '用户名只能包含字母、数字和下划线' }
+ ]}
+ >
+ <Input
+ prefix={<UserOutlined className={styles.prefixIcon} />}
+ placeholder="用户名"
+ size="large"
+ />
+ </Form.Item>
+
+ <Form.Item
+ name="password"
+ rules={[
+ { required: true, message: '请输入密码!' },
+ { min: 6, message: '密码至少6个字符' },
+ { max: 20, message: '密码最多20个字符' }
+ ]}
+ >
+ <Input.Password
+ prefix={<LockOutlined className={styles.prefixIcon} />}
+ type="password"
+ placeholder="密码"
+ size="large"
+ />
+ </Form.Item>
+
+ <Form.Item
+ name="confirmPassword"
+ dependencies={['password']}
+ rules={[
+ { required: true, message: '请确认密码!' },
+ ({ getFieldValue }) => ({
+ validator(_, value) {
+ if (!value || getFieldValue('password') === value) {
+ return Promise.resolve()
+ }
+ return Promise.reject(new Error('两次输入的密码不一致!'))
+ }
+ })
+ ]}
+ >
+ <Input.Password
+ prefix={<LockOutlined className={styles.prefixIcon} />}
+ type="password"
+ placeholder="确认密码"
+ size="large"
+ />
+ </Form.Item>
+
+ <Form.Item>
+ <Button
+ type="primary"
+ htmlType="submit"
+ size="large"
+ className={styles.submit}
+ loading={register.submitting}
+ block
+ >
+ 注册
+ </Button>
+ </Form.Item>
+ </Form>
+ </div>
+ </div>
+ </div>
+ )
+}
+
+export default connect(({ register }) => ({ register }))(RegisterPage)
\ No newline at end of file
diff --git a/src/pages/register/index.less b/src/pages/register/index.less
new file mode 100644
index 0000000..a6337d6
--- /dev/null
+++ b/src/pages/register/index.less
@@ -0,0 +1,52 @@
+.container {
+ display: flex;
+ flex-direction: column;
+ height: 100vh;
+ overflow: auto;
+ background: #f0f2f5;
+ background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
+ background-repeat: no-repeat;
+ background-position: center 110px;
+ background-size: 100%;
+}
+
+.content {
+ flex: 1;
+ padding: 32px 0;
+}
+
+.top {
+ text-align: center;
+}
+
+.header {
+ height: 44px;
+ line-height: 44px;
+
+ .title {
+ position: relative;
+ top: 2px;
+ color: rgba(0, 0, 0, 0.85);
+ font-weight: 600;
+ font-size: 33px;
+ font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
+ }
+}
+
+.main {
+ width: 368px;
+ margin: 0 auto;
+
+ @media screen and (max-width: 576px) {
+ width: 95%;
+ }
+
+ .prefixIcon {
+ color: rgba(0, 0, 0, 0.25);
+ }
+
+ .submit {
+ width: 100%;
+ margin-top: 24px;
+ }
+}
\ No newline at end of file
diff --git a/src/pages/register/model.js b/src/pages/register/model.js
new file mode 100644
index 0000000..891869e
--- /dev/null
+++ b/src/pages/register/model.js
@@ -0,0 +1,65 @@
+import * as registerApi from '@/api/system/register'
+
+export default {
+ namespace: 'register',
+
+ state: {
+ submitting: false,
+ error: null
+ },
+
+ effects: {
+ *submit({ payload }, { call, put }) {
+ yield put({
+ type: 'changeSubmitting',
+ payload: true
+ })
+
+ try {
+ const response = yield call(registerApi.register, payload)
+
+ if (response.code === 200) {
+ yield put({
+ type: 'registerSuccess'
+ })
+ } else {
+ yield put({
+ type: 'registerFailure',
+ payload: response.msg
+ })
+ }
+ } catch (error) {
+ yield put({
+ type: 'registerFailure',
+ payload: error.response?.data?.msg || '注册失败'
+ })
+ }
+
+ yield put({
+ type: 'changeSubmitting',
+ payload: false
+ })
+ }
+ },
+
+ reducers: {
+ changeSubmitting(state, { payload }) {
+ return {
+ ...state,
+ submitting: payload
+ }
+ },
+ registerSuccess(state) {
+ return {
+ ...state,
+ error: null
+ }
+ },
+ registerFailure(state, { payload }) {
+ return {
+ ...state,
+ error: payload
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/router/index.js b/src/router/index.js
new file mode 100644
index 0000000..8194827
--- /dev/null
+++ b/src/router/index.js
@@ -0,0 +1,9 @@
+import Register from '@/pages/register'
+
+// 在路由配置中添加注册路由
+{
+ path: '/user/register',
+ component; Register,
+ layout; false,
+ hideInMenu; true
+}
\ No newline at end of file
diff --git a/src/utils/request.js b/src/utils/request.js
new file mode 100644
index 0000000..536a3cc
--- /dev/null
+++ b/src/utils/request.js
@@ -0,0 +1,44 @@
+// src/utils/request.js
+import axios from 'axios'
+// import { getToken } from 'src/services/system/auth'
+import { message } from 'antd'
+
+// 创建axios实例
+const service = axios.create({
+ baseURL: process.env.REACT_APP_BASE_API, // api的base_url
+ timeout: 5000 // 请求超时时间
+})
+
+// request拦截器
+service.interceptors.request.use(
+ config => {
+ if (getToken()) {
+ config.headers['Authorization'] = 'Bearer ' + getToken()
+ }
+ return config
+ },
+ error => {
+ console.log(error)
+ Promise.reject(error)
+ }
+)
+
+// respone拦截器
+service.interceptors.response.use(
+ response => {
+ const res = response.data
+ if (res.code !== 200) {
+ message.error(res.msg || 'Error')
+ return Promise.reject(res)
+ } else {
+ return res
+ }
+ },
+ error => {
+ console.log('err' + error)
+ message.error(error.message)
+ return Promise.reject(error)
+ }
+)
+
+export default service
\ No newline at end of file