[mod]
This commit is contained in:
23
frontend/src/App.vue
Normal file
23
frontend/src/App.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import Chatroom from "./Chatroom.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "app",
|
||||
components: {
|
||||
Chatroom,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Chatroom title="Client #1" />
|
||||
<Chatroom title="Client #2" />
|
||||
</template>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
236
frontend/src/Chatroom.vue
Normal file
236
frontend/src/Chatroom.vue
Normal file
@@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<div class="Chatroom">
|
||||
<header>{{ title }}</header>
|
||||
<ul class="list" ref="ul">
|
||||
<li v-for="(v, i) in list" :key="i">
|
||||
<div class="content">{{ v.content }}</div>
|
||||
<div class="time">{{ v.time.toLocaleTimeString() }}</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div> <button @click="connect">Connect</button><button @click="start">Start</button> </div>
|
||||
<div class="send">
|
||||
<input placeholder="Say something..." v-model="input" @keypress.enter="send" />
|
||||
<button @click="send">Send</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, nextTick } from "vue";
|
||||
import { getClient } from "./getClient";
|
||||
import { Msgstart } from "./shared/protocols/lobby/Msgstart";
|
||||
import { MsgChat } from "./shared/protocols/MsgChat";
|
||||
|
||||
export default defineComponent({
|
||||
name: "Chatroom",
|
||||
props: {
|
||||
title: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
input: "",
|
||||
list: [] as MsgChat[],
|
||||
client: getClient(),
|
||||
};
|
||||
},
|
||||
|
||||
mounted(): void {
|
||||
// Listen Msg
|
||||
this.client.listenMsg("Chat", (v: MsgChat) => {
|
||||
console.log("Chat")
|
||||
this.list.push(v);
|
||||
|
||||
// Scroll the list to the bottom
|
||||
nextTick(() => {
|
||||
const ul = this.$refs.ul as HTMLElement;
|
||||
ul.scrollTo(0, ul.scrollHeight);
|
||||
});
|
||||
});
|
||||
|
||||
// Listen Msg
|
||||
this.client.listenMsg("lobby/B_start", (v: Msgstart) => {
|
||||
console.log("lobby/B_start")
|
||||
let listdata: MsgChat = {
|
||||
time: v.time,
|
||||
content: v.data.content,
|
||||
};
|
||||
this.list.push(listdata);
|
||||
|
||||
// Scroll the list to the bottom
|
||||
nextTick(() => {
|
||||
const ul = this.$refs.ul as HTMLElement;
|
||||
ul.scrollTo(0, ul.scrollHeight);
|
||||
});
|
||||
});
|
||||
|
||||
// When disconnected
|
||||
this.client.flows.postDisconnectFlow.push((v) => {
|
||||
// alert("Server disconnected");
|
||||
let listdata: MsgChat = {
|
||||
time: new Date(),
|
||||
content: "Server disconnected",
|
||||
};
|
||||
this.list.push(listdata);
|
||||
return v;
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
async connect(): Promise<void> {
|
||||
// this.client = getClient();
|
||||
// let listdata: MsgChat = {
|
||||
// time: new Date(),
|
||||
// content: `connect`,
|
||||
// };
|
||||
// this.list.push(listdata);
|
||||
|
||||
this.client.connect().then((v) => {
|
||||
if (!v.isSucc) {
|
||||
// alert("= Client Connect Error =\n" + v.errMsg);
|
||||
let listdata: MsgChat = {
|
||||
time: new Date(),
|
||||
content: "= Client Connect Error =\n" + v.errMsg,
|
||||
};
|
||||
this.list.push(listdata);
|
||||
} else {
|
||||
// alert("= Client Connect Error =\n" + v.errMsg);
|
||||
let listdata: MsgChat = {
|
||||
time: new Date(),
|
||||
content: "Server Connect",
|
||||
};
|
||||
this.list.push(listdata);
|
||||
}
|
||||
});
|
||||
},
|
||||
async start(): Promise<void> {
|
||||
let ret = await this.client.callApi("lobby/start", {});
|
||||
|
||||
// Error
|
||||
if (!ret.isSucc) {
|
||||
// alert(ret.err.message);
|
||||
let listdata: MsgChat = {
|
||||
time: new Date(),
|
||||
content: ret.err.message,
|
||||
};
|
||||
this.list.push(listdata);
|
||||
return;
|
||||
}
|
||||
|
||||
// Success
|
||||
this.input = "";
|
||||
},
|
||||
// Send input message
|
||||
async send(): Promise<void> {
|
||||
let ret = await this.client.callApi("Send", {
|
||||
content: this.input,
|
||||
});
|
||||
|
||||
// Error
|
||||
if (!ret.isSucc) {
|
||||
// alert(ret.err.message);
|
||||
let listdata: MsgChat = {
|
||||
time: new Date(),
|
||||
content: ret.err.message,
|
||||
};
|
||||
this.list.push(listdata);
|
||||
return;
|
||||
}
|
||||
|
||||
// Success
|
||||
this.input = "";
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
// .div .Chatroom {
|
||||
// display: inline-block;
|
||||
// }
|
||||
|
||||
.Chatroom {
|
||||
// display: inline-block;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 460px;
|
||||
height: 480px;
|
||||
margin: 20px;
|
||||
background: #f7f7f7;
|
||||
border: 1px solid #454545;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
|
||||
>header {
|
||||
background: #454545;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
>.send {
|
||||
flex: 0 0 40px;
|
||||
display: flex;
|
||||
border-top: 1px solid #454545;
|
||||
|
||||
>* {
|
||||
border: none;
|
||||
outline: none;
|
||||
height: 100%;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
>input {
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
>button {
|
||||
flex: 0 0 100px;
|
||||
background: #215fa4;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #4b80bb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
>.list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
list-style: none;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
padding-bottom: 20px;
|
||||
background: #f2f2f2;
|
||||
|
||||
>li {
|
||||
margin-bottom: 10px;
|
||||
padding: 10px;
|
||||
background: #fff;
|
||||
line-height: 1.5em;
|
||||
border-radius: 5px;
|
||||
|
||||
>.content {
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
>.time {
|
||||
font-size: 12px;
|
||||
color: #4b80bb;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
100
frontend/src/assets/index.css
Normal file
100
frontend/src/assets/index.css
Normal file
@@ -0,0 +1,100 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body>h1 {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.app {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.Chatroom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 460px;
|
||||
height: 480px;
|
||||
margin: 20px;
|
||||
background: #f7f7f7;
|
||||
border: 1px solid #454545;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chat-room>header {
|
||||
background: #454545;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.send {
|
||||
flex: 0 0 40px;
|
||||
display: flex;
|
||||
border-top: 1px solid #454545;
|
||||
}
|
||||
|
||||
.send>* {
|
||||
border: none;
|
||||
outline: none;
|
||||
height: 100%;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.send>input {
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.send>button {
|
||||
flex: 0 0 100px;
|
||||
background: #215fa4;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.send>button:hover {
|
||||
background: #4b80bb;
|
||||
}
|
||||
|
||||
.list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
list-style: none;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
padding-bottom: 20px;
|
||||
background: #f2f2f2;
|
||||
}
|
||||
|
||||
.list>li {
|
||||
margin-bottom: 10px;
|
||||
padding: 10px;
|
||||
background: #fff;
|
||||
line-height: 1.5em;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.list>li>.content {
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.list>li>.time {
|
||||
font-size: 12px;
|
||||
color: #4b80bb;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.list>li:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
BIN
frontend/src/assets/logo.png
Normal file
BIN
frontend/src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
52
frontend/src/components/HelloWorld.vue
Normal file
52
frontend/src/components/HelloWorld.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps<{ msg: string }>()
|
||||
|
||||
const count = ref(0)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ msg }}</h1>
|
||||
|
||||
<p>
|
||||
Recommended IDE setup:
|
||||
<a href="https://code.visualstudio.com/" target="_blank">VS Code</a>
|
||||
+
|
||||
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
|
||||
</p>
|
||||
|
||||
<p>See <code>README.md</code> for more information.</p>
|
||||
|
||||
<p>
|
||||
<a href="https://vitejs.dev/guide/features.html" target="_blank">
|
||||
Vite Docs
|
||||
</a>
|
||||
|
|
||||
<a href="https://v3.vuejs.org/" target="_blank">Vue 3 Docs</a>
|
||||
</p>
|
||||
|
||||
<button type="button" @click="count++">count is: {{ count }}</button>
|
||||
<p>
|
||||
Edit
|
||||
<code>components/HelloWorld.vue</code> to test hot module replacement.
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
|
||||
label {
|
||||
margin: 0 0.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #eee;
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
color: #304455;
|
||||
}
|
||||
</style>
|
16
frontend/src/env.d.ts
vendored
Normal file
16
frontend/src/env.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/* eslint-disable */
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
|
||||
// TSRPC would decode ObjectId as string in frontend.
|
||||
declare module 'mongodb' {
|
||||
export type ObjectId = string;
|
||||
export type ObjectID = string;
|
||||
}
|
||||
declare module 'bson' {
|
||||
export type ObjectId = string;
|
||||
export type ObjectID = string;
|
||||
}
|
11
frontend/src/getClient.ts
Normal file
11
frontend/src/getClient.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { WsClient } from "tsrpc-browser";
|
||||
import { serviceProto, ServiceType } from "./shared/protocols/serviceProto";
|
||||
|
||||
export function getClient(): WsClient<ServiceType> {
|
||||
return new WsClient(serviceProto, {
|
||||
server: "ws://127.0.0.1:4000",
|
||||
// Remove this to use binary mode (remove from the server too)
|
||||
json: true,
|
||||
logger: console,
|
||||
})
|
||||
}
|
4
frontend/src/main.ts
Normal file
4
frontend/src/main.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
|
||||
createApp(App).mount('#app')
|
1
frontend/src/shared
Symbolic link
1
frontend/src/shared
Symbolic link
@@ -0,0 +1 @@
|
||||
D:/Project/Test/Test_NodeJS/TSRPC_Badminton-Scoreboard/backend/src/shared
|
Reference in New Issue
Block a user