ThinkPHP与React集成技术方案专业报告

ThinkPHP与React集成技术方案专业报告

一、ThinkPHP与React集成架构设计

1.1 前后端分离架构

ThinkPHP作为后端API服务,React作为前端SPA应用,采用前后端分离架构:

+----------------+     +----------------+     +----------------+
|   React SPA    |     |   Nginx反向    |     |   ThinkPHP     |
|   前端应用     |     |    代理        |     |   后端API      |
|   (端口3000)   |     |   (端口80)     |     |   (端口8000)   |
+----------------+     +----------------+     +----------------+
        |                     |                     |
        | HTTP请求            | 反向代理            | API响应
        +-------------------> | +-----------------> |
        |                     |                     |
        |                     | <-----------------+ |
        | <-------------------+                     |
        |                     |                     |
+----------------+     +----------------+     +----------------+
|   浏览器       |     |   静态资源     |     |   数据库       |
|   (用户访问)   |     |   服务器       |     |   (MySQL)     |
+----------------+     +----------------+     +----------------+

1.2 技术栈选型

后端技术栈:

  • ThinkPHP 6.x
  • MySQL 8.0
  • Redis 6.0
  • JWT认证
  • Swagger API文档

前端技术栈:

  • React 18.x
  • TypeScript 4.x
  • Ant Design 5.x
  • Axios
  • React Router 6.x
  • Redux Toolkit / Zustand

二、ThinkPHP后端API开发

2.1 项目初始化与配置

# 创建ThinkPHP项目
composer create-project topthink/think tp-react-api

# 进入项目目录
cd tp-react-api

# 安装JWT扩展
composer require firebase/php-jwt

2.2 数据库配置

// config/database.php
return [
    'default' => env('database.driver', 'mysql'),
    'connections' => [
        'mysql' => [
            'type' => 'mysql',
            'hostname' => env('database.hostname', '127.0.0.1'),
            'database' => env('database.database', 'tp_react'),
            'username' => env('database.username', 'root'),
            'password' => env('database.password', ''),
            'hostport' => env('database.hostport', '3306'),
            'charset' => 'utf8mb4',
            'prefix' => '',
            'break_reconnect' => true,
            'deploy' => 0,
            'rw_separate' => false,
            'master_num' => 1,
            'slave_no' => '',
            'fields_strict' => true,
            'resultset_type' => 'array',
            'auto_timestamp' => false,
            'datetime_format' => 'Y-m-d H:i:s',
            'sql_explain' => false,
        ],
    ],
];

2.3 JWT认证中间件

// app/middleware/JwtAuth.php
<?php
declare (strict_types = 1);

namespace app\middleware;

use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use think\facade\Config;
use think\Response;

class JwtAuth
{
    public function handle($request, \Closure $next)
    {
        $token = $request->header('Authorization');
        
        if (!$token) {
            return json(['code' => 401, 'msg' => 'Token不存在']);
        }
        
        $token = str_replace('Bearer ', '', $token);
        
        try {
            $jwtConfig = Config::get('jwt');
            $decoded = JWT::decode($token, new Key($jwtConfig['secret'], $jwtConfig['algo']));
            $request->user = $decoded;
        } catch (\Exception $e) {
            return json(['code' => 401, 'msg' => 'Token无效']);
        }
        
        return $next($request);
    }
}

2.4 用户认证API

// app/controller/api/Auth.php
<?php
declare (strict_types = 1);

namespace app\controller\api;

use app\BaseController;
use app\model\User;
use Firebase\JWT\JWT;
use think\facade\Config;
use think\facade\Request;

class Auth extends BaseController
{
    public function login()
    {
        $username = Request::param('username');
        $password = Request::param('password');
        
        $user = User::where('username', $username)->find();
        
        if (!$user || !password_verify($password, $user->password)) {
            return json(['code' => 400, 'msg' => '用户名或密码错误']);
        }
        
        $payload = [
            'sub' => $user->id,
            'username' => $user->username,
            'iat' => time(),
            'exp' => time() + Config::get('jwt.expire'),
        ];
        
        $token = JWT::encode($payload, Config::get('jwt.secret'), Config::get('jwt.algo'));
        
        return json([
            'code' => 200,
            'msg' => '登录成功',
            'data' => [
                'token' => $token,
                'user' => [
                    'id' => $user->id,
                    'username' => $user->username,
                    'nickname' => $user->nickname,
                ]
            ]
        ]);
    }
    
