lamphua 1 kuukausi sitten
vanhempi
commit
992ae8ae47
40 muutettua tiedostoa jossa 789 lisäystä ja 269 poistoa
  1. 6
    8
      oa-back/pom.xml
  2. 47
    0
      oa-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/oa/CmcBudgetController.java
  3. 80
    1
      oa-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/oa/CmcCheckController.java
  4. 19
    0
      oa-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/oa/CmcProjectController.java
  5. 7
    0
      oa-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java
  6. 16
    8
      oa-back/ruoyi-admin/src/main/resources/application.yml
  7. 72
    9
      oa-back/ruoyi-agent/src/main/java/com/ruoyi/agent/service/impl/McpServiceImpl.java
  8. 9
    1
      oa-back/ruoyi-agent/src/main/resources/application.yml
  9. 2
    2
      oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/CmcAgentController.java
  10. 7
    20
      oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/McpController.java
  11. 7
    3
      oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/RagController.java
  12. 7
    3
      oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/SessionController.java
  13. 3
    2
      oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/service/ILangChainMilvusService.java
  14. 18
    9
      oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/service/impl/LangChainMilvusServiceImpl.java
  15. 51
    28
      oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/service/impl/MilvusServiceImpl.java
  16. 9
    5
      oa-back/ruoyi-system/src/main/java/com/ruoyi/llm/service/impl/CmcAgentServiceImpl.java
  17. 23
    0
      oa-back/ruoyi-system/src/main/java/com/ruoyi/oa/domain/CmcCheck.java
  18. 0
    1
      oa-back/ruoyi-system/src/main/resources/mapper/oa/CmcArchiveMapper.xml
  19. 0
    2
      oa-back/ruoyi-system/src/main/resources/mapper/oa/CmcBorrowMapper.xml
  20. 8
    3
      oa-back/ruoyi-system/src/main/resources/mapper/oa/CmcCheckMapper.xml
  21. 0
    1
      oa-back/ruoyi-system/src/main/resources/mapper/oa/CmcPerformanceStaffMapper.xml
  22. 0
    2
      oa-back/ruoyi-system/src/main/resources/mapper/oa/CmcSettleMapper.xml
  23. 1
    0
      oa-ui/package.json
  24. 15
    0
      oa-ui/src/api/oa/budget/check.js
  25. 51
    45
      oa-ui/src/views/flowable/form/budget/addBudget.vue
  26. 2
    2
      oa-ui/src/views/flowable/form/budget/adjust/adjustIndex.vue
  27. 6
    3
      oa-ui/src/views/flowable/form/budget/adjust/budgetAdjust.vue
  28. 2
    2
      oa-ui/src/views/flowable/form/budget/adjust/newBudgetInfo.vue
  29. 3
    3
      oa-ui/src/views/flowable/form/budget/budgetInfo.vue
  30. 49
    18
      oa-ui/src/views/flowable/form/business/subContract.vue
  31. 135
    4
      oa-ui/src/views/llm/chat/index.vue
  32. 1
    0
      oa-ui/src/views/oa/budget/index.vue
  33. 7
    3
      oa-ui/src/views/oa/car/index.vue
  34. 3
    3
      oa-ui/src/views/oa/study/components/studyHead.vue
  35. 4
    4
      oa-ui/src/views/oa/study/record.vue
  36. 4
    4
      oa-ui/src/views/statistics/components/borrowStatistics.vue
  37. 16
    2
      oa-ui/src/views/statistics/components/projectStatistics.vue
  38. 89
    59
      oa-ui/src/views/statistics/components/settleStatistics.vue
  39. 10
    3
      oa-ui/src/views/statistics/index.vue
  40. 0
    6
      package-lock.json

+ 6
- 8
oa-back/pom.xml Näytä tiedosto

@@ -178,19 +178,18 @@
178 178
                 <version>${cmc.version}</version>
179 179
             </dependency>
180 180
 
181
-            <!-- 智能体-->
182
-            <dependency>
183
-                <groupId>com.ruoyi</groupId>
184
-                <artifactId>ruoyi-agent</artifactId>
185
-                <version>${cmc.version}</version>
186
-            </dependency>
187
-
188 181
             <dependency>
189 182
                 <groupId>com.google.protobuf</groupId>
190 183
                 <artifactId>protobuf-java</artifactId>
191 184
                 <version>3.25.5</version>
192 185
             </dependency>
193 186
 
187
+            <dependency>
188
+                <groupId>com.squareup.okhttp3</groupId>
189
+                <artifactId>okhttp</artifactId>
190
+                <version>4.12.0</version>
191
+            </dependency>
192
+
194 193
             <dependency>
195 194
                 <groupId>com.ruoyi</groupId>
196 195
                 <artifactId>ruoyi-flowable</artifactId>
@@ -219,7 +218,6 @@
219 218
     <modules>
220 219
         <module>ruoyi-admin</module>
221 220
         <module>ruoyi-llm</module>
222
-        <module>ruoyi-agent</module>
223 221
         <module>ruoyi-framework</module>
224 222
         <module>ruoyi-system</module>
225 223
         <module>ruoyi-quartz</module>

+ 47
- 0
oa-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/oa/CmcBudgetController.java Näytä tiedosto

@@ -1,10 +1,17 @@
1 1
 package com.ruoyi.web.controller.oa;
2 2
 
3
+import java.math.BigDecimal;
4
+import java.text.ParseException;
5
+import java.text.SimpleDateFormat;
6
+import java.util.Calendar;
3 7
 import java.util.Date;
4 8
 import java.util.List;
5 9
 import javax.servlet.http.HttpServletResponse;
6 10
 
7 11
 
12
+import com.alibaba.fastjson2.JSONArray;
13
+import com.alibaba.fastjson2.JSONObject;
14
+import com.ruoyi.oa.domain.CmcSettle;
8 15
 import org.springframework.beans.factory.annotation.Autowired;
9 16
 import org.springframework.web.bind.annotation.*;
10 17
 import com.ruoyi.common.annotation.Log;
@@ -61,6 +68,46 @@ public class CmcBudgetController extends BaseController
61 68
         return success(cmcBudgetService.selectCmcBudgetByBudgetId(budgetId));
62 69
     }
63 70
 
71
+    /**
72
+     * 获取cmc结算审批详细信息
73
+     */
74
+    @GetMapping("/statistic")
75
+    public AjaxResult getBudgetStatistic(CmcBudget cmcBudget) throws ParseException {
76
+        JSONObject jsonObject = new JSONObject();
77
+        JSONArray yearArray = new JSONArray();
78
+        JSONObject yearObject = new JSONObject();
79
+        JSONArray amountArray = new JSONArray();
80
+        JSONObject amountObject = new JSONObject();
81
+        //每年借款金额
82
+        if (cmcBudget.getZjlTime() == null) {
83
+            for (int i = 2019; i <= Calendar.getInstance().get(Calendar.YEAR); i++) {
84
+                cmcBudget.setZjlTime(new SimpleDateFormat("yyyy").parse(String.valueOf(i)));
85
+                yearObject.put(String.valueOf(i), cmcBudgetService.selectCmcBudgetList(cmcBudget).size());
86
+                BigDecimal amount = new BigDecimal(0);
87
+                for (CmcBudget budget : cmcBudgetService.selectCmcBudgetList(cmcBudget)) {
88
+                    if (budget.getTotalBudget() != null)
89
+                        amount = amount.add(budget.getTotalBudget());
90
+                }
91
+                amountObject.put(String.valueOf(i), amount);
92
+            }
93
+            cmcBudget.setZjlTime(null);
94
+        }
95
+        else {
96
+            yearObject.put(new SimpleDateFormat("yyyy").format(cmcBudget.getZjlTime()), cmcBudgetService.selectCmcBudgetList(cmcBudget).size());
97
+            BigDecimal amount = new BigDecimal(0);
98
+            for (CmcBudget budget : cmcBudgetService.selectCmcBudgetList(cmcBudget)) {
99
+                if (budget.getTotalBudget() != null)
100
+                    amount = amount.add(budget.getTotalBudget());
101
+            }
102
+            amountObject.put(new SimpleDateFormat("yyyy").format(cmcBudget.getZjlTime()), amount);
103
+        }
104
+        yearArray.add(yearObject);
105
+        amountArray.add(amountObject);
106
+        jsonObject.put("year", yearArray);
107
+        jsonObject.put("amount", amountArray);
108
+        return success(jsonObject);
109
+    }
110
+
64 111
     /**
65 112
      * 新增cmc预算管理
66 113
      */

+ 80
- 1
oa-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/oa/CmcCheckController.java Näytä tiedosto

@@ -1,7 +1,18 @@
1 1
 package com.ruoyi.web.controller.oa;
2 2
 
3
+import java.math.BigDecimal;
4
+import java.text.ParseException;
5
+import java.text.SimpleDateFormat;
6
+import java.util.Calendar;
3 7
 import java.util.List;
4 8
 import javax.servlet.http.HttpServletResponse;
9
+
10
+import com.alibaba.fastjson2.JSONArray;
11
+import com.alibaba.fastjson2.JSONObject;
12
+import com.ruoyi.oa.domain.CmcBudget;
13
+import com.ruoyi.oa.domain.CmcCheck;
14
+import com.ruoyi.oa.domain.CmcSettle;
15
+import com.ruoyi.oa.service.ICmcBudgetService;
5 16
 import org.springframework.beans.factory.annotation.Autowired;
6 17
 import org.springframework.web.bind.annotation.GetMapping;
7 18
 import org.springframework.web.bind.annotation.PostMapping;
@@ -15,7 +26,6 @@ import com.ruoyi.common.annotation.Log;
15 26
 import com.ruoyi.common.core.controller.BaseController;
16 27
 import com.ruoyi.common.core.domain.AjaxResult;
17 28
 import com.ruoyi.common.enums.BusinessType;
18
-import com.ruoyi.oa.domain.CmcCheck;
19 29
 import com.ruoyi.oa.service.ICmcCheckService;
20 30
 import com.ruoyi.common.utils.poi.ExcelUtil;
21 31
 import com.ruoyi.common.core.page.TableDataInfo;
@@ -33,6 +43,9 @@ public class CmcCheckController extends BaseController
33 43
     @Autowired
34 44
     private ICmcCheckService cmcCheckService;
35 45
 
46
+    @Autowired
47
+    private ICmcBudgetService cmcBudgetService;
48
+
36 49
     /**
37 50
      * 查询cmc项目核算列表
38 51
      */
@@ -65,6 +78,55 @@ public class CmcCheckController extends BaseController
65 78
         return success(cmcCheckService.selectCmcCheckByCheckId(checkId));
66 79
     }
67 80
 
81
+    /**
82
+     * 获取cmc核算审批详细信息
83
+     */
84
+    @GetMapping("/statistic")
85
+    public AjaxResult getCheckStatistic(CmcCheck cmcCheck) throws ParseException {
86
+        JSONObject jsonObject = new JSONObject();
87
+        JSONArray yearArray = new JSONArray();
88
+        JSONObject yearObject = new JSONObject();
89
+        JSONArray amountArray = new JSONArray();
90
+        JSONObject amountObject = new JSONObject();
91
+        JSONArray yearProjectCountArray = new JSONArray();
92
+        JSONObject yearProjectCountObject = new JSONObject();
93
+        JSONArray yearProjectAmountArray = new JSONArray();
94
+        JSONObject yearProjectAmountObject = new JSONObject();
95
+        //每年核算金额
96
+        if (cmcCheck.getCheckTime() == null) {
97
+            for (int i = 2019; i <= Calendar.getInstance().get(Calendar.YEAR); i++) {
98
+                cmcCheck.setCheckTime(new SimpleDateFormat("yyyy").parse(String.valueOf(i)));
99
+                yearObject.put(String.valueOf(i), cmcCheckService.selectCmcCheckList(cmcCheck).size());
100
+                BigDecimal amount = new BigDecimal(0);
101
+                for (CmcCheck check : cmcCheckService.selectCmcCheckList(cmcCheck)) {
102
+                    if (check.getTotalAdjust() != null)
103
+                        amount = amount.add(check.getTotalAdjust());
104
+                }
105
+                amountObject.put(String.valueOf(i), amount);
106
+            }
107
+            cmcCheck.setCheckTime(null);
108
+        }
109
+        else {
110
+            yearObject.put(new SimpleDateFormat("yyyy").format(cmcCheck.getCheckTime()), cmcCheckService.selectCmcCheckList(cmcCheck).size());
111
+            BigDecimal amount = new BigDecimal(0);
112
+            for (CmcCheck check : cmcCheckService.selectCmcCheckList(cmcCheck)) {
113
+                if (check.getTotalAdjust() != null)
114
+                    amount = amount.add(check.getTotalAdjust());
115
+            }
116
+            amountObject.put(new SimpleDateFormat("yyyy").format(cmcCheck.getCheckTime()), amount);
117
+        }
118
+        getCheckProjectStatistic(cmcCheck, yearProjectCountObject, yearProjectAmountObject);
119
+        yearArray.add(yearObject);
120
+        amountArray.add(amountObject);
121
+        yearProjectCountArray.add(yearProjectCountObject);
122
+        yearProjectAmountArray.add(yearProjectAmountObject);
123
+        jsonObject.put("year", yearArray);
124
+        jsonObject.put("amount", amountArray);
125
+        jsonObject.put("yearProjectCount", yearProjectCountArray);
126
+        jsonObject.put("yearProjectAmount", yearProjectAmountArray);
127
+        return success(jsonObject);
128
+    }
129
+
68 130
     /**
69 131
      * 新增cmc项目核算
70 132
      */
@@ -94,4 +156,21 @@ public class CmcCheckController extends BaseController
94 156
     {
95 157
         return success(cmcCheckService.deleteCmcCheckByCheckIds(checkIds));
96 158
     }
159
+
160
+    public void getCheckProjectStatistic(CmcCheck cmcCheck, JSONObject yearProjectCountObject, JSONObject yearProjectAmountObject) throws ParseException {
161
+        if (cmcCheck.getCheckTime() == null)
162
+            cmcCheck.setCheckTime(new SimpleDateFormat("yyyy").parse("2000-01-01"));
163
+        for (int i = 2019; i <= Calendar.getInstance().get(Calendar.YEAR); i++) {
164
+            BigDecimal pyAmount = new BigDecimal(0);
165
+            int count = 0;
166
+            for (CmcCheck check : cmcCheckService.selectCmcCheckList(cmcCheck)) {
167
+                if (check.getTotalAdjust() != null && check.getProjectNumber() != null && check.getProjectNumber().contains(String.valueOf(i))) {
168
+                    pyAmount = pyAmount.add(check.getTotalAdjust());
169
+                    count ++;
170
+                }
171
+            }
172
+            yearProjectCountObject.put(i + "项目", count);
173
+            yearProjectAmountObject.put(i + "项目", pyAmount);
174
+        }
175
+    }
97 176
 }

+ 19
- 0
oa-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/oa/CmcProjectController.java Näytä tiedosto

@@ -67,6 +67,9 @@ public class CmcProjectController extends BaseController
67 67
     @Autowired
68 68
     private ICmcSettleService cmcSettleService;
69 69
 
70
+    @Autowired
71
+    private ICmcCheckService cmcCheckService;
72
+
70 73
     @Autowired
71 74
     private IFilesAchievementService filesAchievementService;
72 75
 
@@ -212,6 +215,8 @@ public class CmcProjectController extends BaseController
212 215
         JSONObject archiveObject = new JSONObject();
213 216
         JSONArray settleArray = new JSONArray();
214 217
         JSONObject settleObject = new JSONObject();
218
+        JSONArray checkArray = new JSONArray();
219
+        JSONObject checkObject = new JSONObject();
215 220
         //整体
216 221
         if (cmcProject.getProjectNumber() == null) {
217 222
             //每年项目数量
@@ -247,6 +252,12 @@ public class CmcProjectController extends BaseController
247 252
                 cmcSettle.setGmTime(new SimpleDateFormat("yyyy").parse("2000-01-01"));
248 253
                 List<CmcSettle> settleList = cmcSettleService.selectCmcSettleList(cmcSettle);
249 254
                 settleObject.put(String.valueOf(i), settleList.size());
255
+                //已核算
256
+                CmcCheck cmcCheck = new CmcCheck();
257
+                cmcCheck.setProjectNumber(String.valueOf(i));
258
+                cmcCheck.setCheckTime(new SimpleDateFormat("yyyy").parse("2000-01-01"));
259
+                List<CmcCheck> checkList = cmcCheckService.selectCmcCheckList(cmcCheck);
260
+                checkObject.put(String.valueOf(i), checkList.size());
250 261
             }
251 262
             cmcProject.setProjectNumber(null);
252 263
 
@@ -284,6 +295,12 @@ public class CmcProjectController extends BaseController
284 295
             cmcSettle.setGmTime(new SimpleDateFormat("yyyy").parse("2000-01-01"));
285 296
             List<CmcSettle> settleList = cmcSettleService.selectCmcSettleList(cmcSettle);
286 297
             settleObject.put(cmcProject.getProjectNumber(), settleList.size());
298
+            //已核算
299
+            CmcCheck cmcCheck = new CmcCheck();
300
+            cmcCheck.setProjectNumber(cmcProject.getProjectNumber());
301
+            cmcCheck.setCheckTime(new SimpleDateFormat("yyyy").parse("2000-01-01"));
302
+            List<CmcCheck> checkList = cmcCheckService.selectCmcCheckList(cmcCheck);
303
+            checkObject.put(cmcProject.getProjectNumber(), checkList.size());
287 304
 
288 305
         }
289 306
         getProjectSourceStatistic(cmcProject, sourceObject);
@@ -296,6 +313,7 @@ public class CmcProjectController extends BaseController
296 313
         completeArray.add(completeObject);
297 314
         archiveArray.add(archiveObject);
298 315
         settleArray.add(settleObject);
316
+        checkArray.add(checkObject);
299 317
         jsonObject.put("year", yearArray);
300 318
         jsonObject.put("source", sourceArray);
301 319
         jsonObject.put("dept", deptArray);
@@ -303,6 +321,7 @@ public class CmcProjectController extends BaseController
303 321
         jsonObject.put("complete", completeArray);
304 322
         jsonObject.put("archive", archiveArray);
305 323
         jsonObject.put("settle", settleArray);
324
+        jsonObject.put("check", checkArray);
306 325
         return success(jsonObject);
307 326
     }
308 327
 

+ 7
- 0
oa-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java Näytä tiedosto

@@ -7,6 +7,7 @@ import javax.servlet.http.HttpServletResponse;
7 7
 import com.ruoyi.common.core.domain.entity.SysUser;
8 8
 import com.ruoyi.oa.domain.CmcManageDept;
9 9
 import com.ruoyi.oa.service.ICmcManageDeptService;
10
+import com.ruoyi.system.service.ISysDeptService;
10 11
 import com.ruoyi.system.service.ISysUserService;
11 12
 import org.springframework.beans.factory.annotation.Autowired;
12 13
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -43,6 +44,9 @@ public class SysPostController extends BaseController
43 44
     @Autowired
44 45
     private  ISysUserService userService;
45 46
 
47
+    @Autowired
48
+    private ISysDeptService deptService;
49
+
46 50
     @Autowired
47 51
     private  ICmcManageDeptService cmcManageDeptService;
48 52
 
@@ -275,6 +279,9 @@ public class SysPostController extends BaseController
275 279
 
