Browse Source

添加ai编程

一只小菜杨 3 weeks ago
parent
commit
cc2476e521
6 changed files with 172 additions and 43 deletions
  1. 2 2
      src/App.vue
  2. 2 3
      src/stores/global.ts
  3. 9 2
      src/types/global.d.ts
  4. 153 28
      src/views/AIGenerate.vue
  5. 5 7
      src/views/Automate.vue
  6. 1 1
      src/views/ProjectInfo.vue

+ 2 - 2
src/App.vue

@@ -2,7 +2,7 @@
   <div class="page-main" :class="fullScreen.includes(route.path) ? 'full-screen' : ''">
     <router-view></router-view>
   </div>
-  <van-tabbar v-model="tabActive" active-color="#4fb5f9" inactive-color="#333" v-if="route.path !== '/runtime'">
+  <van-tabbar v-model="tabActive" active-color="#4fb5f9" inactive-color="#333" v-if="!fullScreen.includes(route.path)">
     <van-tabbar-item name="home" replace to="/home" icon="wap-home-o">首页</van-tabbar-item>
     <van-tabbar-item name="automate" replace to="/automate" icon="star-o">自动化</van-tabbar-item>
   </van-tabbar>
@@ -23,7 +23,7 @@ watch(
   { immediate: true }
 )
 