    public function register()
    {
        $data = Request::param();
        
        $validate = new \app\validate\User();
        if (!$validate->check($data)) {
            return json(['code' => 400, 'msg' => $validate->getError()]);
        }
        
        $data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
        
        $user = User::create($data);
        
        return json([
            'code' => 200,
            'msg' => '注册成功',
            'data' => [
                'id' => $user->id,
                'username' => $user->username,
            ]
        ]);
    }
    
    public function userInfo()
    {
        $user = Request::user;
        
        $userInfo = User::field('id,username,nickname,email,avatar,created_at')
            ->find($user->sub);
            
        return json([
            'code' => 200,
            'msg' => '获取成功',
            'data' => $userInfo
        ]);
    }
}

2.5 用户管理API

// app/controller/api/User.php
<?php
declare (strict_types = 1);

namespace app\controller\api;

use app\BaseController;
use app\model\User;
use think\facade\Request;

class User extends BaseController
{
    public function index()
    {
        $page = Request::param('page', 1);
        $size = Request::param('size', 10);
        $keyword = Request::param('keyword', '');
        
        $query = User::field('id,username,nickname,email,avatar,status,created_at');
        
        if ($keyword) {
            $query->whereLike('username|nickname|email', "%{$keyword}%");
        }
        
        $users = $query->paginate([
            'list_rows' => $size,
            'page' => $page,
        ]);
        
        return json([
            'code' => 200,
            'msg' => '获取成功',
            'data' => [
                'list' => $users->items(),
                'total' => $users->total(),
                'page' => $users->currentPage(),
                'size' => $users->listRows(),
            ]
        ]);
    }
    
    public function update($id)
    {
        $data = Request::param();
        
        $user = User::find($id);
        if (!$user) {
            return json(['code' => 404, 'msg' => '用户不存在']);
        }
        
        if (isset($data['password'])) {
            $data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
        }
        
        $user->save($data);
        
        return json(['code' => 200, 'msg' => '更新成功']);
    }
    
    public function delete($id)
    {
        $user = User::find($id);
        if (!$user) {
            return json(['code' => 404, 'msg' => '用户不存在']);
        }
        
        $user->delete();
        
        return json(['code' => 200, 'msg' => '删除成功']);
    }
}

2.6 路由配置

// route/app.php
<?php
use think\facade\Route;

// 认证路由
Route::post('api/auth/login', 'api/auth/login');
Route::post('api/auth/register', 'api/auth/register');
Route::get('api/auth/userInfo', 'api/auth/userInfo');

// 用户管理路由
Route::get('api/users', 'api/user/index');
Route::put('api/users/:id', 'api/user/update');
Route::delete('api/users/:id', 'api/user/delete');

// 需要认证的路由组
Route::group(function () {
    Route::get('api/profile', 'api/user/profile');
    Route::put('api/profile', 'api/user/updateProfile');
})->middleware(\app\middleware\JwtAuth::class);

2.7 跨域中间件配置

// app/middleware/Cors.php
<?php
declare (strict_types = 1);

namespace app\middleware;

class Cors
{
    public function handle($request, \Closure $next)
    {
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Headers: Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-Requested-With');
        header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE');
        header('Access-Control-Allow-Credentials: true');
        
        if ($request->isOptions()) {
            return response();
        }
        
        return $next($request);
    }
}

三、React前端开发

3.1 项目初始化

# 创建React项目
npx create-react-app tp-react-frontend --template typescript

# 进入项目目录
cd tp-react-frontend

# 安装依赖
npm install antd axios react-router-dom @reduxjs/toolkit react-redux
npm install @types/react-router-dom -D

3.2 项目结构

src/
├── api/              # API接口
├── components/       # 公共组件
├── pages/           # 页面组件
├── store/           # 状态管理
├── utils/           # 工具函数
├── App.tsx
├── index.tsx
└── index.css

3.3 API请求封装

// src/api/request.ts
import axios from 'axios';
import { message } from 'antd';

const request = axios.create({
  baseURL: process.env.REACT_APP_API_BASE_URL || 'http://localhost:8000',
  timeout: 10000,
});

// 请求拦截器
request.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// 响应拦截器
request.interceptors.response.use(
  (response) => {
    const { data } = response;
    if (data.code === 200) {
      return data.data;
    } else {
      message.error(data.msg);
      return Promise.reject(new Error(data.msg));
    }
  },
  (error) => {
    if (error.response?.status === 401) {
      localStorage.removeItem('token');
      window.location.href = '/login';
    }
    message.error(error.response?.data?.msg || '请求失败');
    return Promise.reject(error);
  }
);

export default request;

3.4 用户认证API

// src/api/auth.ts
import request from './request';

export interface LoginParams {
  username: string;
  password: string;
}

