wlzboy
2025-12-26 5ceca957811a0da3741cf4957e6f1fcfca807be6
feat:增加显示强制完成按钮控制
3个文件已修改
4个文件已添加
314 ■■■■■ 已修改文件
app/APP配置数据库化说明.md 173 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/App.vue 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/api/appConfig.js 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/config.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pagesTask/detail.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/app/AppConfigController.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/app_config.sql 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/APPÅäÖÃÊý¾Ý¿â»¯ËµÃ÷.md
New file
@@ -0,0 +1,173 @@
# APP配置从数据库读取功能说明
## æ¦‚è¿°
将APP的功能开关配置从本地 `config.js` æ–‡ä»¶æ”¹ä¸ºä»Žæ•°æ®åº“动态读取,实现后台可配置化管理。
## å®žçް内容
### 1. åŽç«¯å®žçް
#### 1.1 åˆ›å»ºé…ç½®æŽ§åˆ¶å™¨
**文件**: `ruoyi-admin/src/main/java/com/ruoyi/web/controller/app/AppConfigController.java`
提供两个接口:
- `GET /app/config/features` - èŽ·å–åŠŸèƒ½å¼€å…³é…ç½®
- `GET /app/config/all` - èŽ·å–å®Œæ•´APP配置
特点:
- æ— éœ€æƒé™æ ¡éªŒï¼Œæ‰€æœ‰ç™»å½•用户都可访问
- è‡ªåŠ¨å°†é…ç½®å€¼è½¬æ¢ä¸ºå¸ƒå°”ç±»åž‹
#### 1.2 æ•°æ®åº“配置
**文件**: `sql/app_config.sql`
插入两条配置记录:
```sql
-- å°±ç»ªæŒ‰é’®é…ç½®
config_key: app.feature.showAssigneeReadyButton
config_value: true/false
-- å¼ºåˆ¶å®ŒæˆæŒ‰é’®é…ç½®
config_key: app.feature.showForceCompleteButton
config_value: true/false
```
### 2. å‰ç«¯å®žçް
#### 2.1 API接口文件
**文件**: `app/api/appConfig.js`
封装配置获取接口:
- `getAppFeatures()` - èŽ·å–åŠŸèƒ½å¼€å…³
- `getAppConfig()` - èŽ·å–å®Œæ•´é…ç½®
#### 2.2 åº”用启动加载
**文件**: `app/App.vue`
修改配置加载逻辑:
1. **应用启动时**:
   - å…ˆä½¿ç”¨æœ¬åœ°é»˜è®¤é…ç½®ï¼ˆconfig.js)
   - å¦‚果已登录,从服务器加载配置并覆盖本地配置
2. **用户登录后**:
   - ç›‘听 `user-login` äº‹ä»¶
   - è‡ªåŠ¨ä»ŽæœåŠ¡å™¨åŠ è½½æœ€æ–°é…ç½®
3. **配置合并策略**:
   - æœåŠ¡å™¨é…ç½®ä¼˜å…ˆçº§é«˜äºŽæœ¬åœ°é…ç½®
   - æœ¬åœ°é…ç½®ä½œä¸ºé»˜è®¤å€¼ï¼ˆç½‘络失败时使用)
#### 2.3 æœ¬åœ°é…ç½®ä¿ç•™
**文件**: `app/config.js`
保留 features é…ç½®ä½œä¸ºé»˜è®¤å€¼ï¼š
```javascript
features: {
  showAssigneeReadyButton: true,
  showForceCompleteButton: true
}
```
### 3. é…ç½®é¡¹è¯´æ˜Ž
| é…ç½®é”® | è¯´æ˜Ž | å¯é€‰å€¼ | é»˜è®¤å€¼ |
|-------|------|--------|--------|
| app.feature.showAssigneeReadyButton | æ˜¯å¦æ˜¾ç¤ºæ‰§è¡Œäººå°±ç»ªæŒ‰é’® | true/false | true |
| app.feature.showForceCompleteButton | æ˜¯å¦æ˜¾ç¤ºå¼ºåˆ¶å®ŒæˆæŒ‰é’® | true/false | true |
## ä½¿ç”¨æ–¹å¼
### åŽå°é…ç½®
1. **通过后台管理界面**:
   - ç™»å½•后台管理系统
   - è¿›å…¥ã€ç³»ç»Ÿç®¡ç†ã€‘->【参数设置】
   - æœç´¢ "APP-显示" æ‰¾åˆ°ç›¸å…³é…ç½®
   - ä¿®æ”¹ config_value ä¸º `true` æˆ– `false`
