在开发vue3项目过程中,需要切换不同的服务器部署,代码中配置的服务需要可灵活配置,不随着run npm build把网址打包到代码资源中,不然每次切换都需要重新run npm build。需要一个配置文件可以修改服务地址,而打包的代码资源直接copy就可以了。
记录一下下面的方法
vue3项目跟路径下创建proxy.js, 并且地址写在proxy.js中,挂载到window对象上
window.APP_config = {
apiBaseURL: 'http://localhost:8080',
wsBaseURL: 'ws://localhost:8080/ws',
};
项目的index.html中延迟加载proxy.js文件
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>KXJL Chat OCR</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
<script src="/proxy.js" defer></script>
</body>
</html>
main.js中注入,需要监听dom事件,只有dom加载完成才能调用window对象的属性
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router/router.js'
import ElementPlus from 'element-plus'
import 'element-plus/theme-chalk/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import axios from "axios"
var app = createApp(App)
// 使用 DOMContentLoaded 事件确保 DOM 加载完成后再访问 window.APP_config
document.addEventListener('DOMContentLoaded', () => {
if (window.APP_config) {
// 注入配置到 Vue 应用中
app.provide('APP_config', window.APP_config);
}
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
router.isReady().then(() => {
if (router.currentRoute.value.path !== '/') {
router.push('/');
}
});
app.provide('axios', axios)
app.use(router)
app.use(ElementPlus)
app.mount('#app')
});
其他任何地方可调用
import { ref, onMounted, onUnmounted, inject } from 'vue';
const APP_config = inject('APP_config');
console.log(APP_config)
const ws_url = APP_config.wsBaseURL
console.log("ws_url", ws_url)
ws的调用
<template lang="">
<div class="imgocrinfer">
<div class="input">
<div class="imgup">
<el-upload
class="upload-demo"
drag
action=""
multiple
:auto-upload="false"
:on-change="handleFileChange"
:on-remove="handleRemove"
>
<el-icon class="el-icon--upload"><Plus /></el-icon>
<div class="el-upload__text">点击或拖拽文件到此处上传</div>
<div class="el-upload__text">支持jpg、png</div>
<template #tip>
<div class="el-upload__tip">
jpg/png files with a size less than 500kb
</div>
</template>
</el-upload>
</div>
<div class="prompt_input">
<el-input class="textarea" v-model="prompt" type="textarea" placeholder="请输入提示词" @input="on_input_change"> </el-input>
</div>
<div class="submit">
<el-button type="primary" plain :icon="Edit" @click="on_click">点击OCR识别</el-button>
</div>
</div>
<div class="imgshow">
<div>
<span>图片预览</span>
</div>
<div class="imgPreview" ref="imageRef" style=" margin-top:10px">
<img v-if="previewImage" :src="previewImage" alt="Preview Imag" @load="onImageLoad">
</div>
</div>
<div class="textshow">
<div>
<span>文字识别结果</span>
</div>
<div style="margin-top:10px">
<el-input class="textarea" v-model="text_result" type="textarea" placeholder="" > </el-input>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, inject } from 'vue';
import { Edit } from '@element-plus/icons-vue';
const text_result = ref("");
const previewImage = ref('');
const prompt = ref('');
const image_base64_str = ref("")
const imageRef = ref(null);
const imgWidth = ref("");
const imgHeight = ref("");
const maxImageWidth = ref('');
const maxImageHeight = ref('');
const ws = ref(null);
const isConnected = ref(false);
const APP_config = inject('APP_config');
console.log(APP_config)
const ws_url = APP_config.wsBaseURL
console.log("ws_url", ws_url)
const connectWebSocket =() =>{
ws.value = new WebSocket(ws_url);
ws.value.onopen = onOpenHandler;
ws.value.onmessage = (event) => {
text_result.value += event.data
// 如果这是最后一条预期的消息,关闭连接
if ( event.data.isLastMessage) {
ws.value.close()
}
};
ws.value.onclose = (event) => {
isConnected.value = false
console.log('WebSocket connection closed', event);
};
ws.value.onerror = (error) => {
console.error('WebSocket error', error);
};
}
onUnmounted(()=>{
if (ws.value && ws.value.readyState === WebSocket.OPEN){
ws.value.close()
}
})
const onOpenHandler = () => {
isConnected.value = true;
sendTextAndImage()
}
const on_input_change = () =>{
text_result.value = ""
}
// 发送数据
async function sendTextAndImage() {
console.log("sendTextAndImage")
const payload = {
"text":prompt.value,
"image_base64_str": image_base64_str.value,
};
if (isConnected.value && prompt.value && image_base64_str.value){
console.log("payload: ", payload)
ws.value.send(JSON.stringify(payload));
}
}
const handleFileChange = (file, uploadFiles) =>{
if (uploadFiles.length >= 2){
uploadFiles.splice(0,uploadFiles.length-1)
}
if(file.raw && file.raw.type.startsWith("image/")){
const reader = new FileReader()
reader.onload = (e) => {
previewImage.value = e.target.result
image_base64_str.value = e.target.result.split(",")[1]
let img = new Image()
img.src = e.target.result
img.onload=()=>{
imgWidth.value = img.width
imgHeight.value = img.height
if (imgWidth.value > imgHeight.value) {
// 如果图片宽度大于高度,则限制宽度
maxImageWidth.value = "100%";
maxImageHeight.value = "auto";
} else {
// 否则,限制高度
maxImageWidth.value = 'auto';
maxImageHeight.value = `100%`;
}
}
}
reader.readAsDataURL(file.raw)
}else{
ElMessage.error('Please upload an image file.')
}
};
const handleRemove = (file, uploadFiles) =>{
if (uploadFiles.length ==0){
previewImage.value = ""
prompt.value = ""
text_result.value = ""
}
};
const onImageLoad = () => {
// 在图片加载完成后,强制浏览器重新计算尺寸
// 这可以通过触发一个重排或重绘来实现
requestAnimationFrame(() => {
// 强制浏览器重排
const imgElement = document.querySelector('img')
imgElement.style.width = maxImageWidth.value;
imgElement.style.height = maxImageHeight.value
});
};
const on_click = ()=>{
if ( ! prompt.value && !image_base64_str.value){
alert("prompt 和 图片不能为空,请重新输入")
}
text_result.value = ""
if (! isConnected.value){
connectWebSocket()
}
// onOpenHandler()
// prompt.value = ""
// image_base64_str.value = ""
}
defineOptions({
name: 'imgOcrInference',
});
</script>
<style lang="scss">
.imgocrinfer{
display: flex;
.input{
width: 14.7vw;
height: 90vh;
border:2px solid #ffffff;
box-shadow: 1px 0 4px #8c9eb11a;
border-radius: 8px;
.imgup{
width: 14.7vw;
height: 30vh;
border:2px solid #ffffff;
box-shadow: 1px 0 4px #8c9eb11a;
border-radius: 8px;
.el-upload{
width: 14.3vw;
}
}
.prompt_input{
height: 15vh;
.el-textarea__inner{
width: 14.3vw;
resize: none;
height: 100px;
}
}
.submit{
width: 14.3vw;
.el-button {
width: 14.3vw;
}
}
}
.imgshow{
height: 600px;
width: 29.5vw;
border:2px solid #ffffff;
box-shadow: 1px 0 4px #8c9eb11a;
border-radius: 8px;
margin-left: 1vw;
.imgPreview{
border:2px solid rgb(167, 200, 238);
height: 58vh;
border-radius: 8px;
}
}
.textshow{
width: 19.8vw;
border:2px solid #ffffff;
box-shadow: 1px 0 4px #8c9eb11a;
border-radius: 8px;
.el-textarea__inner{
width: 19.8vw;
height: 58vh;
}
}
}
</style>