export interface RegisterParams {
  username: string;
  password: string;
  nickname: string;
  email: string;
}

export interface UserInfo {
  id: number;
  username: string;
  nickname: string;
  email: string;
  avatar: string;
  created_at: string;
}

export const login = (params: LoginParams) => {
  return request.post('/api/auth/login', params);
};

export const register = (params: RegisterParams) => {
  return request.post('/api/auth/register', params);
};

export const getUserInfo = () => {
  return request.get('/api/auth/userInfo');
};

3.5 用户管理API

// src/api/user.ts
import request from './request';

export interface User {
  id: number;
  username: string;
  nickname: string;
  email: string;
  avatar: string;
  status: number;
  created_at: string;
}

export interface UserListParams {
  page?: number;
  size?: number;
  keyword?: string;
}

export interface UserListResponse {
  list: User[];
  total: number;
  page: number;
  size: number;
}

export const getUserList = (params: UserListParams) => {
  return request.get<UserListResponse>('/api/users', { params });
};

export const updateUser = (id: number, data: Partial<User>) => {
  return request.put(`/api/users/${id}`, data);
};

export const deleteUser = (id: number) => {
  return request.delete(`/api/users/${id}`);
};

3.6 状态管理

// src/store/authSlice.ts
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { getUserInfo, UserInfo } from '../api/auth';

interface AuthState {
  user: UserInfo | null;
  token: string | null;
  loading: boolean;
}

const initialState: AuthState = {
  user: null,
  token: localStorage.getItem('token'),
  loading: false,
};

export const fetchUserInfo = createAsyncThunk('auth/fetchUserInfo', async () => {
  const response = await getUserInfo();
  return response;
});

const authSlice = createSlice({
  name: 'auth',
  initialState,
  reducers: {
    setToken: (state, action) => {
      state.token = action.payload;
      localStorage.setItem('token', action.payload);
    },
    logout: (state) => {
      state.token = null;
      state.user = null;
      localStorage.removeItem('token');
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchUserInfo.pending, (state) => {
        state.loading = true;
      })
      .addCase(fetchUserInfo.fulfilled, (state, action) => {
        state.loading = false;
        state.user = action.payload;
      })
      .addCase(fetchUserInfo.rejected, (state) => {
        state.loading = false;
        state.token = null;
        localStorage.removeItem('token');
      });
  },
});

export const { setToken, logout } = authSlice.actions;
export default authSlice.reducer;
// src/store/index.ts
import { configureStore } from '@reduxjs/toolkit';
import authReducer from './authSlice';

