lamphua 3 дней назад
Родитель
Сommit
276eea2745
24 измененных файлов: 1207 добавлений и 411 удалений
  1. 3
    0
      oa-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
  2. 5
    5
      oa-back/ruoyi-agent/src/main/java/com/ruoyi/agent/service/impl/McpServiceImpl.java
  3. 1
    1
      oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/McpController.java
  4. 60
    29
      oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/SessionController.java
  5. 15
    9
      oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/service/impl/LangChainMilvusServiceImpl.java
  6. 4
    4
      oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/service/impl/MilvusServiceImpl.java
  7. 5
    5
      oa-back/ruoyi-system/src/main/java/com/ruoyi/llm/service/impl/CmcAgentServiceImpl.java
  8. 1
    1
      oa-ui-app/api/qywx/index.js
  9. 87
    31
      oa-ui-app/pages/form/borrow/borrow.vue
  10. 65
    16
      oa-ui-app/pages/form/car/car.vue
  11. 112
    45
      oa-ui-app/pages/form/device/device.vue
  12. 5
    1
      oa-ui-app/pages/work/index.vue
  13. Двоичные данные
      oa-ui-app/static/images/female.png
  14. 6
    2
      oa-ui-app/store/modules/user.js
  15. 19
    13
      oa-ui/src/api/llm/rag.js
  16. 257
    0
      oa-ui/src/api/llm/session.js
  17. 24
    0
      oa-ui/src/views/flowable/form/changeForm.vue
  18. 118
    86
      oa-ui/src/views/flowable/form/finance/borrowForm.vue
  19. 46
    16
      oa-ui/src/views/flowable/form/oa/carForm.vue
  20. 82
    33
      oa-ui/src/views/flowable/form/oa/deviceForm.vue
  21. 279
    106
      oa-ui/src/views/llm/chat/index.vue
  22. 3
    3
      oa-ui/src/views/llm/knowledge/index.vue
  23. 2
    2
      oa-ui/src/views/system/user/profile/index.vue
  24. 8
    3
      oa-ui/src/views/system/user/profile/userInfo.vue

+ 3
- 0
oa-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java Просмотреть файл

64
         currentUser.setNickName(user.getNickName());
64
         currentUser.setNickName(user.getNickName());
65
         currentUser.setEmail(user.getEmail());
65
         currentUser.setEmail(user.getEmail());
66
         currentUser.setPhonenumber(user.getPhonenumber());
66
         currentUser.setPhonenumber(user.getPhonenumber());
67
+        currentUser.setHomePlace(user.getHomePlace());
68
+        currentUser.setContact(user.getContact());
69
+        currentUser.setTelephone(user.getTelephone());
67
         currentUser.setSex(user.getSex());
70
         currentUser.setSex(user.getSex());
68
         if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser))
71
         if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser))
69
         {
72
         {

+ 5
- 5
oa-back/ruoyi-agent/src/main/java/com/ruoyi/agent/service/impl/McpServiceImpl.java Просмотреть файл

80
         if (milvusServiceUrl == null || milvusServiceUrl.isEmpty()) {
80
         if (milvusServiceUrl == null || milvusServiceUrl.isEmpty()) {
81
             throw new IllegalStateException("Milvus service URL 未配置");
81
             throw new IllegalStateException("Milvus service URL 未配置");
82
         }
82
         }
83
-//        milvusClient = new MilvusClientV2(
84
-//                ConnectConfig.builder()
85
-//                        .uri(milvusServiceUrl)
86
-//                        .build());
83
+        milvusClient = new MilvusClientV2(
84
+                ConnectConfig.builder()
85
+                        .uri(milvusServiceUrl)
86
+                        .build());
87
     }
87
     }
88
     /**
88
     /**
89
      * 调用LLM+RAG(外部文件+知识库)生成回答
89
      * 调用LLM+RAG(外部文件+知识库)生成回答
220
      */
220
      */
