【山大会议】注册页的编写

渲染进程代码

src/Views 文件夹下,我们新建一个 Register 文件夹,其中是我们的注册页面。

index.jsx

首先,我们来书写注册页的入口文件

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

App.jsx

现在,我们将 App 组件进行补全

import {
    
     Button, Form, Input } from 'antd';
import React, {
    
     useEffect, useState } from 'react';
import './App.scss';
import bg from './bg.jpg';

export default function App() {
    
    
	const [sendCaptchaTick, setSendCaptchaTick] = useState(0);
	const [sendCaptchaInterval, setSendCaptchaInterval] = useState(null);
	useEffect(() => {
    
    
		return () => {
    
    
			if (sendCaptchaInterval) {
    
    
				clearInterval(sendCaptchaInterval);
				setSendCaptchaInterval(null);
			}
		};
	}, []);

	const [form] = Form.useForm();

	return (
		<div className='register' style={
    
    {
    
     backgroundImage: `url(${
      
      bg})` }}>
			<div className='container'>
				<div className='title'>山大会议 注册账号</div>
				<div className='inputs'>
					<Form onFinish={
    
    submitForm} autoComplete='off' form={
    
    form}>
						<Form.Item
							rules={
    
    [
								{
    
    
									required: true,
									message: '请输入注册用的昵称',
								},
								{
    
    
									pattern: /^[^@]+$/,
									message: '昵称中不允许出现"@"',
								},
							]}
							name={
    
    'username'}>
							<Input placeholder='请输入昵称' />
						</Form.Item>
						<Form.Item
							rules={
    
    [
								{
    
     required: true, message: '请输入密码' },
								{
    
    
									min: 6,
									message: '请输入长度超过6位的密码',
								},
							]}
							name={
    
    'password'}>
							<Input.Password placeholder='请输入密码' />
						</Form.Item>
						<Form.Item
							validateTrigger='onBlur'
							rules={
    
    [
								{
    
    
									required: true,
									message: '请再次输入密码',
								},
								({
     
      getFieldValue }) => ({
    
    
									validator(rule, value) {
    
    
										if (getFieldValue('password') === value) {
    
    
											return Promise.resolve();
										}
										return Promise.reject('两次输入的密码不一致');
									},
								}),
							]}
							name={
    
    'passwordCheck'}>
							<Input.Password placeholder='请再次输入密码' />
						</Form.Item>
						<Form.Item
							rules={
    
    [
								{
    
    
									required: true,
									message: '请输入山大邮箱',
								},
								{
    
    
									pattern: /^[^@]+$/,
									message: '请不要再次输入"@"',
								},
							]}
							name={
    
    'email'}>
							<Input placeholder='请输入山大邮箱' addonAfter='@mail.sdu.edu.cn' />
						</Form.Item>
						<Form.Item
							rules={
    
    [
								{
    
    
									required: true,
									message: '请输入验证码',
								},
							]}
							name={
    
    'captcha'}>
							<Input placeholder='请输入邮箱验证码' />
						</Form.Item>
						<Form.Item>
							<div style={
    
    {
    
     display: 'flex', justifyContent: 'space-around' }}>
								<Button
									disabled={
    
    sendCaptchaTick}
									onClick={
    
    () => {
    
    
										form.validateFields(['username', 'email'])
											.then(() => {
    
    
												sendCaptcha(
													setSendCaptchaTick,
													setSendCaptchaInterval
												);
											})
											.catch(() => {
    
    });
									}}>
									{
    
    sendCaptchaTick
										? `${
      
      sendCaptchaTick}秒后可再次发送`
										: '发送验证码'}
								</Button>
								<Button type='primary' htmlType='submit'>
									注册
								</Button>
							</div>
						</Form.Item>
					</Form>
				</div>
			</div>
		</div>
	);
}

function submitForm(values) {
    
    
	console.log(values);
}