-const fullScreen = ['/runtime']
+const fullScreen = ['/runtime', '/ai-generate']
 
 const init = async () => {
   if (!window.isInWx) {

+ 2 - 3
src/stores/global.ts

@@ -9,8 +9,6 @@ export const useGlobalStore = defineStore(
         name: '工程1',
         id: Date.now() + '',
         time: new Date().toLocaleString(),
-        type: 'default',
-        prompt: '',
         commandList: [
           {
             name: '任务1',
@@ -70,7 +68,8 @@ export const useGlobalStore = defineStore(
           }
         ],
         isCompiled: false,
-        CompileTime: ''
+        CompileTime: '',
+        msgList: []
       }
     ])
 

+ 9 - 2
src/types/global.d.ts

@@ -29,13 +29,20 @@ export type CommandType = {
   stepList: StepType[]
 }
 
+export type MsgItem = {
+  role: 'user' | 'assistant'
+  content: string
+  session_id?: string
+  timestamp?: number
+  result?: string
+}
+
 export type ProjectType = {
   name: string
   id: string
   time: string
-  type: string
-  prompt: string
   isCompiled: boolean
   CompileTime: string
   commandList: CommandType[]
+  msgList: MsgItem[]
 }

+ 153 - 28
src/views/AIGenerate.vue

@@ -5,20 +5,34 @@
         <van-icon name="arrow-left" size="16" />
       </div>
 
-      <van-button type="primary" size="small" @click="generateProject">开始AI生成工程</van-button>
+      <div class="clear-btn" @click="clearMsg">清空</div>
     </div>
 
-    <van-field
-      v-model="msg"
-      rows="8"
-      autosize
-      type="textarea"
-      maxlength="500"
-      placeholder="请输入您要编程的内容"
-      show-word-limit
-    />
-
-    <div class="tip">新生成的工程会覆盖原工程,请做好保存。</div>
+    <div class="msg-list" ref="msgListRef">
+      <div class="msg-item" v-for="item in crtProject?.msgList" :class="item.role === 'user' ? 'r' : 'l'">
+        {{ item.content }}
+      </div>
+    </div>
+    <div class="chat-box">
+      <div class="toolbar">
+        <div class="deep-think" @click="deepThink">深度思考</div>
+      </div>
+      <div class="input-box">
+        <van-cell-group inset>
+          <van-field
+            v-model="msg"
+            rows="1"
+            autosize
+            type="textarea"
+            maxlength="500"
+            placeholder="请输入您要编程的内容"
+          />
+        </van-cell-group>
+        <div class="send-btn" @click="generateProject">
+          <van-icon size="32" name="upgrade" color="#1989fa" />
+        </div>
+      </div>
+    </div>
   </div>
 </template>
 
@@ -38,8 +52,8 @@ watch(
   () => route,
   () => {
     crtProject.value = projectList.value.find((item) => item.id === route.query.id)
-    if (crtProject.value?.type === 'AI') {
-      msg.value = crtProject.value.prompt
+    if (!crtProject.value?.msgList) {
+      crtProject.value!.msgList = []
     }
   },
   {
@@ -49,36 +63,75 @@ watch(
   }
 )
 
+const msgListRef = ref<HTMLDivElement>()
+
+watch(crtProject.value!.msgList, () => {
+  nextTick(() => {
+    msgListRef.value?.scrollTo({
+      top: msgListRef.value?.scrollHeight,
+      behavior: 'smooth'
+    })
+  })
+})
+
+const clearMsg = () => {
+  crtProject.value!.msgList = []
+}
+
+const deepThink = () => {
+  showSuccessToast('敬请期待')
+}
+
 const generateProject = async () => {
+  if (!msg.value) return
   const loading = showLoadingToast({
     message: '正在生成中...',
     forbidClick: true,
     duration: 0
   })
-  let prompt = ''
-  if (crtProject.value?.type === 'default') {
-    prompt = `这是当前工程的内容,请根据这个内容重新生成工程:${JSON.stringify(crtProject.value)},并满足以下要求:`
+  crtProject.value?.msgList.push({
+    role: 'user',
+    content: msg.value
+  })
+  const params: any = {}
+  for (let i = crtProject.value!.msgList.length - 1; i >= 0; i--) {
+    if (crtProject.value!.msgList[i].role === 'assistant' && crtProject.value!.msgList[i].session_id !== '') {
+      const timeDiff = Date.now() - crtProject.value!.msgList[i].timestamp!
+      if (timeDiff >= 60 * 60 * 1000) {
+        // 超时
+        params.messages = crtProject.value!.msgList.map((item) => {
+          return { role: item.role, content: item.role === 'user' ? item.content : item.result }
+        })
+      } else {
+        params.session_id = crtProject.value!.msgList[i].session_id
+        params.prompt = msg.value
+      }
+      break
+    }
   }
-
-  const res = await fetch('http://127.0.0.1:3000/api/chat', {
+  msg.value = ''
+  const res = await fetch('https://plceditor.worldflying.cn/api/ai/chat', {
     method: 'POST',
     headers: {
       'Content-Type': 'application/json'
     },
-    body: JSON.stringify({
-      msg: prompt + msg.value
-    })
+    body: JSON.stringify(params)
   }).then((res) => res.json())
   console.log(res)
   if (res.errcode === '1001') {
     showFailToast('操作失败!')
   } else {
-    if (res.data === '请正确描述您要编程的内容') {
+    if (res.output.text === '请正确描述您要编程的内容。' || res.output.text === '请正确描述您要编程的内容') {
       loading.close()
-      showFailToast('请正确描述您要编程的内容')
+      // showFailToast('请正确描述您要编程的内容')
+      crtProject.value!.msgList.push({
+        role: 'assistant',
+        content: '请正确描述您要编程的内容',
+        result: res.output.text
+      })
     } else {
       try {
-        let jsonData = res.data
+        let jsonData = res.output.text
         if (jsonData.slice(0, 8).includes('json')) {
           jsonData = jsonData.slice(8).slice(0, -3)
         }
@@ -88,13 +141,23 @@ const generateProject = async () => {
         console.log(project)
         // console.log(p)
         crtProject.value!.commandList = project.commandList
-        crtProject.value!.prompt = msg.value
-
+        crtProject.value!.msgList.push({
+          role: 'assistant',
+          content: '生成成功,请返回工程界面查看生成结果。',
+          session_id: res.output.session_id,
+          timestamp: Date.now(),
+          result: res.output.text
+        })
         loading.close()
         showSuccessToast('操作成功!')
       } catch (error) {
         loading.close()
-        showFailToast('操作失败!')
+        // showFailToast('操作失败!')
+        crtProject.value!.msgList.push({
+          role: 'assistant',
+          content: '生成失败。',
+          result: res.output.text
+        })
       }
     }
   }
@@ -105,6 +168,7 @@ const generateProject = async () => {
 .ai-main {
   height: 100%;
   background: #fff;
+  overflow: hidden;
 }
 .header {
   display: flex;
@@ -116,4 +180,65 @@ const generateProject = async () => {
   padding: 16px;
   color: #ccc;
 }
+.clear-btn {
+  color: #1989fa;
+}
+
+.msg-list {
+  padding: 16px;
+  height: calc(100% - 172px);
+  overflow: auto;
+}
+.msg-list .msg-item {
+  padding: 12px;
+  margin-bottom: 12px;
+  border-radius: 8px;
+  background: #f5f5f5;
+  max-width: 80%;
+  width: max-content;
+  line-height: 1;
+}
+.msg-list .msg-item.r {
+  background: #1989fa;
+  color: #fff;
+  margin-left: auto;
+}
+.msg-list .msg-item.l {
+  color: #333;
+  margin-right: auto;
+}
+
+.chat-box {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  background-color: #fff;
+}
+.chat-box .toolbar {
+  padding: 8px 16px 0;
+}
+.chat-box .toolbar .deep-think {
+  border: 1px solid #ccc;
+  border-radius: 8px;
+  font-size: 14px;
+  width: max-content;
+  padding: 8px;
+  box-shadow: 0 0 1px #ddd;
+  line-height: 1;
+}
+.chat-box .input-box {
+  margin: 12px 16px;
+  padding: 4px;
+  display: flex;
+  gap: 8px;
+  align-items: center;
+  box-shadow: 0 0 4px 2px #ddd;
+  border-radius: 16px;
+  height: auto;
+}
+.chat-box .input-box .van-cell-group {
+  flex: 1;
+  margin: 0;
+}
 </style>

+ 5 - 7
src/views/Automate.vue

@@ -45,7 +45,7 @@
 
     <van-popup v-model:show="popupShow" round :style="{ width: '80%' }">
       <div class="label" style="margin: 16px">新建工程</div>
-      <van-form ref="formRef">
+      <van-form ref="formRef" @submit="onSubmit">
         <van-cell-group inset>
           <van-field
             border
@@ -58,8 +58,7 @@
         </van-cell-group>
         <div class="van-button-group">
           <van-button @click="handleCancel" size="small">取消</van-button>
-          <van-button type="primary" @click="submitForm('AI')" size="small">AI编程</van-button>
-          <van-button type="primary" @click="submitForm('default')" size="small">手动编程</van-button>
+          <van-button type="primary" native-type="submit" size="small">确定</van-button>
         </div>
       </van-form>
     </van-popup>
@@ -113,7 +112,7 @@ const handleDelete = (val: string[]) => {
 const popupShow = ref(false)
 const newProjectName = ref('')
 const formRef = ref<FormInstance>()
-const submitForm = (type: string) => {
+const onSubmit = () => {
   formRef.value?.submit()
   const isExist = projectList.value.some((item) => item.name === newProjectName.value)
   if (isExist) {
@@ -124,8 +123,6 @@ const submitForm = (type: string) => {
     projectList.value.unshift({
       name: newProjectName.value,
       id,
-      type,
-      prompt: '',
       time: new Date().toLocaleString(),
       commandList: [
         {
@@ -139,7 +136,8 @@ const submitForm = (type: string) => {
         }
       ],
       isCompiled: false,
-      CompileTime: ''
+      CompileTime: '',
+      msgList: []
     })
     handleCancel()
   }

+ 1 - 1
src/views/ProjectInfo.vue

@@ -41,7 +41,7 @@
         <van-cell-group :style="{ marginTop: '16px' }">
           <van-cell title="编辑工程名称" clickable @click="handleEditName" :title-style="{ color: '#07c160' }" />
           <van-cell
-            title="AI修改编程内容"
+            title="AI编程"
             clickable
             @click="router.push({ path: '/ai-generate', query: { id: crtProject?.id } })"
             :title-style="{ color: '#07c160' }"