1.0版本!
成功地standalone!
This commit is contained in:
parent
ebc5c01eb0
commit
a434ef998c
73
html/index.html
Normal file
73
html/index.html
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>习题识别工具</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/element-plus/dist/index.css">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-row {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-col {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
#app {
|
||||||
|
width: 90%;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app">
|
||||||
|
<h2 style="text-align: center;">导航页面</h2>
|
||||||
|
<p style="text-align: center; color: #999;">请选择您要前往的页面</p>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-button type="primary" @click="navigateTo('/upload')">上传页面</el-button>
|
||||||
|
<el-button type="success" @click="navigateTo('/terminal')">接收终端</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/vue@3"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/element-plus"></script>
|
||||||
|
<script>
|
||||||
|
const {createApp} = Vue;
|
||||||
|
const {ElButton, ElRow, ElCol} = ElementPlus;
|
||||||
|
|
||||||
|
createApp({
|
||||||
|
methods: {
|
||||||
|
navigateTo(path) {
|
||||||
|
window.location.href = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).use(ElementPlus).mount('#app');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
153
html/terminal.html
Normal file
153
html/terminal.html
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>接收终端</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/element-plus/dist/index.css">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-textarea__inner {
|
||||||
|
min-height: 150px;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-row {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-col {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-container {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
#app {
|
||||||
|
width: 90%;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app">
|
||||||
|
<h2 style="text-align: center;">终端-习题识别工具</h2>
|
||||||
|
<p style="text-align: center; color: #999;">这里将接收来自上传端的习题图像及其识别结果</p>
|
||||||
|
<div class="image-container" v-if="imageUrl">
|
||||||
|
<img :src="imageUrl" alt="Received Image">
|
||||||
|
</div>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-input
|
||||||
|
v-model="text"
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
readonly
|
||||||
|
placeholder="接收到的文本将显示在这里..."
|
||||||
|
></el-input>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-button type="primary" @click="copyText">复制</el-button>
|
||||||
|
<el-button type="danger" @click="clearText">清空</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/vue@3"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/element-plus"></script>
|
||||||
|
<script>
|
||||||
|
const {createApp} = Vue;
|
||||||
|
const {ElInput, ElButton, ElRow, ElCol, ElMessage} = ElementPlus;
|
||||||
|
|
||||||
|
createApp({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
text: '',
|
||||||
|
imageUrl: '',
|
||||||
|
websocket: null,
|
||||||
|
reconnectInterval: 10000,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
uri() {
|
||||||
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
|
const host = window.location.host;
|
||||||
|
return `${protocol}//${host}/listener`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.connectWebSocket();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
connectWebSocket() {
|
||||||
|
this.websocket = new WebSocket(this.uri);
|
||||||
|
this.websocket.onopen = () => {
|
||||||
|
console.log("已连接到服务器。等待接收消息...");
|
||||||
|
};
|
||||||
|
this.websocket.onmessage = (listener) => {
|
||||||
|
const data = JSON.parse(listener.data);
|
||||||
|
if (data.type === 'text') {
|
||||||
|
this.text += data.content; // + '\n';
|
||||||
|
} else if (data.type === 'image') {
|
||||||
|
this.imageUrl = `data:image/jpeg;base64,${data.content}`;
|
||||||
|
this.text = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.websocket.onclose = () => {
|
||||||
|
console.log("服务器关闭了连接。10秒后重新连接...");
|
||||||
|
setTimeout(() => {
|
||||||
|
this.connectWebSocket();
|
||||||
|
}, this.reconnectInterval);
|
||||||
|
};
|
||||||
|
this.websocket.onerror = (error) => {
|
||||||
|
console.error("WebSocket 错误:", error);
|
||||||
|
this.websocket.close();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
copyText() {
|
||||||
|
navigator.clipboard.writeText(this.text).then(() => {
|
||||||
|
ElMessage.success('文本已复制到剪贴板');
|
||||||
|
}).catch((error) => {
|
||||||
|
ElMessage.error('复制文本失败: ' + error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
clearText() {
|
||||||
|
this.text = '';
|
||||||
|
this.imageUrl = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).use(ElementPlus).mount('#app');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
186
html/upload.html
Normal file
186
html/upload.html
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>习题上传</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/element-plus/dist/index.css">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-area {
|
||||||
|
border: 1px dashed #ccc;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-area.dragging {
|
||||||
|
border-color: #409EFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-textarea__inner {
|
||||||
|
min-height: 150px;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-container {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.container {
|
||||||
|
width: 90%;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#buttons{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app" class="container">
|
||||||
|
<h2 style="text-align: center;">上传-习题识别工具</h2>
|
||||||
|
<p style="text-align: center; color: #999;">请上传习题图片,AI将自动处理并返回结果。</p>
|
||||||
|
|
||||||
|
<el-upload
|
||||||
|
drag
|
||||||
|
action="/predict"
|
||||||
|
:on-change="handleChange"
|
||||||
|
:auto-upload="false"
|
||||||
|
:show-file-list="false"
|
||||||
|
:before-upload="beforeUpload"
|
||||||
|
:accept="'image/*'"
|
||||||
|
>
|
||||||
|
<div class="upload-area" :class="{ dragging: dragging }">
|
||||||
|
<i class="el-icon-upload" style="font-size: 20px;"></i>
|
||||||
|
<p>请直接粘贴图片</p>
|
||||||
|
<p>或将图像拖放到此处</p>
|
||||||
|
<div class="image-container" v-if="uploadedImageUrl">
|
||||||
|
<img :src="uploadedImageUrl" alt="Uploaded Image">
|
||||||
|
</div>
|
||||||
|
<p>点击上传或替换当前图片</p>
|
||||||
|
</div>
|
||||||
|
</el-upload>
|
||||||
|
|
||||||
|
<el-input
|
||||||
|
v-model="result"
|
||||||
|
type="textarea"
|
||||||
|
:rows="6"
|
||||||
|
readonly
|
||||||
|
placeholder="AI生成的文本将显示在这里..."
|
||||||
|
></el-input>
|
||||||
|
|
||||||
|
<div id="buttons" style="margin-top: 20px; text-align: right;">
|
||||||
|
<el-button type="primary" @click="submit">发送</el-button>
|
||||||
|
<el-button type="danger" @click="clear">清空</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/vue@3"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/element-plus"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const {createApp} = Vue;
|
||||||
|
const {ElUpload, ElInput, ElButton, ElMessage} = ElementPlus;
|
||||||
|
|
||||||
|
const app = createApp({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dragging: false,
|
||||||
|
file: null,
|
||||||
|
result: '',
|
||||||
|
uploadedImageUrl: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleChange(file, fileList) {
|
||||||
|
this.file = file.raw;
|
||||||
|
this.uploadedImageUrl = URL.createObjectURL(file.raw);
|
||||||
|
},
|
||||||
|
beforeUpload(file) {
|
||||||
|
// 文件类型和大小的校验
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
async submit() {
|
||||||
|
if (!this.file) {
|
||||||
|
this.$message.error('请选择文件');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append('file', this.file);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/predict', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Network response was not ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
const reader = response.body.getReader();
|
||||||
|
const decoder = new TextDecoder('utf-8');
|
||||||
|
this.result = ''; // 清空之前的结果
|
||||||
|
let receivedLength = 0; // 接收到的字节数
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const {done, value} = await reader.read();
|
||||||
|
if (done) {
|
||||||
|
// 流已完全读取
|
||||||
|
console.log('Stream complete');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const chunk = decoder.decode(value, {stream: true});
|
||||||
|
this.result += chunk;
|
||||||
|
receivedLength += value.length;
|
||||||
|
console.log(`Received ${receivedLength} bytes of data so far`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.$message.error('上传失败: ' + error.message);
|
||||||
|
console.error('Fetch error:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clear() {
|
||||||
|
this.file = null;
|
||||||
|
this.result = '';
|
||||||
|
this.uploadedImageUrl = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(ElementPlus); // 使用Element Plus
|
||||||
|
app.mount('#app');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
47
main.py
47
main.py
@ -1,27 +1,16 @@
|
|||||||
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, HTTPException, UploadFile, File
|
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, UploadFile, File
|
||||||
from fastapi.responses import StreamingResponse, HTMLResponse
|
from fastapi.responses import StreamingResponse, HTMLResponse
|
||||||
from pydantic import BaseModel
|
|
||||||
from typing import List
|
from typing import List
|
||||||
import base64
|
import base64
|
||||||
import asyncio
|
|
||||||
import openai
|
import openai
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
with open("key", "r") as f:
|
with open("key", "r") as k:
|
||||||
key = f.read()
|
key = k.read()
|
||||||
client = openai.OpenAI(api_key=key,
|
client = openai.OpenAI(api_key=key,
|
||||||
base_url="https://open.bigmodel.cn/api/paas/v4/"
|
base_url="https://open.bigmodel.cn/api/paas/v4/")
|
||||||
)
|
with open("prompt", "r", encoding="utf-8") as p:
|
||||||
|
prompt = p.read()
|
||||||
|
|
||||||
@app.get("/")
|
|
||||||
async def root():
|
|
||||||
return {"message": "Hello World"}
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/hello/{name}")
|
|
||||||
async def say_hello(name: str):
|
|
||||||
return {"message": f"Hello {name}"}
|
|
||||||
|
|
||||||
|
|
||||||
# WebSocket连接管理器
|
# WebSocket连接管理器
|
||||||
@ -36,7 +25,8 @@ class ConnectionManager:
|
|||||||
def disconnect(self, websocket: WebSocket):
|
def disconnect(self, websocket: WebSocket):
|
||||||
self.active_connections.remove(websocket)
|
self.active_connections.remove(websocket)
|
||||||
|
|
||||||
async def send_personal_message(self, message: str, websocket: WebSocket):
|
@staticmethod
|
||||||
|
async def send_personal_message(message: str, websocket: WebSocket):
|
||||||
await websocket.send_text(message)
|
await websocket.send_text(message)
|
||||||
|
|
||||||
async def broadcast(self, message: str):
|
async def broadcast(self, message: str):
|
||||||
@ -52,9 +42,10 @@ manager = ConnectionManager()
|
|||||||
|
|
||||||
|
|
||||||
# WebSocket端点
|
# WebSocket端点
|
||||||
@app.websocket("/event")
|
@app.websocket("/listener")
|
||||||
async def event(websocket: WebSocket):
|
async def event(websocket: WebSocket):
|
||||||
await manager.connect(websocket)
|
await manager.connect(websocket)
|
||||||
|
print("Client connected")
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
# 这里可以添加逻辑处理来自客户端的消息
|
# 这里可以添加逻辑处理来自客户端的消息
|
||||||
@ -80,7 +71,7 @@ async def predict(file: UploadFile = File(...)):
|
|||||||
"content": [
|
"content": [
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"text": "请描述该图片",
|
"text": prompt,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "image_url",
|
"type": "image_url",
|
||||||
@ -109,7 +100,19 @@ async def predict(file: UploadFile = File(...)):
|
|||||||
return StreamingResponse(stream_response(), media_type="text/plain")
|
return StreamingResponse(stream_response(), media_type="text/plain")
|
||||||
|
|
||||||
|
|
||||||
@app.get("/test", response_class=HTMLResponse)
|
@app.get("/terminal", response_class=HTMLResponse)
|
||||||
async def test():
|
async def test():
|
||||||
with open("test.html", "r", encoding="utf-8") as f:
|
with open("html/terminal.html", "r", encoding="utf-8") as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/upload", response_class=HTMLResponse)
|
||||||
|
async def test():
|
||||||
|
with open("html/upload.html", "r", encoding="utf-8") as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/", response_class=HTMLResponse)
|
||||||
|
async def test():
|
||||||
|
with open("html/index.html", "r", encoding="utf-8") as f:
|
||||||
return f.read()
|
return f.read()
|
||||||
|
|||||||
1
prompt
Normal file
1
prompt
Normal file
@ -0,0 +1 @@
|
|||||||
|
请直接输出图片中的习题内容。不要输出除了题目内容外的各种提示语,比如“好的,下面我将...”。不要输出已经图中的题目里已经填写的答案,只要题干。请将按照方便粘贴到word文档的格式进行输出,避免使用markdown格式。遇到题干里有无法转为文字的部分,比如题目的选项为图片等情况,预留好空位并放置一个标记[图片]即可。
|
||||||
132
test.html
132
test.html
@ -1,132 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>WebSocket 文本和图片接收器</title>
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/element-plus/dist/index.css">
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100vh;
|
|
||||||
margin: 0;
|
|
||||||
background-color: #f4f4f4;
|
|
||||||
}
|
|
||||||
#app {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 600px;
|
|
||||||
padding: 20px;
|
|
||||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
|
||||||
background-color: white;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
.el-textarea__inner {
|
|
||||||
min-height: 150px;
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
.el-row {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
.image-container {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
#app {
|
|
||||||
width: 90%;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app">
|
|
||||||
<div class="image-container" v-if="imageUrl">
|
|
||||||
<img :src="imageUrl" alt="Received Image">
|
|
||||||
</div>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="24">
|
|
||||||
<el-input
|
|
||||||
v-model="text"
|
|
||||||
type="textarea"
|
|
||||||
:rows="4"
|
|
||||||
readonly
|
|
||||||
placeholder="接收到的文本将显示在这里..."
|
|
||||||
></el-input>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="24">
|
|
||||||
<el-button type="primary" @click="copyText">复制</el-button>
|
|
||||||
<el-button type="danger" @click="clearText">清空</el-button>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/vue@3"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/element-plus"></script>
|
|
||||||
<script>
|
|
||||||
const { createApp } = Vue;
|
|
||||||
const { ElInput, ElButton, ElRow, ElCol, ElMessage } = ElementPlus;
|
|
||||||
|
|
||||||
createApp({
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
text: '',
|
|
||||||
imageUrl: '',
|
|
||||||
websocket: null,
|
|
||||||
reconnectInterval: 10000,
|
|
||||||
uri: "ws://localhost:8000/event"
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.connectWebSocket();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
connectWebSocket() {
|
|
||||||
this.websocket = new WebSocket(this.uri);
|
|
||||||
this.websocket.onopen = () => {
|
|
||||||
console.log("已连接到服务器。等待接收消息...");
|
|
||||||
};
|
|
||||||
this.websocket.onmessage = (event) => {
|
|
||||||
const data = JSON.parse(event.data);
|
|
||||||
if (data.type === 'text') {
|
|
||||||
this.text += data.content; // + '\n';
|
|
||||||
} else if (data.type === 'image') {
|
|
||||||
this.imageUrl = `data:image/jpeg;base64,${data.content}`;
|
|
||||||
this.text = '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.websocket.onclose = () => {
|
|
||||||
console.log("服务器关闭了连接。10秒后重新连接...");
|
|
||||||
setTimeout(() => {
|
|
||||||
this.connectWebSocket();
|
|
||||||
}, this.reconnectInterval);
|
|
||||||
};
|
|
||||||
this.websocket.onerror = (error) => {
|
|
||||||
console.error("WebSocket 错误:", error);
|
|
||||||
this.websocket.close();
|
|
||||||
};
|
|
||||||
},
|
|
||||||
copyText() {
|
|
||||||
navigator.clipboard.writeText(this.text).then(() => {
|
|
||||||
ElMessage.success('文本已复制到剪贴板');
|
|
||||||
}).catch((error) => {
|
|
||||||
ElMessage.error('复制文本失败: ' + error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
clearText() {
|
|
||||||
this.text = '';
|
|
||||||
this.imageUrl = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).use(ElementPlus).mount('#app');
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Loading…
Reference in New Issue
Block a user