function sendCaptcha(setSendCaptchaTick, setSendCaptchaInterval) {
    
    
	let sendCaptchaTick = 60;
	setSendCaptchaTick(sendCaptchaTick);
	const interval = setInterval(() => {
    
    
		setSendCaptchaTick(--sendCaptchaTick);
		if (sendCaptchaTick === 0) {
    
    
			clearInterval(interval);
			setSendCaptchaInterval(null);
		}
	}, 1000);
	setSendCaptchaInterval(interval);
}

同时为该组件编写样式文件:

// App.scss

@import '~antd/dist/antd.css';

* {
    
    
	margin: 0%;
}
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
    
    
	transition-delay: 111111s;
	transition: color 111111s ease-out, background-color 111111s ease-out;
}
.register {
    
    
	width: 100vw;
	height: 100vh;
	background-repeat: no-repeat;
	background-size: 100% 100%;
	display: flex;
	justify-content: center;
	align-items: center;

	.container {
    
    
		width: 50%;
		min-height: 60%;
		background-color: rgba($color: #000, $alpha: 0.5);
		transition: 300ms;
		border-radius: 1rem;
		color: white;
		display: flex;
		flex-direction: column;
		justify-content: center;
		align-items: center;

		.title {
    
    
			font-size: 2rem;
			user-select: none;
		}

		&:hover,
		&:focus {
    
    
			backdrop-filter: blur(10px);
		}

		.inputs {
    
    
			width: 70%;

			.ant-input-affix-wrapper.ant-input-password,
			.ant-input,
			input:-webkit-autofill,
			input:-webkit-autofill:hover,
			input:-webkit-autofill:focus,
			input:-webkit-autofill:active {
    
    
				background-color: rgba($color: #000000, $alpha: 0);
				color: white;
				border-top: none;
				border-left: none;
				border-right: none;
				box-shadow: none;
				font-size: 1.25rem;

				&::selection {
    
    
					background-color: rgba($color: #000, $alpha: 0.3);
				}

				.ant-input-password-icon {
    
    
					color: white;
				}
			}

			.ant-form-item-has-error :not(.ant-input-disabled):not(.ant-input-borderless) {
    
    
				&.ant-input {
    
    
					&,
					&:focus {
    
    
						background-color: rgba($color: #000000, $alpha: 0);
						box-shadow: none;
					}
				}
			}

			.ant-input-group-addon {
    
    
				color: white;
				background-color: rgba($color: #000000, $alpha: 0);
				// border-top: none;
				// border-right: none;
				border: none;
				user-select: none;
				font-size: 1.25rem;
			}
		}
	}
}

修改登录页代码

修改后的登录页代码如下:

  • App.jsx
import React from 'react';
import './App.scss';
import {
    
     DEVICE_TYPE, exchangeMediaDevice, updateAvailableDevices } from 'Utils/Store/actions';
import store from 'Utils/Store/store';
import Index from 'Components/Index/Index';

// INFO: 由于需要在所有组件挂载之前全局引入 electron ,故只能使用带有构造函数的类声明 App 组件
export default class App extends React.Component {
    
    
	constructor(props) {
    
    
		super(props);
		window.ipcRenderer = window.require('electron').ipcRenderer; // 全局引入 electron 模块
	}

	componentDidMount() {
    
    
		this.overwriteGetDisplayMedia();
		this.getUserMediaDevices();
	}

	render() {
    
    
		return (
			<div className='App'>
				<Index />
			</div>
		);
	}

	/**
	 * 重写 window.mediaDevices.getDisplayMedia() 方法
	 */
	overwriteGetDisplayMedia() {
    
    
		window.navigator.mediaDevices.getDisplayMedia = () => {
    
    
			return new Promise(async (resolve, reject) => {
    
    
				try {
    
    
					const source = await window.ipcRenderer.invoke('DESKTOP_CAPTURE');
					const stream = await window.navigator.mediaDevices.getUserMedia({
    
    
						audio: {
    
    
							mandatory: {
    
    
								chromeMediaSource: 'desktop',
								chromeMediaSourceId: source.id,
							},
						},
						video: {
    
    
							mandatory: {
    
    
								chromeMediaSource: 'desktop',
								chromeMediaSourceId: source.id,
							},
						},
					});
					resolve(stream);
				} catch (err) {
    
    
					reject(err);
				}
			});
		};
	}

	/**
	 * 获取用户多媒体设备
	 */
	getUserMediaDevices() {
    
    
		navigator.mediaDevices.enumerateDevices().then((devices) => {
    
    
			const generateDeviceJson = (device) => {
    
    
				const formerIndex = device.label.indexOf(' (');
				const latterIndex = device.label.lastIndexOf(' (');
				const {
    
     label, webLabel } = ((label, deviceId) => {
    
    
					switch (deviceId) {
    
    
						case 'default':
							return {
    
    
								label: label.replace('Default - ', ''),
								webLabel: label.replace('Default - ', '默认 - '),
							};
						case 'communications':
							return {
    
    
								label: label.replace('Communications - ', ''),
								webLabel: label.replace('Communications - ', '通讯设备 - '),
							};
						default:
							return {
    
     label: label, webLabel: label };
					}
				})(
					formerIndex === latterIndex
						? device.label
						: device.label.substring(0, latterIndex),
					device.deviceId
				);
				return {
    
     label, webLabel, deviceId: device.deviceId };
			};
			let videoDevices = [],
				audioDevices = [];
			for (const index in devices) {
    
    
				const device = devices[index];
				if (device.kind === 'videoinput') {
    
    
					videoDevices.push(generateDeviceJson(device));
				} else if (device.kind === 'audioinput') {
    
    
					audioDevices.push(generateDeviceJson(device));
				}
			}
			store.dispatch(updateAvailableDevices(DEVICE_TYPE.VIDEO_DEVICE, videoDevices));
			store.dispatch(updateAvailableDevices(DEVICE_TYPE.AUDIO_DEVICE, audioDevices));
			const lastVideoDevice = localStorage.getItem('usingVideoDevice');
			const lastAudioDevice = localStorage.getItem('usingAudioDevice');
			(() => {
    
    
				store.dispatch(exchangeMediaDevice(DEVICE_TYPE.VIDEO_DEVICE, videoDevices[0]));
				for (const device of videoDevices) {
    
    
					if (device.deviceId === lastVideoDevice) {
    
    
						store.dispatch(
							exchangeMediaDevice(DEVICE_TYPE.VIDEO_DEVICE, {
    
    
								key: device.deviceId,
								value: device.label,
								children: device.webLabel,
							})
						);
						return;
					}
				}
			})();
			(() => {
    
    
				store.dispatch(exchangeMediaDevice(DEVICE_TYPE.AUDIO_DEVICE, audioDevices[0]));
				for (const device of audioDevices) {
    
    
					if (device.deviceId === lastAudioDevice) {
    
    
						store.dispatch(
							exchangeMediaDevice(DEVICE_TYPE.AUDIO_DEVICE, {
    
    
								key: device.deviceId,
								value: device.label,
								children: device.webLabel,
							})
						);
						return;
					}
				}
			})();
		});
	}
}
  • App.scss
@import '~antd/dist/antd.css';

* {
    
    
	margin: 0;
	padding: 0;
}

.App {
    
    
	position: absolute;
	left: 1.25px;
	top: 1.25px;
	width: calc(100vw - 2.5px);
	height: calc(100vh - 2.5px);
	overflow: hidden;
	box-shadow: 0 0 1px rgba(0, 0, 0, 0.8);
}

::-webkit-scrollbar {
    
    
	width: 0.25rem; // 纵向滚动条宽度
	height: 0.25rem; // 横向滚动条宽度
}

::-webkit-scrollbar-thumb {
    
    
	background-color: #a8a8a8;
	border-radius: 0.25rem;
}

::-webkit-scrollbar-track {
    
    
	background-color: #eaeaea;
}

猜你喜欢

转载自blog.csdn.net/qq_53126706/article/details/125069233