Compare commits

..

No commits in common. "8d3b8d3b3cefb6b2d2dbb7c2a0542e3fe111c098" and "316e8101c32164f0d048b425dd5d3c1ce890e339" have entirely different histories.

3 changed files with 97 additions and 231 deletions

View File

@ -97,27 +97,33 @@
> >
<view class="flex pr-1" @click="previewMoreImg(msg.content)"> <view class="flex pr-1" @click="previewMoreImg(msg.content)">
<view <view
v-for="(file, fileIdx) in msg.content.slice(0, 4)" v-for="(file, fileIdx) in msg.content"
:key="fileIdx" :key="fileIdx"
class="relative rounded-md overflow-hidden ml-2" class="relative rounded-md overflow-hidden mr-1"
:class="{ :class="{
'w-60 h-60': msg.content.length == 1, 'w-60 h-60': msg.content.length == 1,
'w-15 h-15': msg.content.length == 2, 'w-15 h-15': msg.content.length == 2,
'w-30 h-15 ': msg.content.length == 3, 'w-30 h-15 ': msg.content.length == 3,
'w-80 h-15 ': msg.content.length >= 4, ' h-15 flex-grow-0 flex-shrink-0 basis-10': msg.content.length >= 4,
}" }"
> >
<view <view
v-if="fileIdx >= 3" v-if="showImageMask && fileIdx === 4"
class="absolute inset-0 flex items-center justify-center bg-black bg-opacity-70 z-80" class="absolute top-0 left-0 right-0 bottom-0 flex items-center justify-center"
:class="{ 'bg-black bg-opacity-70': showImageMask && fileIdx === 4 }"
> >
+ {{ msg.content.length - 4 }} +{{ msg.content.length - 4 }}
</view> </view>
<image <image
v-if="file.uploadFileType === uploadFileTypeEm.image" v-if="file.uploadFileType === uploadFileTypeEm.image"
:src="file.url || file.tempFilePath" :src="file.url || file.tempFilePath"
class="w-full h-full object-cover" :class="{
'w-100% h-100%': msg.content.length == 1,
'w-15 h-15': msg.content.length == 2,
'w-40 h-15 ': msg.content.length == 3,
'w-40 h-15 object-cover': msg.content.length >= 4,
}"
/> />
</view> </view>
</view> </view>
@ -213,56 +219,55 @@
transition: 'transform 0.3s ease', transition: 'transform 0.3s ease',
}" }"
> >
<div class="flex overflow-x-auto space-x-3 py-2 w-88 flex-nowrap"> <div
v-for="item in uploadList"
:key="item.id"
style="flex: 0 0 4rem"
class="relative w-16 h-16 rounded overflow-hidden"
>
<!-- 预览图成功后用后端返回的 URL上传中可以先用本地预览 -->
<img
v-if="item.uploadFileType !== uploadFileTypeEm.file"
:src="item.url || item.tempFilePath"
class="w-full h-full object-cover"
@click="previewImage(item.url)"
/>
<view v-else class="text-xs text-gray-400 mt-1" @click="previewFile(item.url)">
{{ item.name }}
</view>
<!-- 关闭按钮 -->
<div <div
v-for="item in uploadList" class="absolute top-1 right-1 w-4 h-4 rounded-full bg-black bg-opacity-50 flex items-center justify-center cursor-pointer text-white text-xs z-5"
:key="item.id" @click="removeImage(item.id)"
class="relative w-16 h-16 rounded overflow-hidden flex-shrink-0"
> >
<!-- 预览图成功后用后端返回的 URL上传中可以先用本地预览 --> ×
<img </div>
v-if="item.uploadFileType !== uploadFileTypeEm.file"
:src="item.url || item.tempFilePath"
class="w-full h-full object-cover"
@click="previewImage(item.url)"
/>
<view v-else class="text-xs text-gray-400 mt-1" @click="previewFile(item.url)"> <!-- 重试 -->
{{ item.name }} <span
</view> v-if="item.status === 'error'"
class="absolute w-full h-full bg-black bg-opacity-40 pt-1 left-0 top-0 text-center color-white text-3xl"
@click.stop="retry(item)"
>
</span>
<!-- 关闭按钮 --> <!-- 进度 / 成功 / 失败 -->
<div <div
class="absolute top-1 right-1 w-4 h-4 rounded-full bg-black bg-opacity-50 flex items-center justify-center cursor-pointer text-white text-xs z-5" class="absolute bottom-0 left-0 w-full text-xs text-center py-1 bg-black bg-opacity-50"
@click="removeImage(item.id)" :class="{
> 'text-black': item.status === 'uploading',
× 'text-green': item.status === 'success',
</div> 'text-red': item.status === 'error',
}"
<!-- 重试 --> >
<span <template v-if="item.status === 'uploading'">
v-if="item.status === 'error'" <view class="text-white">{{ item.progress }}%</view>
class="absolute w-full h-full bg-black bg-opacity-40 pt-1 left-0 top-0 text-center color-white text-3xl" </template>
@click.stop="retry(item)" <template v-else-if="item.status === 'success'"> 成功</template>
> <template v-else> 失败</template>
</span>
<!-- 进度 / 成功 / 失败 -->
<div
class="absolute bottom-0 left-0 w-full text-xs text-center py-1 bg-black bg-opacity-50"
:class="{
'text-black': item.status === 'uploading',
'text-green': item.status === 'success',
'text-red': item.status === 'error',
}"
>
<template v-if="item.status === 'uploading'">
<view class="text-white">{{ item.progress }}%</view>
</template>
<template v-else-if="item.status === 'success'"> 成功</template>
<template v-else> 失败</template>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -418,7 +423,7 @@ interface IMessage {
timestamp: Date timestamp: Date
} }
const userAvatar = ref('') const userAvatar = ref('')
const chatMode = ref('qwen-vl-plus') const chatMode = ref('tongyi-app')
const baseUrl = getEnvBaseUrl() const baseUrl = getEnvBaseUrl()
const messages = reactive<IMessage[]>([]) const messages = reactive<IMessage[]>([])
@ -524,7 +529,7 @@ async function fetchHistoryList() {
method: 'POST', method: 'POST',
data: { data: {
page: 1, page: 1,
pageSize: 9990, pageSize: 30,
}, },
header: { header: {
Authorization: token.value, Authorization: token.value,
@ -534,7 +539,9 @@ async function fetchHistoryList() {
rawList.value = resp.data.data.data rawList.value = resp.data.data.data
console.log('fetchHistoryList →', rawList.value) console.log('fetchHistoryList →', rawList.value)
} }
} catch (err) {} } catch (err) {
console.error('fetchHistoryList error:', err)
}
} }
/** 3. 拉取历史记录详情 */ /** 3. 拉取历史记录详情 */
@ -551,16 +558,16 @@ async function fetchHistoryDiets(value) {
Authorization: token.value, Authorization: token.value,
}, },
}) })
console.log(resp, '/**************resp*********************/')
if (resp && resp.data) { if (resp && resp.data) {
const rawList = resp.data.data // const rawList = resp.data.data //
listUuid.value = resp.data.data[0].listUuid
const newMessages = parseBackendMessages(rawList) const newMessages = parseBackendMessages(rawList)
// //
messages.splice(0, messages.length, ...newMessages) messages.splice(0, messages.length, ...newMessages)
} }
} catch (err) {} } catch (err) {
console.error('fetchHistoryList error:', err)
}
} }
function parseBackendMessages(rawList: any[]): IMessage[] { function parseBackendMessages(rawList: any[]): IMessage[] {
@ -577,20 +584,15 @@ function parseBackendMessages(rawList: any[]): IMessage[] {
const arr = JSON.parse(rawContent) as Array<{ text: string; type: string }> const arr = JSON.parse(rawContent) as Array<{ text: string; type: string }>
if (Array.isArray(arr)) { if (Array.isArray(arr)) {
parts = arr.map((el) => { parts = arr.map((el) => {
if (el.type === 'file_url') { if (el.type === 'text') {
return { return { type: 'text', content: el.text }
type: 'video', }
content: [{ url: el.text, uploadFileType: 'video' }], if (el.type === 'image_url' || el.type === 'image') {
}
} else if (el.type === 'image_url' || el.type === 'image') {
return { return {
type: 'image', type: 'image',
content: [{ url: el.text, uploadFileType: 'image' }], content: [{ url: el.text, uploadFileType: 'image' }],
} }
} else if (el.type === 'text') {
return { type: 'text', content: el.text }
} }
// //
return { type: el.type, content: el.text } return { type: el.type, content: el.text }
}) })
@ -697,6 +699,7 @@ const mask = ref('')
onMounted(() => { onMounted(() => {
// 1. init Extras // 1. init Extras
const init = async () => { const init = async () => {
const wv = plus.webview.currentWebview() const wv = plus.webview.currentWebview()
// token.value = wv.token || uni.getStorageSync('token') || import.meta.env.VITE_DEV_TOKEN // token.value = wv.token || uni.getStorageSync('token') || import.meta.env.VITE_DEV_TOKEN
@ -715,6 +718,7 @@ function scrollToBottom() {
const el = scrollEl.value! const el = scrollEl.value!
nextTick(() => { nextTick(() => {
// el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' }) // el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' })
console.log(el.scrollHeight, 'el.scrollHeight')
el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' }) el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' })
}) })
} }
@ -774,13 +778,9 @@ async function newChat() {
} }
const rotation = ref(0) const rotation = ref(0)
function toggleActions() { function toggleActions() {
uni.hideKeyboard() closeKeyboard()
rotation.value += 45
console.log(rotation.value, '2')
showActions.value = !showActions.value showActions.value = !showActions.value
rotation.value += 45
scrollToBottom() scrollToBottom()
} }
@ -791,10 +791,10 @@ const uploadFileTypeEm = {
file: 'file', file: 'file',
text: 'text', text: 'text',
} }
// const url=baseUrl
// //
const uploadConfig = reactive({ const uploadConfig = reactive({
url: `${baseUrl}/upload/img`, url: 'http://114.218.158.24:9020/upload/img',
formData: { formData: {
source: 'chat', source: 'chat',
mask: mask.value, mask: mask.value,
@ -1032,51 +1032,11 @@ function previewImage(url: string) {
} }
// //
// previewFile
const previewFile = (url: string) => { const previewFile = (url: string) => {
if (typeof plus !== 'undefined') { //webview
// App + uni.navigateTo({
downloadAndOpenFile(url) url: '/pages/webview/index?link=' + encodeURIComponent(url),
} else {
// H5 退 WebView
uni.navigateTo({
url: '/pages/webview/index?link=' + encodeURIComponent(url),
})
}
}
const downloadAndOpenFile = (downloadUrl: string) => {
uni.showLoading({ title: '加载中...', mask: true })
if (!downloadUrl) {
uni.hideLoading()
return uni.showToast({ title: '文件路径无效', icon: 'none' })
}
//
const options = {
// _downloads/ Documents/download
filename: '_downloads/',
}
const dtask = plus.downloader.createDownload(downloadUrl, options, (d, status) => {
uni.hideLoading()
if (status === 200) {
const savedPath = d.filename
if (savedPath) {
// PDF/Word/Excel//
plus.runtime.openFile(savedPath, {}, () => {
//
})
} else {
uni.showToast({ title: '文件保存失败', icon: 'none' })
}
} else {
uni.showToast({ title: '下载失败', icon: 'error' })
}
}) })
dtask.start()
} }
const msgLoading = ref(true) const msgLoading = ref(true)
// //
@ -1257,41 +1217,19 @@ function copyText(msg: IMessage) {
} }
} }
//
function refreshText() { function refreshText() {
// const lastUserMsg = historyUserMsgs[historyUserMsgs.length - 1]
const lastUserMsg = messages.findLast((msg) => msg.role === 'user') lastUserMsg.timestamp = new Date()
if (!lastUserMsg) return sendText(lastUserMsg)
//
const clonedMsg = {
...lastUserMsg,
timestamp: new Date(),
}
//
sendText(clonedMsg)
// //
// const lastUserMsg = messages.findLast((msg) => msg.role === 'user')
// if (!lastUserMsg) return
// //
// const newUserMsg = {
// ...lastUserMsg,
// timestamp: new Date(),
// }
// const historyToSend = [...messages, newUserMsg]
// //
// sendText(historyToSend)
} }
const knowledgeOpen = ref(false) const knowledgeOpen = ref(false)
function toggleKnowledge() { function toggleKnowledge() {
console.error('44444', chatMode.value) console.error('44444', chatMode.value)
if (chatMode.value == 'qwen-vl-plus') { if (chatMode.value == 'tongyi-app') {
chatMode.value = 'tongyi-app'
} else {
chatMode.value = 'qwen-vl-plus' chatMode.value = 'qwen-vl-plus'
} else {
chatMode.value = 'tongyi-app'
} }
knowledgeOpen.value = !knowledgeOpen.value knowledgeOpen.value = !knowledgeOpen.value
showActions.value = false showActions.value = false
@ -1372,7 +1310,7 @@ const onFocus = () => {
border-radius: 12rpx 12rpx 0 0; border-radius: 12rpx 12rpx 0 0;
} }
.popup.fullscreen { .popup.fullscreen {
height: 90%; height: 100%;
border-radius: 0; border-radius: 0;
} }
/* Header */ /* Header */
@ -1417,7 +1355,4 @@ const onFocus = () => {
.tops { .tops {
padding-top: var(--status-bar-height); padding-top: var(--status-bar-height);
} }
.flex-i {
display: flex !important;
}
</style> </style>

View File

@ -1,5 +1,5 @@
<template> <template>
<view class="flex flex-col h-90% bg-#ffffff"> <view class="flex flex-col h-screen bg-#ffffff tops">
<view <view
class="flex-none flex items-center justify-between px-5 py-3 bg-white shadow-md h-20 pt-10 z-999 fixed top-0 w-full box-border" class="flex-none flex items-center justify-between px-5 py-3 bg-white shadow-md h-20 pt-10 z-999 fixed top-0 w-full box-border"
> >
@ -11,36 +11,14 @@
:key="index" :key="index"
class="aspect-square overflow-hidden group" class="aspect-square overflow-hidden group"
> >
<image <!-- <image
:src="image.url" :src="image.url"
mode="aspectFill" mode="aspectFill"
class="w-full h-full" class="w-full h-full"
@click="handleImageClick(index)" @click="handleImageClick(index)"
/> /> -->
<!-- <wd-img :width="100" :height="100" :src="image.url" :enable-preview="true" /> --> <wd-img :width="100" :height="100" :src="image.url" :enable-preview="true" />
</view>
</view>
<!-- 自定义预览弹窗 -->
<view
v-if="showPreview"
class="fixed inset-0 bg-black bg-opacity-90 z-999 flex justify-center items-start pt-10"
@click="closePreview"
>
<view class="relative w-full max-w-full mt-8 p-5 box-border">
<image
class="w-full max-h-[calc(100vh-90px)] object-contain"
:src="previewUrl"
mode="widthFix"
@click.stop
/>
<view
class="absolute top--8 right-5 text-white text-3xl z-1000 w-10 h-10 text-center leading-10"
@click.stop="closePreview"
>
×
</view>
</view> </view>
</view> </view>
@ -84,18 +62,10 @@ onMounted(() => {
videoList.value = previewVideos videoList.value = previewVideos
} }
}) })
const showPreview = ref(false)
const previewUrl = ref('')
const previewTop = ref(30) // 30px
const handleImageClick = (index) => {
previewUrl.value = imageList.value[index].url
showPreview.value = true
}
const closePreview = () => {
showPreview.value = false
}
</script> </script>
<style scoped></style> <style scoped>
.tops {
padding-top: var(--status-bar-height);
}
</style>

View File

@ -1,5 +1,5 @@
<template> <template>
<web-view :src="link" :webview-styles="webviewStyles"></web-view> <web-view :src="link"></web-view>
</template> </template>
<script> <script>
@ -7,12 +7,6 @@ export default {
data() { data() {
return { return {
link: '', link: '',
webviewStyles: {
width: '100%',
progress: {
color: '#999',
},
},
} }
}, },
onLoad(options) { onLoad(options) {
@ -20,39 +14,6 @@ export default {
this.link = options.link this.link = options.link
} }
}, },
mounted() {
// Webview plus
// if (window.plus) {
// const wv = plus.webview.currentWebview()
// // padding
// wv.addEventListener('loaded', () => {
// wv.evalJS(`
// document.documentElement.style.padding = '30px';
// document.body.style.margin = '0';
// `)
// })
// }
uni.getSystemInfo({
success: (res) => {
//
const statusBarHeight = res.statusBarHeight
const windowHeight = res.windowHeight
// WebView
this.webviewStyles.height = windowHeight - statusBarHeight
},
fail: (err) => {
console.error('获取系统信息失败:', err)
// 使
uni.getSystemInfo({
success: (res) => {
this.webviewStyles.height = res.windowHeight
},
})
},
})
},
} }
</script> </script>