3 Säilykkeet d492df23b5 ... 4a74581e94

Tekijä SHA1 Viesti Päivämäärä
  lucky 4a74581e94 Merge remote-tracking branch 'zkqyOrigin/master' 3 viikkoa sitten
  lucky 2cfaac8229 Merge remote-tracking branch 'zkqyOrigin/master' 3 viikkoa sitten
  lucky ff55627410 feat:应用平台修改 3 viikkoa sitten
56 muutettua tiedostoa jossa 4745 lisäystä ja 128 poistoa
  1. 18 0
      zkqy-admin/src/main/java/com/zkqy/web/controller/system/SysLoginController.java
  2. 37 0
      zkqy-admin/src/main/java/com/zkqy/web/controller/system/SysTenantController.java
  3. 112 0
      zkqy-admin/src/main/java/com/zkqy/web/controller/system/ZkqyApplicationCategoriesController.java
  4. 113 0
      zkqy-admin/src/main/java/com/zkqy/web/controller/system/ZkqyApplicationDataSourcesController.java
  5. 191 0
      zkqy-admin/src/main/java/com/zkqy/web/controller/system/ZkqyApplicationsController.java
  6. 115 50
      zkqy-admin/src/main/resources/sql/initialize_sys_tenant_menu.json
  7. 5 2
      zkqy-admin/src/main/resources/sql/initialize_sys_tenant_menu_translate_map_all.json
  8. 91 0
      zkqy-common/src/main/java/com/zkqy/common/core/domain/dto/DataSourceDTO.java
  9. 13 0
      zkqy-common/src/main/java/com/zkqy/common/core/domain/entity/SysTenant.java
  10. 115 0
      zkqy-common/src/main/java/com/zkqy/common/utils/MachineCodeUtil.java
  11. 22 0
      zkqy-common/src/main/java/com/zkqy/common/utils/db/DataSourceUtil.java
  12. 12 0
      zkqy-system/src/main/java/com/zkqy/system/domain/SysTenantMenu.java
  13. 126 0
      zkqy-system/src/main/java/com/zkqy/system/domain/ZkqyApplicationCategories.java
  14. 210 0
      zkqy-system/src/main/java/com/zkqy/system/domain/ZkqyApplicationDataSources.java
  15. 178 0
      zkqy-system/src/main/java/com/zkqy/system/domain/ZkqyApplications.java
  16. 29 0
      zkqy-system/src/main/java/com/zkqy/system/domain/vo/CloningVo.java
  17. 7 0
      zkqy-system/src/main/java/com/zkqy/system/domain/vo/SysMenuTenantVo.java
  18. 21 0
      zkqy-system/src/main/java/com/zkqy/system/mapper/SysMenuMapper.java
  19. 7 0
      zkqy-system/src/main/java/com/zkqy/system/mapper/SysTenantMenuMapper.java
  20. 61 0
      zkqy-system/src/main/java/com/zkqy/system/mapper/ZkqyApplicationCategoriesMapper.java
  21. 61 0
      zkqy-system/src/main/java/com/zkqy/system/mapper/ZkqyApplicationDataSourcesMapper.java
  22. 61 0
      zkqy-system/src/main/java/com/zkqy/system/mapper/ZkqyApplicationsMapper.java
  23. 8 0
      zkqy-system/src/main/java/com/zkqy/system/service/ISysTenantService.java
  24. 25 23
      zkqy-system/src/main/java/com/zkqy/system/service/ISysUserService.java
  25. 61 0
      zkqy-system/src/main/java/com/zkqy/system/service/IZkqyApplicationCategoriesService.java
  26. 61 0
      zkqy-system/src/main/java/com/zkqy/system/service/IZkqyApplicationDataSourcesService.java
  27. 84 0
      zkqy-system/src/main/java/com/zkqy/system/service/IZkqyApplicationsService.java
  28. 45 3
      zkqy-system/src/main/java/com/zkqy/system/service/impl/SysTenantServiceImpl.java
  29. 15 0
      zkqy-system/src/main/java/com/zkqy/system/service/impl/SysUserServiceImpl.java
  30. 97 0
      zkqy-system/src/main/java/com/zkqy/system/service/impl/ZkqyApplicationCategoriesServiceImpl.java
  31. 97 0
      zkqy-system/src/main/java/com/zkqy/system/service/impl/ZkqyApplicationDataSourcesServiceImpl.java
  32. 313 0
      zkqy-system/src/main/java/com/zkqy/system/service/impl/ZkqyApplicationsServiceImpl.java
  33. 29 0
      zkqy-system/src/main/resources/mapper/system/SysMenuMapper.xml
  34. 8 3
      zkqy-system/src/main/resources/mapper/system/SysTenantMapper.xml
  35. 11 1
      zkqy-system/src/main/resources/mapper/system/SysTenantMenuMapper.xml
  36. 2 1
      zkqy-system/src/main/resources/mapper/system/SysUserMapper.xml
  37. 97 0
      zkqy-system/src/main/resources/mapper/system/ZkqyApplicationCategoriesMapper.xml
  38. 127 0
      zkqy-system/src/main/resources/mapper/system/ZkqyApplicationDataSourcesMapper.xml
  39. 112 0
      zkqy-system/src/main/resources/mapper/system/ZkqyApplicationsMapper.xml
  40. BIN
      zkqy-ui/public/static/bb3845c295f66fc0e82f3874da4688e.jpg
  41. 84 0
      zkqy-ui/src/api/application/applications.js
  42. 44 0
      zkqy-ui/src/api/application/categories.js
  43. 44 0
      zkqy-ui/src/api/application/sources.js
  44. 11 0
      zkqy-ui/src/api/bpmprocess/process.js
  45. 16 0
      zkqy-ui/src/api/system/tenant.js
  46. 107 0
      zkqy-ui/src/components/AppCard/index.vue
  47. 87 0
      zkqy-ui/src/components/UserInfo/index.vue
  48. 3 12
      zkqy-ui/src/lang/en.js
  49. 9 8
      zkqy-ui/src/lang/zh.js
  50. 10 6
      zkqy-ui/src/layout/components/Navbar.vue
  51. 1 3
      zkqy-ui/src/main.js
  52. 1 1
      zkqy-ui/src/permission.js
  53. 10 1
      zkqy-ui/src/router/index.js
  54. 1426 0
      zkqy-ui/src/views/applicationview/AppPlatform.vue
  55. 10 0
      zkqy-ui/src/views/login.vue
  56. 85 14
      zkqy-ui/src/views/system/tenant/index.vue

+ 18 - 0
zkqy-admin/src/main/java/com/zkqy/web/controller/system/SysLoginController.java

@@ -5,6 +5,7 @@ import java.util.*;
 
 import com.zkqy.common.core.domain.entity.DataSource;
 import com.zkqy.common.core.domain.entity.SysTenant;
+import com.zkqy.common.utils.MachineCodeUtil;
 import com.zkqy.common.utils.StringUtils;
 import com.zkqy.framework.web.service.TokenService;
 import com.zkqy.system.service.*;
@@ -106,6 +107,16 @@ public class SysLoginController {
 //        if (!checkTenantExpirationTimeMsg.isEmpty()) {
 //            return AjaxResult.error(checkTenantExpirationTimeMsg);
 //        }
+        //校验租户的机器嘛信息是否发生变化
+        SysTenant sysTenant = sysTenantService.selectSysTenantByTenantId(Long.valueOf(loginBody.getTenantID()));
+        if(null==sysTenant.getMachineCode()){
+            return AjaxResult.error("请联系管理员申请新的机器码");
+        }
+        if(org.apache.commons.lang3.StringUtils.isBlank(sysTenant.getMachineCode())&&!sysTenant.getMachineCode().equals("跳过")) {
+            if (StringUtils.isNull(sysTenant.getMachineCode()) || !sysTenant.getMachineCode().equals(MachineCodeUtil.getMachineCode())) {
+                return AjaxResult.error("请联系管理员申请新的机器码");
+            }
+        }
         ajax.put(Constants.TOKEN, token);
         return ajax;
     }
@@ -185,6 +196,13 @@ public class SysLoginController {
     @GetMapping("/isTenantExist")
     public AjaxResult isTenantExist(String tenantCode) {
         SysTenant sysTenantInfo = iSysTenantService.selectSysTenantByTenantCode(tenantCode);
+        String machineCode = MachineCodeUtil.getMachineCode();
+
+            if(org.apache.commons.lang3.StringUtils.isBlank(sysTenantInfo.getMachineCode()) ||!machineCode.equals(sysTenantInfo.getMachineCode())){
+                sysTenantInfo.setLoginPageConfiguration(loginPageConfigurationService.selectLoginPageConfigurationByLoginPageNumber(tenantCode, "tool"));
+                sysTenantInfo.setMachineCode("请联系管理员激活租户");
+                return AjaxResult.success(sysTenantInfo);
+            }
         if (sysTenantInfo != null) {
             sysTenantInfo.setLoginPageConfiguration(loginPageConfigurationService.selectLoginPageConfigurationByLoginPageNumber(tenantCode, "tool"));
             return AjaxResult.success(sysTenantInfo);

+ 37 - 0
zkqy-admin/src/main/java/com/zkqy/web/controller/system/SysTenantController.java

@@ -5,6 +5,8 @@ import java.util.Map;
 import javax.servlet.http.HttpServletResponse;
 
 import com.zkqy.common.annotation.Anonymous;
+import com.zkqy.common.utils.MachineCodeUtil;
+import com.zkqy.system.domain.vo.CloningVo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 import com.zkqy.common.annotation.Log;
@@ -39,6 +41,29 @@ public class SysTenantController extends BaseController {
         return getDataTable(list);
     }
 
+    /**
+     * 生成机器码接口
+     * @return
+     */
+    @GetMapping("/create/generateMachineCode")
+    @Anonymous
+    public String generateMachineCode() {
+        String machineCode = MachineCodeUtil.getMachineCode();
+        return machineCode;
+    }
+
+    /**
+     * exe脚本用
+     * @return
+     */
+    @GetMapping("/create/generateMachineCode2")
+    @Anonymous
+    public AjaxResult generateMachineCode2() {
+        String machineCode = MachineCodeUtil.getMachineCode();
+        return AjaxResult.success("机器码获取成功",machineCode);
+    }
+
+
     /**
      * 查询所有租户信息
      */
@@ -166,4 +191,16 @@ public class SysTenantController extends BaseController {
         return AjaxResult.success(sysTenantService.selectTenantTree(getTenantId()));
     }
 
+
+    /**
+     * 克隆租户菜单
+     * 当前租户id
+     * 被克隆的租户id
+     */
+    @PostMapping("/cloning")
+    public AjaxResult cloning(@RequestBody CloningVo cloningVo) {
+        return AjaxResult.success(sysTenantService.cloning(cloningVo));
+    }
+
+
 }

+ 112 - 0
zkqy-admin/src/main/java/com/zkqy/web/controller/system/ZkqyApplicationCategoriesController.java

@@ -0,0 +1,112 @@
+package com.zkqy.web.controller.system;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+
+import com.zkqy.common.annotation.Anonymous;
+import com.zkqy.common.annotation.Log;
+import com.zkqy.common.core.controller.BaseController;
+import com.zkqy.common.core.domain.AjaxResult;
+import com.zkqy.common.core.page.TableDataInfo;
+import com.zkqy.common.enums.BusinessType;
+import com.zkqy.common.utils.poi.ExcelUtil;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.zkqy.system.domain.ZkqyApplicationCategories;
+import com.zkqy.system.service.IZkqyApplicationCategoriesService;
+
+/**
+ * 应用分类信息Controller
+ *
+ * @author hmc
+ * @date 2025-06-11
+ */
+@RestController
+@RequestMapping("/application/categories")
+public class ZkqyApplicationCategoriesController extends BaseController
+{
+    @Autowired
+    private IZkqyApplicationCategoriesService zkqyApplicationCategoriesService;
+
+    /**
+     * 查询应用分类信息列表
+     */
+//    @PreAuthorize("@ss.hasPermi('application:categories:list')")
+    @GetMapping("/list")
+    @Anonymous
+    public TableDataInfo list(ZkqyApplicationCategories zkqyApplicationCategories)
+    {
+        startPage();
+        List<ZkqyApplicationCategories> list = zkqyApplicationCategoriesService.selectZkqyApplicationCategoriesList(zkqyApplicationCategories);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出应用分类信息列表
+     */
+//    @PreAuthorize("@ss.hasPermi('application:categories:export')")
+    @Log(title = "应用分类信息", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    @Anonymous
+    public void export(HttpServletResponse response, ZkqyApplicationCategories zkqyApplicationCategories)
+    {
+        List<ZkqyApplicationCategories> list = zkqyApplicationCategoriesService.selectZkqyApplicationCategoriesList(zkqyApplicationCategories);
+        ExcelUtil<ZkqyApplicationCategories> util = new ExcelUtil<ZkqyApplicationCategories>(ZkqyApplicationCategories.class);
+        util.exportExcel(response, list, "应用分类信息数据");
+    }
+
+    /**
+     * 获取应用分类信息详细信息
+     */
+//    @PreAuthorize("@ss.hasPermi('application:categories:query')")
+    @GetMapping(value = "/{id}")
+    @Anonymous
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return success(zkqyApplicationCategoriesService.selectZkqyApplicationCategoriesById(id));
+    }
+
+    /**
+     * 新增应用分类信息
+     */
+//    @PreAuthorize("@ss.hasPermi('application:categories:add')")
+    @Log(title = "应用分类信息", businessType = BusinessType.INSERT)
+    @PostMapping
+    @Anonymous
+    public AjaxResult add(@RequestBody ZkqyApplicationCategories zkqyApplicationCategories)
+    {
+        return toAjax(zkqyApplicationCategoriesService.insertZkqyApplicationCategories(zkqyApplicationCategories));
+    }
+
+    /**
+     * 修改应用分类信息
+     */
+//    @PreAuthorize("@ss.hasPermi('application:categories:edit')")
+    @Log(title = "应用分类信息", businessType = BusinessType.UPDATE)
+    @PutMapping
+    @Anonymous
+    public AjaxResult edit(@RequestBody ZkqyApplicationCategories zkqyApplicationCategories)
+    {
+        return toAjax(zkqyApplicationCategoriesService.updateZkqyApplicationCategories(zkqyApplicationCategories));
+    }
+
+    /**
+     * 删除应用分类信息
+     */
+//    @PreAuthorize("@ss.hasPermi('application:categories:remove')")
+    @Log(title = "应用分类信息", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    @Anonymous
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(zkqyApplicationCategoriesService.deleteZkqyApplicationCategoriesByIds(ids));
+    }
+}

+ 113 - 0
zkqy-admin/src/main/java/com/zkqy/web/controller/system/ZkqyApplicationDataSourcesController.java

@@ -0,0 +1,113 @@
+package com.zkqy.web.controller.system;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+
+import com.zkqy.common.annotation.Anonymous;
+import com.zkqy.common.annotation.Log;
+import com.zkqy.common.core.controller.BaseController;
+import com.zkqy.common.core.domain.AjaxResult;
+import com.zkqy.common.core.page.TableDataInfo;
+import com.zkqy.common.enums.BusinessType;
+import com.zkqy.common.utils.poi.ExcelUtil;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.zkqy.system.domain.ZkqyApplicationDataSources;
+import com.zkqy.system.service.IZkqyApplicationDataSourcesService;
+
+
+/**
+ * 应用数据源信息Controller
+ *
+ * @author hmc
+ * @date 2025-06-11
+ */
+@RestController
+@RequestMapping("/application/sources")
+public class ZkqyApplicationDataSourcesController extends BaseController
+{
+    @Autowired
+    private IZkqyApplicationDataSourcesService zkqyApplicationDataSourcesService;
+
+    /**
+     * 查询应用数据源信息列表
+     */
+//    @PreAuthorize("@ss.hasPermi('application:sources:list')")
+    @GetMapping("/list")
+    @Anonymous
+    public TableDataInfo list(ZkqyApplicationDataSources zkqyApplicationDataSources)
+    {
+        startPage();
+        List<ZkqyApplicationDataSources> list = zkqyApplicationDataSourcesService.selectZkqyApplicationDataSourcesList(zkqyApplicationDataSources);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出应用数据源信息列表
+     */
+//    @PreAuthorize("@ss.hasPermi('application:sources:export')")
+    @Log(title = "应用数据源信息", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    @Anonymous
+    public void export(HttpServletResponse response, ZkqyApplicationDataSources zkqyApplicationDataSources)
+    {
+        List<ZkqyApplicationDataSources> list = zkqyApplicationDataSourcesService.selectZkqyApplicationDataSourcesList(zkqyApplicationDataSources);
+        ExcelUtil<ZkqyApplicationDataSources> util = new ExcelUtil<ZkqyApplicationDataSources>(ZkqyApplicationDataSources.class);
+        util.exportExcel(response, list, "应用数据源信息数据");
+    }
+
+    /**
+     * 获取应用数据源信息详细信息
+     */
+//    @PreAuthorize("@ss.hasPermi('application:sources:query')")
+    @GetMapping(value = "/{id}")
+    @Anonymous
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return success(zkqyApplicationDataSourcesService.selectZkqyApplicationDataSourcesById(id));
+    }
+
+    /**
+     * 新增应用数据源信息
+     */
+//    @PreAuthorize("@ss.hasPermi('application:sources:add')")
+    @Log(title = "应用数据源信息", businessType = BusinessType.INSERT)
+    @Anonymous
+    @PostMapping
+    public AjaxResult add(@RequestBody ZkqyApplicationDataSources zkqyApplicationDataSources)
+    {
+        return toAjax(zkqyApplicationDataSourcesService.insertZkqyApplicationDataSources(zkqyApplicationDataSources));
+    }
+
+    /**
+     * 修改应用数据源信息
+     */
+//    @PreAuthorize("@ss.hasPermi('application:sources:edit')")
+    @Log(title = "应用数据源信息", businessType = BusinessType.UPDATE)
+    @Anonymous
+    @PutMapping
+    public AjaxResult edit(@RequestBody ZkqyApplicationDataSources zkqyApplicationDataSources)
+    {
+        return toAjax(zkqyApplicationDataSourcesService.updateZkqyApplicationDataSources(zkqyApplicationDataSources));
+    }
+
+    /**
+     * 删除应用数据源信息
+     */
+//    @PreAuthorize("@ss.hasPermi('application:sources:remove')")
+    @Log(title = "应用数据源信息", businessType = BusinessType.DELETE)
+    @Anonymous
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(zkqyApplicationDataSourcesService.deleteZkqyApplicationDataSourcesByIds(ids));
+    }
+}

+ 191 - 0
zkqy-admin/src/main/java/com/zkqy/web/controller/system/ZkqyApplicationsController.java

@@ -0,0 +1,191 @@
+package com.zkqy.web.controller.system;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.servlet.http.HttpServletResponse;
+
+import com.zkqy.common.annotation.Anonymous;
+import com.zkqy.common.annotation.Log;
+import com.zkqy.common.constant.HttpStatus;
+import com.zkqy.common.core.controller.BaseController;
+import com.zkqy.common.core.domain.AjaxResult;
+import com.zkqy.common.core.domain.dto.DataSourceDTO;
+import com.zkqy.common.core.domain.entity.DataSource;
+import com.zkqy.common.core.domain.entity.SysMenu;
+import com.zkqy.common.core.domain.entity.SysTenant;
+import com.zkqy.common.core.domain.entity.SysUser;
+import com.zkqy.common.core.page.TableDataInfo;
+import com.zkqy.common.enums.BusinessType;
+import com.zkqy.common.utils.SecurityUtils;
+import com.zkqy.common.utils.StringUtils;
+import com.zkqy.common.utils.db.DataSourceUtil;
+import com.zkqy.common.utils.poi.ExcelUtil;
+import com.zkqy.system.domain.ZkqyApplicationDataSources;
+import com.zkqy.system.mapper.SysMenuDefaultMapper;
+import com.zkqy.system.service.IZkqyApplicationDataSourcesService;
+import com.zkqy.system.service.impl.SysTenantServiceImpl;
+import com.zkqy.system.service.impl.SysUserServiceImpl;
+import org.apache.poi.ss.formula.functions.T;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.zkqy.system.domain.ZkqyApplications;
+import com.zkqy.system.service.IZkqyApplicationsService;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * 租户应用Controller
+ *
+ * @author hmc
+ * @date 2025-06-11
+ */
+@RestController
+@RequestMapping("/application/applications")
+public class ZkqyApplicationsController extends BaseController
+{
+    @Autowired
+    private IZkqyApplicationsService zkqyApplicationsService;
+
+    @Autowired
+    private SysUserServiceImpl sysUserService;
+
+    @Autowired
+    private IZkqyApplicationDataSourcesService zkqyApplicationDataSourcesService;
+
+    RestTemplate restTemplate = new RestTemplate();
+
+    @Value("${parameter.ip.DATA_ENGINE_INITDATABASE_IP}")
+    public String DATA_ENGINE_INITDATABASE_IP;
+
+    @Autowired
+    SysTenantServiceImpl sysTenantService;
+
+    @Autowired
+    private SysMenuDefaultMapper sysMenuDefaultMapper;
+
+    /**
+     * 查询租户应用列表
+     */
+//    @PreAuthorize("@ss.hasPermi('application:applications:list')")
+    @GetMapping("/list")
+    @Anonymous
+    public TableDataInfo list(ZkqyApplications zkqyApplications)
+    {
+        startPage();
+        if(null!=zkqyApplications.getKeyword()){
+            zkqyApplications.setAppName(zkqyApplications.getKeyword());
+        }
+        List<ZkqyApplications> list = zkqyApplicationsService.selectZkqyApplicationsList(zkqyApplications);
+        return getDataTable(list);
+    }
+
+    /**
+     * 测试数据源是否可以被连接
+     * @param dto
+     * @return
+     */
+    @PostMapping("/testConnection")
+    public AjaxResult testConnection(@RequestBody DataSourceDTO dto) {
+            boolean result = zkqyApplicationsService.isDatabaseConnectionPass(dto);
+            if (result) {
+                return AjaxResult.success("测试通过");
+            } else {
+                return AjaxResult.error("测试失败");
+            }
+    }
+
+
+
+    /**
+     * 初始化数据源信息
+     */
+    @PostMapping("/applicationInitDataSource")
+    public AjaxResult applicationInitDataSource(@RequestBody DataSourceDTO dto) {
+        AjaxResult ajaxResult = zkqyApplicationsService.applicationInitDataSource(dto);
+        return  ajaxResult;
+    }
+
+
+    /**
+     * 导出租户应用列表
+     */
+//    @PreAuthorize("@ss.hasPermi('application:applications:export')")
+    @Log(title = "租户应用", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    @Anonymous
+    public void export(HttpServletResponse response, ZkqyApplications zkqyApplications)
+    {
+        List<ZkqyApplications> list = zkqyApplicationsService.selectZkqyApplicationsList(zkqyApplications);
+        ExcelUtil<ZkqyApplications> util = new ExcelUtil<ZkqyApplications>(ZkqyApplications.class);
+        util.exportExcel(response, list, "租户应用数据");
+    }
+
+    /**
+     * 获取租户应用详细信息
+     */
+//    @PreAuthorize("@ss.hasPermi('application:applications:query')")
+    @GetMapping(value = "/{id}")
+    @Anonymous
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return success(zkqyApplicationsService.selectZkqyApplicationsById(id));
+    }
+
+    /**
+     * 新增租户应用
+     */
+//    @PreAuthorize("@ss.hasPermi('application:applications:add')")
+//    @Log(title = "租户应用", businessType = BusinessType.INSERT)
+    @Anonymous
+    @PostMapping
+    public AjaxResult add(@RequestBody ZkqyApplications zkqyApplications)
+    {
+        //
+        return toAjax(zkqyApplicationsService.insertZkqyApplications(zkqyApplications));
+    }
+
+
+
+    @Anonymous
+    @GetMapping("allClassification")
+    public AjaxResult allClassification()
+    {
+        ZkqyApplications applications=new ZkqyApplications();
+        return AjaxResult.success(zkqyApplicationsService.allClassification(applications));
+    }
+    /**
+     * 修改租户应用
+     */
+//    @PreAuthorize("@ss.hasPermi('application:applications:edit')")
+    @Log(title = "租户应用", businessType = BusinessType.UPDATE)
+    @Anonymous
+    @PutMapping
+    public AjaxResult edit(@RequestBody ZkqyApplications zkqyApplications)
+    {
+        return toAjax(zkqyApplicationsService.updateZkqyApplications(zkqyApplications));
+    }
+
+    /**
+     * 删除租户应用
+     */
+//    @PreAuthorize("@ss.hasPermi('application:applications:remove')")
+    @Log(title = "租户应用", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    @Anonymous
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(zkqyApplicationsService.deleteZkqyApplicationsByIds(ids));
+    }
+}

+ 115 - 50
zkqy-admin/src/main/resources/sql/initialize_sys_tenant_menu.json

@@ -1509,7 +1509,7 @@
         "menuName": "移动端引擎",
         "parentName": null,
         "parentId": 0,
-        "orderNum": 6,
+        "orderNum": 3,
         "path": "ydyq",
         "component": null,
         "query": null,
@@ -1623,53 +1623,118 @@
         "children": [],
         "tenantName": null,
         "tenantId": null
-    },{
-    "createBy": null,
-    "createTime": "2025-03-08 15:22:00",
-    "updateBy": null,
-    "updateTime": null,
-    "remark": null,
-    "menuId": 11441,
-    "menuName": "树形表格",
-    "parentName": null,
-    "parentId": 1103,
-    "orderNum": 5,
-    "path": "treeTable",
-    "component": "treeTable/index",
-    "query": null,
-    "isFrame": "1",
-    "isCache": "0",
-    "menuType": "C",
-    "visible": "0",
-    "status": "0",
-    "perms": "",
-    "icon": "form",
-    "children": [],
-    "tenantName": null,
-    "tenantId": null
-},{
-    "createBy": null,
-    "createTime": "2025-03-08 15:22:01",
-    "updateBy": null,
-    "updateTime": null,
-    "remark": null,
-    "menuId": 11442,
-    "menuName": "树形表格编辑",
-    "parentName": null,
-    "parentId": 1103,
-    "orderNum": 6,
-    "path": "treeTableMange",
-    "component": "treeTableMange/index",
-    "query": null,
-    "isFrame": "1",
-    "isCache": "0",
-    "menuType": "C",
-    "visible": "1",
-    "status": "0",
-    "perms": "",
-    "icon": "form",
-    "children": [],
-    "tenantName": null,
-    "tenantId": null
-}
+    },
+    {
+        "createBy": null,
+        "createTime": "2025-03-08 15:22:00",
+        "updateBy": null,
+        "updateTime": null,
+        "remark": null,
+        "menuId": 11441,
+        "menuName": "树形表格",
+        "parentName": null,
+        "parentId": 1103,
+        "orderNum": 5,
+        "path": "treeTable",
+        "component": "treeTable/index",
+        "query": null,
+        "isFrame": "1",
+        "isCache": "0",
+        "menuType": "C",
+        "visible": "0",
+        "status": "0",
+        "perms": "",
+        "icon": "form",
+        "children": [],
+        "tenantName": null,
+        "tenantId": null
+    },
+    {
+        "createBy": null,
+        "createTime": "2025-03-08 15:22:01",
+        "updateBy": null,
+        "updateTime": null,
+        "remark": null,
+        "menuId": 11442,
+        "menuName": "树形表格编辑",
+        "parentName": null,
+        "parentId": 1103,
+        "orderNum": 6,
+        "path": "treeTableMange",
+        "component": "treeTableMange/index",
+        "query": null,
+        "isFrame": "1",
+        "isCache": "0",
+        "menuType": "C",
+        "visible": "1",
+        "status": "0",
+        "perms": "",
+        "icon": "form",
+        "children": [],
+        "tenantName": null,
+        "tenantId": null
+    },
+    {
+        "menuId": 11089,
+        "menuName": "IOT平台",
+        "parentId": 0,
+        "orderNum": 2,
+        "path": "report",
+        "component": null,
+        "query": null,
+        "isFrame": 1,
+        "isCache": 0,
+        "menuType": "M",
+        "visible": "0",
+        "status": "0",
+        "perms": "",
+        "icon": "bpmn-icon-data-store",
+        "createBy": "huaxian",
+        "createTime": "2024-8-9 13:58:08",
+        "updateBy": "huaxian",
+        "updateTime": "2025-1-1 17:02:58",
+        "remark": ""
+    },
+    {
+        "menuId": 11161,
+        "menuName": "设备数据采集",
+        "parentId": 11089,
+        "orderNum": 7,
+        "path": "http://124.71.18.189:85/#/integratedMonitoring/security/IOT/equipmentState?login",
+        "component": "http://124.71.18.189:85/#/integratedMonitoring/security/IOT/equipmentState?login",
+        "query": null,
+        "isFrame": 0,
+        "isCache": 0,
+        "menuType": "C",
+        "visible": "0",
+        "status": "0",
+        "perms": "",
+        "icon": "a-051_diannao",
+        "createBy": "huaxian",
+        "createTime": "2024-8-9 14:18:05",
+        "updateBy": "huaxian",
+        "updateTime": "2025-1-1 17:03:12",
+        "remark": ""
+    },
+    {
+        "menuId": 11162,
+        "menuName": "设备数据大屏",
+        "parentId": 11089,
+        "orderNum": 7,
+        "path": "http://124.71.18.189:84/#/index",
+        "component": "http://124.71.18.189:84/#/index",
+        "query": null,
+        "isFrame": 0,
+        "isCache": 0,
+        "menuType": "C",
+        "visible": "0",
+        "status": "0",
+        "perms": "",
+        "icon": "a-051_diannao",
+        "createBy": "huaxian",
+        "createTime": "2024-8-9 14:18:05",
+        "updateBy": "huaxian",
+        "updateTime": "2025-1-1 17:03:21",
+        "remark": ""
+    }
 ]

+ 5 - 2
zkqy-admin/src/main/resources/sql/initialize_sys_tenant_menu_translate_map_all.json

@@ -415,5 +415,8 @@
   "树形表格编辑": "Tree Table Edit",
   "导航条配置": "Navigation Bar Config",
   "移动端建模页": "Mobile Modeling Page",
-  "管道节点": "Pipeline Node"
-}
+  "管道节点": "Pipeline Node",
+  "默认菜单": "Default menu",
+  "行业管理":"Industry management",
+  "节点管理":"Node management"
+}

+ 91 - 0
zkqy-common/src/main/java/com/zkqy/common/core/domain/dto/DataSourceDTO.java

@@ -0,0 +1,91 @@
+package com.zkqy.common.core.domain.dto;
+
+public class DataSourceDTO {
+    private String host;
+    private Integer port;
+    private String database;
+    private String username;
+    private String password;
+    private String type; // mysql、postgresql等
+
+    //管理员名称
+    private String adminName;
+
+    //废弃字段
+    private String clintId;
+
+    //应用id
+    private  String clientId;
+
+    public String getClientId() {
+        return clientId;
+    }
+
+    public void setClientId(String clientId) {
+        this.clientId = clientId;
+    }
+
+    public String getClintId() {
+        return clintId;
+    }
+
+    public void setClintId(String clintId) {
+        this.clintId = clintId;
+    }
+
+    public String getAdminName() {
+        return adminName;
+    }
+
+    public void setAdminName(String adminName) {
+        this.adminName = adminName;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public Integer getPort() {
+        return port;
+    }
+
+    public void setPort(Integer port) {
+        this.port = port;
+    }
+
+    public String getDatabase() {
+        return database;
+    }
+
+    public void setDatabase(String database) {
+        this.database = database;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+}

+ 13 - 0
zkqy-common/src/main/java/com/zkqy/common/core/domain/entity/SysTenant.java

@@ -55,6 +55,19 @@ public class SysTenant extends BaseEntity {
     @Excel(name = "是否删除(0:未删除,1已删除)")
     private String isDel;
 
+    /**
+     * 机器码信息
+     */
+    private String  machineCode;
+
+    public String getMachineCode() {
+        return machineCode;
+    }
+
+    public void setMachineCode(String machineCode) {
+        this.machineCode = machineCode;
+    }
+
     /**
      * 数据源id
      */

+ 115 - 0
zkqy-common/src/main/java/com/zkqy/common/utils/MachineCodeUtil.java

@@ -0,0 +1,115 @@
+package com.zkqy.common.utils;
+
+import java.net.NetworkInterface;
+import java.security.MessageDigest;
+import java.util.Enumeration;
+import java.util.Scanner;
+
+public class MachineCodeUtil {
+
+    /**
+     * 机器码盐值
+     */
+    private static final String SALT = "zkqy2024";  // 修改为更安全的盐值
+
+    /**
+     * 获取当前机器的机器码
+     */
+    public static String getMachineCode() {
+        try {
+            // 获取网卡信息
+            StringBuilder sb = new StringBuilder();
+            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
+
+            while (networkInterfaces.hasMoreElements()) {
+                NetworkInterface ni = networkInterfaces.nextElement();
+                byte[] hardwareAddress = ni.getHardwareAddress();
+
+                // 排除虚拟网卡等
+                if (hardwareAddress != null && hardwareAddress.length > 0
+                        && !ni.isVirtual() && !ni.isLoopback()) {
+                    // 获取MAC地址
+                    for (byte b : hardwareAddress) {
+                        sb.append(String.format("%02X", b));
+                    }
+                }
+            }
+
+            // 获取CPU序列号
+            String cpuId = getCPUSerial();
+            if (StringUtils.isNotEmpty(cpuId)) {
+                sb.append(cpuId);
+            }
+
+            // 获取主板序列号
+            String motherboardId = getMotherboardSerial();
+            if (StringUtils.isNotEmpty(motherboardId)) {
+                sb.append(motherboardId);
+            }
+
+            // 添加盐值并使用MD5加密生成最终的机器码
+            String rawMachineCode = sb.toString() + SALT;
+            return getMD5(rawMachineCode);
+
+        } catch (Exception e) {
+            throw new RuntimeException("获取机器码失败", e);
+        }
+    }
+
+    /**
+     * 获取CPU序列号
+     */
+    private static String getCPUSerial() {
+        try {
+            Process process = Runtime.getRuntime().exec(
+                    new String[] { "wmic", "cpu", "get", "ProcessorId" });
+            process.getOutputStream().close();
+            Scanner sc = new Scanner(process.getInputStream());
+            String property = sc.next();
+            String serial = sc.next();
+            return serial;
+        } catch (Exception e) {
+            return "";
+        }
+    }
+
+    /**
+     * 获取主板序列号
+     */
+    private static String getMotherboardSerial() {
+        try {
+            Process process = Runtime.getRuntime().exec(
+                    new String[] { "wmic", "baseboard", "get", "SerialNumber" });
+            process.getOutputStream().close();
+            Scanner sc = new Scanner(process.getInputStream());
+            String property = sc.next();
+            String serial = sc.next();
+            return serial;
+        } catch (Exception e) {
+            return "";
+        }
+    }
+
+    /**
+     * MD5加密
+     */
+    private static String getMD5(String input) {
+        try {
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            byte[] messageDigest = md.digest(input.getBytes());
+            StringBuilder hexString = new StringBuilder();
+
+            for (byte b : messageDigest) {
+                String hex = Integer.toHexString(0xFF & b);
+                if (hex.length() == 1) {
+                    hexString.append('0');
+                }
+                hexString.append(hex);
+            }
+
+            return hexString.toString();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}

+ 22 - 0
zkqy-common/src/main/java/com/zkqy/common/utils/db/DataSourceUtil.java

@@ -0,0 +1,22 @@
+package com.zkqy.common.utils.db;
+
+import com.zkqy.common.core.domain.dto.DataSourceDTO;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+
+public class DataSourceUtil {
+    public static boolean testConnection(DataSourceDTO dto) {
+        String url = "";
+        if ("mysql".equalsIgnoreCase(dto.getType())) {
+            url = "jdbc:mysql://" + dto.getHost() + ":" + dto.getPort() + "/" + dto.getDatabase() + "?useSSL=false&serverTimezone=UTC";
+        }
+        // 可扩展其他数据库类型
+        try (Connection conn = DriverManager.getConnection(url, dto.getUsername(), dto.getPassword())) {
+            return conn != null && !conn.isClosed();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+}

+ 12 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/SysTenantMenu.java

@@ -13,6 +13,18 @@ public class SysTenantMenu {
     /** 菜单ID */
     private Long menuId;
 
+    /** 应用id **/
+    private String appId;
+
+
+    public String getAppId() {
+        return appId;
+    }
+
+    public void setAppId(String appId) {
+        this.appId = appId;
+    }
+
     public Long getTenantId() {
         return tenantId;
     }

+ 126 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/ZkqyApplicationCategories.java

@@ -0,0 +1,126 @@
+package com.zkqy.system.domain;
+
+import com.zkqy.common.annotation.Excel;
+import com.zkqy.common.core.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+
+/**
+ * 应用分类信息对象 zkqy_application_categories
+ *
+ * @author hmc
+ * @date 2025-06-11
+ */
+public class ZkqyApplicationCategories extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 主键iD */
+    private Long id;
+
+    /** 应用分类名称 */
+    @Excel(name = "应用分类名称")
+    private String categoryName;
+
+    /** 备注 */
+    @Excel(name = "备注")
+    private String description;
+
+    /** 排序 */
+    @Excel(name = "排序")
+    private Long sortOrder;
+
+    /** 是否删除(0:未删除,2已删除) */
+    @Excel(name = "是否删除(0:未删除,2已删除)")
+    private String isDel;
+
+    /** 创建人id */
+    @Excel(name = "创建人id")
+    private Long createById;
+
+    /** 更新者id */
+    @Excel(name = "更新者id")
+    private Long updateById;
+
+    public void setId(Long id)
+    {
+        this.id = id;
+    }
+
+    public Long getId()
+    {
+        return id;
+    }
+    public void setCategoryName(String categoryName)
+    {
+        this.categoryName = categoryName;
+    }
+
+    public String getCategoryName()
+    {
+        return categoryName;
+    }
+    public void setDescription(String description)
+    {
+        this.description = description;
+    }
+
+    public String getDescription()
+    {
+        return description;
+    }
+    public void setSortOrder(Long sortOrder)
+    {
+        this.sortOrder = sortOrder;
+    }
+
+    public Long getSortOrder()
+    {
+        return sortOrder;
+    }
+    public void setIsDel(String isDel)
+    {
+        this.isDel = isDel;
+    }
+
+    public String getIsDel()
+    {
+        return isDel;
+    }
+    public void setCreateById(Long createById)
+    {
+        this.createById = createById;
+    }
+
+    public Long getCreateById()
+    {
+        return createById;
+    }
+    public void setUpdateById(Long updateById)
+    {
+        this.updateById = updateById;
+    }
+
+    public Long getUpdateById()
+    {
+        return updateById;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("id", getId())
+            .append("categoryName", getCategoryName())
+            .append("description", getDescription())
+            .append("sortOrder", getSortOrder())
+            .append("isDel", getIsDel())
+            .append("createBy", getCreateBy())
+            .append("createById", getCreateById())
+            .append("createTime", getCreateTime())
+            .append("updateBy", getUpdateBy())
+            .append("updateById", getUpdateById())
+            .append("updateTime", getUpdateTime())
+            .toString();
+    }
+}

+ 210 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/ZkqyApplicationDataSources.java

@@ -0,0 +1,210 @@
+package com.zkqy.system.domain;
+
+import com.zkqy.common.annotation.Excel;
+import com.zkqy.common.core.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+
+/**
+ * 应用数据源信息对象 zkqy_application_data_sources
+ *
+ * @author hmc
+ * @date 2025-06-11
+ */
+public class ZkqyApplicationDataSources extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 主键id */
+    private Long id;
+
+    /** 应用程序id */
+    @Excel(name = "应用程序id")
+    private Long appId;
+
+    /** 数据源类型 */
+    @Excel(name = "数据源类型")
+    private String dsType;
+
+    /** 链接名称 */
+    @Excel(name = "链接名称")
+    private String connectionName;
+
+    /** 地址 */
+    @Excel(name = "地址")
+    private String host;
+
+    /** 端口 */
+    @Excel(name = "端口")
+    private Long port;
+
+    /** 数据库名称 */
+    @Excel(name = "数据库名称")
+    private String databaseName;
+
+    /** 用户名 */
+    @Excel(name = "用户名")
+    private String username;
+
+    /** 密码 */
+    @Excel(name = "密码")
+    private String password;
+
+    /** 备注 */
+    @Excel(name = "备注")
+    private String notes;
+
+    /** 是否删除(0:未删除,2已删除) */
+    @Excel(name = "是否删除(0:未删除,2已删除)")
+    private String isDel;
+
+    /** 创建人id */
+    @Excel(name = "创建人id")
+    private Long createById;
+
+    /** 更新者id */
+    @Excel(name = "更新者id")
+    private Long updateById;
+
+    public void setId(Long id)
+    {
+        this.id = id;
+    }
+
+    public Long getId()
+    {
+        return id;
+    }
+    public void setAppId(Long appId)
+    {
+        this.appId = appId;
+    }
+
+    public Long getAppId()
+    {
+        return appId;
+    }
+    public void setDsType(String dsType)
+    {
+        this.dsType = dsType;
+    }
+
+    public String getDsType()
+    {
+        return dsType;
+    }
+    public void setConnectionName(String connectionName)
+    {
+        this.connectionName = connectionName;
+    }
+
+    public String getConnectionName()
+    {
+        return connectionName;
+    }
+    public void setHost(String host)
+    {
+        this.host = host;
+    }
+
+    public String getHost()
+    {
+        return host;
+    }
+    public void setPort(Long port)
+    {
+        this.port = port;
+    }
+
+    public Long getPort()
+    {
+        return port;
+    }
+    public void setDatabaseName(String databaseName)
+    {
+        this.databaseName = databaseName;
+    }
+
+    public String getDatabaseName()
+    {
+        return databaseName;
+    }
+    public void setUsername(String username)
+    {
+        this.username = username;
+    }
+
+    public String getUsername()
+    {
+        return username;
+    }
+    public void setPassword(String password)
+    {
+        this.password = password;
+    }
+
+    public String getPassword()
+    {
+        return password;
+    }
+    public void setNotes(String notes)
+    {
+        this.notes = notes;
+    }
+
+    public String getNotes()
+    {
+        return notes;
+    }
+    public void setIsDel(String isDel)
+    {
+        this.isDel = isDel;
+    }
+
+    public String getIsDel()
+    {
+        return isDel;
+    }
+    public void setCreateById(Long createById)
+    {
+        this.createById = createById;
+    }
+
+    public Long getCreateById()
+    {
+        return createById;
+    }
+    public void setUpdateById(Long updateById)
+    {
+        this.updateById = updateById;
+    }
+
+    public Long getUpdateById()
+    {
+        return updateById;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("id", getId())
+            .append("appId", getAppId())
+            .append("dsType", getDsType())
+            .append("connectionName", getConnectionName())
+            .append("host", getHost())
+            .append("port", getPort())
+            .append("databaseName", getDatabaseName())
+            .append("username", getUsername())
+            .append("password", getPassword())
+            .append("notes", getNotes())
+            .append("isDel", getIsDel())
+            .append("createBy", getCreateBy())
+            .append("createById", getCreateById())
+            .append("createTime", getCreateTime())
+            .append("updateBy", getUpdateBy())
+            .append("updateById", getUpdateById())
+            .append("updateTime", getUpdateTime())
+            .toString();
+    }
+}

+ 178 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/ZkqyApplications.java

@@ -0,0 +1,178 @@
+package com.zkqy.system.domain;
+
+import com.zkqy.common.annotation.Excel;
+import com.zkqy.common.core.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+
+/**
+ * 租户应用对象 zkqy_applications
+ *
+ * @author hmc
+ * @date 2025-06-11
+ */
+public class ZkqyApplications extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    private String keyword;
+
+    public String getKeyword() {
+        return keyword;
+    }
+
+    public void setKeyword(String keyword) {
+        this.keyword = keyword;
+    }
+
+    /** 主键id */
+    private Long id;
+
+    /** 应用名称 */
+    @Excel(name = "应用名称")
+    private String appName;
+
+    /** 分类 */
+    @Excel(name = "分类")
+    private String category;
+
+    /** 图标地址 */
+    @Excel(name = "图标地址")
+    private String iconUrl;
+
+    /** 图标颜色 */
+    @Excel(name = "图标颜色")
+    private String iconColor;
+
+    /** 描述 */
+    @Excel(name = "描述")
+    private String description;
+
+    /** 租户id */
+    @Excel(name = "租户id")
+    private Long tenantId;
+
+    /** 是否删除(0:未删除,2已删除) */
+    @Excel(name = "是否删除(0:未删除,2已删除)")
+    private String isDel;
+
+    /** 创建人id */
+    @Excel(name = "创建人id")
+    private Long createById;
+
+    /** 更新者id */
+    @Excel(name = "更新者id")
+    private Long updateById;
+
+    public void setId(Long id)
+    {
+        this.id = id;
+    }
+
+    public Long getId()
+    {
+        return id;
+    }
+    public void setAppName(String appName)
+    {
+        this.appName = appName;
+    }
+
+    public String getAppName()
+    {
+        return appName;
+    }
+    public void setCategory(String category)
+    {
+        this.category = category;
+    }
+
+    public String getCategory()
+    {
+        return category;
+    }
+    public void setIconUrl(String iconUrl)
+    {
+        this.iconUrl = iconUrl;
+    }
+
+    public String getIconUrl()
+    {
+        return iconUrl;
+    }
+    public void setIconColor(String iconColor)
+    {
+        this.iconColor = iconColor;
+    }
+
+    public String getIconColor()
+    {
+        return iconColor;
+    }
+    public void setDescription(String description)
+    {
+        this.description = description;
+    }
+
+    public String getDescription()
+    {
+        return description;
+    }
+    public void setTenantId(Long tenantId)
+    {
+        this.tenantId = tenantId;
+    }
+
+    public Long getTenantId()
+    {
+        return tenantId;
+    }
+    public void setIsDel(String isDel)
+    {
+        this.isDel = isDel;
+    }
+
+    public String getIsDel()
+    {
+        return isDel;
+    }
+    public void setCreateById(Long createById)
+    {
+        this.createById = createById;
+    }
+
+    public Long getCreateById()
+    {
+        return createById;
+    }
+    public void setUpdateById(Long updateById)
+    {
+        this.updateById = updateById;
+    }
+
+    public Long getUpdateById()
+    {
+        return updateById;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("id", getId())
+            .append("appName", getAppName())
+            .append("category", getCategory())
+            .append("iconUrl", getIconUrl())
+            .append("iconColor", getIconColor())
+            .append("description", getDescription())
+            .append("tenantId", getTenantId())
+            .append("isDel", getIsDel())
+            .append("createBy", getCreateBy())
+            .append("createById", getCreateById())
+            .append("createTime", getCreateTime())
+            .append("updateBy", getUpdateBy())
+            .append("updateById", getUpdateById())
+            .append("updateTime", getUpdateTime())
+            .toString();
+    }
+}

+ 29 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/vo/CloningVo.java

@@ -0,0 +1,29 @@
+package com.zkqy.system.domain.vo;
+
+public class CloningVo {
+    /**
+     * 当前租户id
+     */
+    private String currentTenantId;
+
+    /**
+     * 被克隆的租户id
+     */
+    private String clonedTenantId;
+
+    public String getCurrentTenantId() {
+        return currentTenantId;
+    }
+
+    public void setCurrentTenantId(String currentTenantId) {
+        this.currentTenantId = currentTenantId;
+    }
+
+    public String getClonedTenantId() {
+        return clonedTenantId;
+    }
+
+    public void setClonedTenantId(String clonedTenantId) {
+        this.clonedTenantId = clonedTenantId;
+    }
+}

+ 7 - 0
zkqy-system/src/main/java/com/zkqy/system/domain/vo/SysMenuTenantVo.java

@@ -0,0 +1,7 @@
+package com.zkqy.system.domain.vo;
+
+import com.zkqy.common.core.domain.entity.SysMenu;
+
+public class SysMenuTenantVo extends SysMenu {
+
+}

+ 21 - 0
zkqy-system/src/main/java/com/zkqy/system/mapper/SysMenuMapper.java

@@ -11,6 +11,26 @@ import com.zkqy.common.core.domain.entity.SysMenu;
  * @author zkqy
  */
 public interface SysMenuMapper {
+
+
+
+    /**
+     * 查询当前租户的菜单信息列表
+     *
+     * @param tenantId 租户id
+     * @return 菜单列表
+     */
+    public List<SysMenu> selectMenuListByTenantId(String tenantId);
+
+    /**
+     * 查询当前租户的菜单信息列表
+     *
+     * @param tenantId 租户id
+     * @return 菜单列表
+     */
+    public int deleteMenuListByTenantId(String tenantId);
+
+
     /**
      * 查询系统菜单列表
      *
@@ -19,6 +39,7 @@ public interface SysMenuMapper {
      */
     public List<SysMenu> selectMenuList(SysMenu menu);
 
+
     /**
      * 查询租户菜单列表
      *

+ 7 - 0
zkqy-system/src/main/java/com/zkqy/system/mapper/SysTenantMenuMapper.java

@@ -17,4 +17,11 @@ public interface SysTenantMenuMapper {
      */
     int executeSqlFromFile(@Param("sql") String sql);
 
+
+    /**
+     * 删除租户和菜单的关联关系表
+     * @param currentTenantId
+     * @return
+     */
+    int deleteSysTenantMenu(@Param("currentTenantId") String currentTenantId);
 }

+ 61 - 0
zkqy-system/src/main/java/com/zkqy/system/mapper/ZkqyApplicationCategoriesMapper.java

@@ -0,0 +1,61 @@
+package com.zkqy.system.mapper;
+
+import java.util.List;
+import com.zkqy.system.domain.ZkqyApplicationCategories;
+
+/**
+ * 应用分类信息Mapper接口
+ * 
+ * @author hmc
+ * @date 2025-06-11
+ */
+public interface ZkqyApplicationCategoriesMapper 
+{
+    /**
+     * 查询应用分类信息
+     * 
+     * @param id 应用分类信息主键
+     * @return 应用分类信息
+     */
+    public ZkqyApplicationCategories selectZkqyApplicationCategoriesById(Long id);
+
+    /**
+     * 查询应用分类信息列表
+     * 
+     * @param zkqyApplicationCategories 应用分类信息
+     * @return 应用分类信息集合
+     */
+    public List<ZkqyApplicationCategories> selectZkqyApplicationCategoriesList(ZkqyApplicationCategories zkqyApplicationCategories);
+
+    /**
+     * 新增应用分类信息
+     * 
+     * @param zkqyApplicationCategories 应用分类信息
+     * @return 结果
+     */
+    public int insertZkqyApplicationCategories(ZkqyApplicationCategories zkqyApplicationCategories);
+
+    /**
+     * 修改应用分类信息
+     * 
+     * @param zkqyApplicationCategories 应用分类信息
+     * @return 结果
+     */
+    public int updateZkqyApplicationCategories(ZkqyApplicationCategories zkqyApplicationCategories);
+
+    /**
+     * 删除应用分类信息
+     * 
+     * @param id 应用分类信息主键
+     * @return 结果
+     */
+    public int deleteZkqyApplicationCategoriesById(Long id);
+
+    /**
+     * 批量删除应用分类信息
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteZkqyApplicationCategoriesByIds(Long[] ids);
+}

+ 61 - 0
zkqy-system/src/main/java/com/zkqy/system/mapper/ZkqyApplicationDataSourcesMapper.java

@@ -0,0 +1,61 @@
+package com.zkqy.system.mapper;
+
+import java.util.List;
+import com.zkqy.system.domain.ZkqyApplicationDataSources;
+
+/**
+ * 应用数据源信息Mapper接口
+ * 
+ * @author hmc
+ * @date 2025-06-11
+ */
+public interface ZkqyApplicationDataSourcesMapper 
+{
+    /**
+     * 查询应用数据源信息
+     * 
+     * @param id 应用数据源信息主键
+     * @return 应用数据源信息
+     */
+    public ZkqyApplicationDataSources selectZkqyApplicationDataSourcesById(Long id);
+
+    /**
+     * 查询应用数据源信息列表
+     * 
+     * @param zkqyApplicationDataSources 应用数据源信息
+     * @return 应用数据源信息集合
+     */
+    public List<ZkqyApplicationDataSources> selectZkqyApplicationDataSourcesList(ZkqyApplicationDataSources zkqyApplicationDataSources);
+
+    /**
+     * 新增应用数据源信息
+     * 
+     * @param zkqyApplicationDataSources 应用数据源信息
+     * @return 结果
+     */
+    public int insertZkqyApplicationDataSources(ZkqyApplicationDataSources zkqyApplicationDataSources);
+
+    /**
+     * 修改应用数据源信息
+     * 
+     * @param zkqyApplicationDataSources 应用数据源信息
+     * @return 结果
+     */
+    public int updateZkqyApplicationDataSources(ZkqyApplicationDataSources zkqyApplicationDataSources);
+
+    /**
+     * 删除应用数据源信息
+     * 
+     * @param id 应用数据源信息主键
+     * @return 结果
+     */
+    public int deleteZkqyApplicationDataSourcesById(Long id);
+
+    /**
+     * 批量删除应用数据源信息
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteZkqyApplicationDataSourcesByIds(Long[] ids);
+}

+ 61 - 0
zkqy-system/src/main/java/com/zkqy/system/mapper/ZkqyApplicationsMapper.java

@@ -0,0 +1,61 @@
+package com.zkqy.system.mapper;
+
+import java.util.List;
+import com.zkqy.system.domain.ZkqyApplications;
+
+/**
+ * 租户应用Mapper接口
+ * 
+ * @author hmc
+ * @date 2025-06-11
+ */
+public interface ZkqyApplicationsMapper 
+{
+    /**
+     * 查询租户应用
+     * 
+     * @param id 租户应用主键
+     * @return 租户应用
+     */
+    public ZkqyApplications selectZkqyApplicationsById(Long id);
+
+    /**
+     * 查询租户应用列表
+     * 
+     * @param zkqyApplications 租户应用
+     * @return 租户应用集合
+     */
+    public List<ZkqyApplications> selectZkqyApplicationsList(ZkqyApplications zkqyApplications);
+
+    /**
+     * 新增租户应用
+     * 
+     * @param zkqyApplications 租户应用
+     * @return 结果
+     */
+    public int insertZkqyApplications(ZkqyApplications zkqyApplications);
+
+    /**
+     * 修改租户应用
+     * 
+     * @param zkqyApplications 租户应用
+     * @return 结果
+     */
+    public int updateZkqyApplications(ZkqyApplications zkqyApplications);
+
+    /**
+     * 删除租户应用
+     * 
+     * @param id 租户应用主键
+     * @return 结果
+     */
+    public int deleteZkqyApplicationsById(Long id);
+
+    /**
+     * 批量删除租户应用
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteZkqyApplicationsByIds(Long[] ids);
+}

+ 8 - 0
zkqy-system/src/main/java/com/zkqy/system/service/ISysTenantService.java

@@ -5,6 +5,7 @@ import java.util.Map;
 
 import com.zkqy.common.core.domain.AjaxResult;
 import com.zkqy.common.core.domain.entity.SysTenant;
+import com.zkqy.system.domain.vo.CloningVo;
 
 /**
  * 租户信息Service接口
@@ -113,4 +114,11 @@ public interface ISysTenantService {
      * @return
      */
     List<Map<String, Object>> selectCodeTenantAllList();
+
+    /**
+     * 克隆租户信息
+     * @param tenantId
+     * @return
+     */
+    AjaxResult cloning(CloningVo tenantId);
 }

+ 25 - 23
zkqy-system/src/main/java/com/zkqy/system/service/ISysUserService.java

@@ -6,14 +6,14 @@ import org.apache.ibatis.annotations.Param;
 
 /**
  * 用户 业务层
- * 
+ *
  * @author zkqy
  */
 public interface ISysUserService
 {
     /**
      * 根据条件分页查询用户列表
-     * 
+     *
      * @param user 用户信息
      * @return 用户信息集合信息
      */
@@ -21,7 +21,7 @@ public interface ISysUserService
 
     /**
      * 根据条件分页查询已分配用户角色列表
-     * 
+     *
      * @param user 用户信息
      * @return 用户信息集合信息
      */
@@ -29,7 +29,7 @@ public interface ISysUserService
 
     /**
      * 根据条件分页查询未分配用户角色列表
-     * 
+     *
      * @param user 用户信息
      * @return 用户信息集合信息
      */
@@ -37,7 +37,7 @@ public interface ISysUserService
 
     /**
      * 通过用户名查询用户
-     * 
+     *
      * @param userName 用户名
      * @return 用户对象信息
      */
@@ -53,7 +53,7 @@ public interface ISysUserService
 
     /**
      * 通过用户ID查询用户
-     * 
+     *
      * @param userId 用户ID
      * @return 用户对象信息
      */
@@ -61,7 +61,7 @@ public interface ISysUserService
 
     /**
      * 根据用户ID查询用户所属角色组
-     * 
+     *
      * @param userName 用户名
      * @return 结果
      */
@@ -69,7 +69,7 @@ public interface ISysUserService
 
     /**
      * 根据用户ID查询用户所属岗位组
-     * 
+     *
      * @param userName 用户名
      * @return 结果
      */
@@ -77,7 +77,7 @@ public interface ISysUserService
 
     /**
      * 校验用户名称是否唯一
-     * 
+     *
      * @param user 用户信息
      * @return 结果
      */
@@ -109,21 +109,21 @@ public interface ISysUserService
 
     /**
      * 校验用户是否允许操作
-     * 
+     *
      * @param user 用户信息
      */
     public void checkUserAllowed(SysUser user);
 
     /**
      * 校验用户是否有数据权限
-     * 
+     *
      * @param userId 用户id
      */
     public void checkUserDataScope(Long userId);
 
     /**
      * 新增用户信息
-     * 
+     *
      * @param user 用户信息
      * @return 结果
      */
@@ -131,7 +131,7 @@ public interface ISysUserService
 
     /**
      * 注册用户信息
-     * 
+     *
      * @param user 用户信息
      * @return 结果
      */
@@ -139,7 +139,7 @@ public interface ISysUserService
 
     /**
      * 修改用户信息
-     * 
+     *
      * @param user 用户信息
      * @return 结果
      */
@@ -147,7 +147,7 @@ public interface ISysUserService
 
     /**
      * 用户授权角色
-     * 
+     *
      * @param userId 用户ID
      * @param roleIds 角色组
      */
@@ -155,7 +155,7 @@ public interface ISysUserService
 
     /**
      * 修改用户状态
-     * 
+     *
      * @param user 用户信息
      * @return 结果
      */
@@ -163,7 +163,7 @@ public interface ISysUserService
 
     /**
      * 修改用户基本信息
-     * 
+     *
      * @param user 用户信息
      * @return 结果
      */
@@ -171,7 +171,7 @@ public interface ISysUserService
 
     /**
      * 修改用户头像
-     * 
+     *
      * @param userName 用户名
      * @param avatar 头像地址
      * @return 结果
@@ -180,7 +180,7 @@ public interface ISysUserService
 
     /**
      * 重置用户密码
-     * 
+     *
      * @param user 用户信息
      * @return 结果
      */
@@ -188,7 +188,7 @@ public interface ISysUserService
 
     /**
      * 重置用户密码
-     * 
+     *
      * @param userName 用户名
      * @param password 密码
      * @return 结果
@@ -197,7 +197,7 @@ public interface ISysUserService
 
     /**
      * 通过用户ID删除用户
-     * 
+     *
      * @param userId 用户ID
      * @return 结果
      */
@@ -205,7 +205,7 @@ public interface ISysUserService
 
     /**
      * 批量删除用户信息
-     * 
+     *
      * @param userIds 需要删除的用户ID
      * @return 结果
      */
@@ -213,7 +213,7 @@ public interface ISysUserService
 
     /**
      * 导入用户数据
-     * 
+     *
      * @param userList 用户数据列表
      * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据
      * @param operName 操作用户
@@ -248,4 +248,6 @@ public interface ISysUserService
      * @return 用户对象信息
      */
     public SysUser selectUserInfoByTenantCode(String tenantCode, String userName);
+
+    boolean selectUserByTenantIdAndUserName(SysUser sysUser);
 }

+ 61 - 0
zkqy-system/src/main/java/com/zkqy/system/service/IZkqyApplicationCategoriesService.java

@@ -0,0 +1,61 @@
+package com.zkqy.system.service;
+
+import java.util.List;
+import com.zkqy.system.domain.ZkqyApplicationCategories;
+
+/**
+ * 应用分类信息Service接口
+ * 
+ * @author hmc
+ * @date 2025-06-11
+ */
+public interface IZkqyApplicationCategoriesService 
+{
+    /**
+     * 查询应用分类信息
+     * 
+     * @param id 应用分类信息主键
+     * @return 应用分类信息
+     */
+    public ZkqyApplicationCategories selectZkqyApplicationCategoriesById(Long id);
+
+    /**
+     * 查询应用分类信息列表
+     * 
+     * @param zkqyApplicationCategories 应用分类信息
+     * @return 应用分类信息集合
+     */
+    public List<ZkqyApplicationCategories> selectZkqyApplicationCategoriesList(ZkqyApplicationCategories zkqyApplicationCategories);
+
+    /**
+     * 新增应用分类信息
+     * 
+     * @param zkqyApplicationCategories 应用分类信息
+     * @return 结果
+     */
+    public int insertZkqyApplicationCategories(ZkqyApplicationCategories zkqyApplicationCategories);
+
+    /**
+     * 修改应用分类信息
+     * 
+     * @param zkqyApplicationCategories 应用分类信息
+     * @return 结果
+     */
+    public int updateZkqyApplicationCategories(ZkqyApplicationCategories zkqyApplicationCategories);
+
+    /**
+     * 批量删除应用分类信息
+     * 
+     * @param ids 需要删除的应用分类信息主键集合
+     * @return 结果
+     */
+    public int deleteZkqyApplicationCategoriesByIds(Long[] ids);
+
+    /**
+     * 删除应用分类信息信息
+     * 
+     * @param id 应用分类信息主键
+     * @return 结果
+     */
+    public int deleteZkqyApplicationCategoriesById(Long id);
+}

+ 61 - 0
zkqy-system/src/main/java/com/zkqy/system/service/IZkqyApplicationDataSourcesService.java

@@ -0,0 +1,61 @@
+package com.zkqy.system.service;
+
+import java.util.List;
+import com.zkqy.system.domain.ZkqyApplicationDataSources;
+
+/**
+ * 应用数据源信息Service接口
+ * 
+ * @author hmc
+ * @date 2025-06-11
+ */
+public interface IZkqyApplicationDataSourcesService 
+{
+    /**
+     * 查询应用数据源信息
+     * 
+     * @param id 应用数据源信息主键
+     * @return 应用数据源信息
+     */
+    public ZkqyApplicationDataSources selectZkqyApplicationDataSourcesById(Long id);
+
+    /**
+     * 查询应用数据源信息列表
+     * 
+     * @param zkqyApplicationDataSources 应用数据源信息
+     * @return 应用数据源信息集合
+     */
+    public List<ZkqyApplicationDataSources> selectZkqyApplicationDataSourcesList(ZkqyApplicationDataSources zkqyApplicationDataSources);
+
+    /**
+     * 新增应用数据源信息
+     * 
+     * @param zkqyApplicationDataSources 应用数据源信息
+     * @return 结果
+     */
+    public int insertZkqyApplicationDataSources(ZkqyApplicationDataSources zkqyApplicationDataSources);
+
+    /**
+     * 修改应用数据源信息
+     * 
+     * @param zkqyApplicationDataSources 应用数据源信息
+     * @return 结果
+     */
+    public int updateZkqyApplicationDataSources(ZkqyApplicationDataSources zkqyApplicationDataSources);
+
+    /**
+     * 批量删除应用数据源信息
+     * 
+     * @param ids 需要删除的应用数据源信息主键集合
+     * @return 结果
+     */
+    public int deleteZkqyApplicationDataSourcesByIds(Long[] ids);
+
+    /**
+     * 删除应用数据源信息信息
+     * 
+     * @param id 应用数据源信息主键
+     * @return 结果
+     */
+    public int deleteZkqyApplicationDataSourcesById(Long id);
+}

+ 84 - 0
zkqy-system/src/main/java/com/zkqy/system/service/IZkqyApplicationsService.java

@@ -0,0 +1,84 @@
+package com.zkqy.system.service;
+
+import java.util.List;
+
+import com.zkqy.common.core.domain.AjaxResult;
+import com.zkqy.common.core.domain.dto.DataSourceDTO;
+import com.zkqy.system.domain.ZkqyApplicationCategories;
+import com.zkqy.system.domain.ZkqyApplications;
+
+/**
+ * 租户应用Service接口
+ *
+ * @author hmc
+ * @date 2025-06-11
+ */
+public interface IZkqyApplicationsService
+{
+    /**
+     * 查询租户应用
+     *
+     * @param id 租户应用主键
+     * @return 租户应用
+     */
+    public ZkqyApplications selectZkqyApplicationsById(Long id);
+
+    /**
+     * 查询租户应用列表
+     *
+     * @param zkqyApplications 租户应用
+     * @return 租户应用集合
+     */
+    public List<ZkqyApplications> selectZkqyApplicationsList(ZkqyApplications zkqyApplications);
+
+    /**
+     * 新增租户应用
+     *
+     * @param zkqyApplications 租户应用
+     * @return 结果
+     */
+    public int insertZkqyApplications(ZkqyApplications zkqyApplications);
+
+    /**
+     * 修改租户应用
+     *
+     * @param zkqyApplications 租户应用
+     * @return 结果
+     */
+    public int updateZkqyApplications(ZkqyApplications zkqyApplications);
+
+    /**
+     * 批量删除租户应用
+     *
+     * @param ids 需要删除的租户应用主键集合
+     * @return 结果
+     */
+    public int deleteZkqyApplicationsByIds(Long[] ids);
+
+    /**
+     * 删除租户应用信息
+     *
+     * @param id 租户应用主键
+     * @return 结果
+     */
+    public int deleteZkqyApplicationsById(Long id);
+
+    /**
+     * 当前租户下所有的应用分类
+     * @param zkqyApplications
+     * @return
+     */
+    List<ZkqyApplicationCategories> allClassification(ZkqyApplications zkqyApplications);
+
+    /**
+     * 测试数据连接是否通过
+     */
+    boolean  isDatabaseConnectionPass(DataSourceDTO dataSourceDTO);
+
+    /**
+     * 初始化数据源
+     * @param dto
+     * @return
+     */
+    AjaxResult applicationInitDataSource(DataSourceDTO dto);
+}

+ 45 - 3
zkqy-system/src/main/java/com/zkqy/system/service/impl/SysTenantServiceImpl.java

@@ -26,6 +26,7 @@ import com.zkqy.common.utils.StringUtils;
 import com.zkqy.common.utils.ip.IpUtils;
 import com.zkqy.system.domain.SysActivationCodeLog;
 import com.zkqy.system.domain.SysTenantMenu;
+import com.zkqy.system.domain.vo.CloningVo;
 import com.zkqy.system.mapper.*;
 import com.zkqy.system.service.ISysUserService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -208,10 +209,10 @@ public class SysTenantServiceImpl implements ISysTenantService {
             //这个东西在部署环境下会出现问题
 //            Resource resource = resourceLoader.getResource("classpath:sql/initialize_sys_tenant_menu.json");
 //            2025/04/29=把这个歌静态数据给做成租户端的管理菜单了
-            Resource resource = new ClassPathResource("sql/fjqycaidanxixni.json");
+//            Resource resource = new ClassPathResource("sql/fjqycaidanxixni.json");
             //获取租户默认菜单信息
-           List<SysMenu> menus = objectMapper.readValue(resource.getInputStream(), objectMapper.getTypeFactory().constructCollectionType(List.class, SysMenu.class));
-//            List<SysMenu> menus = sysMenuDefaultMapper.selectMenuList();
+//           List<SysMenu> menus = objectMapper.readValue(resource.getInputStream(), objectMapper.getTypeFactory().constructCollectionType(List.class, SysMenu.class));
+            List<SysMenu> menus = sysMenuDefaultMapper.selectMenuList();
             //字符串备用方案
             //List<SysMenu> menus = JSON.parseObject(ass, new TypeReference<List<SysMenu>>() {});
             //int a=10/0;
@@ -393,6 +394,47 @@ public class SysTenantServiceImpl implements ISysTenantService {
         return maps;
     }
 
+    /**
+     * 克隆租户信息
+     * @param cloningVo
+     * @return
+     */
+    @Override
+    public AjaxResult cloning(CloningVo cloningVo) {
+        try {
+            List<SysMenu> list;
+
+            //租户id-one
+            String currentTenantId = cloningVo.getCurrentTenantId();//当前租户id
+            //租户id-two
+            String clonedTenantId = cloningVo.getClonedTenantId();//被克隆租户信息
+
+            //删除租户菜单信息
+            int i1 = sysMenuMapper.deleteMenuListByTenantId(currentTenantId);
+            //删除菜单租户的绑定关系
+            int i2= sysTenantMenuMapper.deleteSysTenantMenu(currentTenantId);
+
+            //查询被拷贝租户的菜单信息
+            List<SysMenu> sysMenus = sysMenuMapper.selectMenuListByTenantId(clonedTenantId);
+
+            // 调用初始化租户信息
+            list = sysMenus.stream().filter(menu -> 0L == menu.getParentId()).peek(
+                    //设置子节点信息
+                    menu -> menu.setChildren(getChildrenList(menu, sysMenus))
+            ).collect(Collectors.toList());
+
+            // 循环遍历数据新增
+            for (int i = 0; i < list.size(); i++) {
+                printTree(list.get(i), 0L, Long.valueOf(currentTenantId));
+            }
+        } catch (NumberFormatException e) {
+            throw new RuntimeException(e);
+        }
+
+        return AjaxResult.success("克隆成功");
+    }
+
+
     public List<Map<String, Object>> buildTree(List<SysTenant> sysTenants) {
         // 构造一个Map,方便根据tenantId查找SysTenant
         Map<Long, SysTenant> tenantMap = sysTenants.stream()

+ 15 - 0
zkqy-system/src/main/java/com/zkqy/system/service/impl/SysUserServiceImpl.java

@@ -551,4 +551,19 @@ public class SysUserServiceImpl implements ISysUserService {
     public SysUser selectUserInfoByTenantCode(String tenantCode, String userName) {
         return userMapper.selectUserInfoByTenantCode(tenantCode, userName);
     }
+
+    /**
+     * 根据用户名和租户id查询当前租户下有没有相同名称的租户
+     * @param sysUser
+     * @return
+     */
+    @Override
+    public boolean selectUserByTenantIdAndUserName(SysUser sysUser) {
+        SysUser sysUser1 = userMapper.selectUserByTenantInfo(sysUser.getTenantId().toString(), sysUser.getUserName());
+        if(null==sysUser1){
+            return true;
+        }else {
+            return false;
+        }
+    }
 }

+ 97 - 0
zkqy-system/src/main/java/com/zkqy/system/service/impl/ZkqyApplicationCategoriesServiceImpl.java

@@ -0,0 +1,97 @@
+package com.zkqy.system.service.impl;
+
+import java.util.List;
+
+import com.zkqy.common.utils.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.zkqy.system.mapper.ZkqyApplicationCategoriesMapper;
+import com.zkqy.system.domain.ZkqyApplicationCategories;
+import com.zkqy.system.service.IZkqyApplicationCategoriesService;
+
+/**
+ * 应用分类信息Service业务层处理
+ *
+ * @author hmc
+ * @date 2025-06-11
+ */
+@Service
+public class ZkqyApplicationCategoriesServiceImpl implements IZkqyApplicationCategoriesService
+{
+    @Autowired
+    private ZkqyApplicationCategoriesMapper zkqyApplicationCategoriesMapper;
+
+    /**
+     * 查询应用分类信息
+     *
+     * @param id 应用分类信息主键
+     * @return 应用分类信息
+     */
+    @Override
+    public ZkqyApplicationCategories selectZkqyApplicationCategoriesById(Long id)
+    {
+        return zkqyApplicationCategoriesMapper.selectZkqyApplicationCategoriesById(id);
+    }
+
+    /**
+     * 查询应用分类信息列表
+     *
+     * @param zkqyApplicationCategories 应用分类信息
+     * @return 应用分类信息
+     */
+    @Override
+    public List<ZkqyApplicationCategories> selectZkqyApplicationCategoriesList(ZkqyApplicationCategories zkqyApplicationCategories)
+    {
+        return zkqyApplicationCategoriesMapper.selectZkqyApplicationCategoriesList(zkqyApplicationCategories);
+    }
+
+    /**
+     * 新增应用分类信息
+     *
+     * @param zkqyApplicationCategories 应用分类信息
+     * @return 结果
+     */
+    @Override
+    public int insertZkqyApplicationCategories(ZkqyApplicationCategories zkqyApplicationCategories)
+    {
+        zkqyApplicationCategories.setCreateTime(DateUtils.getNowDate());
+        return zkqyApplicationCategoriesMapper.insertZkqyApplicationCategories(zkqyApplicationCategories);
+    }
+
+    /**
+     * 修改应用分类信息
+     *
+     * @param zkqyApplicationCategories 应用分类信息
+     * @return 结果
+     */
+    @Override
+    public int updateZkqyApplicationCategories(ZkqyApplicationCategories zkqyApplicationCategories)
+    {
+        zkqyApplicationCategories.setUpdateTime(DateUtils.getNowDate());
+        return zkqyApplicationCategoriesMapper.updateZkqyApplicationCategories(zkqyApplicationCategories);
+    }
+
+    /**
+     * 批量删除应用分类信息
+     *
+     * @param ids 需要删除的应用分类信息主键
+     * @return 结果
+     */
+    @Override
+    public int deleteZkqyApplicationCategoriesByIds(Long[] ids)
+    {
+        return zkqyApplicationCategoriesMapper.deleteZkqyApplicationCategoriesByIds(ids);
+    }
+
+    /**
+     * 删除应用分类信息信息
+     *
+     * @param id 应用分类信息主键
+     * @return 结果
+     */
+    @Override
+    public int deleteZkqyApplicationCategoriesById(Long id)
+    {
+        return zkqyApplicationCategoriesMapper.deleteZkqyApplicationCategoriesById(id);
+    }
+}

+ 97 - 0
zkqy-system/src/main/java/com/zkqy/system/service/impl/ZkqyApplicationDataSourcesServiceImpl.java

@@ -0,0 +1,97 @@
+package com.zkqy.system.service.impl;
+
+import java.util.List;
+
+import com.zkqy.common.utils.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.zkqy.system.mapper.ZkqyApplicationDataSourcesMapper;
+import com.zkqy.system.domain.ZkqyApplicationDataSources;
+import com.zkqy.system.service.IZkqyApplicationDataSourcesService;
+
+/**
+ * 应用数据源信息Service业务层处理
+ *
+ * @author hmc
+ * @date 2025-06-11
+ */
+@Service
+public class ZkqyApplicationDataSourcesServiceImpl implements IZkqyApplicationDataSourcesService
+{
+    @Autowired
+    private ZkqyApplicationDataSourcesMapper zkqyApplicationDataSourcesMapper;
+
+    /**
+     * 查询应用数据源信息
+     *
+     * @param id 应用数据源信息主键
+     * @return 应用数据源信息
+     */
+    @Override
+    public ZkqyApplicationDataSources selectZkqyApplicationDataSourcesById(Long id)
+    {
+        return zkqyApplicationDataSourcesMapper.selectZkqyApplicationDataSourcesById(id);
+    }
+
+    /**
+     * 查询应用数据源信息列表
+     *
+     * @param zkqyApplicationDataSources 应用数据源信息
+     * @return 应用数据源信息
+     */
+    @Override
+    public List<ZkqyApplicationDataSources> selectZkqyApplicationDataSourcesList(ZkqyApplicationDataSources zkqyApplicationDataSources)
+    {
+        return zkqyApplicationDataSourcesMapper.selectZkqyApplicationDataSourcesList(zkqyApplicationDataSources);
+    }
+
+    /**
+     * 新增应用数据源信息
+     *
+     * @param zkqyApplicationDataSources 应用数据源信息
+     * @return 结果
+     */
+    @Override
+    public int insertZkqyApplicationDataSources(ZkqyApplicationDataSources zkqyApplicationDataSources)
+    {
+        zkqyApplicationDataSources.setCreateTime(DateUtils.getNowDate());
+        return zkqyApplicationDataSourcesMapper.insertZkqyApplicationDataSources(zkqyApplicationDataSources);
+    }
+
+    /**
+     * 修改应用数据源信息
+     *
+     * @param zkqyApplicationDataSources 应用数据源信息
+     * @return 结果
+     */
+    @Override
+    public int updateZkqyApplicationDataSources(ZkqyApplicationDataSources zkqyApplicationDataSources)
+    {
+        zkqyApplicationDataSources.setUpdateTime(DateUtils.getNowDate());
+        return zkqyApplicationDataSourcesMapper.updateZkqyApplicationDataSources(zkqyApplicationDataSources);
+    }
+
+    /**
+     * 批量删除应用数据源信息
+     *
+     * @param ids 需要删除的应用数据源信息主键
+     * @return 结果
+     */
+    @Override
+    public int deleteZkqyApplicationDataSourcesByIds(Long[] ids)
+    {
+        return zkqyApplicationDataSourcesMapper.deleteZkqyApplicationDataSourcesByIds(ids);
+    }
+
+    /**
+     * 删除应用数据源信息信息
+     *
+     * @param id 应用数据源信息主键
+     * @return 结果
+     */
+    @Override
+    public int deleteZkqyApplicationDataSourcesById(Long id)
+    {
+        return zkqyApplicationDataSourcesMapper.deleteZkqyApplicationDataSourcesById(id);
+    }
+}

+ 313 - 0
zkqy-system/src/main/java/com/zkqy/system/service/impl/ZkqyApplicationsServiceImpl.java

@@ -0,0 +1,313 @@
+package com.zkqy.system.service.impl;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import com.zkqy.common.constant.HttpStatus;
+import com.zkqy.common.core.domain.AjaxResult;
+import com.zkqy.common.core.domain.dto.DataSourceDTO;
+import com.zkqy.common.core.domain.entity.DataSource;
+import com.zkqy.common.core.domain.entity.SysMenu;
+import com.zkqy.common.core.domain.entity.SysTenant;
+import com.zkqy.common.core.domain.entity.SysUser;
+import com.zkqy.common.utils.DateUtils;
+import com.zkqy.common.utils.SecurityUtils;
+import com.zkqy.common.utils.StringUtils;
+import com.zkqy.common.utils.db.DataSourceUtil;
+import com.zkqy.system.domain.SysTenantMenu;
+import com.zkqy.system.domain.ZkqyApplicationCategories;
+import com.zkqy.system.domain.ZkqyApplicationDataSources;
+import com.zkqy.system.mapper.*;
+import com.zkqy.system.service.IZkqyApplicationDataSourcesService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import com.zkqy.system.domain.ZkqyApplications;
+import com.zkqy.system.service.IZkqyApplicationsService;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * 租户应用Service业务层处理
+ *
+ * @author hmc
+ * @date 2025-06-11
+ */
+@Service
+public class ZkqyApplicationsServiceImpl implements IZkqyApplicationsService
+{
+    @Autowired
+    private ZkqyApplicationsMapper zkqyApplicationsMapper;
+
+    @Autowired
+    private ZkqyApplicationCategoriesMapper zkqyApplicationCategoriesMapper;
+
+
+
+    @Autowired
+    private SysTenantMenuMapper sysTenantMenuMapper;
+
+    @Autowired
+    private SysUserServiceImpl sysUserService;
+
+    @Autowired
+    private IZkqyApplicationDataSourcesService zkqyApplicationDataSourcesService;
+
+    RestTemplate restTemplate = new RestTemplate();
+
+    @Value("${parameter.ip.DATA_ENGINE_INITDATABASE_IP}")
+    public String DATA_ENGINE_INITDATABASE_IP;
+
+    @Autowired
+    SysTenantServiceImpl sysTenantService;
+
+    @Autowired
+    private SysMenuDefaultMapper sysMenuDefaultMapper;
+
+    @Autowired
+    private SysMenuMapper sysMenuMapper;
+
+    /**
+     * 查询租户应用
+     *
+     * @param id 租户应用主键
+     * @return 租户应用
+     */
+    @Override
+    public ZkqyApplications selectZkqyApplicationsById(Long id)
+    {
+        return zkqyApplicationsMapper.selectZkqyApplicationsById(id);
+    }
+
+    /**
+     * 查询租户应用列表
+     *
+     * @param zkqyApplications 租户应用
+     * @return 租户应用
+     */
+    @Override
+    public List<ZkqyApplications> selectZkqyApplicationsList(ZkqyApplications zkqyApplications)
+    {
+        return zkqyApplicationsMapper.selectZkqyApplicationsList(zkqyApplications);
+    }
+
+    /**
+     * 新增租户应用
+     *
+     * @param zkqyApplications 租户应用
+     * @return 结果
+     */
+    @Override
+    public int insertZkqyApplications(ZkqyApplications zkqyApplications)
+    {
+        zkqyApplications.setCreateTime(DateUtils.getNowDate());
+        zkqyApplications.setTenantId(SecurityUtils.getTenantId());//租户id
+        return zkqyApplicationsMapper.insertZkqyApplications(zkqyApplications);
+    }
+
+    /**
+     * 修改租户应用
+     *
+     * @param zkqyApplications 租户应用
+     * @return 结果
+     */
+    @Override
+    public int updateZkqyApplications(ZkqyApplications zkqyApplications)
+    {
+        zkqyApplications.setUpdateTime(DateUtils.getNowDate());
+        return zkqyApplicationsMapper.updateZkqyApplications(zkqyApplications);
+    }
+
+    /**
+     * 批量删除租户应用
+     *
+     * @param ids 需要删除的租户应用主键
+     * @return 结果
+     */
+    @Override
+    public int deleteZkqyApplicationsByIds(Long[] ids)
+    {
+        return zkqyApplicationsMapper.deleteZkqyApplicationsByIds(ids);
+    }
+
+    /**
+     * 删除租户应用信息
+     *
+     * @param id 租户应用主键
+     * @return 结果
+     */
+    @Override
+    public int deleteZkqyApplicationsById(Long id)
+    {
+        return zkqyApplicationsMapper.deleteZkqyApplicationsById(id);
+    }
+
+    @Override
+    public List<ZkqyApplicationCategories> allClassification(ZkqyApplications zkqyApplications) {
+        //得到当前租户id
+        Long tenantId = SecurityUtils.getTenantId();
+        zkqyApplications.setTenantId(tenantId);
+
+        //查询所有应用信息
+        List<ZkqyApplications> zkqyApplications1 = zkqyApplicationsMapper.selectZkqyApplicationsList(zkqyApplications);
+        List<String> collect = zkqyApplications1.stream().map(ZkqyApplications::getCategory).collect(Collectors.toList());
+        //根据应用分类信息-构建查询工具栏
+        //查询所有分类信息
+        ZkqyApplicationCategories zkqyApplicationCategories=new ZkqyApplicationCategories();
+        List<ZkqyApplicationCategories> zkqyApplicationCategories1 = zkqyApplicationCategoriesMapper.selectZkqyApplicationCategoriesList(zkqyApplicationCategories);
+        //收集包含的分类信息
+        List<ZkqyApplicationCategories> collect1 = zkqyApplicationCategories1.stream().filter(item -> collect.contains(item.getId().toString())).collect(Collectors.toList());
+
+//        ZkqyApplicationCategories zkqyApplicationCategories2=new ZkqyApplicationCategories();
+//        zkqyApplicationCategories2.setId(0L);
+//        zkqyApplicationCategories2.setCategoryName("全部");
+//
+//        List<ZkqyApplicationCategories> zkqyApplicationCategoriesAll=new ArrayList<>();
+//        zkqyApplicationCategoriesAll.add(zkqyApplicationCategories2);
+//        zkqyApplicationCategoriesAll.addAll(collect1);
+        return collect1;
+    }
+
+    @Override
+    public boolean isDatabaseConnectionPass(DataSourceDTO dataSourceDTO) {
+        boolean result = DataSourceUtil.testConnection(dataSourceDTO);
+        if (result) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    @Transactional
+    public AjaxResult applicationInitDataSource(DataSourceDTO dto) {
+        //检查用户是否当前设置的用户是否存在--查询的是当前租户下的用户信息
+        Long tenantId = SecurityUtils.getTenantId();//租户id
+        //需要初始化的用户信息
+        String adminName = dto.getAdminName();
+        SysUser sysUser=new SysUser();
+        sysUser.setTenantId(tenantId);
+        sysUser.setUserName(adminName);
+        boolean b = sysUserService.selectUserByTenantIdAndUserName(sysUser);
+        if(!b){
+            return AjaxResult.error("当前用户已经存在");
+        }
+        //数据库验重(需要否) 定义规则租户应用库名称规则租户Code+应用id+当前年月日+db
+        //http://192.168.110.83:8099/dataSource/querySelectDataSource?databaseName=
+        //  zkqy-call&databaseIp=192.168.110.83&username=root&password=root&portNumber=3306&databaseType=mysql --返回的是当前数据库连接下的所有用户信息
+
+        //基础信息查询
+        SysTenant sysTenant = sysTenantService.selectSysTenantByTenantId(tenantId);
+        LocalDateTime currentDate = LocalDateTime.now();
+        // 定义格式:yyyyMMddHHmm
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmm");
+        // 格式化为字符串
+        String formattedDate = currentDate.format(formatter);
+
+        System.out.println(formattedDate);
+
+        //构造初始化数据库信息方法
+        DataSource dataSource=new DataSource();
+        dataSource.setDatabaseName(sysTenant.getTenantCode()+dto.getClintId()+formattedDate);
+        dataSource.setDatabaseType(dto.getType());
+        dataSource.setPassword(dto.getPassword());
+        dataSource.setDatabaseIp(dto.getHost());
+        dataSource.setPortNumber(Long.valueOf(dto.getPort()));
+        dataSource.setUsername(dto.getUsername());
+        //调用数据引擎服务初始化数据库接口
+        ResponseEntity<AjaxResult> dataSourceResponseEntity = restTemplate.postForEntity(DATA_ENGINE_INITDATABASE_IP, dataSource, AjaxResult.class);
+        //获取请求状态码
+        int code = (int)dataSourceResponseEntity.getBody().get("code");
+        String msg = (String) dataSourceResponseEntity.getBody().get("msg");
+        if(code == HttpStatus.WARN){
+            return AjaxResult.warn(msg);
+        }
+        //保存应用程序的数据源信息
+        ZkqyApplicationDataSources zkqyApplicationDataSources=new ZkqyApplicationDataSources();
+        zkqyApplicationDataSources.setAppId(Long.valueOf(dto.getClientId()));//应用id
+        zkqyApplicationDataSources.setDatabaseName(dto.getDatabase());// 数据库名称
+        zkqyApplicationDataSources.setConnectionName(dto.getHost());//host
+        zkqyApplicationDataSources.setHost(dto.getHost());//host路径
+        zkqyApplicationDataSources.setPort(dto.getPort().longValue());//端口
+        zkqyApplicationDataSources.setUsername(dto.getUsername());//userName
+        zkqyApplicationDataSources.setPassword(dto.getPassword());//密码
+        int i = zkqyApplicationDataSourcesService.insertZkqyApplicationDataSources(zkqyApplicationDataSources);
+
+        //保存用户信息{"userName":"hmcdb1234","nickName":"11","userType":"01","tenantId":226,"password":"123456"}
+
+        //http://175.27.169.173:1024/prod-api/system/user  post
+        SysUser user=new SysUser();
+        String username = SecurityUtils.getUsername();
+        user.setUserType("01");
+        user.setCreateBy(username);
+        user.setUserName(dto.getAdminName()); //用户名
+        user.setNickName(dto.getAdminName());//用昵称
+        user.setPassword(SecurityUtils.encryptPassword("123456"));
+        //二级租户新建租户用户的时候用,自己选择的租户 否则会出现A租户创建b租户,b租户创建的同时创建b用户,但是这个b用户指向的租户是A
+//        if(StringUtils.isNotBlank(user.getTenantId().toString())){
+//            user.setTenantId(user.getTenantId());
+//        }
+        // 确保 tenantId 不为 null 才设置
+        Long tenantIdd = SecurityUtils.getTenantId().longValue();
+        if (null!=tenantIdd) {
+            user.setTenantId(tenantId);
+        }
+        //保存用户信息
+        sysUserService.insertUser(user);
+
+        //初始化用户应用菜单
+        //http://175.27.169.173:1024/prod-api/system/tenant/initTenantMenuData/226 GET
+        List<SysMenu> list = new ArrayList<>();
+        try {
+            List<SysMenu> menus = sysMenuDefaultMapper.selectMenuList();
+            //筛选出根节点
+            list = menus.stream().filter(menu -> 0L == menu.getParentId()).peek(
+                    //设置子节点信息
+                    menu -> menu.setChildren(getChildrenList(menu, menus))
+            ).collect(Collectors.toList());
+            //循环遍历数据新增
+            for (int t = 0; t < list.size(); t++) {
+                printTree(list.get(t), 0L, tenantId,dto.getClintId());
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return AjaxResult.success("初始化数据应用成功");
+    }
+
+    //获取子节点信息
+    private List<SysMenu> getChildrenList(SysMenu root, List<SysMenu> menus) {
+        List<SysMenu> list = menus.stream().filter(menu ->
+                //筛选出下一节点元素
+                Objects.equals(menu.getParentId(), root.getMenuId())).map(menu -> {
+            //递归set子节点
+            menu.setChildren(this.getChildrenList(menu, menus));
+            return menu;
+        }).collect(Collectors.toList());
+        return list;
+    }
+
+    //新增菜单
+    public void printTree(SysMenu root, Long parentId, Long tenantId,String apiId) {
+        if (root == null) return;
+        //add菜单
+        root.setMenuId(null);
+        root.setParentId(parentId);
+        sysMenuMapper.insertMenu(root);
+        //add租户菜单关联表
+        SysTenantMenu sysTenantMenu = new SysTenantMenu();
+        sysTenantMenu.setTenantId(tenantId);
+        sysTenantMenu.setMenuId(root.getMenuId());
+        sysTenantMenu.setAppId(apiId);
+        sysTenantMenuMapper.insertSysTenantMenu(sysTenantMenu);
+        for (SysMenu child : root.getChildren()) {  // 遍历子节点
+            printTree(child, root.getMenuId(), tenantId,apiId);  // 递归调用
+        }
+    }
+}

+ 29 - 0
zkqy-system/src/main/resources/mapper/system/SysMenuMapper.xml

@@ -460,6 +460,7 @@
         </foreach>
     </delete>
 
+
     <select id="getMenuList" resultMap="SysMenuResult">
         SELECT m.menu_id,
                m.menu_name,
@@ -534,6 +535,34 @@
         LIMIT 1
     </select>
 
+    <!--删除当前租户的菜单信息-->
+    <delete id="deleteMenuListByTenantId">
+        DELETE from sys_menu where menu_id in (select menu_id from sys_tenant_menu where tenant_id='216')
+    </delete>
+
+
+    <!--查询查询当前租户下所有的菜单信息-->
+    <select id="selectMenuListByTenantId" resultMap="SysMenuResult">
+        SELECT m.menu_id,
+               m.menu_name,
+               m.parent_id,
+               m.order_num,
+               m.path,
+               m.component,
+               m.`query`,
+               m.is_frame,
+               m.is_cache,
+               m.menu_type,
+               m.visible,
+               m.STATUS,
+               ifnull(m.perms, '') AS perms,
+               m.icon,
+               m.create_time
+        FROM sys_menu m
+                 join sys_tenant_menu tm on m.menu_id = tm.menu_id
+        where m.menu_type != 'F' and m.STATUS = '0' and tm.tenant_id = #{tenantId}
+    </select>
+
 
     <insert id="insertMenuBatch" parameterType="java.util.List">
         INSERT INTO sys_menu (

+ 8 - 3
zkqy-system/src/main/resources/mapper/system/SysTenantMapper.xml

@@ -22,6 +22,7 @@
         <result property="tenantWechatIndexUrl" column="tenant_wechat_index_url"/>
         <result property="tenantGrade" column="tenant_grade"/>
         <result property="tenantProfession" column="tenant_profession"/>
+        <result property="machineCode" column="machine_code"/>
     </resultMap>
 
     <sql id="selectSysTenantVo">
@@ -41,7 +42,8 @@
                tenant_client_login_url,
                tenant_wechat_index_url,
                tenant_grade,
-               tenant_profession
+               tenant_profession,
+               machine_code
         from sys_tenant
         where is_del = '0'
     </sql>
@@ -100,6 +102,7 @@
             <if test="tenantWechatIndexUrl !=null">tenant_wechat_index_url,</if>
             <if test="tenantGrade != null">tenant_grade,</if>
             <if test="tenantProfession!=null">tenant_profession,</if>
+            <if test="machineCode!=null">machine_code,</if>
             is_del
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
@@ -117,6 +120,7 @@
             <if test="tenantWechatIndexUrl !=null">#{tenantWechatIndexUrl},</if>
             <if test="tenantGrade != null"> #{tenantGrade},</if>
             <if test="tenantProfession!=null">#{tenantProfession},</if>
+            <if test="machineCode!=null">#{machineCode},</if>
             '0'
         </trim>
     </insert>
@@ -138,7 +142,8 @@
             <if test="tenantClientLoginUrl !=null">tenant_client_login_url = #{tenantClientLoginUrl},</if>
             <if test="tenantWechatIndexUrl !=null">tenant_wechat_index_url = #{tenantWechatIndexUrl},</if>
             <if test="tenantGrade != null">tenant_grade = #{tenantGrade},</if>
-            <if test="tenantProfession!=null">tenant_profession=#{tenantProfession}</if>
+            <if test="tenantProfession!=null">tenant_profession=#{tenantProfession},</if>
+            <if test="machineCode!=null">machine_code=#{machineCode}</if>
         </trim>
         where tenant_id = #{tenantId}
     </update>
@@ -165,4 +170,4 @@
         <include refid="selectSysTenantVo"/>
     </select>
 
-</mapper>
+</mapper>

+ 11 - 1
zkqy-system/src/main/resources/mapper/system/SysTenantMenuMapper.xml

@@ -5,12 +5,22 @@
 <mapper namespace="com.zkqy.system.mapper.SysTenantMenuMapper">
 
     <insert id="insertSysTenantMenu" parameterType="SysTenantMenu">
-        insert into sys_tenant_menu(tenant_id ,menu_id )
+        insert into sys_tenant_menu(tenant_id ,menu_id
+            <if test="appId!=null and appId!=''">
+                ,app_id
+            </if>)
         values(#{tenantId}, #{menuId})
+        <if test="appId!=null and appId!=''">
+            ,#{appId}
+        </if>
     </insert>
 
     <insert id="executeSqlFromFile">
         ${sql}
     </insert>
 
+    <delete id="deleteSysTenantMenu"  parameterType="String">
+        DELETE from sys_tenant_menu where tenant_id=#{currentTenantId}
+    </delete>
+
 </mapper>

+ 2 - 1
zkqy-system/src/main/resources/mapper/system/SysUserMapper.xml

@@ -268,6 +268,7 @@
           and del_flag = '0' limit 1
     </select>
 
+
     <select id="checkPhoneUnique" parameterType="String" resultMap="SysUserResult">
         select user_id, phonenumber
         from sys_user
@@ -401,4 +402,4 @@
         where te.tenant_code = #{tenantCode} and u.user_name = #{userName} and u.del_flag = '0'
     </select>
 
-</mapper> 
+</mapper>

+ 97 - 0
zkqy-system/src/main/resources/mapper/system/ZkqyApplicationCategoriesMapper.xml

@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zkqy.system.mapper.ZkqyApplicationCategoriesMapper">
+
+    <resultMap type="com.zkqy.system.domain.ZkqyApplicationCategories" id="ZkqyApplicationCategoriesResult">
+        <result property="id"    column="id"    />
+        <result property="categoryName"    column="category_name"    />
+        <result property="description"    column="description"    />
+        <result property="sortOrder"    column="sort_order"    />
+        <result property="isDel"    column="is_del"    />
+        <result property="createBy"    column="create_by"    />
+        <result property="createById"    column="create_by_id"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateBy"    column="update_by"    />
+        <result property="updateById"    column="update_by_id"    />
+        <result property="updateTime"    column="update_time"    />
+    </resultMap>
+
+    <sql id="selectZkqyApplicationCategoriesVo">
+        select id, category_name, description, sort_order, is_del, create_by, create_by_id, create_time, update_by, update_by_id, update_time from zkqy_application_categories
+    </sql>
+
+    <select id="selectZkqyApplicationCategoriesList" parameterType="com.zkqy.system.domain.ZkqyApplicationCategories" resultMap="ZkqyApplicationCategoriesResult">
+        <include refid="selectZkqyApplicationCategoriesVo"/>
+        <where>
+            <if test="categoryName != null  and categoryName != ''"> and category_name like concat('%', #{categoryName}, '%')</if>
+            <if test="description != null  and description != ''"> and description = #{description}</if>
+            <if test="sortOrder != null "> and sort_order = #{sortOrder}</if>
+            <if test="isDel != null  and isDel != ''"> and is_del = #{isDel}</if>
+            <if test="createById != null "> and create_by_id = #{createById}</if>
+            <if test="updateById != null "> and update_by_id = #{updateById}</if>
+        </where>
+    </select>
+
+    <select id="selectZkqyApplicationCategoriesById" parameterType="Long" resultMap="ZkqyApplicationCategoriesResult">
+        <include refid="selectZkqyApplicationCategoriesVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertZkqyApplicationCategories" parameterType="com.zkqy.system.domain.ZkqyApplicationCategories" useGeneratedKeys="true" keyProperty="id">
+        insert into zkqy_application_categories
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="categoryName != null and categoryName != ''">category_name,</if>
+            <if test="description != null">description,</if>
+            <if test="sortOrder != null">sort_order,</if>
+            <if test="isDel != null">is_del,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="createById != null">create_by_id,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="updateById != null">update_by_id,</if>
+            <if test="updateTime != null">update_time,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="categoryName != null and categoryName != ''">#{categoryName},</if>
+            <if test="description != null">#{description},</if>
+            <if test="sortOrder != null">#{sortOrder},</if>
+            <if test="isDel != null">#{isDel},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="createById != null">#{createById},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="updateById != null">#{updateById},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+         </trim>
+    </insert>
+
+    <update id="updateZkqyApplicationCategories" parameterType="com.zkqy.system.domain.ZkqyApplicationCategories">
+        update zkqy_application_categories
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="categoryName != null and categoryName != ''">category_name = #{categoryName},</if>
+            <if test="description != null">description = #{description},</if>
+            <if test="sortOrder != null">sort_order = #{sortOrder},</if>
+            <if test="isDel != null">is_del = #{isDel},</if>
+            <if test="createBy != null">create_by = #{createBy},</if>
+            <if test="createById != null">create_by_id = #{createById},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            <if test="updateById != null">update_by_id = #{updateById},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteZkqyApplicationCategoriesById" parameterType="Long">
+        delete from zkqy_application_categories where id = #{id}
+    </delete>
+
+    <delete id="deleteZkqyApplicationCategoriesByIds" parameterType="String">
+        delete from zkqy_application_categories where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

+ 127 - 0
zkqy-system/src/main/resources/mapper/system/ZkqyApplicationDataSourcesMapper.xml

@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zkqy.system.mapper.ZkqyApplicationDataSourcesMapper">
+
+    <resultMap type="com.zkqy.system.domain.ZkqyApplicationDataSources" id="ZkqyApplicationDataSourcesResult">
+        <result property="id"    column="id"    />
+        <result property="appId"    column="app_id"    />
+        <result property="dsType"    column="ds_type"    />
+        <result property="connectionName"    column="connection_name"    />
+        <result property="host"    column="host"    />
+        <result property="port"    column="port"    />
+        <result property="databaseName"    column="database_name"    />
+        <result property="username"    column="username"    />
+        <result property="password"    column="password"    />
+        <result property="notes"    column="notes"    />
+        <result property="isDel"    column="is_del"    />
+        <result property="createBy"    column="create_by"    />
+        <result property="createById"    column="create_by_id"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateBy"    column="update_by"    />
+        <result property="updateById"    column="update_by_id"    />
+        <result property="updateTime"    column="update_time"    />
+    </resultMap>
+
+    <sql id="selectZkqyApplicationDataSourcesVo">
+        select id, app_id, ds_type, connection_name, host, port, database_name, username, password, notes, is_del, create_by, create_by_id, create_time, update_by, update_by_id, update_time from zkqy_application_data_sources
+    </sql>
+
+    <select id="selectZkqyApplicationDataSourcesList" parameterType="com.zkqy.system.domain.ZkqyApplicationDataSources" resultMap="ZkqyApplicationDataSourcesResult">
+        <include refid="selectZkqyApplicationDataSourcesVo"/>
+        <where>
+            <if test="appId != null "> and app_id = #{appId}</if>
+            <if test="dsType != null  and dsType != ''"> and ds_type = #{dsType}</if>
+            <if test="connectionName != null  and connectionName != ''"> and connection_name like concat('%', #{connectionName}, '%')</if>
+            <if test="host != null  and host != ''"> and host = #{host}</if>
+            <if test="port != null "> and port = #{port}</if>
+            <if test="databaseName != null  and databaseName != ''"> and database_name like concat('%', #{databaseName}, '%')</if>
+            <if test="username != null  and username != ''"> and username like concat('%', #{username}, '%')</if>
+            <if test="password != null  and password != ''"> and password = #{password}</if>
+            <if test="notes != null  and notes != ''"> and notes = #{notes}</if>
+            <if test="isDel != null  and isDel != ''"> and is_del = #{isDel}</if>
+            <if test="createById != null "> and create_by_id = #{createById}</if>
+            <if test="updateById != null "> and update_by_id = #{updateById}</if>
+        </where>
+    </select>
+
+    <select id="selectZkqyApplicationDataSourcesById" parameterType="Long" resultMap="ZkqyApplicationDataSourcesResult">
+        <include refid="selectZkqyApplicationDataSourcesVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertZkqyApplicationDataSources" parameterType="com.zkqy.system.domain.ZkqyApplicationDataSources" useGeneratedKeys="true" keyProperty="id">
+        insert into zkqy_application_data_sources
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="appId != null">app_id,</if>
+            <if test="dsType != null and dsType != ''">ds_type,</if>
+            <if test="connectionName != null and connectionName != ''">connection_name,</if>
+            <if test="host != null">host,</if>
+            <if test="port != null">port,</if>
+            <if test="databaseName != null">database_name,</if>
+            <if test="username != null">username,</if>
+            <if test="password != null">password,</if>
+            <if test="notes != null">notes,</if>
+            <if test="isDel != null">is_del,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="createById != null">create_by_id,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="updateById != null">update_by_id,</if>
+            <if test="updateTime != null">update_time,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="appId != null">#{appId},</if>
+            <if test="dsType != null and dsType != ''">#{dsType},</if>
+            <if test="connectionName != null and connectionName != ''">#{connectionName},</if>
+            <if test="host != null">#{host},</if>
+            <if test="port != null">#{port},</if>
+            <if test="databaseName != null">#{databaseName},</if>
+            <if test="username != null">#{username},</if>
+            <if test="password != null">#{password},</if>
+            <if test="notes != null">#{notes},</if>
+            <if test="isDel != null">#{isDel},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="createById != null">#{createById},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="updateById != null">#{updateById},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+         </trim>
+    </insert>
+
+    <update id="updateZkqyApplicationDataSources" parameterType="com.zkqy.system.domain.ZkqyApplicationDataSources">
+        update zkqy_application_data_sources
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="appId != null">app_id = #{appId},</if>
+            <if test="dsType != null and dsType != ''">ds_type = #{dsType},</if>
+            <if test="connectionName != null and connectionName != ''">connection_name = #{connectionName},</if>
+            <if test="host != null">host = #{host},</if>
+            <if test="port != null">port = #{port},</if>
+            <if test="databaseName != null">database_name = #{databaseName},</if>
+            <if test="username != null">username = #{username},</if>
+            <if test="password != null">password = #{password},</if>
+            <if test="notes != null">notes = #{notes},</if>
+            <if test="isDel != null">is_del = #{isDel},</if>
+            <if test="createBy != null">create_by = #{createBy},</if>
+            <if test="createById != null">create_by_id = #{createById},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            <if test="updateById != null">update_by_id = #{updateById},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteZkqyApplicationDataSourcesById" parameterType="Long">
+        delete from zkqy_application_data_sources where id = #{id}
+    </delete>
+
+    <delete id="deleteZkqyApplicationDataSourcesByIds" parameterType="String">
+        delete from zkqy_application_data_sources where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

+ 112 - 0
zkqy-system/src/main/resources/mapper/system/ZkqyApplicationsMapper.xml

@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zkqy.system.mapper.ZkqyApplicationsMapper">
+
+    <resultMap type="com.zkqy.system.domain.ZkqyApplications" id="ZkqyApplicationsResult">
+        <result property="id"    column="id"    />
+        <result property="appName"    column="app_name"    />
+        <result property="category"    column="category"    />
+        <result property="iconUrl"    column="icon_url"    />
+        <result property="iconColor"    column="icon_color"    />
+        <result property="description"    column="description"    />
+        <result property="tenantId"    column="tenant_id"    />
+        <result property="isDel"    column="is_del"    />
+        <result property="createBy"    column="create_by"    />
+        <result property="createById"    column="create_by_id"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateBy"    column="update_by"    />
+        <result property="updateById"    column="update_by_id"    />
+        <result property="updateTime"    column="update_time"    />
+    </resultMap>
+
+    <sql id="selectZkqyApplicationsVo">
+        select id, app_name, category, icon_url, icon_color, description, tenant_id, is_del, create_by, create_by_id, create_time, update_by, update_by_id, update_time from zkqy_applications
+    </sql>
+
+    <select id="selectZkqyApplicationsList" parameterType="com.zkqy.system.domain.ZkqyApplications" resultMap="ZkqyApplicationsResult">
+        <include refid="selectZkqyApplicationsVo"/>
+        <where>
+            <if test="appName != null  and appName != ''"> and app_name like concat('%', #{appName}, '%')</if>
+            <if test="category != null  and category != ''"> and category = #{category}</if>
+            <if test="iconUrl != null  and iconUrl != ''"> and icon_url = #{iconUrl}</if>
+            <if test="iconColor != null  and iconColor != ''"> and icon_color = #{iconColor}</if>
+            <if test="description != null  and description != ''"> and description = #{description}</if>
+            <if test="tenantId != null "> and tenant_id = #{tenantId}</if>
+            <if test="isDel != null  and isDel != ''"> and is_del = #{isDel}</if>
+            <if test="createById != null "> and create_by_id = #{createById}</if>
+            <if test="updateById != null "> and update_by_id = #{updateById}</if>
+        </where>
+    </select>
+
+    <select id="selectZkqyApplicationsById" parameterType="Long" resultMap="ZkqyApplicationsResult">
+        <include refid="selectZkqyApplicationsVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertZkqyApplications" parameterType="com.zkqy.system.domain.ZkqyApplications" useGeneratedKeys="true" keyProperty="id">
+        insert into zkqy_applications
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="appName != null and appName != ''">app_name,</if>
+            <if test="category != null and category != ''">category,</if>
+            <if test="iconUrl != null">icon_url,</if>
+            <if test="iconColor != null">icon_color,</if>
+            <if test="description != null">description,</if>
+            <if test="tenantId != null">tenant_id,</if>
+            <if test="isDel != null">is_del,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="createById != null">create_by_id,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="updateById != null">update_by_id,</if>
+            <if test="updateTime != null">update_time,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="appName != null and appName != ''">#{appName},</if>
+            <if test="category != null and category != ''">#{category},</if>
+            <if test="iconUrl != null">#{iconUrl},</if>
+            <if test="iconColor != null">#{iconColor},</if>
+            <if test="description != null">#{description},</if>
+            <if test="tenantId != null">#{tenantId},</if>
+            <if test="isDel != null">#{isDel},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="createById != null">#{createById},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="updateById != null">#{updateById},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+         </trim>
+    </insert>
+
+    <update id="updateZkqyApplications" parameterType="com.zkqy.system.domain.ZkqyApplications">
+        update zkqy_applications
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="appName != null and appName != ''">app_name = #{appName},</if>
+            <if test="category != null and category != ''">category = #{category},</if>
+            <if test="iconUrl != null">icon_url = #{iconUrl},</if>
+            <if test="iconColor != null">icon_color = #{iconColor},</if>
+            <if test="description != null">description = #{description},</if>
+            <if test="tenantId != null">tenant_id = #{tenantId},</if>
+            <if test="isDel != null">is_del = #{isDel},</if>
+            <if test="createBy != null">create_by = #{createBy},</if>
+            <if test="createById != null">create_by_id = #{createById},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            <if test="updateById != null">update_by_id = #{updateById},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteZkqyApplicationsById" parameterType="Long">
+        delete from zkqy_applications where id = #{id}
+    </delete>
+
+    <delete id="deleteZkqyApplicationsByIds" parameterType="String">
+        delete from zkqy_applications where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

BIN
zkqy-ui/public/static/bb3845c295f66fc0e82f3874da4688e.jpg


+ 84 - 0
zkqy-ui/src/api/application/applications.js

@@ -0,0 +1,84 @@
+import request from '@/utils/request'
+
+// 查询租户应用列表
+export function listApplications(query) {
+  return request({
+    url: '/application/applications/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function allClassification(query) {
+  return request({
+    url: '/application/applications/allClassification',
+    method: 'get',
+    params: query
+  })
+}
+
+/**
+ * 测试数据库
+ * @param data
+ * @returns {AxiosPromise}
+ */
+export function testConnection(data) {
+    return request({
+      url: '/application/applications/testConnection',
+      method: 'post',
+      data: data
+    })
+}
+
+//查询数据源信息
+//http://localhost:1024/dev-api/system/dict/data/list?pageNum=1&pageSize=10&dictType=mysql_connection_information
+export function getDataSourceInformation(query) {
+  return request({
+    url: '/system/dict/data/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询租户应用详细
+export function getApplications(id) {
+  return request({
+    url: '/application/applications/' + id,
+    method: 'get'
+  })
+}
+
+// 新增租户应用
+export function addApplications(data) {
+  return request({
+    url: '/application/applications',
+    method: 'post',
+    data: data
+  })
+}
+
+//初始数据源和应用功能菜单
+export function applicationInitDataSource(data) {
+  return request({
+    url: '/application/applications/applicationInitDataSource',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改租户应用
+export function updateApplications(data) {
+  return request({
+    url: '/application/applications',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除租户应用
+export function delApplications(id) {
+  return request({
+    url: '/application/applications/' + id,
+    method: 'delete'
+  })
+}

+ 44 - 0
zkqy-ui/src/api/application/categories.js

@@ -0,0 +1,44 @@
+import request from '@/utils/request'
+
+// 查询应用分类信息列表
+export function listCategories(query) {
+  return request({
+    url: '/application/categories/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询应用分类信息详细
+export function getCategories(id) {
+  return request({
+    url: '/application/categories/' + id,
+    method: 'get'
+  })
+}
+
+// 新增应用分类信息
+export function addCategories(data) {
+  return request({
+    url: '/application/categories',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改应用分类信息
+export function updateCategories(data) {
+  return request({
+    url: '/application/categories',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除应用分类信息
+export function delCategories(id) {
+  return request({
+    url: '/application/categories/' + id,
+    method: 'delete'
+  })
+}

+ 44 - 0
zkqy-ui/src/api/application/sources.js

@@ -0,0 +1,44 @@
+import request from '@/utils/request'
+
+// 查询应用数据源信息列表
+export function listSources(query) {
+  return request({
+    url: '/application/sources/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询应用数据源信息详细
+export function getSources(id) {
+  return request({
+    url: '/application/sources/' + id,
+    method: 'get'
+  })
+}
+
+// 新增应用数据源信息
+export function addSources(data) {
+  return request({
+    url: '/application/sources',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改应用数据源信息
+export function updateSources(data) {
+  return request({
+    url: '/application/sources',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除应用数据源信息
+export function delSources(id) {
+  return request({
+    url: '/application/sources/' + id,
+    method: 'delete'
+  })
+}

+ 11 - 0
zkqy-ui/src/api/bpmprocess/process.js

@@ -175,6 +175,17 @@ export function getfromlist(data) {
   })
 }
 
+
+
+// 查询所有的业务脚本页面
+export function listBusinessFlowVuePage(query) {
+  return request({
+    url: '/system/SysBusinessFlowVuePage/list',
+    method: 'get',
+    params: query
+  })
+}
+
 // 查询所有的业务脚本
 export function listBusinessFlowScript(query) {
   return request({

+ 16 - 0
zkqy-ui/src/api/system/tenant.js

@@ -18,6 +18,14 @@ export function getTenantAllList(query) {
   })
 }
 
+//获取本地机器码信息
+export function getLocalMachineCode() {
+  return request({
+    url: '/system/tenant/create/generateMachineCode2',
+    method: 'get'
+  })
+}
+
 // 选项信息
 export function tenantTree() {
   return request({
@@ -135,3 +143,11 @@ export function commonUpload(data) {
     baseURL: process.env.VUE_APP_BASE_API
   })
 }
+export function cloning(data) {
+  return request({
+    url: `/system/tenant/cloning`,
+    method: 'post',
+    data: data,
+    baseURL: process.env.VUE_APP_BASE_API
+  })
+}

+ 107 - 0
zkqy-ui/src/components/AppCard/index.vue

@@ -0,0 +1,107 @@
+<template>
+  <div
+    class="app-card"
+    @click="handleAppClick"
+    @contextmenu.prevent="handleContextMenu"
+  >
+    <div class="app-icon" :style="{ background: app.iconColor }">
+      <i :class="app.iconUrl" />
+    </div>
+    <h3 class="app-title">{{ app.appName }}</h3>
+    <p class="app-desc">{{ app.description }}</p>
+    <div class="app-footer">
+      <span class="app-date">
+        <i class="el-icon-time" />
+        {{ app.createTime }}
+      </span>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'AppCard',
+  props: {
+    app: {
+      type: Object,
+      required: true
+    }
+  },
+  methods: {
+    handleAppClick() {
+      this.$emit('click', this.app)
+    },
+    handleContextMenu(event) {
+      this.$emit('contextmenu', event, this.app)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.app-card {
+  background: #fff;
+  border-radius: 8px;
+  padding: 20px;
+  cursor: pointer;
+  transition: all 0.3s;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+  position: relative;
+
+  &:hover {
+    transform: translateY(-5px);
+    box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.2);
+  }
+
+  .app-icon {
+    width: 60px;
+    height: 60px;
+    border-radius: 12px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-bottom: 16px;
+
+    i {
+      font-size: 32px;
+      color: #fff;
+    }
+  }
+
+  .app-title {
+    font-size: 16px;
+    font-weight: 600;
+    color: #303133;
+    margin: 0 0 8px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+
+  .app-desc {
+    font-size: 14px;
+    color: #606266;
+    margin: 0 0 16px;
+    height: 40px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: -webkit-box;
+    -webkit-line-clamp: 2;
+    -webkit-box-orient: vertical;
+  }
+
+  .app-footer {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    font-size: 12px;
+    color: #909399;
+
+    .app-date {
+      display: flex;
+      align-items: center;
+      gap: 4px;
+    }
+  }
+}
+</style>

+ 87 - 0
zkqy-ui/src/components/UserInfo/index.vue

@@ -0,0 +1,87 @@
+<template>
+  <el-dropdown class="user-info" trigger="click">
+    <div class="user-avatar-wrapper">
+      <img
+        ref="avatarImg"
+        :src="avatar || require('@/assets/images/profile.jpg')"
+        class="user-avatar"
+        @error="handleAvatarError"
+      >
+      <span class="user-name">{{ name }}</span>
+      <i class="el-icon-arrow-down" />
+    </div>
+    <el-dropdown-menu slot="dropdown">
+      <router-link to="/user/profile">
+        <el-dropdown-item>
+          <i class="el-icon-user" />
+          <span>个人中心</span>
+        </el-dropdown-item>
+      </router-link>
+      <el-dropdown-item @click.native="handleLogout">
+        <i class="el-icon-switch-button" />
+        <span>退出登录</span>
+      </el-dropdown-item>
+    </el-dropdown-menu>
+  </el-dropdown>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+
+export default {
+  name: 'UserInfo',
+  computed: {
+    ...mapGetters([
+      'avatar',
+      'name'
+    ])
+  },
+  methods: {
+    handleAvatarError(e) {
+      // 图片加载失败时,设置为默认头像
+      e.target.src = require('@/assets/images/profile.jpg')
+    },
+    handleLogout() {
+      this.$confirm('确定注销并退出系统吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.$store.dispatch('LogOut').then(() => {
+          location.href = '/index'
+        })
+      }).catch(() => {})
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.user-info {
+  cursor: pointer;
+  padding: 0 20px;
+
+  .user-avatar-wrapper {
+    display: flex;
+    align-items: center;
+
+    .user-avatar {
+      width: 40px;
+      height: 40px;
+      border-radius: 50%;
+      margin-right: 10px;
+    }
+
+    .user-name {
+      font-size: 14px;
+      color: #606266;
+      margin-right: 5px;
+    }
+
+    .el-icon-arrow-down {
+      font-size: 12px;
+      color: #909399;
+    }
+  }
+}
+</style>

+ 3 - 12
zkqy-ui/src/lang/en.js

@@ -423,10 +423,6 @@ export default {
     tenantParentId: 'Parent Tenant ID',
     tenantClientLoginUrl: 'Client Login URL',
     tenantToolLoginUrl: 'Tool Login URL',
-    tenantWechatIndexUrl: 'Wechat Program Login URL',
-    configLoginPageClient:"config client",
-    configLoginPageTool:"config tool",
-    configLoginPageWechat:"config wechat index",
     tenantGrade: 'Tenant Grade',
     tenantExpirationTime: 'Expiration Days',
     tenantProfession: 'Tenant Type',
@@ -1046,6 +1042,9 @@ export default {
     directory: 'Directory',
     outLink: 'External Link',
     innerLink: 'Internal Link',
+    insert: 'Insert',
+    update: 'Update',
+    delete: 'Delete',
     execute: 'Execute Script',
     initiated: 'Initiate Process',
     print: 'Print',
@@ -1056,14 +1055,6 @@ export default {
     addBtnGroupName:'Create a new button group',
     addBtnName: "Create a new button group",
     businessScript:"Business Script",
-    businessScriptPage: "open Business Script Page",
-    otherMethod: 'Other Method',
-    bindBusinessFlowScriptType:"Script Type",
-    selectBusinessFlowScriptType:"Select Script Type",
-    selectBusinessFlowScript:"Select Script",
-    bindBusinessFlowVuePage:"Bind Vue Page",
-    selectBusinessFlowVuePage:"Select Vue Page",
-    valid:"valid",
   },
   formGroup: {
     title: 'Form Group Management',

+ 9 - 8
zkqy-ui/src/lang/zh.js

@@ -419,12 +419,15 @@ export default {
     owner: '负责人',
     contactInfo: '联系方式',
     address: '地址',
+    configLoginPageClient:"客户登录页信息",
+    configLoginPageTool:"工具端登录信息",
+    configLoginPageWechat:"配置微信登录页",
+    tenantWechatIndexUrl:"租户微信登录首页",
     tenantId: '租户ID',
     tenant:'租户',
     tenantParentId: '租户父级ID',
     tenantClientLoginUrl: '客户端访问地址',
     tenantToolLoginUrl: '工具端访问地址',
-    tenantWechatIndexUrl:"微信小程序首页地址",
     tenantGrade: '租户等级',
     tenantExpirationTime: '到期天数',
     tenantProfession: '租户类型',
@@ -442,9 +445,6 @@ export default {
     bindDatasource: '绑定数据源',
     activateTenant: '激活租户',
     configLoginPage: '配置登录页',
-    configLoginPageClient:"配置客户端",
-    configLoginPageTool:"配置工具端",
-    configLoginPageWechat:"配置小程序",
     publicNetwork: '公网',
     privateNetwork: '内网',
     datasourceType: '数据源类型',
@@ -1047,6 +1047,10 @@ export default {
     directory: '目录',
     outLink: '外链',
     innerLink: '内链',
+    businessScript:"业务脚本",
+    insert: '新增',
+    update: '修改',
+    delete: '删除',
     execute: '执行脚本',
     initiated: '发起流程',
     print: '打印',
@@ -1056,16 +1060,13 @@ export default {
     export: '导出',
     addBtnGroupName:'新建按钮组',
     addBtnName: "新建按钮组",
-    insert: '新增',
-    update: '修改',
-    delete: '删除',
+
     bindBusinessFlowScriptType:"脚本类型",
     selectBusinessFlowScriptType:"请选择脚本类型",
     selectBusinessFlowScript:"请选择业务脚本",
     bindBusinessFlowVuePage:"绑定页面",
     selectBusinessFlowVuePage:"请选择绑定页面",
     otherMethod: '其它方法',
-    businessScript:"执行业务脚本",
     businessScriptPage:"打开业务脚本页面",
     query:"查询",
     valid:"校验",

+ 10 - 6
zkqy-ui/src/layout/components/Navbar.vue

@@ -1,11 +1,15 @@
 <template>
   <div class="navbar">
-    
-    <hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container"
-      @toggleClick="toggleSideBar" />
 
-    <breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!topNav" />
-    <top-nav id="topmenu-container" class="topmenu-container" v-if="topNav" />
+    <hamburger
+      id="hamburger-container"
+      :is-active="sidebar.opened"
+      class="hamburger-container"
+      @toggleClick="toggleSideBar"
+    />
+
+    <breadcrumb v-if="!topNav" id="breadcrumb-container" class="breadcrumb-container" />
+    <top-nav v-if="topNav" id="topmenu-container" class="topmenu-container" />
 
     <div class="right-menu">
       <template v-if="device !== 'mobile'">
@@ -96,7 +100,7 @@ export default {
   methods: {
     toggleSideBar() {
       this.$store.dispatch('app/toggleSideBar')
-    },
+    }
     // async logout() {
     //   this.$confirm('确定注销并退出系统吗?', '提示', {
     //     confirmButtonText: '确定',

+ 1 - 3
zkqy-ui/src/main.js

@@ -5,7 +5,6 @@ import 'k-form-design/lib/k-form-design.css'
 // import Vue from 'vue/dist/vue.js'
 // require('k-form-design/lib/k-form-design.css')
 import Cookies from 'js-cookie'
-import ELEMENT from 'element-ui';
 import Element from 'element-ui'
 import './assets/styles/element-variables.scss'
 // require('./assets/styles/element-variables.scss')
@@ -21,7 +20,6 @@ import zkqyTable from '../src/views/formCreate/components/zkqyMenu/zkqyTable.vue
 // Vue.use(FcDesigner);
 // Vue.use(FcDesigner.formCreate);
 Vue.use(formCreate);
-Vue.use(ELEMENT);
 Vue.component("FcDesigner", FcDesigner);
 // 注册自定义组件到设计器
 FcDesigner.component('zkqyTable', zkqyTable);
@@ -196,7 +194,7 @@ Object.keys(Directives).forEach(item => {
  * Currently MockJs will be used in the production environment,
  * please remove it before going online! ! !
  */
-Vue.use(VueI18n) 
+Vue.use(VueI18n)
 Vue.use(Element, {
   size: Cookies.get('size') || 'medium',// set element-ui default size
   i18n: (key, value) => i18n.t(key, value)

+ 1 - 1
zkqy-ui/src/permission.js

@@ -8,7 +8,7 @@ import { isRelogin } from '@/utils/request'
 
 NProgress.configure({ showSpinner: false })
 
-const whiteList = ['/login', '/register', '/404', '/adminLogin', '/401', '/loading', '/redirectAuth']
+const whiteList = ['/login', '/register', '/404', '/adminLogin', '/401', '/loading', '/redirectAuth','/application']
 
 router.beforeEach((to, from, next) => {
   NProgress.start()

+ 10 - 1
zkqy-ui/src/router/index.js

@@ -142,7 +142,16 @@ export const constantRoutes = [
     meta: {
       requireAuth: true
     }
-  }, //luckeysheet设计
+  },
+  { //大屏设计 zkqy-ui/src/
+    path: '/application',
+    component: () => import('@/views/applicationview/AppPlatform'),
+    hidden: true,
+    meta: {
+      requireAuth: true
+    }
+  },
+  //luckeysheet设计
   {
     path: '/excelreport/designer',
     component: () => import('@/views/system/report/excelreport/designer'),

+ 1426 - 0
zkqy-ui/src/views/applicationview/AppPlatform.vue

@@ -0,0 +1,1426 @@
+<template>
+  <div class="app-platform">
+    <!-- 顶部导航栏 -->
+    <header class="platform-header">
+      <div class="header-left">
+        <img src="/static/bb3845c295f66fc0e82f3874da4688e.jpg" alt="logo" class="header-logo">
+        <h1>擎云应用平台</h1>
+      </div>
+      <div class="user-actions">
+        <el-dropdown class="user-info" trigger="click">
+          <div class="user-avatar-wrapper">
+            <img
+              ref="avatarImg"
+              :src="avatar || require('@/assets/images/profile.jpg')"
+              class="user-avatar"
+              @error="handleAvatarError"
+            >
+            <span class="user-name">{{ name }}</span>
+            <i class="el-icon-arrow-down" />
+          </div>
+          <el-dropdown-menu slot="dropdown">
+            <!-- <router-link to="/user/profile">
+              <el-dropdown-item>
+                <i class="el-icon-user" />
+                <span>个人中心</span>
+              </el-dropdown-item>
+            </router-link> -->
+            <el-dropdown-item @click.native="handleLogout">
+              <i class="el-icon-switch-button" />
+              <span>退出登录</span>
+            </el-dropdown-item>
+          </el-dropdown-menu>
+        </el-dropdown>
+      </div>
+    </header>
+
+    <!-- 主要内容区域 -->
+    <div class="platform-content">
+      <!-- 工具栏区域 -->
+      <div class="platform-toolbar">
+        <div class="toolbar-main">
+          <div class="app-categories">
+            <button
+              :class="['category-btn', { active: currentCategory === 'all' }]"
+              @click="currentCategory = 'all'"
+            >
+              全部
+            </button>
+            <button
+              v-for="category in categoriesTwo"
+              :key="category.id"
+              :class="['category-btn', { active: currentCategory === category.id }]"
+              @click="currentCategory = category.id"
+            >
+              {{ category.categoryName }}
+            </button>
+          </div>
+          <div class="search-box">
+            <el-input
+              v-model="searchKeyword"
+              placeholder="搜索应用"
+              clearable
+              @clear="handleSearch"
+              @keyup.enter.native="handleSearch"
+            >
+              <i slot="suffix" class="el-input__icon el-icon-search" @click="handleSearch" />
+            </el-input>
+          </div>
+        </div>
+      </div>
+
+      <!-- 应用卡片网格 -->
+      <div class="app-grid">
+        <div
+          v-for="app in appList"
+          :key="app.id"
+          class="app-card"
+          @click="handleAppClick(app)"
+          @dblclick="handleAppDblClick(app)"
+          @contextmenu.prevent="handleContextMenu($event, app)"
+        >
+          <div class="app-icon" :style="{ background: app.iconColor }">
+            <i :class="app.iconUrl" />
+          </div>
+          <h3 class="app-title">{{ app.appName }}</h3>
+          <p class="app-desc">{{ app.description }}</p>
+          <div class="app-footer">
+            <span class="app-date">
+              <i class="el-icon-time" />
+              {{ app.createTime }}
+            </span>
+          </div>
+        </div>
+
+        <!-- 添加应用卡片 -->
+        <div class="add-card" @click="handleAddCardClick">
+          <i class="el-icon-plus add-icon" />
+          <span class="add-text">添加应用</span>
+        </div>
+
+        <!-- 右键菜单 -->
+        <div
+          v-show="showContextMenu"
+          class="context-menu"
+          :style="{ left: contextMenuX + 'px', top: contextMenuY + 'px' }"
+          @click.stop
+          @contextmenu.prevent.stop
+        >
+          <div class="menu-item" @click.stop="handleEdit">
+            <i class="el-icon-edit" />
+            <span>编辑应用</span>
+          </div>
+          <div class="menu-item" @click.stop="handleDelete">
+            <i class="el-icon-delete" />
+            <span>删除应用</span>
+          </div>
+          <div class="menu-item" @click.stop="handleCopy">
+            <i class="el-icon-document-copy" />
+            <span>复制应用</span>
+          </div>
+          <div class="menu-item" @click.stop="handleShare">
+            <i class="el-icon-share" />
+            <span>分享应用</span>
+          </div>
+          <div class="menu-item" @click.stop="handleBindDataSource">
+            <i class="el-icon-connection" />
+            <span>绑定数据源</span>
+          </div>
+        </div>
+      </div>
+
+      <!-- 分页 -->
+      <div class="pagination-container">
+        <el-pagination
+          background
+          :current-page="queryParams.pageNum"
+          :page-sizes="[12, 24, 36, 48]"
+          :page-size="queryParams.pageSize"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="total"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+
+      <!-- 添加应用弹窗 -->
+      <el-dialog
+        title="添加应用"
+        :visible.sync="showAddAppModal"
+        width="600px"
+        :close-on-click-modal="false"
+        @closed="resetForm"
+      >
+        <el-form
+          ref="addAppForm"
+          :model="addAppForm"
+          :rules="addAppRules"
+          label-width="100px"
+          size="small"
+        >
+          <el-form-item label="应用名称" prop="appName">
+            <el-input v-model="addAppForm.appName" placeholder="请输入应用名称" />
+          </el-form-item>
+
+          <el-form-item label="应用分类" prop="category">
+            <el-select v-model="addAppForm.category" placeholder="请选择应用分类" style="width: 100%">
+              <el-option
+                v-for="item in categories"
+                :key="item.id"
+                :label="item.categoryName"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+
+          <el-form-item label="应用图标" prop="icon">
+            <el-select v-model="addAppForm.iconUrl" placeholder="请选择应用图标" style="width: 100%" @change="handleIconChange">
+              <el-option
+                v-for="icon in iconOptions"
+                :key="icon.value"
+                :label="icon.label"
+                :value="icon.value"
+              >
+                <i :class="icon.value" style="margin-right: 8px;margin-left: 15px;" />
+                {{ icon.label }}
+              </el-option>
+            </el-select>
+          </el-form-item>
+
+          <el-form-item label="图标颜色" prop="iconColor">
+            <el-color-picker v-model="addAppForm.iconColor" show-alpha />
+          </el-form-item>
+
+          <el-form-item label="应用描述" prop="description">
+            <el-input
+              v-model="addAppForm.description"
+              type="textarea"
+              :rows="4"
+              placeholder="请输入应用描述"
+            />
+          </el-form-item>
+        </el-form>
+
+        <div slot="footer" class="dialog-footer">
+          <el-button @click="showAddAppModal = false">取 消</el-button>
+          <el-button type="primary" :loading="submitting" @click="handleAddApp">确 定</el-button>
+        </div>
+      </el-dialog>
+
+      <!-- 编辑应用弹窗 -->
+      <el-dialog
+        title="编辑应用"
+        :visible.sync="showEditDialog"
+        width="600px"
+        :close-on-click-modal="false"
+        @closed="resetEditForm"
+      >
+        <el-form
+          ref="editForm"
+          :model="editForm"
+          :rules="addAppRules"
+          label-width="100px"
+          size="small"
+        >
+          <el-form-item label="应用名称" prop="appName">
+            <el-input v-model="editForm.appName" placeholder="请输入应用名称" />
+          </el-form-item>
+
+          <el-form-item label="应用分类" prop="category">
+            <el-select v-model="editForm.category" placeholder="请选择应用分类" style="width: 100%">
+              <el-option
+                v-for="item in categories"
+                :key="item.id"
+                :label="item.categoryName"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+
+          <el-form-item label="应用图标" prop="iconUrl">
+            <el-select v-model="editForm.iconUrl" placeholder="请选择应用图标" style="width: 100%" @change="handleEditIconChange">
+              <el-option
+                v-for="icon in iconOptions"
+                :key="icon.value"
+                :label="icon.label"
+                :value="icon.value"
+              >
+                <i :class="icon.value" style="margin-right: 8px;margin-left: 15px;" />
+                {{ icon.label }}
+              </el-option>
+            </el-select>
+          </el-form-item>
+
+          <el-form-item label="图标颜色" prop="iconColor">
+            <el-color-picker v-model="editForm.iconColor" show-alpha />
+          </el-form-item>
+
+          <el-form-item label="应用描述" prop="description">
+            <el-input
+              v-model="editForm.description"
+              type="textarea"
+              :rows="4"
+              placeholder="请输入应用描述"
+            />
+          </el-form-item>
+        </el-form>
+
+        <div slot="footer" class="dialog-footer">
+          <el-button @click="showEditDialog = false">取 消</el-button>
+          <el-button type="primary" :loading="submitting" @click="handleEditSubmit">确 定</el-button>
+        </div>
+      </el-dialog>
+
+      <!-- 数据源绑定弹窗 -->
+      <el-dialog
+        title="绑定数据源"
+        :visible.sync="showBindDialog"
+        width="800px"
+        :close-on-click-modal="false"
+        @closed="resetBindForm"
+      >
+        <el-form
+          ref="bindForm"
+          :model="bindForm"
+          :rules="bindRules"
+          label-width="120px"
+          size="small"
+        >
+          <el-form-item label="数据源类型" prop="type">
+            <el-select v-model="bindForm.type" placeholder="请选择数据源类型" style="width: 100%">
+              <!--<el-option label="MySQL" value="mysql">-->
+              <!--  <i class="el-icon-coin" style="color: #4479a1;margin-left: 12px;" />-->
+              <!--  <span style="margin-left: -16px;">MySQL</span>-->
+              <!--</el-option>-->
+              <!--<el-option label="PostgreSQL" value="postgresql">-->
+              <!--  <i class="el-icon-coin" style="color: #336791;margin-left: 12px; " />-->
+              <!--  <span style="margin-left: -16px;;">PostgreSQL</span>-->
+              <!--</el-option>-->
+              <!--<el-option label="Oracle" value="oracle">-->
+              <!--  <i class="el-icon-coin" style="color: #f80102;margin-left: 12px;" />-->
+              <!--  <span style="margin-left: -16px;;">Oracle</span>-->
+              <!--</el-option>-->
+              <!--<el-option label="SQL Server" value="sqlserver">-->
+              <!--  <i class="el-icon-coin" style="color: #f35325;margin-left: 12px;" />-->
+              <!--  <span style="margin-left: -16px;">SQL Server</span>-->
+              <!--</el-option>-->
+              <!--<el-option label="Redis" value="redis">-->
+              <!--  <i class="el-icon-coin" style="color: #dc382d;margin-left: 12px;" />-->
+              <!--  <span style="margin-left: -16px;">Redis</span>-->
+              <!--</el-option>-->
+            </el-select>
+          </el-form-item>
+
+          <el-form-item label="连接名称" prop="name">
+            <el-input v-model="bindForm.name" placeholder="请输入连接名称" />
+          </el-form-item>
+
+          <el-form-item label="主机地址" prop="host">
+            <el-input v-model="bindForm.host" placeholder="请输入主机地址" />
+          </el-form-item>
+
+          <el-form-item label="端口" prop="port">
+            <el-input-number
+              v-model="bindForm.port"
+              :min="1"
+              :max="65535"
+              style="width: 100%"
+            />
+          </el-form-item>
+
+          <el-form-item v-if="bindForm.type !== 'redis'" label="数据库名" prop="database">
+            <el-input v-model="bindForm.database" placeholder="请输入数据库名" />
+          </el-form-item>
+
+          <el-form-item label="用户名" prop="username">
+            <el-input v-model="bindForm.username" placeholder="请输入用户名" />
+          </el-form-item>
+
+          <el-form-item label="密码" prop="password">
+            <el-input
+              v-model="bindForm.password"
+              type="password"
+              placeholder="请输入密码"
+              show-password
+            />
+          </el-form-item>
+          <el-form-item label="应用管理员账号" prop="adminName">
+            <el-input v-model="bindForm.adminName" placeholder="应用管理员账号" />
+          </el-form-item>
+        </el-form>
+
+        <div slot="footer" class="dialog-footer">
+          <el-button @click="showBindDialog = false">取 消</el-button>
+          <el-button type="primary" :loading="testing" @click="handleTestConnection">测试连接</el-button>
+          <el-button type="success" :loading="saving" @click="handleSaveConnection">绑定数据源</el-button>
+        </div>
+      </el-dialog>
+    </div>
+  </div>
+</template>
+
+<script>
+import { listApplications, getApplications, addApplications, updateApplications, delApplications, allClassification, getDataSourceInformation,testConnection,applicationInitDataSource } from '@/api/application/applications'
+import { listCategories } from '@/api/application/categories'
+import { mapGetters } from 'vuex'
+
+export default {
+  name: 'AppPlatform',
+  data() {
+    // 端口号验证函数
+    const validatePort = (rule, value, callback) => {
+      if (value === '') {
+        callback(new Error('请输入端口号'))
+      } else if (value < 1 || value > 65535) {
+        callback(new Error('端口号必须在1-65535之间'))
+      } else {
+        callback()
+      }
+    }
+    return {
+      searchQuery: '',
+      showUserMenu: false,
+      currentCategory: 'all',
+      showAddAppModal: false,
+      currentPage: 1,
+      pageSize: 10,
+      user: {
+        name: '张三',
+        avatar: '' // 可以设置头像URL
+      },
+      categories: [],
+      categoriesTwo: [],
+      apps: [],
+      submitting: false,
+      addAppForm: {
+        appName: '',
+        category: '',
+        iconUrl: '',
+        iconColor: '',
+        description: '',
+        icon: ''
+      },
+      addAppRules: {
+        appName: [
+          { required: true, message: '请输入应用名称', trigger: 'blur' },
+          { min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
+        ],
+        category: [
+          { required: true, message: '请选择应用分类', trigger: 'change' }
+        ],
+        iconUrl: [
+          { required: true, message: '请输入图标地址', trigger: 'blur' }
+        ],
+        iconColor: [
+          { required: true, message: '请选择图标颜色', trigger: 'change' }
+        ],
+        description: [
+          { required: true, message: '请输入应用描述', trigger: 'blur' },
+          { max: 200, message: '描述不能超过200个字符', trigger: 'blur' }
+        ]
+      },
+      iconOptions: [
+        { value: 'el-icon-s-platform', label: 'HIS系统' },
+        { value: 'el-icon-s-management', label: 'WMS系统' },
+        { value: 'el-icon-s-cooperation', label: 'CRM系统' },
+        { value: 'el-icon-s-marketing', label: 'HR系统' },
+        { value: 'el-icon-s-data', label: 'ERP系统' },
+        { value: 'el-icon-s-opportunity', label: 'OA系统' },
+        { value: 'el-icon-s-custom', label: 'MES系统' },
+        { value: 'el-icon-s-grid', label: 'SCM系统' },
+        { value: 'el-icon-s-order', label: 'SRM系统' },
+        { value: 'el-icon-s-finance', label: '财务系统' },
+        { value: 'el-icon-s-marketing', label: '营销系统' },
+        { value: 'el-icon-s-data', label: '数据分析' },
+        { value: 'el-icon-s-opportunity', label: '客户管理' },
+        { value: 'el-icon-s-custom', label: '库存管理' },
+        { value: 'el-icon-s-grid', label: '生产管理' }
+      ],
+      searchKeyword: '',
+      filteredApps: [],
+      showContextMenu: false,
+      contextMenuX: 0,
+      contextMenuY: 0,
+      selectedApp: null,
+      showBindDialog: false,
+      testing: false,
+      saving: false,
+      bindForm: {
+        type: '',
+        name: '',
+        host: '',
+        port: 3306,
+        database: '',
+        username: '',
+        password: '',
+        remark: '',
+        adminName: '',
+        clintId:""
+      },
+      bindRules: {
+        type: [
+          { required: true, message: '请选择数据源类型', trigger: 'change' }
+        ],
+        name: [
+          { required: true, message: '请输入连接名称', trigger: 'blur' },
+          { min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
+        ],
+        host: [
+          { required: true, message: '请输入主机地址', trigger: 'blur' }
+        ],
+        port: [
+          { required: true, message: '请输入端口号', trigger: 'blur' },
+          { validator: validatePort, trigger: 'blur' }
+        ],
+        database: [
+          { required: true, message: '请输入数据库名', trigger: 'blur' }
+        ],
+        username: [
+          { required: true, message: '请输入用户名', trigger: 'blur' }
+        ],
+        password: [
+          { required: true, message: '请输入密码', trigger: 'blur' }
+        ],
+        adminName: [
+          { required: true, message: '请输入应用管理员账号', trigger: 'blur' }
+        ]
+      },
+      showEditDialog: false,
+      editForm: {
+        id: null,
+        appName: '',
+        category: '',
+        iconUrl: '',
+        iconColor: '',
+        description: ''
+      },
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 12,
+        keyword: ''
+      },
+      // 总条数
+      total: 0,
+      // 应用列表
+      appList: [],
+      loading: false,
+      dataSourceInformationQuery: {
+        pageNum: 1,
+        pageSize: 10,
+        dictType: 'mysql_connection_information'
+      }
+    }
+  },
+  computed: {
+    ...mapGetters([
+      'avatar',
+      'name'
+    ]),
+    // 根据当前分类过滤应用
+    currentApps() {
+      return this.filteredApps || []
+    },
+    // 分页后的应用列表
+    paginatedApps() {
+      const start = (this.currentPage - 1) * this.pageSize
+      const end = start + this.pageSize
+      return (this.currentApps || []).slice(start, end)
+    },
+    // 总条目数
+    totalItems() {
+      return (this.currentApps || []).length
+    }
+  },
+  watch: {
+    // 当分类改变时重新获取应用列表
+    currentCategory() {
+      this.currentPage = 1
+      this.getList()
+    }
+  },
+  created() {
+    // 获取分类信息
+    this.getClassificationList()
+    // 获取应用列表
+    this.getList()
+    // 获取新增时候的分类列表
+    this.getCategories()
+    // 获取数据源信息
+    this.getDataSourceInformation()
+  },
+  mounted() {
+    // 点击或右键其他区域关闭右键菜单
+    document.addEventListener('click', this.closeContextMenu)
+    document.addEventListener('contextmenu', this.closeContextMenu)
+    // 滚动时关闭右键菜单
+    document.addEventListener('scroll', this.closeContextMenu)
+  },
+  beforeDestroy() {
+    // 移除事件监听
+    document.removeEventListener('click', this.closeContextMenu)
+    document.removeEventListener('contextmenu', this.closeContextMenu)
+    document.removeEventListener('scroll', this.closeContextMenu)
+  },
+  methods: {
+    // ...其他方法...
+    handleAppDblClick(app) {
+      this.$confirm('是否要进入系统?', '提示', {
+        confirmButtonText: '进入',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        // 这里写进入系统的逻辑,比如跳转
+        this.$message.success('即将进入系统:' + app.appName)
+        // 例如:window.open(app.url) 或 this.$router.push(...)
+      }).catch(() => {
+        // 用户取消
+      })
+    },
+    handleLogout() {
+      this.$confirm('确定注销并退出系统吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.$store.dispatch('LogOut').then(() => {
+          location.href = '/index'
+        })
+      }).catch(() => {})
+    },
+    async getDataSourceInformation() {
+      const response = await getDataSourceInformation(this.dataSourceInformationQuery)
+      if (response.code === 200) {
+        // 转换为对象
+        const info = {}
+        response.rows.forEach(item => {
+          info[item.dictLabel] = item.dictValue
+        })
+        // 填充表单
+        this.bindForm = {
+          ...this.bindForm,
+          database: info.databaseName || '',
+          name: info.databaseIp || '',
+          host: info.databaseIp || '',
+          username: info.username || '',
+          password: info.password || '',
+          port: Number(info.portNumber) || 3306,
+          type: info.databaseType || ''
+        }
+      }
+    },
+    // 获取分类列表
+    async getCategories() {
+      try {
+        const response = await listCategories()
+        if (response.code === 200) {
+          this.categories = response.rows
+        }
+      } catch (error) {
+        console.error('获取分类列表失败:', error)
+        this.$message.error('获取分类列表失败')
+      }
+    },
+    // 获取分类信息
+    getClassificationList() {
+      allClassification().then(response => {
+        if (response.code === 200) {
+          this.categoriesTwo = response.data
+        }
+      })
+    },
+    // 获取应用列表
+    getList() {
+      this.loading = true
+      const params = {
+        ...this.queryParams,
+        category: this.currentCategory === 'all' ? '' : this.currentCategory
+      }
+      listApplications(params).then(response => {
+        this.appList = response.rows
+        this.total = response.total
+        this.loading = false
+        // 获取分类信息
+        this.getClassificationList()
+      }).catch(() => {
+        this.loading = false
+      })
+    },
+    // 搜索
+    handleSearch() {
+      this.queryParams.keyword = this.searchKeyword
+      this.queryParams.pageNum = 1
+      this.getList()
+    },
+    // 分页大小改变
+    handleSizeChange(val) {
+      this.queryParams.pageSize = val
+      this.getList()
+    },
+    // 页码改变
+    handleCurrentChange(val) {
+      this.queryParams.pageNum = val
+      this.getList()
+    },
+    // 重置表单
+    resetForm() {
+      this.addAppForm = {
+        appName: '',
+        category: '',
+        iconUrl: '',
+        iconColor: '',
+        description: ''
+      }
+      if (this.$refs.addAppForm) {
+        this.$refs.addAppForm.resetFields()
+      }
+    },
+    handleAppClick(app) {
+      // 获取应用详情
+      getApplications(app.id).then(response => {
+        if (response.code === 200) {
+          console.log('应用详情:', response.data)
+          // 这里可以添加跳转到应用详情页的逻辑
+        }
+      })
+    },
+    handleAddApp() {
+      this.$refs.addAppForm.validate(valid => {
+        if (valid) {
+          this.submitting = true
+          addApplications(this.addAppForm).then(response => {
+            if (response.code === 200) {
+              this.$message.success('添加应用成功')
+              this.showAddAppModal = false
+              this.getList()
+            }
+            this.submitting = false
+          }).catch(() => {
+            this.submitting = false
+          })
+        }
+      })
+    },
+    // 处理右键菜单
+    handleContextMenu(event, app) {
+      // 阻止事件冒泡,防止触发document的右键事件
+      event.stopPropagation()
+      this.showContextMenu = true
+      this.contextMenuX = event.clientX
+      this.contextMenuY = event.clientY
+      this.selectedApp = app
+    },
+    // 关闭右键菜单
+    closeContextMenu() {
+      this.showContextMenu = false
+    },
+    // 处理编辑图标变化
+    handleEditIconChange(value) {
+      // 根据选择的图标设置默认颜色
+      const defaultColors = {
+        'el-icon-s-platform': '#409EFF', // HIS系统 - 蓝色
+        'el-icon-s-management': '#67C23A', // WMS系统 - 绿色
+        'el-icon-s-cooperation': '#E6A23C', // CRM系统 - 橙色
+        'el-icon-s-marketing': '#F56C6C', // HR系统 - 红色
+        'el-icon-s-data': '#909399', // ERP系统 - 灰色
+        'el-icon-s-opportunity': '#9B59B6', // OA系统 - 紫色
+        'el-icon-s-custom': '#34495E', // MES系统 - 深蓝
+        'el-icon-s-grid': '#16A085', // SCM系统 - 青色
+        'el-icon-s-order': '#D35400', // SRM系统 - 棕色
+        'el-icon-s-finance': '#27AE60' // 财务系统 - 深绿
+      }
+      this.editForm.iconColor = defaultColors[value] || '#409EFF'
+    },
+    // 处理编辑
+    handleEdit() {
+      console.log(this.selectedApp.category)
+      if (this.selectedApp) {
+        this.editForm = {
+          id: this.selectedApp.id,
+          appName: this.selectedApp.appName,
+          category: parseInt(this.selectedApp.category),
+          iconUrl: this.selectedApp.iconUrl,
+          iconColor: this.selectedApp.iconColor,
+          description: this.selectedApp.description
+        }
+        this.showEditDialog = true
+      }
+      this.showContextMenu = false
+    },
+    // 重置编辑表单
+    resetEditForm() {
+      if (this.$refs.editForm) {
+        this.$refs.editForm.resetFields()
+      }
+      this.editForm = {
+        id: null,
+        name: '',
+        category: '',
+        icon: '',
+        iconBg: '#1890ff',
+        description: ''
+      }
+      this.submitting = false
+    },
+    // 保存编辑
+    handleEditSubmit() {
+      this.$refs.editForm.validate((valid) => {
+        if (valid) {
+          this.submitting = true
+          updateApplications(this.editForm).then(response => {
+            if (response.code === 200) {
+              this.$message.success('保存成功')
+              this.showEditDialog = false
+              // 分类信息
+              this.getList()
+            }
+            this.submitting = false
+          }).catch(() => {
+            this.submitting = false
+          })
+        }
+      })
+    },
+    // 删除应用
+    handleDelete() {
+      this.$confirm('确认删除该应用吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        delApplications(this.selectedApp.id).then(response => {
+          if (response.code === 200) {
+            this.$message.success('删除成功')
+            this.getList()
+          }
+        })
+      }).catch(() => {})
+      this.closeContextMenu()
+    },
+    // 复制应用
+    handleCopy() {
+      const newApp = {
+        ...this.selectedApp,
+        id: this.apps.length + 1,
+        name: this.selectedApp.name + ' (副本)',
+        date: new Date().toISOString().split('T')[0],
+        userCount: '0'
+      }
+      this.apps.unshift(newApp)
+      this.filteredApps = this.handleSearch()
+      this.$message.success('复制成功')
+      this.closeContextMenu()
+    },
+    // 处理绑定数据源
+    handleBindDataSource() {
+      // 获取数据源信息
+      this.getDataSourceInformation()
+      // 展示绑定数据源弹窗
+      this.showBindDialog = true
+      // 根据数据源类型设置默认端口
+      this.$watch('bindForm.type', (newType) => {
+        switch (newType) {
+          case 'mysql':
+            this.bindForm.port = 3306
+            break
+          case 'postgresql':
+            this.bindForm.port = 5432
+            break
+          case 'oracle':
+            this.bindForm.port = 1521
+            break
+          case 'sqlserver':
+            this.bindForm.port = 1433
+            break
+          case 'redis':
+            this.bindForm.port = 6379
+            break
+        }
+      })
+      this.bindForm.clientId=this.selectedApp.id;
+      console.log(this.bindForm);
+      this.closeContextMenu()
+    },
+    // 重置绑定表单
+    resetBindForm() {
+      if (this.$refs.bindForm) {
+        this.$refs.bindForm.resetFields()
+      }
+      this.bindForm = {
+        type: '',
+        name: '',
+        host: '',
+        port: 3306,
+        database: '',
+        username: '',
+        password: '',
+        remark: ''
+      }
+      this.testing = false
+      this.saving = false
+    },
+    // 测试连接
+    handleTestConnection() {
+      this.$refs.bindForm.validate((valid) => {
+        if (valid) {
+          this.testing = true
+          this.getTestConnection();
+        }
+      })
+    },
+    async getTestConnection() {
+      const resp = await testConnection(this.bindForm)
+      if (resp.code === 200) {
+        this.testing = false
+        this.$message.success('数据库连接测试成功')
+      } else {
+        this.testing = false;
+        this.$message.error('数据库连接测试失败')
+      }
+    },
+    async applicationInitDataSource() {
+      const resp = await applicationInitDataSource(this.bindForm)
+      if (resp.code === 200) {
+        this.testing = false
+        this.$message.success('初始化数据源成功')
+      } else {
+        this.testing = false
+        this.$message.error('初始化数据源失败')
+      }
+    },
+    // 保存连接
+    handleSaveConnection() {
+      // 前置校验数据库名称规则是否符合规范
+      this.$refs.bindForm.validate((valid) => {
+        if (valid) {
+          this.saving = true
+          const sourceData = {
+            ...this.bindForm,
+            applicationId: this.selectedApp.id
+          }
+          applicationInitDataSource(sourceData).then(response => {
+            if (response.code === 200) {
+              this.$message.success('数据源绑定成功')
+              this.showBindDialog = false
+              if (this.selectedApp) {
+                this.selectedApp.hasDataSource = true
+              }
+              this.getList()
+            }
+            this.saving = false
+          }).catch(() => {
+            this.saving = false
+          })
+        }
+      })
+    },
+    // 分享应用
+    handleShare() {
+      this.$message('分享功能开发中...')
+      this.closeContextMenu()
+    },
+    // 添加一个方法来处理添加卡片的点击
+    handleAddCardClick() {
+      console.log('点击添加卡片')
+      this.showAddAppModal = true
+    },
+    // 处理图标选择变化
+    handleIconChange(value) {
+      // 根据选择的图标设置默认颜色
+      const defaultColors = {
+        'el-icon-s-platform': '#409EFF', // HIS系统 - 蓝色
+        'el-icon-s-management': '#67C23A', // WMS系统 - 绿色
+        'el-icon-s-cooperation': '#E6A23C', // CRM系统 - 橙色
+        'el-icon-s-marketing': '#F56C6C', // HR系统 - 红色
+        'el-icon-s-data': '#909399', // ERP系统 - 灰色
+        'el-icon-s-opportunity': '#9B59B6', // OA系统 - 紫色
+        'el-icon-s-custom': '#34495E', // MES系统 - 深蓝
+        'el-icon-s-grid': '#16A085', // SCM系统 - 青色
+        'el-icon-s-order': '#D35400', // SRM系统 - 棕色
+        'el-icon-s-finance': '#27AE60' // 财务系统 - 深绿
+      }
+      this.addAppForm.iconColor = defaultColors[value] || '#409EFF'
+    },
+    handleAvatarError(e) {
+      // 图片加载失败时,设置为默认头像
+      e.target.src = require('@/assets/images/profile.jpg')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.app-platform {
+  min-height: 100vh;
+  background: #f0f2f5;
+  padding: 20px 24px;
+}
+
+.platform-header {
+  background: white;
+  padding: 16px 24px;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0,0,0,0.05);
+  margin-bottom: 24px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+
+  .header-left {
+    display: flex;
+    align-items: center;
+    gap: 10px;
+
+    .header-logo {
+      width: 170px;
+      height: 50px;
+      object-fit: contain;
+    }
+
+    h1 {
+      margin: 0;
+      font-size: 20px;
+      color: #333;
+    }
+  }
+}
+
+.platform-content {
+  background: white;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0,0,0,0.05);
+  padding: 24px;
+}
+
+.platform-toolbar {
+  padding: 0 24px;
+  background: #fff;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.toolbar-main {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  height: 56px;
+}
+
+/* 分类按钮组样式 */
+.app-categories {
+  display: flex;
+  height: 100%;
+}
+
+.category-btn {
+  height: 100%;
+  padding: 0 20px;
+  border: none;
+  background: none;
+  color: #606266;
+  cursor: pointer;
+  transition: all 0.3s;
+  font-size: 14px;
+  position: relative;
+}
+
+.category-btn:hover {
+  color: #409EFF;
+}
+
+.category-btn.active {
+  color: #409EFF;
+}
+
+.category-btn.active::after {
+  content: '';
+  position: absolute;
+  bottom: 0;
+  left: 20%;
+  width: 60%;
+  height: 2px;
+  background: #409EFF;
+  border-radius: 1px;
+}
+
+/* 搜索框样式调整 */
+.search-box {
+  display: flex;
+  align-items: center;
+}
+
+.search-box .el-input {
+  width: 320px;
+}
+
+.search-box >>> .el-input__inner {
+  height: 32px;
+  line-height: 32px;
+  border-radius: 16px;
+  background: #f5f7fa;
+  border: 1px solid #f5f7fa;
+  padding-right: 35px;
+  transition: all 0.3s;
+}
+
+.search-box >>> .el-input__inner:hover {
+  border-color: #ebeef5;
+  background: #ebeef5;
+}
+
+.search-box >>> .el-input__inner:focus {
+  background: #fff;
+  border-color: #dcdfe6;
+}
+
+.search-box >>> .el-input__suffix {
+  right: 12px;
+}
+
+.search-box >>> .el-input__suffix-inner {
+  display: flex;
+  align-items: center;
+}
+
+.search-box >>> .el-input__icon {
+  line-height: 32px;
+  color: #909399;
+  font-size: 16px;
+  cursor: pointer;
+  transition: all 0.3s;
+}
+
+.search-box >>> .el-input__icon:hover {
+  color: #409EFF;
+}
+
+.search-box >>> .el-input__clear {
+  right: 30px;
+}
+
+.app-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+  gap: 24px;
+  padding: 24px;
+}
+
+.app-card {
+  height: auto;
+  min-height: 180px;
+  padding: 24px;
+  background: #fff;
+  border-radius: 8px;
+  cursor: pointer;
+  transition: all 0.3s;
+  position: relative;
+  border: 1px solid #e8e8e8;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+  display: flex;
+  flex-direction: column;
+}
+
+.app-card:hover {
+  transform: translateY(-2px);
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+  border-color: #d9d9d9;
+}
+
+.app-icon {
+  width: 48px;
+  height: 48px;
+  border-radius: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 16px;
+  font-size: 24px;
+  color: #fff;
+}
+
+.app-title {
+  font-size: 16px;
+  font-weight: 500;
+  color: #262626;
+  margin-bottom: 12px;
+  line-height: 1.4;
+}
+
+.app-desc {
+  font-size: 13px;
+  color: #8c8c8c;
+  line-height: 1.6;
+  margin-bottom: 16px;
+  flex: 1;
+}
+
+.app-footer {
+  display: flex;
+  align-items: center;
+  font-size: 12px;
+  color: #bfbfbf;
+  margin-top: auto;
+  padding-top: 16px;
+  border-top: 1px solid #f0f0f0;
+}
+
+.app-date {
+  display: flex;
+  align-items: center;
+}
+
+.app-date i {
+  margin-right: 6px;
+  font-size: 14px;
+}
+
+/* 添加响应式布局 */
+@media screen and (max-width: 1400px) {
+  .app-grid {
+    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+  }
+}
+
+@media screen and (max-width: 768px) {
+  .app-grid {
+    grid-template-columns: 1fr;
+    padding: 16px;
+    gap: 16px;
+  }
+
+  .app-card {
+    padding: 20px;
+  }
+
+  .app-icon {
+    width: 48px;
+    height: 48px;
+    border-radius: 12px;
+    margin-right: 16px;
+  }
+
+  .app-icon i {
+    font-size: 24px;
+  }
+
+  .app-name {
+    font-size: 16px;
+  }
+
+  .app-description {
+    margin-bottom: 12px;
+    font-size: 13px;
+  }
+
+  .app-stats {
+    gap: 12px;
+    font-size: 12px;
+  }
+
+  .stat {
+    padding: 3px 6px;
+  }
+}
+
+.user-actions {
+  position: relative;
+}
+
+.user-info {
+  cursor: pointer;
+  padding: 0 20px;
+}
+
+.user-avatar-wrapper {
+  display: flex;
+  align-items: center;
+}
+
+.user-avatar {
+  width: 40px;
+  height: 40px;
+  border-radius: 50%;
+  margin-right: 10px;
+}
+
+.user-avatar-placeholder {
+  width: 40px;
+  height: 40px;
+  border-radius: 50%;
+  background-color: #409EFF;
+  color: #fff;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 18px;
+  margin-right: 10px;
+}
+
+.user-name {
+  font-size: 14px;
+  color: #606266;
+  margin-right: 5px;
+}
+
+.el-icon-arrow-down {
+  font-size: 12px;
+  color: #909399;
+}
+
+.menu-item {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  padding: 10px 16px;
+  cursor: pointer;
+  transition: all 0.3s;
+  white-space: nowrap;
+}
+
+.menu-item:hover {
+  background: #f5f5f5;
+}
+
+.menu-item i {
+  margin-right: 8px;
+  font-size: 16px;
+}
+
+.pagination-container {
+  padding: 24px;
+  text-align: right;
+  background: white;
+  border-top: 1px solid #f0f0f0;
+}
+
+@media screen and (max-width: 768px) {
+  .pagination-container {
+    padding: 16px;
+    text-align: center;
+  }
+}
+
+.dialog-footer {
+  text-align: right;
+}
+
+.el-color-picker {
+  width: 100%;
+}
+
+/* 右键菜单样式 */
+.context-menu {
+  position: fixed;
+  background: white;
+  border-radius: 4px;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+  padding: 8px 0;
+  z-index: 3000;
+  min-width: 120px;
+}
+
+.context-menu .menu-item {
+  padding: 8px 16px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  transition: background-color 0.3s;
+}
+
+.context-menu .menu-item:hover {
+  background-color: #f5f7fa;
+}
+
+.context-menu .menu-item i {
+  margin-right: 8px;
+  font-size: 16px;
+}
+
+.context-menu .menu-item span {
+  font-size: 14px;
+}
+
+/* 数据源图标样式 */
+.el-select-dropdown__item i {
+  margin-right: 8px;
+  font-size: 16px;
+}
+
+/* 调整表单中的数字输入框样式 */
+.el-input-number {
+  width: 100%;
+}
+
+/* 调整表单项间距 */
+.el-form-item {
+  margin-bottom: 18px;
+}
+
+/* 添加应用卡片样式 */
+.add-card {
+  height: auto;
+  min-height: 180px;
+  padding: 24px;
+  background: #fff;
+  border-radius: 8px;
+  cursor: pointer;
+  transition: all 0.3s;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  border: 2px dashed #e8e8e8;
+}
+
+.add-card:hover {
+  border-color: #409EFF;
+  background: #f5f7fa;
+}
+
+.add-icon {
+  font-size: 40px;
+  color: #bfbfbf;
+  margin-bottom: 12px;
+  transition: all 0.3s;
+}
+
+.add-card:hover .add-icon {
+  color: #409EFF;
+  transform: scale(1.1);
+}
+
+.add-text {
+  font-size: 15px;
+  color: #8c8c8c;
+  transition: all 0.3s;
+}
+
+.add-card:hover .add-text {
+  color: #409EFF;
+}
+.app-platform {
+  display: flex;
+  flex-direction: column;
+  min-height: 100vh;
+  background: #f0f2f5;
+  padding: 20px 24px;
+}
+
+.platform-content {
+  background: white;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0,0,0,0.05);
+  padding: 24px;
+  flex: 1 1 0;
+  min-height: 0;
+  display: flex;
+  flex-direction: column;
+}
+.pagination-container {
+  margin-top: auto;
+  align-self: flex-end;
+  text-align: right;
+  background: white;
+  border-top: 1px solid #f0f0f0;
+  padding: 24px 0 0 0; /* 顶部留间距,底部不留白 */
+}
+</style>

+ 10 - 0
zkqy-ui/src/views/login.vue

@@ -135,6 +135,7 @@ export default {
       // 注册开关
       register: false,
       redirect: undefined,
+      machineCode:null,
       // 页面配置数据
       config: {},
     };
@@ -183,7 +184,16 @@ export default {
             if (res.data.loginPageConfiguration != null) {
               // 得到租户的信息
               this.config = res.data.loginPageConfiguration || {};
+              console.log("res.data.machineCode",res.data.machineCode)
+              if(res.data.machineCode=="请联系管理员激活租户"){
+                this.$modal.msgWarning(res.data.machineCode);
+              }
               this.setConfig();
+            }else if(res.data.machineCode!=null) {
+              console.log("res.data.machineCode",res.data.machineCode)
+              if(res.data.machineCode=="请联系管理员激活租户"){
+                this.$modal.msgWarning(res.data.machineCode);
+              }
             }
           } else {
             console.log("租户有问题!");

+ 85 - 14
zkqy-ui/src/views/system/tenant/index.vue

@@ -81,7 +81,7 @@
           size="mini"
           @click="handleExport"
           v-hasPermi="['system:tenant:export']"
-          >导出{{$t('normal.export')}}
+          >{{$t('normal.export')}}
         </el-button>
         <ExcelDownLoad
           v-else
@@ -244,10 +244,10 @@
     />
 
     <!-- 添加或修改租户信息对话框 -->
-    <el-dialog :title="title"  :visible.sync="open" width="800px" append-to-body>
+    <el-dialog :title="title" :visible.sync="open" width="900px" append-to-body>
       <el-row>
         <el-form ref="form" :model="form" :rules="rules" label-width="120px">
-          <el-col :span="12">
+          <el-col :span="11">
             <el-form-item :label="$t('tenant.selectParentTenant')" prop="tenantParentId">
               <el-cascader
                 v-model="form.tenantParentId"
@@ -260,11 +260,11 @@
                 clearable
                 filterable
                 @change="changeTenantParentId"
-                style="width: 260px"
+                style="width: 274px"
               ></el-cascader>
             </el-form-item>
             <el-form-item :label="$t('tenant.tenantProfession')" prop="tenantProfession" >
-              <el-select v-model="form.tenantProfession" :placeholder="$t('tenant.pleaseSelectTenantProfession')" style="width: 260px">
+              <el-select v-model="form.tenantProfession" :placeholder="$t('tenant.pleaseSelectTenantProfession')"   style="width: 274px">
                 <el-option
                   v-for="item in tenantProfessionList"
                   :key="item.id"
@@ -288,7 +288,7 @@
             </el-form-item>
           </el-col>
 
-          <el-col :span="12">
+          <el-col :span="11">
             <el-form-item :label="$t('tenant.address')" prop="address">
               <el-input v-model="form.address" :placeholder="$t('tenant.pleaseEnterAddress')" />
             </el-form-item>
@@ -298,8 +298,16 @@
             <el-form-item :label="$t('tenant.owner')" prop="owner">
               <el-input v-model="form.owner" :placeholder="$t('tenant.pleaseEnterOwner')" />
             </el-form-item>
+            <el-form-item :label="$t('tenant.machineCode')" prop="machineCode">
+              <el-input v-model="form.machineCode" :placeholder="$t('tenant.machineCode')">
+                <el-button slot="append" icon="el-icon-refresh" @click="getMacAddress" :loading="macLoading">
+                  <el-tooltip class="item" effect="dark" content="从后端获取MAC地址" placement="top">
+                    <span>获取</span>
+                  </el-tooltip>
+                </el-button>
+              </el-input>
+            </el-form-item>
           </el-col>
-
         </el-form>
       </el-row>
       <div slot="footer" class="dialog-footer">
@@ -550,7 +558,7 @@
       append-to-body
     >
       <el-form ref="loginFormCloneRef" :model="loginFormClone" :rules="rulesClone" label-width="100px" >
-        <el-select v-model="loginFormClone.tenantId" filterable placeholder="请选择被克隆菜单的租户"  style="width: 364px">
+        <el-select v-model="loginFormClone.clonedTenantId" filterable placeholder="请选择被克隆菜单的租户"  style="width: 364px">
           <el-option
             v-for="item in allTenantInfo"
             :key="item.tenantId"
@@ -561,8 +569,8 @@
       </el-form>
 
       <div slot="footer" class="dialog-footer">
-        <el-button type="primary" @click="submitLoginForm">{{$t('normal.confirm')}}</el-button>
-        <el-button @click="cancelLoginForm">{{$t('normal.cancel')}}</el-button>
+        <el-button type="primary" @click="submitCloneTenantInfo">{{$t('normal.confirm')}}</el-button>
+        <el-button @click="cancelCloenQx">{{$t('normal.cancel')}}</el-button>
       </div>
     </el-dialog>
   </div>
@@ -582,7 +590,10 @@ import {
   activationOperation,
   getTenantChildrenInfo,
   getTenantAllList,
-  tenantTree, commonUpload,
+  tenantTree,
+  getLocalMachineCode,
+  commonUpload,
+  cloning
 } from "@/api/system/tenant";
 import { getDataSourceInfo, insertDataSource } from "@/api/system/data";
 
@@ -615,8 +626,8 @@ export default {
       loginFormCloneOpenTitle:"克隆租户菜单",
       loginFormCloneOpen:false,
       loginFormClone:{
-        tenantName:"",
-        tenantId:"",
+        currentTenantId:"",
+        clonedTenantId:"",
       },
       // 表单校验
       rulesClone: {
@@ -702,6 +713,7 @@ export default {
         owner: null,
         contactInfo: null,
         address: null,
+        machineCode:null,
       },
       // 表单校验
       rules: {
@@ -776,7 +788,8 @@ export default {
       // 已存在的数据库名称
       databaseNameList: [],
       //克隆租户查询所有租户信息
-      allTenantInfo:[]
+      allTenantInfo:[],
+      macLoading: false, // 添加loading状态
     };
   },
   computed: {
@@ -793,9 +806,28 @@ export default {
     this.selectAllUser();
   },
   methods: {
+    async submitCloneTenantInfo(){
+      console.log("abc");
+      console.log("克隆租户功能")
+      // loginFormClone
+      console.log(this.loginFormClone)
+      let res = await cloning(this.loginFormClone);
+      if (res.code == 200) {
+        this.$message.success(`租户克隆成功`);
+        this.loginFormCloneOpen=false;
+      } else {
+        this.$message.error(`租户克隆失败,请检查网络`);
+      }
+    },
+    cancelCloenQx(){
+      this.loginFormCloneOpen=false;
+    },
     async  tenantCloneMenu(row){
       //查询所有租户信息
       let response = await getTenantAllList();
+      console.log(row,"当前选择行");
+      this.loginFormClone.currentTenantId=row.tenantId;
+      console.log(this.loginFormClone);
       //克隆警告提示
       console.log("当前租户克隆菜单",row)
       //查询所有租户信息
@@ -1196,6 +1228,7 @@ export default {
           tempArr = [0];
         }
         this.form.tenantParentId = tempArr;
+        this.form.tenantProfession=parseInt(this.form.tenantProfession);
         this.open = true;
         this.title = this.$t('tenant.titles.modifyTenant');
       });
@@ -1429,6 +1462,44 @@ export default {
         tenantExpirationTime: null,
       };
     },
+    // 获取MAC地址方法
+    async getMacAddress() {
+      try {
+        this.macLoading = true;
+        const res = await getLocalMachineCode();
+        if (res.code === 200) {
+          this.form.machineCode = res.data;
+          this.$message.success(this.$t('tenant.messages.getMachineCodeSuccess'));
+        } else {
+          this.$message.error("获取失败"|| this.$t('tenant.messages.getMachineCodeFailed'));
+        }
+      } catch (error) {
+        console.error('获取机器码失败:', error);
+        this.$message.error(this.$t('tenant.messages.getMachineCodeFailed'));
+      } finally {
+        this.macLoading = false;
+      }
+    },
   },
 };
 </script>
+
+<style lang="scss" scoped>
+.tenant-dialog {
+  .tenant-form {
+    padding: 20px;
+
+    .el-form-item {
+      margin-bottom: 22px;
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+    }
+
+    .el-textarea {
+      font-family: inherit;
+    }
+  }
+}
+</style>