2. **直接修改数据库**:
```sql
-- éšè—å¼ºåˆ¶å®ŒæˆæŒ‰é’®
UPDATE sys_config
SET config_value = 'false'
WHERE config_key = 'app.feature.showForceCompleteButton';
-- æ˜¾ç¤ºå¼ºåˆ¶å®ŒæˆæŒ‰é’®
UPDATE sys_config
SET config_value = 'true'
WHERE config_key = 'app.feature.showForceCompleteButton';
```
### APP生效时机
配置修改后,APP在以下情况会获取最新配置:
1. ç”¨æˆ·é‡æ–°ç™»å½•
2. åº”用重新启动
3. ä»ŽåŽå°åˆ‡æ¢å›žå‰å°ï¼ˆå¦‚果已登录)
## é…ç½®ä¼˜å…ˆçº§
```
服务器配置 > æœ¬åœ°é»˜è®¤é…ç½®
```
- **服务器配置可用时**:使用数据库中的配置
- **服务器配置加载失败**:使用 config.js ä¸­çš„默认配置
- **用户未登录**:使用 config.js ä¸­çš„默认配置
## æ‰©å±•说明
### æ·»åŠ æ–°çš„é…ç½®é¡¹
1. **数据库添加配置**:
```sql
INSERT INTO sys_config (config_name, config_key, config_value, config_type, create_by, create_time, remark)
VALUES ('APP-新功能开关', 'app.feature.newFeature', 'true', 'N', 'admin', sysdate(), '新功能开关说明');
```
2. **后端添加读取逻辑**:
```java
// AppConfigController.java
String newFeature = configService.selectConfigByKey("app.feature.newFeature");
features.put("newFeature", "true".equalsIgnoreCase(newFeature));
```
3. **前端config.js添加默认值**:
```javascript
features: {
  showAssigneeReadyButton: true,
  showForceCompleteButton: true,
  newFeature: true  // æ–°å¢ž
}
```
4. **页面使用配置**:
```javascript
// åœ¨ methods ä¸­æ·»åŠ åˆ¤æ–­æ–¹æ³•
showNewFeature() {
  return !!(config && config.features && config.features.newFeature)
}
// åœ¨æ¨¡æ¿ä¸­ä½¿ç”¨
<button v-if="showNewFeature()">新功能按钮</button>
```
## æ³¨æ„äº‹é¡¹
1. **配置缓存**:若依框架对 sys_config è¡¨æœ‰ç¼“存机制,修改配置后可能需要刷新缓存
2. **配置格式**:config_value åªæ”¯æŒå­—符串,后端会转换为布尔值
3. **兼容性**:保留本地默认配置确保网络异常时功能可用
4. **安全性**:配置接口无需权限,确保不暴露敏感信息
## ç›¸å…³æ–‡ä»¶æ¸…单
### åŽç«¯
- `ruoyi-admin/src/main/java/com/ruoyi/web/controller/app/AppConfigController.java` - é…ç½®æŽ§åˆ¶å™¨
- `sql/app_config.sql` - é…ç½®æ•°æ®SQL
### å‰ç«¯
- `app/api/appConfig.js` - API接口
- `app/App.vue` - åº”用启动配置加载
- `app/config.js` - æœ¬åœ°é»˜è®¤é…ç½®
- `app/pagesTask/detail.vue` - ä»»åŠ¡è¯¦æƒ…é¡µï¼ˆä½¿ç”¨é…ç½®ï¼‰
app/App.vue
@@ -5,6 +5,7 @@
  import { getUnreadCount } from '@/api/message'
  import storage from '@/utils/storage'
  import { redirectToLoginByEnvironment } from '@/utils/wechat'
  import { getAppFeatures } from '@/api/appConfig'
  export default {
    data() {
@@ -29,6 +30,8 @@
          this.lastToken = token
          this.updateUnreadMessageBadge()
          // this.startMessagePolling()
          // åŠ è½½æœåŠ¡å™¨é…ç½®
          this.loadServerConfig()
        }
      })
      
@@ -91,8 +94,37 @@
        // æ³¨æ„ï¼šä¸åœ¨åº”用启动时自动启动轮询
        // åªæœ‰åœ¨ç”¨æˆ·ä¸»åŠ¨ç™»å½•æˆåŠŸåŽæ‰å¯åŠ¨ï¼ˆé€šè¿‡ user-login äº‹ä»¶è§¦å‘)
      },
      // åˆå§‹åŒ–配置:合并本地配置和服务器配置
      initConfig() {
        // å…ˆä½¿ç”¨æœ¬åœ°é»˜è®¤é…ç½®
        this.globalData.config = config
        // å¦‚果用户已登录,尝试加载服务器配置
        if (getToken()) {
          this.loadServerConfig()
        } else {
          console.log('用户未登录,使用本地默认配置')
        }
      },
      // ä»ŽæœåŠ¡å™¨åŠ è½½é…ç½®
      loadServerConfig() {
        getAppFeatures().then(response => {
          console.log('加载服务器配置成功:', response.data)
          // åˆå¹¶é…ç½®ï¼šæœåŠ¡å™¨é…ç½®è¦†ç›–æœ¬åœ°é…ç½®
          if (response.data) {
            this.globalData.config.features = Object.assign({},
              this.globalData.config.features || {},
              response.data
            )
            // æ›´æ–°å…¨å±€config对象,确保其他地方也能获取到最新配置
            config.features = this.globalData.config.features
          }
        }).catch(error => {
          console.error('加载服务器配置失败:', error)
          console.log('使用本地默认配置')
        })
      },
      // æ£€æŸ¥ç™»å½•状态并自动跳转到合适的登录页面
      checkLoginAndRedirect(options) {
app/api/appConfig.js
New file
@@ -0,0 +1,21 @@
import request from '@/utils/request'
/**
 * èŽ·å–APP功能开关配置
 */
export function getAppFeatures() {
  return request({
    url: '/app/config/features',
    method: 'get'
  })
}
/**
 * èŽ·å–å®Œæ•´çš„APP配置
 */
export function getAppConfig() {
  return request({
    url: '/app/config/all',
    method: 'get'
  })
}
app/config.js
@@ -63,7 +63,9 @@
  
  // åŠŸèƒ½å¼€å…³
  features: {
    // æ˜¯å¦æ˜¾ç¤ºæ‰§è¡Œäººâ€œå°±ç»ªâ€æŒ‰é’®
    showAssigneeReadyButton: true
    // æ˜¯å¦æ˜¾ç¤ºæ‰§è¡Œäºº"就绪"按钮
    showAssigneeReadyButton: true,
    // æ˜¯å¦æ˜¾ç¤º"强制完成"按钮
    showForceCompleteButton: true
  }
}
app/pagesTask/detail.vue
@@ -470,6 +470,7 @@
            å–消
          </button>
          <button 
            v-if="showForceCompleteFeature() && taskDetail.taskStatus === 'PENDING'"
            class="action-btn force-complete" 
            @click="showForceCompleteTimeDialog()"
          >