export const store = configureStore({
  reducer: {
    auth: authReducer,
  },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

3.7 登录页面

// src/pages/Login.tsx
import React, { useState } from 'react';
import { Form, Input, Button, Card, message } from 'antd';
import { UserOutlined, LockOutlined } from '@ant-design/icons';
import { useNavigate } from 'react-router-dom';
import { login } from '../api/auth';
import { setToken } from '../store/authSlice';
import { useAppDispatch } from '../store/hooks';

const Login: React.FC = () => {
  const [loading, setLoading] = useState(false);
  const navigate = useNavigate();
  const dispatch = useAppDispatch();

  const onFinish = async (values: any) => {
    setLoading(true);
    try {
      const response = await login(values);
      dispatch(setToken(response.token));
      message.success('登录成功');
      navigate('/');
    } catch (error) {
      console.error(error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
      <Card title="系统登录" style={{ width: 400 }}>
        <Form
          name="login"
          initialValues={{ remember: true }}
          onFinish={onFinish}
          autoComplete="off"
        >
          <Form.Item
            name="username"
            rules={[{ required: true, message: '请输入用户名!' }]}
          >
            <Input prefix={<UserOutlined />} placeholder="用户名" />
          </Form.Item>

          <Form.Item
            name="password"
            rules={[{ required: true, message: '请输入密码!' }]}
          >
            <Input.Password prefix={<LockOutlined />} placeholder="密码" />
          </Form.Item>

          <Form.Item>
            <Button type="primary" htmlType="submit" loading={loading} style={{ width: '100%' }}>
              登录
            </Button>
          </Form.Item>
        </Form>
      </Card>
    </div>
  );
};

export default Login;

3.8 用户列表页面

// src/pages/UserList.tsx
import React, { useEffect, useState } from 'react';
import { Table, Button, Space, Input, Modal, message } from 'antd';
import { PlusOutlined, SearchOutlined } from '@ant-design/icons';
import { getUserList, deleteUser, User } from '../api/user';

const UserList: React.FC = () => {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(false);
  const [total, setTotal] = useState(0);
  const [page, setPage] = useState(1);
  const [size, setSize] = useState(10);
  const [keyword, setKeyword] = useState('');

  const columns = [
    {
      title: 'ID',
      dataIndex: 'id',
      key: 'id',
      width: 80,
    },
    {
      title: '用户名',
      dataIndex: 'username',
      key: 'username',
    },
    {
      title: '昵称',
      dataIndex: 'nickname',
      key: 'nickname',
    },
    {
      title: '邮箱',
      dataIndex: 'email',
      key: 'email',
    },
    {
      title: '状态',
      dataIndex: 'status',
      key: 'status',
      render: (status: number) => (status === 1 ? '正常' : '禁用'),
    },
    {
      title: '创建时间',
      dataIndex: 'created_at',
      key: 'created_at',
    },
    {
      title: '操作',
      key: 'action',
      render: (record: User) => (
        <Space size="middle">
          <Button type="link" size="small">
            编辑
          </Button>
          <Button
            type="link"
            size="small"
            danger
            onClick={() => handleDelete(record.id)}
          >
            删除
          </Button>
        </Space>
      ),
    },
  ];

  const fetchUsers = async () => {
    setLoading(true);
    try {
      const response = await getUserList({ page, size, keyword });
      setUsers(response.list);
      setTotal(response.total);
    } catch (error) {
      console.error(error);
    } finally {
      setLoading(false);
    }
  };

  const handleDelete = (id: number) => {
    Modal.confirm({
      title: '确认删除',
      content: '确定要删除该用户吗?',
      onOk: async () => {
        try {
          await deleteUser(id);
          message.success('删除成功');
          fetchUsers();
        } catch (error) {
          console.error(error);
        }
      },
    });
  };

  const handleSearch = () => {
    setPage(1);
    fetchUsers();
  };

  useEffect(() => {
    fetchUsers();
  }, [page, size]);

  return (
    <div>
      <div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between' }}>
        <Space>
          <Input
            placeholder="搜索用户名、昵称或邮箱"
            value={keyword}
            onChange={(e) => setKeyword(e.target.value)}
            style={{ width: 300 }}
            onPressEnter={handleSearch}
          />
          <Button type="primary" icon={<SearchOutlined />} onClick={handleSearch}>
            搜索
          </Button>
        </Space>
        <Button type="primary" icon={<PlusOutlined />}>
          新增用户
        </Button>
      </div>
      <Table
        columns={columns}
        dataSource={users}
        rowKey="id"
        loading={loading}
        pagination={{
          current: page,
          pageSize: size,
          total,
          showSizeChanger: true,
          showQuickJumper: true,
          showTotal: (total) => `共 ${total} 条`,
          onChange: (page, size) => {
            setPage(page);
            setSize(size || 10);
          },
        }}
      />
    </div>
  );
};

export default UserList;

3.9 路由配置

// src/App.tsx
import React, { useEffect } from 'react';
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import { useAppDispatch, useAppSelector } from './store/hooks';
import { fetchUserInfo } from './store/authSlice';
import Login from './pages/Login';
import UserList from './pages/UserList';
import Layout from './components/Layout';

const App: React.FC = () => {
  const dispatch = useAppDispatch();
  const { token } = useAppSelector((state) => state.auth);

  useEffect(() => {
    if (token) {
      dispatch(fetchUserInfo());
    }
  }, [token, dispatch]);

  return (
    <BrowserRouter>
      <Routes>
        <Route path="/login" element={!token ? <Login /> : <Navigate to="/" replace />} />
        <Route
          path="/*"
          element={token ? <Layout /> : <Navigate to="/login" replace />}
        />
      </Routes>
    </BrowserRouter>
  );
};

export default App;

3.10 主布局组件

// src/components/Layout.tsx
import React from 'react';
import { Layout, Menu } from 'antd';
import { Outlet, useNavigate, useLocation } from 'react-router-dom';
import { UserOutlined } from '@ant-design/icons';

const { Header, Content, Sider } = Layout;

const AppLayout: React.FC = () => {
  const navigate = useNavigate();
  const location = useLocation();

  const menuItems = [
    {
      key: '/',
      icon: <UserOutlined />,
      label: '用户管理',
    },
  ];

  const handleMenuClick = (key: string) => {
    navigate(key);
  };

  return (
    <Layout style={{ minHeight: '100vh' }}>
      <Header style={{ padding: 0, background: '#fff' }}>
        <div style={{ padding: '0 24px', fontSize: '18px', fontWeight: 'bold' }}>
          ThinkPHP + React 管理系统
        </div>
      </Header>
      <Layout>
        <Sider width={200} style={{ background: '#fff' }}>
          <Menu
            mode="inline"
            selectedKeys={[location.pathname]}
            items={menuItems}
            onClick={({ key }) => handleMenuClick(key)}
            style={{ height: '100%', borderRight: 0 }}
          />
        </Sider>
        <Layout style={{ padding: '24px' }}>
          <Content
            style={{
              padding: 24,
              margin: 0,
              background: '#fff',
              minHeight: 280,
            }}
          >
            <Outlet />
          </Content>
        </Layout>
      </Layout>
    </Layout>
  );
};

export default AppLayout;

四、部署与配置

4.1 环境变量配置

# .env.development
REACT_APP_API_BASE_URL=http://localhost:8000

# .env.production
REACT_APP_API_BASE_URL=https://api.yourdomain.com

4.2 Nginx配置

# ThinkPHP后端配置
server {
    listen 8000;
    server_name localhost;
    root /path/to/tp-react-api/public;
    index index.php index.html index.htm;

    location / {
        if (!-e $request_filename) {
            rewrite ^(.*)$ /index.php?s=$1 last;
            break;
        }
    }

    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }
}

# React前端配置
server {
    listen 80;
    server_name yourdomain.com;
    root /path/to/tp-react-frontend/build;
    index index.html index.htm;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location /api {
        proxy_pass http://localhost:8000;
        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;
    }
}

4.3 数据库迁移

-- 创建数据库
CREATE DATABASE tp_react CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 创建用户表
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL COMMENT '用户名',
  `password` varchar(255) NOT NULL COMMENT '密码',
  `nickname` varchar(50) DEFAULT NULL COMMENT '昵称',
  `email` varchar(100) DEFAULT NULL COMMENT '邮箱',
  `avatar` varchar(255) DEFAULT NULL COMMENT '头像',
  `status` tinyint(1) DEFAULT '1' COMMENT '状态:0-禁用,1-正常',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP,
  `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`),
  KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

-- 插入测试数据
INSERT INTO `user` (`username`, `password`, `nickname`, `email`) VALUES
('admin', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', '管理员', 'admin@example.com');

五、性能优化建议

5.1 后端性能优化

  1. 开启OPcache:生产环境务必开启OPcache扩展
  2. 配置缓存:使用Redis作为缓存驱动
  3. 数据库优化:为常用查询字段添加索引
  4. 开启路由缓存route_check_cache设置为true
  5. 使用连接池:配置数据库连接池和Redis连接池

5.2 前端性能优化

  1. 代码分割:使用React.lazy和Suspense实现路由懒加载
  2. 图片优化:使用WebP格式图片,配置图片压缩
  3. CDN加速:静态资源使用CDN加速
  4. 缓存策略:配置HTTP缓存头
  5. Tree Shaking:生产环境移除未使用的代码

5.3 安全建议

  1. HTTPS:生产环境使用HTTPS协议
  2. XSS防护:对用户输入进行转义和过滤
  3. CSRF防护:使用CSRF令牌验证
  4. SQL注入防护:使用预处理语句
  5. JWT安全:设置合理的token过期时间,使用HTTPS传输

六、总结

ThinkPHP与React的集成方案提供了完整的前后端分离开发体验。ThinkPHP作为后端API服务,提供了稳定的数据接口和业务逻辑处理;React作为前端SPA应用,提供了良好的用户体验和交互效果。通过JWT认证、Redux状态管理、Ant Design UI组件等技术,构建了现代化的Web应用架构。

该方案具有以下优势:

  • 前后端分离:前后端独立开发部署,提高开发效率
  • 技术栈成熟:ThinkPHP和React都是经过验证的成熟技术
  • 性能优良:前后端分离架构,前端SPA加载速度快
  • 扩展性强:模块化设计,便于功能扩展和维护
  • 生态丰富:拥有丰富的第三方库和工具支持

在实际项目中,可以根据具体需求进行定制化开发,如添加权限管理、数据统计、文件上传等功能模块。

版权声明:本文为JienDa博主的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
若内容若侵犯到您的权益,请发送邮件至:platform_service@jienda.com我们将第一时间处理!
所有资源仅限于参考和学习,版权归JienDa作者所有,更多请访问JienDa首页。

给TA赞助
共{{data.count}}人
人已赞助
后端

ThinkPHP 6.x 相比 5.x 性能提升详细报告

2025-12-3 17:16:26

后端

ThinkPHP 8.0.4 全面技术报告

2025-12-3 17:18:39

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索