前端轮数实现以及后端WxReviewServiceImpl修改

hot-fix
chantouRichard 2 months ago
parent 3a814e1214
commit a89d6bb9ec

@ -64,7 +64,7 @@ public class WxReviewServiceImpl implements WxReviewService {
+ "}," + "},"
+ "{" + "{"
+ "\"role\": \"" + roleUser + "\"," + "\"role\": \"" + roleUser + "\","
+ "\"content\": \"" + content + "\"" + "\"content\": \"" + escapeJson(content) + "\""
+ "}" + "}"
+ "]," + "],"
+ "\"model\": \"" + model + "\"," + "\"model\": \"" + model + "\","
@ -106,11 +106,25 @@ public class WxReviewServiceImpl implements WxReviewService {
// 工具方法:转义 JSON 字符串中的特殊字符 // 工具方法:转义 JSON 字符串中的特殊字符
private String escapeJson(String input) { private String escapeJson(String input) {
if (input == null) return ""; if (input == null) return "";
return input.replace("\\", "\\\\") StringBuilder escaped = new StringBuilder();
.replace("\"", "\\\"") for (char c : input.toCharArray()) {
.replace("\n", "\\n") switch (c) {
.replace("\r", "\\r") case '"': escaped.append("\\\""); break;
.replace("\t", "\\t"); case '\\': escaped.append("\\\\"); break;
case '\b': escaped.append("\\b"); break;
case '\f': escaped.append("\\f"); break;
case '\n': escaped.append("\\n"); break;
case '\r': escaped.append("\\r"); break;
case '\t': escaped.append("\\t"); break;
default:
if (c <= 0x1F) { // 处理其他控制字符
escaped.append(String.format("\\u%04x", (int) c));
} else {
escaped.append(c);
}
}
}
return escaped.toString();
} }
@Override @Override

@ -23,7 +23,11 @@
"@dcloudio/uni-mp-weixin": "3.0.0-4040520250104002", "@dcloudio/uni-mp-weixin": "3.0.0-4040520250104002",
"@dcloudio/uni-mp-xhs": "3.0.0-4040520250104002", "@dcloudio/uni-mp-xhs": "3.0.0-4040520250104002",
"@dcloudio/uni-quickapp-webview": "3.0.0-4040520250104002", "@dcloudio/uni-quickapp-webview": "3.0.0-4040520250104002",
"markdown-it": "^14.1.0",
"marked": "^15.0.12",
"mini-html-parser2": "^0.3.0",
"pinia": "^3.0.2", "pinia": "^3.0.2",
"towxml": "^3.0.6",
"vue": "^3.5.15", "vue": "^3.5.15",
"vue-i18n": "^9.1.9" "vue-i18n": "^9.1.9"
}, },
@ -5670,6 +5674,12 @@
"node_modules/dom-walk": { "node_modules/dom-walk": {
"version": "0.1.2" "version": "0.1.2"
}, },
"node_modules/domelementtype": {
"version": "1.3.1",
"resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-1.3.1.tgz",
"integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
"license": "BSD-2-Clause"
},
"node_modules/domexception": { "node_modules/domexception": {
"version": "2.0.1", "version": "2.0.1",
"dev": true, "dev": true,
@ -5691,6 +5701,15 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/domhandler": {
"version": "2.4.2",
"resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-2.4.2.tgz",
"integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
"license": "BSD-2-Clause",
"dependencies": {
"domelementtype": "1"
}
},
"node_modules/dunder-proto": { "node_modules/dunder-proto": {
"version": "1.0.1", "version": "1.0.1",
"dev": true, "dev": true,
@ -6276,6 +6295,15 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"license": "MIT",
"engines": {
"node": ">=0.8.x"
}
},
"node_modules/execa": { "node_modules/execa": {
"version": "5.1.1", "version": "5.1.1",
"dev": true, "dev": true,
@ -7995,6 +8023,15 @@
"node": "^12.20.0 || ^14.13.1 || >=16.0.0" "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
} }
}, },
"node_modules/linkify-it": {
"version": "5.0.0",
"resolved": "https://registry.npmmirror.com/linkify-it/-/linkify-it-5.0.0.tgz",
"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
"license": "MIT",
"dependencies": {
"uc.micro": "^2.0.0"
}
},
"node_modules/load-bmfont": { "node_modules/load-bmfont": {
"version": "1.4.2", "version": "1.4.2",
"license": "MIT", "license": "MIT",
@ -8148,6 +8185,41 @@
"tmpl": "1.0.5" "tmpl": "1.0.5"
} }
}, },
"node_modules/markdown-it": {
"version": "14.1.0",
"resolved": "https://registry.npmmirror.com/markdown-it/-/markdown-it-14.1.0.tgz",
"integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1",
"entities": "^4.4.0",
"linkify-it": "^5.0.0",
"mdurl": "^2.0.0",
"punycode.js": "^2.3.1",
"uc.micro": "^2.1.0"
},
"bin": {
"markdown-it": "bin/markdown-it.mjs"
}
},
"node_modules/markdown-it/node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"license": "Python-2.0"
},
"node_modules/marked": {
"version": "15.0.12",
"resolved": "https://registry.npmmirror.com/marked/-/marked-15.0.12.tgz",
"integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==",
"license": "MIT",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/math-intrinsics": { "node_modules/math-intrinsics": {
"version": "1.1.0", "version": "1.1.0",
"dev": true, "dev": true,
@ -8156,6 +8228,12 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/mdurl": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/mdurl/-/mdurl-2.0.0.tgz",
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
"license": "MIT"
},
"node_modules/media-typer": { "node_modules/media-typer": {
"version": "0.3.0", "version": "0.3.0",
"dev": true, "dev": true,
@ -8271,6 +8349,22 @@
"dom-walk": "^0.1.0" "dom-walk": "^0.1.0"
} }
}, },
"node_modules/mini-html-parser2": {
"version": "0.3.0",
"resolved": "https://registry.npmmirror.com/mini-html-parser2/-/mini-html-parser2-0.3.0.tgz",
"integrity": "sha512-W4x1MCmtlnAH5M9qQ1WbRn+hTvv7bdrJx4VI+6SD0MUZatW/6K7v213Aidx7VDQmSKoRv+iAn5TswJnesOs71Q==",
"dependencies": {
"domhandler": "^2.4.2",
"entities": "^1.1.1",
"events": "^3.0.0"
}
},
"node_modules/mini-html-parser2/node_modules/entities": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/entities/-/entities-1.1.2.tgz",
"integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
"license": "BSD-2-Clause"
},
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "3.1.2", "version": "3.1.2",
"dev": true, "dev": true,
@ -8965,6 +9059,15 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/punycode.js": {
"version": "2.3.1",
"resolved": "https://registry.npmmirror.com/punycode.js/-/punycode.js-2.3.1.tgz",
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/qrcode-reader": { "node_modules/qrcode-reader": {
"version": "1.0.4", "version": "1.0.4",
"license": "Apache-2.0" "license": "Apache-2.0"
@ -10137,6 +10240,47 @@
"node": ">= 4.0.0" "node": ">= 4.0.0"
} }
}, },
"node_modules/towxml": {
"version": "3.0.6",
"resolved": "https://registry.npmmirror.com/towxml/-/towxml-3.0.6.tgz",
"integrity": "sha512-AiEEyTYemk3xLlypfihR+vr/slyjvQZvZbGGXQdRklwYY8dbDEGUDopFwAtfW7tVkOc09rfZ7yu040vjJU84cw==",
"license": "MIT",
"dependencies": {
"fs-extra": "^8.1.0"
}
},
"node_modules/towxml/node_modules/fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
},
"engines": {
"node": ">=6 <7 || >=8"
}
},
"node_modules/towxml/node_modules/jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
"license": "MIT",
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/towxml/node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"license": "MIT",
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/tr46": { "node_modules/tr46": {
"version": "2.1.0", "version": "2.1.0",
"dev": true, "dev": true,
@ -10191,6 +10335,12 @@
"is-typedarray": "^1.0.0" "is-typedarray": "^1.0.0"
} }
}, },
"node_modules/uc.micro": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/uc.micro/-/uc.micro-2.1.0.tgz",
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
"license": "MIT"
},
"node_modules/ufo": { "node_modules/ufo": {
"version": "1.5.4", "version": "1.5.4",
"license": "MIT" "license": "MIT"

@ -25,7 +25,11 @@
"@dcloudio/uni-mp-weixin": "3.0.0-4040520250104002", "@dcloudio/uni-mp-weixin": "3.0.0-4040520250104002",
"@dcloudio/uni-mp-xhs": "3.0.0-4040520250104002", "@dcloudio/uni-mp-xhs": "3.0.0-4040520250104002",
"@dcloudio/uni-quickapp-webview": "3.0.0-4040520250104002", "@dcloudio/uni-quickapp-webview": "3.0.0-4040520250104002",
"markdown-it": "^14.1.0",
"marked": "^15.0.12",
"mini-html-parser2": "^0.3.0",
"pinia": "^3.0.2", "pinia": "^3.0.2",
"towxml": "^3.0.6",
"vue": "^3.5.15", "vue": "^3.5.15",
"vue-i18n": "^9.1.9" "vue-i18n": "^9.1.9"
}, },

