parent
497c5196f8
commit
731781b19a
@ -1,33 +1,146 @@ |
|||||||
|
<template> |
||||||
|
<input |
||||||
|
ref="fileInput" |
||||||
|
class="file-input" |
||||||
|
type="file" |
||||||
|
accept="image/jpeg,image/png,image/gif,image/webp" |
||||||
|
@change="handleFileChange" |
||||||
|
/> |
||||||
|
</template> |
||||||
|
|
||||||
<script setup> |
<script setup> |
||||||
import { ref, inject, onMounted } from 'vue' |
import { ref, inject, unref } from 'vue' |
||||||
import { showToast } from 'vant' |
import { showToast } from 'vant' |
||||||
import utils from '@/utils/utils' |
|
||||||
import { compressImage } from '@/utils/CompressImage' |
import { compressImage } from '@/utils/CompressImage' |
||||||
import { Post } from '@/utils/https' |
import { upLoadImgHttp } from '@/utils/https' |
||||||
|
|
||||||
|
const emit = defineEmits(['image-selected', 'image-sent', 'image-failed']) |
||||||
|
|
||||||
const store = inject('appStore', null) |
|
||||||
const userId = inject('userId', ref('')) |
const userId = inject('userId', ref('')) |
||||||
const token = inject('token', ref('')) |
|
||||||
const manualService = inject('manualService', ref('1')) |
const manualService = inject('manualService', ref('1')) |
||||||
const sessionId = inject('sessionId', ref('')) |
const sessionId = inject('sessionId', ref('')) |
||||||
const source = inject('source', ref('')) |
const source = inject('source', ref('')) |
||||||
const mescrollContent = inject('mescrollContent', null) |
|
||||||
const elder = inject('elder', false) |
|
||||||
|
|
||||||
const upImage = ref(true) |
const fileInput = ref(null) |
||||||
const showImage = ref(false) |
const uploading = ref(false) |
||||||
const srcImg = ref('') |
|
||||||
const isWxAndroid = ref(false) |
const MAX_IMAGE_SIZE = 5 * 1024 * 1024 |
||||||
|
const ALLOWED_IMAGE_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'] |
||||||
|
|
||||||
|
function openFilePicker() { |
||||||
|
if (uploading.value) { |
||||||
|
showToast('图片正在发送,请稍候') |
||||||
|
return |
||||||
|
} |
||||||
|
fileInput.value?.click() |
||||||
|
} |
||||||
|
|
||||||
|
function readAsDataUrl(file) { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
const reader = new FileReader() |
||||||
|
reader.onload = () => resolve(reader.result) |
||||||
|
reader.onerror = reject |
||||||
|
reader.readAsDataURL(file) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
async function getUploadFile(file, dataUrl) { |
||||||
|
try { |
||||||
|
const result = await compressImage(dataUrl, { |
||||||
|
file: { file }, |
||||||
|
quality: 0.8, |
||||||
|
maxWidth: 1600, |
||||||
|
maxHeight: 1600, |
||||||
|
}) |
||||||
|
if (!result?.blob || result.blob.size >= file.size) return file |
||||||
|
return new File([result.blob], file.name, { |
||||||
|
type: result.blob.type || file.type, |
||||||
|
lastModified: Date.now(), |
||||||
|
}) |
||||||
|
} catch (error) { |
||||||
|
console.log('图片压缩失败,使用原图上传', error) |
||||||
|
return file |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function getResponseData(response) { |
||||||
|
let data = response?.data ?? response |
||||||
|
if (typeof data === 'string') { |
||||||
|
try { |
||||||
|
data = JSON.parse(data) |
||||||
|
} catch (error) { |
||||||
|
return { value: data } |
||||||
|
} |
||||||
|
} |
||||||
|
return data?.data ?? data ?? {} |
||||||
|
} |
||||||
|
|
||||||
|
function isBusinessFailure(response) { |
||||||
|
if (response?.success === false) return true |
||||||
|
const code = Number(response?.code) |
||||||
|
return Number.isFinite(code) && (code < 0 || code >= 400) |
||||||
|
} |
||||||
|
|
||||||
|
async function handleFileChange(event) { |
||||||
|
const input = event.target |
||||||
|
const file = input.files?.[0] |
||||||
|
input.value = '' |
||||||
|
|
||||||
|
if (!file || uploading.value) return |
||||||
|
if (!ALLOWED_IMAGE_TYPES.includes(file.type)) { |
||||||
|
showToast('请选择 JPG、PNG、GIF 或 WebP 图片') |
||||||
|
return |
||||||
|
} |
||||||
|
if (file.size > MAX_IMAGE_SIZE) { |
||||||
|
showToast('图片大小不能超过5M') |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
const localId = `image_${Date.now()}_${Math.random().toString(36).slice(2, 8)}` |
||||||
|
let previewUrl = '' |
||||||
|
|
||||||
|
try { |
||||||
|
previewUrl = await readAsDataUrl(file) |
||||||
|
emit('image-selected', { localId, imageUrl: previewUrl, fileName: file.name }) |
||||||
|
uploading.value = true |
||||||
|
|
||||||
|
const uploadFile = await getUploadFile(file, previewUrl) |
||||||
|
const formData = new FormData() |
||||||
|
formData.append('file', uploadFile, file.name) |
||||||
|
formData.append('fromFlag', '1') |
||||||
|
formData.append('userId', unref(userId) || '') |
||||||
|
formData.append('sessionId', unref(sessionId) || '') |
||||||
|
formData.append('source', unref(source) || '') |
||||||
|
formData.append('reqAndResType', '2') |
||||||
|
formData.append('msgSource', String(unref(manualService) || '1')) |
||||||
|
formData.append('timestamp', String(Date.now())) |
||||||
|
|
||||||
onMounted(() => { |
const response = await upLoadImgHttp('FileUpLoad/uploadImgAndVideo', formData) |
||||||
if (utils.isWeiXin() && utils.isAndroid() === 'isAndroid') { |
if (isBusinessFailure(response)) { |
||||||
isWxAndroid.value = true |
throw new Error(response?.msg || '图片发送失败') |
||||||
|
} |
||||||
|
|
||||||
|
const data = getResponseData(response) |
||||||
|
emit('image-sent', { |
||||||
|
localId, |
||||||
|
imageUrl: data?.imgUrl || data?.url || previewUrl, |
||||||
|
imageKey: data?.imgKey || data?.fileKey || data?.key || '', |
||||||
|
response, |
||||||
|
}) |
||||||
|
} catch (error) { |
||||||
|
console.log('图片发送失败', error) |
||||||
|
if (previewUrl) emit('image-failed', { localId }) |
||||||
|
showToast(error?.message || '图片发送失败,请稍后重试') |
||||||
|
} finally { |
||||||
|
uploading.value = false |
||||||
} |
} |
||||||
}) |
} |
||||||
|
|
||||||
// ... 方法中: |
defineExpose({ openFilePicker }) |
||||||
// this.$store.commit('set_sendLoading') → store?.set_sendLoading?.(value) |
|
||||||
// this.$post('FileUpLoad/uploadImgAndVideo') → Post('FileUpLoad/uploadImgAndVideo', ...) |
|
||||||
// this.mescrollContent → mescrollContent.value |
|
||||||
// this.$elder → elder |
|
||||||
</script> |
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
.file-input { |
||||||
|
display: none; |
||||||
|
} |
||||||
|
</style> |
||||||
|
|||||||
@ -1,64 +1,17 @@ |
|||||||
let curWwwPath = window.location.href; |
const currentUrl = new URL(window.location.href) |
||||||
let pathName = ''; // window.location.pathname
|
const host = currentUrl.host |
||||||
let pos = curWwwPath.indexOf(pathName); |
const protocol = currentUrl.protocol |
||||||
let host = window.location.host; |
const isWs = protocol === 'https:' ? 'wss' : 'ws' |
||||||
let protocol = window.location.protocol; |
|
||||||
let localhostPath = curWwwPath // downSource.substring(0, pos);
|
|
||||||
let projectName = pathName.substring(0, pathName.substr(1).indexOf('/') + 1); |
|
||||||
let isWs = protocol == 'https:' ? 'wss' : 'ws'; |
|
||||||
|
|
||||||
let baseUrl = ""; |
// liics 的 HTTP/WebSocket 服务与页面使用同一域名;本地开发也由 Vite
|
||||||
let WSURL = ''; |
// 直接代理 /liics_api,因此浏览器里看到的请求路径在各环境保持一致。
|
||||||
let envType = ''; |
const config = { |
||||||
|
baseUrl: '/liics_api/', |
||||||
//开发环境
|
WSURL: `${isWs}://${host}/liics_api/webSocket/`, |
||||||
if(localhostPath.indexOf("localhost") >=0 || localhostPath.indexOf("172.16") >=0){ |
envType: import.meta.env.DEV ? 'dev' : 'production', |
||||||
baseUrl = '/api'; |
BASEURL: currentUrl.origin, |
||||||
WSURL = `${isWs}://${host}/api/liics_api/webSocket/`; //ws://ssp-dev.trace.com.cn/liics_api/webSocket/
|
|
||||||
envType = 'dev'; |
|
||||||
|
|
||||||
//测试环境
|
|
||||||
}else if( |
|
||||||
localhostPath.includes("-dev") || |
|
||||||
localhostPath.includes("-sit") || |
|
||||||
localhostPath.includes("-uat") || |
|
||||||
localhostPath.includes("-pt") |
|
||||||
){ |
|
||||||
baseUrl = localhostPath; |
|
||||||
WSURL = `${isWs}://${host}/liics_api/webSocket/`; //ws://ssp-dev.trace.com.cn/liics_api/webSocket/
|
|
||||||
envType = 'sit'; |
|
||||||
//生产环境
|
|
||||||
}else{ |
|
||||||
baseUrl = localhostPath; |
|
||||||
WSURL = `${isWs}://${host}/liics_api/webSocket/`; //ws://ssp-dev.trace.com.cn/liics_api/webSocket/
|
|
||||||
envType = 'production'; |
|
||||||
} |
} |
||||||
console.log('baseUrl', baseUrl, localhostPath, window.location.href, window.location.pathname); |
|
||||||
|
|
||||||
// switch (process.env.NODE_ENV) {
|
|
||||||
// case 'development':
|
|
||||||
// baseUrl = process.env.VUE_APP_BASE_API //这里是本地的请求url
|
|
||||||
// WSURL = process.env.VUE_APP_WEBSOCKET_URL;
|
|
||||||
// break
|
|
||||||
// case 'sit': // 注意这里的名字要和步骤二中设置的环境名字对应起来
|
|
||||||
// baseUrl = process.env.VUE_APP_BASE_API //这里是测试环境中的url
|
|
||||||
// WSURL = process.env.VUE_APP_WEBSOCKET_URL;
|
|
||||||
// break
|
|
||||||
// case 'production':
|
|
||||||
// baseUrl = process.env.VUE_APP_BASE_API //生产环境url
|
|
||||||
// WSURL = process.env.VUE_APP_WEBSOCKET_URL;
|
|
||||||
// break
|
|
||||||
// default:
|
|
||||||
// baseUrl = process.env.VUE_APP_BASE_API //生产环境url
|
|
||||||
// WSURL = process.env.VUE_APP_WEBSOCKET_URL;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
console.log('liics api config', config) |
||||||
|
|
||||||
let config = { |
export default config |
||||||
baseUrl: `${baseUrl}/liics_api/`,//后台请求链接
|
|
||||||
WSURL: WSURL,//websocket链接
|
|
||||||
envType: envType,//环境类型
|
|
||||||
BASEURL: baseUrl, |
|
||||||
} |
|
||||||
|
|
||||||
export default config; |
|
||||||
|
|||||||
@ -0,0 +1,29 @@ |
|||||||
|
import { fetchPost, upLoadImgHttp } from '@/utils/https' |
||||||
|
|
||||||
|
// The legacy liics chat used a small set of HTTP endpoints. Keeping those
|
||||||
|
// calls here gives the Vue 3 store a single transport boundary and preserves
|
||||||
|
// the payload contract expected by the existing chat components.
|
||||||
|
export const postConversation = (url, data = {}) => fetchPost(url, data) |
||||||
|
|
||||||
|
export const sendRobotMessage = (payload) => postConversation('semantic/qaChat', payload) |
||||||
|
|
||||||
|
export const sendVoiceMessage = (payload) => postConversation('upload/hxVoice', payload) |
||||||
|
|
||||||
|
export const uploadConversationImage = (formData) => upLoadImgHttp('upload/image', formData) |
||||||
|
|
||||||
|
export const connectHumanService = (payload) => postConversation('user/connectToEasemob', payload) |
||||||
|
|
||||||
|
export const updateReadMessages = (payload) => postConversation('HumanChat/msgCallBack', payload) |
||||||
|
|
||||||
|
export const submitUserBehavior = (payload) => postConversation('user/interactive', payload) |
||||||
|
|
||||||
|
export const submitSatisfaction = (payload) => postConversation('semantic/savePaperResult', payload) |
||||||
|
|
||||||
|
export const loadSystemConfig = (payload) => postConversation('config/sysConfig', payload) |
||||||
|
|
||||||
|
export const loadHeaderAvatar = (payload) => postConversation('config/avatar', payload) |
||||||
|
|
||||||
|
export const loadTipsConfig = (payload) => |
||||||
|
postConversation('insuranceQuery/longTermRiskConfigValue', payload) |
||||||
|
|
||||||
|
export const getLegacyApi = (url, params) => postConversation(url, params) |
||||||
Loading…
Reference in new issue