Commit 15fe7465 authored by Ruizi Yu's avatar Ruizi Yu
Browse files

firstly add the program

parent 6b00473e
/* eslint-env node */
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:react-hooks/recommended',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: true,
tsconfigRootDir: __dirname,
},
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
'@typescript-eslint/no-non-null-assertion': 'off',
},
}
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
{
"name": "chat-room",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.15.0",
"swr": "^2.2.2"
},
"devDependencies": {
"@types/react": "^18.2.14",
"@types/react-dom": "^18.2.6",
"@typescript-eslint/eslint-plugin": "^5.61.0",
"@typescript-eslint/parser": "^5.61.0",
"@vitejs/plugin-react": "^4.0.1",
"eslint": "^8.44.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.1",
"typescript": "^5.0.2",
"vite": "^4.4.0"
}
}
import { useState } from 'react'
import { Route, Routes } from 'react-router-dom'
import SetName from './pages/SetName/SetName'
import ChatRoom from './pages/ChatRoom/ChatRoom'
import './App.css'
function App() {
return(
<>
<Routes>
<Route path="/" element={<SetName />} />
<Route path="/index" element={<ChatRoom />} />
</Routes>
</>
)
}
export default App
\ No newline at end of file
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import { HashRouter } from 'react-router-dom'
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<HashRouter>
<App />
</HashRouter>
</React.StrictMode>,
)
body {
margin: 0;
padding: 0;
background: url('https://pica.zhimg.com/80/v2-6751d05491af76c00c1a2a9102ac2cc4_1440w.webp?source=1940ef5c') no-repeat center center fixed;
background-size: cover;
overflow-x: hidden;
}
.container
{
display: flex;
height:100vh;
}
.roomList
{
width: 25%;
border: 2px solid rgba(255, 255, 255, 0.5);
background: transparent;
backdrop-filter: blur(8px);
display:flex;
flex-direction:column;
}
.roomlist
{
overflow: auto;
}
.messagelist
{
flex:1;
border-bottom: 2px solid rgba(255, 255, 255, 0.5);
border-right: 2px solid rgba(255, 255, 255, 0.5);
border-top: 2px solid rgba(255, 255, 255, 0.5);
background: transparent;
backdrop-filter: blur(5px);
/* overflow: auto; */
display:flex;
flex-direction:column;
}
.room
{
background: transparent;
text-align: left;
border-radius: 5px;
color: aliceblue;
margin: 5px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* .room_message
{
} */
.room div
{
display:flex;
justify-content: space-between;
}
.room p
{
margin-left: 10px;
margin-right: 10px;
}
.room:hover
{
background-color:#ffffff40;
}
.listTitle
{
border-bottom: 2px solid rgba(255, 255, 255, 0.5);
background-color:#ffffff40;
/* background-color:#ffffffc1; */
color: aliceblue;
padding-left: 10px;
}
.listTitle.room_list
{
display:flex;
justify-content: space-between;
}
.add_room
{
border-radius: 10px;
border-color:#ffffffc1;
background: transparent;
background-color:#ffffffa2;
background-color:#ffffff40;
padding-bottom: 0;
padding-top: 0;
line-height: 1;
color: aliceblue;
}
.add_room:hover
{
background-color:#ffffffa2;
}
.chatroom
{
height:80vh;
border-bottom:2px solid rgba(255, 255, 255, 0.5);
overflow: auto;
}
.input
{
flex:1;
background: transparent;
background-color:#ffffff40;
padding:2vh;
display:flex;
justify-content: space-between;
}
.input_blank
{
background: transparent;
border-radius: 1vh;
width:90%;
}
.input_blank:hover
{
background-color:#ffffff40;
}
.submit
{
border-radius: 10px;
border-color:#ffffffc1;
background: transparent;
background-color:#ffffffa2;
background-color:#ffffff40;
padding-bottom: 0;
padding-top: 0;
line-height: 1;
color: aliceblue;
}
.submit:hover
{
background-color:#ffffffa2;
}
.message_item
{
display:flex;
}
.head_photo
{
background-color: aliceblue;
margin: 10px;
text-align: center;
border-radius: 4px;
height: 8vh;
width: 8vh;
}
.head
{
display:flex;
flex-direction: column;
}
.nickname
{
flex:1;
color: aliceblue;
font-size: 0.9em;
display: flex;
justify-content: space-between;
padding-right: 20px;
}
.message_time
{
color: rgb(143, 144, 144);
}
.message
{
margin-right: 20px;
display: inline-block;
background-color: #f2f2f2;
padding-left: 10px;
padding-right: 10px;
border-radius: 4px;
min-width: 50px;
}
.context-menu
{
position: fixed;
top: 0;
left: 0;
background: #f1f1f1;
border: 1px solid #ccc;
padding: 8px;
border-radius: 5px;
cursor:pointer;
}
\ No newline at end of file
import React , { useState } from "react";
import "./ChatRoom.css";
interface RoomEntryProps {
roomId: number;
roomName: string;
lastMessage: MessageProps | null;
onDelete: (index: number) => void;
}
function RoomEntry (props:RoomEntryProps) {
const [isContextMenuVisible, setContextMenuVisible] = useState(false);
const [contextMenuStyle, setContextMenuStyle] = useState({});
function handleContextMenu(e: { preventDefault: () => void; button: number; clientY: any; clientX: any; }) {
e.preventDefault(); // 阻止默认的右键菜单
if (e.button === 2) {
const style = {
top: `${e.clientY}px`,
left: `${e.clientX}px`,
};
// 显示右键菜单
setContextMenuStyle(style);
setContextMenuVisible(true);
}
else if (e.button === 1)
{
}
}
function handleDeleteRoom() {
setContextMenuVisible(false);
props.onDelete(props.roomId);
}
return(
<>
<div className="room" onContextMenu={handleContextMenu}>
<div>
<p>房间</p>
<p>时间</p>
</div>
<p>{props.lastMessage?.sender??'昵称'}</p>
</div>
{isContextMenuVisible && (
<div className="context-menu" style={contextMenuStyle}>
<div onClick={handleDeleteRoom}>删除房间</div>
</div>
)}
</>
);
}
interface MessageProps {
messageId: number;
roomId: number;
sender: string;
content: string;
time: number;
}
function MessageItem (props:MessageProps) {
const formattedTime = new Date(props.time).toLocaleString();
return(
<>
<div className="message_item">
<div className="head_photo">
<p>头像</p>
</div>
<div className="head">
<div className="nickname">
<p>{props.sender}:</p>
<p className="message_time">{formattedTime}</p>
</div>
<div>
<div className="message">
<p>{props.content}</p>
</div>
</div>
</div>
</div>
</>
);
}
export default function ChatRoom () {
type Entry = number;
const [roomEntries, setRoomEntries] = useState<Entry[]>([]);
type Messages = {
content: string;
time: number;
};
const [messages, setMessages] = useState<Messages[]>([]);
var text = "";
function addRoom(event: { preventDefault: () => void; }) {
event.preventDefault();
setRoomEntries(prevEntries => [...prevEntries, prevEntries.length]);
}
function handleDeleteRoom(index: number) {
setRoomEntries(prevEntries => prevEntries.filter((_, i) => i !== index)); // 通过过滤删除指定索引的房间
}
function messageChange(event: { target: { value: string; }; preventDefault: () => void; }) {
text = event.target.value;
event.preventDefault();
}
function handleSubmit(event: { preventDefault: () => void; }) {
const currentTime = Date.now();
const newMessage = {
content:text,
time:currentTime
}
event.preventDefault();
setMessages(prevMessages => [...prevMessages, newMessage]);
console.log(messages);
const inputElement = document.querySelector('.input_blank') as HTMLInputElement;
if (inputElement) {
inputElement.value = '';
}
}
return(
<>
<div className="container">
{/* 消息列表 */}
<div className = "roomList">
<div className="listTitle room_list">
<h3>昵称</h3>
<button className="add_room" onClick={addRoom}>新建房间</button>
</div>
<div className = "roomlist">
{roomEntries.map((_,index) => (
<RoomEntry
key={index}
roomId={0}
onDelete={handleDeleteRoom}
roomName={""}
lastMessage={null}
/>
))}
</div>
</div>
<div className = "messagelist">
<div className="listTitle">
<h3>房间一</h3>
</div>
<div className='chatroom'>
{messages.map((message,index) => (
<MessageItem
messageId={index}
roomId={0}
sender={"昵称"}
content={message.content}
time={message.time}
/>
))}
</div>
<div className="input">
<input type="text" required className="input_blank" onChange={messageChange} />
<button className="submit" onClick={handleSubmit}>发送</button>
</div>
</div>
</div>
</>
);
}
\ No newline at end of file
body {
margin: 0;
padding: 0;
background: url('https://pica.zhimg.com/80/v2-6751d05491af76c00c1a2a9102ac2cc4_1440w.webp?source=1940ef5c') no-repeat center center fixed;
background-size: cover;
overflow-x: hidden;
}
section {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
width: 100%;
background: url('image.webp') no-repeat;
background-position: center;
background-size: cover;
}
.form-box {
position: relative;
width: 400px;
height: 450px;
background: transparent;
border: 2px solid rgba(255, 255, 255, 0.5);
backdrop-filter: blur(8px);
border-radius: 10px;
display: flex;
justify-content: center;
align-items: center;
}
h2 {
font-size: 30px;
color: #fff;
text-align: center;
}
.inputbox {
position: relative;
margin: 30px 0;
width: 310px;
border-bottom: 2px solid #fff;
}
.inputbox label {
position: absolute;
top: 50%;
left: 5px;
transform: translateY(-50%);
color: #fff;
font-size: 1em;
pointer-events: none;
transition: 0.5s;
}
input:focus ~ label,
input:valid ~ label {
top: -5px;
}
.inputbox input {
width: 100%;
height: 50px;
background-color: transparent;
border: none;
outline: none;
font-size: 1em;
padding: 0 35px 0 5px;
color: #fff;
}
.setname-button {
width: 100%;
height: 40px;
border-radius: 40px;
background: #fff;
border: none;
outline: none;
cursor: pointer;
font-size: 16px;
font-weight: 600;
}
import React, { useState } from 'react';
import "./SetName.css";
import { Link } from 'react-router-dom';
export default function Setname() {
const [nickname, setNickname] = useState("");
function handleNicknameChange(event: { target: { value: any; }; }) {
setNickname(event.target.value);
}
// function handleSubmit() {
// window.location.href = "/index";
// }
return(
<>
<section>
<div className='form-box'>
<div className='form-value'>
<form action=''>
<h2>欢迎</h2>
<div className="inputbox">
<input type="text" value={nickname} onChange={handleNicknameChange} required />
<label htmlFor="">昵称</label>
</div>
<div className='inputbox'>
<input type="password" required />
<label htmlFor="">密码</label>
</div>
<Link to = "/index">
<button className='setname-button'>登录</button>
</Link>
</form>
</div>
</div>
</section>
</>
)
}
\ No newline at end of file
/// <reference types="vite/client" />
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
})
This diff is collapsed.
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment