소스 검색

[Mod 0000]对智能通风助手的部分功能和bug进行优化

wangkeyi 4 일 전
부모
커밋
ac3b111c6b
32개의 변경된 파일1194개의 추가작업 그리고 74개의 파일을 삭제
  1. 0 3
      src/assets/images/ventAI/chatModalBlue/2-10.svg
  2. 0 3
      src/assets/images/ventAI/chatModalBlue/2-11.svg
  3. BIN
      src/assets/images/ventAI/chatModalBlue/2-13.png
  4. BIN
      src/assets/images/ventAI/chatModalBlue/2-15.png
  5. BIN
      src/assets/images/ventAI/chatModalBlue/2-9.png
  6. 3 0
      src/assets/images/ventAI/chatModalBlue/3-1.svg
  7. 6 0
      src/assets/images/ventAI/chatModalBlue/3-10.svg
  8. 3 0
      src/assets/images/ventAI/chatModalBlue/3-11.svg
  9. 3 0
      src/assets/images/ventAI/chatModalBlue/3-12.svg
  10. 3 0
      src/assets/images/ventAI/chatModalBlue/3-2.svg
  11. 1 0
      src/assets/images/ventAI/chatModalBlue/3-3.svg
  12. 1 0
      src/assets/images/ventAI/chatModalBlue/3-4.svg
  13. 1 0
      src/assets/images/ventAI/chatModalBlue/3-5.svg
  14. 1 0
      src/assets/images/ventAI/chatModalBlue/3-6.svg
  15. 3 0
      src/assets/images/ventAI/chatModalBlue/3-9.svg
  16. BIN
      src/assets/images/ventAI/chatModalBlue/fullscreen/1-1.png
  17. BIN
      src/assets/images/ventAI/chatModalBlue/fullscreen/1-2.png
  18. BIN
      src/assets/images/ventAI/chatModalBlue/fullscreen/1-3.png
  19. BIN
      src/assets/images/ventAI/chatModalBlue/fullscreen/1-4.png
  20. BIN
      src/assets/images/ventAI/chatModalBlue/fullscreen/1-5.png
  21. BIN
      src/assets/images/ventAI/chatModalBlue/fullscreen/1-6.png
  22. BIN
      src/assets/images/ventAI/chatModalBlue/fullscreen/1-7.png
  23. BIN
      src/assets/images/ventAI/chatModalBlue/fullscreen/1-8.png
  24. 36 0
      src/assets/images/ventAI/chatModalBlue/fullscreen/1-9.svg
  25. 21 4
      src/design/themify/deepblue.less
  26. 10 4
      src/design/themify/default.less
  27. 81 0
      src/views/ventAI/manageAssistent/api.ts
  28. 86 9
      src/views/ventAI/manageAssistent/components/AiAssistantModal.vue
  29. 791 11
      src/views/ventAI/manageAssistent/components/chatModal/ChatInputArea.vue
  30. 12 18
      src/views/ventAI/manageAssistent/components/chatModal/ChatMessages.vue
  31. 108 21
      src/views/ventAI/manageAssistent/components/chatModal/TaskListPanel.vue
  32. 24 1
      src/views/ventAI/manageAssistent/components/chatModal/utils.ts

+ 0 - 3
src/assets/images/ventAI/chatModalBlue/2-10.svg

@@ -1,3 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="16.836" height="18.704" viewBox="0 0 16.836 18.704">
-  <path id="路径_58030" data-name="路径 58030" d="M143.9,102.337a.935.935,0,1,1,0,1.871H128.936a.935.935,0,1,1,0-1.871H143.9ZM136.419,85.5a.935.935,0,0,1,.935.935v11.3l4.48-4.48a.935.935,0,0,1,1.4,1.234l-.079.088-6.081,6.085a.935.935,0,0,1-1.235.079l-.088-.078L129.678,94.6a.935.935,0,0,1,1.233-1.4l.088.078,4.484,4.475V86.439A.935.935,0,0,1,136.419,85.5Z" transform="translate(-128 -85.504)" fill="#1889ed"/>
-</svg>

+ 0 - 3
src/assets/images/ventAI/chatModalBlue/2-11.svg

@@ -1,3 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="18.843" height="18.705" viewBox="0 0 18.843 18.705">
-  <path id="路径_58031" data-name="路径 58031" d="M65.722,76.638H70.4l-5.141,5.139a.974.974,0,0,0,1.378,1.378l5.221-5.221v4.639a.975.975,0,0,0,1.95,0V75.661a.974.974,0,0,0-.975-.974h-7.11a.976.976,0,0,0,0,1.952ZM75.5,73.7h7.111a.974.974,0,0,0,0-1.949H77.939l5.141-5.141A.974.974,0,0,0,81.7,65.234L76.48,70.454V65.817a.975.975,0,1,0-1.95,0v6.911a.974.974,0,0,0,.975.974Z" transform="translate(-64.747 -64.842)" fill="#1889ed"/>
-</svg>

BIN
src/assets/images/ventAI/chatModalBlue/2-13.png


BIN
src/assets/images/ventAI/chatModalBlue/2-15.png


BIN
src/assets/images/ventAI/chatModalBlue/2-9.png


+ 3 - 0
src/assets/images/ventAI/chatModalBlue/3-1.svg

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="7.789" height="11.035" viewBox="0 0 7.789 11.035">
+  <path id="路径_58148" data-name="路径 58148" d="M281.877,153.942v6.426h-1.3v-6.426l-1.363,1.363-.909-.974,2.921-2.921,2.921,2.921-.909.909Zm3.245-3.31h-7.789v-1.3h7.789Z" transform="translate(-277.333 -149.334)" fill="#fff"/>
+</svg>

+ 6 - 0
src/assets/images/ventAI/chatModalBlue/3-10.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="12.214" height="14.196" viewBox="0 0 12.214 14.196">
+  <g id="组_16859" data-name="组 16859" transform="translate(-470.331 -148.095)">
+    <path id="路径_58158" data-name="路径 58158" d="M107.97,34.133l.592.418a9.788,9.788,0,0,0,1.887.978,7.033,7.033,0,0,0,2.147.457l.981.014v.461l0,5.06a4.353,4.353,0,0,1-.607,2.114,7.535,7.535,0,0,1-1.433,1.791,8.152,8.152,0,0,1-1.786,1.278,4.065,4.065,0,0,1-1.78.512,4,4,0,0,1-1.77-.5,8.147,8.147,0,0,1-1.8-1.286,5.675,5.675,0,0,1-2.039-3.9V36l.981-.013a6.782,6.782,0,0,0,2.151-.458,9.667,9.667,0,0,0,1.889-.979Zm0,1.151-.025.02a10.972,10.972,0,0,1-2.088,1.089,7.738,7.738,0,0,1-2.5.524l-.041,0v4.6a4.779,4.779,0,0,0,1.745,3.229,7.115,7.115,0,0,0,1.573,1.127,3.163,3.163,0,0,0,1.335.4,5.108,5.108,0,0,0,2.906-1.531,4.773,4.773,0,0,0,1.744-3.228l0-4.6-.037,0a7.848,7.848,0,0,1-2.5-.524A10.9,10.9,0,0,1,108,35.305l-.027-.02Z" transform="translate(368.465 114.574)" fill="#cdeeff" stroke="rgba(0,0,0,0)" stroke-miterlimit="10" stroke-width="1" opacity="0.5"/>
+    <path id="路径_58159" data-name="路径 58159" d="M64.294,89.988h-.531a.242.242,0,0,0-.191.092L60.44,94.048l-1.5-1.9a.243.243,0,0,0-.191-.092h-.531a.061.061,0,0,0-.048.1l2.081,2.636a.243.243,0,0,0,.382,0l3.71-4.7a.06.06,0,0,0-.048-.1Z" transform="translate(415.182 62.815)" fill="#cdeeff" stroke="rgba(0,0,0,0)" stroke-miterlimit="10" stroke-width="1" opacity="0.5"/>
+  </g>
+</svg>

+ 3 - 0
src/assets/images/ventAI/chatModalBlue/3-11.svg

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="11.228" height="13.501" viewBox="0 0 11.228 13.501">
+  <path id="路径_58160" data-name="路径 58160" d="M135.387,42.667a.568.568,0,0,1,.568.568v.284h1.7a.568.568,0,0,1,.568.568V54.6a.568.568,0,0,1-.568.568h-9.092A.568.568,0,0,1,128,54.6V44.087a.568.568,0,0,1,.568-.568h1.7v-.284a.568.568,0,0,1,.568-.568h4.546Zm-5.114,1.7h-1.421v9.944h8.524V44.371h-1.421v.284a.568.568,0,0,1-.568.568h-4.546a.568.568,0,0,1-.568-.568Zm2.7,5.967a.426.426,0,1,1,0,.852H130.7a.426.426,0,0,1,0-.852Zm2.557-2.557a.426.426,0,0,1,0,.852H130.7a.426.426,0,0,1,0-.852Zm-.426-4.262h-3.978v.852H135.1v-.852Z" transform="translate(-127.5 -42.167)" fill="#cdeeff" stroke="rgba(0,0,0,0)" stroke-miterlimit="10" stroke-width="1" opacity="0.5"/>
+</svg>

