style(sass): 调整聊天图标样式

- 调整聊天图标的左右边距- 优化 voice.svg 图标的视觉效果
- 修复公告组件中的样式问题
- 优化热门产品、消息、导航列表等组件的样式
This commit is contained in:
陈昱达
2025-06-10 18:18:11 +08:00
parent db11e06a12
commit 7b3947008a
12 changed files with 477 additions and 312 deletions

View File

@@ -3,5 +3,5 @@ NODE_ENV = 'dev' // 如果是生产环境请记得切换为production
# flag # flag
VUE_APP_FLAG='dev' VUE_APP_FLAG='dev'
VUE_APP_BASE='http://ebiz-fooge.320.io:7015' VUE_APP_BASE='https://weixin.devops.ebiz-digits.com:7718'

View File

@@ -55,4 +55,7 @@ body{
.chat-icon{ .chat-icon{
width: 16px; width: 16px;
height: 16px; height: 16px;
margin-left: 0!important;
margin-right: 2px!important;
} }

View File

@@ -1 +1 @@
<svg t="1730875233854" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7506" width="30" height="30"><path d="M512 938.666667C277.333333 938.666667 85.333333 746.666667 85.333333 512S277.333333 85.333333 512 85.333333s426.666667 192 426.666667 426.666667-192 426.666667-426.666667 426.666667z m0-768c-187.733333 0-341.333333 153.6-341.333333 341.333333s153.6 341.333333 341.333333 341.333333 341.333333-153.6 341.333333-341.333333-153.6-341.333333-341.333333-341.333333z" fill="#707070" p-id="7507"></path><path d="M512 256c46.933333 0 85.333333 38.4 85.333333 85.333333v170.666667c0 46.933333-38.4 85.333333-85.333333 85.333333s-85.333333-38.4-85.333333-85.333333V341.333333c0-46.933333 38.4-85.333333 85.333333-85.333333z" fill="#707070" p-id="7508"></path><path d="M512 704c-106.666667 0-192-85.333333-192-192 0-12.8 8.533333-21.333333 21.333333-21.333333s21.333333 8.533333 21.333334 21.333333c0 81.066667 68.266667 149.333333 149.333333 149.333333s149.333333-68.266667 149.333333-149.333333c0-12.8 8.533333-21.333333 21.333334-21.333333s21.333333 8.533333 21.333333 21.333333c0 106.666667-85.333333 192-192 192z" fill="#707070" p-id="7509"></path><path d="M490.666667 682.666667h42.666666v170.666666h-42.666666z" fill="#707070" p-id="7510"></path></svg> <svg t="1730875233854" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7506" width="40" height="40"><path d="M512 938.666667C277.333333 938.666667 85.333333 746.666667 85.333333 512S277.333333 85.333333 512 85.333333s426.666667 192 426.666667 426.666667-192 426.666667-426.666667 426.666667z m0-768c-187.733333 0-341.333333 153.6-341.333333 341.333333s153.6 341.333333 341.333333 341.333333 341.333333-153.6 341.333333-341.333333-153.6-341.333333-341.333333-341.333333z" fill="#707070" p-id="7507"></path><path d="M512 256c46.933333 0 85.333333 38.4 85.333333 85.333333v170.666667c0 46.933333-38.4 85.333333-85.333333 85.333333s-85.333333-38.4-85.333333-85.333333V341.333333c0-46.933333 38.4-85.333333 85.333333-85.333333z" fill="#707070" p-id="7508"></path><path d="M512 704c-106.666667 0-192-85.333333-192-192 0-12.8 8.533333-21.333333 21.333333-21.333333s21.333333 8.533333 21.333334 21.333333c0 81.066667 68.266667 149.333333 149.333333 149.333333s149.333333-68.266667 149.333333-149.333333c0-12.8 8.533333-21.333333 21.333334-21.333333s21.333333 8.533333 21.333333 21.333333c0 106.666667-85.333333 192-192 192z" fill="#707070" p-id="7509"></path><path d="M490.666667 682.666667h42.666666v170.666666h-42.666666z" fill="#707070" p-id="7510"></path></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,19 +1,15 @@
<template> <template>
<div class="announcement"> <div class="announcement">
<van-notice-bar <van-notice-bar background="#2e5ca9" left-icon="volume-o" color="#fff" :text="announcements[0].content" />
background='#2e5ca9'
left-icon="volume-o"
color='#fff'
:text="announcements[0].content"/>
</div> </div>
</template> </template>
<script> <script>
import { NoticeBar } from 'vant'; import { NoticeBar } from 'vant'
export default { export default {
name: 'Announcement', name: 'Announcement',
components: { components: {
[NoticeBar.name]:NoticeBar [NoticeBar.name]: NoticeBar,
}, },
data() { data() {
return { return {
@@ -21,12 +17,12 @@ export default {
{ {
title: '新功能上线', title: '新功能上线',
content: '我们很高兴地宣布,全新的“智能问答”功能现已上线!您可以体验更高效、更智能的问答服务。', content: '我们很高兴地宣布,全新的“智能问答”功能现已上线!您可以体验更高效、更智能的问答服务。',
date: '2023-09-25' date: '2023-09-25',
},
],
}
}, },
]
};
} }
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -71,6 +71,7 @@ export default {
async getHotProducts() { async getHotProducts() {
const res = await haslProducts() const res = await haslProducts()
this.hotProducts = res.content this.hotProducts = res.content
this.$emit('getHotList',this.hotProducts)
}, },
}, },
components: { components: {

View File

@@ -1,16 +1,15 @@
<template> <template>
<van-swipe class="my-swipe"> <van-swipe class="my-swipe">
<van-swipe-item class='item'> <van-swipe-item class="item">
<div v-for='item in navigationItems'> <div v-for="item in navigationItems">
<div class='icon-contact mt20'> <div class="icon-contact mt20">
<svg-icon :icon-class='item.icon' class-name='icon '></svg-icon> <svg-icon :icon-class="item.icon" class-name="icon "></svg-icon>
</div> </div>
<div class='nav-title mb10'>{{item.title}}</div> <div class="nav-title mb10">{{ item.title }}</div>
</div> </div>
</van-swipe-item> </van-swipe-item>
<!-- <van-swipe-item>2</van-swipe-item>--> <!-- <van-swipe-item>2</van-swipe-item>-->
</van-swipe> </van-swipe>
</template> </template>
<script> <script>
@@ -32,19 +31,18 @@ export default {
{ title: '停售产品', icon: 'stopSale' }, { title: '停售产品', icon: 'stopSale' },
{ title: '服务中心', icon: 'service' }, { title: '服务中心', icon: 'service' },
], ],
}; }
}, },
}; }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
// 主题颜色定义 // 主题颜色定义
$primary-color: #2E5CA9; $primary-color: #2e5ca9;
$primary-text-color: #F6AA21; $primary-text-color: #f6aa21;
$primary-trans-color: #87A2D0; $primary-trans-color: #87a2d0;
.my-swipe { .my-swipe {
background: $primary-color; background: $primary-color;
border-radius: 5px; border-radius: 5px;
.item { .item {

View File

@@ -1,24 +1,20 @@
<template> <template>
<div> <div>
<div class='tab-box' v-for='item in list'> <div class="tab-box" v-for="item in list">
<!-- <div class='box-title'>--> <!-- <div class='box-title'>-->
<!-- {{item.title}}--> <!-- {{item.title}}-->
<!-- </div>--> <!-- </div>-->
<div class='box-container'> <div class="box-container">
<van-cell v-for='list in item.list' :class='list.value?"link":"" ' @click='view(list)' is-link> <van-cell v-for="list in item.list" :class="list.value ? 'link' : ''" @click="view(list)" is-link>
{{ list.title }} {{ list.title }}
</van-cell> </van-cell>
</div> </div>
</div> </div>
<van-dialog v-model="show" width="90%" :title="title" confirm-button-color="#2E5CA9">
<div style="height: 70vh; overflow: scroll">
<van-dialog v-model="show" width='90%' :title='title' confirm-button-color='#2E5CA9'> <pdf :url="url" v-if="show"></pdf>
<div style='height: 70vh;overflow: scroll' >
<pdf :url='url' v-if='show'></pdf>
</div> </div>
</van-dialog> </van-dialog>
</div> </div>
</template> </template>
<script> <script>
@@ -30,21 +26,21 @@ export default {
return { return {
url: '', url: '',
show: false, show: false,
title:'' title: '',
} }
}, },
props: { props: {
list: { list: {
type: Array, type: Array,
default: () => [] default: () => [],
} },
}, },
watch: {}, watch: {},
components: { components: {
[Dialog.name]: Dialog, [Dialog.name]: Dialog,
[CellGroup.name]: CellGroup, [CellGroup.name]: CellGroup,
[Cell.name]: Cell, [Cell.name]: Cell,
pdf pdf,
}, },
filters: {}, filters: {},
methods: { methods: {
@@ -56,17 +52,15 @@ export default {
this.show = true this.show = true
}, },
}, },
created() { created() {},
}, mounted() {},
mounted() { computed: {},
},
computed: {}
} }
</script> </script>
<style scoped lang='scss'> <style scoped lang="scss">
// 主题颜色定义 // 主题颜色定义
$primary-color: #2E5CA9; // 修复了 SCSS 变量的定义方式 $primary-color: #2e5ca9; // 修复了 SCSS 变量的定义方式
$primary-text-color: #F6AA21; // 修复了 SCSS 变量的定义方式 $primary-text-color: #f6aa21; // 修复了 SCSS 变量的定义方式
$primary-trans-color: rgba(135, 162, 208, 0.5); // 使用rgba定义颜色透明度为0.8 $primary-trans-color: rgba(135, 162, 208, 0.5); // 使用rgba定义颜色透明度为0.8
.tab-box { .tab-box {
@@ -78,10 +72,9 @@ $primary-trans-color: rgba(135, 162, 208, 0.5); // 使用rgba定义颜色
.box-title { .box-title {
padding: 10px 15px; padding: 10px 15px;
background: $primary-trans-color; background: $primary-trans-color;
color : #2E5CA9; // 修复了 SCSS 变量的定义方式 color: #2e5ca9; // 修复了 SCSS 变量的定义方式
font-size: 13px; font-size: 13px;
font-weight: 600; font-weight: 600;
} }
.box-container { .box-container {
//padding: 10px; //padding: 10px;
@@ -91,15 +84,14 @@ $primary-trans-color: rgba(135, 162, 208, 0.5); // 使用rgba定义颜色
flex-wrap: wrap; flex-wrap: wrap;
//gap: 8px; //gap: 8px;
.link { .link {
div,i{ div,
i {
color: $primary-color; color: $primary-color;
} }
//padding: 5px 12px; //padding: 5px 12px;
border-radius: 5px; border-radius: 5px;
//background: $primary-trans-color; //background: $primary-trans-color;
} }
} }
} }
</style> </style>