221
     public String generateAnswer(String prompt) throws IOException {
221
     public String generateAnswer(String prompt) throws IOException {
222
         ChatModel chatModel = ChatModel.of(llmServiceUrl)
222
         ChatModel chatModel = ChatModel.of(llmServiceUrl)
223
-                .model("Qwen2.5")
223
+                .model("Qwen")
224
                 .build();
224
                 .build();
225
 
225
 
226
         List<ChatMessage> messages = new ArrayList<>();
226
         List<ChatMessage> messages = new ArrayList<>();

+ 1
- 1
oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/McpController.java Просмотреть файл

60
                 .url("http://localhost:8080/mcp/sse")
60
                 .url("http://localhost:8080/mcp/sse")
61
                 .build();
61
                 .build();
62
         ChatModel chatModel = ChatModel.of(llmServiceUrl)
62
         ChatModel chatModel = ChatModel.of(llmServiceUrl)
63
-                .model("Qwen2.5")
63
+                .model("Qwen")
64
                 .defaultToolsAdd(clientProvider)
64
                 .defaultToolsAdd(clientProvider)
65
                 .build();
65
                 .build();
66
 
66
 

+ 60
- 29
oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/SessionController.java Просмотреть файл

17
 import org.reactivestreams.Publisher;
17
 import org.reactivestreams.Publisher;
18
 import org.springframework.beans.factory.annotation.Autowired;
18
 import org.springframework.beans.factory.annotation.Autowired;
19
 import org.springframework.beans.factory.annotation.Value;
19
 import org.springframework.beans.factory.annotation.Value;
20
+import org.springframework.http.MediaType;
20
 import org.springframework.web.bind.annotation.GetMapping;
21
 import org.springframework.web.bind.annotation.GetMapping;
21
 import org.springframework.web.bind.annotation.RequestMapping;
22
 import org.springframework.web.bind.annotation.RequestMapping;
22
 import org.springframework.web.bind.annotation.RestController;
23
 import org.springframework.web.bind.annotation.RestController;
23
 import reactor.core.publisher.Flux;
24
 import reactor.core.publisher.Flux;
24
 
25
 
26
+import javax.annotation.PostConstruct;
25
 import java.io.*;
27
 import java.io.*;
26
 import java.util.ArrayList;
28
 import java.util.ArrayList;
27
 import java.util.List;
29
 import java.util.List;
45
     @Value("${llmService.url}")
47
     @Value("${llmService.url}")
46
     private String llmServiceUrl;
48
     private String llmServiceUrl;
47
 
49
 
48
-    /**
49
-     * 生成回答
50
-     */
51
-    @GetMapping("/answer")
52
-    public Flux<AssistantMessage> answer(String topicId, String question) throws IOException {
53
-        McpClientProvider clientProvider = McpClientProvider.builder()
50
+    // 复用 ChatModel 实例
51
+    private ChatModel chatModel;
52
+    private McpClientProvider clientProvider;
53
+
54
+    @PostConstruct
55
+    public void init() {
56
+        // 初始化 McpClientProvider
57
+        clientProvider = McpClientProvider.builder()
54
                 .channel(McpChannel.SSE)
58
                 .channel(McpChannel.SSE)
55
                 .url("http://localhost:8080/mcp/sse")
59
                 .url("http://localhost:8080/mcp/sse")
56
                 .build();
60
                 .build();
57
-        ChatModel chatModel = ChatModel.of(llmServiceUrl)
58
-                .model("Qwen2.5")
61
+
62
+        // 初始化 ChatModel
63
+        chatModel = ChatModel.of(llmServiceUrl)
64
+                .model("Qwen")
59
                 .defaultToolsAdd(clientProvider)
65
                 .defaultToolsAdd(clientProvider)
60
                 .build();
66
                 .build();
67
+    }
61
 
68
 
69
+    /**
70
+     * 生成回答
71
+     */
72
+    @GetMapping(value = "/answer", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
73
+    public Flux<AssistantMessage> answer(String topicId, String question) throws IOException {
62
         List<ChatMessage> messages = new ArrayList<>();
74
         List<ChatMessage> messages = new ArrayList<>();
63
         CmcChat cmcChat = new CmcChat();
75
         CmcChat cmcChat = new CmcChat();
64
         cmcChat.setTopicId(topicId);
76
         cmcChat.setTopicId(topicId);
96
                 "7. 输出格式:使用[]包含sql文本,例如:[select 1 from dual]\n";
108
                 "7. 输出格式:使用[]包含sql文本,例如:[select 1 from dual]\n";
97
         messages.add(ChatMessage.ofUser(prompt));
109
         messages.add(ChatMessage.ofUser(prompt));
98
         ChatSession chatSession =  InMemoryChatSession.builder().messages(messages).build();
110
         ChatSession chatSession =  InMemoryChatSession.builder().messages(messages).build();
99
-        ChatResponse response = chatModel.prompt(chatSession).call();
100
-        String resultContent = response.lastChoice().getMessage().getResultContent();
101
-
102
-        if (resultContent.contains("<tool_call>")) {
103
-            resultContent = resultContent.split("<tool_call>\n")[1];
104
-            String content = resultContent.replace("\n</tool_call>", "");
105
-            JSONObject jsonObject = JSONObject.parseObject(content);
106
-            String name = jsonObject.getString("name");
107
-            JSONObject arguments = jsonObject.getJSONObject("arguments");
108
-            resultContent = clientProvider.callToolAsText(name, arguments).getContent();
109
-            resultContent = "根据查询结果:\n" +
110
-                    resultContent + "回答问题:\n" +
111
-                    question + " \n";
112
-            return langChainMilvusService.generateAnswer(topicId, resultContent);
113
-        } else {
114
-            // 不需要调用工具,直接返回原始响应,避免重复调用大模型
115
-            // 创建一个包含原始响应内容的 AssistantMessage 并返回 Flux
116
-            AssistantMessage assistantMessage = ChatMessage.ofAssistant(resultContent);
117
-            return Flux.just(assistantMessage);
118
-        }
111
+
112
+        Publisher<ChatResponse> responsePublisher = chatModel.prompt(chatSession).stream();
113
+
114
+        // 收集完整的响应内容,用于判断是否需要工具调用
115
+        StringBuilder resultBuilder = new StringBuilder();
116
+
117
+        // 先创建一个处理器,用于处理流式响应
118
+        Flux<AssistantMessage> flux = Flux.from(responsePublisher)
119
+                .map(chatResponse -> chatResponse.lastChoice().getMessage())
120
+                .doOnNext(assistantMessage -> {
121
+                    String content = assistantMessage.getResultContent();
122
+                    resultBuilder.append(content);
123
+                });
124
+
125
+        // 检查是否需要工具调用(异步处理)
126
+        return flux.thenMany(Flux.defer(() -> {
127
+            String resultContent = resultBuilder.toString();
128
+
129
+            if (resultContent.contains("<tool_call>")) {
130
+                // 需要工具调用,解析工具调用内容
131
+                resultContent = resultContent.split("<tool_call>\n")[1];
132
+                String content = resultContent.replace("\n</tool_call>", "");
133
+                JSONObject jsonObject = JSONObject.parseObject(content);
134
+                String name = jsonObject.getString("name");
135
+                JSONObject arguments = jsonObject.getJSONObject("arguments");
136
+
137
+                // 调用工具
138
+                resultContent = clientProvider.callToolAsText(name, arguments).getContent();
139
+                resultContent = "根据查询结果:\n" +
140
+                        resultContent + "回答问题:\n" +
141
+                        question + " \n";
142
+
143
+                // 再次调用LLM生成最终回答
144
+                return langChainMilvusService.generateAnswer(topicId, resultContent);
145
+            } else {
146
+                // 不需要工具调用,已经通过上面的flux返回了流式内容
147
+                return Flux.just(ChatMessage.ofAssistant(resultContent));
148
+            }
149
+        }));
119
     }
150
     }
120
 
151
 
121
     /**
152
     /**
122
      * 调用LLM+RAG(外部文件)生成回答
153
      * 调用LLM+RAG(外部文件)生成回答
123
      */
154
      */
124
-    @GetMapping("/answerWithDocument")
155
+    @GetMapping(value = "/answerWithDocument", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
125
     public Flux<AssistantMessage> answerWithDocument(String topicId, String chatId, String question) throws Exception
156
     public Flux<AssistantMessage> answerWithDocument(String topicId, String chatId, String question) throws Exception
126
     {
157
     {
127
         return langChainMilvusService.generateAnswerWithDocument(topicId, chatId, question);
158
         return langChainMilvusService.generateAnswerWithDocument(topicId, chatId, question);

+ 15
- 9
oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/service/impl/LangChainMilvusServiceImpl.java Просмотреть файл

74
     private String milvusServiceUrl;
74
     private String milvusServiceUrl;
75
 
75
 
76
     private MilvusClientV2 milvusClient;
76
     private MilvusClientV2 milvusClient;
77
+    
78
+    // 复用 ChatModel 实例
79
+    private ChatModel chatModel;
77
 
80
 
78
     @PostConstruct
81
     @PostConstruct
79
     public void initMilvusClient() {
82
     public void initMilvusClient() {
80
         if (milvusServiceUrl == null || milvusServiceUrl.isEmpty()) {
83
         if (milvusServiceUrl == null || milvusServiceUrl.isEmpty()) {
81
             throw new IllegalArgumentException("milvusServiceUrl 配置不能为空");
84
             throw new IllegalArgumentException("milvusServiceUrl 配置不能为空");
82
         }
85
         }
83
-//        milvusClient = new MilvusClientV2(
84
-//                ConnectConfig.builder()
85
-//                        .uri(milvusServiceUrl)
86
-//                        .build());
86
+        milvusClient = new MilvusClientV2(
87
+                ConnectConfig.builder()
88
+                        .uri(milvusServiceUrl)
89
+                        .build());
90
+    }
91
+    
92
+    @PostConstruct
93
+    public void initChatModel() {
94
+        // 初始化 ChatModel
95
+        chatModel = ChatModel.of(llmServiceUrl)
96
+                .model("Qwen")
97
+                .build();
87
     }
98
     }
88
 
99
 
89
     /**
100
     /**
242
      */
253
      */
243
     @Override
254
     @Override
244
     public Flux<AssistantMessage> generateAnswer(String topicId, String prompt) {
255
     public Flux<AssistantMessage> generateAnswer(String topicId, String prompt) {
245
-        ChatModel chatModel = ChatModel.of(llmServiceUrl)
246
-
247
-                .model("Qwen2.5")
248
-                .build();
249
-
250
         List<ChatMessage> messages = new ArrayList<>();
256
         List<ChatMessage> messages = new ArrayList<>();
251
         if (topicId != null) {
257
         if (topicId != null) {
252
             CmcChat cmcChat = new CmcChat();
258
             CmcChat cmcChat = new CmcChat();

+ 4
- 4
oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/service/impl/MilvusServiceImpl.java Просмотреть файл

34
         if (milvusServiceUrl == null || milvusServiceUrl.isEmpty()) {
34
         if (milvusServiceUrl == null || milvusServiceUrl.isEmpty()) {
35
             throw new IllegalArgumentException("milvusServiceUrl 配置不能为空");
35
             throw new IllegalArgumentException("milvusServiceUrl 配置不能为空");
36
         }
36
         }
37
-//        milvusClient = new MilvusClientV2(
38
-//                ConnectConfig.builder()
39
-//                        .uri(milvusServiceUrl)
40
-//                        .build());
37
+        milvusClient = new MilvusClientV2(
38
+                ConnectConfig.builder()
39
+                        .uri(milvusServiceUrl)
40
+                        .build());
41
     }
41
     }
42
 
42
 
43
     /**
43
     /**

+ 5
- 5
oa-back/ruoyi-system/src/main/java/com/ruoyi/llm/service/impl/CmcAgentServiceImpl.java Просмотреть файл

90
         if (milvusServiceUrl == null || milvusServiceUrl.isEmpty()) {
90
         if (milvusServiceUrl == null || milvusServiceUrl.isEmpty()) {
91
             throw new IllegalArgumentException("milvusServiceUrl 配置不能为空");
91
             throw new IllegalArgumentException("milvusServiceUrl 配置不能为空");
92
         }
92
         }
93
-//        milvusClient = new MilvusClientV2(
94
-//                ConnectConfig.builder()
95
-//                        .uri(milvusServiceUrl)
96
-//                        .build());
93
+        milvusClient = new MilvusClientV2(
94
+                ConnectConfig.builder()
95
+                        .uri(milvusServiceUrl)
96
+                        .build());
97
     }
97
     }
98
 
98
 
99
     /**
99
     /**
644
      */
644
      */
645
     public String generateAnswer(String prompt) throws IOException {
645
     public String generateAnswer(String prompt) throws IOException {
646
         ChatModel chatModel = ChatModel.of(llmServiceUrl)
646
         ChatModel chatModel = ChatModel.of(llmServiceUrl)
647
-                .model("Qwen2.5")
647
+                .model("Qwen")
648
                 .build();
648
                 .build();
649
 
649
 
650
         List<ChatMessage> messages = new ArrayList<>();
650
         List<ChatMessage> messages = new ArrayList<>();

+ 1
- 1
oa-ui-app/api/qywx/index.js Просмотреть файл

3
 // 发送企业微信消息
3
 // 发送企业微信消息
4
 export function sendQyMessage(data) {
4
 export function sendQyMessage(data) {
5
   return request({
5
   return request({
6
-    url: '/qywx/message/send',
6
+    url: '/qyweixin/sendMessage',
7
     method: 'post',
7
     method: 'post',
8
     data: data
8
     data: data
9
   })
9
   })

+ 87
- 31
oa-ui-app/pages/form/borrow/borrow.vue Просмотреть файл

154
 import { listSite, delSite } from "@/api/oa/budget/budgetSite.js";
154
 import { listSite, delSite } from "@/api/oa/budget/budgetSite.js";
155
 import { listBorrow, getBorrow, delBorrow, addBorrow, updateBorrow } from "@/api/oa/borrow/borrow";
155
 import { listBorrow, getBorrow, delBorrow, addBorrow, updateBorrow } from "@/api/oa/borrow/borrow";
156
 import { getUserByPost, getUsersDeptLeader, getUsersDeptLeaderByDept, getUsersViceDeptLeaderByDept, getUsersManageLeaderByDept } from '@/api/system/post.js'
156
 import { getUserByPost, getUsersDeptLeader, getUsersDeptLeaderByDept, getUsersViceDeptLeaderByDept, getUsersManageLeaderByDept } from '@/api/system/post.js'
157
+import { sendQyMessage } from "@/api/qywx/index"
158
+import { listUser, getUser } from "@/api/system/user";
157
 import BorrowDetail from './borrowDetail.vue';
159
 import BorrowDetail from './borrowDetail.vue';
158
 import FlowNote from '@/pages/components/flowNote.vue';
160
 import FlowNote from '@/pages/components/flowNote.vue';
159
 import ReturnPopup from '@/pages/components/returnPopup.vue';
161
 import ReturnPopup from '@/pages/components/returnPopup.vue';
179
     this.deptId = this.$store.getters.deptId;
181
     this.deptId = this.$store.getters.deptId;
180
     this.applierUserName = this.startUserName;
182
     this.applierUserName = this.startUserName;
181
     this.initForm();
183
     this.initForm();
184
+    this.getUserList();
182
     this.initRules();
185
     this.initRules();
183
     uni.setNavigationBarTitle({
186
     uni.setNavigationBarTitle({
184
       title: '借款审批'
187
       title: '借款审批'
194
   },
197
   },
195
   data() {
198
   data() {
196
     return {
199
     return {
200
+      userList: [],
197
       applierUserName: '',
201
       applierUserName: '',
198
       form: {
202
       form: {
199
         submitTime: '',
203
         submitTime: '',
303
       getBorrow(this.taskForm.formId).then(async res => {
307
       getBorrow(this.taskForm.formId).then(async res => {
304
         if (res.data) {
308
         if (res.data) {
305
           this.formTotal = 1;
309
           this.formTotal = 1;
306
-          this.form = res.data;
307
-          if (this.form.applyDept == 104) //董事长分管的综合事务部,跳过分管审批
310
+          this.form = res.data;
311
+          if (this.form.applyDept == 104) //董事长分管的综合事务部,跳过分管审批
308
             this.need = false;
312
             this.need = false;
309
           if (!this.applierUserName) {
313
           if (!this.applierUserName) {
310
             this.applierUserName = this.form.applierUser.nickName;
314
             this.applierUserName = this.form.applierUser.nickName;
328
       })
332
       })
329
 
333
 
330
     },
334
     },
335
+    // 获取用户列表
336
+    getUserList() {
337
+      listUser({
338
+        pageNum: 1,
339
+        pageSize: 9999
340
+      }).then(res => {
341
+        if (res.code == 200) {
342
+          this.userList = res.rows
343
+        }
344
+      })
345
+    },
331
     async getMaxExpense() {
346
     async getMaxExpense() {
332
       let budgetData = await listBudget({ projectId: this.form.projectId })
347
       let budgetData = await listBudget({ projectId: this.form.projectId })
333
       if (budgetData.total == 1) {
348
       if (budgetData.total == 1) {
469
             // 处理不同的流程节点
484
             // 处理不同的流程节点
470
             if (this.taskName == '借款申请') {
485
             if (this.taskName == '借款申请') {
471
               // 处理借款申请节点
486
               // 处理借款申请节点
472
-              await this.borrowApprovalFun();
487
+              let userId = await this.borrowApprovalFun();
488
+              let userIds = [];
489
+              if (userId) {
490
+                userIds.push(userId);
491
+              }
492
+              await this.sendQyMessage(userIds);
473
             } else if (this.taskName == '部门审核') {
493
             } else if (this.taskName == '部门审核') {
474
               this.taskForm.variables.need = this.need;
494
               this.taskForm.variables.need = this.need;
495
+              let userIds = [];
475
               if (this.need) {
496
               if (this.need) {
476
-				  // 得到分管领导根据部门
477
-				  const res = await getUsersManageLeaderByDept({ deptId: this.form.applyDept });
478
-				  let userIds = [];
479
-				  if (res.data) {
480
-					res.data.forEach(item => {
481
-					  userIds.push(item.userId)
482
-					})
483
-				  }
484
-				  this.taskForm.variables.approvalList = userIds;
485
-				  this.handleComplete(this.taskForm);
497
+			  // 得到分管领导根据部门
498
+			  const res = await getUsersManageLeaderByDept({ deptId: this.form.applyDept });
499
+			  if (res.data) {
500
+				res.data.forEach(item => {
501
+				  userIds.push(item.userId)
502
+				})
486
 			  }
503
 			  }
487
-			  else {
488
-				  const res = await getUsersManageLeaderByDept({ deptId: this.form.applyDept });
489
-				  let userIds = [];
490
-				  if (res.data) {
491
-					res.data.forEach(item => {
492
-					  userIds.push(item.userId)
493
-					})
494
-				  }
495
-				  this.taskForm.variables.approvalList = userIds;
496
-				  const response = await getUserByPost({ postName: '总经理' });
497
-				  this.taskForm.variables.approval = response.data[0].userId;
498
-				  this.handleComplete(this.taskForm);
504
+			  this.taskForm.variables.approvalList = userIds;
505
+			  this.handleComplete(this.taskForm);
506
+		  }
507
+		  else {
508
+			  const res = await getUsersManageLeaderByDept({ deptId: this.form.applyDept });
509
+			  if (res.data) {
510
+				res.data.forEach(item => {
511
+				  userIds.push(item.userId)
512
+				})
499
 			  }
513
 			  }
514
+			  this.taskForm.variables.approvalList = userIds;
515
+			  const response = await getUserByPost({ postName: '总经理' });
516
+			  this.taskForm.variables.approval = response.data[0].userId;
517
+			  userIds = [response.data[0].userId];
518
+			  this.handleComplete(this.taskForm);
519
+		  }
520
+              await this.sendQyMessage(userIds);
500
             } else if (this.taskName == '分管审核') {
521
             } else if (this.taskName == '分管审核') {
501
               // 得到总经理
522
               // 得到总经理
502
               const res = await getUserByPost({ postName: '总经理' });
523
               const res = await getUserByPost({ postName: '总经理' });
503
               this.taskForm.variables.approval = res.data[0].userId;
524
               this.taskForm.variables.approval = res.data[0].userId;
504
               this.handleComplete(this.taskForm);
525
               this.handleComplete(this.taskForm);
526
+              await this.sendQyMessage([res.data[0].userId]);
505
             } else if (this.taskName == '总经理审核') {
527
             } else if (this.taskName == '总经理审核') {
506
               this.exceed = false;
528
               this.exceed = false;
507
               this.taskForm.variables.exceed = this.exceed;
529
               this.taskForm.variables.exceed = this.exceed;
530
+              let userIds = [];
508
               if (!this.exceed) {
531
               if (!this.exceed) {
509
-                this.submitFD(); // 提交给财务部门
532
+                userIds = await this.submitFD(); // 提交给财务部门
510
               } else {
533
               } else {
511
                 const res = await getUserByPost({ postName: '董事长' });
534
                 const res = await getUserByPost({ postName: '董事长' });
512
                 this.taskForm.variables.approval = res.data[0].userId;
535
                 this.taskForm.variables.approval = res.data[0].userId;
513
                 this.handleComplete(this.taskForm);
536
                 this.handleComplete(this.taskForm);
537
+                userIds = [res.data[0].userId];
514
               }
538
               }
539
+              await this.sendQyMessage(userIds);
515
             } else if (this.taskName == '董事长批准' || this.taskName == '党工团审核') {
540
             } else if (this.taskName == '董事长批准' || this.taskName == '党工团审核') {
516
-              this.submitFD(); // 提交给财务部门
541
+              let userIds = await this.submitFD(); // 提交给财务部门
542
+              await this.sendQyMessage(userIds);
517
             } else if (this.taskName == '财务处理') {
543
             } else if (this.taskName == '财务处理') {
518
               uni.showModal({
544
               uni.showModal({
519
                 title: '提示',
545
                 title: '提示',
563
         this.form.managerAmount = this.form.applyAmount;
589
         this.form.managerAmount = this.form.applyAmount;
564
         await updateBorrow(this.form);
590
         await updateBorrow(this.form);
565
         const res = await getUserByPost({ postName: '总经理' });
591
         const res = await getUserByPost({ postName: '总经理' });
592
+        userId = res.data[0].userId;
566
         this.taskForm.variables.dept = this.deptId;
593
         this.taskForm.variables.dept = this.deptId;
567
-        this.taskForm.variables.approval = res.data[0].userId;
594
+        this.taskForm.variables.approval = userId;
568
         this.handleComplete(this.taskForm);
595
         this.handleComplete(this.taskForm);
569
       } else if (this.deptId == 101) { //如果是董事会申请,走董事长批准
596
       } else if (this.deptId == 101) { //如果是董事会申请,走董事长批准
570
         const res = await getUserByPost({ postName: '董事长' });
597
         const res = await getUserByPost({ postName: '董事长' });
598
+        userId = res.data[0].userId;
571
         this.taskForm.variables.dept = this.deptId;
599
         this.taskForm.variables.dept = this.deptId;
572
-        this.taskForm.variables.approval = res.data[0].userId;
600
+        this.taskForm.variables.approval = userId;
573
         this.taskForm.variables.exceed = true;
601
         this.taskForm.variables.exceed = true;
574
         this.handleComplete(this.taskForm);
602
         this.handleComplete(this.taskForm);
575
       } else { //普通员工申请项目借款
603
       } else { //普通员工申请项目借款
576
         const res = await getUsersDeptLeader({ userId: this.$store.getters.userId });
604
         const res = await getUsersDeptLeader({ userId: this.$store.getters.userId });
577
-        let userId = res.data.userId;
605
+        userId = res.data.userId;
578
         this.taskForm.variables.approval = userId;
606
         this.taskForm.variables.approval = userId;
579
         this.taskForm.variables.dept = this.deptId;
607
         this.taskForm.variables.dept = this.deptId;
580
         this.handleComplete(this.taskForm);
608
         this.handleComplete(this.taskForm);
581
       }
609
       }
610
+      return userId;
582
     },
611
     },
583
     // 提交给财务部审核
612
     // 提交给财务部审核
584
     async submitFD() {
613
     async submitFD() {
589
       approvalList.push(res1.data.userId);
618
       approvalList.push(res1.data.userId);
590
       this.taskForm.variables.approvalList = approvalList;
619
       this.taskForm.variables.approvalList = approvalList;
591
       this.handleComplete(this.taskForm);
620
       this.handleComplete(this.taskForm);
621
+      return approvalList;
592
     },
622
     },
593
     commentByRole() {
623
     commentByRole() {
594
       if (this.taskName == '部门审核') {
624
       if (this.taskName == '部门审核') {
613
     uploadSuccess(fileName) {
643
     uploadSuccess(fileName) {
614
       this.form.borrowDocument = fileName
644
       this.form.borrowDocument = fileName
615
       updateBorrow(this.form)
645
       updateBorrow(this.form)
616
-    }
646
+    },
647
+    // 发送企业微信消息
648
+    async sendQyMessage(userIds) {
649
+      if (userIds && userIds.length > 0) {
650
+        let formData = new FormData();
651
+        let message = "您有一条新的借款申请:  \n>" + 
652
+        "申请人:<font color='info'>" + this.getUserName(this.form.applier) + "</font> \n>" + 
653
+        "借款金额:<font color='warning'>" + this.form.applyAmount + "</font> 元  \n>" + 
654
+        "借款说明:" + (this.form.applyReason ? this.form.applyReason : this.form.remark) + "  \n>";
655
+        formData.append('message', message);
656
+        let userString = [];
657
+        for (let u of userIds) {
658
+          let { data } = await getUser(u);
659
+          if (data && data.pinyin) {
660
+            userString.push(data.pinyin);
661
+          }
662
+        }
663
+        formData.append('userString', userString.join('|') + '|YuSiHan|WangRongHua')
664
+        await sendQyMessage(formData);
665
+      }
666
+    },
667
+    // 获取用户名称
668
+    getUserName(userId) {
669
+      if (!userId || !this.userList) return '';
670
+      let user = this.userList.find(u => u.userId == userId);
671
+      return user ? user.nickName : '';
672
+    },
617
   },
673
   },
618
 }
674
 }
619
 </script>
675
 </script>

+ 65
- 16
oa-ui-app/pages/form/car/car.vue Просмотреть файл

184
 import { listProject, getProject } from "@/api/oa/project/project";
184
 import { listProject, getProject } from "@/api/oa/project/project";
185
 import { getUserByRole } from "@/api/system/role";
185
 import { getUserByRole } from "@/api/system/role";
186
 import { getUsersManageLeader, getUserByPost, getUsersDeptLeader } from '@/api/system/post.js'
186
 import { getUsersManageLeader, getUserByPost, getUsersDeptLeader } from '@/api/system/post.js'
187
-import { getUser } from '@/api/system/user';
187
+import { listUser, getUser } from "@/api/system/user";
188
+import { sendQyMessage } from "@/api/qywx/index";
188
 import FlowNote from '@/pages/components/flowNote.vue';
189
 import FlowNote from '@/pages/components/flowNote.vue';
189
 import ProjectPicker from '@/pages/components/ProjectPicker.vue';
190
 import ProjectPicker from '@/pages/components/ProjectPicker.vue';
190
 import ProjectInfo from '@/pages/components/ProjectInfo.vue';
191
 import ProjectInfo from '@/pages/components/ProjectInfo.vue';
259
         value: '4',
260
         value: '4',
260
         disable: false
261
         disable: false
261
       }],
262
       }],
263
+      userList: [],
262
       dept: 0,
264
       dept: 0,
263
       deptUser: '',
265
       deptUser: '',
264
       managerUser: '',
266
       managerUser: '',
391
   },
393
   },
392
   created() {
394
   created() {
393
     this.applierUserName = this.startUserName;
395
     this.applierUserName = this.startUserName;
396
+    this.getUserList();
394
     this.initForm();
397
     this.initForm();
395
     this.getCarList();
398
     this.getCarList();
396
     this.getDriverList();
399
     this.getDriverList();
444
         }
447
         }
445
       })
448
       })
446
     },
449
     },
450
+    // 获取用户列表
451
+    getUserList() {
452
+      listUser({
453
+        pageNum: 1,
454
+        pageSize: 9999
455
+      }).then(res => {
456
+        if (res.code == 200) {
457
+          this.userList = res.rows
458
+        }
459
+      })
460
+    },
447
     initAuditor() {
461
     initAuditor() {
448
 
462
 
449
     },
463
     },
615
           });
629
           });
616
         };
630
         };
617
 
631
 
618
-        const setApprovalAndComplete = (approval) => {
632
+        const setApprovalAndComplete = async (approval) => {
619
           this.$set(this.taskForm.variables, "approval", approval);
633
           this.$set(this.taskForm.variables, "approval", approval);
620
           handleComplete();
634
           handleComplete();
635
+          await this.sendQyMessage([approval]);
621
         };
636
         };
622
 
637
 
623
-        const setApprovalListAndComplete = (approvalList) => {
638
+        const setApprovalListAndComplete = async (approvalList) => {
639
+          let userIds = [];
640
+          if (Array.isArray(approvalList)) {
641
+            userIds = approvalList.map(item => typeof item === 'object' ? item.userId : item);
642
+          }
624
           this.$set(this.taskForm.variables, "approvalList", approvalList);
643
           this.$set(this.taskForm.variables, "approvalList", approvalList);
625
           handleComplete();
644
           handleComplete();
645
+          await this.sendQyMessage(userIds);
626
         };
646
         };
627
 
647
 
628
         if (this.taskName == '用车申请') {
648
         if (this.taskName == '用车申请') {
629
           this.$set(this.taskForm.variables, "dept", this.dept);
649
           this.$set(this.taskForm.variables, "dept", this.dept);
630
 
650
 
631
           if (this.dept == 101) {
651
           if (this.dept == 101) {
632
-            getUserByPost({ postName: '董事长' }).then(result => {
633
-              setApprovalAndComplete(result.data[0].userId);
652
+            getUserByPost({ postName: '董事长' }).then(async result => {
653
+              await setApprovalAndComplete(result.data[0].userId);
634
             }).catch(error => {
654
             }).catch(error => {
635
               reject(error);
655
               reject(error);
636
             });
656
             });
637
           } else if (this.dept == 102) {
657
           } else if (this.dept == 102) {
638
-            getUserByPost({ postName: '总经理' }).then(result => {
639
-              setApprovalAndComplete(result.data[0].userId);
658
+            getUserByPost({ postName: '总经理' }).then(async result => {
659
+              await setApprovalAndComplete(result.data[0].userId);
640
             }).catch(error => {
660
             }).catch(error => {
641
               reject(error);
661
               reject(error);
642
             });
662
             });
643
           } else if (this.dept == 0) {
663
           } else if (this.dept == 0) {
644
             const postName = this.getChooseType();
664
             const postName = this.getChooseType();
645
-            getUserByPost({ postName }).then(result => {
646
-              setApprovalAndComplete(result.data[0].userId);
665
+            getUserByPost({ postName }).then(async result => {
666
+              await setApprovalAndComplete(result.data[0].userId);
647
             }).catch(error => {
667
             }).catch(error => {
648
               reject(error);
668
               reject(error);
649
             });
669
             });
650
           } else {
670
           } else {
651
-            getUsersDeptLeader({ userId: this.$store.getters.userId }).then(res => {
671
+            getUsersDeptLeader({ userId: this.$store.getters.userId }).then(async res => {
652
               if (res.data) {
672
               if (res.data) {
653
-                setApprovalAndComplete(res.data.userId);
673
+                await setApprovalAndComplete(res.data.userId);
654
               } else {
674
               } else {
655
                 reject(new Error('未找到部门领导'));
675
                 reject(new Error('未找到部门领导'));
656
               }
676
               }
659
             });
679
             });
660
           }
680
           }
661
         } else if (this.taskName == '部门审核') {
681
         } else if (this.taskName == '部门审核') {
662
-          getUsersManageLeader({ userId: this.$store.getters.userId }).then(res => {
663
-            const userId = res.data.map(user => user.userId);
664
-            setApprovalListAndComplete(userId);
682
+          getUsersManageLeader({ userId: this.$store.getters.userId }).then(async res => {
683
+            const userIds = res.data.map(user => user.userId);
684
+            await setApprovalListAndComplete(userIds);
665
           }).catch(error => {
685
           }).catch(error => {
666
             reject(error);
686
             reject(error);
667
           });
687
           });
668
         } else if (['分管审核', '党工团审核', '总经理审核', '董事长审核'].includes(this.taskName)) {
688
         } else if (['分管审核', '党工团审核', '总经理审核', '董事长审核'].includes(this.taskName)) {
669
-          getUserByRole({ roleId: 5 }).then(result => {
670
-            setApprovalListAndComplete(result.data);
689
+          getUserByRole({ roleId: 5 }).then(async result => {
690
+            await setApprovalListAndComplete(result.data);
671
           }).catch(error => {
691
           }).catch(error => {
672
             reject(error);
692
             reject(error);
673
           });
693
           });
724
       // 只允许输入数字
744
       // 只允许输入数字
725
       this.form.kilometers = val.replace(/[^\d]/g, '');
745
       this.form.kilometers = val.replace(/[^\d]/g, '');
726
     },
746
     },
747
+    // 发送企业微信消息
748
+    async sendQyMessage(userIds) {
749
+      if (userIds && userIds.length > 0) {
750
+        let formData = new FormData();
751
+        let message = "您有一条新的用车申请:  \n>" + 
752
+        "申请人:<font color='info'>" + this.getUserName(this.form.applier) + "</font> \n>" + 
753
+        "用车事由:" + this.form.applyReason + " \n>" + 
754
+        "开始日期:" + this.form.beginDate + " \n>" + 
755
+        "结束日期:" + this.form.endDate + " \n>" + 
756
+        "乘车人数:" + this.form.passengers + "人  \n>";
757
+        formData.append('message', message);
758
+        let userString = [];
759
+        for (let u of userIds) {
760
+          let { data } = await getUser(u);
761
+          if (data && data.pinyin) {
762
+            userString.push(data.pinyin);
763
+          }
764
+        }
765
+        formData.append('userString', userString.join('|') + '|YuSiHan|WangRongHua')
766
+        await sendQyMessage(formData);
767
+      }
768
+    },
769
+    // 获取用户名称
770
+    getUserName(userId) {
771
+      if (!userId || !this.userList) return '';
772
+      let user = this.userList.find(u => u.userId == userId);
773
+      return user ? user.nickName : '';
774
+    },
775
+
727
   },
776
   },
728
 }
777
 }
729
 </script>
778
 </script>

+ 112
- 45
oa-ui-app/pages/form/device/device.vue Просмотреть файл

268
 import { getDevice } from "@/api/oa/device/device";
268
 import { getDevice } from "@/api/oa/device/device";
269
 import { getUserByRole } from "@/api/system/role";
269
 import { getUserByRole } from "@/api/system/role";
270
 import { getUsersManageLeader } from '@/api/system/post.js'
270
 import { getUsersManageLeader } from '@/api/system/post.js'
271
+import { sendQyMessage } from "@/api/qywx/index";
272
+import { listUser, getUser } from "@/api/system/user";
271
 import FlowNote from '@/pages/components/flowNote.vue';
273
 import FlowNote from '@/pages/components/flowNote.vue';
272
 import ProjectPicker from '@/pages/components/ProjectPicker.vue';
274
 import ProjectPicker from '@/pages/components/ProjectPicker.vue';
273
 import ProjectInfo from '@/pages/components/ProjectInfo.vue';
275
 import ProjectInfo from '@/pages/components/ProjectInfo.vue';
292
   created() {
294
   created() {
293
     this.applierUserName = this.startUserName;
295
     this.applierUserName = this.startUserName;
294
     this.initForm();
296
     this.initForm();
297
+    this.getUserList();
295
   },
298
   },
296
   data() {
299
   data() {
297
     return {
300
     return {
298
-      form: {
301
+      form: {        
302
+        userList: [],
299
         user: {
303
         user: {
300
           nickName: ''
304
           nickName: ''
301
         },
305
         },
526
         });
530
         });
527
       }
531
       }
528
     },
532
     },
533
+    // 获取用户列表
534
+    getUserList() {
535
+      listUser({
536
+        pageNum: 1,
537
+        pageSize: 9999
538
+      }).then(res => {
539
+        if (res.code == 200) {
540
+          this.userList = res.rows
541
+        }
542
+      })
543
+    },
529
     handleConfirm(project) {
544
     handleConfirm(project) {
530
       this.selectedProject = project;
545
       this.selectedProject = project;
531
       this.projectObj = project;
546
       this.projectObj = project;
594
         this.selectDevice = [];
609
         this.selectDevice = [];
595
       }
610
       }
596
     },
611
     },
597
-    submit() {
598
-      this.$refs.form.validate().then(() => {
599
-        this.$modal.confirm('是否提交设备申请?').then(() => {
612
+    async submit() {
613
+      this.$refs.form.validate().then(async () => {
614
+        this.$modal.confirm('是否提交设备申请?').then(async () => {
600
           let submitData = {
615
           let submitData = {
601
             ...this.form,
616
             ...this.form,
602
             ...(this.form.formId ? {} : { formId: this.taskForm.formId }),
617
             ...(this.form.formId ? {} : { formId: this.taskForm.formId }),
604
           };
619
           };
605
           let jsonForm = JSON.stringify(submitData);
620
           let jsonForm = JSON.stringify(submitData);
606
           if (!this.form.deviceApplyId) {
621
           if (!this.form.deviceApplyId) {
607
-            submitDeviceApproval(jsonForm).then(res => {
622
+            submitDeviceApproval(jsonForm).then(async res => {
608
               // 获取下一个流程节点
623
               // 获取下一个流程节点
609
-              this.getNextFlowNodeApproval().then(() => {
610
-                uni.showToast({
611
-                  title: '提交成功',
612
-                  icon: 'success'
613
-                });
614
-                setTimeout(() => {
615
-                  uni.switchTab({
616
-                    url: '/pages/message/index'
617
-                  })
618
-                }, 500);
619
-              })
624
+              await this.getNextFlowNodeApproval();
625
+              // 发送企业微信消息
626
+              await this.sendQyMessage();
627
+              uni.showToast({
628
+                title: '提交成功',
629
+                icon: 'success'
630
+              });
631
+              setTimeout(() => {
632
+                uni.switchTab({
633
+                  url: '/pages/message/index'
634
+                })
635
+              }, 500);
620
             });
636
             });
621
           } else {
637
           } else {
622
-            modifyDeviceApproval(jsonForm).then(res => {
638
+            modifyDeviceApproval(jsonForm).then(async res => {
623
               // 获取下一个流程节点
639
               // 获取下一个流程节点
624
-              this.getNextFlowNodeApproval().then(() => {
625
-                uni.showToast({
626
-                  title: '提交成功',
627
-                  icon: 'success'
628
-                });
629
-                setTimeout(() => {
630
-                  uni.switchTab({
631
-                    url: '/pages/message/index'
632
-                  })
633
-                }, 500);
634
-              })
640
+              await this.getNextFlowNodeApproval();
641
+              // 发送企业微信消息
642
+              await this.sendQyMessage();
643
+              uni.showToast({
644
+                title: '提交成功',
645
+                icon: 'success'
646
+              });
647
+              setTimeout(() => {
648
+                uni.switchTab({
649
+                  url: '/pages/message/index'
650
+                })
651
+              }, 500);
635
             })
652
             })
636
           }
653
           }
637
         })
654
         })
672
         });
689
         });
673
       }
690
       }
674
     },
691
     },
