vue3前端后端地址可配置方案

发布于:2025-05-21 ⋅ 阅读:(13) ⋅ 点赞:(0)

在开发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>


网站公告

今日签到

点亮在社区的每一天
去签到