View File

@@ -19,14 +19,14 @@
</span> </span>
<div> <div>
<p v-html="md.render(message.text)" v-if="message.text"></p> <p v-html="md.render(message.text)" v-if="message.text"></p>
<span class="speakLoadingToast pv10" v-else-if='!message.text && !thinkOk && !message.isThink'> <span class="speakLoadingToast pv10" v-else-if="!message.text && !thinkOk && !message.isThink">
<van-loading type="spinner" color="#2e5ca9" size="20px" v-if="!message.text" /> <van-loading type="spinner" color="#2e5ca9" size="20px" v-if="!message.text" />
</span> </span>
</div> </div>
</div> </div>
<!--百宝箱--> <!--百宝箱-->
<div v-else class='mb10'> <div v-else class="mb10">
<treasure-box :item="message" @setProductName='setProductName'></treasure-box> <treasure-box :item="message" @setProductName="setProductName"></treasure-box>
</div> </div>
<!-- 新增点赞和踩按钮 --> <!-- 新增点赞和踩按钮 -->
<div class="reaction-buttons mb10" v-if="message.type !== 'user'"> <div class="reaction-buttons mb10" v-if="message.type !== 'user'">
@@ -51,8 +51,7 @@ const md = new MarkdownIt({
html: true, html: true,
linkify: true, linkify: true,
typographer: true, typographer: true,
}) }).use(markdownItKatex)
.use(markdownItKatex)
export default { export default {
name: 'message', name: 'message',
components: { TreasureBox, [Icon.name]: Icon }, components: { TreasureBox, [Icon.name]: Icon },

View File

@@ -1,6 +1,6 @@
<template> <template>
<div style='height: 100%;'> <div style="height: 100%">
<div v-if='!numPages' style='height: 100%;display: flex;align-items: center;justify-content: center;'> <div v-if="!numPages" style="height: 100%; display: flex; align-items: center; justify-content: center">
<van-loading color="#2E5CA9" vertical>加载中...</van-loading> <van-loading color="#2E5CA9" vertical>加载中...</van-loading>
</div> </div>
<pdf-container :src="url" ref="pdfContainer" v-for="index in numPages" :key="index" :page="index"></pdf-container> <pdf-container :src="url" ref="pdfContainer" v-for="index in numPages" :key="index" :page="index"></pdf-container>
@@ -14,17 +14,17 @@ export default {
return { return {
// url: 'https://www.gjtool.cn/pdfh5/git.pdf', // url: 'https://www.gjtool.cn/pdfh5/git.pdf',
// url: 'http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf', // url: 'http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf',
numPages: null numPages: null,
} }
}, },
props: { props: {
url: { url: {
type: String, type: String,
default: '' default: '',
} },
}, },
components: { components: {
pdfContainer pdfContainer,
}, },
created() { created() {
this.getNumPages() this.getNumPages()
@@ -32,17 +32,14 @@ export default {
methods: { methods: {
getNumPages() { getNumPages() {
let loadingTask = pdfContainer.createLoadingTask(this.url) let loadingTask = pdfContainer.createLoadingTask(this.url)
loadingTask.promise.then(pdf => { loadingTask.promise.then((pdf) => {
this.numPages = pdf.numPages this.numPages = pdf.numPages
this.url = loadingTask this.url = loadingTask
}) })
// .catch(err => { // .catch(err => {
// console.log('pdf加载失败', err) // console.log('pdf加载失败', err)
// }) // })
} },
} },
} }
</script> </script>

View File

@@ -0,0 +1,194 @@
<template>
<div
class="sticky"
v-if="productName"
:style="{
color: isDisabled ? '#999' : '#2e5ca9',
}"
>
<!--产品选择-->
<van-dropdown-menu active-color="#2e5ca9" :disabled="isDisabled">
<van-dropdown-item v-model="value1" :options="options" @change="changeName" :disabled="isDisabled">
<template #title>
<div
class="fs14 title"
:style="{
color: isDisabled ? '#999' : '#2e5ca9',
}"
>
{{ value1 }}
</div>
</template>
</van-dropdown-item>
</van-dropdown-menu>
<!--工具箱或其他模块-->
<van-dropdown-menu active-color="#2e5ca9" :disabled="isDisabled" style="width: 50px">
<template> </template>
<van-dropdown-item v-model="value2" :options="treasureList" @change="changeTreasureBox" :disabled="isDisabled" title-class="more-drown">
<template #title>
<van-icon name="ellipsis" class="more-treasure"></van-icon>
</template>
</van-dropdown-item>
</van-dropdown-menu>
</div>
</template>
<script>
import { DropdownMenu, DropdownItem, Cell, Icon } from 'vant'
export default {
name: 'sticky',
components: {
[DropdownMenu.name]: DropdownMenu,
[DropdownItem.name]: DropdownItem,
[Cell.name]: Cell,
[Icon.name]: Icon,
},
props: {
isDisabled: {
type: Boolean,
default: false,
},
autoScrollEnabled: {
type: Boolean,
default: false,
},
messagesList: {
type: Array,
default: () => [],
},
hotList: {
type: Array,
default: () => [],
},
productName: {
type: String,
default: '',
},
},
watch: {
productName: {
handler(val) {
this.value1 = val
console.log(val)
},
},
//
hotList: {
handler(val) {
if (val) {
this.options = val.map((item) => {
return {
text: item.productName,
value: item.productName,
}
})
}
},
},
},
data() {
return {
prdName: '',
value1: '',
options: [],
value2: '',
treasureList: [
{
text: '工具箱',
value: '1',
// icon: 'more-o',
},
],
//
}
},
methods: {
changeTreasureBox(item) {
switch (item) {
case '1':
// this.$emit('view', item)
this.messagesList.push({
type: 'box',
text: this.productName,
})
this.$emit('update:messageList', this.messagesList)
break
}
this.value2 = ''
},
changeName(item) {
this.$emit('cellClick')
this.prdName = item
// this.messagesList.push({
// type: 'box',
// text: item,
// })
// this.$emit('update:messageList', this.messagesList)
// this.$emit('update:autoScrollEnabled', true)
// 只暴露名称
this.$emit('setProductName', item)
},
},
created() {
//
},
mounted() {
//
},
}
</script>
<style lang="scss" scoped>
.title {
//width: 80%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.sticky {
display: flex;
align-items: center;
position: sticky;
justify-content: space-between;
top: 0;
left: 0;
right: 0;
z-index: 10;
//height: 10px;
padding: 5px 10px;
background: #fff;
//gap: 10px;
}
::v-deep .more-treasure {
//flex:1;
//width: 20px;
font-size: 16px;
margin-left: 10px;
}
::v-deep .van-dropdown-menu__bar {
box-shadow: unset;
}
::v-deep .van-dropdown-menu {
//flex: 1;
width: 85%;
}
::v-deep .van-dropdown-menu__bar {
height: auto;
}
::v-deep .van-dropdown-menu__item {
justify-content: start;
}
::v-deep .van-dropdown-menu__title {
padding: 8px 12px 8px 8px;
width: 80%;
}
::v-deep .van-cell {
font-size: 14px;
}
::v-deep .more-drown {
width: 100%;
&::after {
display: none;
}
}
</style>