+ 3 - 0
src/assets/images/ventAI/chatModalBlue/3-12.svg

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="11.214" height="13.083" viewBox="0 0 11.214 13.083">
+  <path id="路径_58157" data-name="路径 58157" d="M107.97,34.133l.592.418a9.788,9.788,0,0,0,1.887.978,7.033,7.033,0,0,0,2.147.457l.981.014v.461l0,5.06a4.353,4.353,0,0,1-.607,2.114,7.535,7.535,0,0,1-1.433,1.791,8.152,8.152,0,0,1-1.786,1.278,4.065,4.065,0,0,1-1.78.512,4,4,0,0,1-1.77-.5,8.147,8.147,0,0,1-1.8-1.286,5.675,5.675,0,0,1-2.039-3.9V36l.981-.013a6.782,6.782,0,0,0,2.151-.458,9.667,9.667,0,0,0,1.889-.979Zm0,1.151-.025.02a10.972,10.972,0,0,1-2.088,1.089,7.738,7.738,0,0,1-2.5.524l-.041,0v4.6a4.779,4.779,0,0,0,1.745,3.229,7.115,7.115,0,0,0,1.573,1.127,3.163,3.163,0,0,0,1.335.4,5.108,5.108,0,0,0,2.906-1.531,4.773,4.773,0,0,0,1.744-3.228l0-4.6-.037,0a7.848,7.848,0,0,1-2.5-.524A10.9,10.9,0,0,1,108,35.305l-.027-.02Zm0,6.8a.467.467,0,1,1-.467.467.467.467,0,0,1,.467-.467Zm0-4.209a.467.467,0,0,1,.467.467v2.8a.467.467,0,1,1-.935,0v-2.8a.467.467,0,0,1,.467-.467Z" transform="translate(-102.366 -34.133)" fill="#fff"/>
+</svg>

+ 3 - 0
src/assets/images/ventAI/chatModalBlue/3-2.svg

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="10.407" height="11.447" viewBox="0 0 10.407 11.447">
+  <path id="路径_58147" data-name="路径 58147" d="M114.805,65.757h-.52v8.325a1.041,1.041,0,0,1-1.041,1.041H107a1.041,1.041,0,0,1-1.041-1.041V65.757h-.52a.52.52,0,0,1,0-1.041h3.122a1.041,1.041,0,0,1,1.041-1.041h1.041a1.041,1.041,0,0,1,1.041,1.041h3.122a.52.52,0,0,1,0,1.041Zm-1.561,0H107v8.325h6.244ZM109.082,66.8a.52.52,0,0,1,.52.52v5.2a.52.52,0,1,1-1.041,0v-5.2A.52.52,0,0,1,109.082,66.8Zm2.081,0a.52.52,0,0,1,.52.52v5.2a.52.52,0,1,1-1.041,0v-5.2A.52.52,0,0,1,111.163,66.8Z" transform="translate(-104.919 -63.676)" fill="#fff"/>
+</svg>

+ 1 - 0
src/assets/images/ventAI/chatModalBlue/3-3.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1785310549625" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11695" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M924.672 126.976q36.864 36.864 54.784 82.432t17.92 93.696-17.92 93.696-54.784 82.432l-392.192 389.12q-36.864 36.864-90.624 61.44t-113.664 28.672-122.368-16.384-115.712-73.728q-52.224-52.224-72.704-113.152t-16.384-121.344 28.16-113.664 60.928-90.112l348.16-345.088q9.216-9.216 27.136-4.608t27.136 13.824q8.192 9.216 13.312 27.136t-4.096 27.136l-347.136 344.064q-27.648 27.648-46.08 64.512t-21.504 78.848 12.288 84.992 55.296 82.944q35.84 35.84 79.36 50.688t86.528 12.288 81.92-18.944 66.56-44.032l391.168-388.096q27.648-27.648 39.424-57.344t11.264-58.88-13.824-56.832-36.864-51.2q-44.032-43.008-98.816-40.448t-110.08 57.856l-353.28 351.232q-23.552 23.552-23.04 52.224t18.944 47.104q22.528 22.528 51.712 18.432t47.616-22.528l320.512-318.464q9.216-9.216 27.136-4.608t27.136 13.824 14.336 27.136-4.096 27.136l-321.536 318.464q-36.864 36.864-70.656 51.2t-63.488 12.8-55.296-15.872-47.104-34.816q-17.408-16.384-31.232-41.984t-15.872-56.32 10.752-65.536 49.664-70.656q18.432-18.432 32.768-33.792 12.288-13.312 23.04-23.552t11.776-11.264l285.696-284.672q36.864-36.864 80.384-57.856t88.576-24.064 88.576 12.288 80.384 52.224z" p-id="11696" fill="#ffffff"></path></svg>

+ 1 - 0
src/assets/images/ventAI/chatModalBlue/3-4.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1785309784811" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7437" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 938.652444c-235.264 0-426.666667-191.402667-426.666667-426.666666s191.402667-426.666667 426.666667-426.666667 426.666667 191.402667 426.666667 426.666667c0 55.552-10.538667 109.639111-31.317334 160.739555a42.709333 42.709333 0 0 1-55.608889 23.438223 42.695111 42.695111 0 0 1-23.452444-55.608889A339.256889 339.256889 0 0 0 853.333333 511.985778c0-188.202667-153.130667-341.333333-341.333333-341.333334s-341.333333 153.130667-341.333333 341.333334 153.130667 341.333333 341.333333 341.333333c53.333333 0 104.391111-11.946667 151.736889-35.484444a42.666667 42.666667 0 0 1 38.001778 76.416A422.684444 422.684444 0 0 1 512 938.652444" fill="#ffffff" p-id="7438"></path><path d="M512 369.763556c-78.421333 0-142.222222 63.800889-142.222222 142.222222s63.800889 142.222222 142.222222 142.222222a142.222222 142.222222 0 0 0 142.222222-142.222222c0-78.421333-63.800889-142.222222-142.222222-142.222222m0 369.777777c-125.468444 0-227.555556-102.087111-227.555556-227.555555s102.087111-227.555556 227.555556-227.555556 227.555556 102.087111 227.555556 227.555556a227.498667 227.498667 0 0 1-227.555556 227.555555" fill="#ffffff" p-id="7439"></path><path d="M765.368889 782.791111c-7.822222 0-15.431111-0.768-22.755556-2.275555C689.749333 769.592889 654.222222 722.488889 654.222222 663.324444V327.096889a42.666667 42.666667 0 1 1 85.333334 0v336.213333c0 10.993778 2.631111 29.980444 20.337777 33.649778 19.569778 3.982222 49.322667-12.202667 68.750223-57.230222a42.680889 42.680889 0 0 1 78.364444 33.806222c-33.393778 77.312-91.562667 109.269333-141.653333 109.269333" fill="#ffffff" p-id="7440"></path></svg>

+ 1 - 0
src/assets/images/ventAI/chatModalBlue/3-5.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1785309973161" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9438" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M643.7888 573.1328v-336.896c0-33.8944-27.5456-61.44-61.44-61.44H180.5312c-33.8944 0-61.44 27.5456-61.44 61.44v336.9984c0 33.8944 27.5456 61.44 61.44 61.44H230.4v54.8864c0 11.5712 6.4512 22.1184 16.7936 27.3408 4.4032 2.2528 9.216 3.3792 13.9264 3.3792 6.3488 0 12.6976-1.9456 18.0224-5.8368l109.9776-79.872h193.2288c33.8944 0 61.44-27.5456 61.44-61.44z m-257.9456 20.48c-6.5536 0-12.8 2.048-18.0224 5.8368L271.36 669.4912v-45.056c0-16.896-13.824-30.72-30.72-30.72h-60.1088c-11.264 0-20.48-9.216-20.48-20.48V236.2368c0-11.264 9.216-20.48 20.48-20.48h401.8176c11.264 0 20.48 9.216 20.48 20.48v336.9984c0 11.264-9.216 20.48-20.48 20.48H385.8432z" p-id="9439" fill="#ffffff"></path><path d="M843.4688 308.9408H708.7104c-11.264 0-20.48 9.216-20.48 20.48s9.216 20.48 20.48 20.48h134.7584c11.264 0 20.48 9.216 20.48 20.48v338.1248c0 11.264-9.216 20.48-20.48 20.48h-80.4864c-16.896 0-30.72 13.824-30.72 30.72v38.912l-96.0512-68.1984c-5.2224-3.6864-11.3664-5.632-17.8176-5.632h-95.3344c-11.264 0-20.48-9.216-20.48-20.48v-19.456c0-11.264-9.216-20.48-20.48-20.48s-20.48 9.216-20.48 20.48v19.456c0 33.8944 27.5456 61.44 61.44 61.44h92.0576l109.568 77.824c5.3248 3.7888 11.5712 5.7344 17.8176 5.7344 4.8128 0 9.6256-1.1264 14.1312-3.4816 10.24-5.3248 16.5888-15.7696 16.5888-27.3408V770.048h70.2464c33.8944 0 61.44-27.5456 61.44-61.44V370.3808c0-33.8944-27.5456-61.44-61.44-61.44z" p-id="9440" fill="#ffffff"></path></svg>

+ 1 - 0
src/assets/images/ventAI/chatModalBlue/3-6.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1785310039627" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10668" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M804.571429 73.142857H219.428571c-80.457143 0-146.285714 65.828571-146.285714 146.285714v585.142858c0 80.457143 65.828571 146.285714 146.285714 146.285714h585.142858c80.457143 0 146.285714-65.828571 146.285714-146.285714V219.428571c0-80.457143-65.828571-146.285714-146.285714-146.285714z m73.142857 731.428572c0 40.228571-32.914286 73.142857-73.142857 73.142857H219.428571c-40.228571 0-73.142857-32.914286-73.142857-73.142857V219.428571c0-40.228571 32.914286-73.142857 73.142857-73.142857h585.142858c40.228571 0 73.142857 32.914286 73.142857 73.142857v585.142858z m-512-73.142858h292.571428V658.285714H365.714286v73.142857zM219.428571 658.285714h73.142858V585.142857H219.428571v73.142857z m0-365.714285v73.142857h73.142858V292.571429H219.428571z m146.285715 219.428571H292.571429v73.142857h73.142857V512zM292.571429 365.714286v73.142857h73.142857V365.714286H292.571429z m146.285714 73.142857H365.714286v73.142857h73.142857V438.857143z" fill="#ffffff" p-id="10669"></path></svg>