276 280
     public  List<SysUser> getManageId(String deptId, String partyAId) {
277 281
         List<SysUser> userList = new ArrayList<>();
282
+        Long parentId = deptService.selectDeptById(Long.valueOf(deptId)).getParentId();
283
+        if (parentId > 100)
284
+            deptId = parentId.toString();
278 285
         CmcManageDept cmcManageDept = new CmcManageDept();
279 286
         cmcManageDept.setDeptId(deptId);
280 287
         cmcManageDept.setPartyAId(partyAId);

+ 16
- 8
oa-back/ruoyi-admin/src/main/resources/application.yml Näytä tiedosto

@@ -7,8 +7,9 @@ cmc:
7 7
   # 版权年份
8 8
   copyrightYear: 2023
9 9
   # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
10
-#  profile: /home/cmc/Java-server/综合办公/
11
-  profile: D:/ruoyi/uploadPath
10
+#  profile: /home/cmc/Java-server/综合办公
11
+  profile: /home/cmc/projects/cmc-oa
12
+#  profile: D:/ruoyi/uploadPath
12 13
   # 获取ip地址开关
13 14
   addressEnabled: false
14 15
   # 验证码类型 math 数字计算 char 字符验证
@@ -17,8 +18,8 @@ cmc:
17 18
 # 开发环境配置
18 19
 server:
19 20
   # 服务器的HTTP端口,默认为8080
20
-  port: 8080
21
-#  port: 8086
21
+#  port: 8087
22
+  port: 8086
22 23
   servlet:
23 24
     # 应用的访问路径
24 25
     context-path: /
@@ -61,7 +62,7 @@ spring:
61 62
       # 单个文件大小
62 63
       max-file-size: 1024MB
63 64
       # 设置总上传的文件大小
64
-      max-request-size: 10240MB
65
+      max-request-size: 1024MB
65 66
   # 服务模块
66 67
   devtools:
67 68
     restart:
@@ -71,6 +72,7 @@ spring:
71 72
   redis:
72 73
     # 地址
73 74
     host: localhost
75
+#    host: cmc-redis
74 76
     # 端口,默认为6379
75 77
     port: 6379
76 78
     # 数据库索引
@@ -97,7 +99,7 @@ token:
97 99
   # 令牌密钥
98 100
   secret: abcdefghijklmnopqrstuvwxyz
99 101
   # 令牌有效期(默认30分钟)
100
-  expireTime: 240
102
+  expireTime: 480
101 103
 
102 104
 # MyBatis配置
103 105
 mybatis:
@@ -133,6 +135,12 @@ xss:
133 135
 # flowable相关表
134 136
 flowable:
135 137
   # true 会对数据库中所有表进行更新操作。如果表不存在,则自动创建(建议开发时使用)
136
-  database-schema-update: false
138
+#  database-schema-update: false
139
+  database-schema-update: true
137 140
   # 关闭定时任务JOB
138
-  async-executor-activate: false
141
+  async-executor-activate: false
142
+
143
+llmService:
144
+  url: http://192.168.28.196:8000/v1/chat/completions
145
+milvusService:
146
+  url: http://192.168.28.196:19530

+ 72
- 9
oa-back/ruoyi-agent/src/main/java/com/ruoyi/agent/service/impl/McpServiceImpl.java Näytä tiedosto

@@ -37,9 +37,11 @@ import org.noear.solon.ai.chat.session.InMemoryChatSession;
37 37
 import org.noear.solon.ai.mcp.McpChannel;
38 38
 import org.noear.solon.ai.mcp.server.annotation.McpServerEndpoint;
39 39
 import org.noear.solon.annotation.Param;
40
+import org.springframework.beans.factory.annotation.Value;
40 41
 import org.springframework.stereotype.Service;
41 42
 
42 43
 import java.io.*;
44
+import java.sql.*;
43 45
 import java.util.*;
44 46
 
45 47
 @Service
@@ -48,12 +50,23 @@ public class McpServiceImpl implements IMcpService {
48 50
 
49 51
     private static final EmbeddingModel embeddingModel = new BgeSmallZhV15EmbeddingModel();
50 52
 
51
-    private static final String llmServiceUrl = "http://192.168.28.196:8000/v1/chat/completions";
53
+    @Value("${llmService.url}")
54
+    private static String llmServiceUrl;
52 55
 
53
-    private static final MilvusClientV2 milvusClient = new MilvusClientV2(
54
-            ConnectConfig.builder()
55
-                    .uri("http://192.168.28.196:19530")
56
-                    .build());
56
+    @Value("${lmilvusService.url}")
57
+    private static String milvusServiceUrl;
58
+
59
+    @Value("${mysqlService.jdbcUrl}")
60
+    private static String url ;
61
+
62
+    @Value("${mysqlService.username}")
63
+    private static String user;
64
+
65
+    @Value("${mysqlService.password}")
66
+    private static String password;
67
+
68
+    @Value("${cmc.profile}")
69
+    private static String profile;
57 70
 
58 71
     /**
59 72
      * 调用LLM+RAG(外部文件+知识库)生成回答
@@ -66,8 +79,8 @@ public class McpServiceImpl implements IMcpService {
66 79
     {
67 80
             try {
68 81
                 if (templatePath.startsWith("/dev-api"))
69
-                    templatePath = templatePath.replace("/dev-api/profile", Solon.cfg().getProperty("cmc.profile"));
70
-                templatePath = Solon.cfg().getProperty("cmc.profile") + templatePath;
82
+                    templatePath = templatePath.replace("/dev-api/profile", profile);
83
+                templatePath =profile + templatePath;
71 84
                 List<String> subTitles = extractSubTitles(templatePath, title);
72 85
                 List<JSONObject> contexts = retrieveFromMilvus(collectionName, title, 10);
73 86
                 return generateAnswerWithDocumentAndCollection(agentName, templatePath, subTitles, contexts);
@@ -76,6 +89,52 @@ public class McpServiceImpl implements IMcpService {
76 89
             }
77 90
     }
78 91
 
92
+    /**
93
+     * 查询OA数据生成回答
94
+     */
95
+    @ToolMapping(description = "数据查询")
96
+    public AssistantMessage SQLQuery(@Param(description = "sql语句") String sqlString) throws Exception
97
+    {
98
+            try {
99
+                List<Map<String, Object>> data = executeQuery(sqlString, 100.0);
100
+                return ChatMessage.ofAssistant(generateAnswer(sqlString));
101
+            } catch (IOException e) {
102
+                throw new RuntimeException(e);
103
+            }
104
+    }
105
+
106
+    public static List<Map<String, Object>> executeQuery(String sql, Object... params) {
107
+        List<Map<String, Object>> result = new ArrayList<>();
108
+
109
+        try (Connection conn = DriverManager.getConnection(url, user, password);
110
+             PreparedStatement stmt = conn.prepareStatement(sql)) {
111
+
112
+            // 设置参数
113
+            for (int i = 0; i < params.length; i++) {
114
+                stmt.setObject(i + 1, params[i]);
115
+            }
116
+
117
+            ResultSet rs = stmt.executeQuery();
118
+            ResultSetMetaData metaData = rs.getMetaData();
119
+            int columnCount = metaData.getColumnCount();
120
+
121
+            while (rs.next()) {
122
+                Map<String, Object> row = new HashMap<>();
123
+                for (int i = 1; i <= columnCount; i++) {
124
+                    String columnName = metaData.getColumnLabel(i);
125
+                    Object value = rs.getObject(i);
126
+                    row.put(columnName, value);
127
+                }
128
+                result.add(row);
129
+            }
130
+
131
+        } catch (SQLException e) {
132
+            throw new RuntimeException("执行 SQL 失败: " + sql, e);
133
+        }
134
+
135
+        return result;
136
+    }
137
+
79 138
     /**
80 139
      * 从Milvus检索相关文档
81 140
      * @return
@@ -124,7 +183,7 @@ public class McpServiceImpl implements IMcpService {
124 183
             sb.append("针对本项目招标文件内容,补全以下章节部分:\n\n").append(title);
125 184
             content.append(generateAnswer(sb.toString()));
126 185
         }
127
-        String absolutePath = templatePath.replace("/dev-api/profile", Solon.cfg().getProperty("cmc.profile"));
186
+        String absolutePath = templatePath.replace("/dev-api/profile", profile);
128 187
         writeContent(content.toString(), titles, absolutePath);
129 188
         for (JSONObject context : contexts) {
130 189
             sb.append("文件").append(": ")
@@ -133,7 +192,7 @@ public class McpServiceImpl implements IMcpService {
133 192
                     .append(context.getString("content")).append("\n\n");
134 193
         }
135 194
         content.append( "招标文件分析完成,章节内容已写入【<a href='")
136
-                .append(templatePath.replace(Solon.cfg().getProperty("cmc.profile"), "/dev-api/profile"))
195
+                .append(templatePath.replace(profile, "/dev-api/profile"))
137 196
                 .append("'> 技术文件" + "</a>】,请查阅\n\n")
138 197
                 .append("如需修改,请输入技术文件已有内容的章节标题\n\n")
139 198
                 .append(extractTitles(templatePath));
@@ -303,6 +362,10 @@ public class McpServiceImpl implements IMcpService {
303 362
      * @return
304 363
      */
305 364
     private List<List<SearchResp.SearchResult>> retrieve(String collectionName, String query, int topK) {
365
+        MilvusClientV2 milvusClient = new MilvusClientV2(
366
+                ConnectConfig.builder()
367
+                        .uri(milvusServiceUrl)
368
+                        .build());
306 369
         List<BaseVector> queryVector = Collections.singletonList(new FloatVec(embeddingModel.embed(query).content().vector()));
307 370
 
308 371
         //  加载集合

+ 9
- 1
oa-back/ruoyi-agent/src/main/resources/application.yml Näytä tiedosto

@@ -2,4 +2,12 @@
2 2
 cmc:
3 3
   # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
4 4
   #  profile: /home/cmc/projects/cmc-llm
5
-  profile: E:/home/cmc/projects/cmc-llm
5
+  profile: /home/cmc/projects/cmc-oa
6
+  llmService:
7
+    url: http://192.168.28.196:8000/v1/chat/completions
8
+  milvusService:
9
+    url: http://192.168.28.196:19530
10
+  mysqlService:
11
+    jdbcUrl: jdbc:mysql://localhost:3306/cmc_oa?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
12
+    username: root
13
+    password: cmcroot

+ 2
- 2
oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/CmcAgentController.java Näytä tiedosto

@@ -5,7 +5,7 @@ import java.util.Date;
5 5
 import java.util.List;
6 6
 import javax.servlet.http.HttpServletResponse;
7 7
 
8
-import org.noear.solon.ai.chat.message.AssistantMessage;
8
+import org.noear.solon.ai.chat.message.ChatMessage;
9 9
 import org.springframework.beans.factory.annotation.Autowired;
10 10
 import org.springframework.web.bind.annotation.GetMapping;
11 11
 import org.springframework.web.bind.annotation.PostMapping;
@@ -76,7 +76,7 @@ public class CmcAgentController extends BaseController
76 76
      */
77 77
     @GetMapping("/opening")
78 78
     public AjaxResult opening(String agentName) {
79
-        return success(new AssistantMessage(cmcAgentService.getOpening(agentName)));
79
+        return success(ChatMessage.ofAssistant(cmcAgentService.getOpening(agentName)));
80 80
     }
81 81
 
82 82
     /**

+ 7
- 20
oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/McpController.java Näytä tiedosto

@@ -1,18 +1,11 @@
1 1
 package com.ruoyi.web.llm.controller;
2 2
 
3 3
 import com.alibaba.fastjson2.JSONObject;
4
-import com.ruoyi.common.config.RuoYiConfig;
5 4
 import com.ruoyi.common.core.controller.BaseController;
6 5
 import com.ruoyi.llm.domain.CmcChat;
7 6
 import com.ruoyi.llm.service.ICmcAgentService;
8 7
 import com.ruoyi.llm.service.ICmcChatService;
9 8
 import com.ruoyi.llm.service.ICmcTopicService;
10
-import com.ruoyi.web.llm.service.ILangChainMilvusService;
11
-import dev.langchain4j.model.embedding.EmbeddingModel;
12
-import dev.langchain4j.model.embedding.onnx.bgesmallzhv15.BgeSmallZhV15EmbeddingModel;
13
-import io.milvus.client.MilvusServiceClient;
14
-import io.milvus.param.ConnectParam;
15
-import org.noear.solon.Solon;
16 9
 import org.noear.solon.ai.chat.ChatModel;
17 10
 import org.noear.solon.ai.chat.ChatResponse;
18 11
 import org.noear.solon.ai.chat.ChatSession;
@@ -22,6 +15,7 @@ import org.noear.solon.ai.chat.session.InMemoryChatSession;
22 15
 import org.noear.solon.ai.mcp.McpChannel;
23 16
 import org.noear.solon.ai.mcp.client.McpClientProvider;
24 17
 import org.springframework.beans.factory.annotation.Autowired;
18
+import org.springframework.beans.factory.annotation.Value;
25 19
 import org.springframework.web.bind.annotation.GetMapping;
26 20
 import org.springframework.web.bind.annotation.RequestMapping;
27 21
 import org.springframework.web.bind.annotation.RestController;
@@ -40,9 +34,6 @@ import java.util.*;
40 34
 @RequestMapping("/llm/mcp")
41 35
 public class McpController extends BaseController
42 36
 {
43
-    @Autowired
44
-    private ILangChainMilvusService langChainMilvusService;
45
-
46 37
     @Autowired
47 38
     private ICmcChatService cmcChatService;
48 39
 
@@ -52,15 +43,11 @@ public class McpController extends BaseController
52 43
     @Autowired
53 44
     private ICmcAgentService cmcAgentService;
54 45
 
55
-    private static final EmbeddingModel embeddingModel = new BgeSmallZhV15EmbeddingModel();
56
-
57
-    private static final String llmServiceUrl = "http://192.168.28.196:8000/v1/chat/completions";
46
+    @Value("${llmService.url}")
47
+    private String llmServiceUrl;
58 48
 
59
-    private static final MilvusServiceClient milvusClient = new MilvusServiceClient(
60
-            ConnectParam.newBuilder()
61
-                    .withHost("192.168.28.196")
62
-                    .withPort(19530)
63
-                    .build());
49
+    @Value("${milvusService.url}")
50
+    private String milvusServiceUrl;
64 51
 
65 52
     /**
66 53
      * 自动调用mcp工具问答
@@ -70,7 +57,7 @@ public class McpController extends BaseController
70 57
     public AssistantMessage answer(String topicId, String question) throws IOException {
71 58
         McpClientProvider clientProvider = McpClientProvider.builder()
72 59
                 .channel(McpChannel.SSE)
73
-                .apiUrl("http://localhost:8080/mcp/sse")
60
+                .url("http://localhost:8080/mcp/sse")
74 61
                 .build();
75 62
         ChatModel chatModel = ChatModel.of(llmServiceUrl)
76 63
                 .model("Qwen2.5-7B-Instruct")
@@ -102,7 +89,7 @@ public class McpController extends BaseController
102 89
                 arguments.put("title", question);
103 90
             }
104 91
             resultContent = clientProvider.callToolAsText(name, arguments).getContent();
105
-            assistantMessage = new AssistantMessage(resultContent);
92
+            assistantMessage = ChatMessage.ofAssistant(resultContent);
106 93
         }
107 94
         else
108 95
             throw new IOException("模型上下文工具未准备就绪,请重试");

+ 7
- 3
oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/RagController.java Näytä tiedosto

@@ -5,11 +5,13 @@ import com.ruoyi.web.llm.service.ILangChainMilvusService;
5 5
 import com.ruoyi.common.core.controller.BaseController;
6 6
 import org.noear.solon.ai.chat.message.AssistantMessage;
7 7
 import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.beans.factory.annotation.Value;
8 9
 import org.springframework.web.bind.annotation.GetMapping;
9 10
 import org.springframework.web.bind.annotation.RequestMapping;
10 11
 import org.springframework.web.bind.annotation.RestController;
11 12
 import reactor.core.publisher.Flux;
12 13
 
14
+import java.io.IOException;
13 15
 import java.util.List;
14 16
 
15 17
 /**
@@ -25,14 +27,16 @@ public class RagController extends BaseController
25 27
     @Autowired
26 28
     private ILangChainMilvusService langChainMilvusService;
27 29
 
30
+    @Value("${llmService.url}")
31
+    private String llmServiceUrl;
32
+
28 33
     /**
29 34
      * 调用LLM+RAG(知识库)生成回答
30 35
      */
31 36
     @GetMapping("/answer")
32
-    public Flux<AssistantMessage> answerWithCollection(String collectionName, String topicId, String question)
33
-    {
37
+    public Flux<AssistantMessage> answerWithCollection(String collectionName, String topicId, String question) throws IOException {
34 38
         List<JSONObject> contexts = langChainMilvusService.retrieveFromMilvus(collectionName, question, 10);
35
-        return langChainMilvusService.generateAnswerWithCollection(topicId, question, contexts, "http://192.168.28.196:8000/v1/chat/completions");
39
+        return langChainMilvusService.generateAnswerWithCollection(topicId, question, contexts, llmServiceUrl);
36 40
     }
37 41
 
38 42
     /**

+ 7
- 3
oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/SessionController.java Näytä tiedosto

@@ -4,6 +4,7 @@ import com.ruoyi.common.core.controller.BaseController;
4 4
 import com.ruoyi.web.llm.service.ILangChainMilvusService;
5 5
 import org.noear.solon.ai.chat.message.AssistantMessage;
6 6
 import org.springframework.beans.factory.annotation.Autowired;
7
+import org.springframework.beans.factory.annotation.Value;
7 8
 import org.springframework.web.bind.annotation.GetMapping;
8 9
 import org.springframework.web.bind.annotation.RequestMapping;
9 10
 import org.springframework.web.bind.annotation.RestController;
@@ -24,21 +25,24 @@ public class SessionController extends BaseController
24 25
     @Autowired
25 26
     private ILangChainMilvusService langChainMilvusService;
26 27
 
28
+    @Value("${llmService.url}")
29
+    private String llmServiceUrl;
30
+
27 31
     /**
28 32
      * 生成回答
29 33
      */
30 34
     @GetMapping("/answer")
31 35
     public Flux<AssistantMessage> answer(String topicId, String question) {
32
-        return langChainMilvusService.generateAnswer(topicId, question, "http://192.168.28.196:8000/v1/chat/completions");
36
+        return langChainMilvusService.generateAnswer(topicId, question, llmServiceUrl);
33 37
     }
34 38
 
35 39
     /**
36 40
      * 调用LLM+RAG(外部文件)生成回答
37 41
      */
38 42
     @GetMapping("/answerWithDocument")
39
-    public Flux<AssistantMessage> answerWithDocument(String topicId, String chatId, String question) throws IOException
43
+    public Flux<AssistantMessage> answerWithDocument(String topicId, String chatId, String question) throws Exception
40 44
     {
41
-        return langChainMilvusService.generateAnswerWithDocument(topicId, chatId, question, "http://192.168.28.196:8000/v1/chat/completions");
45
+        return langChainMilvusService.generateAnswerWithDocument(topicId, chatId, question, llmServiceUrl);
42 46
     }
43 47
 
44 48
 }

+ 3
- 2
oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/service/ILangChainMilvusService.java Näytä tiedosto

@@ -5,6 +5,7 @@ import org.noear.solon.ai.chat.message.AssistantMessage;
5 5
 import org.springframework.web.multipart.MultipartFile;
6 6
 import reactor.core.publisher.Flux;
7 7
 
8
+import java.io.FileNotFoundException;
8 9
 import java.io.IOException;
9 10
 import java.util.List;
10 11
 
@@ -51,13 +52,13 @@ public interface ILangChainMilvusService {
51 52
      * 调用LLM+RAG(外部文件)生成回答
52 53
      * @return
53 54
      */
54
-    public Flux<AssistantMessage> generateAnswerWithDocument(String topicId, String chatId, String question, String llmServiceUrl) throws IOException;
55
+    public Flux<AssistantMessage> generateAnswerWithDocument(String topicId, String chatId, String question, String llmServiceUrl) throws Exception;
55 56
 
56 57
     /**
57 58
      * 调用LLM+RAG(外部文件+知识库)生成回答
58 59
      * @return
59 60
      */
60
-    public Flux<AssistantMessage> generateAnswerWithDocumentAndCollection(String topicId, String question,  List<JSONObject> requests, String llmServiceUrl) throws IOException;
61
+    public Flux<AssistantMessage> generateAnswerWithDocumentAndCollection(String topicId, String question,  List<JSONObject> requests, String llmServiceUrl) throws Exception;
61 62
 
62 63
     /**
63 64
      * 获取二级标题下三级标题列表

+ 18
- 9
oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/service/impl/LangChainMilvusServiceImpl.java Näytä tiedosto

@@ -42,9 +42,9 @@ import org.noear.solon.ai.chat.ChatSession;
42 42
 import org.noear.solon.ai.chat.message.AssistantMessage;
43 43
 import org.noear.solon.ai.chat.message.ChatMessage;
44 44
 import org.noear.solon.ai.chat.session.InMemoryChatSession;
45
-import org.noear.solon.web.sse.SseEvent;
46 45
 import org.reactivestreams.Publisher;
47 46
 import org.springframework.beans.factory.annotation.Autowired;
47
+import org.springframework.beans.factory.annotation.Value;
48 48
 import org.springframework.stereotype.Service;
49 49
 import org.springframework.web.multipart.MultipartFile;
50 50
 import reactor.core.publisher.Flux;
@@ -61,15 +61,16 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
61 61
     @Autowired
62 62
     private ICmcDocumentService cmcDocumentService;
63 63
 
64
-    private static final MilvusClientV2 milvusClient = new MilvusClientV2(
65
-            ConnectConfig.builder()
66
-                    .uri("http://192.168.28.196:19530")
67
-                    .build());
68
-
69 64
     private static final EmbeddingModel embeddingModel = new BgeSmallZhV15EmbeddingModel();
70 65
 
71 66
     private String processValue = "";
72 67
 
68
+    @Value("${llmService.url}")
69
+    private String llmServiceUrl;
70
+
71
+    @Value("${milvusService.url}")
72
+    private String milvusServiceUrl;
73
+
73 74
     /**
74 75
      * 上传多文件
75 76
      *
@@ -86,6 +87,10 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
86 87
     @Override
87 88
     public int insertLangchainEmbeddingDocument(MultipartFile[] fileList, String collectionName)
88 89
     {
90
+        MilvusClientV2 milvusClient = new MilvusClientV2(
91
+                ConnectConfig.builder()
92
+                        .uri(llmServiceUrl)
93
+                        .build());
89 94
         processValue = "";
90 95
         int successfullyInsertedFiles = 0;
91 96
 
@@ -253,7 +258,7 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
253 258
      * 调用LLM生成回答
254 259
      */
255 260
     @Override
256
-    public Flux<AssistantMessage> generateAnswerWithDocument(String topicId, String chatId, String question, String llmServiceUrl) throws IOException {
261
+    public Flux<AssistantMessage> generateAnswerWithDocument(String topicId, String chatId, String question, String llmServiceUrl) throws Exception {
257 262
         CmcDocument cmcDocument = new CmcDocument();
258 263
         cmcDocument.setChatId(chatId);
259 264
         List<CmcDocument> documentList = cmcDocumentService.selectCmcDocumentList(cmcDocument);
@@ -286,7 +291,7 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
286 291
      * 调用LLM生成回答
287 292
      */
288 293
     @Override
289
-    public Flux<AssistantMessage> generateAnswerWithDocumentAndCollection(String topicId, String question, List<JSONObject> contexts, String llmServiceUrl) throws IOException {
294
+    public Flux<AssistantMessage> generateAnswerWithDocumentAndCollection(String topicId, String question, List<JSONObject> contexts, String llmServiceUrl) throws Exception {
290 295
         StringBuilder sb = new StringBuilder("招标文件内容:\n\n");
291 296
         CmcChat cmcChat = new CmcChat();
292 297
         cmcChat.setTopicId(topicId);
@@ -369,6 +374,10 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
369 374
      * @return
370 375
      */
371 376
     private List<List<SearchResp.SearchResult>> retrieve(String collectionName, String query, int topK) {
377
+        MilvusClientV2 milvusClient = new MilvusClientV2(
378
+                ConnectConfig.builder()
379
+                        .uri(milvusServiceUrl)
380
+                        .build());
372 381
         List<BaseVector> queryVector = Collections.singletonList(new FloatVec(embeddingModel.embed(query).content().vector()));
373 382
 
374 383
         //  加载集合
@@ -405,7 +414,7 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
405 414
     /**
406 415
      * 检索知识库
407 416
      */
408
-    private List<TextSegment> splitDocument(File transferFile) throws FileNotFoundException {
417
+    private List<TextSegment> splitDocument(File transferFile) throws Exception {
409 418
         // 加载文档
410 419
         Document document;
411 420
         InputStream fileInputStream = new FileInputStream(transferFile);

+ 51
- 28
oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/service/impl/MilvusServiceImpl.java Näytä tiedosto

@@ -13,6 +13,7 @@ import io.milvus.v2.service.collection.response.ListCollectionsResp;
13 13
 import io.milvus.v2.service.vector.request.DeleteReq;
14 14
 import io.milvus.v2.service.vector.request.QueryReq;
15 15
 import io.milvus.v2.service.vector.response.QueryResp;
16
+import org.springframework.beans.factory.annotation.Value;
16 17
 import org.springframework.stereotype.Service;
17 18
 
18 19
 import java.text.SimpleDateFormat;
@@ -22,16 +23,18 @@ import java.util.stream.Collectors;
22 23
 @Service
23 24
 public class MilvusServiceImpl implements IMilvusService {
24 25
 
25
-    private static final MilvusClientV2 milvusClient = new MilvusClientV2(
26
-            ConnectConfig.builder()
27
-                    .uri("http://192.168.28.196:19530")
28
-                    .build());
26
+    @Value("${milvusService.url}")
27
+    private String milvusServiceUrl;
29 28
 
30 29
     /**
31 30
      * 新建知识库Collection(含Schema、Field、Index)
32 31
      */
33 32
     @Override
34 33
     public void createCollection(String collectionName, String description, int dimension) {
34
+        MilvusClientV2 milvusClient = new MilvusClientV2(
35
+                ConnectConfig.builder()
36
+                        .uri(milvusServiceUrl)
37
+                        .build());
35 38
         CreateCollectionReq.CollectionSchema schema = MilvusClientV2.CreateSchema();
36 39
 
37 40
         schema.addField(AddFieldReq.builder()
@@ -90,6 +93,10 @@ public class MilvusServiceImpl implements IMilvusService {
90 93
      */
91 94
     @Override
92 95
     public JSONArray getCollectionNames() {
96
+        MilvusClientV2 milvusClient = new MilvusClientV2(
97
+                ConnectConfig.builder()
98
+                        .uri(milvusServiceUrl)
99
+                        .build());
93 100
         JSONArray jsonArray = new JSONArray();
94 101
         ListCollectionsResp listResponse = milvusClient.listCollections();
95 102
         if (listResponse != null) {
@@ -119,9 +126,13 @@ public class MilvusServiceImpl implements IMilvusService {
119 126
      */
120 127
     @Override
121 128
     public JSONArray listKnowLedgeByCollectionName(String collectionName) {
129
+        MilvusClientV2 milvusClient = new MilvusClientV2(
130
+                ConnectConfig.builder()
131
+                        .uri(milvusServiceUrl)
132
+                        .build());
122 133
         JSONArray jsonArray = new JSONArray();
123 134
         ListCollectionsResp listResponse = milvusClient.listCollections();
124
-        
135
+
125 136
         if (listResponse != null) {
126 137
             List<String> allCollectionNames = listResponse.getCollectionNames();
127 138
             for (String name : allCollectionNames) {
@@ -150,6 +161,10 @@ public class MilvusServiceImpl implements IMilvusService {
150 161
      */
151 162
     @Override
152 163
     public void collectionRename(String collectionName, String newCollectionName) {
164
+        MilvusClientV2 milvusClient = new MilvusClientV2(
165
+                ConnectConfig.builder()
166
+                        .uri(milvusServiceUrl)
167
+                        .build());
153 168
         RenameCollectionReq renameCollectionReq = RenameCollectionReq.builder()
154 169
                 .collectionName(collectionName)
155 170
                 .newCollectionName(newCollectionName)
@@ -163,6 +178,10 @@ public class MilvusServiceImpl implements IMilvusService {
163 178
      */
164 179
     @Override
165 180
     public void deleteCollectionName(String collectionName) {
181
+        MilvusClientV2 milvusClient = new MilvusClientV2(
182
+                ConnectConfig.builder()
183
+                        .uri(milvusServiceUrl)
184
+                        .build());
166 185
         DropCollectionReq dropCollectionReq = DropCollectionReq.builder()
167 186
                 .collectionName(collectionName)
168 187
                 .build();
@@ -174,8 +193,15 @@ public class MilvusServiceImpl implements IMilvusService {
174 193
      */
175 194
     @Override
176 195
     public List<String> listDocument(String collectionName, String fileType) {
196
+        MilvusClientV2 milvusClient = new MilvusClientV2(
197
+                ConnectConfig.builder()
198
+                        .uri(milvusServiceUrl)
199
+                        .build());
177 200
         List<String> documentList = new ArrayList<>();
178
-        loadCollectionName(collectionName);
201
+        LoadCollectionReq loadCollectionReq = LoadCollectionReq.builder()
202
+                .collectionName(collectionName)
203
+                .build();
204
+        milvusClient.loadCollection(loadCollectionReq);
179 205
         QueryReq queryParam = QueryReq.builder()
180 206
                 .collectionName(collectionName)
181 207
                 .filter("id > 0")
@@ -195,7 +221,10 @@ public class MilvusServiceImpl implements IMilvusService {
195 221
                 documentList.add(rowRecord.getEntity().get("file_name").toString());
196 222
             }
197 223
         }
198
-        releaseCollectionName(collectionName);
224
+        ReleaseCollectionReq releaseCollectionReq = ReleaseCollectionReq.builder()
225
+                .collectionName(collectionName)
226
+                .build();
227
+        milvusClient.releaseCollection(releaseCollectionReq);
199 228
         return documentList.stream().distinct().collect(Collectors.toList());
200 229
     }
201 230
 
@@ -204,7 +233,14 @@ public class MilvusServiceImpl implements IMilvusService {
204 233
      */
205 234
     @Override
206 235
     public void removeDocument(String collectionName, String fileName) {
207
-        loadCollectionName(collectionName);
236
+        MilvusClientV2 milvusClient = new MilvusClientV2(
237
+                ConnectConfig.builder()
238
+                        .uri(milvusServiceUrl)
239
+                        .build());
240
+        LoadCollectionReq loadCollectionReq = LoadCollectionReq.builder()
241
+                .collectionName(collectionName)
242
+                .build();
243
+        milvusClient.loadCollection(loadCollectionReq);
208 244
         DeleteReq deleteReq = DeleteReq.builder()
209 245
                 .collectionName(collectionName)
210 246
                 .filter(String.format("file_name == \"%s\"", fileName))
@@ -217,32 +253,19 @@ public class MilvusServiceImpl implements IMilvusService {
217 253
      */
218 254
     @Override
219 255
     public void removeAllDocument(String collectionName) {
220
-        loadCollectionName(collectionName);
221
-        DeleteReq deleteReq = DeleteReq.builder()
222
-                .collectionName(collectionName)
223
-                .filter("id > 0")
224
-                .build();
225
-        milvusClient.delete(deleteReq);
226
-    }
227
-
228
-    /**
229
-     * 加载知识库Collection
230
-     */
231
-    public void loadCollectionName(String collectionName) {
256
+        MilvusClientV2 milvusClient = new MilvusClientV2(
257
+                ConnectConfig.builder()
258
+                        .uri(milvusServiceUrl)
259
+                        .build());
232 260
         LoadCollectionReq loadCollectionReq = LoadCollectionReq.builder()
233 261
                 .collectionName(collectionName)
234 262
                 .build();
235 263
         milvusClient.loadCollection(loadCollectionReq);
236
-    }
237
-
238
-    /**
239
-     * 释放知识库Collection
240
-     */
241
-    public void releaseCollectionName(String collectionName) {
242
-        ReleaseCollectionReq releaseCollectionReq = ReleaseCollectionReq.builder()
264
+        DeleteReq deleteReq = DeleteReq.builder()
243 265
                 .collectionName(collectionName)
266
+                .filter("id > 0")
244 267
                 .build();
245
-        milvusClient.releaseCollection(releaseCollectionReq);
268
+        milvusClient.delete(deleteReq);
246 269
     }
247 270
 
248 271
 }

+ 9
- 5
oa-back/ruoyi-system/src/main/java/com/ruoyi/llm/service/impl/CmcAgentServiceImpl.java Näytä tiedosto

@@ -42,6 +42,7 @@ import org.noear.solon.ai.chat.ChatSession;
42 42
 import org.noear.solon.ai.chat.message.ChatMessage;
43 43
 import org.noear.solon.ai.chat.session.InMemoryChatSession;
44 44
 import org.springframework.beans.factory.annotation.Autowired;
45
+import org.springframework.beans.factory.annotation.Value;
45 46
 import org.springframework.stereotype.Service;
46 47
 import org.springframework.web.multipart.MultipartFile;
47 48
 
@@ -74,12 +75,11 @@ public class CmcAgentServiceImpl implements ICmcAgentService
74 75
 
75 76
     private static final EmbeddingModel embeddingModel = new BgeSmallZhV15EmbeddingModel();
76 77
 
77
-    private static final String llmServiceUrl = "http://192.168.28.196:8000/v1/chat/completions";
78
+    @Value("${llmService.url}")
79
+    private String llmServiceUrl;
78 80
 
79
-    private static final MilvusClientV2 milvusClient = new MilvusClientV2(
80
-            ConnectConfig.builder()
81
-                    .uri("http://192.168.28.196:19530")
82
-                    .build());
81
+    @Value("${milvusService.url}")
82
+    private String milvusServiceUrl;
83 83
 
84 84
     /**
85 85
      * 查询智能体
@@ -650,6 +650,10 @@ public class CmcAgentServiceImpl implements ICmcAgentService
650 650
      * 检索知识库
651 651
      */
652 652
     private List<List<SearchResp.SearchResult>> retrieve(String collectionName, String query, int topK) {
653
+        MilvusClientV2 milvusClient = new MilvusClientV2(
654
+                ConnectConfig.builder()
655
+                        .uri(milvusServiceUrl)
656
+                        .build());
653 657
         List<BaseVector> queryVector = Collections.singletonList(new FloatVec(embeddingModel.embed(query).content().vector()));
654 658
 
655 659
         //  加载集合

+ 23
- 0
oa-back/ruoyi-system/src/main/java/com/ruoyi/oa/domain/CmcCheck.java Näytä tiedosto

@@ -1,5 +1,6 @@
1 1
 package com.ruoyi.oa.domain;
2 2
 
3
+import java.math.BigDecimal;
3 4
 import java.util.Date;
4 5
 import com.fasterxml.jackson.annotation.JsonFormat;
5 6
 import com.ruoyi.common.core.domain.entity.SysUser;
@@ -45,6 +46,10 @@ public class CmcCheck extends BaseEntity
45 46
     @Excel(name = "核算说明")
46 47
     private String checkComment;
47 48
 
49
+    /** 核算总额 */
50
+    @Excel(name = "核算总额")
51
+    private BigDecimal totalAdjust;
52
+
48 53
     /** 财务审核人 */
49 54
     @Excel(name = "财务审核人")
50 55
     private String cwUserName;
@@ -151,6 +156,15 @@ public class CmcCheck extends BaseEntity
151 156
     {
152 157
         return checkComment;
153 158
     }
159
+    public void setTotalAdjust(BigDecimal totalAdjust)
160
+    {
161
+        this.totalAdjust = totalAdjust;
162
+    }
163
+
164
+    public BigDecimal getTotalAdjust()
165
+    {
166
+        return totalAdjust;
167
+    }
154 168
     public void setCwUserId(Long cwUserId) 
155 169
     {
156 170
         this.cwUserId = cwUserId;
@@ -283,6 +297,15 @@ public class CmcCheck extends BaseEntity
283 297
     {
284 298
         return project;
285 299
     }
300
+    public void setProjectNumber(String projectNumber)
301
+    {
302
+        this.projectNumber = projectNumber;
303
+    }
304
+
305
+    public String getProjectNumber()
306
+    {
307
+        return projectNumber;
308
+    }
286 309
 
287 310
     @Override
288 311
     public String toString() {

+ 0
- 1
oa-back/ruoyi-system/src/main/resources/mapper/oa/CmcArchiveMapper.xml Näytä tiedosto

@@ -7,7 +7,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
7 7
     <resultMap type="CmcArchive" id="CmcArchiveResult">
8 8
         <result property="archiveId"    column="archive_id"    />
9 9
         <result property="projectId"    column="project_id"    />
10
-        <result property="projectNumber"    column="project_number"    />
11 10
         <result property="projectLeader"    column="project_leader"    />
12 11
         <result property="submitTime"    column="submit_time"    />
13 12
         <result property="submitSituation"    column="submit_situation"    />

+ 0
- 2
oa-back/ruoyi-system/src/main/resources/mapper/oa/CmcBorrowMapper.xml Näytä tiedosto

@@ -7,8 +7,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
7 7
     <resultMap type="CmcBorrow" id="CmcBorrowResult">
8 8
         <result property="borrowId"    column="borrow_id"    />
9 9
         <result property="projectId"    column="project_id"    />
10
-        <result property="projectNumber"    column="project_number"    />
11
-        <result property="undertakingDept"    column="undertaking_dept"    />
12 10
         <result property="applyReason"    column="apply_reason"    />
13 11
         <result property="borrowUsage"    column="borrow_usage"    />
14 12
         <result property="applier"    column="applier"    />

+ 8
- 3
oa-back/ruoyi-system/src/main/resources/mapper/oa/CmcCheckMapper.xml Näytä tiedosto

@@ -11,6 +11,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
11 11
         <result property="checker"    column="checker"    />
12 12
         <result property="checkTime"    column="check_time"    />
13 13
         <result property="checkComment"    column="check_comment"    />
14
+        <result property="totalAdjust"    column="total_adjust"    />
14 15
         <result property="cwUserId"    column="cw_user_id"    />
15 16
         <result property="cwTime"    column="cw_time"    />
16 17
         <result property="cwComment"    column="cw_comment"    />
@@ -54,7 +55,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
54 55
     </resultMap>
55 56
 
56 57
     <sql id="selectCmcCheckVo">
57
-        select c.check_id, c.budget_id, c.project_id, p.project_number, p.project_name, c.checker, u.nick_name as check_nick_name, c.check_time, c.check_comment, c.cw_user_id, u1.nick_name as cw_nick_name, c.cw_time,
58
+        select c.check_id, c.budget_id, c.project_id, p.project_number, p.project_name, c.checker, u.nick_name as check_nick_name, c.check_time, c.check_comment, c.total_adjust, c.cw_user_id, u1.nick_name as cw_nick_name, c.cw_time,
58 59
                c.cw_comment, c.manager_user_id, u2.nick_name as manager_nick_name, c.manager_time, c.manager_comment, c.zjl_user_id, u3.nick_name as zjl_nick_name, c.zjl_time, c.zjl_comment from cmc_check as c
59 60
         left join sys_user as u on u.user_id = c.checker
60 61
         left join sys_user as u1 on u1.user_id = c.cw_user_id
@@ -68,8 +69,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
68 69
         <where>  
69 70
             <if test="budgetId != null  and budgetId != ''"> and c.budget_id = #{budgetId}</if>
70 71
             <if test="projectId != null  and projectId != ''"> and c.project_id = #{projectId}</if>
72
+            <if test="projectNumber != null  and projectNumber != ''"> and p.project_number like concat('%', #{projectNumber}, '%')</if>
71 73
             <if test="checker != null "> and c.checker = #{checker}</if>
72
-            <if test="checkTime != null "> and c.check_time = #{checkTime}</if>
73 74
             <if test="checkComment != null  and checkComment != ''"> and c.check_comment = #{checkComment}</if>
74 75
             <if test="cwUserId != null "> and c.cw_user_id = #{cwUserId}</if>
75 76
             <if test="cwTime != null "> and c.cw_time = #{cwTime}</if>
@@ -78,7 +79,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
78 79
             <if test="managerTime != null "> and c.manager_time = #{managerTime}</if>
79 80
             <if test="managerComment != null  and managerComment != ''"> and c.manager_comment = #{managerComment}</if>
80 81
             <if test="zjlUserId != null "> and c.zjl_user_id = #{zjlUserId}</if>
81
-            <if test="zjlTime != null "> and c.zjl_time = #{zjlTime}</if>
82
+            <if test="checkTime != null and @com.ruoyi.common.utils.DateUtils@parseDateToStr('yyyy', checkTime) == '2000' "> and c.check_time is not null</if>
83
+            <if test="checkTime != null and @com.ruoyi.common.utils.DateUtils@parseDateToStr('yyyy', checkTime) != '2000' "> and YEAR(c.check_time) = YEAR(#{checkTime})</if>
82 84
             <if test="zjlComment != null  and zjlComment != ''"> and c.zjl_comment = #{zjlComment}</if>
83 85
         </where>
84 86
     </select>
@@ -97,6 +99,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
97 99
             <if test="checker != null">checker,</if>
98 100
             <if test="checkTime != null">check_time,</if>
99 101
             <if test="checkComment != null">check_comment,</if>
102
+            <if test="totalAdjust != null">total_adjust,</if>
100 103
             <if test="cwUserId != null">cw_user_id,</if>
101 104
             <if test="cwTime != null">cw_time,</if>
102 105
             <if test="cwComment != null">cw_comment,</if>
@@ -114,6 +117,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
114 117
             <if test="checker != null">#{checker},</if>
115 118
             <if test="checkTime != null">#{checkTime},</if>
116 119
             <if test="checkComment != null">#{checkComment},</if>
120
+            <if test="totalAdjust != null">#{totalAdjust},</if>
117 121
             <if test="cwUserId != null">#{cwUserId},</if>
118 122
             <if test="cwTime != null">#{cwTime},</if>
119 123
             <if test="cwComment != null">#{cwComment},</if>
@@ -134,6 +138,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
134 138
             <if test="checker != null">checker = #{checker},</if>
135 139
             <if test="checkTime != null">check_time = #{checkTime},</if>
136 140
             <if test="checkComment != null">check_comment = #{checkComment},</if>
141
+            <if test="totalAdjust != null">total_adjust = #{totalAdjust},</if>
137 142
             <if test="cwUserId != null">cw_user_id = #{cwUserId},</if>
138 143
             <if test="cwTime != null">cw_time = #{cwTime},</if>
139 144
             <if test="cwComment != null">cw_comment = #{cwComment},</if>

+ 0
- 1
oa-back/ruoyi-system/src/main/resources/mapper/oa/CmcPerformanceStaffMapper.xml Näytä tiedosto

@@ -8,7 +8,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
8 8
         <result property="performanceStaffId"    column="performance_staff_id"    />
9 9
         <result property="performanceId"    column="performance_id"    />
10 10
         <result property="projectId"    column="project_id"    />
11
-        <result property="projectNumber"    column="project_number"    />
12 11
         <result property="userId"    column="user_id"    />
13 12
         <result property="nickName"    column="nick_name"    />
14 13
         <result property="workType"    column="work_type"    />

+ 0
- 2
oa-back/ruoyi-system/src/main/resources/mapper/oa/CmcSettleMapper.xml Näytä tiedosto

@@ -7,8 +7,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
7 7
     <resultMap type="CmcSettle" id="CmcSettleResult">
8 8
         <result property="settleId"    column="settle_id"    />
9 9
         <result property="projectId"    column="project_id"    />
10
-        <result property="projectNumber"    column="project_number"    />
11
-        <result property="undertakingDept"    column="undertaking_dept"    />
12 10
         <result property="reportDept"    column="report_dept"    />
13 11
         <result property="xmName"    column="xm_name"    />
14 12
         <result property="workloadReport"    column="workload_report"    />

+ 1
- 0
oa-ui/package.json Näytä tiedosto

@@ -59,6 +59,7 @@
59 59
     "js-beautify": "1.13.0",
60 60
     "js-cookie": "3.0.1",
61 61
     "jsencrypt": "3.0.0-rc.1",
62
+    "marked": "^4.0.14",
62 63
     "nprogress": "0.2.0",
63 64
     "ol": "^7.1.0",
64 65
     "pdfjs-dist": "^2.0.943",

+ 15
- 0
oa-ui/src/api/oa/budget/check.js Näytä tiedosto

@@ -1,3 +1,9 @@
1
+/*
2
+ * @Author: wrh
3
+ * @Date: 2025-05-15 09:14:39
4
+ * @LastEditors: wrh
5
+ * @LastEditTime: 2025-12-25 17:08:07
6
+ */
1 7
 import request from '@/utils/request'
2 8
 
3 9
 // 查询cmc项目核算列表
@@ -42,3 +48,12 @@ export function delCheck(checkId) {
42 48
     method: 'delete'
43 49
   })
44 50
 }
51
+
52
+// 核算统计
53
+export function getCheckStatistic(query) {
54
+  return request({
55
+    url: '/oa/check/statistic',
56
+    method: 'get',
57
+    params: query
58
+  })
59
+}

+ 51
- 45
oa-ui/src/views/flowable/form/budget/addBudget.vue Näytä tiedosto

@@ -1,14 +1,14 @@
1 1
 <!--
2 2
  * @Author: ysh
3 3
  * @Date: 2024-06-21 18:51:51
4
- * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-07-08 10:23:25
4
+ * @LastEditors: wrh
5
+ * @LastEditTime: 2025-12-31 16:41:48
6 6
 -->
7 7
 <template>
8 8
   <div class="app-container">
9 9
     <h2 class="text-center">项目直接生产成本预算表</h2>
10
-    <p style="text-align: center" v-if="taskName == '预算编制'">编制人:{{ $store.getters.name }}</p>
11
-    <p style="text-align: center" v-else>编制人:{{ budgetForm.compilerUser ? budgetForm.compilerUser.nickName : '' }}</p>
10
+    <p style="text-align: center" v-if="taskName == '预算编制'">编制人:{{ $store.getters.name }} | 编制时间:{{ budgetForm.createTime }} </p>
11
+    <p style="text-align: center" v-else>编制人:{{ budgetForm.compilerUser ? budgetForm.compilerUser.nickName : '' }} | 编制时间:{{ budgetForm.createTime }} </p>
12 12
     <el-divider></el-divider>
13 13
     <div class="mt20 mb20" v-if="showAlter">
14 14
       <el-alert title="任务被退回,请修改后重新提交" type="error" :closable="false">
@@ -142,19 +142,16 @@
142 142
         @cancel="returnOpen = false"></return-btn>
143 143
     </el-dialog>
144 144
     <!-- 选择修改有多个预算的项目中的预算 -->
145
-    <el-dialog title="选择要修改的预算" :visible.sync="updateData" :close-on-click-modal="false"
146
-      :close-on-press-escape="false" :show-close="false"  width="30%" append-to-body>
147
-        <el-select v-model="selectedBudget" placeholder="请选择" style="width:100%">
148
-          <el-option
149
-            v-for="item in budgets"
150
-            :key="item.budgetId"
151
-            :label="item.project.projectName +'_'+ item.createTime"
152
-            :value="item.budgetId">
153
-          </el-option>
154
-        </el-select>
155
-        <div style="text-align: center">
156
-          <el-button type="success" style=" margin-top: 10px;"  @click="getBudgeNeesUpdate()">确定</el-button>
157
-        </div>
145
+    <el-dialog title="选择要修改的预算" :visible.sync="updateData" :close-on-click-modal="false" :close-on-press-escape="false"
146
+      :show-close="false" width="30%" append-to-body>
147
+      <el-select v-model="selectedBudget" placeholder="请选择" style="width:100%">
148
+        <el-option v-for="item in budgets" :key="item.budgetId" :label="item.project.projectName + '_' + item.createTime"
149
+          :value="item.budgetId">
150
+        </el-option>
151
+      </el-select>
152
+      <div style="text-align: center">
153
+        <el-button type="success" style=" margin-top: 10px;" @click="getBudgeNeesUpdate()">确定</el-button>
154
+      </div>
158 155
     </el-dialog>
159 156
   </div>
160 157
 </template>
@@ -214,9 +211,9 @@ export default {
214 211
       viewOpen: false,
215 212
       innerSettleLimit: 0, // 内业预算绩效合计限制
216 213
       outerUsers: [], // 存储外业人员
217
-      updateData:false,
218
-      selectedBudget:"",
219
-      budgets:{},
214
+      updateData: false,
215
+      selectedBudget: "",
216
+      budgets: {},
220 217
     };
221 218
   },
222 219
   props: {
@@ -250,29 +247,38 @@ export default {
250 247
     async initBudgetForm() {
251 248
       const params = { pageNum: 1, pageSize: 20, projectId: this.taskForm.formId }
252 249
       let res = await listBudget(params)
253
-      if (res.rows[0] && res.rows[0].budgetId){
254
-        // 如项目下已有预算,询问是新建预算还是修改原有预算
255
-        try{
256
-          await this.$confirm('该项目下已有预算,请选择修改已有预算还是创建新预算','系统提示',{
257
-                              distinguishCancelAndClose: true,confirmButtonText: '创建新预算',
258
-                              cancelButtonText: '修改已有预算',showClose: false, 
259
-                              closeOnClickModal: false, closeOnPressEscape: false});
260
-          this.flag = false;
261
-          this.budgetForm.projectId = this.taskForm.formId;
262
-          this.budgetForm.budgetId = new Snowflake(1n, 1n, 0n).nextId().toString();
263
-          this.budgetForm.procInstId = this.taskForm.procInsId;
264
-        }catch(action){
265
-          if (action === 'cancel') {
266
-            this.flag = true;
267
-            this.budgets = res.rows;
268
-            this.updateData = true;
269
-            // this.budgetForm = res.rows[0];
270
-            // this.budgetId = res.rows[0].budgetId;
250
+      if (res.rows[0] && res.rows[0].budgetId) {
251
+        if (this.taskName == '预算编制') {
252
+          // 如项目下已有预算,询问是新建预算还是修改原有预算
253
+          try {
254
+            await this.$confirm('该项目下已有预算,请选择修改已有预算还是创建新预算', '系统提示', {
255
+              distinguishCancelAndClose: true, confirmButtonText: '创建新预算',
256
+              cancelButtonText: '修改已有预算', showClose: false,
257
+              closeOnClickModal: false, closeOnPressEscape: false
258
+            });
259
+            this.flag = false;
260
+            this.budgetForm.projectId = this.taskForm.formId;
261
+            this.budgetForm.budgetId = new Snowflake(1n, 1n, 0n).nextId().toString();
262
+            this.budgetForm.procInstId = this.taskForm.procInsId;
263
+          } catch (action) {
264
+            if (action === 'cancel') {
265
+              this.flag = true;
266
+              this.budgets = res.rows;
267
+              this.updateData = true;
268
+              // this.budgetForm = res.rows[0];
269
+              // this.budgetId = res.rows[0].budgetId;
270
+            }
271
+          }
272
+        }
273
+        else {          
274
+          this.flag = true;
275
+          this.budgetForm = res.rows[0];
276
+          this.budgetId = res.rows[0].budgetId;
271 277
         }
272
-      }}else {
273
-          this.flag = false;
274
-          this.budgetForm.projectId = this.taskForm.formId;
275
-          this.budgetForm.procInstId = this.taskForm.procInsId;
278
+      } else {
279
+        this.flag = false;
280
+        this.budgetForm.projectId = this.taskForm.formId;
281
+        this.budgetForm.procInstId = this.taskForm.procInsId;
276 282
       }
277 283
       // listBudget({ pageNum: 1, pageSize: 20, projectId: this.taskForm.formId }).then(
278 284
       //   res => {
@@ -483,11 +489,11 @@ export default {
483 489
         this.budgetForm.document = ""
484 490
       }
485 491
     },
486
-    getBudgeNeesUpdate(){
492
+    getBudgeNeesUpdate() {
487 493
       this.budgetForm = this.budgets.find(item => item.budgetId === this.selectedBudget);
488 494
       this.budgetId = this.selectedBudget;
489
-      this.updateData =false;
490
-   }
495
+      this.updateData = false;
496
+    }
491 497
   },
492 498
 };
493 499
 </script>

+ 2
- 2
oa-ui/src/views/flowable/form/budget/adjust/adjustIndex.vue Näytä tiedosto

@@ -2,7 +2,7 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2025-05-14 16:09:56
4 4
  * @LastEditors: wrh
5
- * @LastEditTime: 2025-09-30 16:39:33
5
+ * @LastEditTime: 2025-12-31 16:26:40
6 6
 -->
7 7
 <template>
8 8
   <div class="app-container">
@@ -50,13 +50,13 @@
50 50
           </el-table-column>
51 51
           <el-table-column label="项目编号" align="center" prop="project.projectNumber" />
52 52
           <el-table-column label="项目名称" align="center" prop="project.projectName" />
53
-          <el-table-column label="提交时间" align="center" prop="createTime" />
54 53
           <el-table-column label="预算总额" align="center" prop="totalBudget" />
55 54
           <el-table-column label="编制人" align="center" prop="compiler">
56 55
             <template slot-scope="scope">
57 56
               {{ getUserName(scope.row.compiler) }}
58 57
             </template>
59 58
           </el-table-column>
59
+          <el-table-column label="编制时间" align="center" prop="createTime" />
60 60
           <el-table-column label="预算当前流程节点" align="center" prop="taskName" />
61 61
           <!-- <el-table-column label="审核人" align="center" prop="auditor">
62 62
             <template slot-scope="scope">

+ 6
- 3
oa-ui/src/views/flowable/form/budget/adjust/budgetAdjust.vue Näytä tiedosto

@@ -2,7 +2,7 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2025-05-07 11:01:39
4 4
  * @LastEditors: wrh
5
- * @LastEditTime: 2025-09-30 16:13:13
5
+ * @LastEditTime: 2025-12-31 16:45:31
6 6
 -->
7 7
 <template>
8 8
   <div class="main">
@@ -33,7 +33,10 @@
33 33
       <el-descriptions-item label="项目备注" :span="3">
34 34
         {{ project.remark }}
35 35
       </el-descriptions-item>
36
-      <el-descriptions-item label="预算表单备注" :span="5">
36
+      <el-descriptions-item label="编制时间" :span="1">
37
+        {{ budgetForm.createTime }}
38
+      </el-descriptions-item>
39
+      <el-descriptions-item label="预算表单备注" :span="3">
37 40
         {{ budgetForm.remark }}
38 41
       </el-descriptions-item>
39 42
       <el-descriptions-item label="预算附件" :span="3" v-if="budgetForm.document">
@@ -820,7 +823,7 @@ export default {
820 823
 
821 824
         // 并发处理基础数据保存
822 825
         const savePromises = [];
823
-
826
+        this.checkForm.totalAdjust = this.totalBudgetAdjust
824 827
         if (!this.checkForm.checkId) {
825 828
           savePromises.push(addCheck(obj));
826 829
           this.$emit('preserve');

+ 2
- 2
oa-ui/src/views/flowable/form/budget/adjust/newBudgetInfo.vue Näytä tiedosto

@@ -2,12 +2,12 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2025-05-07 11:01:39
4 4
  * @LastEditors: wrh
5
- * @LastEditTime: 2025-09-30 16:23:05
5
+ * @LastEditTime: 2025-12-31 16:22:00
6 6
 -->
7 7
 <template>
8 8
   <div class="main" v-loading="loading">
9 9
     <h2 class="text-center">项目直接生产成本预算表</h2>
10
-    <p style="text-align: center">编制人:{{ budgetForm.compilerUser ? budgetForm.compilerUser.nickName : '' }}</p>
10
+    <p style="text-align: center">编制人:{{ budgetForm.compilerUser ? budgetForm.compilerUser.nickName : '' }} | 编制时间:{{ budgetForm.createTime }} </p>
11 11
     <el-divider></el-divider>
12 12
     <div class="mt20 mb20" v-if="showAlter && taskName">
13 13
       <el-alert title="任务被退回,请修改后重新提交" type="error" :closable="false">

+ 3
- 3
oa-ui/src/views/flowable/form/budget/budgetInfo.vue Näytä tiedosto

@@ -1,14 +1,14 @@
1 1
 <!--
2 2
  * @Author: ysh
3 3
  * @Date: 2024-04-03 16:28:09
4
- * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-05-27 15:11:17
4
+ * @LastEditors: wrh
5
+ * @LastEditTime: 2025-12-31 16:22:43
6 6
 -->
7 7
 <template>
8 8
   <div class="main">
9 9
     <div v-loading="loading">
10 10
       <h2 class="text-center">项目直接生产成本预算表(旧版)</h2>
11
-      <p style="text-align: center;">编制人:{{ budgetForm.compilerUser ? budgetForm.compilerUser.nickName : '' }}</p>
11
+      <p style="text-align: center;">编制人:{{ budgetForm.compilerUser ? budgetForm.compilerUser.nickName : '' }} | 编制时间:{{ budgetForm.createTime }} </p>
12 12
       <el-descriptions :column="3" border class="descriptions">
13 13
         <el-descriptions-item label="项目编号">
14 14
           {{ projectForm.projectNumber }}

+ 49
- 18
oa-ui/src/views/flowable/form/business/subContract.vue Näytä tiedosto

@@ -1,8 +1,8 @@
1 1
 <!--
2 2
  * @Author: ysh
3 3
  * @Date: 2024-05-10 15:31:57
4
- * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-08-15 09:49:17
4
+ * @LastEditors: wrh
5
+ * @LastEditTime: 2025-12-16 11:06:33
6 6
 -->
7 7
 <template>
8 8
   <div class="app-container">
@@ -27,8 +27,7 @@
27 27
           <el-form-item label="承接单位" prop="partnerId">
28 28
             <el-select allow-create v-model="form.partnerId" multiple filterable placeholder="请选择"
29 29
               :style="taskName == '合同拟稿' ? { width: '84%' } : { width: '100%' }" disabled clearable>
30
-              <el-option v-for="item in partnerList" :key="item.value" :label="item.partnerName"
31
-                :value="item.partnerId">
30
+              <el-option v-for="item in partnerList" :key="item.value" :label="item.partnerName" :value="item.partnerId">
32 31
               </el-option>
33 32
             </el-select>
34 33
             <el-button type="primary" @click="partnerOpen = true" size="mini" v-if="taskName == '合同拟稿'">选择单位</el-button>
@@ -303,8 +302,8 @@
303 302
           </el-row>
304 303
           <el-divider></el-divider>
305 304
           <el-form-item label="签订日期" prop="signDate">
306
-            <el-date-picker clearable v-model="form.signDate" type="date" value-format="yyyy-MM-dd"
307
-              placeholder="请选择签订日期" :disabled="taskName != '合同签订'">
305
+            <el-date-picker clearable v-model="form.signDate" type="date" value-format="yyyy-MM-dd" placeholder="请选择签订日期"
306
+              :disabled="taskName != '合同签订'">
308 307
             </el-date-picker>
309 308
           </el-form-item>
310 309
           <el-form-item label="签订备注" prop="signRemark">
@@ -350,6 +349,9 @@
350 349
     <el-dialog title="选择合作单位" :visible.sync="partnerOpen" width="70%" append-to-body>
351 350
       <choose-partner @confirm="confirmPartners"></choose-partner>
352 351
     </el-dialog>
352
+    <el-dialog title="选择承接合同" :visible.sync="contractOpen" width="70%" append-to-body>
353
+      <choose-contract @choose="confirmContract"></choose-contract>
354
+    </el-dialog>
353 355
     <el-drawer :visible.sync="drawerOpen" title="" :size="'70%'" append-to-body>
354 356
       <projectInfo :needReturn="false"></projectInfo>
355 357
     </el-drawer>
@@ -390,6 +392,7 @@ import workTable from './components/workTable.vue';
390 392
 import paymentTable from './components/paymentTable.vue';
391 393
 import projectChoose from '../components/chooseProject.vue';
392 394
 import ChoosePartner from '../components/choosePartner.vue';
395
+import ChooseContract from '@/views/flowable/form/components/chooseContract.vue';
393 396
 import contractForm from '@/views/flowable/form/business/contractForm.vue';
394 397
 import ReturnBtn from '@/views/flowable/form/components/flowBtn/returnBtn.vue';
395 398
 import ReturnComment from '@/views/flowable/form/components/flowBtn/returnComment.vue';
@@ -398,6 +401,7 @@ export default {
398 401
   components: {
399 402
     flow,
400 403
     projectChoose,
404
+    ChooseContract,
401 405
     FileItem,
402 406
     ChoosePartner,
403 407
     workTable,
@@ -708,17 +712,18 @@ export default {
708 712
                   commentTime: undefined
709 713
                 },
710 714
               ]
711
-              let num=0;
715
+              let num = 0;
712 716
               for (let comment of this.commentList) {
713 717
                 num = num + 1;
714
-                if(num<=3){
718
+                if (num <= 3) {
715 719
                   getUsersDeptLeaderByDept({ deptId: comment.deptId }).then(res => {
716 720
                     comment.userId = res.data.userId;
717
-                })}
718
-                else{
721
+                  })
722
+                }
723
+                else {
719 724
                   getUserByPost({ postName: '专职安全员' }).then(res => {
720
-                  comment.userId =res.data[0].userId;
721
-                })
725
+                    comment.userId = res.data[0].userId;
726
+                  })
722 727
                 }
723 728
               }
724 729
             }
@@ -833,17 +838,18 @@ export default {
833 838
       else if (val == '2') {
834 839
         this.commentOpen = true;
835 840
         this.meetingOpen = false;
836
-        let num=0;
841
+        let num = 0;
837 842
         for (let comment of this.commentList) {
838 843
           num = num + 1;
839
-          if(num<=3){
844
+          if (num <= 3) {
840 845
             getUsersDeptLeaderByDept({ deptId: comment.deptId }).then(res => {
841 846
               comment.userId = res.data.userId;
842
-          })}
843
-          else{
847
+            })
848
+          }
849
+          else {
844 850
             getUserByPost({ postName: '专职安全员' }).then(res => {
845
-            comment.userId =res.data[0].userId;
846
-          })
851
+              comment.userId = res.data[0].userId;
852
+            })
847 853
           }
848 854
         }
849 855
       }
@@ -892,6 +898,9 @@ export default {
892 898
         this.chooseProjectList.forEach(item => {
893 899
           addProjectSubcontract({ subContractId: subContractId, projectId: item.projectId })
894 900
         })
901
+        this.connectContractList.forEach(item => {
902
+          addContractSubcontract({ subContractId: subContractId, contractId: item.contractId })
903
+        })
895 904
         for (let work of this.workList) {
896 905
           work.contractId = subContractId;
897 906
           await addContractWork(work);
@@ -996,6 +1005,9 @@ export default {
996 1005
             this.chooseProjectList.forEach(item => {
997 1006
               addProjectSubcontract({ subContractId: subContractId, projectId: item.projectId })
998 1007
             })
1008
+            this.connectContractList.forEach(item => {
1009
+              addContractSubcontract({ subContractId: subContractId, contractId: item.contractId })
1010
+            })
999 1011
             for (let work of this.workList) {
1000 1012
               work.contractId = subContractId;
1001 1013
               addContractWork(work);
@@ -1065,6 +1077,12 @@ export default {
1065 1077
         for (let item of this.chooseProjectList) {
1066 1078
           await addProjectSubcontract({ subContractId: subContractId, projectId: item.projectId });
1067 1079
         }
1080
+        // 删除承接分包合同
1081
+        await delContractSubcontract(subContractId);
1082
+        // 添加新的承接分包合同
1083
+        for (let item of this.connectContractList) {
1084
+          await addContractSubcontract({ subContractId: subContractId, contractId: item.contractId });
1085
+        }
1068 1086
         // 删除合同工作
1069 1087
         await delContractWork(subContractId);
1070 1088
         // 添加新的合同工作
@@ -1120,6 +1138,19 @@ export default {
1120 1138
         })
1121 1139
       }
1122 1140
     },
1141
+    async confirmContract(val) {
1142
+      let isSame = this.haveSameIds([val], this.connectContractList, 'contractId');
1143
+      if (isSame) {
1144
+        this.$message.error('请勿重复添加已有的合同');
1145
+        return
1146
+      }
1147
+      this.contractOpen = false;
1148
+      this.connectContractList.push(val)
1149
+
1150
+    },
1151
+    haveSameIds(arr1, arr2, key) {
1152
+      return arr1.some(obj1 => arr2.some(obj2 => obj1[key] === obj2[key]))
1153
+    },
1123 1154
     // 获取合同详情
1124 1155
     fetchContracts(projectId) {
1125 1156
       return new Promise((resolve, reject) => {

+ 135
- 4
oa-ui/src/views/llm/chat/index.vue Näytä tiedosto

@@ -2,7 +2,7 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2025-04-07 14:14:05
4 4
  * @LastEditors: wrh
5
- * @LastEditTime: 2025-11-19 16:23:11
5
+ * @LastEditTime: 2025-12-17 16:24:24
6 6
 -->
7 7
 <template>
8 8
   <div class="app-container">
@@ -262,7 +262,7 @@
262 262
 
263 263
             <div class="input-wrapper">
264 264
               <el-input v-model="inputMessage" type="textarea" :rows="1" :autosize="{ minRows: 1, maxRows: 6 }"
265
-                placeholder="输入您的问题..." @keydown.native.enter="handleEnter" @keydown.native.ctrl.enter="handleCtrlEnter"
265
+                placeholder="输入您的问题..." @keydown.native.enter="handleKeyDown" @keydown.native.ctrl.enter="handleKeyDown"
266 266
                 class="message-input" resize="none" />
267 267
               <div class="input-actions">
268 268
                 <el-button circle size="small" class="action-btn" @click="handleFileUpload" icon="el-icon-paperclip">
@@ -294,6 +294,7 @@ import { listChat, addChat, updateChat } from "@/api/llm/chat";
294 294
 import { listDocument, uploadDocument } from "@/api/llm/document";
295 295
 import { getAnswer, getAnswerWithDocument } from "@/api/llm/session";
296 296
 import logoImg from '@/assets/images/logo.png'
297
+import { marked } from 'marked';
297 298
 
298 299
 export default {
299 300
   name: 'ChatView',
@@ -679,8 +680,8 @@ export default {
679 680
     },
680 681
 
681 682
     formatMessage(content) {
682
-      // 简单的消息格式化,可以扩展支持markdown等
683
-      return content.replace(/\n/g, '<br>');
683
+      // 使用marked.js解析markdown内容
684
+      return marked(content);
684 685
     },
685 686
 
686 687
     formatMessageTime(time) {
@@ -1324,4 +1325,134 @@ export default {
1324 1325
     transform: rotate(360deg);
1325 1326
   }
1326 1327
 }
1328
+
1329
+/* Markdown样式 */
1330
+.message-bubble >>> .message-text {
1331
+  /* 标题样式 */
1332
+  h1 {
1333
+    font-size: 24px;
1334
+    font-weight: 700;
1335
+    margin: 16px 0 12px 0;
1336
+    color: #333;
1337
+  }
1338
+
1339
+  h2 {
1340
+    font-size: 20px;
1341
+    font-weight: 600;
1342
+    margin: 14px 0 10px 0;
1343
+    color: #333;
1344
+  }
1345
+
1346
+  h3 {
1347
+    font-size: 18px;
1348
+    font-weight: 600;
1349
+    margin: 12px 0 8px 0;
1350
+    color: #333;
1351
+  }
1352
+
1353
+  h4, h5, h6 {
1354
+    font-size: 16px;
1355
+    font-weight: 600;
1356
+    margin: 10px 0 6px 0;
1357
+    color: #333;
1358
+  }
1359
+
1360
+  /* 段落样式 */
1361
+  p {
1362
+    margin: 8px 0;
1363
+    line-height: 1.6;
1364
+  }
1365
+
1366
+  /* 列表样式 */
1367
+  ul, ol {
1368
+    margin: 8px 0;
1369
+    padding-left: 24px;
1370
+  }
1371
+
1372
+  li {
1373
+    margin: 4px 0;
1374
+    line-height: 1.6;
1375
+  }
1376
+
1377
+  /* 代码块样式 */
1378
+  pre {
1379
+    background-color: #f5f5f5;
1380
+    border-radius: 6px;
1381
+    padding: 12px;
1382
+    overflow-x: auto;
1383
+    margin: 12px 0;
1384
+    font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
1385
+    font-size: 14px;
1386
+    line-height: 1.5;
1387
+  }
1388
+
1389
+  code {
1390
+    background-color: #f0f0f0;
1391
+    border-radius: 3px;
1392
+    padding: 2px 6px;
1393
+    font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
1394
+    font-size: 14px;
1395
+  }
1396
+
1397
+  pre code {
1398
+    background-color: transparent;
1399
+    padding: 0;
1400
+  }
1401
+
1402
+  /* 引用样式 */
1403
+  blockquote {
1404
+    border-left: 4px solid #007bff;
1405
+    padding: 8px 12px;
1406
+    margin: 12px 0;
1407
+    background-color: #f8f9fa;
1408
+    color: #666;
1409
+    border-radius: 0 6px 6px 0;
1410
+  }
1411
+
1412
+  /* 表格样式 */
1413
+  table {
1414
+    border-collapse: collapse;
1415
+    width: 100%;
1416
+    margin: 12px 0;
1417
+  }
1418
+
1419
+  th, td {
1420
+    border: 1px solid #e9ecef;
1421
+    padding: 8px 12px;
1422
+    text-align: left;
1423
+  }
1424
+
1425
+  th {
1426
+    background-color: #f8f9fa;
1427
+    font-weight: 600;
1428
+  }
1429
+
1430
+  /* 链接样式 */
1431
+  a {
1432
+    color: #007bff;
1433
+    text-decoration: none;
1434
+    transition: color 0.2s ease;
1435
+  }
1436
+
1437
+  a:hover {
1438
+    color: #0056b3;
1439
+    text-decoration: underline;
1440
+  }
1441
+
1442
+  /* 粗体和斜体样式 */
1443
+  strong {
1444
+    font-weight: 600;
1445
+  }
1446
+
1447
+  em {
1448
+    font-style: italic;
1449
+  }
1450
+
1451
+  /* 水平线样式 */
1452
+  hr {
1453
+    border: none;
1454
+    border-top: 1px solid #e9ecef;
1455
+    margin: 20px 0;
1456
+  }
1457
+}
1327 1458
 </style>

+ 1
- 0
oa-ui/src/views/oa/budget/index.vue Näytä tiedosto

@@ -51,6 +51,7 @@
51 51
           {{ getUserName(scope.row.compiler) }}
52 52
         </template>
53 53
       </el-table-column>
54
+      <el-table-column label="编制时间" align="center" prop="createTime" />
54 55
       <el-table-column label="审核人" align="center" prop="auditor">
55 56
         <template slot-scope="scope">
56 57
           {{ getUserName(scope.row.auditor) }}

+ 7
- 3
oa-ui/src/views/oa/car/index.vue Näytä tiedosto

@@ -132,18 +132,22 @@
132 132
           <el-input v-model="form.expectKm" placeholder="请输入里程" style="width:130px;margin-right:10px;" />
133 133
           <span>万千米</span>
134 134
         </el-form-item>
135
-        <el-form-item label="油耗(元/km)" prop="cost">
135
+        <el-form-item label="油耗(元/km)" prop="fuel">
136 136
           <el-input style="width:130px;margin-right:10px;" v-model="form.fuel" placeholder="请输入金额" />
137 137
           <span>元</span>
138 138
         </el-form-item>
139
-        <el-form-item label="保险(元/天)" prop="cost">
139
+        <el-form-item label="保险(元/天)" prop="insurance">
140 140
           <el-input style="width:130px;margin-right:10px;" v-model="form.insurance" placeholder="请输入金额" />
141 141
           <span>元</span>
142 142
         </el-form-item>
143
-        <el-form-item label="维修(元/天)" prop="cost">
143
+        <el-form-item label="维修(元/天)" prop="maintenance">
144 144
           <el-input style="width:130px;margin-right:10px;" v-model="form.maintenance" placeholder="请输入金额" />
145 145
           <span>元</span>
146 146
         </el-form-item>
147
+        <el-form-item label="单日成本" prop="dayCost">
148
+          <el-input style="width:130px;margin-right:10px;" v-model="form.dayCost" placeholder="请输入金额" />
149
+          <span>元</span>
150
+        </el-form-item>
147 151
         <el-form-item label="驾驶员" prop="driver">
148 152
           <el-select v-model="form.driver" filterable placeholder="请选择" clearable>
149 153
             <el-option v-for="item in driverList" :key="item.userId" :label="item.nickName" :value="item.userId">

+ 3
- 3
oa-ui/src/views/oa/study/components/studyHead.vue Näytä tiedosto

@@ -1,8 +1,8 @@
1 1
 <!--
2 2
  * @Author: ysh
3 3
  * @Date: 2025-03-05 14:19:02
4
- * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-08-12 16:40:17
4
+ * @LastEditors: wrh
5
+ * @LastEditTime: 2026-01-04 11:30:16
6 6
 -->
7 7
 <template>
8 8
   <div class="head-bg">
@@ -22,7 +22,7 @@
22 22
         <div class="user-item">
23 23
           <div class="u-info-learn" style="cursor:pointer;">
24 24
             <em>{{ hours }}h</em>
25
-            <span>2025学时</span>
25
+            <span>{{new Date().getFullYear()+"学时"}}</span>
26 26
           </div>
27 27
         </div>
28 28
       </div>

+ 4
- 4
oa-ui/src/views/oa/study/record.vue Näytä tiedosto

@@ -1,8 +1,8 @@
1 1
 <!--
2 2
  * @Author: ysh
3 3
  * @Date: 2025-03-12 10:06:03
4
- * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-08-12 16:39:40
4
+ * @LastEditors: wrh
5
+ * @LastEditTime: 2026-01-05 09:22:11
6 6
 -->
7 7
 <template>
8 8
   <div class="app-container">
@@ -83,7 +83,7 @@
83 83
                   <!-- 人员节点 -->
84 84
                   <span v-else class="user-node">
85 85
                     <i class="el-icon-user"></i>
86
-                    {{ `${data.user.nickName} (学时:${data.getHours}h)` }}
86
+                    {{ `${data.user.nickName} (学时:${data.getHours.toFixed(1)}h)` }}
87 87
                   </span>
88 88
                 </div>
89 89
               </el-tree>
@@ -186,7 +186,7 @@ export default {
186 186
       getStudyStatistic(this.rankParams).then(res => {
187 187
         this.rankList = res.rows.map((item, index) => ({
188 188
           ...item,
189
-          getHours: item.getHours ? item.getHours : 0,
189
+          getHours: item.getHours ? item.getHours.toFixed(1) : 0,
190 190
           rankIndex: (this.rankParams.pageNum - 1) * this.rankParams.pageSize + index + 1
191 191
         }));
192 192
         this.rankTotal = res.total;

+ 4
- 4
oa-ui/src/views/statistics/components/borrowStatistics.vue Näytä tiedosto

@@ -1,12 +1,12 @@
1 1
 <!--
2 2
  * @Author: ysh
3 3
  * @Date: 2024-10-18 11:17:48
4
- * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2024-12-20 10:00:35
4
+ * @LastEditors: wrh
5
+ * @LastEditTime: 2025-12-26 15:00:24
6 6
 -->
7 7
 <template>
8 8
   <div style="width:100%;" v-loading="loading">
9
-    <div class="titles">借款和结算统计</div>
9
+    <div class="titles">借款统计</div>
10 10
     <div class="warpper">
11 11
       <div class="left">
12 12
         <div class="left-top" id="borrowBar"></div>
@@ -318,7 +318,7 @@ export default {
318 318
         yAxis: [
319 319
           {
320 320
             type: 'value',
321
-            name: '金额',
321
+            name: '单位:元',
322 322
           }
323 323
         ],
324 324
         series: [

+ 16
- 2
oa-ui/src/views/statistics/components/projectStatistics.vue Näytä tiedosto

@@ -1,8 +1,8 @@
1 1
 <!--
2 2
  * @Author: ysh
3 3
  * @Date: 2024-10-11 16:41:17
4
- * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2024-10-24 16:16:05
4
+ * @LastEditors: wrh
5
+ * @LastEditTime: 2025-12-24 19:13:16
6 6
 -->
7 7
 <template>
8 8
   <div style="width:100%;" v-loading="loading">
@@ -48,6 +48,7 @@ export default {
48 48
       archive:{},
49 49
       complete:{},
50 50
       settle:{},
51
+      check:{},
51 52
       loading: true,
52 53
       activeYear: '',
53 54
       dataLoading: false,
@@ -93,6 +94,7 @@ export default {
93 94
         this.archive = this.datas.archive[0];
94 95
         this.complete = this.datas.complete[0];
95 96
         this.settle = this.datas.settle[0];
97
+        this.check = this.datas.check[0];
96 98
         this.loading = false;
97 99
       }
98 100
 
@@ -189,6 +191,18 @@ export default {
189 191
             },
190 192
             z:3
191 193
           },
194
+          {
195
+            name:'已核算',
196
+            type: 'line',
197
+            data: Object.values(this.check),
198
+            smooth: true,
199
+            areaStyle: {},
200
+            label: {
201
+              show: true,
202
+              position: 'top',
203
+            },
204
+            z:5
205
+          },
192 206
           {
193 207
             name:'总数',
194 208
             type: 'line',

+ 89
- 59
oa-ui/src/views/statistics/components/settleStatistics.vue Näytä tiedosto

@@ -1,12 +1,12 @@
1 1
 <!--
2 2
  * @Author: ysh
3 3
  * @Date: 2024-10-18 11:17:48
4
- * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2024-12-20 10:11:56
4
+ * @LastEditors: wrh
5
+ * @LastEditTime: 2025-12-26 14:47:09
6 6
 -->
7 7
 <template>
8 8
   <div style="width:100%;" v-loading="loading">
9
-    <div class="titles">结算和算统计</div>
9
+    <div class="titles">结算和算统计</div>
10 10
     <div class="warpper">
11 11
       <div class="left">
12 12
         <div class="left-top" id="settleBar"></div>
@@ -30,35 +30,45 @@ export default {
30 30
       type: Object,
31 31
       require: true,
32 32
     },
33
+    checkData: {
34
+      type: Object,
35
+      require: true,
36
+    },
33 37
   },
34 38
   watch: {
35 39
     settleType() {
36 40
       this.initSettleTypePie(this.activeYear)
37 41
     },
38 42
     settleData() {
39
-      this.initDatas();
43
+      this.initSettleDatas();
44
+    },
45
+    checkData() {
46
+      this.initCheckDatas();
40 47
     }
41 48
   },
42 49
   data() {
43 50
     return {
44 51
       loading: false,
45
-      settleAmount: {},
46 52
       settleType: {},
47 53
       settleTypeAmount: {},
48 54
       settleYear: {},
49
-      settleYear: {},
50 55
       settleAmount: {},
51
-      yearProjectAmount: {},
52
-      yearProjectCount: {},
53
-      sumSettleAmount: 0,
56
+      yearSettleProjectAmount: {},
57
+      yearSettleProjectCount: {},
58
+      yearCheckProjectAmount: {},
59
+      yearCheckProjectCount: {},
54 60
       sumSettleAmount: 0,
61
+      sumCheckAmount: 0,
62
+      checkYear: {},
63
+      checkAmount: {},
55 64
       dataLoading: false,
56 65
       activeYear: ''
57 66
     }
58 67
   },
59 68
   created() {
60 69
     this.loading = true;
61
-    this.initDatas();
70
+    this.initSettleDatas();
71
+    this.initCheckDatas();
62 72
   },
63 73
   mounted() {
64 74
     this.initSettleYearBar();
@@ -66,17 +76,16 @@ export default {
66 76
     this.initSettleTypePie('');
67 77
     // this.initSettleTypeAmountPie('');
68 78
     this.initSettleProjectLineBar();
69
-    this.initSettleAmountLine();
70 79
   },
71 80
   methods: {
72
-    initDatas() {
81
+    initSettleDatas() {
73 82
       if (Object.keys(this.settleData).length !== 0) {
74 83
         this.settleAmount = this.settleData.amount[0];
75 84
         this.settleType = this.settleData.type[0];
76 85
         this.settleTypeAmount = this.settleData.typeAmount[0];
77 86
         this.settleYear = this.settleData.year[0];
78
-        this.yearProjectAmount = this.settleData.yearProjectAmount[0];
79
-        this.yearProjectCount = this.settleData.yearProjectCount[0];
87
+        this.yearSettleProjectAmount = this.settleData.yearProjectAmount[0];
88
+        this.yearSettleProjectCount = this.settleData.yearProjectCount[0];
80 89
         let sum = Object.values(this.settleAmount).reduce((accumulator, currentValue) => accumulator + currentValue, 0);
81 90
         this.sumSettleAmount = (sum / 10000).toFixed(1)
82 91
         this.loading = false;
@@ -84,6 +93,19 @@ export default {
84 93
         this.loading = true;
85 94
       }
86 95
     },
96
+    initCheckDatas() {
97
+      if (Object.keys(this.checkData).length !== 0) {
98
+        this.checkAmount = this.checkData.amount[0];
99
+        this.checkYear = this.checkData.year[0];
100
+        this.yearCheckProjectAmount = this.checkData.yearProjectAmount[0];
101
+        this.yearCheckProjectCount = this.checkData.yearProjectCount[0];
102
+        let sum = Object.values(this.checkAmount).reduce((accumulator, currentValue) => accumulator + currentValue, 0);
103
+        this.sumCheckAmount = (sum / 10000).toFixed(1)
104
+        this.loading = false;
105
+      } else {
106
+        this.loading = true;
107
+      }
108
+    },
87 109
     clickYear(charts) {
88 110
       let that = this;
89 111
       charts.on('click', function (event) {
@@ -115,6 +137,7 @@ export default {
115 137
           height: '60%',
116 138
           containLabel: true
117 139
         },
140
+        legend: {},
118 141
         graphic: [
119 142
           {
120 143
             type: 'text',
@@ -149,6 +172,7 @@ export default {
149 172
         },
150 173
         series: [
151 174
           {
175
+            name: "结算",
152 176
             data: Object.values(this.settleYear),
153 177
             type: 'bar',
154 178
             smooth: true,
@@ -159,6 +183,19 @@ export default {
159 183
             itemStyle: {
160 184
               color: '#3498DB'
161 185
             }
186
+          },
187
+          {
188
+            name: "核算",
189
+            data: Object.values(this.checkYear),
190
+            type: 'bar',
191
+            smooth: true,
192
+            label: {
193
+              show: true,
194
+              position: 'top'
195
+            },
196
+            itemStyle: {
197
+              color: '#46CB2B'
198
+            }
162 199
           }
163 200
         ]
164 201
       };
@@ -170,7 +207,10 @@ export default {
170 207
         title: {
171 208
           text: '各年结算金额',
172 209
           subtext: '结算总额:' + (Object.values(this.settleAmount).reduce((accumulator, currentValue) => {return accumulator + currentValue}, 0)).toFixed(2) + '元' +
173
-            '(约' + this.sumSettleAmount + '万元)'
210
+            '(约' + this.sumSettleAmount + '万元)' +
211
+            "\n" + 
212
+            '核算总额:' + (Object.values(this.checkAmount).reduce((accumulator, currentValue) => {return accumulator + currentValue}, 0)).toFixed(2) + '元' +
213
+            '(约' + this.sumCheckAmount + '万元)'
174 214
         },
175 215
         grid: {
176 216
           left: '1%',
@@ -178,6 +218,10 @@ export default {
178 218
           bottom: '0%',
179 219
           height: '60%',
180 220
           containLabel: true
221
+        },        
222
+        legend: {
223
+          right: '0%',
224
+          orient: 'vertical'
181 225
         },
182 226
         tooltip: {
183 227
           trigger: 'axis',
@@ -198,7 +242,7 @@ export default {
198 242
         },
199 243
         series: [
200 244
           {
201
-            min: 0,
245
+            name: "结算",
202 246
             data: Object.values(this.settleAmount),
203 247
             type: 'line',
204 248
             smooth: true,
@@ -212,6 +256,22 @@ export default {
212 256
                 }
213 257
               }
214 258
             }
259
+          },
260
+          {
261
+            name: "核算",
262
+            data: Object.values(this.checkAmount),
263
+            type: 'line',
264
+            smooth: true,
265
+            label: {
266
+              show: true,
267
+              position: 'top',
268
+              formatter: function (params) {
269
+                let value = params.value;
270
+                if (value >= 10000) {
271
+                  return '约' + (value / 10000).toFixed(1) + '万元'
272
+                }
273
+              }
274
+            }
215 275
           }
216 276
         ]
217 277
       };
@@ -315,7 +375,7 @@ export default {
315 375
         xAxis: [
316 376
           {
317 377
             type: 'category',
318
-            data: Object.keys(this.yearProjectAmount),
378
+            data: Object.keys(this.yearSettleProjectAmount),
319 379
             axisPointer: {
320 380
               type: 'shadow'
321 381
             }
@@ -324,11 +384,12 @@ export default {
324 384
         yAxis: [
325 385
           {
326 386
             type: 'value',
327
-            name: '金额',
387
+            name: '单位:元',
328 388
           }
329 389
         ],
330 390
         series: [
331 391
           {
392
+            name: "结算",
332 393
             type: 'line',
333 394
             tooltip: {
334 395
               valueFormatter: function (value) {
@@ -336,7 +397,7 @@ export default {
336 397
               }
337 398
             },
338 399
             smooth: true,
339
-            data: Object.values(this.yearProjectAmount),
400
+            data: Object.values(this.yearSettleProjectAmount),
340 401
             label: {
341 402
               show: true,
342 403
               position: 'top',
@@ -348,47 +409,16 @@ export default {
348 409
               }
349 410
             }
350 411
           },
351
-        ]
352
-      };
353
-      ehcartsInit(settleProjectChart, 'settleProject', option);
354
-    },
355
-    initSettleAmountLine() {
356
-      let option = {
357
-        title: {
358
-          text: '各年结算金额',
359
-          subtext: '结算总额:' + Object.values(this.settleAmount).reduce((accumulator, currentValue) => accumulator + currentValue, 0) + '元' +
360
-            '(约' + this.sumSettleAmount + '万元)'
361
-        },
362
-        grid: {
363
-          left: '1%',
364
-          right: '1%',
365
-          bottom: '0%',
366
-          height: '60%',
367
-          containLabel: true
368
-        },
369
-        tooltip: {
370
-          trigger: 'axis',
371
-          axisPointer: {
372
-            type: 'none',
373
-            label: {
374
-              backgroundColor: '#6a7985'
375
-            }
376
-          }
377
-        },
378
-        xAxis: {
379
-          type: 'category',
380
-          data: Object.keys(this.settleAmount),
381
-        },
382
-        yAxis: {
383
-          type: 'value',
384
-          name: '单位:元'
385
-        },
386
-        series: [
387 412
           {
388
-            min: 0,
389
-            data: Object.values(this.settleAmount),
413
+            name: "核算",
390 414
             type: 'line',
415
+            tooltip: {
416
+              valueFormatter: function (value) {
417
+                return value + ' 元';
418
+              }
419
+            },
391 420
             smooth: true,
421
+            data: Object.values(this.yearCheckProjectAmount),
392 422
             label: {
393 423
               show: true,
394 424
               position: 'top',
@@ -402,8 +432,8 @@ export default {
402 432
           }
403 433
         ]
404 434
       };
405
-      ehcartsInit(settleAmountChart, 'settleLine', option);
406
-    }
435
+      ehcartsInit(settleProjectChart, 'settleProject', option);
436
+    },
407 437
   },
408 438
 }
409 439
 </script>

+ 10
- 3
oa-ui/src/views/statistics/index.vue Näytä tiedosto

@@ -1,8 +1,8 @@
1 1
 <!--
2 2
  * @Author: ysh
3 3
  * @Date: 2024-10-11 09:23:15
4
- * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2024-10-25 09:50:41
4
+ * @LastEditors: wrh
5
+ * @LastEditTime: 2025-12-25 18:31:30
6 6
 -->
7 7
 <template>
8 8
   <div class="app-container bg">
@@ -16,7 +16,7 @@
16 16
       <car-device-statistics v-else-if="isActive == 'device'" :deviceData="deviceInfo"
17 17
         :carData="carInfo"></car-device-statistics>
18 18
       <borrow-statistics v-else-if="isActive == 'borrow'" :borrowData="borrowInfo"></borrow-statistics>
19
-      <settle-statistics v-else-if="isActive == 'settle'" :settleData="settleInfo"></settle-statistics>
19
+      <settle-statistics v-else-if="isActive == 'settle'" :settleData="settleInfo" :checkData="checkInfo"></settle-statistics>
20 20
     </div>
21 21
   </div>
22 22
 </template>
@@ -28,6 +28,7 @@ import { getCarStatistic } from '@/api/oa/car/car';
28 28
 import { getDeviceStatistic } from '@/api/oa/device/device';
29 29
 import { getBorrowStatistic } from '@/api/oa/borrow/borrow';
30 30
 import { getSettleStatistic } from '@/api/oa/settle/settle';
31
+import { getCheckStatistic } from '@/api/oa/budget/check';
31 32
 import ProjectStatistics from './components/projectStatistics.vue';
32 33
 import contractStatistics from './components/contractStatistics.vue';
33 34
 import topHead from './components/topHead.vue';
@@ -45,6 +46,7 @@ export default {
45 46
       deviceInfo: {},
46 47
       borrowInfo: {},
47 48
       settleInfo:{},
49
+      checkInfo:{},
48 50
       dataLoading: true,
49 51
     }
50 52
   },
@@ -56,6 +58,7 @@ export default {
56 58
     this.getDeviceInfo();
57 59
     this.getBorrowInfo();
58 60
     this.getSettleInfo();
61
+    this.getCheckInfo();
59 62
   },
60 63
   mounted() {
61 64
 
@@ -90,6 +93,10 @@ export default {
90 93
       let res = await getSettleStatistic();
91 94
       this.settleInfo = res.data;
92 95
     },
96
+    async getCheckInfo(){
97
+      let res = await getCheckStatistic();
98
+      this.checkInfo = res.data;
99
+    },
93 100
     changeClick(val) {
94 101
       this.isActive = val
95 102
     }

+ 0
- 6
package-lock.json Näytä tiedosto

@@ -1,6 +0,0 @@
1
-{
2
-  "name": "cmc-oa",
3
-  "lockfileVersion": 3,
4
-  "requires": true,
5
-  "packages": {}
6
-}

Loading…
Peruuta
Tallenna