View File

@@ -2,57 +2,22 @@
<div class="treasure-box"> <div class="treasure-box">
<h3>{{ item.text.indexOf('工具箱') !== -1 ? item.text : item.text + '工具箱' }}</h3> <h3>{{ item.text.indexOf('工具箱') !== -1 ? item.text : item.text + '工具箱' }}</h3>
<van-tabs v-model="active" color="#2E5CA9" title-active-color="#2E5CA9" line-width="20"> <van-tabs v-model="active" color="#2E5CA9" title-active-color="#2E5CA9" line-width="20">
<!-- <van-tab title="爆款图文">-->
<!-- <TabBox></TabBox>-->
<!-- </van-tab>-->
<van-tab title="产品知识"> <van-tab title="产品知识">
<table style="border: 1px solid #b6ccd9; text-align: left; border-radius: 8px; overflow: hidden" cellspacing="0" cellpadding="0" class="my-table">
<!-- <van-collapse v-model="activeNames">--> <tr v-for="item in knowledge">
<!-- <van-collapse-item :title="item.title" :name="index" v-for="(item, index) in knowledge" >--> <th class="fs12 fw600">{{ item.title }}</th>
<!-- <span v-for="(it, index) in item.list" :key="index" :title="item.title" class='cells'>--> <th class="fs12 flex">
<!-- {{ it.title}}--> <span v-for="(it, index) in item.list" :key="index" :title="item.title" class="cells">
<!-- </span>-->
<!-- </van-collapse-item>-->
<!-- </van-collapse>-->
<!-- -->
<table style='border :1px solid #b6ccd9;text-align: left;border-radius:8px;overflow: hidden ' cellspacing='0' cellpadding='0' class='my-table' >
<tr v-for='item in knowledge'>
<th class='fs12 fw600'>{{item.title}}</th>
<th class='fs12 flex '>
<span v-for="(it, index) in item.list" :key="index" :title="item.title" class='cells'>
{{ it.title }} {{ it.title }}
</span> </span>
</th> </th>
</tr> </tr>
</table> </table>
<!-- <van-cell-group>-->
<!-- <van-cell v-for="(item, index) in knowledge" :key="index" :title="item.title" class='cells'>-->
<!-- <template #default>-->
<!--&lt;!&ndash; 截取钱10个&ndash;&gt;-->
<!-- <span v-for='it in item.list'>{{-->
<!-- it.title.substring(0,8)}}...</span>-->
<!-- </template>-->
<!-- <template #right-icon>-->
<!-- <van-icon name="arrow" ></van-icon>-->
<!-- </template>-->
<!-- </van-cell>-->
<!-- </van-cell-group>-->
<!-- <TabBox :list='knowledge'></TabBox>-->
</van-tab> </van-tab>
<van-tab title="常用工具"> <van-tab title="常用工具">
<TabBox :list='tools'></TabBox> <TabBox :list="tools"></TabBox>
</van-tab> </van-tab>
</van-tabs> </van-tabs>
<!-- 在这里添加百宝箱的具体内容 -->
</div> </div>
</template> </template>
@@ -86,7 +51,6 @@ export default {
tools: [], tools: [],
knowledge: [], knowledge: [],
activeNames: [1], activeNames: [1],
} }
}, },
watch: { watch: {
@@ -97,10 +61,7 @@ export default {
} else { } else {
this.setList(this.item.detail) this.setList(this.item.detail)
this.$emit('setProductName', this.item.detail.productName) this.$emit('setProductName', this.item.detail.productName)
} }
}, },
immediate: true, immediate: true,
}, },
@@ -108,25 +69,26 @@ export default {
methods: { methods: {
setList() { setList() {
this.tools = [ this.tools = [
{title: '工具', {
title: '工具',
list: [ list: [
{ value: this.item.detail.instructionUrl, title: '产品说明书' }, { value: this.item.detail.instructionUrl, title: '产品说明书' },
{ value: this.item.detail.clauseUrl, title: '条款' }, { value: this.item.detail.clauseUrl, title: '条款' },
]}, ],
},
] ]
this.knowledge = [] this.knowledge = []
for (let i in this.item.detail.knowledge) { for (let i in this.item.detail.knowledge) {
this.knowledge.push({ this.knowledge.push({
title: i, title: i,
list:this.item.detail.knowledge[i].split(',').map(item=>{ list: this.item.detail.knowledge[i].split(',').map((item) => {
return { return {
title: item, title: item,
// value:item // value:item
} }
}) }),
}) })
} }
}, },
@@ -174,15 +136,12 @@ $primary-trans-color: #87a2d0;
align-items: center; align-items: center;
} }
.my-table { .my-table {
tr { tr {
padding: 6px 8px; padding: 6px 8px;
} }
tr:nth-child(2n) { tr:nth-child(2n) {
background: #e9f3ff; background: #e9f3ff;
} }
tr:last-child { tr:last-child {
th { th {
@@ -203,9 +162,7 @@ $primary-trans-color: #87a2d0;
color: #253243; color: #253243;
font-weight: unset; font-weight: unset;
gap: 5px; gap: 5px;
border-bottom:1px solid #b6ccd9 border-bottom: 1px solid #b6ccd9;
} }
} }
</style> </style>

View File

@@ -1,10 +1,25 @@
<template> <template>
<div class="chat-page"> <div class="chat-page">
<sticky
:hotList="hotList"
:productName="productName"
:messagesList.sync="messages"
:autoScrollEnabled.sync="autoScrollEnabled"
@setProductName="setProductName"
:isDisabled="messageStatus === 'send'"
></sticky>
<main class="chat-main"> <main class="chat-main">
<div class="chat-content"> <div class="chat-content">
<div class="message-area" ref="messageArea" @scroll="handleScroll"> <div class="message-area" ref="messageArea" @scroll="handleScroll">
<HotProducts class="mb10" :messagesList.sync="messages"></HotProducts> <HotProducts class="mb10" :messagesList.sync="messages" @getHotList="getHotProducts"></HotProducts>
<messageComponent :messagesList="messages" :is-deep="isDeep" :is-search="isSearching" :think-ok='isThink' @setProductName='setProductName'></messageComponent> <messageComponent
:messagesList="messages"
:is-deep="isDeep"
:is-search="isSearching"
:think-ok="isThink"
@setProductName="setProductName"
></messageComponent>
</div> </div>
</div> </div>
<!-- 滚动到顶部按钮 --> <!-- 滚动到顶部按钮 -->
@@ -13,12 +28,12 @@
</div> </div>
</main> </main>
<div v-if='isVoiceMode && newMessage' class='isVoiceModeText'> <div v-if="isVoiceMode && newMessage" class="isVoiceModeText">
<textarea class="textarea" placeholder="请输入内容" v-model='newMessage' ></textarea> <textarea class="textarea" placeholder="请输入内容" v-model="newMessage"></textarea>
</div> </div>
<section class="section"> <section class="section">
<button @click="searchInternet" :class="{ active: isSearching }"> <button @click="searchInternet" :class="{ active: isSearching }" class="ml10">
<svg-icon icon-class="earth" class-name="chat-icon"></svg-icon> <svg-icon icon-class="earth" class-name="chat-icon"></svg-icon>
联网搜索 联网搜索
</button> </button>
@@ -33,22 +48,19 @@
</section> </section>
<footer class="chat-footer"> <footer class="chat-footer">
<!-- 输入框 or 按住说话提示 --> <!-- 输入框 or 按住说话提示 -->
<div class="input-wrapper"> <div class="input-wrapper ml10">
<input <input v-if="!isVoiceMode" type="text" v-model="newMessage" placeholder="请简短描述您的问题" @keyup.enter="sendMessage" />
v-if="!isVoiceMode" <div
type="text" v-else
v-model="newMessage" class="voice-hint-container"
placeholder="请简短描述您的问题"
@keyup.enter="sendMessage"
/>
<div v-else class="voice-hint-container"
:class="{ disabled: messageStatus === 'send' }" :class="{ disabled: messageStatus === 'send' }"
@mousedown="startRecording" @mousedown="startRecording"
@selectstart="() => false" @selectstart="() => false"
@mouseup="stopRecording" @mouseup="stopRecording"
@mouseleave="stopRecording" @mouseleave="stopRecording"
@touchend="stopRecording" @touchend="stopRecording"
@touchstart="startRecording"> @touchstart="startRecording"
>
<div class="waveform" :class="{ active: isRecording }"> <div class="waveform" :class="{ active: isRecording }">
<span class="bar"></span> <span class="bar"></span>
<span class="bar"></span> <span class="bar"></span>
@@ -60,21 +72,12 @@
</div> </div>
</div> </div>
<!-- 语音按钮按住说话 --> <!-- 语音按钮按住说话 -->
<button <button @click="isVoiceMode = !isVoiceMode" class="mic-button ml10 mr10">
@click='isVoiceMode = !isVoiceMode' <svg-icon v-if="!isVoiceMode" icon-class="voice" class-name="chat-icon ml10" style="width: 25px; height: 25px"></svg-icon>
class="mic-button ml10 mr10" <span v-else style="font-size: 20px; color: #707070" class="ml15 mr5"></span>
>
<svg-icon v-if='!isVoiceMode' icon-class="voice" class-name="chat-icon" style='width: 20px;height: 20px'></svg-icon>
<span v-else style='font-size: 18px;color:#707070' class='ml5 mr5'></span>
</button> </button>
<!-- 发送按钮 --> <!-- 发送按钮 -->
<button <button @click="sendMessage" :disabled="messageStatus === 'send'" :class="{ disabled: messageStatus === 'send' }" class="mr10 fs16">发送</button>
@click="sendMessage"
:disabled="messageStatus === 'send'"
:class="{ disabled: messageStatus === 'send' }"
>
发送
</button>
</footer> </footer>
</div> </div>
</template> </template>
@@ -84,6 +87,7 @@ import { Icon } from 'vant'
import messageComponent from './components/message.vue' import messageComponent from './components/message.vue'
import SvgIcon from '@/components/svg-icon/index.vue' import SvgIcon from '@/components/svg-icon/index.vue'
import HotProducts from '@/views/AI/components/HotProducts.vue' import HotProducts from '@/views/AI/components/HotProducts.vue'
import sticky from '@/views/AI/components/sticky.vue'
import { chat, chatProduct, audioToText } from '@/api/generatedApi' import { chat, chatProduct, audioToText } from '@/api/generatedApi'
export default { export default {
@@ -92,9 +96,11 @@ export default {
[Icon.name]: Icon, [Icon.name]: Icon,
messageComponent, messageComponent,
HotProducts, HotProducts,
sticky,
}, },
data() { data() {
return { return {
hotList: [],
productName: '', productName: '',
answerMap: '', answerMap: '',
timer: null, timer: null,
@@ -119,6 +125,10 @@ export default {
} }
}, },
methods: { methods: {
getHotProducts(e) {
console.log(e)
this.hotList = e
},
deepInternet() { deepInternet() {
this.isDeep = !this.isDeep this.isDeep = !this.isDeep
}, },
@@ -131,13 +141,15 @@ export default {
this.productName = '' this.productName = ''
}, },
hasTreasureBox() { hasTreasureBox() {
chatProduct({ query: this.newMessage }).then((res) => { chatProduct({ query: this.newMessage })
.then((res) => {
if (res) { if (res) {
this.messageStatus = 'stop' this.messageStatus = 'stop'
this.messages.push({ type: 'box', text: this.newMessage, detail: res.content }) this.messages.push({ type: 'box', text: this.newMessage, detail: res.content })
this.newMessage = '' this.newMessage = ''
} }
}).catch(() => { })
.catch(() => {
this.messageStatus = 'stop' this.messageStatus = 'stop'
}) })
}, },
@@ -185,9 +197,7 @@ export default {
const messageArea = this.$refs.messageArea const messageArea = this.$refs.messageArea
if (!messageArea) return if (!messageArea) return
const threshold = 10 const threshold = 10
const isAtBottom = const isAtBottom = messageArea.scrollHeight - messageArea.clientHeight <= messageArea.scrollTop + threshold
messageArea.scrollHeight - messageArea.clientHeight <=
messageArea.scrollTop + threshold
this.autoScrollEnabled = isAtBottom this.autoScrollEnabled = isAtBottom
this.scrollPosition = messageArea.scrollTop this.scrollPosition = messageArea.scrollTop
}, },
@@ -198,7 +208,7 @@ export default {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true }) const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
this.mediaRecorder = new MediaRecorder(stream) this.mediaRecorder = new MediaRecorder(stream)
this.audioChunks = [] this.audioChunks = []
this.mediaRecorder.ondataavailable = event => { this.mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) { if (event.data.size > 0) {
this.audioChunks.push(event.data) this.audioChunks.push(event.data)
} }
@@ -238,15 +248,17 @@ export default {
}, },
callVoiceRecognitionAPI(blob) { callVoiceRecognitionAPI(blob) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const formData = new FormData(); const formData = new FormData()
formData.append('file', blob); formData.append('file', blob)
formData.append('appType', 'haslBigHelper'); formData.append('appType', 'haslBigHelper')
formData.append('user', 'chenyuda'); formData.append('user', 'chenyuda')
audioToText(formData).then(res => { audioToText(formData)
.then((res) => {
if (res) { if (res) {
resolve(res.content) resolve(res.content)
} }
}).catch(err => { })
.catch((err) => {
reject(err) reject(err)
}) })
}) })
@@ -262,7 +274,7 @@ export default {
think: '', think: '',
isLike: false, isLike: false,
isDisLike: false, isDisLike: false,
}) }),
) )
this.messages.push(this.currentMessage) this.messages.push(this.currentMessage)
const params = { const params = {
@@ -317,7 +329,7 @@ export default {
const cleanLine = line.replace(/^data:\s*/, '') const cleanLine = line.replace(/^data:\s*/, '')
if (!cleanLine) return null if (!cleanLine) return null
const data = JSON.parse(cleanLine) const data = JSON.parse(cleanLine)
console.log(data) // console.log(data)
if (data.answer) { if (data.answer) {
this.answerMap += data.answer this.answerMap += data.answer
} }
@@ -343,8 +355,8 @@ export default {
this.messageStatus = 'stop' this.messageStatus = 'stop'
} }
console.log(answer) // console.log(answer)
console.log(this.currentMessage) // console.log(this.currentMessage)
if (!this.currentMessage || !answer) return if (!this.currentMessage || !answer) return
const mode = this.isThink ? 'think' : 'text' const mode = this.isThink ? 'think' : 'text'
this.currentMessage[mode] += answer this.currentMessage[mode] += answer
@@ -382,7 +394,6 @@ $primary-trans-color: rgba(135, 162, 208, 0.5);
background: #f7f8fa; background: #f7f8fa;
position: relative; position: relative;
.button-container { .button-container {
position: fixed; position: fixed;
bottom: 150px; bottom: 150px;
@@ -421,7 +432,7 @@ $primary-trans-color: rgba(135, 162, 208, 0.5);
gap: 10px; gap: 10px;
button { button {
padding: 5px 12px; padding: 4px 8px;
border: none; border: none;
background-color: $primary-trans-color; background-color: $primary-trans-color;
color: #fff; color: #fff;
@@ -439,7 +450,9 @@ $primary-trans-color: rgba(135, 162, 208, 0.5);
.chat-footer { .chat-footer {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 10px; padding: 10px 10px 20px 10px;
//padding-bottom: constant(safe-area-inset-bottom);
//padding-bottom: env(safe-area-inset-bottom);
background-color: #fff; background-color: #fff;
.input-wrapper { .input-wrapper {
@@ -474,7 +487,6 @@ $primary-trans-color: rgba(135, 162, 208, 0.5);
background-color: #eaeaea; background-color: #eaeaea;
} }
.waveform { .waveform {
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
@@ -495,11 +507,26 @@ $primary-trans-color: rgba(135, 162, 208, 0.5);
background-color: $primary-color; background-color: $primary-color;
margin: 0 1px; margin: 0 1px;
&:nth-child(1) { height: 10px; animation-delay: 0s; } &:nth-child(1) {
&:nth-child(2) { height: 16px; animation-delay: 0.1s; } height: 10px;
&:nth-child(3) { height: 12px; animation-delay: 0.2s; } animation-delay: 0s;
&:nth-child(4) { height: 18px; animation-delay: 0.3s; } }
&:nth-child(5) { height: 14px; animation-delay: 0.4s; } &:nth-child(2) {
height: 16px;
animation-delay: 0.1s;
}
&:nth-child(3) {
height: 12px;
animation-delay: 0.2s;
}
&:nth-child(4) {
height: 18px;
animation-delay: 0.3s;
}
&:nth-child(5) {
height: 14px;
animation-delay: 0.4s;
}
} }
} }
@@ -548,7 +575,8 @@ $primary-trans-color: rgba(135, 162, 208, 0.5);
} }
@keyframes wave-animation { @keyframes wave-animation {
0%, 100% { 0%,
100% {
transform: scaleY(1); transform: scaleY(1);
} }
50% { 50% {