├── .env.development ├── .env.production ├── README.MD ├── package.json ├── public └── index.html ├── readme ├── 00c7a5fd.png └── 33408b0f.png ├── src ├── App.tsx ├── assets │ └── style.css ├── components │ ├── Content.tsx │ ├── ContentForm.tsx │ └── NavBar.tsx ├── index.tsx └── utils │ └── custom-icon.tsx └── tsconfig.json /.env.development: -------------------------------------------------------------------------------- 1 | REACT_APP_API_BASE_URL = http://127.0.0.1:5000/ 2 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | GENERATE_SOURCEMAP = false 2 | REACT_APP_API_BASE_URL = / -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # 😲 `gsuite-user-auto-create` 2 | 3 | [![forthebadge](https://forthebadge.com/images/badges/made-with-typescript.svg)](https://forthebadge.com) 4 | 5 | ![](readme/33408b0f.png) 6 | ![](readme/00c7a5fd.png) 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gsuite-user-auto-create", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@ant-design/icons": "^4.4.0", 7 | "@testing-library/jest-dom": "^5.11.9", 8 | "@testing-library/react": "^11.2.3", 9 | "@testing-library/user-event": "^12.6.2", 10 | "@types/jest": "^26.0.20", 11 | "@types/node": "^12.19.15", 12 | "@types/react": "^16.14.2", 13 | "@types/react-dom": "^16.9.10", 14 | "antd": "^4.10.3", 15 | "axios": "^0.21.1", 16 | "copy-to-clipboard": "^3.3.1", 17 | "react": "^17.0.1", 18 | "react-dom": "^17.0.1", 19 | "react-scripts": "4.0.1", 20 | "typescript": "^4.1.3", 21 | "web-vitals": "^0.2.4" 22 | }, 23 | "scripts": { 24 | "start": "react-scripts start", 25 | "build": "react-scripts build", 26 | "test": "react-scripts test", 27 | "eject": "react-scripts eject" 28 | }, 29 | "eslintConfig": { 30 | "extends": [ 31 | "react-app", 32 | "react-app/jest" 33 | ] 34 | }, 35 | "browserslist": { 36 | "production": [ 37 | ">0.2%", 38 | "not dead", 39 | "not op_mini all" 40 | ], 41 | "development": [ 42 | "last 1 chrome version", 43 | "last 1 firefox version", 44 | "last 1 safari version" 45 | ] 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 13 | Google Workspace 14 | 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /readme/00c7a5fd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realByg/gsuite-user-auto-create/82cb8a75327cf50c81108ca0c0a0a33c0b19c8a1/readme/00c7a5fd.png -------------------------------------------------------------------------------- /readme/33408b0f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realByg/gsuite-user-auto-create/82cb8a75327cf50c81108ca0c0a0a33c0b19c8a1/readme/33408b0f.png -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Layout} from 'antd' 3 | 4 | import NavBar from './components/NavBar' 5 | import Content from './components/Content' 6 | 7 | export default function App() { 8 | return ( 9 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ) 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/assets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | overflow: hidden; 6 | height: 100vh; 7 | width: 100% !important 8 | } 9 | 10 | .background-white { 11 | background-color: #ffffff; 12 | } 13 | 14 | .text-white { 15 | color: #ffffff; 16 | } 17 | 18 | .justify-between { 19 | justify-content: space-between; 20 | } 21 | 22 | .justify-around { 23 | justify-content: space-around; 24 | } 25 | 26 | .align-center { 27 | align-items: center; 28 | } 29 | 30 | .display-flex { 31 | display: flex; 32 | } 33 | 34 | .text-center { 35 | text-align: center; 36 | } 37 | 38 | .lineOneFont { 39 | font-size: 60px; 40 | font-weight: 700; 41 | color: #ffffff 42 | } 43 | 44 | .lineTwoFont { 45 | font-size: 50px; 46 | font-weight: 400; 47 | color: #ffffff 48 | } 49 | 50 | @media screen and (max-width: 600px) { 51 | .hide-on-small { 52 | display: none; 53 | } 54 | 55 | .lineOneFont { 56 | font-size: 30px; 57 | } 58 | 59 | .lineTwoFont { 60 | font-size: 20px; 61 | font-weight: 300 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/components/Content.tsx: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react' 2 | import {Row, Col, Button, Space, Modal} from 'antd' 3 | 4 | import ContentForm from './ContentForm' 5 | 6 | export default function Content() { 7 | const contentBg = { 8 | background: 'url(https://i.loli.net/2020/01/22/Rgv3xJAVYN4n9Za.png) no-repeat', 9 | backgroundSize: 'cover', 10 | backgroundPosition: 'center', 11 | backgroundRepeat: 'no-repeat', 12 | height: '100vh' 13 | } 14 | 15 | const [modalVisible, setModalVisible] = useState(false) 16 | 17 | return ( 18 | 19 | 23 |
24 | 25 | Google Workspace 26 | 27 | 28 |
29 | 30 | 31 | 您所需的一切工具,都汇集在这里。 32 | 33 | 34 |
35 |
36 | 37 | 38 | 47 | 48 | setModalVisible(false)} 57 | > 58 | 59 | 60 | 61 | 70 | 71 |
72 | 73 |
74 | ) 75 | } 76 | -------------------------------------------------------------------------------- /src/components/ContentForm.tsx: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect} from 'react' 2 | import {Spin, Form, Button, Input, message, Row, Col, Select} from 'antd' 3 | import {CopyOutlined, KeyOutlined, MailOutlined} from '@ant-design/icons' 4 | import copy from 'copy-to-clipboard' 5 | import axios from 'axios' 6 | 7 | 8 | export default function ContentForm() { 9 | const [spinning, setSpinning] = useState(false) 10 | const [gsConfig, setGsConfig] = useState([ 11 | { 12 | domains: [], 13 | id: '0', 14 | name: '', 15 | }, 16 | ]) 17 | const [domains, setDomains] = useState([]) 18 | const [createdAccount, setCreatedAccount] = useState({ 19 | email: '', 20 | password: '', 21 | }) 22 | const [form] = Form.useForm() 23 | 24 | const createUser = (formData: object) => { 25 | setSpinning(true) 26 | axios({ 27 | method: 'post', 28 | url: 'createUser', 29 | baseURL: process.env.REACT_APP_API_BASE_URL, 30 | data: formData, 31 | }) 32 | .then(async (r: any) => { 33 | if (r.data.success) { 34 | message.success('账号创建成功') 35 | setCreatedAccount({ 36 | email: r.data.account.primaryEmail, 37 | password: r.data.account.password, 38 | }) 39 | } else { 40 | message.error(r.data.msg) 41 | form.resetFields() 42 | } 43 | }) 44 | .catch(async (e: any) => { 45 | message.error(e) 46 | }) 47 | .then(() => { 48 | setSpinning(false) 49 | }) 50 | } 51 | 52 | useEffect(() => { 53 | setSpinning(true) 54 | axios({ 55 | method: 'post', 56 | url: 'getGSConfig', 57 | baseURL: process.env.REACT_APP_API_BASE_URL, 58 | }) 59 | .then((r: any) => { 60 | setGsConfig(r.data) 61 | }) 62 | .catch(async (e: any) => { 63 | message.error(e) 64 | }) 65 | .then(() => { 66 | setSpinning(false) 67 | }) 68 | }, []) 69 | 70 | useEffect(() => { 71 | form.setFieldsValue({ 72 | 'email': { 73 | 'domain': domains[0] 74 | } 75 | }) 76 | }, [domains, form]) 77 | 78 | const CreatedForm = ( 79 |
83 | 84 | } 87 | suffix={ { 89 | copy(createdAccount.email) 90 | message.success('已复制邮箱') 91 | }} 92 | />} 93 | /> 94 | 95 | 96 | 97 | } 100 | suffix={ { 102 | copy(createdAccount.password) 103 | message.success('已复制密码') 104 | }} 105 | />} 106 | /> 107 | 108 | 109 | 110 | 118 | 119 |
120 | ) 121 | 122 | const CreateForm = ( 123 |
130 | 135 | 153 | 154 | 155 | 163 | 164 | 176 | 181 | 182 | 183 | 188 | 203 | 204 | 205 | 206 | 207 | 217 | 获取激活码 224 | // )} 225 | /> 226 | 227 | 228 | 229 | 235 | 236 |
237 | ) 238 | 239 | 240 | return ( 241 | 242 | 243 | 247 | {!!createdAccount.password ? CreatedForm : CreateForm} 248 | 249 | 250 | 251 | ) 252 | } 253 | -------------------------------------------------------------------------------- /src/components/NavBar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Row, Col, Space} from 'antd' 3 | import CustomIcon from '../utils/custom-icon' 4 | 5 | export default function NavBar() { 6 | const gsFont = { 7 | fontWeight: 500, 8 | fontSize: 24 9 | } 10 | 11 | return ( 12 | 13 | 16 |
17 | 18 | google 23 | 24 |
28 | | 29 |
30 | 31 |
35 | Google Workspace 36 |
37 |
38 | 39 | 40 | 47 | 48 | 54 | 55 | 61 | 62 | 68 | 69 | 76 | 77 | 84 | 85 |
86 | 87 |
88 | ) 89 | } 90 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | 5 | import 'antd/dist/antd.css' 6 | import './assets/style.css' 7 | 8 | ReactDOM.render( 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /src/utils/custom-icon.tsx: -------------------------------------------------------------------------------- 1 | import {createFromIconfontCN} from '@ant-design/icons' 2 | 3 | export default createFromIconfontCN({ 4 | scriptUrl: 'https://at.alicdn.com/t/font_2344266_q4e9zpytr8a.js' 5 | }) 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | --------------------------------------------------------------------------------