@ -1,13 +1,20 @@
{ {
"miniprogramRoot": "", "miniprogramRoot": "./",
"libVersion": "3.0.2", "libVersion": "3.0.2",
"appid": "wxdc5c8df2ec2453e4", "appid": "wxdc5c8df2ec2453e4",
"compileType": "miniprogram", "compileType": "miniprogram",
"packOptions": { "packOptions": {
"ignore": [], "ignore": [{ "type": "folder", "value": "node_modules" }],
"include": [] "include": []
}, },
"setting": { "setting": {
"packNpmManmediately": true,
"packNpmRelationList": [
{
"packageJsonPath": "./package.json",
"miniprogramNpmDistDir": "./"
}
],
"babelSetting": { "babelSetting": {
"ignore": [], "ignore": [],
"disablePlugins": [], "disablePlugins": [],
@ -19,4 +26,4 @@
"tabIndent": "insertSpaces", "tabIndent": "insertSpaces",
"tabSize": 2 "tabSize": 2
} }
} }

@ -2,12 +2,7 @@
<view class="debate-component"> <view class="debate-component">
<!-- 动态背景元素 --> <!-- 动态背景元素 -->
<view class="animated-bg"> <view class="animated-bg">
<view <view v-for="i in 6" :key="i" class="bg-circle" :style="getRandomCircleStyle()"></view>
v-for="i in 6"
:key="i"
class="bg-circle"
:style="getRandomCircleStyle()"
></view>
</view> </view>
<!-- 顶部卡片 - 可收缩 --> <!-- 顶部卡片 - 可收缩 -->
@ -15,17 +10,12 @@
<view class="card-header" @click="toggleCard"> <view class="card-header" @click="toggleCard">
<view class="card-icon-wrapper"> <view class="card-icon-wrapper">
<view class="card-icon"> <view class="card-icon">
<image <image src="/static/icons/robot-2-line.png" mode="aspectFit"></image>
src="/static/icons/robot-2-line.png"
mode="aspectFit"
></image>
</view> </view>
</view> </view>
<view class="card-text"> <view class="card-text">
<view class="card-title">模拟辩论</view> <view class="card-title">模拟辩论</view>
<view class="card-content" v-if="!isCardCollapsed" <view class="card-content" v-if="!isCardCollapsed">AI</view>
>与AI进行实时辩论对练提升应变能力</view
>
</view> </view>
<view class="card-icon-wrapper"> <view class="card-icon-wrapper">
<view class="card-icon" @click.stop="showHistory = true"> <view class="card-icon" @click.stop="showHistory = true">
@ -33,14 +23,10 @@
</view> </view>
</view> </view>
<view class="collapse-icon"> <view class="collapse-icon">
<image <image :src="isCardCollapsed
:src=" ? '/static/icons/arrow-down-s-line.png'
isCardCollapsed : '/static/icons/arrow-up-s-line.png'
? '/static/icons/arrow-down-s-line.png' " mode="aspectFit"></image>
: '/static/icons/arrow-up-s-line.png'
"
mode="aspectFit"
></image>
</view> </view>
</view> </view>
</view> </view>
@ -48,32 +34,17 @@
<!-- 辩论主题选择器 --> <!-- 辩论主题选择器 -->
<view class="topic-selector" v-if="!isCardCollapsed"> <view class="topic-selector" v-if="!isCardCollapsed">
<scroll-view scroll-x class="topic-scroll" show-scrollbar="false"> <scroll-view scroll-x class="topic-scroll" show-scrollbar="false">
<view <view v-for="(topic, idx) in debateTopics" :key="idx" :class="['topic-pill', { active: selectedTopic === idx }]"
v-for="(topic, idx) in debateTopics" @click="selectTopic(idx)">
:key="idx"
:class="['topic-pill', { active: selectedTopic === idx }]"
@click="selectTopic(idx)"
>
{{ topic }} {{ topic }}
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
<!-- 聊天展示区 --> <!-- 聊天展示区 -->
<scroll-view <scroll-view class="chat-area" scroll-y :scroll-into-view="scrollToView" scroll-with-animation>
class="chat-area" <view v-for="(msg, index) in messages" :key="index" :id="'msg' + index" class="chat-message"
scroll-y :class="msg.role === 'user' ? 'from-user' : 'from-ai'" @longpress="onLongPress()">
:scroll-into-view="scrollToView"
scroll-with-animation
>
<view
v-for="(msg, index) in messages"
:key="index"
:id="'msg' + index"
class="chat-message"
:class="msg.role === 'user' ? 'from-user' : 'from-ai'"
@longpress="onLongPress()"
>
<view class="avatar" v-if="msg.role === 'ai'">AI</view> <view class="avatar" v-if="msg.role === 'ai'">AI</view>
<view class="bubble"> <view class="bubble">
<block v-if="msg.loading"> <block v-if="msg.loading">
@ -91,22 +62,41 @@
</view> </view>
</scroll-view> </scroll-view>
<!-- 轮数选择与进度条区域 -->
<view class="rounds-control" :class="{ 'rounds-selected': debateRounds > 0 }">
<!-- 未选择轮数时显示选择按钮 -->
<view v-if="roundsSelected === false" class="select-rounds-btn" @click="showRoundsPopup = true">
<image src="/static/icons/repeat-line.png" mode="aspectFit"></image>
<text>选择辩论轮数</text>
</view>
<!-- 已选择轮数时显示进度条 -->
<view v-else class="rounds-selected-container">
<view class="rounds-label" @click="showRoundsPopup = true">
<image src="/static/icons/repeat-line.png" mode="aspectFit"></image>
<text>{{ debateRounds }}回合</text>
</view>
<view class="progress-container">
<view class="progress-bar">
<view class="progress-inner" :style="{ width: progressPercentage + '%' }"></view>
</view>
<view class="rounds-counter">{{ currentRound }}/{{ debateRounds }}</view>
<!-- 添加复盘按钮 -->
<view v-if="showReviewButton" class="review-button" @click="showReviewModal">
<image src="/static/icons/file-chart-line.png" mode="aspectFit"></image>
</view>
</view>
</view>
</view>
<!-- 输入框与发送按钮 --> <!-- 输入框与发送按钮 -->
<view class="chat-input"> <view class="chat-input">
<view class="input-group"> <view class="input-group">
<view class="textarea-container"> <view class="textarea-container">
<textarea <textarea class="textarea-box" v-model="content" placeholder="输入你的辩论观点与AI进行思辨交锋" auto-height
class="textarea-box" maxlength="1000" />
v-model="content"
placeholder="输入你的辩论观点与AI进行思辨交锋"
auto-height
maxlength="1000"
/>
<button class="send-button-embedded" @click="sendMessage()"> <button class="send-button-embedded" @click="sendMessage()">
<image <image src="/static/icons/send-plane-fill.png" mode="aspectFit"></image>
src="/static/icons/send-plane-fill.png"
mode="aspectFit"
></image>
</button> </button>
</view> </view>
</view> </view>
@ -115,22 +105,59 @@
<!-- 底部安全区域 --> <!-- 底部安全区域 -->
<view class="safe-area-bottom"></view> <view class="safe-area-bottom"></view>
<Popup <Popup :visible="showSheet" buttonText="开始复盘" buttonIcon="🧠" @close="showSheet = false"
:visible="showSheet" @click="handleSheetClick" />
buttonText="开始复盘"
buttonIcon="🧠" <!-- 轮数选择弹窗 -->
@close="showSheet = false" <view v-if="showRoundsPopup" class="rounds-popup-mask" @click="showRoundsPopup = false">
@click="handleSheetClick" <view class="rounds-popup" @click.stop>
/> <view class="popup-header">
<view class="popup-title">选择辩论轮数</view>
<ConversationHistory <view class="close-icon" @click="showRoundsPopup = false">
:visible="showHistory" <image src="/static/icons/close-line.png" mode="aspectFit"></image>
:history-list="chatHistory" </view>
:type="1" </view>
@update:visible="showHistory = $event"
@select="handleSelect" <view class="rounds-options">
@createNew="createNew" <view class="round-option" :class="{ selected: selectedOption === 1 }" @click="selectRounds(1)">
/> <view class="option-icon"></view>
<view class="option-text">
<view class="option-title">快速交锋</view>
<view class="option-desc">10回合 · 适合快速练习</view>
</view>
</view>
<view class="round-option" :class="{ selected: selectedOption === 2 }" @click="selectRounds(2)">
<view class="option-icon">🔍</view>
<view class="option-text">
<view class="option-title">深度辩论</view>
<view class="option-desc">20回合 · 深入探讨话题</view>
</view>
</view>
</view>
<button class="confirm-button-selectRounds" @click="confirmRounds"></button>
</view>
</view>
<!-- 复盘确认弹窗 -->
<view v-if="showReviewConfirm" class="review-confirm-mask" @click="showReviewConfirm = false">
<view class="review-confirm-popup" @click.stop>
<view class="popup-header">
<view class="popup-title">辩论完成</view>
</view>
<view class="popup-content">
<text>辩论已完成{{ debateRounds }}回合是否立即进行复盘分析</text>
</view>
<view class="popup-buttons">
<button class="cancel-button" @click="showReviewConfirm = false">稍后再说</button>
<button class="confirm-button-gotoReview" @click="startReview"></button>
</view>
</view>
</view>
<ConversationHistory :visible="showHistory" :history-list="chatHistory" :type="1"
@update:visible="showHistory = $event" @select="handleSelect" @createNew="createNew" />
</view> </view>
</template> </template>
@ -142,7 +169,7 @@ import ConversationHistory from "./ConversationHistory.vue";
import { useTokenStore } from "../stores/tokenStore"; import { useTokenStore } from "../stores/tokenStore";
export default { export default {
components: { Popup,ConversationHistory }, components: { Popup, ConversationHistory },
props: { props: {
argument: { argument: {
type: Object, type: Object,
@ -152,13 +179,16 @@ export default {
mounted() { mounted() {
const pinia = this.$pinia; const pinia = this.$pinia;
const store = useArgumentStore(pinia); const store = useArgumentStore(pinia);
if(store.selectedArgument.content)this.content = store.selectedArgument.content; if (store.selectedArgument.content) this.content = store.selectedArgument.content;
this.getHistoryList(); this.getHistoryList();
this.conversationId = useDebateStore().conversationId; this.conversationId = useDebateStore().conversationId;
console.log("conversationId:", this.conversationId); console.log("conversationId:", this.conversationId);
this.messages = useDebateStore().conversation; this.messages = useDebateStore().conversation;
//
this.showRoundsPopup = true;
}, },
data() { data() {
@ -167,8 +197,8 @@ export default {
showHistory: false, showHistory: false,
chatHistory: ["test1", "test2"], chatHistory: ["test1", "test2"],
showSheet:false, showSheet: false,
StoreHistory:"", StoreHistory: "",
input: "", input: "",
messages: [ messages: [
{ {
@ -189,8 +219,39 @@ export default {
], ],
selectedTopic: -1, selectedTopic: -1,
isCardCollapsed: false, // isCardCollapsed: false, //
//
showRoundsPopup: false, //
roundsSelected: false, //
debateRounds: 0, //
currentRound: 0, //
selectedOption: 0, //
showReviewConfirm: false,//
showReviewButton: false,//
}; };
}, },
computed: {
//
progressPercentage() {
if (this.debateRounds === 0) return 0;
return (this.currentRound / this.debateRounds) * 100;
},
},
watch: {
//
currentRound(newVal) {
if (newVal >= this.debateRounds && this.debateRounds > 0) {
//
setTimeout(() => {
this.showReviewConfirm = true;
}, 1000);
}
}
},
methods: { methods: {
async getHistoryList() { async getHistoryList() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -263,15 +324,15 @@ export default {
success: (res) => { success: (res) => {
console.log("res:", res); console.log("res:", res);
if (res.statusCode === 200 && res.data.code === 200) { if (res.statusCode === 200 && res.data.code === 200) {
this.messages=[]; this.messages = [];
for(let item of res.data.data){ for (let item of res.data.data) {
this.messages.push({ this.messages.push({
role:"user", role: "user",
content:item.userMessage content: item.userMessage
}); });
this.messages.push({ this.messages.push({
role:"ai", role: "ai",
content:item.content content: item.content
}); });
} }
useDebateStore().setConversation(this.messages); useDebateStore().setConversation(this.messages);
@ -353,6 +414,28 @@ export default {
}, },
async sendMessage() { async sendMessage() {
//------
//
if (!this.roundsSelected) {
uni.showToast({
title: "请选择辩论轮数",
icon: "none",
duration: 2000,
});
return;
}
//
if (this.currentRound >= this.debateRounds) {
uni.showToast({
title: "辩论轮数已达上限",
icon: "none",
duration: 2000,
});
return;
}
//--------
if (!this.content.trim()) return; if (!this.content.trim()) return;
// //
@ -388,6 +471,24 @@ export default {
// loading AI // loading AI
this.messages.splice(aiIndex, 1, { role: "ai", content: reply }); this.messages.splice(aiIndex, 1, { role: "ai", content: reply });
this.scrollToBottom(); this.scrollToBottom();
//----------------------
//
this.currentRound++;
//
if (this.currentRound >= this.debateRounds) {
this.messages.push({
role: "ai",
content: `本轮辩论已完成(${this.debateRounds}回合)。您可以点击消息进行复盘分析。`,
});
};
if (this.currentRound >= this.debateRounds) {
//
this.showReviewButton = true;
return;
}
}, },
async callAI(history, content) { async callAI(history, content) {
@ -446,6 +547,82 @@ export default {
this.scrollToView = "msg" + (this.messages.length - 1); this.scrollToView = "msg" + (this.messages.length - 1);
}); });
}, },
//------------
//
selectRounds(type) {
this.selectedOption = type;
},
//
confirmRounds() {
if (this.selectedOption === 0) {
uni.showToast({
title: '请选择轮数',
icon: 'none',
duration: 2000
});
return;
}
//
if (this.roundsSelected) {
uni.showToast({
title: '已选择轮数',
icon: 'none',
duration: 2000
});
return;
}
//
if (this.selectedOption === 1) {
this.debateRounds = 10;
} else if (this.selectedOption === 2) {
this.debateRounds = 20;
}
this.currentRound = 0;
this.roundsSelected = true;
this.showRoundsPopup = false;
this.showReviewButton = false;
this.showReviewConfirm = false;
//
this.message = [{
role: 'ai',
content: `已选择 ${this.debateRounds} 回合辩论!请分享你的观点,我们开始吧!`
}];
this.scrollToBottom();
},
//
startReview() {
// //
// let history = this.messages
// .filter((msg) => !msg.loading)
// .map((msg) => {
// if (msg.role === 'user') {
// return '${ msg.content}';
// } else if (msg.role === 'ai') {
// return 'AI ${ msg.content}';
// }
// return '';
// }).join('\n');
//
// //store
// const pinia = this.$pinia;
// const store = useDebateStore(pinia);
// store.setDebate(history);
// //
// this.$emit('tab-change', 3);
this.onLongPress();
//
this.showReviewConfirm = false;
},
//
showReviewModal() {
this.showReviewConfirm = true;
},
}, },
}; };
</script> </script>
@ -460,7 +637,8 @@ export default {
background: linear-gradient(135deg, #4338ca 0%, #7c3aed 100%); background: linear-gradient(135deg, #4338ca 0%, #7c3aed 100%);
overflow-x: hidden; overflow-x: hidden;
box-sizing: border-box; box-sizing: border-box;
padding-bottom: 180rpx; /* 为底部TabBar留出空间 */ padding-bottom: 180rpx;
/* 为底部TabBar留出空间 */
position: relative; position: relative;
} }
@ -478,11 +656,9 @@ export default {
.bg-circle { .bg-circle {
position: absolute; position: absolute;
border-radius: 50%; border-radius: 50%;
background: linear-gradient( background: linear-gradient(135deg,
135deg, rgba(255, 255, 255, 0.05),
rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.02));
rgba(255, 255, 255, 0.02)
);
animation: float 20s infinite ease-in-out; animation: float 20s infinite ease-in-out;
opacity: 0.4; opacity: 0.4;
/* 添加过渡效果 */ /* 添加过渡效果 */
@ -490,16 +666,20 @@ export default {
} }
@keyframes float { @keyframes float {
0%, 0%,
100% { 100% {
transform: translate(0, 0) scale(1); transform: translate(0, 0) scale(1);
} }
25% { 25% {
transform: translate(5%, 10%) scale(1.05); transform: translate(5%, 10%) scale(1.05);
} }
50% { 50% {
transform: translate(10%, 5%) scale(0.95); transform: translate(10%, 5%) scale(0.95);
} }
75% { 75% {
transform: translate(5%, 15%) scale(1.1); transform: translate(5%, 15%) scale(1.1);
} }
@ -552,6 +732,7 @@ export default {
opacity: 0; opacity: 0;
transform: translateY(-30rpx); transform: translateY(-30rpx);
} }
to { to {
opacity: 1; opacity: 1;
transform: translateY(0); transform: translateY(0);
@ -588,12 +769,10 @@ export default {
left: -50%; left: -50%;
width: 200%; width: 200%;
height: 200%; height: 200%;
background: linear-gradient( background: linear-gradient(to bottom right,
to bottom right, rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.1) 50%,
rgba(255, 255, 255, 0.1) 50%, rgba(255, 255, 255, 0) 100%);
rgba(255, 255, 255, 0) 100%
);
transform: rotate(45deg); transform: rotate(45deg);
animation: shine 3s infinite; animation: shine 3s infinite;
} }
@ -602,6 +781,7 @@ export default {
0% { 0% {
transform: translateX(-100%) rotate(45deg); transform: translateX(-100%) rotate(45deg);
} }
20%, 20%,
100% { 100% {
transform: translateX(100%) rotate(45deg); transform: translateX(100%) rotate(45deg);
@ -708,6 +888,7 @@ export default {
opacity: 0; opacity: 0;
transform: translateY(20rpx); transform: translateY(20rpx);
} }
to { to {
opacity: 1; opacity: 1;
transform: translateY(0); transform: translateY(0);
@ -754,7 +935,8 @@ export default {
word-break: break-word; word-break: break-word;
box-shadow: 0 4rpx 15rpx rgba(0, 0, 0, 0.1); box-shadow: 0 4rpx 15rpx rgba(0, 0, 0, 0.1);
position: relative; position: relative;
transition: all 0.3s ease; /* 添加过渡效果 */ transition: all 0.3s ease;
/* 添加过渡效果 */
} }
.from-user .bubble { .from-user .bubble {
@ -773,7 +955,8 @@ export default {
height: 0; height: 0;
border-left: 16rpx solid #f59e0b; border-left: 16rpx solid #f59e0b;
border-top: 16rpx solid transparent; border-top: 16rpx solid transparent;
transition: all 0.3s ease; /* 添加过渡效果 */ transition: all 0.3s ease;
/* 添加过渡效果 */
} }
.from-ai .bubble { .from-ai .bubble {
@ -792,7 +975,8 @@ export default {
height: 0; height: 0;
border-right: 16rpx solid rgba(255, 255, 255, 0.2); border-right: 16rpx solid rgba(255, 255, 255, 0.2);
border-top: 16rpx solid transparent; border-top: 16rpx solid transparent;
transition: all 0.3s ease; /* 添加过渡效果 */ transition: all 0.3s ease;
/* 添加过渡效果 */
} }
/* 加载动画 */ /* 加载动画 */
@ -821,16 +1005,144 @@ export default {
} }
@keyframes bounce { @keyframes bounce {
0%, 0%,
80%, 80%,
100% { 100% {
transform: scale(0); transform: scale(0);
} }
40% { 40% {
transform: scale(1); transform: scale(1);
} }
} }
/* 轮数控制区域 ------------------------*/
.rounds-control {
display: flex;
align-items: center;
padding: 20rpx 30rpx;
background: rgba(255, 255, 255, 0.15);
border-radius: 16rpx;
margin: 0 auto 20rpx;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
animation: slideUp 0.4s ease-out;
width: 70%;
transition: all 0.5s ease;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 选择轮数按钮样式 */
.select-rounds-btn {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
padding: 20rpx;
background: rgba(104, 57, 224, 0.3);
border-radius: 16rpx;
font-size: 28rpx;
color: white;
font-weight: 500;
}
.select-rounds-btn image {
width: 30rpx;
height: 30rpx;
margin-right: 10rpx;
filter: brightness(0) invert(1);
}
.rounds-selected-container {
display: flex;
align-items: center;
width: 100%;
}
.rounds-label {
display: flex;
align-items: center;
padding: 10rpx 20rpx;
background: rgba(104, 57, 224, 0.3);
border-radius: 12rpx;
margin-right: 20rpx;
font-size: 26rpx;
color: white;
font-weight: 500;
min-width: 140rpx;
flex-shrink: 0;
margin-right: 20rpx;
}
.rounds-label image {
width: 30rpx;
height: 30rpx;
margin-right: 10rpx;
filter: brightness(0) invert(1);
}
.progress-container {
flex: 1;
display: flex;
align-items: center;
}
.progress-bar {
flex: 1;
height: 16rpx;
background: rgba(255, 255, 255, 0.2);
border-radius: 8rpx;
overflow: hidden;
margin-right: 20rpx;
}
/* 进度条动画 */
.progress-inner {
height: 100%;
background: linear-gradient(90deg, #f59e0b, #fcd34d);
border-radius: 8rpx;
transition: width 0.5s ease;
}
/* 进度条加载动画 */
@keyframes progress-grow {
from {
width: 0;
}
to {
width: v-bind('progressPercentage + "%"');
}
}
.progress-inner {
animation: progress-grow 0.8s ease-out forwards;
}
.rounds-counter {
font-size: 28rpx;
font-weight: 600;
color: white;
min-width: 80rpx;
text-align: center;
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
}
/* 输入区域 */ /* 输入区域 */
.chat-input { .chat-input {
display: flex; display: flex;
@ -847,13 +1159,15 @@ export default {
.textarea-box { .textarea-box {
background: rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.2);
border-radius: 20rpx; border-radius: 20rpx;
padding: 16rpx 20rpx 16rpx 90rpx; /* 增加左侧内边距为发送按钮留出空间 */ padding: 16rpx 20rpx 16rpx 90rpx;
/* 增加左侧内边距为发送按钮留出空间 */
font-size: 28rpx; font-size: 28rpx;
color: #ffffff; color: #ffffff;
border: 1px solid rgba(255, 255, 255, 0.3); border: 1px solid rgba(255, 255, 255, 0.3);
transition: all 0.3s; transition: all 0.3s;
min-height: 70rpx; min-height: 70rpx;
max-height: 180rpx; /* 限制最大高度 */ max-height: 180rpx;
/* 限制最大高度 */
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
} }
@ -923,14 +1237,252 @@ export default {
.send-button-embedded image { .send-button-embedded image {
width: 40rpx; width: 40rpx;
height: 40rpx; height: 40rpx;
filter: invert(72%) sepia(87%) saturate(1242%) hue-rotate(325deg) filter: invert(72%) sepia(87%) saturate(1242%) hue-rotate(325deg) brightness(101%) contrast(96%);
brightness(101%) contrast(96%);
} }
/* 底部安全区域 */ /* 底部安全区域 */
.safe-area-bottom { .safe-area-bottom {
height: 20rpx; height: 20rpx;
height: calc(20rpx + constant(safe-area-inset-bottom)); /* iOS 11.0-11.2 */ height: calc(20rpx + constant(safe-area-inset-bottom));
height: calc(20rpx + env(safe-area-inset-bottom)); /* iOS 11.2+ */ /* iOS 11.0-11.2 */
height: calc(20rpx + env(safe-area-inset-bottom));
/* iOS 11.2+ */
}
/* 轮数选择弹窗样式 */
.rounds-popup-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.rounds-popup {
width: 80%;
max-width: 600rpx;
background: white;
border-radius: 28rpx;
overflow: hidden;
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.3);
animation: scaleIn 0.3s ease-out;
}
@keyframes scaleIn {
from {
transform: scale(0.9);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
background: #7C3AED;
}
.popup-title {
font-size: 36rpx;
font-weight: 700;
color: white;
}
.close-icon {
width: 50rpx;
height: 50rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
}
.close-icon image {
width: 30rpx;
height: 30rpx;
filter: brightness(0) invert(1);
}
.rounds-options {
padding: 30rpx;
}
.round-option {
display: flex;
align-items: center;
padding: 30rpx;
border-radius: 20rpx;
background: #f8f9ff;
border: 2rpx solid #e6e6ff;
margin-bottom: 25rpx;
transition: all 0.3s;
}
.round-option.selected {
background: #6839E0;
border-color: #6839E0;
transform: translateY(-5rpx);
box-shadow: 0 8rpx 20rpx rgba(104, 57, 224, 0.3);
width: auto;
margin: 0 30rpx 20rpx;
}
.option-icon {
font-size: 50rpx;
margin-right: 25rpx;
width: 80rpx;
text-align: center;
}
.round-option.selected .option-icon {
color: white;
}
.option-title {
font-size: 32rpx;
font-weight: 600;
margin-bottom: 8rpx;
}
.option-desc {
font-size: 26rpx;
color: #666;
}
.round-option.selected .option-title,
.round-option.selected .option-desc {
color: white;
}
.confirm-button-selectRounds {
background: #6839E0;
color: white;
border-radius: 0;
height: 100rpx;
line-height: 100rpx;
font-size: 32rpx;
font-weight: 600;
border: none;
}
/* 复盘确认弹窗样式 */
.review-confirm-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
z-index: 2000;
animation: fadeIn 0.3s;
}
.review-confirm-popup {
width: 80%;
max-width: 600rpx;
background: white;
border-radius: 28rpx;
overflow: hidden;
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.3);
}
.popup-header {
padding: 30rpx;
background: #7C3AED;
text-align: center;
}
.popup-title {
font-size: 36rpx;
font-weight: 700;
color: white;
}
.popup-content {
padding: 40rpx 30rpx;
font-size: 30rpx;
color: #333;
text-align: center;
line-height: 1.6;
}
.popup-buttons {
display: flex;
padding: 20rpx;
border-top: 1rpx solid #eee;
}
.cancel-button {
flex: 1;
background: #f5f5f5;
color: #666;
border-radius: 12rpx;
margin-right: 15rpx;
height: 80rpx;
line-height: 80rpx;
font-size: 30rpx;
}
.confirm-button-gotoReview {
flex: 1;
background: #6839E0;
color: white;
border-radius: 12rpx;
margin-left: 15rpx;
height: 80rpx;
line-height: 80rpx;
font-size: 30rpx;
}
/* 复盘按钮样式 */
.review-button {
width: 50rpx;
height: 50rpx;
margin-left: 20rpx;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
padding: 8rpx;
}
.review-button image {
width: 100%;
height: 100%;
filter: brightness(0) invert(1);
}
/* 调整进度条容器布局 */
.progress-container {
flex: 1;
display: flex;
align-items: center;
} }
</style> </style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

@ -15,7 +15,7 @@ export const useDebateStore = defineStore('debate', {
}), }),
actions: { actions: {
setDebate(content) { setDebate(content) {
this.selectedDebate.content = content; this.selectedDebate = content;
}, },
setConversationId(id) { setConversationId(id) {
this.conversationId = id; this.conversationId = id;

Loading…
Cancel
Save