index.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. <template>
  2. <div class="counsel-container">
  3. <div class="counsel-header">
  4. <span>会话<i class="ai el-icon-chat-dot-round"></i></span>
  5. </div>
  6. <div class="counsel-chats">
  7. <div class="counsel-left flex">
  8. <div class="header-img">
  9. <img src="/static/images/aiModel/kefu.png" />
  10. </div>
  11. <div class="content">
  12. <span>Hi,有问题您可以这样问我:</span>
  13. <span class="high" @click="sendDefault">{{ defaultMsg }}</span>
  14. </div>
  15. </div>
  16. <!--右侧-->
  17. <div v-for="(item, index) in questionList" :key="index">
  18. <!--右侧-->
  19. <div class="counsel-right flex" v-if="item.type == 0">
  20. <i class="el-icon-loading" v-if="item.mstatus == 0"></i>
  21. <i
  22. class="el-icon-warning cursor"
  23. v-else-if="item.mstatus == 1"
  24. @click="sendToBackend(item.content, index)"
  25. ></i>
  26. <div class="content" v-if="item.content">
  27. <span>{{ item.content }}</span>
  28. </div>
  29. <audio v-else controls autoplay :src="item.mp3url"></audio>
  30. <div class="header-img">
  31. <img src="/static/images/aiModel/default.png" />
  32. </div>
  33. </div>
  34. <!--左侧-->
  35. <div class="counsel-left flex" v-else>
  36. <div class="header-img">
  37. <img src="/static/images/aiModel/kefu.png" />
  38. </div>
  39. <div class="content">
  40. <span v-if="item.type" class="tit">参考意见</span>
  41. <span :class="{ high: item.type }" @click="onClick(item)">
  42. {{ item.content }}
  43. </span>
  44. </div>
  45. </div>
  46. </div>
  47. </div>
  48. <div class="voice" v-if="spdisabled">
  49. <div>正在识别</div>
  50. <div class="noise"></div>
  51. </div>
  52. <div class="searchBtom">
  53. <div
  54. class="icon"
  55. :class="
  56. spdisabled ? 'el-icon-turn-off-microphone' : 'el-icon-microphone'
  57. "
  58. title="按住说话"
  59. :disabled="spdisabled"
  60. @click="voice"
  61. ></div>
  62. <el-input
  63. clearable
  64. v-model="sendContent"
  65. @input="change_witch"
  66. class="search"
  67. placeholder="请输入问题"
  68. ></el-input>
  69. <div
  70. class="el-icon-s-promotion icon sendIcon"
  71. :class="sendContent ? 'ok' : 'no'"
  72. @click="send"
  73. ></div>
  74. </div>
  75. </div>
  76. </template>
  77. <script>
  78. import { AddFzxz } from "../../api/ghss/ghxz.js";
  79. import record from "./record.js";
  80. let recognition = null;
  81. export default {
  82. props: {},
  83. data() {
  84. return {
  85. questionList: [
  86. { type: 0, mp3url: "" },
  87. {
  88. type: "selectLand",
  89. content: "sss",
  90. data: { rwbsm: "5f3d25e556374e78966feca5a64fa887" },
  91. },
  92. ],
  93. defaultMsg:
  94. "我要在抱坡区进行选址,用地面积30到50亩,选择居住用地,距离幼儿园200米,距离医院300米,距离火葬场大于5000米",
  95. sendContent: "",
  96. spdisabled: false,
  97. };
  98. },
  99. created() {},
  100. mounted() {},
  101. methods: {
  102. initSpeech() {
  103. // 检查浏览器是否支持SpeechRecognition
  104. const SpeechRecognition =
  105. window.SpeechRecognition || window.webkitSpeechRecognition;
  106. if (!SpeechRecognition) {
  107. alert(
  108. "你的浏览器不支持语音识别功能,请使用支持的浏览器,例如Google Chrome。"
  109. );
  110. } else {
  111. recognition = new SpeechRecognition();
  112. recognition.lang = "zh-CN"; // 设置识别语言为中文
  113. recognition.interimResults = false; // 不返回临时结果
  114. recognition.maxAlternatives = 1; // 返回的识别结果数量
  115. recognition.onstart = () => {
  116. this.$message.success("开始语音识别...");
  117. this.spdisabled = true;
  118. };
  119. recognition.onresult = (event) => {
  120. this.sendContent = event.results[0][0].transcript;
  121. // this.send(); // 将识别结果发送到后台
  122. };
  123. recognition.onerror = (event) => {
  124. this.$message.warning("语音识别出错: " + event.error);
  125. };
  126. recognition.onend = () => {
  127. // this.$message.success("语音识别结束:");
  128. console.log("语音识别结束");
  129. this.spdisabled = false;
  130. };
  131. }
  132. },
  133. /**初始化 */
  134. init() {
  135. let _this = this;
  136. record.getPermission(function (permiss) {
  137. if (permiss.status == "fail") {
  138. _this.$message.warning("语音识别出错: " + permiss.data);
  139. } else {
  140. record.startRecorder();
  141. _this.$message.success("开始语音识别...");
  142. _this.spdisabled = true;
  143. }
  144. });
  145. },
  146. /**结束录音 */
  147. stop() {
  148. let _this = this;
  149. record.stopRecorder(function (res) {
  150. console.log("-blob----", res);
  151. _this.questionList.push({
  152. type: 0,
  153. mp3url: (window.URL || webkitURL).createObjectURL(res.blob),
  154. mstatus: 0,
  155. });
  156. /**处理 */
  157. axios({
  158. method: "POST",
  159. url: `${window.aiURI}/upload`,
  160. data: res.formData,
  161. headers: {
  162. "Content-Type": "application/json",
  163. },
  164. })
  165. .then((mres) => {
  166. if (mres.status == 200) {
  167. } else {
  168. }
  169. })
  170. .catch(() => {});
  171. });
  172. this.spdisabled = false;
  173. },
  174. sendDefault() {
  175. this.sendContent = this.defaultMsg;
  176. // this.send();
  177. },
  178. send() {
  179. // 咨询问题
  180. if (this.sendContent) {
  181. this.questionList.push({
  182. type: 0,
  183. content: this.sendContent,
  184. mstatus: 0,
  185. });
  186. this.sendToBackend(this.sendContent, this.questionList.length - 1);
  187. this.sendContent = "";
  188. }
  189. },
  190. voice() {
  191. console.log("---this.spdisabled-", this.spdisabled);
  192. if (!this.spdisabled) {
  193. // if (!recognition) this.initSpeech();
  194. // recognition.start(); // 开始语音识别
  195. this.init();
  196. } else {
  197. this.stop();
  198. }
  199. },
  200. sendToBackend(msg, mindex) {
  201. this.questionList[mindex].mstatus = 0;
  202. axios({
  203. method: "POST",
  204. url: `${window.aiURI}/msg`,
  205. data: JSON.stringify({ msg }),
  206. headers: {
  207. "Content-Type": "application/json",
  208. },
  209. })
  210. .then((mres) => {
  211. if (mres.status == 200) {
  212. this.questionList[mindex].mstatus = 2;
  213. if (mres.data.type == "selectLand") {
  214. this.questionList.push({ content: "分析中...请稍等" });
  215. const loading = this.$loading({
  216. lock: true,
  217. text: "分析中",
  218. spinner: "el-icon-loading",
  219. background: "rgba(0, 0, 0, 0.7)",
  220. });
  221. AddFzxz(mres.data.data)
  222. .then((res) => {
  223. loading.close();
  224. this.questionList.push({
  225. type: mres.data.type,
  226. content: "查看智能选址结果",
  227. data: res.data,
  228. });
  229. this.$message({
  230. message: res.message,
  231. type: res.success ? "success" : "warning",
  232. });
  233. })
  234. .catch((error) => {
  235. loading.close();
  236. this.$message.error(error);
  237. });
  238. }
  239. } else {
  240. this.questionList[mindex].mstatus = 1;
  241. // this.$message.error("发送到后台时发生错误:");
  242. }
  243. })
  244. .catch(() => {
  245. this.questionList[mindex].mstatus = 1;
  246. // this.$message.error("发送到后台时发生错误:");
  247. });
  248. },
  249. onClick(item) {
  250. if (item.type == "selectLand") {
  251. this.$router.push({
  252. path: "/siteselection",
  253. query: { rwbsm: item.data.rwbsm },
  254. });
  255. this.$emit("close");
  256. }
  257. },
  258. },
  259. };
  260. </script>
  261. <style lang="less" scoped>
  262. .flex {
  263. display: flex;
  264. }
  265. //智能咨询
  266. .counsel-container {
  267. padding: 15px;
  268. width: 100%;
  269. height: 100%;
  270. position: absolute;
  271. background: #222;
  272. .counsel-header {
  273. display: flex;
  274. align-items: center;
  275. padding: 20px;
  276. font-size: 16px;
  277. color: #fff;
  278. border-bottom: 1px solid #1f4099;
  279. }
  280. .time {
  281. display: block;
  282. text-align: center;
  283. margin-bottom: 20px;
  284. color: #999999;
  285. line-height: 40px;
  286. }
  287. .counsel-chats {
  288. height: calc(100% - 150px);
  289. overflow-x: hidden;
  290. overflow-y: auto;
  291. .header-img {
  292. // flex: 0 0 50px;
  293. margin-left: 10px;
  294. width: 50px;
  295. height: 50px;
  296. border-radius: 50px;
  297. display: block;
  298. overflow: hidden;
  299. img {
  300. width: 50px;
  301. height: 50px;
  302. border-radius: 50px;
  303. object-fit: cover;
  304. }
  305. }
  306. .high {
  307. color: #b6e0ff !important;
  308. cursor: pointer;
  309. display: block;
  310. }
  311. .cursor {
  312. cursor: pointer;
  313. }
  314. .content {
  315. // flex: 1;
  316. margin-left: 10px;
  317. max-width: calc(100% - 100px);
  318. padding: 5px 20px;
  319. border-radius: 8px;
  320. background: #40a0ff6c;
  321. .tit {
  322. display: block;
  323. color: #f99f2b;
  324. margin-bottom: 10px;
  325. }
  326. span {
  327. color: #ffffff;
  328. line-height: 20px;
  329. }
  330. }
  331. .counsel-left {
  332. margin-bottom: 30px;
  333. align-items: flex-start;
  334. .content {
  335. background: #b6e1ff3b;
  336. }
  337. }
  338. .counsel-right {
  339. margin-bottom: 30px;
  340. justify-content: flex-end;
  341. align-items: flex-start;
  342. width: 100%;
  343. i {
  344. font-size: 20px;
  345. }
  346. }
  347. }
  348. audio {
  349. height: 40px;
  350. }
  351. audio::-webkit-media-controls-enclosure {
  352. background-color: #b6e1ff3b;
  353. color: #fff;
  354. }
  355. }
  356. .voice {
  357. width: 75%;
  358. // height: 100px;
  359. position: absolute;
  360. left: 15%;
  361. bottom: 92px;
  362. text-align: center;
  363. border-radius: 8px;
  364. background: #40a0ff6c;
  365. padding: 5px 20px;
  366. .noise {
  367. width: 110px;
  368. height: 40px;
  369. background-image: url("/static/images/aiModel/noise.png");
  370. background-size: 100% 100%;
  371. margin: 10px auto;
  372. }
  373. }
  374. .searchBtom {
  375. position: absolute;
  376. bottom: 20px;
  377. width: calc(100% - 20px);
  378. height: 70px;
  379. background: #44444e !important;
  380. border: 1px solid rgba(0, 0, 0, 0.12);
  381. box-shadow: #0003 0 0 10px;
  382. border-radius: 8px;
  383. display: flex;
  384. padding-top: 15px;
  385. .search {
  386. width: calc(100% - 120px);
  387. margin: 0 10px;
  388. }
  389. .icon {
  390. width: 40px;
  391. height: 40px;
  392. font-size: 40px;
  393. cursor: pointer;
  394. }
  395. .no {
  396. color: #2f2f39;
  397. }
  398. .ok {
  399. color: #0f7ac8;
  400. }
  401. }
  402. </style>