+ 3 - 0
src/assets/images/ventAI/chatModalBlue/3-9.svg

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="12.177" height="13.499" viewBox="0 0 12.177 13.499">
+  <path id="路径_58155" data-name="路径 58155" d="M137.832,66.594a1.374,1.374,0,0,0-.453.076v-.878a1.339,1.339,0,0,0-1.346-1.329,1.356,1.356,0,0,0-.6.138,1.349,1.349,0,0,0-2.425.04,1.427,1.427,0,0,0-.691-.178,1.487,1.487,0,0,0-1.465,1.5V69.12l-.215-.518-.009-.018a1.311,1.311,0,0,0-.76-.684,1.35,1.35,0,0,0-1.681,1.82l1.66,3.97.012.021a7.869,7.869,0,0,0,1.7,2.21,1.849,1.849,0,0,0,1.2.443h3.062a2.52,2.52,0,0,0,1.7-.841,4.718,4.718,0,0,0,1.118-1.805,12.561,12.561,0,0,0,.531-3.98V67.905a1.331,1.331,0,0,0-1.347-1.311Zm.452,3.142a11.725,11.725,0,0,1-.485,3.695,3.859,3.859,0,0,1-.9,1.457,1.694,1.694,0,0,1-1.067.581h-3.062a.951.951,0,0,1-.62-.227,6.958,6.958,0,0,1-1.49-1.941l-1.644-3.932-.008-.018a.444.444,0,0,1,.224-.585.457.457,0,0,1,.349-.02.422.422,0,0,1,.242.212l.9,2.172a.534.534,0,0,0,1.029-.206V65.968a.569.569,0,1,1,1.136-.077v3.727a.447.447,0,0,0,.895.009v-.909a.458.458,0,0,0,0-.065V65.968c0-.04,0-.081,0-.121V65.2a.452.452,0,0,1,.9,0v4.368a.453.453,0,0,0,.435.456.447.447,0,0,0,.46-.447V65.793a.452.452,0,0,1,.9,0v4.4a.453.453,0,0,0,.435.456.447.447,0,0,0,.46-.447v-2.3a.454.454,0,0,1,.9,0v1.831Z" transform="translate(-127.502 -63.364)" fill="#cdeeff" stroke="rgba(0,0,0,0)" stroke-miterlimit="10" stroke-width="1" opacity="0.5"/>
+</svg>

BIN
src/assets/images/ventAI/chatModalBlue/fullscreen/1-1.png


BIN
src/assets/images/ventAI/chatModalBlue/fullscreen/1-2.png


BIN
src/assets/images/ventAI/chatModalBlue/fullscreen/1-3.png


BIN
src/assets/images/ventAI/chatModalBlue/fullscreen/1-4.png


BIN
src/assets/images/ventAI/chatModalBlue/fullscreen/1-5.png


BIN
src/assets/images/ventAI/chatModalBlue/fullscreen/1-6.png


BIN
src/assets/images/ventAI/chatModalBlue/fullscreen/1-7.png


BIN
src/assets/images/ventAI/chatModalBlue/fullscreen/1-8.png


+ 36 - 0
src/assets/images/ventAI/chatModalBlue/fullscreen/1-9.svg

@@ -0,0 +1,36 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="31.028" height="37.013" viewBox="0 0 31.028 37.013">
+  <defs>
+    <filter id="矩形_3967" x="0" y="12.479" width="13.717" height="12.055" filterUnits="userSpaceOnUse">
+      <feOffset input="SourceAlpha"/>
+      <feGaussianBlur stdDeviation="1.5" result="blur"/>
+      <feFlood flood-color="#73ccff" flood-opacity="0.702"/>
+      <feComposite operator="in" in2="blur"/>
+      <feComposite in="SourceGraphic"/>
+    </filter>
+    <filter id="路径_45164" x="3.566" y="0" width="27.462" height="37.013" filterUnits="userSpaceOnUse">
+      <feOffset input="SourceAlpha"/>
+      <feGaussianBlur stdDeviation="3" result="blur-2"/>
+      <feFlood flood-color="#00b4ff" flood-opacity="0.502"/>
+      <feComposite operator="in" in2="blur-2"/>
+      <feComposite in="SourceGraphic"/>
+    </filter>
+    <filter id="路径_45150" x="4.717" y="9.354" width="13.854" height="18.307" filterUnits="userSpaceOnUse">
+      <feOffset input="SourceAlpha"/>
+      <feGaussianBlur stdDeviation="1.5" result="blur-3"/>
+      <feFlood flood-color="#73ccff" flood-opacity="0.702"/>
+      <feComposite operator="in" in2="blur-3"/>
+      <feComposite in="SourceGraphic"/>
+    </filter>
+  </defs>
+  <g id="组_16884" data-name="组 16884" transform="translate(-1426.704 -273.629)">
+    <g transform="matrix(1, 0, 0, 1, 1426.7, 273.63)" filter="url(#矩形_3967)">
+      <rect id="矩形_3967-2" data-name="矩形 3967" width="4.717" height="3.054" transform="translate(4.5 16.98)" fill="#fff"/>
+    </g>
+    <g transform="matrix(1, 0, 0, 1, 1426.7, 273.63)" filter="url(#路径_45164)">
+      <path id="路径_45164-2" data-name="路径 45164" d="M257.2,0V5.09l4.4,4.417-4.4,4.417v5.09l9.462-9.507Z" transform="translate(-244.63 9)" fill="#3ba3dc"/>
+    </g>
+    <g transform="matrix(1, 0, 0, 1, 1426.7, 273.63)" filter="url(#路径_45150)">
+      <path id="路径_45150-2" data-name="路径 45150" d="M185.274,4.653l-4.855,4.653V0Z" transform="translate(-171.2 13.85)" fill="#fff"/>
+    </g>
+  </g>
+</svg>

+ 21 - 4
src/design/themify/deepblue.less

