1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072 |
- <template>
- <view class="chat-container">
- <scroll-view class="messages" scroll-y :scroll-top="scrollTop" scroll-with-animation>
- <block v-for="message in messages" :key="message.id">
- <view :class="['message', message.user]">
- <view class="avatar-container">
- <image :src="message.user === 'user' ? userAvatar : '/static/机器人.png'" class="avatar"
- mode="aspectFill" />
- <text class="sender-name">{{ message.user === 'user' ? userInfo.nickName : 'AI助理' }}</text>
- </view>
- <view class="message-content"> <!-- 改为 message-content 类 -->
- <template v-if="message.message.includes('相关资料来源')">
- <view>{{ getMainContent(message.message) }}</view>
- <view class="source-section" v-if="minioUrls && minioUrls.length > 0">
- <view class="source-title">相关资料来源:</view>
- <view class="source-links">
- <view v-for="(item, index) in minioUrls" :key="index" class="source-link"
- @tap="copyLink(item.url)">
- {{ item.object_name }}
- </view>
- </view>
- </view>
- </template>
- <template v-else>
- {{ message.message }}
- </template>
- </view>
- </view>
- </block>
- </scroll-view>
- <!-- 添加新建对话按钮 -->
- <view class="new-chat-btn">
- <button @tap="startNewChat" class="btn-new">新建对话</button>
- </view>
- <view class="input-container">
- <textarea v-model="userInput" @keydown="handleKeydown" placeholder="有问题可以咨询我..." maxlength="100"></textarea>
- <button @tap="sendMessage" class="btn">发送</button>
- </view>
- </view>
- </template>
- <script>
- import MarkdownIt from "markdown-it";
- const md = new MarkdownIt();
- export default {
- data() {
- return {
- messages: [{
- user: "bot",
- messageType: "TEXT",
- message: "欢迎使用智能AI助理",
- html: "",
- time: "",
- done: true,
- }, ],
- userInput: "",
- websocket: null,
- AiUrl: '',
- apiUrl: 'https://ylaiapi.raycos.com.cn',
- scrollTop: 0,
- tweaks: {},
- userAvatar: '/static/用户.png',
- userInfo: {},
- level: 1,
- idArray: [],
- minioUrls: [], // 新增:用于存储 Minio URL
- generating: false, // 是否正在生成回复
- typewriterTimer: null,
- currentIndex: 0,
- fullMessage: '',
- session_id: "",
- platform: {
- type: '', // 平台类型
- name: '', // 平台名称
- isWeixin: false
- }
- };
- },
- created() {
- },
- onLoad() {
- // this.getKbmUrl()
- this.loadUserInfo()
- this.getPlatformInfo();
- },
- onShow() {
- this.getKbmUrl()
- },
- watch: {
- messages: {
- handler() {
- this.$nextTick(() => {
- this.scrollToBottom();
- });
- },
- deep: true
- }
- },
- methods: {
- // 添加新建对话方法
- startNewChat() {
- uni.showModal({
- title: '确认新建对话',
- content: '是否清空当前对话内容开始新的对话?',
- success: (res) => {
- if (res.confirm) {
- // 重置消息列表,只保留欢迎消息
- this.messages = [{
- user: "bot",
- messageType: "TEXT",
- message: "欢迎使用智能AI助理",
- html: "",
- time: "",
- done: true,
- }];
- // 清空session_id
- this.session_id = "";
- // 清空MinioUrls
- this.minioUrls = [];
- // 重置其他相关状态
- this.generating = false;
- this.currentIndex = 0;
- this.fullMessage = '';
- // 显示提示
- uni.showToast({
- title: '已开始新对话',
- icon: 'success',
- duration: 1500
- });
- }
- }
- });
- },
- // 获取平台信息
- getPlatformInfo() {
- try {
- const systemInfo = uni.getSystemInfoSync();
- // console.log('系统信息:', systemInfo);
- // 判断平台类型
- // #ifdef MP-WEIXIN
- this.platform.type = 'mp-weixin';
- this.platform.name = '微信小程序';
- this.platform.isWeixin = true;
- // #endif
- // #ifdef MP-ALIPAY
- this.platform.type = 'mp-alipay';
- this.platform.name = '支付宝小程序';
- // #endif
- // #ifdef MP-BAIDU
- this.platform.type = 'mp-baidu';
- this.platform.name = '百度小程序';
- // #endif
- // #ifdef MP-TOUTIAO
- this.platform.type = 'mp-toutiao';
- this.platform.name = '头条小程序';
- // #endif
- // #ifdef H5
- this.platform.type = 'h5';
- this.platform.name = 'H5';
- // 在H5中判断是否在微信浏览器中
- if (navigator.userAgent.toLowerCase().indexOf('micromessenger') !== -1) {
- this.platform.isWeixin = true;
- this.platform.name = 'H5微信浏览器';
- }
- // #endif
- // #ifdef APP-PLUS
- this.platform.type = 'app';
- this.platform.name = 'App';
- // #endif
- // console.log('当前平台:', this.platform);
- // 获取更详细的平台信息
- const platformInfo = {
- brand: systemInfo.brand, // 手机品牌
- model: systemInfo.model, // 手机型号
- system: systemInfo.system, // 操作系统版本
- platform: systemInfo.platform, // 客户端平台
- SDKVersion: systemInfo.SDKVersion, // 小程序基础库版本
- version: systemInfo.version, // 微信版本号
- };
- // console.log('平台详细信息:', platformInfo);
- return this.platform;
- } catch (error) {
- console.error('获取平台信息失败:', error);
- return {
- type: 'unknown',
- name: '未知平台',
- isWeixin: false
- };
- }
- },
- getMainContent(message) {
- const parts = message.split('相关资料来源');
- return parts[0].trim();
- },
- extractUrls(message) {
- const urlRegex = /href="([^"]+)"/g;
- const urls = [];
- let match;
- while ((match = urlRegex.exec(message)) !== null) {
- urls.push(match[1]);
- }
- return urls;
- },
- getFileName(url) {
- try {
- const decodedUrl = decodeURIComponent(url);
- const parts = decodedUrl.split('/');
- let fileName = parts[parts.length - 1];
- // 如果文件名过长,截断它
- if (fileName.length > 30) {
- fileName = fileName.substring(0, 27) + '...';
- }
- return fileName;
- } catch (e) {
- return '文件';
- }
- },
- // 修改 copyLink 方法
- copyLink(url) {
- if (!url) return;
- uni.setClipboardData({
- data: url,
- success: () => {
- uni.showToast({
- title: '链接已复制',
- icon: 'none',
- duration: 1500
- });
- }
- });
- },
- loadUserInfo() {
- const userDataString = uni.getStorageSync('userInfo');
- if (userDataString) {
- this.userInfo = JSON.parse(userDataString);
- this.userAvatar = this.userInfo.avatarUrl || '/static/用户.png'; // Fallback to default if no avatar
- }
- },
- scrollToBottom() {
- // 使用一个很大的值来确保滚动到底部
- this.scrollTop += 9999;
- // 重置 scrollTop 以便下次滚动生效
- // this.$nextTick(() => {
- // this.scrollTop = 0;
- // });
- },
- /* 请求链接 */
- getKbmUrl() {
- let userDataString;
- if (uni.getStorageSync('userData') == '') {
- console.log('userData is empty');
- // 跳转到个人页面
- uni.switchTab({
- url: '/pages/my/index',
- success: function() {
- console.log('Successfully switched to personal page');
- },
- fail: function(err) {
- console.error('Failed to switch to personal page:', err);
- }
- });
- return;
- } else {
- userDataString = JSON.parse(uni.getStorageSync('userData'));
- }
- uni.request({
- url: this.apiUrl + '/wechat/getUserInfo',
- method: 'POST',
- data: {
- openid: userDataString.openid
- },
- header: {
- 'content-type': 'application/x-www-form-urlencoded'
- },
- success: (res) => {
- console.log('获取用户信息成功', res.data);
- this.AiUrl = this.apiUrl + '/chatbot/chat'
- this.level = res.data.data.level
- },
- fail: (err) => {
- console.error('获取用户信息失败', err);
- uni.showToast({
- title: '获取用户信息失败,请重试',
- icon: 'none'
- });
- }
- });
- /* uni.request({
- url: this.apiUrl + '/wechat/getKbmUrl', // 确保这是正确的URL
- method: 'POST',
- data: {
- openid: userDataString.openid
- }, //JSON.stringify()
- header: {
- 'content-type': 'application/x-www-form-urlencoded' // 设置正确的内容类型
- },
- success: (res) => {
- console.log('Login response:', res.data);
- if (res.data && res.data.status == 200) {
- this.tweaks = res.data.data.tweaks
- this.connectWebSocket();
- }
- },
- fail: (err) => {
- console.error('请求失败', err);
- uni.showToast({
- title: '请求失败,请重试',
- icon: 'none'
- });
- }
- }) */
- },
- getUuid() {
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
- /[xy]/g,
- function(c) {
- var r = (Math.random() * 16) | 0,
- v = c == "x" ? r : (r & 0x3) | 0x8;
- return v.toString(16);
- }
- );
- },
- connectWebSocket() {
- const wsUrl = this.AiUrl
- //'http://ai.raycos.net/api/v1/run/c7ea3a2d-f50c-4936-b96c-3fe7c34fa062?stream=false';
- // 使用 XMLHttpRequest 替代 fetch
- uni.request({
- url: wsUrl,
- method: 'POST',
- data: JSON.stringify({}), // 如果需要发送数据,可以在这里添加
- header: {
- 'content-type': 'application/json'
- },
- success: (res) => {
- if (res.statusCode === 200) {
- const result = res.data;
- this.handleWebSocketMessage(result);
- } else {
- console.error("请求失败:", res.statusCode);
- }
- },
- fail: (err) => {
- console.error("网络连接错误:", err);
- }
- });
- // 如果需要发送数据,可以在这里添加
- },
- // 新增方法来处理消息
- handleWebSocketMessage(result) {
- console.log(result.content);
- // 检查消息是否完结
- if (result.done) {
- this.messages[this.messages.length - 1].done = true;
- return;
- }
- if (this.messages[this.messages.length - 1].done) {
- // 添加新的消息
- this.messages.push({
- time: Date.now(),
- message: result.content,
- messageType: "TEXT",
- user: "bot",
- done: false,
- });
- } else {
- // 更新最后一条消息
- let lastMessage = this.messages[this.messages.length - 1];
- lastMessage.message += result.content;
- this.messages[this.messages.length - 1] = lastMessage;
- }
- },
- // 修改 sendMessage 方法
- async sendMessage() {
- const wsUrl = this.AiUrl;
- let message = this.userInput.trim();
- if (!message || this.typewriterTimer) return; // 防止重复发送
- try {
- // 添加用户消息
- const userMessage = {
- id: this.getUuid(),
- user: "user",
- messageType: "TEXT",
- message: message,
- time: Date.now(),
- done: true,
- };
- this.messages.push(userMessage);
- // 清空输入框
- this.userInput = "";
- this.scrollToBottom();
- // 添加AI助理的消息
- const botMessage = {
- id: this.getUuid(),
- user: "bot",
- messageType: "TEXT",
- message: "正在思考中...",
- time: Date.now(),
- done: false,
- };
- this.messages.push(botMessage);
- const response = await uni.request({
- url: wsUrl,
- method: 'POST',
- header: {
- 'Content-Type': 'application/json'
- },
- data: {
- message: message,
- chat_config_id: '17',
- user_id: this.userInfo.phoneNumber,
- session_id: this.session_id || "",
- source: this.platform.type //uni.getAccountInfoSync().miniProgram.appName,
- }
- });
- // 检查响应状态和数据
- if (response.statusCode !== 200 || !response.data || !response.data.data?.answer) {
- throw new Error('Invalid response from server');
- }
- if (this.level === '0' && response.data.data?.milvus_ids) {
- await this.getMinioUrls({
- ids: response.data.data.milvus_ids
- });
- }
- this.session_id = response.data.data.session_id;
- await this.handleResponse(response.data, botMessage);
- } catch (error) {
- console.error("Error sending message:", error);
- // 找到最后一条消息(应该是机器人的消息)并更新它
- const lastMessage = this.messages[this.messages.length - 1];
- if (lastMessage && lastMessage.user === 'bot') {
- lastMessage.message = "抱歉,处理消息时出现错误,请重试。";
- lastMessage.done = true;
- }
- uni.showToast({
- title: '发送消息失败,请重试',
- icon: 'none'
- });
- }
- },
- // 修改 getMinioUrls 方法
- async getMinioUrls(idObject) {
- try {
- const response = await uni.request({
- url: `${this.apiUrl}/milvus/getMinioURl`,
- method: 'POST',
- data: idObject,
- header: {
- 'Content-Type': 'application/json'
- }
- });
- if (response.statusCode === 200 && response.data?.data) {
- this.minioUrls = response.data.data;
- console.log("Received Minio URLs:", this.minioUrls);
- } else {
- this.minioUrls = [];
- console.error("Invalid response format:", response);
- }
- } catch (error) {
- console.error("Error fetching Minio URLs:", error);
- this.minioUrls = [];
- uni.showToast({
- title: '获取资源链接失败',
- icon: 'none'
- });
- }
- },
- // 修改 handleResponse 方法
- async handleResponse(value, existingMessage) {
- try {
- if (!value?.data?.answer) {
- throw new Error('Invalid response format');
- }
- const data = value.data.answer;
- this.fullMessage = data;
- this.currentIndex = 0;
- existingMessage.message = '';
- // 开始打字效果
- this.startTypewriter(existingMessage);
- } catch (error) {
- console.error("Error handling response:", error);
- existingMessage.message = "抱歉,处理响应时出现错误。";
- existingMessage.done = true;
- }
- },
- // 新的打字效果实现
- startTypewriter(existingMessage) {
- // 清除可能存在的旧计时器
- if (this.typewriterTimer) {
- clearInterval(this.typewriterTimer);
- this.typewriterTimer = null;
- }
- const doTypewriter = () => {
- if (this.currentIndex < this.fullMessage.length) {
- existingMessage.message = this.fullMessage.slice(0, this.currentIndex + 1);
- this.currentIndex++;
- // 使用 nextTick 确保视图更新
- this.$nextTick(() => {
- this.scrollToBottom();
- });
- // 使用 setTimeout 代替 setInterval
- this.typewriterTimer = setTimeout(() => {
- doTypewriter();
- }, 30);
- } else {
- // 打字效果完成
- if (this.minioUrls && this.minioUrls.length > 0) {
- existingMessage.message += '\n\n相关资料来源';
- }
- existingMessage.done = true;
- this.typewriterTimer = null;
- this.scrollToBottom();
- }
- };
- // 立即开始打字效果
- doTypewriter();
- },
- // 修改 renderMarkdown 方法以保留 HTML 标签
- /* renderMarkdown(rawMarkdown) {
- if (typeof rawMarkdown !== 'string') {
- console.warn('Invalid input for renderMarkdown:', rawMarkdown);
- return '';
- }
- try {
- const md = new MarkdownIt({
- html: true, // 允许 HTML 标签
- breaks: true,
- linkify: true
- });
- // 自定义链接渲染
- md.renderer.rules.link_open = (tokens, idx) => {
- const token = tokens[idx];
- const hrefIndex = token.attrIndex('href');
- const href = token.attrs[hrefIndex][1];
- return `<text class="link" style="color: #1296db; text-decoration: underline;" data-href="${href}">`;
- };
- md.renderer.rules.link_close = () => '</text>';
- // 自定义段落渲染
- md.renderer.rules.paragraph_open = () =>
- '<p style="font-size: 12px; margin: 5px 0 10px; padding: 0; line-height: 22px;">';
- return md.render(rawMarkdown);
- } catch (error) {
- console.error('Markdown rendering error:', error);
- return rawMarkdown;
- }
- }, */
- // 修改 sendMessage 方法
- /* async sendMessage() {
- const wsUrl = this.AiUrl;
- let message = this.userInput.trim();
- if (message) {
- // 添加用户消息
- const userMessage = {
- id: this.getUuid(),
- user: "user",
- messageType: "TEXT",
- message: message,
- time: Date.now(),
- done: true,
- };
- this.messages.push(userMessage);
- // 清空输入框并滚动
- this.userInput = "";
- this.scrollToBottom();
- // 添加AI助理的消息
- const botMessage = {
- id: this.getUuid(),
- user: "bot",
- messageType: "TEXT",
- message: "正在思考中...", // 添加加载提示
- time: Date.now(),
- done: false,
- };
- this.messages.push(botMessage);
- try {
- const response = await uni.request({
- url: wsUrl,
- method: 'POST',
- header: {
- 'Content-Type': 'application/json'
- },
- data: {
- message: message,
- chat_config_id: '17',
- user_id: this.userInfo.phoneNumber,
- session_id: this.session_id || "",
- }
- });
- if (response.statusCode === 200 && response.data) {
- // 如果需要获取 MinioUrls,先获取
- if (this.level === '0' && response.data.data?.milvus_ids) {
- await this.getMinioUrls({
- ids: response.data.data.milvus_ids
- });
- }
- // 然后处理完整响应
- await this.handleResponse(response.data, botMessage);
- }
- } catch (error) {
- console.error("Error sending message:", error);
- botMessage.message = "抱歉,发送消息失败,请重试。";
- botMessage.done = true;
- uni.showToast({
- title: '发送消息失败,请重试',
- icon: 'none'
- });
- }
- }
- }, */
- // 修改 renderMarkdown 方法
- /* renderMarkdown(rawMarkdown) {
- if (typeof rawMarkdown !== 'string') {
- console.warn('Invalid input for renderMarkdown:', rawMarkdown);
- return '';
- }
-
- try {
- const md = new MarkdownIt({
- html: true,
- breaks: true,
- linkify: true
- });
-
- // 自定义链接渲染
- md.renderer.rules.link_open = (tokens, idx) => {
- const token = tokens[idx];
- const hrefIndex = token.attrIndex('href');
- const href = token.attrs[hrefIndex][1];
- return `<text class="link" data-href="${href}">`;
- };
- md.renderer.rules.link_close = () => '</text>';
-
- return md.render(rawMarkdown);
- } catch (error) {
- console.error('Markdown rendering error:', error);
- return rawMarkdown;
- }
- }, */
- // 添加链接点击处理方法
- // 修改 handleLinkClick 方法
- // 修改 renderMarkdown 方法
- renderMarkdown(rawMarkdown) {
- if (typeof rawMarkdown !== 'string') {
- console.warn('Invalid input for renderMarkdown:', rawMarkdown);
- return '';
- }
- try {
- const md = new MarkdownIt({
- html: true,
- breaks: true,
- linkify: true
- });
- // 自定义链接渲染
- md.renderer.rules.link_open = (tokens, idx) => {
- const token = tokens[idx];
- const hrefIndex = token.attrIndex('href');
- const href = token.attrs[hrefIndex][1];
- // 使用自定义的类名和数据属性
- return `<view class="custom-link" data-url="${href}">`;
- };
- md.renderer.rules.link_close = () => '</view>';
- const html = md.render(rawMarkdown);
- return html;
- } catch (error) {
- console.error('Markdown rendering error:', error);
- return rawMarkdown;
- }
- },
- // 修改 handleLinkClick 方法
- handleLinkClick(e) {
- // 查找最近的带有 custom-link 类的父元素
- let target = e.target;
- while (target && !target.classList?.contains('custom-link')) {
- target = target.parentElement;
- }
- if (target && target.dataset.url) {
- const url = target.dataset.url;
- console.log('Clicked URL:', url);
- // 复制链接到剪贴板
- uni.setClipboardData({
- data: url,
- success: () => {
- uni.showToast({
- title: '链接已复制',
- icon: 'none'
- });
- }
- });
- }
- },
- /* renderMarkdown(rawMarkdown) {
- if (typeof rawMarkdown !== 'string') {
- console.warn('Invalid input for renderMarkdown:', rawMarkdown);
- return '';
- }
- return md.render(rawMarkdown);
- }, */
- handleKeydown(event) {
- // Check if 'Enter' is pressed without the 'Alt' key
- if (event.key === "Enter" && !(event.shiftKey || event.altKey)) {
- event.preventDefault(); // Prevent the default action to avoid line break in textarea
- this.sendMessage();
- } else if (event.key === "Enter" && event.altKey) {
- // Allow 'Alt + Enter' to insert a newline
- const cursorPosition = event.target.selectionStart;
- const textBeforeCursor = this.userInput.slice(0, cursorPosition);
- const textAfterCursor = this.userInput.slice(cursorPosition);
- // Insert the newline character at the cursor position
- this.userInput = textBeforeCursor + "\n" + textAfterCursor;
- // Move the cursor to the right after the inserted newline
- this.$nextTick(() => {
- event.target.selectionStart = cursorPosition + 1;
- event.target.selectionEnd = cursorPosition + 1;
- });
- }
- },
- beforeDestroy() {
- if (this.websocket) {
- this.websocket.close();
- }
- },
- },
- // updated() {
- // const messagesContainer = this.$el.querySelector(".messages");
- // messagesContainer.scrollTop = messagesContainer.scrollHeight;
- // },
- };
- </script>
- <style scoped>
- /* 这里引入 ChatBox.css 的内容 */
- /* @import '/static/css/ChatBox.css'; */
- .chat-container {
- display: flex;
- flex-direction: column;
- height: 100vh;
- /* 或者按照实际需要调整高度 */
- }
- .messages {
- width: 90%;
- /* 设置消息列表宽度为屏幕宽度的 80% */
- max-width: 90%;
- /* 限制最大宽度,避免超出视口 */
- margin: 0 auto;
- /* 上下保持原有的 margin,左右自动调整以居中 */
- overflow-y: auto;
- /* 如果消息太多,允许滚动 */
- height: calc(100vh - 190px);
- /* 高度需要根据输入框和其他元素的高度调整 */
- padding: 10px 10px 0;
- /* 或你希望的内边距大小 */
- }
- .message {
- display: flex;
- padding: 10px;
- align-items: flex-start;
- }
- .avatar-container {
- display: flex;
- flex-direction: column;
- align-items: center;
- min-width: 50px;
- /* 确保头像容器有固定宽度 */
- }
- .messages li {
- display: flex;
- align-items: flex-start;
- /* 这将确保所有子元素对齐到容器的顶部 */
- margin-bottom: 10px;
- }
- .avatar-container {
- display: flex;
- /* flex-direction: column; */
- flex-direction: row;
- margin-right: 10px;
- }
- .avatar {
- width: 30px;
- height: 30px;
- border-radius: 50%;
- margin-bottom: 5px;
- }
- .sender-name {
- margin-left: 15px;
- font-size: 12px;
- color: #666;
- margin-top: 2px;
- }
- .message {
- display: flex;
- align-items: flex-start;
- flex-direction: column;
- /* 增加消息间距 */
- }
- .user-avatar {
- /* background-image: url('/static/用户.png'); */
- /* 替换为您的 GIF 文件路径 */
- background-size: cover;
- /* 确保 GIF 覆盖整个头像区域 */
- background-repeat: no-repeat;
- /* 防止 GIF 重复 */
- background-position: center;
- /* 将 GIF 居中 */
- border-radius: 50%;
- }
- .bot-avatar {
- background-image: url('/static/机器人.png');
- /* 替换为您的 GIF 文件路径 */
- background-size: cover;
- /* 确保 GIF 覆盖整个头像区域 */
- background-repeat: no-repeat;
- /* 防止 GIF 重复 */
- background-position: center;
- /* 将 GIF 居中 */
- }
- .message-content {
- background-color: #f0f0f0;
- /* 消息背景 */
- padding-left: 20px;
- /* 上下左右的内边距 */
- padding-right: 10px;
- /* 上下左右的内边距 */
- padding-top: 10px;
- /* 上下左右的内边距 */
- padding-bottom: 10px;
- /* 上下左右的内边距 */
- max-width: calc(90% - 15px);
- /* 计算头像和间距 */
- border-radius: 10px 10px 10px 10px;
- /* 边框圆角 */
- margin-bottom: 10px;
- /* 和下一条消息之间的间距 */
- /* 消息内容样式 */
- display: flex;
- flex-direction: column;
- /* 排列文本 */
- margin-left: 30px;
- margin-top: 0;
- font-size: 12px;
- }
- .message-text {
- margin: 0;
- white-space: normal;
- /* 确保文本会换行 */
- word-wrap: break-word;
- /* 长单词或 URL 会被断开以适应容器宽度 */
- }
- .input-container {
- position: fixed;
- bottom: 0;
- left: 50%;
- /* 把容器移动到屏幕中间 */
- transform: translateX(-50%);
- /* 通过变换抵消容器宽度的一半,实现居中 */
- width: 100%;
- /* 容器宽度设置为屏幕宽度的 80% */
- display: flex;
- justify-content: space-between;
- /* 确保按钮和输入框间有间隔 */
- align-items: center;
- padding: 20px;
- background: white;
- /* 根据你的设计调整 */
- box-shadow: 0px -2px 5px rgba(0, 0, 0, 0.1);
- }
- textarea {
- width: 90%;
- /* 可以根据需要调整 */
- height: 100px;
- padding: 15px;
- border-radius: 7px;
- }
- .input-container textarea {
- width: 80%;
- /* 输入框占据所有可用空间 */
- padding: 10px;
- margin-right: 10px;
- /* 与按钮的间隔 */
- box-sizing: border-box;
- }
- .input-container button {
- min-width: 40px;
- /* 例如,按钮的最小宽度为 100px */
- width: 40px;
- height: 30px;
- line-height: 20px;
- font-size: 12px;
- white-space: nowrap;
- /* 防止文字折行 */
- text-align: center;
- padding: 5px;
- cursor: pointer;
- }
- .sender-name {
- font-weight: bold;
- color: #333;
- font-size: 12px;
- margin-bottom: 5px;
- }
- .btn {
- border: none;
- background-color: #1296db;
- color: #fff;
- }
- .message-content :deep(p) {
- font-size: 12px !important;
- margin: 5px 0 10px;
- padding: 0;
- line-height: 22px;
- font-weight: inherit;
- }
- .message-content :deep(ol) {
- padding-bottom: 10px;
- }
- .message-content :deep(.source-section) {
- margin: 20px 0 3px;
- color: #333;
- font-size: 16px;
- }
- .message-content :deep(ol li) {
- padding-top: 3px;
- }
- .message-content :deep(ol li a) {
- font-size: 14px;
- color: #1296db;
- }
- /* 添加资料来源相关的新样式 */
- .source-section {
- border-top: 1px solid #e5e5e5;
- padding-top: 10px;
- margin-top: 10px;
- }
- .source-title {
- font-size: 14px;
- color: #666;
- margin-bottom: 8px;
- }
- .source-links {
- display: flex;
- flex-direction: column;
- gap: 5px;
- }
- .source-link {
- color: #1296db;
- font-size: 12px;
- cursor: pointer;
- /* text-decoration: underline; */
- }
- .source-link:hover {
- opacity: 0.8;
- }
- /* 添加新建对话按钮样式 */
- .new-chat-btn {
- padding: 0px 20px;
- display: flex;
- justify-content: flex-start;
- background-color: #fff;
- }
-
- .btn-new {
- background-color: #1296db;
- color: #fff;
- font-size: 14px;
- border-radius: 5px;
- border: none;
- height: 35px;
- margin: 0;
- }
- </style>
|