@@ -1421,10 +1422,15 @@
        console.log('附件删除成功:', attachmentId)
      },
      // æ˜¯å¦æ˜¾ç¤ºâ€œå°±ç»ªâ€åŠŸèƒ½ï¼ˆé…ç½®å¼€å…³ï¼‰
      // æ˜¯å¦æ˜¾ç¤º"就绪"功能(配置开关)
      showAssigneeReadyFeature() {
        return !!(config && config.features && config.features.showAssigneeReadyButton)
      },
      // æ˜¯å¦æ˜¾ç¤º"强制完成"功能(配置开关)
      showForceCompleteFeature() {
        return !!(config && config.features && config.features.showForceCompleteButton)
      },
      // å½“前用户是否为该执行人
      isAssigneeSelf(assignee) {
ruoyi-admin/src/main/java/com/ruoyi/web/controller/app/AppConfigController.java
New file
@@ -0,0 +1,66 @@
package com.ruoyi.web.controller.app;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.system.service.ISysConfigService;
/**
 * APP配置控制器
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/app/config")
public class AppConfigController extends BaseController
{
    @Autowired
    private ISysConfigService configService;
    /**
     * èŽ·å–APP功能开关配置
     * ä¸éœ€è¦æƒé™æ ¡éªŒï¼Œæ‰€æœ‰ç™»å½•用户都可以访问
     */
    @GetMapping("/features")
    public AjaxResult getFeatures()
    {
        Map<String, Object> features = new HashMap<>();
        // è¯»å–就绪按钮配置
        String showAssigneeReadyButton = configService.selectConfigByKey("app.feature.showAssigneeReadyButton");
        features.put("showAssigneeReadyButton", "true".equalsIgnoreCase(showAssigneeReadyButton));
        // è¯»å–强制完成按钮配置
        String showForceCompleteButton = configService.selectConfigByKey("app.feature.showForceCompleteButton");
        features.put("showForceCompleteButton", "true".equalsIgnoreCase(showForceCompleteButton));
        return success(features);
    }
    /**
     * èŽ·å–å®Œæ•´çš„APP配置
     * åŒ…括baseUrl、appInfo、features等
     */
    @GetMapping("/all")
    public AjaxResult getAllConfig()
    {
        Map<String, Object> config = new HashMap<>();
        // åŠŸèƒ½å¼€å…³é…ç½®
        Map<String, Object> features = new HashMap<>();
        String showAssigneeReadyButton = configService.selectConfigByKey("app.feature.showAssigneeReadyButton");
        features.put("showAssigneeReadyButton", "true".equalsIgnoreCase(showAssigneeReadyButton));
        String showForceCompleteButton = configService.selectConfigByKey("app.feature.showForceCompleteButton");
        features.put("showForceCompleteButton", "true".equalsIgnoreCase(showForceCompleteButton));
        config.put("features", features);
        return success(config);
    }
}
sql/app_config.sql
New file
@@ -0,0 +1,8 @@
-- APP功能开关配置
-- æ’入就绪按钮配置
INSERT INTO sys_config (config_name, config_key, config_value, config_type, create_by, create_time, remark)
VALUES ('APP-显示执行人就绪按钮', 'app.feature.showAssigneeReadyButton', 'true', 'N', 'admin', sysdate(), '控制APP任务详情页是否显示执行人就绪按钮(true显示,false隐藏)');
-- æ’入强制完成按钮配置
INSERT INTO sys_config (config_name, config_key, config_value, config_type, create_by, create_time, remark)
VALUES ('APP-显示强制完成按钮', 'app.feature.showForceCompleteButton', 'true', 'N', 'admin', sysdate(), '控制APP任务详情页是否显示强制完成按钮(true显示,false隐藏)');