AIGenerate.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. <template>
  2. <div class="ai-main">
  3. <div class="header">
  4. <div class="go-back" @click="router.push(`/project-info/${crtProject!.id}`)">
  5. <van-icon name="arrow-left" size="16" />
  6. </div>
  7. <div class="clear-btn" @click="clearMsg">清空</div>
  8. </div>
  9. <div class="msg-list" ref="msgListRef">
  10. <div class="msg-item" v-for="item in crtProject?.msgList" :class="item.role === 'user' ? 'r' : 'l'">
  11. {{ item.content }}
  12. </div>
  13. </div>
  14. <div class="chat-box">
  15. <div class="toolbar">
  16. <div class="deep-think" @click="deepThink">深度思考</div>
  17. </div>
  18. <div class="input-box">
  19. <van-cell-group inset>
  20. <van-field
  21. v-model="msg"
  22. rows="1"
  23. autosize
  24. type="textarea"
  25. maxlength="500"
  26. placeholder="请输入您要编程的内容"
  27. />
  28. </van-cell-group>
  29. <div class="send-btn" @click="generateProject">
  30. <van-icon size="32" name="upgrade" color="#1989fa" />
  31. </div>
  32. </div>
  33. </div>
  34. </div>
  35. </template>
  36. <script setup lang="ts">
  37. import type { ProjectType } from '@/types/global'
  38. import { useGlobalStore } from '@/stores/global'
  39. import { storeToRefs } from 'pinia'
  40. import { showLoadingToast } from 'vant'
  41. const { projectList } = storeToRefs(useGlobalStore())
  42. const route = useRoute()
  43. const router = useRouter()
  44. const crtProject = ref<ProjectType>()
  45. const msg = ref('')
  46. watch(
  47. () => route,
  48. () => {
  49. crtProject.value = projectList.value.find((item) => item.id === route.query.id)
  50. if (!crtProject.value?.msgList) {
  51. crtProject.value!.msgList = []
  52. }
  53. },
  54. {
  55. deep: true,
  56. immediate: true,
  57. once: true
  58. }
  59. )
  60. const msgListRef = ref<HTMLDivElement>()
  61. watch(crtProject.value!.msgList, () => {
  62. nextTick(() => {
  63. msgListRef.value?.scrollTo({
  64. top: msgListRef.value?.scrollHeight,
  65. behavior: 'smooth'
  66. })
  67. })
  68. })
  69. const clearMsg = () => {
  70. crtProject.value!.msgList = []
  71. }
  72. const deepThink = () => {
  73. showSuccessToast('敬请期待')
  74. }
  75. const generateProject = async () => {
  76. if (!msg.value) return
  77. const loading = showLoadingToast({
  78. message: '正在生成中...',
  79. forbidClick: true,
  80. duration: 0
  81. })
  82. const params: any = {}
  83. params.prompt = msg.value
  84. for (let i = crtProject.value!.msgList.length - 1; i >= 0; i--) {
  85. if (crtProject.value!.msgList[i].role === 'assistant' && crtProject.value!.msgList[i].session_id !== '') {
  86. const timeDiff = Date.now() - crtProject.value!.msgList[i].timestamp!
  87. if (timeDiff >= 60 * 60 * 1000) {
  88. // 超时
  89. params.messages = crtProject.value!.msgList.map((item) => {
  90. return { role: item.role, content: item.role === 'user' ? item.content : item.result }
  91. })
  92. } else {
  93. params.session_id = crtProject.value!.msgList[i].session_id
  94. }
  95. break
  96. }
  97. }
  98. crtProject.value?.msgList.push({
  99. role: 'user',
  100. content: msg.value
  101. })
  102. msg.value = ''
  103. const res = await fetch('https://plceditor.worldflying.cn/api/ai/chat', {
  104. method: 'POST',
  105. headers: {
  106. 'Content-Type': 'application/json'
  107. },
  108. body: JSON.stringify(params)
  109. }).then((res) => res.json())
  110. console.log(res)
  111. if (res.errcode === 0) {
  112. if (res.data.output.text === '请正确描述您要编程的内容。' || res.data.output.text === '请正确描述您要编程的内容') {
  113. loading.close()
  114. // showFailToast('请正确描述您要编程的内容')
  115. crtProject.value!.msgList.push({
  116. role: 'assistant',
  117. content: '请正确描述您要编程的内容',
  118. result: res.data.output.text
  119. })
  120. } else {
  121. try {
  122. let jsonData = res.data.output.text
  123. if (jsonData.slice(0, 8).includes('json')) {
  124. jsonData = jsonData.slice(8).slice(0, -3)
  125. }
  126. console.log(jsonData)
  127. const project = JSON.parse(jsonData)
  128. console.log(project)
  129. // console.log(p)
  130. crtProject.value!.commandList = project.commandList
  131. crtProject.value!.msgList.push({
  132. role: 'assistant',
  133. content: '生成成功,请返回工程界面查看生成结果。',
  134. session_id: res.data.output.session_id,
  135. timestamp: Date.now(),
  136. result: res.data.output.text
  137. })
  138. loading.close()
  139. showSuccessToast('操作成功!')
  140. } catch (error) {
  141. loading.close()
  142. // showFailToast('操作失败!')
  143. crtProject.value!.msgList.push({
  144. role: 'assistant',
  145. content: '生成失败。',
  146. result: res.data.output.text
  147. })
  148. }
  149. }
  150. } else {
  151. showFailToast('操作失败!')
  152. }
  153. }
  154. </script>
  155. <style scoped>
  156. .ai-main {
  157. height: 100%;
  158. background: #fff;
  159. overflow: hidden;
  160. }
  161. .header {
  162. display: flex;
  163. justify-content: space-between;
  164. align-items: center;
  165. padding: 16px;
  166. }
  167. .tip {
  168. padding: 16px;
  169. color: #ccc;
  170. }
  171. .clear-btn {
  172. color: #1989fa;
  173. }
  174. .msg-list {
  175. padding: 16px;
  176. height: calc(100% - 172px);
  177. overflow: auto;
  178. }
  179. .msg-list .msg-item {
  180. padding: 12px;
  181. margin-bottom: 12px;
  182. border-radius: 8px;
  183. background: #f5f5f5;
  184. max-width: 80%;
  185. width: max-content;
  186. line-height: 1;
  187. }
  188. .msg-list .msg-item.r {
  189. background: #1989fa;
  190. color: #fff;
  191. margin-left: auto;
  192. }
  193. .msg-list .msg-item.l {
  194. color: #333;
  195. margin-right: auto;
  196. }
  197. .chat-box {
  198. position: fixed;
  199. bottom: 0;
  200. left: 0;
  201. width: 100%;
  202. background-color: #fff;
  203. }
  204. .chat-box .toolbar {
  205. padding: 8px 16px 0;
  206. }
  207. .chat-box .toolbar .deep-think {
  208. border: 1px solid #ccc;
  209. border-radius: 8px;
  210. font-size: 14px;
  211. width: max-content;
  212. padding: 8px;
  213. box-shadow: 0 0 1px #ddd;
  214. line-height: 1;
  215. }
  216. .chat-box .input-box {
  217. margin: 12px 16px;
  218. padding: 4px;
  219. display: flex;
  220. gap: 8px;
  221. align-items: center;
  222. box-shadow: 0 0 4px 2px #ddd;
  223. border-radius: 16px;
  224. height: auto;
  225. }
  226. .chat-box .input-box .van-cell-group {
  227. flex: 1;
  228. margin: 0;
  229. }
  230. </style>