@@ -89,12 +89,12 @@ html[data-theme='deepblue'] {
   --img-chat-send-icon: url('/src/assets/images/ventAI/chatModalBlue/2-6.svg');
   --img-chat-close-bg: url('/src/assets/images/ventAI/chatModalBlue/2-7.png');
   --img-chat-download-icon: url('/src/assets/images/ventAI/chatModalBlue/2-8.png');
-  --img-chat-ai-avatar: url('/src/assets/images/ventAI/chatModalBlue/2-9.png');
-  --img-chat-upload-icon: url('/src/assets/images/ventAI/chatModalBlue/2-10.svg');
-  --img-chat-close-icon: url('/src/assets/images/ventAI/chatModalBlue/2-11.svg');
   --img-chat-input-bg: url('/src/assets/images/ventAI/chatModalBlue/2-13.png');
-  --img-chat-collapse-bg: url('/src/assets/images/ventAI/chatModalBlue/2-15.png');
   --img-chat-attach-icon: url('/src/assets/images/ventAI/chatModalBlue/3-7.svg');
+  --img-chat-popup-attach-icon: url('/src/assets/images/ventAI/chatModalBlue/3-3.svg');
+  --img-chat-popup-mention-icon: url('/src/assets/images/ventAI/chatModalBlue/3-4.svg');
+  --img-chat-popup-session-icon: url('/src/assets/images/ventAI/chatModalBlue/3-5.svg');
+  --img-chat-popup-command-icon: url('/src/assets/images/ventAI/chatModalBlue/3-6.svg');
   --img-chat-scroll-bottom: url('/src/assets/images/ventAI/chatModalBlue/3-8.png');
   --img-chat-arrow-icon: url('/src/assets/images/ventAI/chatModalBlue/4-2.svg');
   --img-chat-copy-icon: url('/src/assets/images/ventAI/chatModalBlue/4-3.svg');
@@ -102,4 +102,21 @@ html[data-theme='deepblue'] {
   --img-chat-download-icon-btn: url('/src/assets/images/ventAI/chatModalBlue/4-5.svg');
   --img-chat-preview-icon: url('/src/assets/images/ventAI/chatModalBlue/4-6.svg');
   --img-chat-welcome-img: url('/src/assets/images/ventAI/chatModalBlue/5-1.png');
+  --img-chat-pin-icon: url('/src/assets/images/ventAI/chatModalBlue/3-1.svg');
+  --img-chat-delete-icon: url('/src/assets/images/ventAI/chatModalBlue/3-2.svg');
+  --img-chat-mode-confirm-icon: url('/src/assets/images/ventAI/chatModalBlue/3-9.svg');
+  --img-chat-mode-auto-icon: url('/src/assets/images/ventAI/chatModalBlue/3-10.svg');
+  --img-chat-mode-plan-icon: url('/src/assets/images/ventAI/chatModalBlue/3-11.svg');
+  --img-chat-mode-full-icon: url('/src/assets/images/ventAI/chatModalBlue/3-12.svg');
+
+  // 全屏模式图片
+  --img-chat-fs-header-bg: url('/src/assets/images/ventAI/chatModalBlue/fullscreen/1-1.png');
+  --img-chat-fs-modal-bg: url('/src/assets/images/ventAI/chatModalBlue/fullscreen/1-2.png');
+  --img-chat-fs-task-panel-bg: url('/src/assets/images/ventAI/chatModalBlue/fullscreen/1-3.png');
+  --img-chat-fs-panel-bg: url('/src/assets/images/ventAI/chatModalBlue/fullscreen/1-4.png');
+  --img-chat-fs-collapse-btn-bg: url('/src/assets/images/ventAI/chatModalBlue/fullscreen/1-5.png');
+  --img-chat-fs-options-item-bg: url('/src/assets/images/ventAI/chatModalBlue/fullscreen/1-6.png');
+  --img-chat-fs-options-item-active-bg: url('/src/assets/images/ventAI/chatModalBlue/fullscreen/1-7.png');
+  --img-chat-fs-header-title-bg: url('/src/assets/images/ventAI/chatModalBlue/fullscreen/1-8.png');
+  --img-chat-fs-header-title-icon: url('/src/assets/images/ventAI/chatModalBlue/fullscreen/1-9.svg');
 }

+ 10 - 4
src/design/themify/default.less

@@ -95,12 +95,12 @@ html {
   --img-chat-copy-success-icon: url('/src/assets/images/ventAI/chatModalBlue/2-5.svg');
   --img-chat-send-icon: url('/src/assets/images/ventAI/chatModalBlue/2-6.svg');
   --img-chat-download-icon: url('/src/assets/images/ventAI/chatModalBlue/2-8.png');
-  --img-chat-ai-avatar: url('/src/assets/images/ventAI/chatModalBlue/2-9.png');
-  --img-chat-upload-icon: url('/src/assets/images/ventAI/chatModalBlue/2-10.svg');
-  --img-chat-close-icon: url('/src/assets/images/ventAI/chatModalBlue/2-11.svg');
   --img-chat-input-bg: url('/src/assets/images/ventAI/chatModalBlue/1-7.svg');
-  --img-chat-collapse-bg: url('/src/assets/images/ventAI/chatModalBlue/2-15.png');
   --img-chat-attach-icon: url('/src/assets/images/ventAI/chatModalBlue/3-7.svg');
+  --img-chat-popup-attach-icon: url('/src/assets/images/ventAI/chatModalBlue/3-3.svg');
+  --img-chat-popup-mention-icon: url('/src/assets/images/ventAI/chatModalBlue/3-4.svg');
+  --img-chat-popup-session-icon: url('/src/assets/images/ventAI/chatModalBlue/3-5.svg');
+  --img-chat-popup-command-icon: url('/src/assets/images/ventAI/chatModalBlue/3-6.svg');
   --img-chat-scroll-bottom: url('/src/assets/images/ventAI/chatModalBlue/3-8.png');
   --img-chat-arrow-icon: url('/src/assets/images/ventAI/chatModalBlue/4-2.svg');
   --img-chat-copy-icon: url('/src/assets/images/ventAI/chatModalBlue/4-3.svg');
@@ -108,6 +108,12 @@ html {
   --img-chat-download-icon-btn: url('/src/assets/images/ventAI/chatModalBlue/4-5.svg');
   --img-chat-preview-icon: url('/src/assets/images/ventAI/chatModalBlue/4-6.svg');
   --img-chat-welcome-img: url('/src/assets/images/ventAI/chatModalBlue/5-1.png');
+  --img-chat-pin-icon: url('/src/assets/images/ventAI/chatModalBlue/3-1.svg');
+  --img-chat-delete-icon: url('/src/assets/images/ventAI/chatModalBlue/3-2.svg');
+  --img-chat-mode-confirm-icon: url('/src/assets/images/ventAI/chatModalBlue/3-9.svg');
+  --img-chat-mode-auto-icon: url('/src/assets/images/ventAI/chatModalBlue/3-10.svg');
+  --img-chat-mode-plan-icon: url('/src/assets/images/ventAI/chatModalBlue/3-11.svg');
+  --img-chat-mode-full-icon: url('/src/assets/images/ventAI/chatModalBlue/3-12.svg');
 
   // 全屏模式图片
   --img-chat-fs-header-bg: url('/src/assets/images/ventAI/chatModalBlue/fullscreen/1-1.png');

+ 81 - 0
src/views/ventAI/manageAssistent/api.ts

@@ -5,6 +5,7 @@ enum Api {
   getHistoryList = '/ventAI/api/sessions',
   getDetail = '/ventAI/api/chat/history/',
   deleteSession = '/ventAI/api/sessions/',
+  reviewPdf = '/ventAI/api/vent/review/pdf',
 }
 
 /**
@@ -103,3 +104,83 @@ export const unifiedStream = async (
     throw error;
   }
 };
+
+/**
+ * PDF审查接口(SSE流式响应)
+ * @param params.file - 配风计划PDF文件,必填
+ * @param params.session_id - 会话唯一标识ID,可选
+ * @param params.message - 附加消息/审查指令,可选
+ * @param onChunk - 流式数据回调函数
+ * @returns Promise<{ session_id: string }>
+ */
+export const reviewPdfStream = async (
+  params: { file: File; session_id?: string; message?: string },
+  onChunk: (chunk: string) => void
+): Promise<{ session_id: string }> => {
+  try {
+    const formData = new FormData();
+    formData.append('file', params.file);
+    if (params.session_id) {
+      formData.append('session_id', params.session_id);
+    }
+    if (params.message) {
+      formData.append('message', params.message);
+    }
+
+    const response = await fetch(Api.reviewPdf, {
+      method: 'POST',
+      body: formData,
+    });
+
+    if (!response.ok) {
+      throw new Error(`HTTP错误: ${response.status}`);
+    }
+
+    const reader = response.body!.getReader();
+    const decoder = new TextDecoder();
+    let buffer = '';
+    let sessionId = '';
+    let isDone = false;
+
+    // eslint-disable-next-line no-constant-condition
+    while (true) {
+      const { done, value } = await reader.read();
+      if (done || isDone) break;
+
+      buffer += decoder.decode(value, { stream: true });
+      const lines = buffer.split('\n');
+      buffer = lines.pop() || '';
+
+      for (const line of lines) {
+        if (line.trim() === '') continue;
+
+        if (line.startsWith('data: ')) {
+          const dataStr = line.replace(/^data: /, '');
+
+          try {
+            const data = JSON.parse(dataStr);
+
+            onChunk(line);
+
+            if (data.type === 'done' || data.type === 'error') {
+              if (data.session_id) {
+                sessionId = data.session_id;
+              }
+              isDone = true;
+              break;
+            }
+          } catch (e) {
+            console.warn('SSE数据解析失败:', dataStr);
+          }
+        }
+      }
+
+      if (isDone) break;
+    }
+
+    return { session_id: sessionId };
+  } catch (error) {
+    console.error('PDF审查流式请求失败:', error);
+    throw error;
+  }
+};

+ 86 - 9
src/views/ventAI/manageAssistent/components/AiAssistantModal.vue

@@ -81,7 +81,7 @@
 <script setup lang="ts">
   import { ref, nextTick, watch, computed, onMounted, onBeforeUnmount } from 'vue';
   import dayjs from 'dayjs';
-  import { unifiedStream, getHistoryList, getDetail, deleteSession } from '../api';
+  import { unifiedStream, reviewPdfStream, getHistoryList, getDetail, deleteSession } from '../api';
   import { message, Modal } from 'ant-design-vue';
   import { isPdfFile } from './chatModal/utils';
   import TaskListPanel from './chatModal/TaskListPanel.vue';
@@ -276,18 +276,22 @@
     switchTask(newTask.id);
   };
 
-  const handleDeleteTask = (task: Task, index: number) => {
+  const handleDeleteTask = (task: Task, _index: number) => {
     Modal.confirm({
       title: '提示',
       content: '是否删除该对话?',
       okText: '确认',
       cancelText: '取消',
+      wrapClassName: 'delete-task-confirm-modal',
       onOk: async () => {
         try {
           if (task.sessionId) {
             await deleteSession(task.sessionId);
           }
-          taskList.value.splice(index, 1);
+          const realIndex = taskList.value.findIndex((t) => t.id === task.id);
+          if (realIndex !== -1) {
+            taskList.value.splice(realIndex, 1);
+          }
           if (currentTaskId.value === task.id) {
             if (taskList.value.length > 0) {
               switchTask(taskList.value[0].id);
@@ -632,7 +636,7 @@
         upsertStep(aiMsg.thinkingSteps!, {
           type: 'agent_done',
           agent: data.cn_agent || data.agent || '智能助手',
-          message: `${data.message || '审查完成'}${data.duration_ms ? `(${(data.duration_ms / 1000).toFixed(0)}s)` : ''}${data.progress ? ` [${data.progress}]` : ''}`,
+          message: `${(data.message || '审查完成').replace(/(\d+ms)/g, '')}${data.duration_ms ? `(${(data.duration_ms / 1000).toFixed(0)}s)` : ''}${data.progress ? ` [${data.progress}]` : ''}`,
           timestamp: Date.now(),
         });
         if (data.preview) {
@@ -819,11 +823,11 @@
       throw new Error('缺少原始文件对象');
     }
 
-    const result = await unifiedStream(
+    const result = await reviewPdfStream(
       {
-        message: userInput || `上传了文件:${file.name}`,
+        file: file.originalFile!,
         session_id: getCurrentSessionId() || undefined,
-        file: file.originalFile,
+        message: userInput || undefined,
       },
       (chunk: string) => {
         parseSSEData(chunk, aiMsgIndex);
@@ -1045,7 +1049,7 @@
   }
 
   .task-panel-wrapper {
-    width: 260px;
+    width: 300px;
     flex-shrink: 0;
     overflow: hidden;
     transition:
@@ -1252,6 +1256,7 @@
       padding: 0 !important;
       max-width: 100vw !important;
       margin: 0 !important;
+      background-color: #09172c;
 
       .zxm-modal-wrap,
       .ant-modal-wrap {
@@ -1267,9 +1272,14 @@
         margin: 0;
         border-radius: 0;
         overflow: hidden;
+        background-image: var(--img-chat-fs-modal-bg);
       }
       .zxm-modal-header {
-        height: 120px;
+        height: 90px;
+        background-image: var(--img-chat-fs-header-bg) !important;
+        background-repeat: no-repeat;
+        background-size: 100% 100%;
+        margin-bottom: 16px;
       }
       .no-drag {
         cursor: default;
@@ -1287,6 +1297,66 @@
         height: 100%;
         overflow: hidden;
       }
+      .chat-panel {
+        background-image: var(--img-chat-fs-panel-bg) !important;
+      }
+      .chat-header {
+        padding: 0 32px;
+        height: 50px;
+      }
+      .collapse-btn {
+        top: 60px;
+        background-image: var(--img-chat-fs-collapse-btn-bg) !important;
+        background-repeat: no-repeat;
+        background-size: 100% 100%;
+      }
+      .task-list-panel {
+        padding: 20px;
+        background-image: var(--img-chat-fs-task-panel-bg) !important;
+        background-repeat: no-repeat !important;
+        background-size: 100% 100% !important;
+      }
+      .options-area {
+        gap: 12px;
+      }
+      .options-area-item {
+        background-image: var(--img-chat-fs-options-item-bg) !important;
+        background-repeat: no-repeat !important;
+        background-size: 100% 100% !important;
+
+        &.active,
+        &:active {
+          background-image: var(--img-chat-fs-options-item-active-bg) !important;
+        }
+        .options-img {
+          background-image: none !important;
+        }
+      }
+      .chat-header-title {
+        height: 40px;
+        line-height: 40px;
+        position: relative;
+        background-image: var(--img-chat-fs-header-title-bg) !important;
+        background-repeat: no-repeat !important;
+        background-size: auto 100% !important;
+        padding: 0 28px;
+
+        &::before {
+          content: '';
+          position: absolute;
+          left: 0;
+          top: 50%;
+          transform: translateY(-50%);
+          width: 40px;
+          height: 40px;
+          background-image: var(--img-chat-fs-header-title-icon);
+          background-repeat: no-repeat;
+          background-size: 100% 100%;
+        }
+      }
+      .chat-input-area {
+        margin: 24px;
+      }
     }
 
     .zxm-modal-header {
@@ -1318,4 +1388,11 @@
       padding: 12px 24px;
     }
   }
+
+  .delete-task-confirm-modal {
+    .zxm-modal-confirm-title,
+    .zxm-modal-confirm-content {
+      color: #fff;
+    }
+  }
 </style>

+ 791 - 11
src/views/ventAI/manageAssistent/components/chatModal/ChatInputArea.vue

@@ -24,14 +24,139 @@
     />
     <div class="input-actions">
       <div class="action-left">
-        <a-tooltip title="从本地上传">
-          <div class="btn-upload" @click="emit('file-upload')">
-            <div class="plus-icon"></div>
+        <div class="btn-upload-wrapper">
+          <a-tooltip title="添加上下文">
+            <div class="btn-upload" @click="togglePopup">
+              <div class="plus-icon"></div>
+            </div>
+          </a-tooltip>
+          <div v-if="showPopup" ref="popupRef" class="upload-popup" @click.stop>
+            <div class="popup-item" @click="handleAddAttachment">
+              <div class="popup-item-icon attach-icon"></div>
+              <span class="popup-item-text">添加附件</span>
+            </div>
+            <div class="popup-item" @click="handleInsertMention">
+              <div class="popup-item-icon mention-icon"></div>
+              <span class="popup-item-text">插入 @ 提及</span>
+            </div>
+            <div class="popup-item" @click="handleInsertSession">
+              <div class="popup-item-icon session-icon"></div>
+              <span class="popup-item-text">插入 # 会话</span>
+            </div>
+            <div class="popup-item" @click="handleInsertCommand">
+              <div class="popup-item-icon command-icon"></div>
+              <span class="popup-item-text">插入 / 命令</span>
+            </div>
           </div>
-        </a-tooltip>
+        </div>
+        <div class="action-divider"></div>
+        <div class="btn-edit-mode-wrapper">
+          <a-tooltip title="切换模式">
+            <div class="btn-edit-mode" @click="toggleEditModePopup">
+              <div class="btn-icon" :class="`mode-icon-${selectedEditMode.key}`"></div>
+              <span class="btn-text">{{ selectedEditMode.label }}</span>
+              <div class="btn-arrow"></div>
+            </div>
+          </a-tooltip>
+          <div v-if="showEditModePopup" ref="editModePopupRef" class="edit-mode-popup">
+            <div
+              v-for="item in editModeOptions"
+              :key="item.key"
+              class="edit-mode-item"
+              :class="{ active: selectedEditMode.key === item.key }"
+              @click="handleSelectEditMode(item)"
+            >
+              <div class="edit-mode-item-icon" :class="`mode-icon-${item.key}`"></div>
+              <div class="edit-mode-item-main">
+                <span class="edit-mode-item-label">{{ item.label }}</span>
+                <span class="edit-mode-item-desc">{{ item.desc }}</span>
+              </div>
+              <div class="edit-mode-check" :class="{ visible: selectedEditMode.key === item.key }"></div>
+            </div>
+          </div>
+        </div>
         <input ref="fileInputRef" type="file" style="display: none" @change="emit('file-change', $event)" accept=".pdf" />
       </div>
       <div class="action-right">
+        <div class="btn-context-wrapper" ref="contextPopupRef">
+          <div class="btn-context" @click="toggleContextPopup" @mouseenter="showContextPopup = true" @mouseleave="showContextPopup = false">
+            <svg class="context-ring" viewBox="0 0 32 32">
+              <circle class="context-ring-bg" cx="16" cy="16" r="13" />
+              <circle
+                class="context-ring-fill"
+                cx="16"
+                cy="16"
+                r="13"
+                :stroke-dasharray="contextCircumference"
+                :stroke-dashoffset="contextOffset"
+                :style="{ stroke: contextColor }"
+              />
+            </svg>
+            <span class="context-percent">{{ contextPercent }}%</span>
+          </div>
+          <div v-if="showContextPopup" class="context-popup" @click.stop>
+            <div class="context-popup-title">上下文容量</div>
+            <div class="context-popup-info">
+              <span class="context-used">{{ contextDisplayUsed }}</span>
+              <span class="context-sep">/</span>
+              <span class="context-max">{{ contextDisplayMax }}</span>
+              <span class="context-pct">({{ contextPercent }}%)</span>
+            </div>
+            <div class="context-popup-bar-track">
+              <div class="context-popup-bar-fill" :style="{ width: contextPercent + '%', background: contextColor }"></div>
+            </div>
+          </div>
+        </div>
+        <div class="action-divider"></div>
+        <div class="btn-model-wrapper" ref="modelPopupRef">
+          <a-tooltip title="选择模型">
+            <div class="btn-model" @click="toggleModelPopup">
+              <span class="btn-text">{{ selectedModel.label }}</span>
+              <div class="btn-arrow"></div>
+            </div>
+          </a-tooltip>
+          <div v-if="showModelPopup" class="model-popup" @click.stop>
+            <div
+              v-for="item in modelOptions"
+              :key="item.key"
+              class="model-popup-item"
+              :class="{ active: selectedModel.key === item.key }"
+              @click="handleSelectModel(item)"
+            >
+              <div class="model-item-main">
+                <span class="model-item-label">{{ item.label }}</span>
+                <span class="model-item-desc">{{ item.desc }}</span>
+              </div>
+              <div class="model-item-check" :class="{ visible: selectedModel.key === item.key }"></div>
+            </div>
+          </div>
+        </div>
+        <div class="action-divider"></div>
+        <div class="btn-think-wrapper" ref="thinkPopupRef">
+          <a-tooltip title="思考级别">
+            <div class="btn-think" @click="toggleThinkPopup">
+              <div class="btn-icon think-icon"></div>
+              <span class="btn-text">{{ selectedThinkLevel.label }}</span>
+              <div class="btn-arrow"></div>
+            </div>
+          </a-tooltip>
+          <div v-if="showThinkPopup" class="think-popup" @click.stop>
+            <div
+              v-for="item in thinkLevelOptions"
+              :key="item.key"
+              class="think-popup-item"
+              :class="{ active: selectedThinkLevel.key === item.key }"
+              @click="handleSelectThinkLevel(item)"
+            >
+              <div class="think-item-main">
+                <span class="think-item-label">{{ item.label }}</span>
+                <span class="think-item-desc">{{ item.desc }}</span>
+              </div>
+              <div class="think-item-check" :class="{ visible: selectedThinkLevel.key === item.key }"></div>
+            </div>
+          </div>
+        </div>
+        <div class="action-divider"></div>
         <div class="btn-send" :class="{ loading, disabled: !modelValue.trim() }" @click="handleSend">
           <div class="send-icon"></div>
         </div>
@@ -41,16 +166,25 @@
 </template>
 
 <script setup lang="ts">
-  import { ref } from 'vue';
+  import { ref, computed } from 'vue';
+  import { onClickOutside } from '@vueuse/core';
   import { CloseOutlined } from '@ant-design/icons-vue';
   import { formatFileSize } from './utils';
   import type { AttachedFile } from './types';
 
-  const props = defineProps<{
-    modelValue: string;
-    pendingFile: AttachedFile | null;
-    loading: boolean;
-  }>();
+  const props = withDefaults(
+    defineProps<{
+      modelValue: string;
+      pendingFile: AttachedFile | null;
+      loading: boolean;
+      contextUsed?: number;
+      contextMax?: number;
+    }>(),
+    {
+      contextUsed: 0,
+      contextMax: 200000,
+    }
+  );
 
   const emit = defineEmits<{
     (e: 'update:modelValue', value: string): void;
@@ -58,9 +192,172 @@
     (e: 'file-upload'): void;
     (e: 'file-change', event: Event): void;
     (e: 'remove-pending-file'): void;
+    (e: 'model-change', modelKey: string): void;
+    (e: 'think-level-change', levelKey: string): void;
   }>();
 
+  const handleSelectModel = (item: ModelOption) => {
+    selectedModel.value = item;
+    showModelPopup.value = false;
+    emit('model-change', item.key);
+  };
+
   const fileInputRef = ref<HTMLInputElement>();
+  const popupRef = ref<HTMLElement>();
+  const showPopup = ref(false);
+
+  // 上下文容量
+  const contextPopupRef = ref<HTMLElement>();
+  const showContextPopup = ref(false);
+
+  const contextPercent = computed(() => {
+    if (!props.contextMax) return 0;
+    return Math.min(100, Math.round((props.contextUsed / props.contextMax) * 100));
+  });
+
+  const contextCircumference = computed(() => 2 * Math.PI * 13);
+  const contextOffset = computed(() => contextCircumference.value * (1 - contextPercent.value / 100));
+
+  const contextColor = computed(() => {
+    if (contextPercent.value >= 90) return '#ff4d4f';
+    if (contextPercent.value >= 70) return '#faad14';
+    return '#52c41a';
+  });
+
+  const contextDisplayUsed = computed(() => {
+    if (props.contextUsed >= 10000) return (props.contextUsed / 10000).toFixed(1) + '万';
+    return props.contextUsed.toLocaleString();
+  });
+
+  const contextDisplayMax = computed(() => {
+    if (props.contextMax >= 10000) return (props.contextMax / 10000).toFixed(0) + '万';
+    return props.contextMax.toLocaleString();
+  });
+
+  const toggleContextPopup = () => {
+    showContextPopup.value = !showContextPopup.value;
+  };
+
+  onClickOutside(contextPopupRef, () => {
+    showContextPopup.value = false;
+  });
+
+  // 模型选择
+  const modelPopupRef = ref<HTMLElement>();
+  const showModelPopup = ref(false);
+
+  interface ModelOption {
+    key: string;
+    label: string;
+    desc: string;
+  }
+
+  const modelOptions: ModelOption[] = [
+    { key: 'deepseek-v4-pro', label: 'DeepSeek-V4-Pro', desc: '默认模型,综合能力强' },
+    { key: 'deepseek-v4', label: 'DeepSeek-V4', desc: '轻量快速,适合简单任务' },
+    { key: 'gpt-4o', label: 'GPT-4o', desc: 'OpenAI 多模态模型' },
+    { key: 'gpt-4o-mini', label: 'GPT-4o Mini', desc: 'OpenAI 轻量模型' },
+    { key: 'claude-sonnet-4', label: 'Claude Sonnet 4', desc: 'Anthropic 高性能模型' },
+    { key: 'qwen-max', label: '通义千问 Max', desc: '阿里云旗舰模型' },
+  ];
+
+  const selectedModel = ref<ModelOption>(modelOptions[0]);
+
+  const toggleModelPopup = () => {
+    showModelPopup.value = !showModelPopup.value;
+  };
+
+  onClickOutside(modelPopupRef, () => {
+    showModelPopup.value = false;
+  });
+
+  // 思考级别
+  const thinkPopupRef = ref<HTMLElement>();
+  const showThinkPopup = ref(false);
+
+  interface ThinkLevelOption {
+    key: string;
+    label: string;
+    desc: string;
+  }
+
+  const thinkLevelOptions: ThinkLevelOption[] = [
+    { key: 'off', label: '关闭', desc: '不使用深度思考,响应最快' },
+    { key: 'high', label: '高', desc: '深度思考,平衡速度与质量' },
+    { key: 'highest', label: '最高', desc: '极致思考,质量最高但较慢' },
+  ];
+
+  const selectedThinkLevel = ref<ThinkLevelOption>(thinkLevelOptions[0]);
+
+  const toggleThinkPopup = () => {
+    showThinkPopup.value = !showThinkPopup.value;
+  };
+
+  const handleSelectThinkLevel = (item: ThinkLevelOption) => {
+    selectedThinkLevel.value = item;
+    showThinkPopup.value = false;
+    emit('think-level-change', item.key);
+  };
+
+  onClickOutside(thinkPopupRef, () => {
+    showThinkPopup.value = false;
+  });
+
+  const editModePopupRef = ref<HTMLElement>();
+  const showEditModePopup = ref(false);
+
+  interface EditModeOption {
+    key: string;
+    label: string;
+    desc: string;
+  }
+
+  const editModeOptions: EditModeOption[] = [
+    { key: 'confirm', label: '变更前确认', desc: '改文件前先问我' },
+    { key: 'auto', label: '自动编辑', desc: '自动编辑文件' },
+    { key: 'plan', label: '计划模式', desc: '编辑前先出计划' },
+    { key: 'full', label: '完全访问', desc: '减少确认次数' },
+  ];
+
+  const selectedEditMode = ref<EditModeOption>(editModeOptions[1]);
+
+  onClickOutside(editModePopupRef, () => {
+    showEditModePopup.value = false;
+  });
+
+  const toggleEditModePopup = () => {
+    showEditModePopup.value = !showEditModePopup.value;
+  };
+
+  const handleSelectEditMode = (item: EditModeOption) => {
+    selectedEditMode.value = item;
+    showEditModePopup.value = false;
+  };
+
+  onClickOutside(popupRef, () => {
+    showPopup.value = false;
+  });
+
+  const togglePopup = () => {
+    showPopup.value = !showPopup.value;
+  };
+
+  const handleAddAttachment = () => {
+    showPopup.value = false;
+    emit('file-upload');
+  };
+
+  const handleInsertMention = () => {
+    showPopup.value = false;
+  };
+
+  const handleInsertSession = () => {
+    showPopup.value = false;
+  };
+
+  const handleInsertCommand = () => {
+    showPopup.value = false;
+  };
 
   const handleEnterKey = (e: KeyboardEvent) => {
     if (!e.shiftKey && props.modelValue.trim()) {
@@ -174,18 +471,64 @@
       align-items: center;
       margin-top: 12px;
 
+      .mode-icon-confirm {
+        background-image: var(--img-chat-mode-confirm-icon);
+      }
+      .mode-icon-auto {
+        background-image: var(--img-chat-mode-auto-icon);
+      }
+      .mode-icon-plan {
+        background-image: var(--img-chat-mode-plan-icon);
+      }
+      .mode-icon-full {
+        background-image: var(--img-chat-mode-full-icon);
+      }
+
       .action-left,
       .action-right {
         display: flex;
         gap: 8px;
+        .action-divider {
+          width: 1px;
+          height: 18px;
+          background: rgba(255, 255, 255, 0.15);
+          align-self: center;
+          flex-shrink: 0;
+        }
+        .btn-icon {
+          width: 14px;
+          height: 14px;
+          background-repeat: no-repeat;
+          background-size: 100% 100%;
+          flex-shrink: 0;
+        }
+        .think-icon {
+          background-image: var(--img-chat-thinking-icon);
+        }
+        .btn-text {
+          font-size: 12px;
+          color: #e0e6ed;
+          white-space: nowrap;
+        }
+        .btn-arrow {
+          width: 0;
+          height: 0;
+          border-left: 4px solid transparent;
+          border-right: 4px solid transparent;
+          border-top: 4px solid #8b949e;
+          flex-shrink: 0;
+        }
+        .btn-upload-wrapper {
+          position: relative;
+        }
         .btn-upload {
           width: 30px;
           height: 30px;
           border-radius: 6px;
-          margin-right: 10px;
           display: flex;
           align-items: center;
           justify-content: center;
+          cursor: pointer;
         }
         .plus-icon {
           width: 20px;
@@ -195,6 +538,443 @@
           background-size: 100% 100%;
           cursor: pointer;
         }
+        .upload-popup {
+          position: absolute;
+          bottom: 38px;
+          left: 0;
+          background: #0a2a3f;
+          border: 1px solid #1a4a6f;
+          border-radius: 8px;
+          min-width: 160px;
+          z-index: 1000;
+          box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
+
+          .popup-item {
+            display: flex;
+            align-items: center;
+            gap: 10px;
+            padding: 9px 16px;
+            cursor: pointer;
+            transition: background 0.2s;
+            border-radius: 8px;
+
+            &:hover {
+              background: rgba(10, 132, 255, 0.15);
+            }
+
+            .popup-item-icon {
+              width: 18px;
+              height: 18px;
+              background-repeat: no-repeat;
+              background-size: 100% 100%;
+              flex-shrink: 0;
+            }
+
+            .attach-icon {
+              background-image: var(--img-chat-popup-attach-icon);
+            }
+
+            .mention-icon {
+              background-image: var(--img-chat-popup-mention-icon);
+            }
+
+            .session-icon {
+              background-image: var(--img-chat-popup-session-icon);
+            }
+
+            .command-icon {
+              background-image: var(--img-chat-popup-command-icon);
+            }
+
+            .popup-item-text {
+              font-size: 13px;
+              color: #e0e6ed;
+              white-space: nowrap;
+            }
+          }
+        }
+        .btn-edit-mode-wrapper {
+          position: relative;
+        }
+        .btn-edit-mode {
+          height: 28px;
+          padding: 0 10px;
+          border-radius: 6px;
+          display: flex;
+          align-items: center;
+          gap: 4px;
+          cursor: pointer;
+          transition: all 0.2s;
+          border: 1px solid rgba(255, 255, 255, 0.12);
+
+          &:hover {
+            border-color: #3a8fd4;
+            background: rgba(10, 132, 255, 0.1);
+          }
+        }
+        .edit-mode-popup {
+          position: absolute;
+          bottom: 38px;
+          left: 50%;
+          transform: translateX(-50%);
+          background: #0a2a3f;
+          border: 1px solid #1a4a6f;
+          border-radius: 8px;
+          min-width: 200px;
+          z-index: 1000;
+          box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
+
+          .edit-mode-item {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            padding: 10px 12px;
+            cursor: pointer;
+            transition: background 0.2s;
+            border-radius: 6px;
+
+            &:hover {
+              background: rgba(10, 132, 255, 0.15);
+            }
+
+            &.active {
+              background: rgba(10, 132, 255, 0.1);
+            }
+
+            .edit-mode-item-icon {
+              width: 18px;
+              height: 18px;
+              background-repeat: no-repeat;
+              background-size: 100% 100%;
+              flex-shrink: 0;
+              margin-right: 10px;
+            }
+
+            .edit-mode-item-main {
+              display: flex;
+              flex-direction: column;
+              gap: 2px;
+
+              .edit-mode-item-label {
+                font-size: 13px;
+                color: #e0e6ed;
+                white-space: nowrap;
+              }
+
+              .edit-mode-item-desc {
+                font-size: 11px;
+                color: #8b949e;
+                white-space: nowrap;
+              }
+            }
+
+            .edit-mode-check {
+              width: 16px;
+              height: 16px;
+              flex-shrink: 0;
+              display: flex;
+              align-items: center;
+              justify-content: center;
+              visibility: hidden;
+
+              &.visible {
+                visibility: visible;
+              }
+
+              &::after {
+                content: '✓';
+                font-size: 12px;
+                color: #3a8fd4;
+              }
+            }
+          }
+        }
+        .btn-context-wrapper {
+          position: relative;
+          display: flex;
+          align-items: center;
+        }
+        .btn-context {
+          width: 30px;
+          height: 30px;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          cursor: pointer;
+          position: relative;
+          border-radius: 6px;
+          transition: background 0.2s;
+
+          &:hover {
+            background: rgba(10, 132, 255, 0.1);
+          }
+
+          .context-ring {
+            width: 24px;
+            height: 24px;
+            transform: rotate(-90deg);
+
+            .context-ring-bg {
+              fill: none;
+              stroke: rgba(255, 255, 255, 0.1);
+              stroke-width: 3;
+            }
+
+            .context-ring-fill {
+              fill: none;
+              stroke-width: 3;
+              stroke-linecap: round;
+              transition: stroke-dashoffset 0.3s ease;
+            }
+          }
+
+          .context-percent {
+            position: absolute;
+            font-size: 8px;
+            color: #e0e6ed;
+            font-weight: 500;
+            line-height: 1;
+          }
+        }
+        .context-popup {
+          position: absolute;
+          bottom: 42px;
+          left: 50%;
+          transform: translateX(-50%);
+          background: #0a2a3f;
+          border: 1px solid #1a4a6f;
+          border-radius: 8px;
+          min-width: 200px;
+          padding: 14px 16px;
+          z-index: 1000;
+          box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
+
+          .context-popup-title {
+            font-size: 13px;
+            color: #8b949e;
+            margin-bottom: 8px;
+          }
+
+          .context-popup-info {
+            display: flex;
+            align-items: baseline;
+            gap: 2px;
+            margin-bottom: 10px;
+
+            .context-used {
+              font-size: 18px;
+              font-weight: 600;
+              color: #e0e6ed;
+            }
+
+            .context-sep {
+              font-size: 14px;
+              color: #8b949e;
+            }
+
+            .context-max {
+              font-size: 14px;
+              color: #8b949e;
+            }
+
+            .context-pct {
+              font-size: 13px;
+              color: #8b949e;
+            }
+          }
+
+          .context-popup-bar-track {
+            width: 100%;
+            height: 6px;
+            background: rgba(255, 255, 255, 0.08);
+            border-radius: 3px;
+            overflow: hidden;
+
+            .context-popup-bar-fill {
+              height: 100%;
+              border-radius: 3px;
+              transition: width 0.3s ease;
+            }
+          }
+        }
+        .btn-model-wrapper {
+          position: relative;
+          display: flex;
+          align-items: center;
+        }
+        .btn-model {
+          height: 28px;
+          padding: 0 10px;
+          border-radius: 6px;
+          display: flex;
+          align-items: center;
+          gap: 4px;
+          cursor: pointer;
+          transition: all 0.2s;
+          border: 1px solid rgba(255, 255, 255, 0.12);
+
+          &:hover {
+            border-color: #3a8fd4;
+            background: rgba(10, 132, 255, 0.1);
+          }
+        }
+        .model-popup {
+          position: absolute;
+          bottom: 38px;
+          left: 50%;
+          transform: translateX(-50%);
+          background: #0a2a3f;
+          border: 1px solid #1a4a6f;
+          border-radius: 8px;
+          min-width: 220px;
+          z-index: 1000;
+          box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
+
+          .model-popup-item {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            padding: 10px 12px;
+            cursor: pointer;
+            transition: background 0.2s;
+            border-radius: 6px;
+
+            &:hover {
+              background: rgba(10, 132, 255, 0.15);
+            }
+
+            &.active {
+              background: rgba(10, 132, 255, 0.1);
+            }
+
+            .model-item-main {
+              display: flex;
+              flex-direction: column;
+              gap: 2px;
+
+              .model-item-label {
+                font-size: 13px;
+                color: #e0e6ed;
+                white-space: nowrap;
+              }
+
+              .model-item-desc {
+                font-size: 11px;
+                color: #8b949e;
+                white-space: nowrap;
+              }
+            }
+
+            .model-item-check {
+              width: 16px;
+              height: 16px;
+              flex-shrink: 0;
+              display: flex;
+              align-items: center;
+              justify-content: center;
+              visibility: hidden;
+
+              &.visible {
+                visibility: visible;
+              }
+
+              &::after {
+                content: '✓';
+                font-size: 12px;
+                color: #3a8fd4;
+              }
+            }
+          }
+        }
+        .btn-think-wrapper {
+          position: relative;
+          display: flex;
+          align-items: center;
+        }
+        .btn-think {
+          height: 28px;
+          padding: 0 10px;
+          border-radius: 6px;
+          display: flex;
+          align-items: center;
+          gap: 4px;
+          cursor: pointer;
+          transition: all 0.2s;
+          border: 1px solid rgba(255, 255, 255, 0.12);
+
+          &:hover {
+            border-color: #3a8fd4;
+            background: rgba(10, 132, 255, 0.1);
+          }
+        }
+        .think-popup {
+          position: absolute;
+          bottom: 38px;
+          left: 50%;
+          transform: translateX(-50%);
+          background: #0a2a3f;
+          border: 1px solid #1a4a6f;
+          border-radius: 8px;
+          min-width: 200px;
+          z-index: 1000;
+          box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
+
+          .think-popup-item {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            padding: 10px 12px;
+            cursor: pointer;
+            transition: background 0.2s;
+            border-radius: 6px;
+
+            &:hover {
+              background: rgba(10, 132, 255, 0.15);
+            }
+
+            &.active {
+              background: rgba(10, 132, 255, 0.1);
+            }
+
+            .think-item-main {
+              display: flex;
+              flex-direction: column;
+              gap: 2px;
+
+              .think-item-label {
+                font-size: 13px;
+                color: #e0e6ed;
+                white-space: nowrap;
+              }
+
+              .think-item-desc {
+                font-size: 11px;
+                color: #8b949e;
+                white-space: nowrap;
+              }
+            }
+
+            .think-item-check {
+              width: 16px;
+              height: 16px;
+              flex-shrink: 0;
+              display: flex;
+              align-items: center;
+              justify-content: center;
+              visibility: hidden;
+
+              &.visible {
+                visibility: visible;
+              }
+
+              &::after {
+                content: '✓';
+                font-size: 12px;
+                color: #3a8fd4;
+              }
+            }
+          }
+        }
         .btn-send {
           width: 32px;
           height: 32px;

+ 12 - 18
src/views/ventAI/manageAssistent/components/chatModal/ChatMessages.vue

@@ -8,9 +8,6 @@
       <div v-for="(message, index) in messages" :key="index" :class="['message-wrapper', message.type]" @mouseleave="copiedMessageIndex = -1">
         <!-- AI消息 -->
         <div v-if="message.type === 'ai'" class="ai-message">
-          <!-- <div class="message-agent">
-            <div class="agent-icon"></div>
-          </div> -->
           <div class="message-body">
             <!-- 思考过程折叠卡片 -->
             <div v-if="message.thinkingSteps && message.thinkingSteps.length > 0" class="thinking-card">
@@ -170,6 +167,8 @@
   import dayjs from 'dayjs';
   import { renderMarkdown, formatFileSize, tableHtmlToMarkdown, tableHtmlToCsv } from './utils';
   import type { Message, AttachedFile, ThinkingStep } from './types';
+  // import katex from 'katex';
+  // import 'katex/dist/katex.min.css';
 
   const getCssUrl = (varName: string) => {
     const val = getComputedStyle(document.documentElement).getPropertyValue(varName).trim();
@@ -452,19 +451,14 @@
           margin-bottom: 8px;
           opacity: 0;
 
-          .agent-icon {
-            width: 40px;
-            height: 40px;
-            background-image: var(--img-chat-ai-avatar);
-            background-repeat: no-repeat;
-            background-size: 100% 100%;
-          }
         }
 
         .message-body {
           padding: 12px;
           border-radius: 6px;
           width: calc(100% - 50px);
+          min-width: 0;
+          overflow: hidden;
 
           /* 思考过程卡片 */
           .thinking-card {
@@ -702,6 +696,8 @@
             font-weight: 400;
             line-height: 1.6;
             color: #e0e6ed;
+            word-break: break-word;
+            overflow-wrap: break-word;
 
             :deep(h1),
             :deep(h2),
@@ -762,6 +758,8 @@
             :deep(.markdown-table-wrapper) {
               position: relative;
               margin: 6px 0;
+              max-width: 100%;
+              overflow-x: auto;
 
               .table-actions {
                 position: absolute;
@@ -848,14 +846,6 @@
           display: flex;
           margin-bottom: 8px;
           margin-left: 5px;
-
-          .user-icon {
-            width: 40px;
-            height: 40px;
-            background-image: var(--img-chat-collapse-bg);
-            background-repeat: no-repeat;
-            background-size: 100% 100%;
-          }
         }
 
         .message-body {
@@ -863,12 +853,16 @@
           padding: 12px;
           border-radius: 6px 0 6px 6px;
           max-width: 700px;
+          min-width: 0;
+          overflow: hidden;
 
           .message-text {
             font-size: 14px;
             font-weight: 400;
             line-height: 1.6;
             color: #e0e6ed;
+            word-break: break-word;
+            overflow-wrap: break-word;
 
             :deep(h1, h2, h3, h4, h5, h6) {
               color: #fff;

+ 108 - 21
src/views/ventAI/manageAssistent/components/chatModal/TaskListPanel.vue

@@ -9,7 +9,22 @@
         <a-button size="small" type="primary">{{ item.label }}</a-button>
       </div>
     </div>
-
+    <!-- 置顶任务区域 -->
+    <div v-if="pinnedTaskList.length > 0" class="pinned-task-list">
+      <div
+        v-for="(task, index) in pinnedTaskList"
+        :key="'pinned-' + task.id"
+        :class="['task-item', { active: currentTaskId === task.id }]"
+        @click="emit('switch-task', task.id)"
+      >
+        <div class="task-pin-icon pinned" @click.stop="togglePin(task.id)" title="取消置顶"></div>
+        <span class="task-name">{{ task.name }}</span>
+        <div class="task-right">
+          <span class="task-time">{{ formatRelativeTime(task.updatedAt) }}</span>
+          <div class="task-delete" @click.stop="emit('delete-task', task, index)" title="删除"></div>
+        </div>
+      </div>
+    </div>
     <!-- 搜索模式 -->
     <div v-if="activeOption === 'search'" class="search-header">
       <a-input v-model:value="searchKeyword" placeholder="搜索任务名称..." allow-clear size="small" class="search-input" />
@@ -22,22 +37,21 @@
         :class="['task-item', { active: currentTaskId === task.id }]"
         @click="emit('switch-task', task.id)"
       >
+        <div class="task-pin-icon" @click.stop="togglePin(task.id)" title="置顶"></div>
         <span class="task-name">{{ task.name }}</span>
         <div class="task-right">
           <span class="task-time">{{ formatRelativeTime(task.updatedAt) }}</span>
-          <div class="task-delete" @click.stop="emit('delete-task', task, index)">
-            <DeleteOutlined />
-          </div>
+          <div class="task-delete" @click.stop="emit('delete-task', task, index)" title="删除"></div>
         </div>
       </div>
       <div v-if="activeOption === 'search' && searchKeyword && filteredTaskList.length === 0" class="empty-hint"> 未找到匹配的任务 </div>
+      <div v-else-if="filteredTaskList.length === 0" class="empty-hint"> 还没有任务 </div>
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
   import { ref, computed } from 'vue';
-  import { DeleteOutlined } from '@ant-design/icons-vue';
   import type { Task } from './types';
 
   const props = defineProps<{
@@ -55,6 +69,44 @@
 
   const activeOption = ref<OptionType>('add');
   const searchKeyword = ref('');
+  const PINNED_TASK_KEY = 'chat_pinned_tasks';
+  const pinnedTaskIds = ref<string[]>(loadPinnedTasks());
+
+  function loadPinnedTasks(): string[] {
+    try {
+      const cached = localStorage.getItem(PINNED_TASK_KEY);
+      return cached ? JSON.parse(cached) : [];
+    } catch {
+      return [];
+    }
+  }
+
+  function savePinnedTasks(ids: string[]) {
+    localStorage.setItem(PINNED_TASK_KEY, JSON.stringify(ids));
+  }
+
+  const pinnedTaskList = computed(() => {
+    return props.taskList.filter((task) => pinnedTaskIds.value.includes(task.id));
+  });
+
+  const filteredTaskList = computed(() => {
+    let list = props.taskList.filter((task) => !pinnedTaskIds.value.includes(task.id));
+    if (activeOption.value === 'search' && searchKeyword.value.trim()) {
+      const keyword = searchKeyword.value.trim().toLowerCase();
+      list = list.filter((task) => task.name.toLowerCase().includes(keyword));
+    }
+    return list;
+  });
+
+  const togglePin = (taskId: string) => {
+    const index = pinnedTaskIds.value.indexOf(taskId);
+    if (index >= 0) {
+      pinnedTaskIds.value.splice(index, 1);
+    } else {
+      pinnedTaskIds.value.push(taskId);
+    }
+    savePinnedTasks(pinnedTaskIds.value);
+  };
 
   const optionItems = [
     { type: 'add' as OptionType, label: '新建任务' },
@@ -62,13 +114,6 @@
     { type: 'skill' as OptionType, label: '技能' },
   ];
 
-  const filteredTaskList = computed(() => {
-    if (activeOption.value !== 'search' || !searchKeyword.value.trim()) {
-      return props.taskList;
-    }
-    const keyword = searchKeyword.value.trim().toLowerCase();
-    return props.taskList.filter((task) => task.name.toLowerCase().includes(keyword));
-  });
 
   const formatRelativeTime = (dateStr?: string) => {
     if (!dateStr) return '刚刚';
@@ -95,7 +140,7 @@
 
 <style scoped lang="less">
   .task-list-panel {
-    width: 260px;
+    width: 100%;
     height: 100%;
     display: flex;
     flex-direction: column;
@@ -122,15 +167,26 @@
       }
     }
 
+    .pinned-task-list {
+      max-height: 180px;
+      overflow-y: auto;
+      border-bottom: 1px solid rgba(63, 80, 106, 0.3);
+      margin-bottom: 8px;
+      padding-bottom: 8px;
+    }
+
     .task-list {
       flex: 1;
       overflow-y: auto;
+    }
 
+    .pinned-task-list,
+    .task-list {
       .task-item {
         height: 36px;
         display: flex;
         align-items: center;
-        gap: 10px;
+        gap: 4px;
         padding: 4px;
         cursor: pointer;
         transition: all 0.3s;
@@ -149,6 +205,31 @@
           color: #e0e6ed;
         }
 
+        .task-pin-icon {
+          width: 28px;
+          height: 28px;
+          flex-shrink: 0;
+          background-image: var(--img-chat-pin-icon);
+          background-repeat: no-repeat;
+          background-size: 16px 16px;
+          background-position: center;
+          opacity: 0;
+          transition: opacity 0.2s;
+          border-radius: 4px;
+
+          &.pinned {
+            opacity: 1;
+          }
+
+          &:hover {
+            background-color: rgba(10, 132, 255, 0.2);
+          }
+        }
+
+        &:hover .task-pin-icon {
+          opacity: 1;
+        }
+
         .task-icon {
           width: 16px;
           height: 16px;
@@ -168,6 +249,13 @@
           flex: 1;
         }
 
+        .task-right {
+          display: flex;
+          align-items: center;
+          gap: 6px;
+          flex-shrink: 0;
+        }
+
         .task-time {
           font-size: 12px;
           color: #8b949e;
@@ -180,16 +268,15 @@
           width: 28px;
           height: 28px;
           display: none;
-          align-items: center;
-          justify-content: center;
-          border-radius: 4px;
-          color: #8b949e;
-          font-size: 14px;
+          background-image: var(--img-chat-delete-icon);
+          background-repeat: no-repeat;
+          background-size: 16px 16px;
+          background-position: center;
           transition: all 0.2s;
+          border-radius: 4px;
 
           &:hover {
-            color: #ff4d4f;
-            background: rgba(220, 38, 38, 0.1);
+            background-color: rgba(10, 132, 255, 0.2);
           }
         }
 

+ 24 - 1
src/views/ventAI/manageAssistent/components/chatModal/utils.ts

@@ -1,9 +1,32 @@
 import { marked } from 'marked';
+import katex from 'katex';
+
+const renderLatexInHtml = (html: string): string => {
+  // 匹配块级公式($$...$$)
+  html = html.replace(/\$\$(.*?)\$\$/gs, (_match, formula) => {
+    return katex.renderToString(formula.trim(), {
+      displayMode: true,
+      throwOnError: false,
+      strict: false,
+      trust: true,
+    });
+  });
+  // 匹配行内公式($...$)
+  html = html.replace(/\$(.*?)\$/g, (_match, formula) => {
+    return katex.renderToString(formula.trim(), {
+      displayMode: false,
+      throwOnError: false,
+      strict: false,
+    });
+  });
+  return html;
+};
 
 export const renderMarkdown = (text: string, icons?: { copy: string; download: string; preview: string; copySuccess: string }): string => {
   if (!text) return '';
   const processed = text.replace(/<br\s*\/?>/gi, '  \n');
-  const html = marked.parse(processed) as string;
+  let html = marked.parse(processed) as string;
+  html = renderLatexInHtml(html);
   return icons ? wrapTables(html, icons) : html;
 };