taskArea.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911
  1. <template>
  2. <div class="task">
  3. <div class="taskTop" v-if="false">
  4. <div class="tt_title">任务时间分配</div>
  5. <div class="tt_item">
  6. <div class="tt_i_title">
  7. <span>课程时间轴</span>
  8. </div>
  9. <div class="tt_i_box">
  10. <img :src="require('../../../assets/icon/course/group.png')" />
  11. </div>
  12. </div>
  13. <div class="tt_item">
  14. <div class="tt_i_title">
  15. <span>任务状态</span>
  16. <span>学生活动中...</span>
  17. </div>
  18. <div class="tt_i_box">
  19. <div class="tt_i_b_item">
  20. <div class="tt_i_b_title">作业提交</div>
  21. <div class="tt_i_b_box">
  22. <div class="tt_i_b_b_item">
  23. <div>提交人数</div>
  24. <span>34<span>人</span></span>
  25. </div>
  26. <div class="tt_i_b_b_item">
  27. <div>正确率</div>
  28. <span>88<span>%</span></span>
  29. </div>
  30. </div>
  31. </div>
  32. <div class="tt_i_b_item">
  33. <div class="tt_i_b_title">交流互动</div>
  34. <div class="tt_i_b_box">
  35. <div class="tt_i_b_b_item">
  36. <div>参与人数</div>
  37. <span>56<span>人</span></span>
  38. </div>
  39. <div class="tt_i_b_b_item">
  40. <div>点赞数</div>
  41. <span>36<span>个</span></span>
  42. </div>
  43. <div class="tt_i_b_b_item">
  44. <div>评论数</div>
  45. <span>128<span>条</span></span>
  46. </div>
  47. </div>
  48. </div>
  49. </div>
  50. </div>
  51. </div>
  52. <div class="t_top" ref="chatRef">
  53. <div class="t_t_chat" v-for="(item, index) in chatList" :key="index">
  54. <div class="t_t_c_user" v-if="item.content">
  55. <div class="t_t_c_u_left">
  56. <div class="t_t_c_u_l_content">{{ item.content }}</div>
  57. <div class="t_t_c_u_l_time">{{ item.createtime }}</div>
  58. </div>
  59. <div class="t_t_c_u_right">
  60. <span>我</span>
  61. </div>
  62. </div>
  63. <div class="t_t_c_ai">
  64. <div class="t_t_c_a_left">
  65. <el-avatar v-if="item.filename" :src="item.filename"></el-avatar>
  66. <span v-else>Ai</span>
  67. </div>
  68. <div class="t_t_c_a_right">
  69. <div class="t_t_c_a_r_content" style="display: flex;justify-content: space-between;flex-wrap: wrap;" v-if="item.content.includes('图片')">
  70. <div v-for="i in pan(item.aiContent)" :key="i.image">
  71. <img style="width: 120px;height: 120px;" :src="i.image" alt="" @click="previewImg(i.image)">
  72. </div>
  73. </div>
  74. <div
  75. v-else
  76. class="t_t_c_a_r_content"
  77. v-loading="item.loading"
  78. v-html="item.aiContent"
  79. ></div>
  80. <div class="t_t_c_a_r_time">{{ item.createtime }}</div>
  81. </div>
  82. </div>
  83. </div>
  84. </div>
  85. <div class="t_bottom">
  86. <div class="t_b_btnArea">
  87. <!-- <div class="s_b_ba-item" @click.stop="choiceRole()">选择角色</div> -->
  88. <div class="t_b_ba_item" @click="sendType('智能总结')">智能总结</div>
  89. <div class="t_b_ba_item" @click="sendType('智能出题')">智能出题</div>
  90. <!-- <div class="t_b_ba_item" @click="sendType('优秀标兵')">优秀标兵</div> -->
  91. <div class="t_b_ba_item" @click="sendType('扩展知识')">扩展知识</div>
  92. <div class="t_b_ba_item" @click="clear()">清空聊天记录</div>
  93. </div>
  94. <div class="t_b_inputArea">
  95. <!-- <div class="t_b_tape" @click="goTape()"></div> -->
  96. <div class="t_b_input">
  97. <el-input
  98. :disabled="loading || chatLoading"
  99. v-loading="loading || chatLoading"
  100. @keyup.enter.native="send()"
  101. class="t_b_i_left"
  102. v-model="text"
  103. ></el-input>
  104. <!-- <div class="t_b_i_right" @click="sendFile()">
  105. <span></span>
  106. </div> -->
  107. </div>
  108. <div class="t_b_btn" @click="send()">
  109. <span></span>
  110. </div>
  111. </div>
  112. </div>
  113. </div>
  114. </template>
  115. <script>
  116. // import { Loading } from 'element-ui';
  117. import { v4 as uuidv4 } from "uuid";
  118. import MarkdownIt from "markdown-it";
  119. export default {
  120. props: {
  121. courseDetail: {
  122. type: Object,
  123. default: () => {}
  124. }
  125. },
  126. data() {
  127. return {
  128. userid: this.$route.query.userid,
  129. courseId: this.$route.query.courseId,
  130. text: "",
  131. loading: false,
  132. chatLoading: false,
  133. chatList: []
  134. };
  135. },
  136. computed: {
  137. pan() {
  138. return (content) => {
  139. try {
  140. return JSON.parse(content)
  141. } catch (error) {
  142. return []
  143. }
  144. };
  145. }
  146. },
  147. methods: {
  148. previewImg(url) {
  149. this.$hevueImgPreview(url);
  150. },
  151. clear(){
  152. this.chatList = [];
  153. },
  154. // 获取ai对话
  155. getAiContent(_uid) {
  156. let _source = new EventSource(`https://gpt4.cocorobo.cn/stream/${_uid}`); //http://gpt4.cocorobo.cn:8011/stream/ https://gpt4.cocorobo.cn/stream/
  157. let _allText = "";
  158. let _mdText = "";
  159. const md = new MarkdownIt();
  160. _source.onmessage = _e => {
  161. if (_e.data.replace("'", "").replace("'", "") == "[DONE]") {
  162. //对话已经完成
  163. _mdText = _mdText.replace("_", "");
  164. _source.close();
  165. this.chatLoading = false;
  166. this.scrollBottom();
  167. this.chatList.find(i => i.uid == _uid).aiContent = _mdText;
  168. this.chatList.find(i => i.uid == _uid).isalltext = true;
  169. this.chatList.find(i => i.uid == _uid).isShowSynchronization = true;
  170. this.chatList.find(i => i.uid == _uid).loading = false;
  171. // 这里保存对话
  172. this.insertChat(_uid);
  173. return;
  174. } else {
  175. //对话还在继续
  176. let _text = "";
  177. _text = _e.data.replaceAll("'", "");
  178. if (_allText == "") {
  179. _allText = _text.replace(/^\n+/, ""); //去掉回复消息中偶尔开头就存在的连续换行符
  180. } else {
  181. _allText += _text;
  182. }
  183. _mdText = _allText + "_";
  184. _mdText = _mdText.replace(/\\n/g, "\n");
  185. _mdText = _mdText.replace(/\\/g, "");
  186. if (_allText.split("```").length % 2 == 0) _mdText += "\n```\n";
  187. //转化返回的回复流数据
  188. _mdText = md.render(_mdText);
  189. this.chatList.find(i => i.uid == _uid).aiContent = _mdText;
  190. this.chatList.find(i => i.uid == _uid).loading = false;
  191. this.scrollBottom();
  192. // 处理流数据
  193. }
  194. };
  195. },
  196. //保存消息
  197. insertChat(_uid) {
  198. let _data = this.chatList.find(i => i.uid == _uid);
  199. if (!_data) return;
  200. let params = {
  201. userId: this.userid,
  202. userName: "qgt",
  203. groupId: "602def61-005d-11ee-91d8-005056b8q12w",
  204. answer: _data.aiContent,
  205. problem: _data.content,
  206. file_id: _data.fileid ? _data.fileid : "",
  207. alltext: _data.aiContent,
  208. type: "chat",
  209. filename: _data.filename,
  210. session_name: `${this.courseId}-studyStudent` //这是对话记录位置
  211. };
  212. this.ajax
  213. .post("https://gpt4.cocorobo.cn/insert_chat", params)
  214. .then(res => {});
  215. },
  216. // 获取对应的聊天记录
  217. getChatList() {
  218. return new Promise((resolve, reject) => {
  219. if (this.loading) return this.$message.info("请稍等...");
  220. this.chatList = [];
  221. this.loading = true;
  222. this.chatLoading = true;
  223. let params = {
  224. userid: this.userid,
  225. groupid: "602def61-005d-11ee-91d8-005056b8q12w",
  226. // session_name:``
  227. session_name: `${this.courseId}-studyStudent`
  228. };
  229. this.ajax
  230. .post("https://gpt4.cocorobo.cn/get_agent_park_chat", params)
  231. .then(res => {
  232. let _data = JSON.parse(res.data.FunctionResponse);
  233. if (_data.length > 0) {
  234. let _chatList = [];
  235. for (let i = 0; i < _data.length; i++) {
  236. _chatList.push({
  237. loading: false,
  238. role: "user",
  239. content: _data[i].problem,
  240. uid: _data[i].id,
  241. AI: "AI",
  242. aiContent: _data[i].answer,
  243. oldContent: _data[i].answer,
  244. isShowSynchronization: false,
  245. filename: _data[i].filename,
  246. index: i,
  247. is_mind_map: false,
  248. fileid: _data[i].fileid
  249. });
  250. }
  251. this.chatList = _chatList;
  252. this.loading = false;
  253. this.chatLoading = false;
  254. } else {
  255. //没有对话记录
  256. this.loading = false;
  257. this.chatLoading = false;
  258. }
  259. resolve();
  260. })
  261. .catch(err => {
  262. console.log(err);
  263. this.$message.error("获取对话记录失败");
  264. this.loading = false;
  265. this.chatLoading = false;
  266. resolve();
  267. });
  268. });
  269. },
  270. choiceRole() {
  271. this.cardType = 1;
  272. },
  273. sendFile() {
  274. if (this.loading) return this.$message.info("请稍等");
  275. this.$message.info("点击了附件");
  276. },
  277. goTape() {
  278. if (this.loading) return this.$message.info("请稍等");
  279. this.$message.info(`点击了语音`);
  280. },
  281. send(_text = this.text) {
  282. if (this.loading || this.chatLoading) return this.$message.info("请稍等");
  283. if (_text.trim().length == 0) return this.$message.info("请输入内容");
  284. this.chatLoading = true;
  285. let _uuid = uuidv4();
  286. this.chatList.push({
  287. role: "user",
  288. content: `${_text}`,
  289. uid: _uuid,
  290. AI: "AI",
  291. aiContent: "",
  292. oldContent: "",
  293. isShowSynchronization: false,
  294. filename: "",
  295. index: this.chatList.length,
  296. is_mind_map: false,
  297. loading: true
  298. });
  299. this.scrollBottom();
  300. let params = {
  301. model: "gpt-3.5-turbo",
  302. temperature: 0,
  303. max_tokens: 4096,
  304. top_p: 1,
  305. frequency_penalty: 0,
  306. presence_penalty: 0,
  307. messages: [{ role: "user", content: _text }],
  308. uid: _uuid,
  309. mind_map_question: ""
  310. };
  311. this.text = "";
  312. this.ajax
  313. .post("https://gpt4.cocorobo.cn/chat", params)
  314. .then(res => {
  315. if (res.data.FunctionResponse.result == "发送成功") {
  316. } else {
  317. this.$message.warning(res.data.FunctionResponse.result);
  318. this.chatLoading = false;
  319. }
  320. })
  321. .catch(e => {
  322. console.log(e);
  323. this.chatLoading = false;
  324. });
  325. this.getAiContent(_uuid);
  326. },
  327. sendType(_text) {
  328. if (this.loading || this.chatLoading) return this.$message.info("请稍等");
  329. let _msg = ``;
  330. if (_text == "扩展知识") {
  331. // return console.log(this.courseDetail);
  332. _msg = `
  333. NOTICE
  334. Language: Please use the same language as the user requirement, if the user speaks Chinese, the specific text of your answer should also be in Chinese.
  335. ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example".
  336. Instruction: Based on the context, follow "Format example", write content.
  337. # Context
  338. ## 任务
  339. 你的任务是根据用户的请求,结合以下“课程信息”包含的子条目(“课程标题”,“主题”,“学科”以及“年级”),向用户输出相关的扩展知识点,将结果以有序列表的形式返回给用户。
  340. 课程信息
  341. 课程标题:${this.courseDetail.title}
  342. 分类:${this.courseDetail.name?this.courseDetail.name:"无"}
  343. 学生年级:${this.courseDetail.classname?this.courseDetail.classname:"无"}
  344. ## 规则
  345. 1. 内容应该与“课程信息”相关,避免提供无关的信息。
  346. 2. 当课程信息中的子条目内容为“无”时,无视这些条目进行即可。
  347. 3. 搜索建议的结果应该符合伦理规范。
  348. 4. 一步步思考如何根据“课程信息”来输出“扩展知识点”,但是你不需要“扩展知识点”以外的内容。
  349. ## 格式要求
  350. 1. 应包括5个相关的“扩展知识点”。
  351. 2. 搜索建议应以有序列表形式呈现,每个建议包括关键词和简短描述。
  352. # Format example
  353. ## 课程标题:垃圾回收
  354. 主题:无
  355. 学科:无
  356. 年级:无
  357. 1. 垃圾分类标准:不同国家的垃圾分类标准和方法。
  358. 2. 可回收垃圾处理:可回收垃圾的处理流程和再利用方法。
  359. 3. 有害垃圾的影响:有害垃圾对环境和人体健康的潜在影响。
  360. 4. 垃圾减量化策略:减少垃圾产生的有效策略和实践。
  361. 5. 环保教育活动:在学校中推广环保意识的活动和资源。
  362. `;
  363. } else if (_text == "智能出题") {
  364. _msg = `
  365. NOTICE
  366. Language: Please use the same language as the user requirement, if the user speaks Chinese, the specific text of your answer should also be in Chinese.
  367. ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example".
  368. Instruction: Based on the context, follow "Format example", write content.
  369. # Context
  370. ## 任务
  371. 你的任务是根据用户的请求,结合以下“课程信息”包含的子条目(“课程标题”,“主题”,“学科”以及“年级”),向用户输出相关的练习题目,将结果以有序列表的形式返回给用户。
  372. 课程信息
  373. 课程标题:${this.courseDetail.title}
  374. 分类:${this.courseDetail.name?this.courseDetail.name:"无"}
  375. 学生年级:${this.courseDetail.classname?this.courseDetail.classname:"无"}
  376. ## 规则
  377. 1. 练习题目内容应该与“课程信息”相关,避免提供无关的信息。
  378. 2. 当课程信息中的子条目内容为“无”时,无视这些条目进行输出即可。
  379. 3. 一步步思考如何根据“课程信息”输出“练习题目”,但是你不需要输出“练习题目”以外的内容。
  380. 4.输出练习题目后,提示用户“希望这些题目能帮到你,如果需要其他类型题目或者更多题目,可以随时向我输入你的需求”
  381. ## 格式要求
  382. 1. 输出应包括3道相关的“练习题目”。
  383. 2. 练习题目默认只包含3道4选1的单选题,除非用户在提示词中更具体地指明了题目类型和数量。
  384. 3. 练习题目应以有序列表形式呈现,每个建议包括关键词和简短描述。
  385. # Format example
  386. ## 课程标题:垃圾回收
  387. 主题:无
  388. 学科:无
  389. 年级:无
  390. 1. 垃圾回收的主要目的是: a) 增加垃圾的数量 b) 减少垃圾的数量 c) 提高垃圾的质量 d) 增加垃圾的种类
  391. 2. 以下哪种垃圾是可以回收的? a) 食物残渣 b) 塑料瓶 c) 湿纸巾 d) 动物粪便
  392. 3. 垃圾分类中,玻璃瓶通常属于哪一类? a) 可回收垃圾 b) 厨余垃圾 c) 有害垃圾 d) 其他垃圾
  393. `;
  394. } else if (_text == "智能总结") {
  395. _msg = `
  396. NOTICE
  397. Language: Please use the same language as the user requirement, if the user speaks Chinese, the specific text of your answer should also be in Chinese.
  398. ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example".
  399. Instruction: Based on the context, follow "Format example", write content.
  400. # Context
  401. ## 任务
  402. 你的任务是根据用户的请求,结合以下“课程信息”包含的子条目(“课程标题”,“主题”,“学科”,“年级”以及“学习内容”),对课程进行课程总结,请根据输出格式将内容输出给用户。
  403. 课程信息
  404. 课程标题:${this.courseDetail.title}
  405. 分类:${this.courseDetail.name?this.courseDetail.name:"无"}
  406. 学生年级:${this.courseDetail.classname?this.courseDetail.classname:"无"}
  407. ## 规则
  408. 1. “课程总结”内容应该与“课程信息”相关,避免提供无关的信息。
  409. 2. 当课程信息中的子条目内容为“无”时,无视这些条目进行输出即可。
  410. 3. 一步步思考如何根据“课程信息”输出“课程总结”。
  411. ## 格式要求
  412. 1. 以无序列表的形式输出“课程信息”中的“课程标题”,“主题”,“学科”以及“年级”,若这些条目中的内容为“无”,则不需要输出
  413. -----
  414. # Format example
  415. ## 课程标题:垃圾回收
  416. 主题:无
  417. 学科:无
  418. 年级:无
  419. 课程信息
  420. 课程标题:垃圾回收
  421. 学科:自然科学 语文 数学
  422. 学习内容:学习内容包含一份引导学生思考为何要进行垃圾回收的文档材料
  423. `;
  424. }
  425. if (!_msg) return;
  426. // console.log(_msg)
  427. this.chatLoading = true;
  428. let _uuid = uuidv4();
  429. this.chatList.push({
  430. role: "user",
  431. content: `${_text}`,
  432. uid: _uuid,
  433. AI: "AI",
  434. aiContent: "",
  435. oldContent: "",
  436. isShowSynchronization: false,
  437. filename: "",
  438. index: this.chatList.length,
  439. is_mind_map: false,
  440. loading: true
  441. });
  442. this.scrollBottom();
  443. let params = {
  444. model: "gpt-3.5-turbo",
  445. temperature: 0,
  446. max_tokens: 4096,
  447. top_p: 1,
  448. frequency_penalty: 0,
  449. presence_penalty: 0,
  450. messages: [{ role: "user", content: _msg }],
  451. uid: _uuid,
  452. mind_map_question: ""
  453. };
  454. this.text = "";
  455. this.ajax
  456. .post("https://gpt4.cocorobo.cn/chat", params)
  457. .then(res => {
  458. if (res.data.FunctionResponse.result == "发送成功") {
  459. } else {
  460. this.$message.warning(res.data.FunctionResponse.result);
  461. this.chatLoading = false;
  462. }
  463. })
  464. .catch(e => {
  465. console.log(e);
  466. this.chatLoading = false;
  467. });
  468. this.getAiContent(_uuid);
  469. // this.send(text);
  470. },
  471. sendImage() {
  472. this.$message.info("发送图片");
  473. },
  474. sendVideo() {
  475. this.$message.info("发送视频");
  476. },
  477. scrollBottom() {
  478. this.$nextTick(() => {
  479. this.$refs.chatRef.scrollTop = this.$refs.chatRef.scrollHeight;
  480. });
  481. }
  482. },
  483. mounted() {
  484. this.getChatList().then(_ => {
  485. this.scrollBottom();
  486. });
  487. }
  488. };
  489. </script>
  490. <style scoped>
  491. .task {
  492. width: 100%;
  493. height: 100%;
  494. box-sizing: border-box;
  495. }
  496. .taskTop {
  497. width: 100%;
  498. height: 280px;
  499. box-sizing: border-box;
  500. padding: 10px;
  501. overflow-x: hidden;
  502. }
  503. .tt_title {
  504. font-size: 18px;
  505. font-weight: bold;
  506. margin-bottom: 5px;
  507. }
  508. .tt_item {
  509. width: 100%;
  510. height: auto;
  511. margin-top: 10px;
  512. }
  513. .tt_i_title {
  514. display: flex;
  515. justify-content: space-between;
  516. align-items: center;
  517. }
  518. .tt_i_title > span:nth-of-type(2) {
  519. box-sizing: border-box;
  520. padding: 0 10px;
  521. height: 25px;
  522. background-color: #effcf5;
  523. color: #83e0af;
  524. }
  525. .tt_i_box {
  526. width: 100%;
  527. height: auto;
  528. display: flex;
  529. margin-top: 10px;
  530. justify-content: space-between;
  531. }
  532. .tt_i_box > img {
  533. width: 100%;
  534. height: 50px;
  535. }
  536. .tt_i_b_item {
  537. width: auto;
  538. height: 90px;
  539. background-color: #f6f9ff;
  540. }
  541. .tt_i_b_title {
  542. width: 100%;
  543. height: 30px;
  544. display: flex;
  545. justify-content: center;
  546. align-items: center;
  547. box-sizing: border-box;
  548. border: solid 1px #e0eafb;
  549. color: #626466;
  550. }
  551. .tt_i_b_box {
  552. width: auto;
  553. height: 60px;
  554. box-sizing: border-box;
  555. border: solid 1px #e0eafb;
  556. border-top: none;
  557. display: flex;
  558. }
  559. .tt_i_b_b_item {
  560. width: 70px;
  561. height: 100%;
  562. display: flex;
  563. flex-direction: column;
  564. justify-content: center;
  565. align-items: center;
  566. border-left: solid 1px #e0eafb;
  567. }
  568. .tt_i_b_box > .tt_i_b_b_item:nth-of-type(1) {
  569. border-left: none;
  570. }
  571. .tt_i_b_b_item > div {
  572. font-size: 14px;
  573. color: #949599;
  574. }
  575. .tt_i_b_b_item > span {
  576. font-weight: bold;
  577. }
  578. .tt_i_b_b_item > span > span {
  579. font-size: 12px;
  580. font-weight: 200;
  581. }
  582. .t_top {
  583. width: 100%;
  584. height: calc(100% - 90px);
  585. /* height: calc(100% - 370px); */
  586. overflow: auto;
  587. box-sizing: border-box;
  588. padding: 20px 0;
  589. }
  590. .t_t_chat {
  591. width: 100%;
  592. display: flex;
  593. box-sizing: border-box;
  594. padding: 10px;
  595. flex-direction: column;
  596. }
  597. .t_t_chat > div {
  598. display: flex;
  599. align-items: flex-start;
  600. width: 100%;
  601. }
  602. .t_t_c_user {
  603. box-sizing: border-box;
  604. padding-left: 35px;
  605. }
  606. .t_t_c_u_left {
  607. width: 90%;
  608. height: auto;
  609. }
  610. .t_t_c_u_l_content {
  611. width: auto;
  612. max-width: 100%;
  613. height: auto;
  614. box-sizing: border-box;
  615. padding: 10px;
  616. color: white;
  617. background-color: #3681fc;
  618. border-radius: 8px 2px 8px 8px;
  619. white-space: pre-line;
  620. }
  621. .t_t_c_u_l_time {
  622. width: 100%;
  623. display: flex;
  624. justify-content: flex-end;
  625. font-size: 12px;
  626. color: #9f9f9f;
  627. margin-top: 5px;
  628. }
  629. .t_t_c_u_right {
  630. width: 35px;
  631. height: 35px;
  632. display: flex;
  633. justify-content: center;
  634. margin-left: 5px;
  635. }
  636. .t_t_c_u_right > span {
  637. width: 32px;
  638. height: 32px;
  639. display: flex;
  640. justify-content: center;
  641. align-items: center;
  642. color: white;
  643. background-color: #3681fc;
  644. border-radius: 50%;
  645. }
  646. .t_t_c_ai {
  647. margin-top: 10px;
  648. box-sizing: border-box;
  649. padding-right: 35px;
  650. }
  651. .t_t_c_a_right {
  652. min-width: 90%;
  653. height: auto;
  654. }
  655. .t_t_c_a_r_content {
  656. width: auto;
  657. max-width: 100%;
  658. height: auto;
  659. box-sizing: border-box;
  660. padding: 10px;
  661. background-color: #f6f8ff;
  662. border-radius: 2px 8px 8px 8px;
  663. white-space: pre-line;
  664. word-break: break-all;
  665. }
  666. .t_t_c_a_r_content2 {
  667. background-color: #f6f8ff;
  668. width: 100%;
  669. height: auto;
  670. box-sizing: border-box;
  671. padding: 10px;
  672. border-radius: 2px 8px 8px 8px;
  673. white-space: pre-line;
  674. box-shadow: 0 0px 10px #c5cbee;
  675. }
  676. .t_t_c_a_r_c_title {
  677. display: flex;
  678. align-items: center;
  679. }
  680. .t_t_c_a_r_c_title > img {
  681. width: 16px;
  682. height: 16px;
  683. }
  684. .t_t_c_a_r_c_item {
  685. width: 100%;
  686. height: auto;
  687. box-sizing: border-box;
  688. padding: 10px;
  689. background-color: #ffffff;
  690. border-radius: 5px;
  691. margin-top: 5px;
  692. color: #666666;
  693. font-size: 14px;
  694. cursor: pointer;
  695. }
  696. .t_t_c_a_r_c_item:hover {
  697. border: solid #b8d2fe 1px;
  698. box-shadow: 0 0 5px 2px #b8d2fe;
  699. }
  700. .t_t_c_a_r_c_title > span {
  701. font-weight: bold;
  702. }
  703. .t_t_c_a_r_time {
  704. width: 100%;
  705. display: flex;
  706. justify-content: flex-start;
  707. font-size: 12px;
  708. color: #9f9f9f;
  709. margin-top: 5px;
  710. }
  711. .t_t_c_a_left {
  712. width: 35px;
  713. height: 35px;
  714. display: flex;
  715. justify-content: center;
  716. margin-right: 5px;
  717. }
  718. .t_t_c_a_left > span {
  719. width: 32px;
  720. height: 32px;
  721. display: flex;
  722. justify-content: center;
  723. align-items: center;
  724. color: white;
  725. background-color: #3681fc;
  726. border-radius: 50%;
  727. }
  728. .t_bottom {
  729. width: 100%;
  730. height: 90px;
  731. display: flex;
  732. flex-direction: column;
  733. justify-content: space-between;
  734. }
  735. .t_b_btnArea {
  736. width: 100%;
  737. height: 30px;
  738. display: flex;
  739. align-items: center;
  740. box-sizing: border-box;
  741. padding: 0 10px;
  742. overflow-y: hidden;
  743. }
  744. .t_b_ba_item {
  745. width: auto;
  746. box-sizing: border-box;
  747. padding: 0 10px;
  748. height: 25px;
  749. background-color: white;
  750. display: flex;
  751. justify-content: center;
  752. align-items: center;
  753. /* 阴影 */
  754. box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.363);
  755. border-radius: 15px;
  756. font-size: 14px;
  757. cursor: pointer;
  758. margin-right: 10px;
  759. white-space: nowrap;
  760. transition: 0.3s;
  761. }
  762. .t_b_ba_item:hover {
  763. background-color: rgb(236, 236, 236);
  764. }
  765. .t_b_inputArea {
  766. width: 100%;
  767. height: 55px;
  768. box-sizing: border-box;
  769. border-top: solid 1px #ededed;
  770. display: flex;
  771. justify-content: space-between;
  772. align-items: center;
  773. padding-right: 10px;
  774. }
  775. .t_b_tape {
  776. width: 35px;
  777. height: 35px;
  778. background: url("../../../assets/icon/course/tape.png") no-repeat;
  779. background-size: 50% 60%;
  780. background-position: center;
  781. cursor: pointer;
  782. }
  783. .t_b_input {
  784. /* width: 75%; */
  785. flex: 1;
  786. height: 45px;
  787. background-color: #f3f3f3;
  788. border-radius: 50px;
  789. margin: 0 10px;
  790. display: flex;
  791. overflow: hidden;
  792. align-items: center;
  793. }
  794. .t_b_i_left {
  795. /* width: calc(100% - 45px); */
  796. width: 100%;
  797. line-height: 45px;
  798. height: 100%;
  799. }
  800. .t_b_i_left >>> .el-input__inner {
  801. border: none;
  802. background-color: #f3f3f3;
  803. outline: none;
  804. border-radius: 50px 0 0 50px;
  805. }
  806. .t_b_i_right {
  807. width: 45px;
  808. height: 45px;
  809. display: flex;
  810. justify-content: center;
  811. align-items: center;
  812. }
  813. .t_b_i_right > span {
  814. width: 35px;
  815. height: 35px;
  816. background: url("../../../assets/icon/course/file.png") no-repeat;
  817. background-size: 50% 60%;
  818. background-position: center;
  819. cursor: pointer;
  820. }
  821. .t_b_btn {
  822. width: 40px;
  823. height: 40px;
  824. background-color: #3681fc;
  825. display: flex;
  826. justify-content: center;
  827. align-items: center;
  828. border-radius: 50%;
  829. cursor: pointer;
  830. }
  831. .t_b_btn > span {
  832. width: 30px;
  833. height: 30px;
  834. background: url("../../../assets/icon/course/send.png") no-repeat;
  835. background-size: 70% 70%;
  836. background-position: center;
  837. }
  838. td,
  839. th {
  840. padding: 10px;
  841. }
  842. </style>