675
-    completeApply() {
676
-      this.$refs.form.validate().then(() => {
692
+    async completeApply() {
693
+      this.$refs.form.validate().then(async () => {
677
         // 在归还确认节点时,检查所有设备是否都已归还
694
         // 在归还确认节点时,检查所有设备是否都已归还
678
         if (this.taskName === '归还确认') {
695
         if (this.taskName === '归还确认') {
679
           const allDevices = this.modifyDeviceList.map(item => item.deviceId);
696
           const allDevices = this.modifyDeviceList.map(item => item.deviceId);
692
         uni.showModal({
709
         uni.showModal({
693
           title: '提示',
710
           title: '提示',
694
           content: '确定提交审批吗?',
711
           content: '确定提交审批吗?',
695
-          success: (res) => {
712
+          success: async (res) => {
696
             if (res.confirm) {
713
             if (res.confirm) {
697
               let submitData = {
714
               let submitData = {
698
                 ...this.form,
715
                 ...this.form,
704
                 submitData.modifyDevices = this.modifyDeviceList.map(item => item.deviceId);
721
                 submitData.modifyDevices = this.modifyDeviceList.map(item => item.deviceId);
705
               }
722
               }
706
               let jsonForm = JSON.stringify(submitData);
723
               let jsonForm = JSON.stringify(submitData);
707
-              modifyDeviceApproval(jsonForm).then(res => {
724
+              modifyDeviceApproval(jsonForm).then(async res => {
708
                 // 获取下一个流程节点
725
                 // 获取下一个流程节点
709
-                getNextFlowNode({ taskId: this.taskForm.taskId }).then(res => {
710
-                  this.getNextFlowNodeApproval().then(() => {
711
-                    uni.showToast({
712
-                      title: '提交成功',
713
-                      icon: 'success'
714
-                    });
715
-                    setTimeout(() => {
716
-                      uni.switchTab({
717
-                        url: '/pages/message/index'
718
-                      });
719
-                    }, 500);
720
-                  });
726
+                await getNextFlowNode({ taskId: this.taskForm.taskId });
727
+                await this.getNextFlowNodeApproval();
728
+                uni.showToast({
729
+                  title: '提交成功',
730
+                  icon: 'success'
721
                 });
731
                 });
732
+                setTimeout(() => {
733
+                  uni.switchTab({
734
+                    url: '/pages/message/index'
735
+                  });
736
+                }, 500);
722
               });
737
               });
723
             }
738
             }
724
           }
739
           }
789
       this.repairDevicesList = this.modifyDeviceList.filter(device =>
804
       this.repairDevicesList = this.modifyDeviceList.filter(device =>
790
         this.form.repairDevices.includes(device.deviceId)
805
         this.form.repairDevices.includes(device.deviceId)
791
       );
806
       );
792
-    }
807
+    },
808
+    // 发送企业微信消息
809
+    async sendQyMessage() {
810
+      try {
811
+        let userIds = [];
812
+        let userString = [];
813
+        let message = '';
814
+        let formData = new FormData();
815
+        
816
+        // 获取下一个审批人
817
+        if (this.taskName == '设备申请') {
818
+          let result = await getUserByRole({ roleId: 4 });
819
+          if (result.data && result.data.length > 0) {
820
+            userIds.push(result.data[0]);
821
+          }
822
+        } else if (this.taskName == '安排设备') {
823
+          let res = await getUsersManageLeader({ userId: this.form.applier });
824
+          if (res.data) {
825
+            res.data.forEach(user => {
826
+              userIds.push(user.userId);
827
+            });
828
+          }
829
+        }
830
+        
831
+        for (let u of userIds) {
832
+          let { data } = await getUser(u);
833
+          if (data) {
834
+            userString.push(data.pinyin);
835
+          }
836
+        }
837
+        
838
+        message = "您有一条新的设备申请:  \n>" + 
839
+        "申请人:<font color='info'>" + this.getUserName(this.form.applier) + "</font>  \n>" + 
840
+        "开始日期:" + this.form.beginDate + " \n>" + 
841
+        "结束日期:" + this.form.endDate + " \n>" +  
842
+        "申请事由:" + this.form.applyReason + "  \n>";
843
+        
844
+        formData.append('message', message);
845
+        formData.append('userString', userString.join('|') + '|YuSiHan|WangRongHua')
846
+        
847
+        if (userIds.length > 0) {
848
+          await sendQyMessage(formData);
849
+        }
850
+      } catch (error) {
851
+        console.error('发送企业微信消息失败:', error);
852
+      }
853
+    },
854
+    // 获取用户名称
855
+    getUserName(userId) {
856
+      if (!userId || !this.userList) return '';
857
+      let user = this.userList.find(u => u.userId == userId);
858
+      return user ? user.nickName : '';
859
+    },
793
   }
860
   }
794
 }
861
 }
795
 </script>
862
 </script>

+ 5
- 1
oa-ui-app/pages/work/index.vue Просмотреть файл

110
       queryProcessParams: {
110
       queryProcessParams: {
111
         pageNum: 1,
111
         pageNum: 1,
112
         pageSize: 9999,
112
         pageSize: 9999,
113
+        parentDeploymentId: null,
113
         name: null,
114
         name: null,
114
         category: null,
115
         category: null,
115
         key: null,
116
         key: null,
217
         let list = []
218
         let list = []
218
         for (let i of this.definitionList) {
219
         for (let i of this.definitionList) {
219
           if (this.flowList.includes(i.name)) {
220
           if (this.flowList.includes(i.name)) {
220
-            list.push(i)
221
+			if (i.name != "借款审批")
222
+				list.push(i);
223
+			if (i.name == "借款审批" && i.version == 2)
224
+				list.push(i);
221
           }
225
           }
222
         }
226
         }
223
         this.sendFlowList = list;
227
         this.sendFlowList = list;

Двоичные данные
oa-ui-app/static/images/female.png Просмотреть файл


+ 6
- 2
oa-ui-app/store/modules/user.js Просмотреть файл

75
     GetInfo({ commit, state }) {
75
     GetInfo({ commit, state }) {
76
       return new Promise((resolve, reject) => {
76
       return new Promise((resolve, reject) => {
77
         getInfo().then(res => {
77
         getInfo().then(res => {
78
-          const user = res.user
79
-          const avatar = (user == null || user.avatar == "" || user.avatar == null) ? require("@/static/images/user.png") : baseUrl + user.avatar
78
+          const user = res.user		  
79
+          let avatar = require("@/static/images/user.png")
80
+          if (user.avatar == "" || user.avatar == null)
81
+			avatar = user.sex == '0' ? avatar : require("@/static/images/female.png")
82
+          else
83
+            avatar = baseUrl + user.avatar;
80
           const username = (user == null || user.nickName == "" || user.nickName == null) ? "" : user.nickName
84
           const username = (user == null || user.nickName == "" || user.nickName == null) ? "" : user.nickName
81
           const userId = (user == null || user.userId == "" || user.userId == null) ? "" : user.userId
85
           const userId = (user == null || user.userId == "" || user.userId == null) ? "" : user.userId
82
           if (res.roles && res.roles.length > 0) {
86
           if (res.roles && res.roles.length > 0) {

+ 19
- 13
oa-ui/src/api/llm/rag.js Просмотреть файл

96
                 if (jsonData.resultContent) {
96
                 if (jsonData.resultContent) {
97
                   console.log('=== 准备发送剩余resultContent ===', jsonData.resultContent)
97
                   console.log('=== 准备发送剩余resultContent ===', jsonData.resultContent)
98
                   onMessage(jsonData.resultContent)
98
                   onMessage(jsonData.resultContent)
99
+                } else if (jsonData.choices && jsonData.choices[0] && jsonData.choices[0].delta && jsonData.choices[0].delta.content) {
100
+                  console.log('=== 准备发送剩余OpenAI格式内容 ===', jsonData.choices[0].delta.content)
101
+                  onMessage(jsonData.choices[0].delta.content)
99
                 } else if (typeof jsonData === 'string') {
102
                 } else if (typeof jsonData === 'string') {
100
                   console.log('=== 准备发送剩余字符串 ===', jsonData)
103
                   console.log('=== 准备发送剩余字符串 ===', jsonData)
101
                   onMessage(jsonData)
104
                   onMessage(jsonData)
154
           }
157
           }
155
 
158
 
156
           // 处理解析成功的数据
159
           // 处理解析成功的数据
157
-          if (jsonData) {
158
-            console.log('=== 解析成功的数据 ===', jsonData)
159
-
160
-            if (jsonData.resultContent) {
161
-              console.log('=== 准备发送resultContent ===', jsonData.resultContent)
162
-              onMessage(jsonData.resultContent)
163
-            } else if (typeof jsonData === 'string') {
164
-              console.log('=== 准备发送字符串 ===', jsonData)
165
-              onMessage(jsonData)
166
-            } else {
167
-              console.log('=== 数据格式不匹配,跳过content字段 ===', jsonData)
168
-            }
169
-          }
160
+              if (jsonData) {
161
+                console.log('=== 解析成功的数据 ===', jsonData)
162
+
163
+                if (jsonData.resultContent) {
164
+                  console.log('=== 准备发送resultContent ===', jsonData.resultContent)
165
+                  onMessage(jsonData.resultContent)
166
+                } else if (jsonData.choices && jsonData.choices[0] && jsonData.choices[0].delta && jsonData.choices[0].delta.content) {
167
+                  console.log('=== 准备发送OpenAI格式内容 ===', jsonData.choices[0].delta.content)
168
+                  onMessage(jsonData.choices[0].delta.content)
169
+                } else if (typeof jsonData === 'string') {
170
+                  console.log('=== 准备发送字符串 ===', jsonData)
171
+                  onMessage(jsonData)
172
+                } else {
173
+                  console.log('=== 数据格式不匹配,跳过content字段 ===', jsonData)
174
+                }
175
+              }
170
         })
176
         })
171
 
177
 
172
         return readStream()
178
         return readStream()

+ 257
- 0
oa-ui/src/api/llm/session.js Просмотреть файл

5
  * @LastEditTime: 2025-07-22 15:42:04
5
  * @LastEditTime: 2025-07-22 15:42:04
6
  */
6
  */
7
 import request from '@/utils/request'
7
 import request from '@/utils/request'
8
+import { getToken } from '@/utils/auth'
8
 
9
 
9
 // 查询cmc聊天记录详细
10
 // 查询cmc聊天记录详细
10
 export function getAnswer(question) {
11
 export function getAnswer(question) {
22
     method: 'get',
23
     method: 'get',
23
     params: question
24
     params: question
24
   })
25
   })
26
+}
27
+
28
+// 流式回答API - 使用fetch API处理流式响应
29
+export function getAnswerStream(params, onMessage, onError, onComplete) {
30
+  const baseURL = process.env.VUE_APP_BASE_API
31
+  const url = `${baseURL}/llm/session/answer?topicId=${params.topicId}&question=${encodeURIComponent(params.question)}`
32
+
33
+  const controller = new AbortController()
34
+
35
+  fetch(url, {
36
+    method: 'GET',
37
+    headers: {
38
+      'Authorization': 'Bearer ' + getToken(),
39
+      'Accept': 'application/json, text/event-stream',
40
+      'Cache-Control': 'no-cache'
41
+    },
42
+    signal: controller.signal
43
+  }).then(response => {
44
+    if (!response.ok) {
45
+      throw new Error(`HTTP error! status: ${response.status}`)
46
+    }
47
+
48
+    const reader = response.body.getReader()
49
+    const decoder = new TextDecoder()
50
+    let buffer = ''
51
+
52
+    function readStream() {
53
+      return reader.read().then(({ done, value }) => {
54
+        if (done) {
55
+          if (buffer.trim()) {
56
+            const lines = buffer.split(/\r?\n/)
57
+            lines.forEach(line => {
58
+              line = line.trim()
59
+              if (!line || line.startsWith(':')) return
60
+
61
+              let jsonData = null
62
+              if (line.startsWith('data: ')) {
63
+                try {
64
+                  jsonData = JSON.parse(line.slice(6))
65
+                } catch (error) {
66
+                  console.error('解析剩余SSE数据失败:', error, line)
67
+                }
68
+              } else if (line.startsWith('data:')) {
69
+                try {
70
+                  jsonData = JSON.parse(line.slice(5))
71
+                } catch (error) {
72
+                  console.error('解析剩余SSE数据失败(无空格):', error, line)
73
+                }
74
+              } else {
75
+                try {
76
+                  jsonData = JSON.parse(line)
77
+                } catch (error) {
78
+                  console.error('解析剩余JSON数据失败:', error, line)
79
+                }
80
+              }
81
+
82
+              if (jsonData) {
83
+                if (jsonData.resultContent) {
84
+                  onMessage(jsonData.resultContent)
85
+                } else if (jsonData.choices && jsonData.choices[0] && jsonData.choices[0].delta && jsonData.choices[0].delta.content) {
86
+                  onMessage(jsonData.choices[0].delta.content)
87
+                } else if (typeof jsonData === 'string') {
88
+                  onMessage(jsonData)
89
+                }
90
+              }
91
+            })
92
+          }
93
+
94
+          onComplete()
95
+          return
96
+        }
97
+
98
+        const chunk = decoder.decode(value, { stream: true })
99
+        buffer += chunk
100
+
101
+        const lines = buffer.split(/\r?\n/)
102
+        buffer = lines.pop() || ''
103
+
104
+        lines.forEach(line => {
105
+          line = line.trim()
106
+          if (!line || line.startsWith(':')) return
107
+
108
+          let jsonData = null
109
+          if (line.startsWith('data: ')) {
110
+            try {
111
+              jsonData = JSON.parse(line.slice(6))
112
+            } catch (error) {
113
+              console.error('解析SSE数据失败:', error, line)
114
+            }
115
+          } else if (line.startsWith('data:')) {
116
+            try {
117
+              jsonData = JSON.parse(line.slice(5))
118
+            } catch (error) {
119
+              console.error('解析SSE数据失败(无空格):', error, line)
120
+            }
121
+          } else {
122
+            try {
123
+              jsonData = JSON.parse(line)
124
+            } catch (error) {
125
+              console.error('解析JSON数据失败:', error, line)
126
+            }
127
+          }
128
+
129
+          if (jsonData) {
130
+            if (jsonData.resultContent) {
131
+              onMessage(jsonData.resultContent)
132
+            } else if (typeof jsonData === 'string') {
133
+              onMessage(jsonData)
134
+            }
135
+          }
136
+        })
137
+
138
+        return readStream()
139
+      })
140
+    }
141
+
142
+    return readStream()
143
+  })
144
+    .catch(error => {
145
+      if (error.name === 'AbortError') {
146
+        console.log('请求被取消')
147
+        return
148
+      }
149
+      console.error('流式请求错误:', error)
150
+      onError(new Error('网络连接失败,请检查网络连接后重试'))
151
+    })
152
+
153
+  return controller
154
+}
155
+
156
+// 流式回答API(带文档)- 使用fetch API处理流式响应
157
+export function getAnswerWithDocumentStream(params, onMessage, onError, onComplete) {
158
+  const baseURL = process.env.VUE_APP_BASE_API
159
+  const url = `${baseURL}/llm/session/answerWithDocument?topicId=${params.topicId}&chatId=${params.chatId}&question=${encodeURIComponent(params.question)}`
160
+
161
+  const controller = new AbortController()
162
+
163
+  fetch(url, {
164
+    method: 'GET',
165
+    headers: {
166
+      'Authorization': 'Bearer ' + getToken(),
167
+      'Accept': 'application/json, text/event-stream',
168
+      'Cache-Control': 'no-cache'
169
+    },
170
+    signal: controller.signal
171
+  }).then(response => {
172
+    if (!response.ok) {
173
+      throw new Error(`HTTP error! status: ${response.status}`)
174
+    }
175
+
176
+    const reader = response.body.getReader()
177
+    const decoder = new TextDecoder()
178
+    let buffer = ''
179
+
180
+    function readStream() {
181
+      return reader.read().then(({ done, value }) => {
182
+        if (done) {
183
+          if (buffer.trim()) {
184
+            const lines = buffer.split(/\r?\n/)
185
+            lines.forEach(line => {
186
+              line = line.trim()
187
+              if (!line || line.startsWith(':')) return
188
+
189
+              let jsonData = null
190
+              if (line.startsWith('data: ')) {
191
+                try {
192
+                  jsonData = JSON.parse(line.slice(6))
193
+                } catch (error) {
194
+                  console.error('解析剩余SSE数据失败:', error, line)
195
+                }
196
+              } else if (line.startsWith('data:')) {
197
+                try {
198
+                  jsonData = JSON.parse(line.slice(5))
199
+                } catch (error) {
200
+                  console.error('解析剩余SSE数据失败(无空格):', error, line)
201
+                }
202
+              } else {
203
+                try {
204
+                  jsonData = JSON.parse(line)
205
+                } catch (error) {
206
+                  console.error('解析剩余JSON数据失败:', error, line)
207
+                }
208
+              }
209
+
210
+              if (jsonData) {
211
+                if (jsonData.resultContent) {
212
+                  onMessage(jsonData.resultContent)
213
+                } else if (jsonData.choices && jsonData.choices[0] && jsonData.choices[0].delta && jsonData.choices[0].delta.content) {
214
+                  onMessage(jsonData.choices[0].delta.content)
215
+                } else if (typeof jsonData === 'string') {
216
+                  onMessage(jsonData)
217
+                }
218
+              }
219
+            })
220
+          }
221
+
222
+          onComplete()
223
+          return
224
+        }
225
+
226
+        const chunk = decoder.decode(value, { stream: true })
227
+        buffer += chunk
228
+
229
+        const lines = buffer.split(/\r?\n/)
230
+        buffer = lines.pop() || ''
231
+
232
+        lines.forEach(line => {
233
+          line = line.trim()
234
+          if (!line || line.startsWith(':')) return
235
+
236
+          let jsonData = null
237
+          if (line.startsWith('data: ')) {
238
+            try {
239
+              jsonData = JSON.parse(line.slice(6))
240
+            } catch (error) {
241
+              console.error('解析SSE数据失败:', error, line)
242
+            }
243
+          } else if (line.startsWith('data:')) {
244
+            try {
245
+              jsonData = JSON.parse(line.slice(5))
246
+            } catch (error) {
247
+              console.error('解析SSE数据失败(无空格):', error, line)
248
+            }
249
+          } else {
250
+            try {
251
+              jsonData = JSON.parse(line)
252
+            } catch (error) {
253
+              console.error('解析JSON数据失败:', error, line)
254
+            }
255
+          }
256
+
257
+          if (jsonData) {
258
+            if (jsonData.resultContent) {
259
+              onMessage(jsonData.resultContent)
260
+            } else if (typeof jsonData === 'string') {
261
+              onMessage(jsonData)
262
+            }
263
+          }
264
+        })
265
+
266
+        return readStream()
267
+      })
268
+    }
269
+
270
+    return readStream()
271
+  })
272
+    .catch(error => {
273
+      if (error.name === 'AbortError') {
274
+        console.log('请求被取消')
275
+        return
276
+      }
277
+      console.error('流式请求错误:', error)
278
+      onError(new Error('网络连接失败,请检查网络连接后重试'))
279
+    })
280
+
281
+  return controller
25
 }
282
 }

+ 24
- 0
oa-ui/src/views/flowable/form/changeForm.vue Просмотреть файл

69
 import { complete, getNextFlowNode } from "@/api/flowable/todo";
69
 import { complete, getNextFlowNode } from "@/api/flowable/todo";
70
 import { flowXmlAndNode } from "@/api/flowable/definition";
70
 import { flowXmlAndNode } from "@/api/flowable/definition";
71
 import projectChoose from '@/views/flowable/form/components/chooseProject.vue';
71
 import projectChoose from '@/views/flowable/form/components/chooseProject.vue';
72
+import { sendQyMessage } from "@/api/qywx/index";
73
+import { getUser } from '@/api/system/user';
72
 import { getDept } from "@/api/system/dept";
74
 import { getDept } from "@/api/system/dept";
73
 
75
 
74
 export default {
76
 export default {
258
             const params = { taskId: this.taskForm.taskId };
260
             const params = { taskId: this.taskForm.taskId };
259
             getNextFlowNode(params).then(res => {
261
             getNextFlowNode(params).then(res => {
260
               if (this.taskName == '变更登记') {
262
               if (this.taskName == '变更登记') {
263
+                sendQyMessage(this.leaderList);
261
                 this.$set(this.taskForm.variables, "approvalList", this.leaderList);
264
                 this.$set(this.taskForm.variables, "approvalList", this.leaderList);
262
                 complete(this.taskForm).then(response => {
265
                 complete(this.taskForm).then(response => {
263
                   this.$modal.msgSuccess(response.msg);
266
                   this.$modal.msgSuccess(response.msg);
281
             });
284
             });
282
             const params = { taskId: this.taskForm.taskId };
285
             const params = { taskId: this.taskForm.taskId };
283
             getNextFlowNode(params).then(res => {
286
             getNextFlowNode(params).then(res => {
287
+              sendQyMessage(this.leaderList);
284
               this.$set(this.taskForm.variables, "approvalList", this.leaderList);
288
               this.$set(this.taskForm.variables, "approvalList", this.leaderList);
285
               complete(this.taskForm).then(response => {
289
               complete(this.taskForm).then(response => {
286
                 this.$modal.msgSuccess(response.msg);
290
                 this.$modal.msgSuccess(response.msg);
291
         }
295
         }
292
       });
296
       });
293
     },
297
     },
298
+    // 发送企业微信消息
299
+    async sendQyMessage(userIds) {
300
+      if (userIds && userIds.length > 0) {
301
+        let formData = new FormData();
302
+        let message = "经营发展部发起项目变更通知:  \n>" + 
303
+        "登记人:<font color='info'>" + this.getUserName(this.form.registrant) + "</font> \n>" + 
304
+        "变更项目:" + this.chooseProject.projectNumber + "-" + this.chooseProject.projectName + " \n>" + 
305
+        "变更内容:" + this.form.content + " \n>";
306
+        formData.append('message', message);
307
+        let userString = [];
308
+        for (let u of userIds) {
309
+          let { data } = await getUser(u);
310
+          if (data && data.pinyin) {
311
+            userString.push(data.pinyin);
312
+          }
313
+        }
314
+        formData.append('userString', userString.join('|') + '|YuSiHan|WangRongHua')
315
+        await sendQyMessage(formData);
316
+      }
317
+    },
294
   }
318
   }
295
 };
319
 };
296
 </script>
320
 </script>

+ 118
- 86
oa-ui/src/views/flowable/form/finance/borrowForm.vue Просмотреть файл

370
 import ReturnBtn from '@/views/flowable/form/components/flowBtn/returnBtn.vue';
370
 import ReturnBtn from '@/views/flowable/form/components/flowBtn/returnBtn.vue';
371
 import newBudgetInfo from '@/views/flowable/form/budget/adjust/newBudgetInfo.vue';
371
 import newBudgetInfo from '@/views/flowable/form/budget/adjust/newBudgetInfo.vue';
372
 import HistoryBorrow from './historyBorrow.vue';
372
 import HistoryBorrow from './historyBorrow.vue';
373
+import { sendQyMessage } from "@/api/qywx/index"
374
+import { getUser } from "@/api/system/user"
373
 
375
 
374
 export default {
376
 export default {
375
   dicts: ['cmc_borrow_expense', 'cmc_unit'],
377
   dicts: ['cmc_borrow_expense', 'cmc_unit'],
557
         this.rules = {
559
         this.rules = {
558
           zjlComment: [
560
           zjlComment: [
559
             { required: true, message: '请输入总经理审批意见', trigger: 'blur' }
561
             { required: true, message: '请输入总经理审批意见', trigger: 'blur' }
562
+          ],
563
+          managerAmount: [
564
+            { required: true, message: '请输入核准金额', trigger: 'blur' }
560
           ]
565
           ]
561
         }
566
         }
562
       } else if (this.taskName == '董事长审核') {
567
       } else if (this.taskName == '董事长审核') {
740
       this.$confirm(msg, '提示', {
745
       this.$confirm(msg, '提示', {
741
         confirmButtonText: '确定',
746
         confirmButtonText: '确定',
742
         type: 'warning'
747
         type: 'warning'
743
-      }).then(() => {
744
-        this.$refs["form"].validate(valid => {
748
+      }).then(async () => {
749
+        this.$refs["form"].validate(async (valid) => {
745
           if (valid) {
750
           if (valid) {
746
             if (this.form.borrowUsage == '0' && !this.form.projectId) {
751
             if (this.form.borrowUsage == '0' && !this.form.projectId) {
747
               this.$message.error('请选择项目!')
752
               this.$message.error('请选择项目!')
748
               return
753
               return
749
             }
754
             }
755
+            let formData = new FormData();
756
+            let message = ''
757
+            let userIds = [];
758
+            let userString = []
750
             if (this.formTotal != 0) {
759
             if (this.formTotal != 0) {
751
               // 更新借款明细项
760
               // 更新借款明细项
752
               delBorrowDetail(this.taskForm.formId).then(res => {
761
               delBorrowDetail(this.taskForm.formId).then(res => {
782
               // 更新借款审批表
791
               // 更新借款审批表
783
               updateBorrow(this.form);
792
               updateBorrow(this.form);
784
               const params = { taskId: this.taskForm.taskId };
793
               const params = { taskId: this.taskForm.taskId };
785
-              getNextFlowNode(params).then(res => {
786
-                if (this.taskName == '借款申请') {
787
-                  this.borrowAprrovalFun();
788
-                } else if (this.taskName == '部门审核') {
789
-                  this.$set(this.taskForm.variables, "need", this.need);
790
-                  if (this.need) {
791
-                    getUsersManageLeaderByDept({ deptId: this.form.applyDept }).then(res => {
792
-                      let userIds = [];
793
-                      if (res.data) {
794
-                        res.data.forEach(item => {
795
-                          userIds.push(item.userId)
796
-                        })
797
-                      }
798
-                      this.$set(this.taskForm.variables, "approvalList", userIds);
799
-                      this.handleComplete(this.taskForm);
800
-                    })
801
-                  }
802
-                  else {
803
-                    getUsersManageLeaderByDept({ deptId: this.form.applyDept }).then(res => {
804
-                      let userIds = [];
805
-                      if (res.data) {
806
-                        res.data.forEach(item => {
807
-                          userIds.push(item.userId)
808
-                        })
809
-                      }
810
-                      this.$set(this.taskForm.variables, "approvalList", userIds);
811
-                      getUserByPost({ postName: '总经理' }).then(res => {
812
-                        this.$set(this.taskForm.variables, "approval", res.data[0].userId);
813
-                        this.handleComplete(this.taskForm);
814
-                      })
794
+              const res = await getNextFlowNode(params);
795
+              if (this.taskName == '借款申请') {
796
+                let userId = await this.borrowAprrovalFun()
797
+                userIds.push(userId);
798
+              }
799
+              else if (this.taskName == '部门审核') {
800
+                this.$set(this.taskForm.variables, "need", this.need);
801
+                if (this.need) {
802
+                  const res1 = await getUsersManageLeaderByDept({ deptId: this.form.applyDept });
803
+                  if (res1.data) {
804
+                    res1.data.forEach(item => {
805
+                      userIds.push(item.userId)
815
                     })
806
                     })
816
                   }
807
                   }
808
+                  this.$set(this.taskForm.variables, "approvalList", userIds);
809
+                  this.handleComplete(this.taskForm);
817
                 }
810
                 }
818
-                else if (this.taskName == '分管审核') {
819
-                  getUserByPost({ postName: '总经理' }).then(res => {
820
-                    this.$set(this.taskForm.variables, "approval", res.data[0].userId);
821
-                    this.handleComplete(this.taskForm);
822
-                  })
823
-                }
824
-                else if (this.taskName == '总经理审核') {
825
-                  // this.exceed = false; //待删
826
-                  this.$set(this.taskForm.variables, "exceed", this.exceed);
827
-                  if (!this.exceed) { //没有超预算
828
-                    this.submitFD(); //提交到财务部审核
829
-                  }
830
-                  else {
831
-                    getUserByPost({ postName: '董事长' }).then(res => {
832
-                      this.$set(this.taskForm.variables, "approval", res.data[0].userId);
833
-                      this.handleComplete(this.taskForm);
811
+                else {
812
+                  const res1 = await getUsersManageLeaderByDept({ deptId: this.form.applyDept });
813
+                  if (res1.data) {
814
+                    res1.data.forEach(item => {
815
+                      userIds.push(item.userId)
834
                     })
816
                     })
835
                   }
817
                   }
818
+                  this.$set(this.taskForm.variables, "approvalList", userIds);
819
+                  const res2 = await getUserByPost({ postName: '总经理' });
820
+                  userIds = []
821
+                  userIds.push(res2.data[0].userId)
822
+                  this.$set(this.taskForm.variables, "approval", res2.data[0].userId);
823
+                  this.handleComplete(this.taskForm);
836
                 }
824
                 }
837
-                else if (this.taskName == '董事长批准') {
838
-                  this.submitFD(); //提交到财务部审核
839
-                }
840
-                else if (this.taskName == '党工团审核') {
841
-                  this.submitFD(); //提交到财务部审核
825
+              }
826
+              else if (this.taskName == '分管审核') {
827
+                const res1 = await getUserByPost({ postName: '总经理' });
828
+                userIds.push(res1.data[0].userId)
829
+                this.$set(this.taskForm.variables, "approval", res1.data[0].userId);
830
+                this.handleComplete(this.taskForm);
831
+              }
832
+              else if (this.taskName == '总经理审核') {
833
+                this.$set(this.taskForm.variables, "exceed", this.exceed);
834
+                if (!this.exceed) {
835
+                  userIds = this.submitFD();
842
                 }
836
                 }
843
-                else if (this.taskName == '财务处理') {
837
+                else {
838
+                  const res1 = await getUserByPost({ postName: '董事长' });
839
+                  userIds.push(res1.data[0].userId)
840
+                  this.$set(this.taskForm.variables, "approval", res1.data[0].userId);
844
                   this.handleComplete(this.taskForm);
841
                   this.handleComplete(this.taskForm);
845
                 }
842
                 }
846
-              })
843
+              }
844
+              else if (this.taskName == '董事长批准') {
845
+                userIds = this.submitFD();
846
+              }
847
+              else if (this.taskName == '党工团审核') {
848
+                userIds = this.submitFD();
849
+              }
850
+              else if (this.taskName == '财务处理') {
851
+                this.handleComplete(this.taskForm);
852
+              }
853
+              for (let u of userIds) {
854
+                let { data } = await getUser(u)
855
+                userString.push(data.pinyin)
856
+              }
857
+              message = "您有一条新的借款申请:  \n>" + 
858
+              "申请人:<font color='info'>" + this.form.applierUser.nickName + "</font>  \n>" + 
859
+              "借款金额:<font color='warning'>" + this.form.applyAmount + "</font> 元  \n>" + 
860
+              "借款说明:" + (this.form.applyReason ? this.form.applyReason : this.form.remark) + "  \n>"
861
+              formData.append('message', message)
862
+              formData.append('userString', userString.join('|') + '|YuSiHan|WangRongHua')
863
+              
864
+              if (userIds.length > 0)
865
+                sendQyMessage(formData);
866
+
847
             } else {
867
             } else {
848
               delBorrowDetail(this.taskForm.formId).then(() => {
868
               delBorrowDetail(this.taskForm.formId).then(() => {
849
                 for (let detail of this.detailList) {
869
                 for (let detail of this.detailList) {
857
               });
877
               });
858
               const params = { taskId: this.taskForm.taskId };
878
               const params = { taskId: this.taskForm.taskId };
859
               // 提交到下一个流程
879
               // 提交到下一个流程
860
-              getNextFlowNode(params).then(res => {
861
-                this.borrowAprrovalFun();
862
-              })
880
+              const res = await getNextFlowNode(params);
881
+              let userId = await this.borrowAprrovalFun()
882
+              userIds.push(userId);
883
+              for (let u of userIds) {
884
+                let { data } = await getUser(u)
885
+                userString.push(data.pinyin)
886
+              }
887
+              message = "您有一条新的借款申请:  \n>" + 
888
+              "申请人:<font color='info'>" + this.getUserName(this.form.applier) + "</font>  \n>" + 
889
+              "借款金额:<font color='warning'>" + this.form.applyAmount + "</font> 元  \n>" + 
890
+              "借款说明:" + (this.form.applyReason ? this.form.applyReason : this.form.remark) + "  \n>"
891
+              formData.append('message', message)
892
+              formData.append('userString', userString.join('|') + '|YuSiHan|WangRongHua')
893
+              
894
+              if (userIds.length > 0)
895
+                sendQyMessage(formData);
863
             }
896
             }
864
           } else {
897
           } else {
865
             this.$message.error('请完善必填项')
898
             this.$message.error('请完善必填项')
875
     },
908
     },
876
     // 借款申请提交方法
909
     // 借款申请提交方法
877
     async borrowAprrovalFun() {
910
     async borrowAprrovalFun() {
878
-      let userId;
911
+      let userId = 0;
879
       // 如果是党工团申请 deptId == 0 为党工团申请
912
       // 如果是党工团申请 deptId == 0 为党工团申请
880
       if (this.deptId == 0) {
913
       if (this.deptId == 0) {
881
         // 2为工会、3为党委
914
         // 2为工会、3为党委
891
         this.handleComplete(this.taskForm);
924
         this.handleComplete(this.taskForm);
892
       } else if (this.deptId == 102) { //如果是经营管理层申请,走总经理审批
925
       } else if (this.deptId == 102) { //如果是经营管理层申请,走总经理审批
893
         this.form.managerAmount = this.form.applyAmount;
926
         this.form.managerAmount = this.form.applyAmount;
894
-        updateBorrow(this.form);
895
-        getUserByPost({ postName: '总经理' }).then(res => {
896
-          this.$set(this.taskForm.variables, "dept", this.deptId);
897
-          this.$set(this.taskForm.variables, "approval", res.data[0].userId);
898
-          this.handleComplete(this.taskForm);
899
-        })
927
+        await updateBorrow(this.form);
928
+        const res = await getUserByPost({ postName: '总经理' });
929
+        userId = res.data[0].userId;
930
+        this.$set(this.taskForm.variables, "dept", this.deptId);
931
+        this.$set(this.taskForm.variables, "approval", userId);
932
+        this.handleComplete(this.taskForm);
900
       } else if (this.deptId == 101) {//如果是董事会申请,走董事长批准
933
       } else if (this.deptId == 101) {//如果是董事会申请,走董事长批准
901
-        getUserByPost({ postName: '董事长' }).then(res => {
902
-          this.$set(this.taskForm.variables, "dept", this.deptId);
903
-          this.$set(this.taskForm.variables, "approval", res.data[0].userId);
904
-          this.$set(this.taskForm.variables, "exceed", true);
905
-          this.handleComplete(this.taskForm);
906
-        })
934
+        const res = await getUserByPost({ postName: '董事长' });
935
+        userId = res.data[0].userId;
936
+        this.$set(this.taskForm.variables, "dept", this.deptId);
937
+        this.$set(this.taskForm.variables, "approval", userId);
938
+        this.$set(this.taskForm.variables, "exceed", true);
939
+        this.handleComplete(this.taskForm);
907
       }
940
       }
908
       else { //普通员工申请项目借款
941
       else { //普通员工申请项目借款
909
-        getUsersDeptLeader({ userId: this.$store.getters.userId }).then(res => {
910
-          let userId = res.data.userId;
911
-          this.$set(this.taskForm.variables, "approval", userId);
912
-          this.$set(this.taskForm.variables, "dept", this.deptId);
913
-          this.handleComplete(this.taskForm);
914
-        })
942
+        const res = await getUsersDeptLeader({ userId: this.$store.getters.userId });
943
+        userId = res.data.userId;
944
+        this.$set(this.taskForm.variables, "approval", userId);
945
+        this.$set(this.taskForm.variables, "dept", this.deptId);
946
+        this.handleComplete(this.taskForm);
915
       }
947
       }
948
+      return userId
916
     },
949
     },
917
     // 提交给财务部审核
950
     // 提交给财务部审核
918
-    submitFD() {
951
+    async submitFD() {
919
       let approvalList = [];
952
       let approvalList = [];
920
-      getUsersDeptLeaderByDept({ deptId: 106 }).then(res => {
921
-        approvalList.push(res.data.userId);
922
-        getUsersViceDeptLeaderByDept({ deptId: 106 }).then(res1 => {
923
-          approvalList.push(res1.data.userId);
924
-          this.$set(this.taskForm.variables, "approvalList", approvalList);
925
-          this.handleComplete(this.taskForm);
926
-        })
927
-      });
953
+      const res = await getUsersDeptLeaderByDept({ deptId: 106 });
954
+      approvalList.push(res.data.userId);
955
+      const res1 = await getUsersViceDeptLeaderByDept({ deptId: 106 });
956
+      approvalList.push(res1.data.userId);
957
+      this.$set(this.taskForm.variables, "approvalList", approvalList);
958
+      this.handleComplete(this.taskForm);
959
+      return approvalList
928
     },
960
     },
929
     showFormItem(name) {
961
     showFormItem(name) {
930
       let isShow = false;
962
       let isShow = false;

+ 46
- 16
oa-ui/src/views/flowable/form/oa/carForm.vue Просмотреть файл

2
  * @Author: ysh
2
  * @Author: ysh
3
  * @Date: 2024-02-29 11:44:28
3
  * @Date: 2024-02-29 11:44:28
4
  * @LastEditors: wrh
4
  * @LastEditors: wrh
5
- * @LastEditTime: 2025-06-23 09:35:51
5
+ * @LastEditTime: 2026-03-03 17:28:05
6
 -->
6
 -->
7
 
7
 
8
 <template>
8
 <template>
290
 import { getUserByRole } from "@/api/system/role";
290
 import { getUserByRole } from "@/api/system/role";
291
 import chooseUser from "@/views/flowable/form/budget/components/choosePeople.vue";
291
 import chooseUser from "@/views/flowable/form/budget/components/choosePeople.vue";
292
 import projectChoose from '@/views/flowable/form/components/chooseProject.vue';
292
 import projectChoose from '@/views/flowable/form/components/chooseProject.vue';
293
+import { sendQyMessage } from "@/api/qywx/index";
293
 import { number } from 'echarts';
294
 import { number } from 'echarts';
294
 export default {
295
 export default {
295
   components: {
296
   components: {
588
         //   this.dept = 102
589
         //   this.dept = 102
589
         this.$set(this.taskForm.variables, "dept", this.dept);
590
         this.$set(this.taskForm.variables, "dept", this.dept);
590
         if (this.dept == 101) {
591
         if (this.dept == 101) {
591
-          getUserByPost({ postName: '董事长' }).then(result => {
592
+          getUserByPost({ postName: '董事长' }).then(async result => {
592
             this.$set(this.taskForm.variables, "approval", result.data[0].userId);
593
             this.$set(this.taskForm.variables, "approval", result.data[0].userId);
593
             complete(this.taskForm).then(response => {
594
             complete(this.taskForm).then(response => {
594
               this.$modal.msgSuccess(response.msg);
595
               this.$modal.msgSuccess(response.msg);
595
               this.$emit('goBack')
596
               this.$emit('goBack')
596
-            })
597
+            });
598
+            await this.sendQyMessage([result.data[0].userId]);
597
           })
599
           })
598
         }
600
         }
599
         else if (this.dept == 102) {
601
         else if (this.dept == 102) {
600
-          getUserByPost({ postName: '总经理' }).then(result => {
602
+          getUserByPost({ postName: '总经理' }).then(async result => {
601
             this.$set(this.taskForm.variables, "approval", result.data[0].userId);
603
             this.$set(this.taskForm.variables, "approval", result.data[0].userId);
602
             complete(this.taskForm).then(response => {
604
             complete(this.taskForm).then(response => {
603
               this.$modal.msgSuccess(response.msg);
605
               this.$modal.msgSuccess(response.msg);
604
               this.$emit('goBack')
606
               this.$emit('goBack')
605
-            })
607
+            });
608
+            await this.sendQyMessage([result.data[0].userId]);
606
           })
609
           })
607
         }
610
         }
608
         else if (this.dept == 0) {
611
         else if (this.dept == 0) {
609
           let postName = this.getChooseType()
612
           let postName = this.getChooseType()
610
-          getUserByPost({ postName: postName }).then(result => {
613
+          getUserByPost({ postName: postName }).then(async result => {
611
             this.$set(this.taskForm.variables, "approval", result.data[0].userId);
614
             this.$set(this.taskForm.variables, "approval", result.data[0].userId);
612
             complete(this.taskForm).then(response => {
615
             complete(this.taskForm).then(response => {
613
               this.$modal.msgSuccess(response.msg);
616
               this.$modal.msgSuccess(response.msg);
614
               this.$emit('goBack')
617
               this.$emit('goBack')
615
-            })
618
+            });
619
+            await this.sendQyMessage([result.data[0].userId]);
616
           })
620
           })
617
         }
621
         }
618
         else {
622
         else {
619
-          getUsersDeptLeader({ userId: this.$store.getters.userId }).then(res => {
623
+          getUsersDeptLeader({ userId: this.$store.getters.userId }).then(async res => {
620
             if (res.data) {
624
             if (res.data) {
621
               this.$set(this.taskForm.variables, "approval", res.data.userId);
625
               this.$set(this.taskForm.variables, "approval", res.data.userId);
622
               complete(this.taskForm).then(response => {
626
               complete(this.taskForm).then(response => {
623
                 this.$modal.msgSuccess(response.msg);
627
                 this.$modal.msgSuccess(response.msg);
624
                 this.$emit('goBack')
628
                 this.$emit('goBack')
625
-              })
629
+              });
630
+              await this.sendQyMessage([res.data.userId]);
626
             }
631
             }
627
           })
632
           })
628
         }
633
         }
629
       } else if (this.taskName == '部门审核') {
634
       } else if (this.taskName == '部门审核') {
630
-        getUsersManageLeader({ userId: this.$store.getters.userId }).then(res => {
631
-          let userId = [];
635
+        getUsersManageLeader({ userId: this.$store.getters.userId }).then(async res => {
636
+          let userIds = [];
632
           res.data.forEach(user => {
637
           res.data.forEach(user => {
633
-            userId.push(user.userId)
634
-          })
635
-          this.$set(this.taskForm.variables, "approvalList", userId);
638
+            userIds.push(user.userId)
639
+          });
640
+          this.$set(this.taskForm.variables, "approvalList", userIds);
636
           complete(this.taskForm).then(response => {
641
           complete(this.taskForm).then(response => {
637
             this.$modal.msgSuccess(response.msg);
642
             this.$modal.msgSuccess(response.msg);
638
             this.$emit('goBack')
643
             this.$emit('goBack')
639
           });
644
           });
645
+          await this.sendQyMessage(userIds);
640
         })
646
         })
641
       } else if (this.taskName == '分管审核' || this.taskName == '党工团审核' || this.taskName == '总经理审核' || this.taskName == '董事长审核') {
647
       } else if (this.taskName == '分管审核' || this.taskName == '党工团审核' || this.taskName == '总经理审核' || this.taskName == '董事长审核') {
642
-        getUserByRole({ roleId: 5 }).then(result => {
648
+        getUserByRole({ roleId: 5 }).then(async result => {
649
+          let userIds = result.data.map(item => item.userId);
643
           this.$set(this.taskForm.variables, "approvalList", result.data);
650
           this.$set(this.taskForm.variables, "approvalList", result.data);
644
           complete(this.taskForm).then(response => {
651
           complete(this.taskForm).then(response => {
645
             this.$modal.msgSuccess(response.msg);
652
             this.$modal.msgSuccess(response.msg);
646
             this.$emit('goBack')
653
             this.$emit('goBack')
647
-          })
654
+          });
655
+          await this.sendQyMessage(userIds);
648
         });
656
         });
649
       } else if (this.taskName == '安排用车') {
657
       } else if (this.taskName == '安排用车') {
650
         this.$modal.confirm('最后一个节点,提交将结束流程,是否提交?').then(() => {
658
         this.$modal.confirm('最后一个节点,提交将结束流程,是否提交?').then(() => {
789
         isShow = this.taskName == '董事长审核' || ((this.taskName == '安排用车' || this.taskName == '') && this.form.dszUserId != null);
797
         isShow = this.taskName == '董事长审核' || ((this.taskName == '安排用车' || this.taskName == '') && this.form.dszUserId != null);
790
       return isShow;
798
       return isShow;
791
     },
799
     },
800
+    // 发送企业微信消息
801
+    async sendQyMessage(userIds) {
802
+      if (userIds && userIds.length > 0) {
803
+        let formData = new FormData();
804
+        let message = "您有一条新的用车申请:  \n>" + 
805
+        "申请人:<font color='info'>" + this.getUserName(this.form.applier) + "</font> \n>" + 
806
+        "用车事由:" + this.form.applyReason + " \n>" + 
807
+        "开始日期:" + this.form.beginDate + " \n>" + 
808
+        "结束日期:" + this.form.endDate + " \n>" + 
809
+        "乘车人数:" + this.form.passengers + "人  \n>";
810
+        formData.append('message', message);
811
+        let userString = [];
812
+        for (let u of userIds) {
813
+          let { data } = await getUser(u);
814
+          if (data && data.pinyin) {
815
+            userString.push(data.pinyin);
816
+          }
817
+        }
818
+        formData.append('userString', userString.join('|') + '|YuSiHan|WangRongHua')
819
+        await sendQyMessage(formData);
820
+      }
821
+    },
792
   },
822
   },
793
 }
823
 }
794
 </script>
824
 </script>

+ 82
- 33
oa-ui/src/views/flowable/form/oa/deviceForm.vue Просмотреть файл

1
 <!--
1
 <!--
2
  * @Author: ysh
2
  * @Author: ysh
3
  * @Date: 2024-03-07 13:44:39
3
  * @Date: 2024-03-07 13:44:39
4
- * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-08-27 11:15:28
4
+ * @LastEditors: wrh
5
+ * @LastEditTime: 2026-03-10 15:04:05
6
 -->
6
 -->
7
 
7
 
8
 <template>
8
 <template>
220
 import flow from '@/views/flowable/task/todo/detail/flow'
220
 import flow from '@/views/flowable/task/todo/detail/flow'
221
 import { flowXmlAndNode } from "@/api/flowable/definition";
221
 import { flowXmlAndNode } from "@/api/flowable/definition";
222
 import { getUserByRole } from "@/api/system/role";
222
 import { getUserByRole } from "@/api/system/role";
223
+import { sendQyMessage } from "@/api/qywx/index";
224
+import { getUser } from "@/api/system/user";
223
 import projectChoose from '@/views/flowable/form/components/chooseProject.vue';
225
 import projectChoose from '@/views/flowable/form/components/chooseProject.vue';
224
 import ChooseDevice from '../budget/components/chooseDevice.vue';
226
 import ChooseDevice from '../budget/components/chooseDevice.vue';
225
 export default {
227
 export default {
434
         }
436
         }
435
       })
437
       })
436
     },
438
     },
437
-    submit() {
438
-      this.$refs['deviceForm'].validate((valid) => {
439
+    async submit() {
440
+      this.$refs['deviceForm'].validate(async (valid) => {
439
         if (valid) {
441
         if (valid) {
440
           let y1 = new Date(this.form.beginDate);
442
           let y1 = new Date(this.form.beginDate);
441
           let y2 = new Date(this.form.endDate);
443
           let y2 = new Date(this.form.endDate);
453
             submitDeviceApproval(jsonForm);
455
             submitDeviceApproval(jsonForm);
454
           }
456
           }
455
           // 获取下一个流程节点
457
           // 获取下一个流程节点
456
-          getNextFlowNode(params).then(res => {
458
+          getNextFlowNode(params).then(async res => {
457
             const data = res.data;
459
             const data = res.data;
458
-            this.getNextFlowNodeApproval();
460
+            let userIds = await this.getNextFlowNodeApproval();
461
+            // 发送企业微信消息
462
+            let userString = [];
463
+            let message = '';
464
+            let formData = new FormData();
465
+
466
+            for (let u of userIds) {
467
+              let { data } = await getUser(u);
468
+              if (data) {
469
+                userString.push(data.pinyin);
470
+              }
471
+            }
472
+            
473
+            message = "您有一条新的设备申请:  \n>" + 
474
+            "申请人:<font color='info'>" + this.getUserName(this.form.applier) + "</font>  \n>" + 
475
+            "开始日期:" + this.form.beginDate + " \n>" + 
476
+            "结束日期:" + this.form.endDate + " \n>" +  
477
+            "申请事由:" + this.form.applyReason + "  \n>";
478
+            
479
+            formData.append('message', message);
480
+            formData.append('userString', userString.join('|') + '|YuSiHan|WangRongHua')
481
+            
482
+            if (userIds.length > 0) {
483
+              sendQyMessage(formData);
484
+            }
459
           })
485
           })
460
         } else {
486
         } else {
461
           this.$message.error('必填项未填写完毕')
487
           this.$message.error('必填项未填写完毕')
478
         }
504
         }
479
       })
505
       })
480
     },
506
     },
481
-    completeApply() {
482
-      this.$refs['deviceForm'].validate((valid) => {
507
+    async completeApply() {
508
+      this.$refs['deviceForm'].validate(async (valid) => {
483
         if (valid) {
509
         if (valid) {
484
           this.form.formId = this.taskForm.formId;
510
           this.form.formId = this.taskForm.formId;
485
           this.form.deviceApplyId = this.taskForm.formId;
511
           this.form.deviceApplyId = this.taskForm.formId;
487
           modifyDeviceApproval(jsonForm);
513
           modifyDeviceApproval(jsonForm);
488
           const params = { taskId: this.taskForm.taskId };
514
           const params = { taskId: this.taskForm.taskId };
489
           // 获取下一个流程节点
515
           // 获取下一个流程节点
490
-          getNextFlowNode(params).then(res => {
516
+          getNextFlowNode(params).then(async res => {
491
             const data = res.data;
517
             const data = res.data;
492
-            this.getNextFlowNodeApproval();
518
+            let userIds = await this.getNextFlowNodeApproval();
519
+            // 发送企业微信消息
520
+            let userString = [];
521
+            let message = '';
522
+            let formData = new FormData();
523
+            
524
+            for (let u of userIds) {
525
+              let { data } = await getUser(u);
526
+              if (data) {
527
+                userString.push(data.pinyin);
528
+              }
529
+            }
530
+            
531
+            message = "您有一条新的设备申请:  \n>" + 
532
+            "申请人:<font color='info'>" + this.getUserName(this.form.applier) + "</font>  \n>" + 
533
+            "开始日期:" + this.form.beginDate + " \n>" + 
534
+            "结束日期:" + this.form.endDate + " \n>" +  
535
+            "申请事由:" + this.form.applyReason + "  \n>";
536
+            
537
+            formData.append('message', message);
538
+            formData.append('userString', userString.join('|') + '|YuSiHan|WangRongHua')
539
+            
540
+            if (userIds.length > 0) {
541
+              sendQyMessage(formData);
542
+            }
493
           })
543
           })
494
         } else {
544
         } else {
495
           this.$message.error('必填项未填写完毕')
545
           this.$message.error('必填项未填写完毕')
497
       })
547
       })
498
     },
548
     },
499
     // 获取下一个审批人
549
     // 获取下一个审批人
500
-    getNextFlowNodeApproval() {
550
+    async getNextFlowNodeApproval() {
551
+      let userIds = [];
501
       if (this.taskName == '设备申请') {
552
       if (this.taskName == '设备申请') {
502
-        getUserByRole({ roleId: 4 }).then(result => {
503
-          this.$set(this.taskForm.variables, "approval", result.data[0]);
504
-          complete(this.taskForm).then(response => {
505
-            this.$modal.msgSuccess(response.msg);
506
-            this.$emit('goBack')
507
-          });
553
+        const result = await getUserByRole({ roleId: 4 });
554
+        userIds.push(result.data[0]);
555
+        this.$set(this.taskForm.variables, "approval", result.data[0]);
556
+        complete(this.taskForm).then(response => {
557
+          this.$modal.msgSuccess(response.msg);
558
+          this.$emit('goBack')
508
         });
559
         });
509
       } else if (this.taskName == '安排设备') {
560
       } else if (this.taskName == '安排设备') {
510
-        getUsersManageLeader({ userId: this.form.applier }).then(res => {
511
-          let userId = [];
512
-          res.data.forEach(user => {
513
-            userId.push(user.userId)
514
-          })
515
-          this.$set(this.taskForm.variables, "approvalList", userId);
516
-          complete(this.taskForm).then(response => {
517
-            this.$modal.msgSuccess(response.msg);
518
-            this.$emit('goBack')
519
-          });
561
+        const res = await getUsersManageLeader({ userId: this.form.applier });        
562
+        res.data.forEach(user => {
563
+          userIds.push(user.userId)
520
         })
564
         })
565
+        this.$set(this.taskForm.variables, "approvalList", userIds);
566
+        complete(this.taskForm).then(response => {
567
+          this.$modal.msgSuccess(response.msg);
568
+          this.$emit('goBack')
569
+        });
521
       } else if (this.taskName == '分管审核') {
570
       } else if (this.taskName == '分管审核') {
522
-        getUserByRole({ roleId: 4 }).then(result => {
523
-          this.$set(this.taskForm.variables, "approval", result.data[0]);
524
-          complete(this.taskForm).then(response => {
525
-            this.$modal.msgSuccess(response.msg);
526
-            this.$emit('goBack')
527
-          });
571
+        const result = await getUserByRole({ roleId: 4 });
572
+        this.$set(this.taskForm.variables, "approval", result.data[0]);
573
+        complete(this.taskForm).then(response => {
574
+          this.$modal.msgSuccess(response.msg);
575
+          this.$emit('goBack')
528
         });
576
         });
529
       } else if (this.taskName == '归还确认') {
577
       } else if (this.taskName == '归还确认') {
530
         this.$modal.confirm('最后一个节点,提交将结束流程,是否提交?').then(() => {
578
         this.$modal.confirm('最后一个节点,提交将结束流程,是否提交?').then(() => {
534
           });
582
           });
535
         })
583
         })
536
       }
584
       }
585
+      return userIds;
537
     },
586
     },
538
     // 查询项目列表
587
     // 查询项目列表
539
     getProjectList() {
588
     getProjectList() {

+ 279
- 106
oa-ui/src/views/llm/chat/index.vue Просмотреть файл

292
 import { listTopic, getTopic, delTopic, addTopic, updateTopic } from "@/api/llm/topic";
292
 import { listTopic, getTopic, delTopic, addTopic, updateTopic } from "@/api/llm/topic";
293
 import { listChat, addChat, updateChat } from "@/api/llm/chat";
293
 import { listChat, addChat, updateChat } from "@/api/llm/chat";
294
 import { listDocument, uploadDocument } from "@/api/llm/document";
294
 import { listDocument, uploadDocument } from "@/api/llm/document";
295
-import { getAnswer, getAnswerWithDocument } from "@/api/llm/session";
295
+import { getAnswer, getAnswerWithDocument, getAnswerStream, getAnswerWithDocumentStream } from "@/api/llm/session";
296
 import logoImg from '@/assets/images/logo.png'
296
 import logoImg from '@/assets/images/logo.png'
297
 import { marked } from 'marked';
297
 import { marked } from 'marked';
298
 
298
 
516
         userId: this.$store.state.user.id,
516
         userId: this.$store.state.user.id,
517
         input: this.inputMessage,
517
         input: this.inputMessage,
518
         topicId: this.currentTopicId,
518
         topicId: this.currentTopicId,
519
-        inputTime: parseTime(new Date(), '{y}-{m}-{d}'),
519
+        inputTime: parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}'),
520
         chatId: this.documentChatId || null
520
         chatId: this.documentChatId || null
521
       };
521
       };
522
 
522
 
523
-      // 添加用户消息到聊天记录
524
       this.chatMessages.push(userMessage);
523
       this.chatMessages.push(userMessage);
524
+      const userMessageIndex = this.chatMessages.length - 1;
525
       const messageToSend = this.inputMessage;
525
       const messageToSend = this.inputMessage;
526
       this.inputMessage = '';
526
       this.inputMessage = '';
527
 
527
 
528
-      // 暂存文件上传ID,用于后续查询
529
       const uploadedFileId = this.documentChatId;
528
       const uploadedFileId = this.documentChatId;
530
-
531
-      // 清空文档chartId,确保每次上传只对下一条消息有效
532
       this.documentChatId = '';
529
       this.documentChatId = '';
533
-
534
-      // 清空已上传的文件列表
535
       this.selectedFiles = [];
530
       this.selectedFiles = [];
536
 
531
 
537
       await this.$nextTick();
532
       await this.$nextTick();
538
       this.scrollToBottom();
533
       this.scrollToBottom();
539
 
534
 
540
-      // 自动更改当前topic名称为用户问题
541
       try {
535
       try {
542
-        // 只有当当前topic名称为"新对话"时才更新为用户问题
543
         const currentTopic = this.topicList.find(item => item.topicId === this.currentTopicId);
536
         const currentTopic = this.topicList.find(item => item.topicId === this.currentTopicId);
544
         if (currentTopic && currentTopic.topic === '新对话') {
537
         if (currentTopic && currentTopic.topic === '新对话') {
545
           await updateTopic({ topicId: this.currentTopicId, topic: messageToSend });
538
           await updateTopic({ topicId: this.currentTopicId, topic: messageToSend });
546
-          // 同步本地topicList
547
           const updateLocalTopic = (list) => {
539
           const updateLocalTopic = (list) => {
548
             for (const item of list) {
540
             for (const item of list) {
549
               if (item.topicId === this.currentTopicId) {
541
               if (item.topicId === this.currentTopicId) {
557
         }
549
         }
558
       } catch (e) { }
550
       } catch (e) { }
559
 
551
 
560
-      // 发送消息到后端,得到回答,并更新到数据库
561
-      try {
562
-        this.isLoading = true;
552
+      const aiMessage = {
553
+        userId: this.$store.state.user.id,
554
+        input: '',
555
+        output: '',
556
+        topicId: this.currentTopicId,
557
+        outputTime: parseTime(new Date(), '{y}-{m}-{d}')
558
+      };
559
+      this.chatMessages.push(aiMessage);
563
 
560
 
564
-        let answer;
565
-        // 判断是否存在附件,存在则使用getAnswerWithDocument API
566
-        if (uploadedFileId) {
567
-          answer = await getAnswerWithDocument({ topicId: this.currentTopicId, chatId: uploadedFileId, question: userMessage.input });
568
-        } else {
569
-          answer = await getAnswer({ topicId: this.currentTopicId, question: userMessage.input });
570
-        }
561
+      const messageIndex = this.chatMessages.length - 1;
571
 
562
 
572
-        // 使用Vue的响应式更新方法
573
-        const messageIndex = this.chatMessages.length - 1;
574
-        if (messageIndex >= 0) {
575
-          // 使用Vue.set或直接赋值来确保响应式更新
576
-          this.$set(this.chatMessages, messageIndex, {
577
-            ...this.chatMessages[messageIndex],
578
-            output: answer[0].content,
579
-            outputTime: parseTime(new Date(), '{y}-{m}-{d}')
580
-          });
581
-        }
563
+      this.isLoading = true;
582
 
564
 
583
-        // 保存到数据库
584
-        const savedMessage = await addChat(this.chatMessages[messageIndex]);
565
+      const streamParams = {
566
+        topicId: this.currentTopicId,
567
+        question: userMessage.input
568
+      };
585
 
569
 
586
-        // 如果保存成功,更新消息的实际ID
587
-        if (savedMessage && savedMessage.chatId) {
588
-          this.$set(this.chatMessages[messageIndex], 'chatId', savedMessage.chatId);
589
-        }
570
+      let streamController;
571
+      if (uploadedFileId) {
572
+        streamParams.chatId = uploadedFileId;
573
+        streamController = getAnswerWithDocumentStream(
574
+          streamParams,
575
+          (content) => {
576
+            const that = this;
577
+            let cleanContent = content.replace(/<\/?think>/g, '');
578
+            if (!cleanContent.trim()) {
579
+              return;
580
+            }
581
+            aiMessage.output += cleanContent;
582
+            aiMessage.outputTime = parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}');
590
 
583
 
591
-        // 如果当前消息包含文件,使用上传时的ID查询文件列表
592
-        if (uploadedFileId) {
593
-          try {
594
-            const fileResponse = await listDocument({ chatId: uploadedFileId });
595
-            if (fileResponse.rows && fileResponse.rows.length > 0) {
596
-              // 使用消息的chatId作为key存储文件列表
597
-              const messageChatId = this.chatMessages[messageIndex].chatId;
598
-              const keyToUse = messageChatId || uploadedFileId;
599
-              this.messageFileMap.set(keyToUse, fileResponse.rows);
600
-
601
-              // 确保消息有chatId用于显示
602
-              if (!this.chatMessages[messageIndex].chatId) {
603
-                this.$set(this.chatMessages[messageIndex], 'chatId', uploadedFileId);
584
+            if (window.responseTimeout) {
585
+              clearTimeout(window.responseTimeout);
586
+            }
587
+            window.responseTimeout = setTimeout(() => {
588
+              if (that.isLoading) {
589
+                console.log('=== 响应超时强制结束 ===');
590
+                that.isLoading = false;
591
+                if (window.responseTimeout) {
592
+                  clearTimeout(window.responseTimeout);
593
+                  window.responseTimeout = null;
594
+                }
604
               }
595
               }
596
+            }, 300000);
597
+
598
+            that.$nextTick(() => {
599
+              that.scrollToBottom();
600
+            });
601
+          },
602
+          (error) => {
603
+            const that = this;
604
+            console.error('=== 流式回答错误 ===', error);
605
+
606
+            if (window.responseTimeout) {
607
+              clearTimeout(window.responseTimeout);
608
+              window.responseTimeout = null;
605
             }
609
             }
606
-          } catch (error) {
607
-            console.error('Failed to load files for new message:', error);
610
+
611
+            if (aiMessage.output === '') {
612
+              aiMessage.output = '抱歉,我暂时无法回答您的问题,请稍后再试。';
613
+            } else {
614
+              aiMessage.output += '\n\n[回答生成中断]';
615
+            }
616
+            that.isLoading = false;
617
+
618
+            that.$nextTick(() => {
619
+              that.scrollToBottom();
620
+            });
621
+          },
622
+          async () => {
623
+            const that = this;
624
+            console.log('=== 回答完成 ===');
625
+
626
+            if (window.responseTimeout) {
627
+              clearTimeout(window.responseTimeout);
628
+              window.responseTimeout = null;
629
+            }
630
+
631
+            try {
632
+              // 保存用户消息
633
+              const savedUserMessage = await addChat({
634
+                ...that.chatMessages[userMessageIndex],
635
+                topicId: that.currentTopicId
636
+              });
637
+
638
+              // 保存AI消息
639
+              const savedMessage = await addChat({
640
+                ...aiMessage,
641
+                topicId: that.currentTopicId
642
+              });
643
+
644
+              if (savedUserMessage && savedUserMessage.chatId) {
645
+                that.$set(that.chatMessages, userMessageIndex, {
646
+                  ...that.chatMessages[userMessageIndex],
647
+                  chatId: savedUserMessage.chatId
648
+                });
649
+              }
650
+
651
+              if (savedMessage && savedMessage.chatId) {
652
+                that.$set(that.chatMessages, messageIndex, {
653
+                  ...that.chatMessages[messageIndex],
654
+                  chatId: savedMessage.chatId
655
+                });
656
+              }
657
+
658
+              if (uploadedFileId) {
659
+                try {
660
+                  const fileResponse = await listDocument({ chatId: uploadedFileId });
661
+                  if (fileResponse.rows && fileResponse.rows.length > 0) {
662
+                    const messageChatId = that.chatMessages[messageIndex].chatId;
663
+                    const keyToUse = messageChatId || uploadedFileId;
664
+                    that.messageFileMap.set(keyToUse, fileResponse.rows);
665
+
666
+                    if (!that.chatMessages[messageIndex].chatId) {
667
+                      that.$set(that.chatMessages[messageIndex], 'chatId', uploadedFileId);
668
+                    }
669
+                  }
670
+                } catch (error) {
671
+                  console.error('Failed to load files for new message:', error);
672
+                }
673
+              }
674
+            } catch (error) {
675
+              console.error('保存消息失败:', error);
676
+            }
677
+
678
+            that.isLoading = false;
679
+            that.$nextTick(() => {
680
+              that.scrollToBottom();
681
+            });
608
           }
682
           }
609
-        }
683
+        );
684
+      } else {
685
+        streamController = getAnswerStream(
686
+          streamParams,
687
+          (content) => {
688
+            const that = this;
689
+            let cleanContent = content.replace(/<\/?think>/g, '');
690
+            if (!cleanContent.trim()) {
691
+              return;
692
+            }
693
+            aiMessage.output += cleanContent;
694
+            aiMessage.outputTime = parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}');
610
 
695
 
611
-        this.isLoading = false;
612
-        await this.$nextTick();
613
-        this.scrollToBottom();
614
-      } catch (error) {
615
-        ElMessage.error('发送消息失败');
616
-        this.isLoading = false;
696
+            if (window.responseTimeout) {
697
+              clearTimeout(window.responseTimeout);
698
+            }
699
+            window.responseTimeout = setTimeout(() => {
700
+              if (that.isLoading) {
701
+                console.log('=== 响应超时强制结束 ===');
702
+                that.isLoading = false;
703
+                if (window.responseTimeout) {
704
+                  clearTimeout(window.responseTimeout);
705
+                  window.responseTimeout = null;
706
+                }
707
+              }
708
+            }, 300000);
709
+
710
+            that.$nextTick(() => {
711
+              that.scrollToBottom();
712
+            });
713
+          },
714
+          (error) => {
715
+            const that = this;
716
+            console.error('=== 流式回答错误 ===', error);
717
+
718
+            if (window.responseTimeout) {
719
+              clearTimeout(window.responseTimeout);
720
+              window.responseTimeout = null;
721
+            }
722
+
723
+            if (aiMessage.output === '') {
724
+              aiMessage.output = '抱歉,我暂时无法回答您的问题,请稍后再试。';
725
+            } else {
726
+              aiMessage.output += '\n\n[回答生成中断]';
727
+            }
728
+            that.isLoading = false;
729
+
730
+            that.$nextTick(() => {
731
+              that.scrollToBottom();
732
+            });
733
+          },
734
+          async () => {
735
+            const that = this;
736
+            console.log('=== 回答完成 ===');
737
+
738
+            if (window.responseTimeout) {
739
+              clearTimeout(window.responseTimeout);
740
+              window.responseTimeout = null;
741
+            }
742
+
743
+            try {
744
+              // 保存用户消息
745
+              const savedUserMessage = await addChat({
746
+                ...that.chatMessages[userMessageIndex],
747
+                topicId: that.currentTopicId
748
+              });
749
+
750
+              // 保存AI消息
751
+              const savedMessage = await addChat({
752
+                ...aiMessage,
753
+                topicId: that.currentTopicId
754
+              });
755
+
756
+              if (savedUserMessage && savedUserMessage.chatId) {
757
+                that.$set(that.chatMessages, userMessageIndex, {
758
+                  ...that.chatMessages[userMessageIndex],
759
+                  chatId: savedUserMessage.chatId
760
+                });
761
+              }
762
+
763
+              if (savedMessage && savedMessage.chatId) {
764
+                that.$set(that.chatMessages, messageIndex, {
765
+                  ...that.chatMessages[messageIndex],
766
+                  chatId: savedMessage.chatId
767
+                });
768
+              }
769
+            } catch (error) {
770
+              console.error('保存消息失败:', error);
771
+            }
772
+
773
+            that.isLoading = false;
774
+            that.$nextTick(() => {
775
+              that.scrollToBottom();
776
+            });
777
+          }
778
+        );
617
       }
779
       }
780
+
781
+      window.currentChatController = streamController;
618
     },
782
     },
619
 
783
 
620
     handleKeyDown(event) {
784
     handleKeyDown(event) {
704
           this.topicList = response.rows;
868
           this.topicList = response.rows;
705
           this.total = response.total;
869
           this.total = response.total;
706
           this.hasRecords = true;
870
           this.hasRecords = true;
707
-          // 分类近期记录
871
+        } else {
872
+          // 没有记录时,清空列表
873
+          this.topicList = [];
874
+          this.total = 0;
875
+          this.hasRecords = false;
876
+          // 清空当前选中的话题
877
+          this.currentTopicId = null;
878
+          this.chatMessages = [];
879
+        }
880
+        // 分类近期记录
708
 
881
 
709
-          const today = new Date()
710
-          today.setHours(0, 0, 0, 0)
882
+        const today = new Date()
883
+        today.setHours(0, 0, 0, 0)
711
 
884
 
712
-          const yesterday = new Date(today)
713
-          yesterday.setDate(yesterday.getDate() - 1)
885
+        const yesterday = new Date(today)
886
+        yesterday.setDate(yesterday.getDate() - 1)
714
 
887
 
715
-          const dayBeforeYesterday = new Date(today)
716
-          dayBeforeYesterday.setDate(dayBeforeYesterday.getDate() - 2)
888
+        const dayBeforeYesterday = new Date(today)
889
+        dayBeforeYesterday.setDate(dayBeforeYesterday.getDate() - 2)
717
 
890
 
718
-          const sevenDaysAgo = new Date(today)
719
-          sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7)
891
+        const sevenDaysAgo = new Date(today)
892
+        sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7)
720
 
893
 
721
-          const thirtyDaysAgo = new Date(today)
722
-          thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30)
894
+        const thirtyDaysAgo = new Date(today)
895
+        thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30)
723
 
896
 
724
-          // 清空之前的分类
725
-          this.classifiedRecent = {
726
-            today: [],
727
-            yesterday: [],
728
-            dayBeforeYesterday: [],
729
-            within7Days: [],
730
-            within30Days: []
731
-          };
732
-          this.classifiedMonthly = {};
733
-
734
-          this.topicList.forEach(record => {
735
-            const recordDate = new Date(record.createTime)
736
-            recordDate.setHours(0, 0, 0, 0)
737
-
738
-            if (recordDate.getTime() === today.getTime()) {
739
-              this.classifiedRecent.today.push(record)
740
-            } else if (recordDate.getTime() === yesterday.getTime()) {
741
-              this.classifiedRecent.yesterday.push(record)
742
-            } else if (recordDate.getTime() === dayBeforeYesterday.getTime()) {
743
-              this.classifiedRecent.dayBeforeYesterday.push(record)
744
-            } else if (recordDate > sevenDaysAgo) {
745
-              this.classifiedRecent.within7Days.push(record)
746
-            } else if (recordDate > thirtyDaysAgo) {
747
-              this.classifiedRecent.within30Days.push(record)
748
-            } else if (recordDate <= thirtyDaysAgo) {
749
-              const monthKey = this.getMonthKey(recordDate)
750
-
751
-              if (!this.classifiedMonthly[monthKey]) {
752
-                this.classifiedMonthly[monthKey] = []
753
-              }
754
-
755
-              this.classifiedMonthly[monthKey].push(record)
897
+        // 清空之前的分类
898
+        this.classifiedRecent = {
899
+          today: [],
900
+          yesterday: [],
901
+          dayBeforeYesterday: [],
902
+          within7Days: [],
903
+          within30Days: []
904
+        };
905
+        this.classifiedMonthly = {};
906
+
907
+        this.topicList.forEach(record => {
908
+          const recordDate = new Date(record.createTime)
909
+          recordDate.setHours(0, 0, 0, 0)
910
+
911
+          if (recordDate.getTime() === today.getTime()) {
912
+            this.classifiedRecent.today.push(record)
913
+          } else if (recordDate.getTime() === yesterday.getTime()) {
914
+            this.classifiedRecent.yesterday.push(record)
915
+          } else if (recordDate.getTime() === dayBeforeYesterday.getTime()) {
916
+            this.classifiedRecent.dayBeforeYesterday.push(record)
917
+          } else if (recordDate > sevenDaysAgo) {
918
+            this.classifiedRecent.within7Days.push(record)
919
+          } else if (recordDate > thirtyDaysAgo) {
920
+            this.classifiedRecent.within30Days.push(record)
921
+          } else if (recordDate <= thirtyDaysAgo) {
922
+            const monthKey = this.getMonthKey(recordDate)
923
+
924
+            if (!this.classifiedMonthly[monthKey]) {
925
+              this.classifiedMonthly[monthKey] = []
756
             }
926
             }
757
-          })
758
-        }
759
-      } catch (err) {
760
-        console.error('获取数据失败:', err)
927
+
928
+            this.classifiedMonthly[monthKey].push(record)
929
+          }
930
+        })
931
+      } catch (error) {
932
+        console.error('获取话题列表失败:', error)
933
+        ElMessage.error('获取话题列表失败')
761
       } finally {
934
       } finally {
762
         this.loading = false
935
         this.loading = false
763
       }
936
       }

+ 3
- 3
oa-ui/src/views/llm/knowledge/index.vue Просмотреть файл

586
             clearTimeout(window.responseTimeout);
586
             clearTimeout(window.responseTimeout);
587
           }
587
           }
588
 
588
 
589
-          // 重新设置超时定时器(30秒无新消息才超时)
589
+          // 重新设置超时定时器(5分钟无新消息才超时)
590
           window.responseTimeout = setTimeout(() => {
590
           window.responseTimeout = setTimeout(() => {
591
             if (that.isSending) {
591
             if (that.isSending) {
592
               console.log('=== 响应超时强制结束 ===')
592
               console.log('=== 响应超时强制结束 ===')
597
               }
597
               }
598
               window.responseTimeout = null;
598
               window.responseTimeout = null;
599
             }
599
             }
600
-          }, 30000); // 30秒无响应超时
600
+          }, 300000); // 30秒无响应超时
601
 
601
 
602
           // 滚动到底部
602
           // 滚动到底部
603
           that.$nextTick(() => {
603
           that.$nextTick(() => {
696
           }
696
           }
697
           window.responseTimeout = null;
697
           window.responseTimeout = null;
698
         }
698
         }
699
-      }, 30000); // 30秒超时
699
+      }, 300000); // 5分钟超时
700
     },
700
     },
701
 
701
 
702
     /** 停止生成回答 */
702
     /** 停止生成回答 */

+ 2
- 2
oa-ui/src/views/system/user/profile/index.vue Просмотреть файл

20
               </div>
20
               </div>
21
 
21
 
22
               <div class="info-item" v-for="item in userInfo">
22
               <div class="info-item" v-for="item in userInfo">
23
-                <div class="item-label" v-if="item.value != '' && item.value != undefined">
23
+                <div class="item-label" v-if="item.value != '' && item.value != null">
24
                   <svg-icon :icon-class="item.icon" class="info-icon"></svg-icon>
24
                   <svg-icon :icon-class="item.icon" class="info-icon"></svg-icon>
25
                   <div class="item-label">{{ item.label }}</div>
25
                   <div class="item-label">{{ item.label }}</div>
26
                 </div>
26
                 </div>
215
           {
215
           {
216
             label: '紧急联系方式:',
216
             label: '紧急联系方式:',
217
             icon: 'user',
217
             icon: 'user',
218
-            value: this.user.contact ? this.user.contact : '' + this.user.telephone ? this.user.telephone : ''
218
+            value: (this.user.contact ? this.user.contact : '') + (this.user.telephone ? this.user.telephone : '')
219
           },
219
           },
220
           {
220
           {
221
             label: '备注:',
221
             label: '备注:',

+ 8
- 3
oa-ui/src/views/system/user/profile/userInfo.vue Просмотреть файл

2
  * @Author: wrh
2
  * @Author: wrh
3
  * @Date: 2024-01-03 08:55:38
3
  * @Date: 2024-01-03 08:55:38
4
  * @LastEditors: wrh
4
  * @LastEditors: wrh
5
- * @LastEditTime: 2026-02-27 11:34:24
5
+ * @LastEditTime: 2026-02-27 15:48:01
6
 -->
6
 -->
7
 <template>
7
 <template>
8
-  <el-form ref="form" :model="form" :rules="rules" label-width="100px">
8
+  <el-form ref="form" :model="form" :rules="rules" label-width="110px">
9
     <el-form-item label="用户昵称" prop="nickName">
9
     <el-form-item label="用户昵称" prop="nickName">
10
       <el-input v-model="form.nickName" maxlength="30" />
10
       <el-input v-model="form.nickName" maxlength="30" />
11
     </el-form-item> 
11
     </el-form-item> 
69
           { required: true, message: "紧急联系人不能为空", trigger: "blur" }
69
           { required: true, message: "紧急联系人不能为空", trigger: "blur" }
70
         ],
70
         ],
71
         telephone: [
71
         telephone: [
72
-          { required: true, message: "紧急联系电话不能为空", trigger: "blur" }
72
+          { required: true, message: "紧急联系电话不能为空", trigger: "blur" },
73
+          {
74
+            pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
75
+            message: "请输入正确的手机号码",
76
+            trigger: "blur"
77
+          }
73
         ],
78
         ],
74
         phonenumber: [
79
         phonenumber: [
75
           { required: true, message: "手机号码不能为空", trigger: "blur" },
80
           { required: true, message: "手机号码不能为空", trigger: "blur" },

Загрузка…
Отмена
Сохранить