Compare commits

..

No commits in common. "5292ede3c574338c55154c4395e60f67e7ca0716" and "c6271e9f4b892258a7c18bb28ffeb0f7b93e4f9e" have entirely different histories.

14 changed files with 162 additions and 705 deletions

View File

@ -1,17 +0,0 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 4
tab_width = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
insert_final_newline = false

View File

@ -3,6 +3,3 @@ export const SCROLL_TO_BOTTOM = 'SCROLL_TO_BOTTOM'
// 推荐帖子
export const RECOMMEND_POSTS_TITLE = 'RECOMMEND_POSTS_TITLE'
// 发送命令
export const SEND_COMMAND_TEXT = 'SEND_COMMAND_TEXT'

View File

@ -13,17 +13,6 @@
"navigationStyle": "custom"
}
},
{
"path": "pages/chat/ChatMainList",
"style": {
"navigationStyle": "custom",
"app-plus": {
"softinputMode": "adjustPan",
"bounce": "none",
"titleNView": false
}
}
},
{
"path": "pages/chat/ChatQuickAccess",
"style": {

View File

@ -1,267 +1,59 @@
<template>
<view class="area-input">
<!-- 语音/键盘切换 -->
<view class="input-container-voice" @click="toggleVoiceMode">
<image v-if="!isVoiceMode" src='/static/input_voice_icon.png'></image>
<image v-else src='/static/input_keyboard_icon.png'></image>
<!-- 发送语音 -->
<view class="input-container-voice">
<image src='/static/input_voice_icon.png'></image>
</view>
<!-- 输入框/语音按钮容器 -->
<view class="input-button-container">
<textarea
ref="textareaRef"
class="textarea"
type="text"
:placeholder="placeholder"
cursor-spacing="65"
confirm-type='done'
v-model="inputMessage"
@confirm="sendMessage"
@focus="handleFocus"
@blur="handleBlur"
@touchstart="handleTouchStart"
@touchend="handleTouchEnd"
:confirm-hold="true"
auto-height
:show-confirm-bar='false'
:hold-keyboard="holdKeyboard"
:adjust-position="true"
maxlength="300"
/>
<view
v-if="isVoiceMode"
class="hold-to-talk-button"
@touchstart.stop="startRecording"
@touchend.stop="stopRecording"
@touchmove.stop="handleTouchMove"
>
按住说话
</view>
<!-- 输入框 -->
<textarea
class="textarea"
type="text"
:placeholder="placeholder"
cursor-spacing="65"
confirm-type='done'
v-model="inputMessage"
@confirm="sendMessage"
@touchend="handleNoHideKeyboard"
:confirm-hold="true"
auto-height
:show-confirm-bar='false'
:hold-keyboard="holdKeyboard"
maxlength="300"
/>
<view class="input-container-send" @click="sendMessage">
<image src='/static/input_send_icon.png'></image>
</view>
<view class="input-container-send">
<view v-if="!isVoiceMode" class="input-container-send-btn" @click="sendMessage">
<image v-if="props.isSessionActive" src='/static/input_stop_icon.png'></image>
<image v-else src='/static/input_send_icon.png'></image>
</view>
</view>
</view>
<!-- 录音界面 -->
<uni-popup v-model:show="isRecording" position="center" :mask-click-able="false">
<view class="recording-popup">
<view class="recording-wave">
<!-- 波形动画 -->
<view class="wave-animation"></view>
</view>
<view class="recording-text">
{{ isSlideToText ? '松开发送 转文字' : '松开发送' }}
</view>
<view class="recording-cancel">
取消
</view>
</view>
</uni-popup>
<!-- 语音结果界面 -->
<uni-popup v-model:show="showVoiceResult" position="center" :mask-click-able="false">
<view class="voice-result-popup">
<view class="voice-result-bubble">
{{ voiceText }}
</view>
<view class="voice-result-actions">
<view class="action-button cancel" @click="cancelVoice">取消</view>
<view class="action-button voice">发送原语音</view>
<view class="action-button send" @click="sendVoiceMessage">发送</view>
</view>
</view>
</uni-popup>
</template>
<script setup>
import { ref, watch, nextTick, onMounted, onUnmounted } from 'vue'
import { ref, watch } from 'vue'
const props = defineProps({
inputMessage: String,
holdKeyboard: Boolean,
isSessionActive: Boolean,
stopRequest: Function
holdKeyboard: Boolean
})
const emit = defineEmits(['update:inputMessage', 'send', 'noHideKeyboard', 'keyboardShow', 'keyboardHide', 'sendVoice'])
const emit = defineEmits(['update:inputMessage', 'send', 'noHideKeyboard'])
const textareaRef = ref(null)
const placeholder = ref('快告诉朵朵您在想什么~')
const inputMessage = ref(props.inputMessage || '')
const isFocused = ref(false)
const keyboardHeight = ref(0)
const isVoiceMode = ref(false)
const isRecording = ref(false)
const recordingTime = ref(0)
const recordingTimer = ref(null)
const voiceText = ref('')
const showVoiceResult = ref(false)
const isSlideToText = ref(false)
//
watch(() => props.inputMessage, (val) => {
inputMessage.value = val
})
// /
const toggleVoiceMode = () => {
isVoiceMode.value = !isVoiceMode.value
}
//
const startRecording = () => {
isRecording.value = true
return
recordingTime.value = 0
//
recordingTimer.value = setInterval(() => {
recordingTime.value += 1
}, 1000)
// uni-appAPI
uni.startRecord({
success: (res) => {
//
const tempFilePath = res.tempFilePath
//
//
setTimeout(() => {
voiceText.value = '这是语音转文字的结果'
showVoiceResult.value = true
}, 1000)
},
fail: (err) => {
console.error('录音失败:', err)
isRecording.value = false
clearInterval(recordingTimer.value)
}
})
}
//
const stopRecording = () => {
isRecording.value = false
return
clearInterval(recordingTimer.value)
// 1
if (recordingTime.value < 1) {
uni.stopRecord()
return
}
//
uni.stopRecord()
}
//
const handleTouchMove = (e) => {
//
// UI
const touchY = e.touches[0].clientY
//
isSlideToText.value = touchY < 200
}
//
const sendVoiceMessage = () => {
//
emit('sendVoice', {
text: voiceText.value,
//
})
showVoiceResult.value = false
isVoiceMode.value = false
}
//
const cancelVoice = () => {
showVoiceResult.value = false
isVoiceMode.value = false
}
//
onMounted(() => {
//
uni.onKeyboardHeightChange((res) => {
keyboardHeight.value = res.height
if (res.height > 0) {
emit('keyboardShow', res.height)
} else {
emit('keyboardHide')
}
})
})
const sendMessage = () => {
if (props.isSessionActive) {
//
if (props.stopRequest) {
props.stopRequest();
}
} else {
//
if (!inputMessage.value.trim()) return;
emit('send', inputMessage.value)
//
if (props.holdKeyboard && textareaRef.value) {
nextTick(() => {
textareaRef.value.focus()
})
}
}
if (!inputMessage.value.trim()) return;
emit('send', inputMessage.value)
inputMessage.value = ''
emit('update:inputMessage', inputMessage.value)
}
const handleFocus = () => {
isFocused.value = true
const handleNoHideKeyboard = () => {
emit('noHideKeyboard')
}
const handleBlur = () => {
isFocused.value = false
}
const handleTouchStart = () => {
emit('noHideKeyboard')
}
const handleTouchEnd = () => {
emit('noHideKeyboard')
}
//
const focusInput = () => {
if (textareaRef.value) {
textareaRef.value.focus()
}
}
//
const blurInput = () => {
if (textareaRef.value) {
textareaRef.value.blur()
}
}
//
defineExpose({
focusInput,
blurInput,
isFocused,
toggleVoiceMode
})
</script>
<style scoped lang="scss">
@ -272,8 +64,6 @@ defineExpose({
background-color: #FFFFFF;
box-shadow: 0px 0px 20px 0px rgba(52,25,204,0.05);
margin: 0 12px;
/* 确保输入框在安全区域内 */
margin-bottom: 8px;
.input-container-voice {
display: flex;
@ -283,7 +73,6 @@ defineExpose({
height: 44px;
flex-shrink: 0;
align-self: flex-end;
cursor: pointer;
image {
width: 22px;
@ -291,26 +80,6 @@ defineExpose({
}
}
.input-button-container {
flex: 1;
position: relative;
height: 44px;
}
.hold-to-talk-button {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
color: #333333;
font-size: 16px;
display: flex;
justify-content: center;
align-items: center;
background-color: #FFFFFF;
}
.input-container-send {
display: flex;
align-items: center;
@ -320,14 +89,6 @@ defineExpose({
flex-shrink: 0;
align-self: flex-end;
.input-container-send-btn {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
image {
width: 28px;
height: 28px;
@ -335,116 +96,13 @@ defineExpose({
}
.textarea {
flex: 1;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
flex: 1;
max-height: 92px;
min-height: 44px;
min-height: 22px;
font-size: 16px;
line-height: 22px;
padding: 3px 0 0;
margin-bottom: 2px;
align-items: center;
justify-content: center;
}
/* 确保textarea在iOS上的样式正常 */
.textarea::-webkit-input-placeholder {
color: #CCCCCC;
}
.textarea:focus {
outline: none;
}
}
/* 录音弹窗样式 */
.recording-popup {
width: 280px;
height: 280px;
background-color: rgba(0, 0, 0, 0.7);
border-radius: 20px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
}
.recording-wave {
width: 160px;
height: 160px;
border-radius: 50%;
background-color: rgba(76, 217, 100, 0.3);
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20px;
}
.wave-animation {
width: 100px;
height: 100px;
/* 这里可以添加波形动画 */
background-image: url('/static/wave_icon.png');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.recording-text {
font-size: 16px;
margin-bottom: 20px;
}
.recording-cancel {
font-size: 14px;
color: #CCCCCC;
}
/* 语音结果弹窗样式 */
.voice-result-popup {
width: 300px;
background-color: white;
border-radius: 16px;
padding: 20px;
}
.voice-result-bubble {
background-color: #4CD964;
color: white;
padding: 15px;
border-radius: 18px;
border-top-left-radius: 4px;
margin-bottom: 20px;
min-height: 60px;
font-size: 16px;
}
.voice-result-actions {
display: flex;
justify-content: space-between;
}
.action-button {
padding: 8px 16px;
border-radius: 20px;
font-size: 14px;
}
.cancel {
color: #666666;
background-color: #F5F5F5;
}
.voice {
color: #007AFF;
background-color: #E8F0FE;
}
.send {
color: white;
background-color: #007AFF;
}
</style>

View File

@ -61,27 +61,22 @@
<view class="footer-area">
<ChatQuickAccess @replySent="handleReplyInstruct"/>
<ChatInputArea
ref="inputAreaRef"
v-model="inputMessage"
:holdKeyboard="holdKeyboard"
:is-session-active="isSessionActive"
:stop-request="stopRequest"
@send="sendMessageAction"
@noHideKeyboard="handleNoHideKeyboard"
@keyboardShow="handleKeyboardShow"
@keyboardHide="handleKeyboardHide"
/>
v-model="inputMessage"
:holdKeyboard="holdKeyboard"
@send="sendMessageAction"
@noHideKeyboard="handleNoHideKeyboard"
/>
</view>
</view>
</view>
</template>
<script setup >
import { onMounted, nextTick, computed } from 'vue'
import { onMounted, nextTick } from 'vue'
import { ref } from 'vue'
import { defineEmits } from 'vue'
import { onLoad } from '@dcloudio/uni-app';
import { SCROLL_TO_BOTTOM, RECOMMEND_POSTS_TITLE, SEND_COMMAND_TEXT } from '@/constant/constant'
import { SCROLL_TO_BOTTOM, RECOMMEND_POSTS_TITLE } from '@/constant/constant'
import { MessageRole, MessageType, CompName } from '../../model/ChatModel';
import ChatTopWelcome from './ChatTopWelcome.vue';
@ -111,18 +106,12 @@
///
const statusBarHeight = ref(20);
///
const inputAreaRef = ref(null);
const timer = ref(null)
/// focus
const holdKeyboard = ref(false)
///
const holdKeyboardFlag = ref(true)
///
const keyboardHeight = ref(0)
///
const isKeyboardShow = ref(false)
///
const scrollTop = ref(99999);
@ -131,8 +120,6 @@
const chatMsgList = ref([])
///
const inputMessage = ref('')
///
let currentAIMsgIndex = 0
///
const sceneId = ref('')
@ -144,15 +131,11 @@
const mainPageDataModel = ref({})
//
const isSessionActive = ref(false);
//
const requestTaskRef = ref(null);
let isSessionActive = false;
///
let commonType = ''
//
const emits = defineEmits(['openDrawer'])
const openDrawer = () => {
@ -161,37 +144,23 @@
}
const handleTouchEnd = () => {
// #ifdef MP-WEIXIN
clearTimeout(timer.value)
timer.value = setTimeout(() => {
//
if (holdKeyboardFlag.value && isKeyboardShow.value) {
if (handleNoHideKeyboard) {
uni.hideKeyboard()
}
holdKeyboardFlag.value = true
}, 100)
}, 50)
// #endif
}
//
const handleNoHideKeyboard = () => {
// #ifdef MP-WEIXIN
holdKeyboardFlag.value = false
}
//
const handleKeyboardShow = (height) => {
keyboardHeight.value = height
isKeyboardShow.value = true
holdKeyboard.value = true
//
setTimeout(() => {
scrollToBottom()
}, 150)
}
//
const handleKeyboardHide = () => {
keyboardHeight.value = 0
isKeyboardShow.value = false
holdKeyboard.value = false
// #endif
}
///
@ -235,11 +204,8 @@
if (!inputText.trim()) return;
handleNoHideKeyboard()
sendMessage(inputText)
//
if (holdKeyboard.value && inputAreaRef.value) {
setTimeout(() => {
inputAreaRef.value.focusInput()
}, 100)
if(!isSessionActive) {
inputMessage.value = ''
}
setTimeoutScrollToBottom()
}
@ -272,15 +238,6 @@
handleReply(value)
}
})
uni.$on(SEND_COMMAND_TEXT, (value) => {
console.log('SEND_COMMAND_TEXT:', value)
if(value && value.length > 0) {
commonType = 'Command.quickBooking'
sendMessage(value, true)
setTimeoutScrollToBottom()
}
})
}
/// id
@ -322,14 +279,14 @@
///
const sendMessage = (message, isInstruct = false) => {
if (isSessionActive.value) {
if (isSessionActive) {
uni.showToast({
title: '请等待当前回复完成',
icon: 'none'
});
return;
}
isSessionActive.value = true;
isSessionActive = true;
const newMsg = {
msgId: `msg_${chatMsgList.value.length}`,
msgType: MessageRole.ME,
@ -340,7 +297,6 @@
}
}
chatMsgList.value.push(newMsg)
inputMessage.value = '';
sendChat(message, isInstruct)
console.log("发送的新消息:",JSON.stringify(newMsg))
}
@ -372,7 +328,6 @@
}
chatMsgList.value.push(aiMsg)
const aiMsgIndex = chatMsgList.value.length - 1
currentAIMsgIndex = aiMsgIndex
//
let dotCount = 1;
@ -390,7 +345,7 @@
}
//
const { promise, requestTask } = agentChatStream(args, (chunk) => {
agentChatStream(args, (chunk) => {
console.log('分段内容:', chunk)
if (chunk && chunk.error) {
chatMsgList.value[aiMsgIndex].msg = '请求错误,请重试';
@ -398,7 +353,7 @@
loadingTimer = null;
isTyping = false;
typeWriterTimer = null;
isSessionActive.value = false; //
isSessionActive = false; //
console.error('流式错误:', chunk.message, chunk.detail);
return;
}
@ -449,22 +404,14 @@
chatMsgList.value[aiMsgIndex].question = chunk.question
}
isSessionActive.value = false;
isSessionActive = false;
scrollToBottom();
}
}, 50);
}
})
//
requestTaskRef.value = requestTask;
// Promise/
promise.then(data => {
console.log('请求完成');
}).catch(err => {
isSessionActive.value = false; //
console.log('error:', err);
}).catch(e => {
isSessionActive = false; //
console.log('error:', e)
});
//
@ -485,34 +432,6 @@
}
//
const stopRequest = () => {
if (requestTaskRef.value && requestTaskRef.value.abort) {
//
requestTaskRef.value.isAborted = true;
//
requestTaskRef.value.abort();
//
isSessionActive.value = false;
const msg = chatMsgList.value[currentAIMsgIndex].msg;
if (!msg || msg === '加载中.' || msg.startsWith('加载中')) {
chatMsgList.value[currentAIMsgIndex].msg = '已终止请求,请重试';
}
//
if (loadingTimer) {
clearInterval(loadingTimer);
loadingTimer = null;
}
if (typeWriterTimer) {
clearTimeout(typeWriterTimer);
typeWriterTimer = null;
}
setTimeoutScrollToBottom()
//
requestTaskRef.value = null;
}
}
</script>
<style lang="scss" scoped>

View File

@ -1,4 +1,4 @@
.chat-container {
.chat-container {
width: 100vw;
height: 100vh;
background-color: #E9F3F7;
@ -7,8 +7,6 @@
flex-direction: column;
overflow: hidden !important;
position: relative;
/* 确保在键盘弹起时布局正确 */
box-sizing: border-box;
.chat-container-bg {
position: fixed;
@ -22,10 +20,12 @@
.chat-content {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
z-index: 1;
overflow: hidden;
}
@ -77,19 +77,13 @@
.footer-area {
width: 100vw;
flex-shrink: 0;
padding: 4px 0 20px 0; /* 直接设置20px底部安全距离 */
padding: 4px 0 24px 0;
background-color: #E9F3F7;
touch-action: pan-x;
overflow-x: auto;
overflow-y: hidden;
touch-action: pan-x; /* 仅允许横向触摸滚动 */
overflow-x: auto; /* 允许横向滚动 */
overflow-y: hidden; /* 禁止垂直滚动 */
/* 确保高度能够正确计算 */
min-height: fit-content;
/* 安卓键盘适配 - 使用相对定位配合adjustPan */
position: relative;
z-index: 1;
transition: padding-bottom 0.3s ease;
/* 确保输入区域始终可见 */
transform: translateZ(0);
-webkit-transform: translateZ(0);
}
.area-input {

View File

@ -31,7 +31,7 @@
</view>
<!-- 底部退出按钮 -->
<text class="logout-btn" @click="handleLogout">退出登录</text>
<button class="logout-btn" @click="handleLogout">退出登录</button>
</view>
</template>
@ -140,11 +140,8 @@ const handleLogout = () => {
}
.logout-btn {
display: flex;
align-items: center;
justify-content: center;
height: 42px;
margin-top: 40px;
width: 90%;
margin: 80rpx auto;
background-color: #fff;
color: #333;
border-radius: 8rpx;

View File

@ -8,7 +8,7 @@
:interval="interval"
:duration="duration">
<swiper-item v-for="item in activityList" :key="item.id">
<view class="swiper-item" @click="handleClick(item)">
<view class="swiper-item">
<image :src="item.activityCover" mode="aspectFill"></image>
<view class="corner-btn">快速预定</view>
</view>
@ -20,7 +20,6 @@
<script setup>
import { ref } from 'vue'
import { SEND_COMMAND_TEXT } from '@/constant/constant'
const autoplay = ref(true)
const interval = ref(3000)
@ -33,10 +32,6 @@
}
})
const handleClick = (item) => {
uni.$emit(SEND_COMMAND_TEXT, '快速预定')
}
</script>
<style scoped lang="scss">

View File

@ -3,18 +3,18 @@
<ModuleTitle :title="commodityDTO.title" />
<view class="container-scroll">
<view class="mk-card-item" v-for="(item) in commodityDTO.commodityList" :key="item.commodityName">
<!-- <view class="card-badge">超值推荐</view> -->
<image class="card-img" :src="item.commodityIcon" mode="aspectFill" />
<view class="card-badge">超值推荐</view>
<image class="card-img" :src="item.commodityIcon" mode="widthFix" />
<view class="card-content">
<view class="card-title-column">
<text class="card-title">{{ item.commodityName }}</text>
<view class="card-tags" v-for="(tag) in item.commodityTradeRuleList" :key="tag">
<text class="card-tag">{{ tag }}</text>
<view class="card-tags">
<text class="card-tag">随时可退</text>
<text class="card-tag">民俗表演</text>
</view>
</view>
<template v-for="(serviceItem, index) in item.commodityServices" :key="serviceItem.serviceTitle">
<view v-if="index < 3" class="card-desc">· {{ serviceItem.serviceTitle }}</view>
</template>
<view class="card-desc">· 往返观光车票</view>
<view class="card-desc">· 营业时间9:00-22:00</view>
<view class="card-bottom-row">
<view class="card-price-row">
<text class="card-price-fu"></text>
@ -72,7 +72,7 @@
flex-direction: column;
align-items: start;
width: 188px;
// height: 244px;
height: 244px;
background-color: #ffffff;
border-radius: 10px;
margin-right: 8px;
@ -94,42 +94,27 @@
width: 188px;
height: 114px;
border-radius: 10px;
object-fit: cover; /* 确保图片不变形,保持比例裁剪 */
flex-shrink: 0; /* 防止图片被压缩 */
}
.card-content {
box-sizing: border-box;
box-sizing: border-box;
padding: 10px 12px 0 12px;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: start;
width: 100%;
flex: 1; /* 让内容区域占据剩余空间 */
overflow: hidden; /* 防止内容溢出 */
}
.card-title-column {
display: flex;
align-items: start;
flex-direction: column;
width: 100%;
}
.card-title {
font-size: 16px;
font-weight: bold;
color: #222;
width: 100%;
/* 限制标题最多显示两行 */
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.4;
max-height: 2.8em; /* 2行的高度 */
}
.card-tags {

View File

@ -7,17 +7,16 @@ const API = '/agent/assistant/chat';
* 获取AI聊天流式信息仅微信小程序支持
* @param {Object} params 请求参数
* @param {Function} onChunk 回调每收到一段数据触发
* @returns {Object} 包含Promise和requestTask的对象
* @returns {Promise}
*/
function agentChatStream(params, onChunk) {
let requestTask;
const promise = new Promise((resolve, reject) => {
return new Promise((resolve, reject) => {
const token = uni.getStorageSync('token');
let hasError = false;
console.log("发送请求内容: ", params)
// #ifdef MP-WEIXIN
requestTask = uni.request({
const requestTask = uni.request({
url: BASE_URL + API, // 替换为你的接口地址
method: 'POST',
data: params,
@ -29,21 +28,22 @@ function agentChatStream(params, onChunk) {
},
responseType: 'arraybuffer',
success(res) {
resolve(res.data);
resolve(res.data);
},
fail(err) {
console.log("====> ", JSON.stringify(err))
reject(err);
reject(err);
},
complete(res) {
complete(res) {
if(res.statusCode !== 200) {
console.log("====> ", JSON.stringify(res))
if (onChunk) {
onChunk({ error: true, message: '服务器错误', detail: res });
}
reject(res);
}
}
console.log("====> ", JSON.stringify(res))
if (onChunk) {
onChunk({ error: true, message: '服务器错误', detail: res });
}
reject(res);
}
}
});
requestTask.onHeadersReceived(res => {
@ -59,12 +59,11 @@ function agentChatStream(params, onChunk) {
});
requestTask.onChunkReceived(res => {
// 检查请求是否已被中止
if (hasError || requestTask.isAborted) return;
if (hasError) return;
const base64 = uni.arrayBufferToBase64(res.data);
let data = '';
try {
data = decodeURIComponent(escape(weAtob(base64)));
data = decodeURIComponent(escape(atob(base64)));
} catch (e) {
// 某些平台可能不支持 atob可以直接用 base64
data = base64;
@ -76,62 +75,8 @@ function agentChatStream(params, onChunk) {
});
// #endif
});
return {
promise,
requestTask
};
}
// window.atob兼容性处理
const weAtob = (string) => {
const b64re = /^(?:[A-Za-z\d+/]{4})*?(?:[A-Za-z\d+/]{2}(?:==)?|[A-Za-z\d+/]{3}=?)?$/;
const b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
// 去除空白字符
string = String(string).replace(/[\t\n\f\r ]+/g, '');
// 验证 Base64 编码
if (!b64re.test(string)) {
throw new TypeError(
// eslint-disable-next-line quotes
"Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded."
);
}
// 填充字符
string += '=='.slice(2 - (string.length & 3));
let bitmap,
result = '',
r1,
r2,
i = 0;
for (; i < string.length;) {
bitmap =
(b64.indexOf(string.charAt(i++)) << 18) |
(b64.indexOf(string.charAt(i++)) << 12) |
((r1 = b64.indexOf(string.charAt(i++))) << 6) |
(r2 = b64.indexOf(string.charAt(i++)));
if (r1 === 64) {
result += String.fromCharCode((bitmap >> 16) & 255);
} else if (r2 === 64) {
result += String.fromCharCode(
(bitmap >> 16) & 255,
(bitmap >> 8) & 255
);
} else {
result += String.fromCharCode(
(bitmap >> 16) & 255,
(bitmap >> 8) & 255,
bitmap & 255
);
}
}
return result;
};
// 解析SSE分段数据
function parseSSEChunk(raw) {
// 拆分为多段

View File

@ -1,5 +1,4 @@
import { BASE_URL } from "../../constant/base";
import { goLogin } from "@/hooks/useGoLogin";
const defaultConfig = {
header: {
@ -50,10 +49,6 @@ function request(url, args = {}, method = 'POST', customConfig = {}) {
success: (res) => {
console.log("请求响应:" + JSON.stringify(res))
resolve(res.data)
if(res.statusCode && res.statusCode === 424) {
uni.setStorageSync('token', '')
goLogin();
}
},
fail: (err) => {
console.error("请求失败:", err);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB