LIJIAN 1 lună în urmă
părinte
comite
e7052d2383

+ 12 - 0
ema-admin/src/main/java/com/ema/admin/modules/system/controller/SysFileController.java

@@ -3,10 +3,12 @@ package com.ema.admin.modules.system.controller;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.io.FileUtil;
+import com.mybatisflex.core.paginate.Page;
 import com.ema.admin.modules.system.converter.SysFileConverter;
 import com.ema.admin.modules.system.entity.SysFile;
 import com.ema.admin.modules.system.service.SysAuthService;
 import com.ema.admin.modules.system.service.SysFileService;
+import com.ema.admin.modules.system.vo.SysFileQueryVo;
 import com.ema.common.exceptions.MyAssert;
 import com.ema.common.utils.CodeUtil;
 import com.ema.common.aspect.logger.LogTypeEnum;
@@ -21,6 +23,7 @@ import org.dromara.x.file.storage.core.FileStorageService;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.multipart.MultipartFile;
 
@@ -105,5 +108,14 @@ public class SysFileController {
         fileStorageService.delete(url);
     }
 
+    /**
+     * 文件分页列表
+     */
+    @ApiOperation(value = "文件分页列表")
+    @PostMapping("/file/page")
+    public Page<SysFile> page(@RequestBody SysFileQueryVo queryVo) {
+        return sysFileService.page(queryVo);
+    }
+
 
 }

+ 7 - 0
ema-admin/src/main/java/com/ema/admin/modules/system/service/SysFileService.java

@@ -1,9 +1,12 @@
 package com.ema.admin.modules.system.service;
 
+import com.mybatisflex.core.paginate.Page;
 import com.mybatisflex.core.service.IService;
 import com.ema.admin.modules.system.entity.SysFile;
+import com.ema.admin.modules.system.vo.SysFileQueryVo;
 
 import javax.validation.constraints.NotBlank;
+import java.util.List;
 
 /**
  * 系统文件信息表 服务层。
@@ -14,4 +17,8 @@ import javax.validation.constraints.NotBlank;
 public interface SysFileService extends IService<SysFile> {
 
     SysFile getByUrl(@NotBlank String url);
+
+    List<SysFile> list();
+
+    Page<SysFile> page(SysFileQueryVo queryVo);
 }

+ 3 - 1
ema-admin/src/main/java/com/ema/admin/modules/system/service/impl/SysAuthServiceImpl.java

@@ -89,7 +89,7 @@ public class SysAuthServiceImpl implements SysAuthService {
         StpUtil.login(userVo.getId());
         //获取登录后的token
         userVo.setToken(StpUtil.getTokenValue());
-        StpUtil.getSession().set(userVo.getToken(), userVo); // 缓存 userVo 对象
+
         SysDepartment dept = sysDepartmentService.getById(sysUser.getDeptId());
         userVo.setDeptName(dept != null ? dept.getDeptName() : null);
 
@@ -103,6 +103,8 @@ public class SysAuthServiceImpl implements SysAuthService {
         userVo.setPermsKeys(sysMenus.stream().map(SysMenu::getPerms).filter(StrUtil::isNotBlank).distinct().collect(Collectors.toList()));
         StpUtil.getSession().set(ROLE + userVo.getToken(), sysRoles);//缓存角色信息
         StpUtil.getSession().set(MENU + userVo.getToken(), sysMenus);
+
+        StpUtil.getSession().set(userVo.getToken(), userVo); // 缓存 userVo 对象
         return userVo;
     }
 

+ 20 - 0
ema-admin/src/main/java/com/ema/admin/modules/system/service/impl/SysFileServiceImpl.java

@@ -1,11 +1,17 @@
 package com.ema.admin.modules.system.service.impl;
 
+import cn.hutool.core.util.StrUtil;
+import com.mybatisflex.core.paginate.Page;
+import com.mybatisflex.core.query.QueryWrapper;
 import com.mybatisflex.spring.service.impl.ServiceImpl;
 import com.ema.admin.modules.system.entity.SysFile;
 import com.ema.admin.modules.system.mapper.SysFileMapper;
 import com.ema.admin.modules.system.service.SysFileService;
+import com.ema.admin.modules.system.vo.SysFileQueryVo;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
+
 /**
  * 系统文件信息表 服务层实现。
  *
@@ -19,4 +25,18 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFile>  imp
     public SysFile getByUrl(String url) {
         return this.queryChain().eq(SysFile::getUrl, url).one();
     }
+
+    @Override
+    public List<SysFile> list() {
+        return this.queryChain().orderBy(SysFile::getCreateTime,false).list();
+    }
+
+    @Override
+    public Page<SysFile> page(SysFileQueryVo queryVo) {
+        QueryWrapper queryWrapper = QueryWrapper.create();
+        queryWrapper.like(SysFile::getOriginalFilename, queryVo.getFileName(), StrUtil.isNotBlank(queryVo.getFileName()));
+        queryWrapper.eq(SysFile::getExt, queryVo.getFileType(), StrUtil.isNotBlank(queryVo.getFileType()));
+        queryWrapper.orderBy(SysFile::getCreateTime, false);
+        return this.page(new Page<>(queryVo.getPageNumber(), queryVo.getPageSize()), queryWrapper);
+    }
 }

+ 51 - 0
ema-admin/src/main/java/com/ema/admin/modules/system/vo/SysFileQueryVo.java

@@ -0,0 +1,51 @@
+package com.ema.admin.modules.system.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 文件查询条件 VO。
+ *
+ * @author LIJIAN
+ * @since 2026-05-19
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@ApiModel("文件查询条件")
+public class SysFileQueryVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 当前页。
+     */
+    @ApiModelProperty("当前页")
+    private long pageNumber = 1;
+
+    /**
+     * 每页数据数量。
+     */
+    @ApiModelProperty("每页数据数量")
+    private long pageSize = 12;
+
+    /**
+     * 文件名(支持模糊查询)
+     */
+    @ApiModelProperty("文件名")
+    private String fileName;
+
+    /**
+     * 文件类型
+     */
+    @ApiModelProperty("文件类型")
+    private String fileType;
+
+}

+ 1 - 1
ema-admin/src/main/resources/web/css/main.css

@@ -241,7 +241,7 @@ body {
 
 /* 子菜单 */
 .layui-nav-child {
-    background: transparent !important;
+    background: #ffffff !important;
     padding: 0;
 }
 

+ 120 - 0
ema-admin/src/main/resources/web/js/config.js

@@ -83,6 +83,8 @@ var Config = {
 
         // 文件管理
         file: {
+            list: '/file/list',
+            page: '/file/page',
             upload: '/upload/file',
             batchUpload: '/upload/batch',
             download: '/download',
@@ -126,6 +128,124 @@ var Config = {
             update: '/assetStatusHistory/update',
             remove: '/assetStatusHistory/remove',
             getInfo: '/assetStatusHistory/getInfo'
+        },
+
+        // AI模块
+        ai: {
+            // AI模型配置
+            model: {
+                list: '/ai/model/list',
+                page: '/ai/model/page',
+                save: '/ai/model/save',
+                update: '/ai/model/update',
+                remove: '/ai/model/remove',
+                getInfo: '/ai/model/getInfo',
+                getDefault: '/ai/model/getDefault',
+                setDefault: '/ai/model/setDefault',
+                enable: '/ai/model/enable',
+                disable: '/ai/model/disable'
+            },
+
+            // AI密钥管理
+            apikey: {
+                list: '/ai/apikey/list',
+                page: '/ai/apikey/page',
+                save: '/ai/apikey/save',
+                update: '/ai/apikey/update',
+                remove: '/ai/apikey/remove',
+                getInfo: '/ai/apikey/getInfo',
+                setDefault: '/ai/apikey/setDefault',
+                enable: '/ai/apikey/enable',
+                disable: '/ai/apikey/disable',
+                test: '/ai/apikey/test'
+            },
+
+            // AI应用
+            application: {
+                list: '/ai/application/list',
+                listEnabled: '/ai/application/listEnabled',
+                page: '/ai/application/page',
+                save: '/ai/application/save',
+                update: '/ai/application/update',
+                remove: '/ai/application/remove',
+                getInfo: '/ai/application/getInfo',
+                getByCode: '/ai/application/getByCode',
+                enable: '/ai/application/enable',
+                disable: '/ai/application/disable'
+            },
+
+            // AI对话
+            chat: {
+                send: '/ai/chat/send',
+                sendStream: '/ai/chat/sendStream',
+                conversation: {
+                    create: '/ai/chat/conversation/create',
+                    messages: '/ai/chat/conversation/messages',
+                    list: '/ai/chat/conversation/list',
+                    delete: '/ai/chat/conversation/delete',
+                    archive: '/ai/chat/conversation/archive',
+                    pin: '/ai/chat/conversation/pin'
+                }
+            },
+
+            // 提示词模板
+            prompt: {
+                list: '/ai/prompt/list',
+                listByType: '/ai/prompt/listByType',
+                save: '/ai/prompt/save',
+                update: '/ai/prompt/update',
+                remove: '/ai/prompt/remove',
+                getInfo: '/ai/prompt/getInfo',
+                copy: '/ai/prompt/copy'
+            },
+
+            // 知识库
+            knowledge: {
+                list: '/ai/knowledge/list',
+                page: '/ai/knowledge/page',
+                save: '/ai/knowledge/save',
+                update: '/ai/knowledge/update',
+                remove: '/ai/knowledge/remove',
+                getInfo: '/ai/knowledge/getInfo',
+                sync: '/ai/knowledge/sync',
+                documents: '/ai/knowledge/documents',
+                documentUpload: '/ai/knowledge/document/upload',
+                documentDelete: '/ai/knowledge/document'
+            },
+
+            // AI技能
+            skill: {
+                list: '/ai/skill/list',
+                listEnabled: '/ai/skill/listEnabled',
+                page: '/ai/skill/page',
+                save: '/ai/skill/save',
+                update: '/ai/skill/update',
+                remove: '/ai/skill/remove',
+                getInfo: '/ai/skill/getInfo',
+                test: '/ai/skill/test'
+            },
+
+            // AI工作流
+            workflow: {
+                list: '/ai/workflow/list',
+                page: '/ai/workflow/page',
+                save: '/ai/workflow/save',
+                update: '/ai/workflow/update',
+                remove: '/ai/workflow/remove',
+                getInfo: '/ai/workflow/getInfo',
+                run: '/ai/workflow/run',
+                runs: '/ai/workflow/runs',
+                enable: '/ai/workflow/enable',
+                disable: '/ai/workflow/disable'
+            },
+
+            // 使用统计
+            usage: {
+                statistics: '/ai/usage/statistics',
+                logList: '/ai/usage/log/list',
+                logDetail: '/ai/usage/log',
+                trend: '/ai/usage/trend'
+            }
         }
     },
 

+ 96 - 35
ema-admin/src/main/resources/web/pages/file/file_list.html

@@ -24,14 +24,30 @@
     <div class="upload-area" id="uploadArea">
         <i class="layui-icon layui-icon-upload-drag"></i>
         <p>点击上传文件或将文件拖拽到此处</p>
-        <p style="font-size: 12px; margin-top: 5px;">支持 jpg、png、gif、pdf、doc、docx、xls、xlsx 等格式</p>
+        <p style="font-size: 12px; margin-top: 5px;">支持 jpg、png、gif、pdf、doc、docx、xls、xlsx、zip、rar 等格式</p>
     </div>
     
     <div class="layui-form" style="margin-bottom: 15px;">
         <div class="layui-inline">
-            <input type="text" name="keyword" placeholder="搜索文件名" class="layui-input">
+            <select name="fileType" id="fileTypeSelect" class="layui-select" style="width: 120px;">
+                <option value="">全部类型</option>
+                <option value="jpg">JPG</option>
+                <option value="png">PNG</option>
+                <option value="gif">GIF</option>
+                <option value="pdf">PDF</option>
+                <option value="doc">DOC</option>
+                <option value="docx">DOCX</option>
+                <option value="xls">XLS</option>
+                <option value="xlsx">XLSX</option>
+                <option value="zip">ZIP</option>
+                <option value="rar">RAR</option>
+            </select>
         </div>
-        <button type="button" class="layui-btn" id="btnSearch"><i class="layui-icon layui-icon-search"></i></button>
+        <div class="layui-inline">
+            <input type="text" name="keyword" id="keywordInput" placeholder="搜索文件名" class="layui-input">
+        </div>
+        <button type="button" class="layui-btn" id="btnSearch"><i class="layui-icon layui-icon-search"></i> 搜索</button>
+        <button type="button" class="layui-btn layui-btn-normal" id="btnReset"><i class="layui-icon layui-icon-refresh"></i> 重置</button>
     </div>
     
     <div class="file-list" id="fileList">
@@ -45,20 +61,45 @@
     <script src="../../js/config.js"></script>
     <script src="../../js/common.js"></script>
     <script>
-        layui.use(['layer', 'upload', 'laypage'], function() {
+        layui.use(['layer', 'upload', 'laypage', 'form'], function() {
             var layer = layui.layer;
             var upload = layui.upload;
             var laypage = layui.laypage;
+            var form = layui.form;
             var $ = layui.jquery;
             
-            var files = [];
             var currentPage = 1;
             var pageSize = 12;
+            var total = 0;
+            
+            // 加载文件列表
+            function loadFileList() {
+                var params = {
+                    pageNumber: currentPage,
+                    pageSize: pageSize,
+                    fileName: $('#keywordInput').val(),
+                    fileType: $('#fileTypeSelect').val()
+                };
+                
+                Common.ajax({
+                    url: Config.api.file.page,
+                    type: 'POST',
+                    data: params,
+                    success: function(res) {
+                        // 兼容直接返回 Page 对象和包装格式
+                        var data = res.data || res;
+                        total = data.totalRow || data.total || 0;
+                        renderFileList(data.records || data.list || []);
+                        renderPagination();
+                    }
+                });
+            }
             
             // 上传组件
             upload.render({
                 elem: '#uploadArea',
                 url: Config.getApiUrl(Config.api.file.upload),
+                accept: 'file',  // 支持所有文件类型
                 headers: function() {
                     var token = localStorage.getItem('token');
                     return token ? { 'Authorization': token } : {};
@@ -66,8 +107,7 @@
                 done: function(res) {
                     if (res.code === 200) {
                         Common.success('上传成功');
-                        files.unshift(res.data);
-                        renderFileList();
+                        loadFileList();
                     } else {
                         Common.error(res.msg || '上传失败');
                     }
@@ -79,36 +119,45 @@
             
             // 搜索
             $('#btnSearch').click(function() {
-                var keyword = $('input[name="keyword"]').val().toLowerCase();
-                renderFileList(keyword);
+                currentPage = 1;
+                loadFileList();
             });
             
-            // 渲染文件列表
-            function renderFileList(keyword) {
-                var filtered = files;
-                if (keyword) {
-                    filtered = files.filter(function(f) {
-                        return f.originalName.toLowerCase().indexOf(keyword) !== -1;
-                    });
+            // 重置
+            $('#btnReset').click(function() {
+                $('#keywordInput').val('');
+                $('#fileTypeSelect').val('');
+                layui.form.render('select');
+                currentPage = 1;
+                loadFileList();
+            });
+            
+            // 回车搜索
+            $('#keywordInput').on('keypress', function(e) {
+                if (e.which === 13) {
+                    currentPage = 1;
+                    loadFileList();
                 }
-                
-                var start = (currentPage - 1) * pageSize;
-                var end = start + pageSize;
-                var pageData = filtered.slice(start, end);
-                
+            });
+            
+            // 渲染文件列表
+            function renderFileList(files) {
                 var html = '';
-                pageData.forEach(function(file) {
-                    var isImage = /\.(jpg|jpeg|png|gif|bmp)$/i.test(file.originalName);
+                files.forEach(function(file) {
+                    var fileName = file.originalFilename || file.filename || '';
+                    var fileUrl = file.url || '';
+                    var isImage = /\.(jpg|jpeg|png|gif|bmp)$/i.test(fileName);
                     var preview = isImage 
-                        ? '<img src="' + file.url + '" alt="' + file.originalName + '">' 
+                        ? '<img src="' + fileUrl + '" alt="' + fileName + '">' 
                         : '<div style="height:120px; display:flex; align-items:center; justify-content:center; background:#f5f5f5; border-radius:4px;"><i class="layui-icon layui-icon-file-b" style="font-size:48px; color:#999;"></i></div>';
                     
                     html += '<div class="file-item">';
                     html += preview;
-                    html += '<div class="file-name" title="' + file.originalName + '">' + file.originalName + '</div>';
+                    html += '<div class="file-name" title="' + fileName + '">' + fileName + '</div>';
                     html += '<div class="file-actions">';
-                    html += '<a href="javascript:;" onclick="copyUrl(\'' + file.url + '\')">复制链接</a>';
-                    html += '<a href="javascript:;" onclick="deleteFile(\'' + file.url + '\', this)">删除</a>';
+                    html += '<a href="javascript:;" onclick="downloadFile(\'' + fileUrl + '\', \'' + fileName + '\')">下载</a>';
+                    html += '<a href="javascript:;" onclick="copyUrl(\'' + fileUrl + '\')">复制链接</a>';
+                    html += '<a href="javascript:;" onclick="deleteFile(\'' + fileUrl + '\', this)">删除</a>';
                     html += '</div></div>';
                 });
                 
@@ -117,25 +166,33 @@
                 }
                 
                 $('#fileList').html(html);
-                
-                // 渲染分页
+            }
+            
+            // 渲染分页
+            function renderPagination() {
                 laypage.render({
                     elem: 'pagination',
-                    count: filtered.length,
+                    count: total,
                     limit: pageSize,
                     curr: currentPage,
+                    layout: ['count', 'prev', 'page', 'next', 'limit', 'skip'],
+                    limits: [12, 24, 48, 96],
                     jump: function(obj, first) {
                         if (!first) {
                             currentPage = obj.curr;
-                            renderFileList(keyword);
+                            pageSize = obj.limit;
+                            loadFileList();
                         }
                     }
                 });
             }
             
+            // 初始化加载
+            loadFileList();
+            
             // 复制链接
             window.copyUrl = function(url) {
-                var fullUrl = Config.baseUrl + url;
+                var fullUrl = url.startsWith('http') ? url : Config.baseUrl + url;
                 var input = document.createElement('input');
                 input.value = fullUrl;
                 document.body.appendChild(input);
@@ -145,19 +202,23 @@
                 Common.success('链接已复制到剪贴板');
             };
             
+            // 下载文件(新标签页打开)
+            window.downloadFile = function(url, fileName) {
+                var fullUrl = url.startsWith('http') ? url : Config.baseUrl + url;
+                window.open(fullUrl, '_blank');
+            };
+            
             // 删除文件
             window.deleteFile = function(url, el) {
                 Common.confirm('确定要删除这个文件吗?', function() {
                     Common.get(Config.api.file.delete + '?url=' + encodeURIComponent(url), function(res) {
                         Common.success('删除成功', function() {
                             $(el).closest('.file-item').remove();
+                            loadFileList();
                         });
                     });
                 });
             };
-            
-            // 初始化
-            renderFileList();
         });
     </script>
 </body>

+ 2 - 9
ema-admin/src/main/resources/web/pages/job/job_form.html

@@ -18,14 +18,7 @@
         <div class="layui-form-item">
             <label class="layui-form-label"><span class="required">*</span>任务名称</label>
             <div class="layui-input-block">
-                <input type="text" name="jobName" lay-verify="required" placeholder="请输入任务名称" class="layui-input">
-            </div>
-        </div>
-        
-        <div class="layui-form-item">
-            <label class="layui-form-label">任务分组</label>
-            <div class="layui-input-block">
-                <input type="text" name="jobGroup" placeholder="请输入任务分组" class="layui-input">
+                <input type="text" name="timerName" lay-verify="required" placeholder="请输入任务名称" class="layui-input">
             </div>
         </div>
         
@@ -51,7 +44,7 @@
         <div class="layui-form-item">
             <label class="layui-form-label"><span class="required">*</span>任务状态</label>
             <div class="layui-input-block">
-                <input type="radio" name="jobStatus" value="0" title="停止" checked>
+                <input type="radio" name="jobStatus" value="2" title="停止" checked>
                 <input type="radio" name="jobStatus" value="1" title="运行">
             </div>
         </div>

+ 18 - 11
ema-admin/src/main/resources/web/pages/job/job_list.html

@@ -25,8 +25,9 @@
                     <div class="layui-input-inline">
                         <select name="jobStatus" class="layui-select">
                             <option value="">全部</option>
-                            <option value="0">停止</option>
                             <option value="1">运行中</option>
+                            <option value="2">已停止</option>
+                            <option value="3">异常</option>
                         </select>
                     </div>
                 </div>
@@ -44,11 +45,14 @@
     
     <table id="tableList" lay-filter="tableList"></table>
     
+    <!-- 分页组件 -->
+    <div id="pagination" style="text-align: center; padding: 10px;"></div>
+    
     <script type="text/html" id="toolbar">
-        {{# if(d.jobStatus === '0') { }}
-            <a class="layui-btn layui-btn-xs layui-btn-normal" lay-event="start"><i class="layui-icon layui-icon-play"></i> 启动</a>
-        {{# } else { }}
+        {{# if(d.jobStatus === '1') { }}
             <a class="layui-btn layui-btn-xs layui-btn-warm" lay-event="stop"><i class="layui-icon layui-icon-pause"></i> 暂停</a>
+        {{# } else { }}
+            <a class="layui-btn layui-btn-xs layui-btn-normal" lay-event="start"><i class="layui-icon layui-icon-play"></i> 启动</a>
         {{# } }}
         <a class="layui-btn layui-btn-xs" lay-event="edit"><i class="layui-icon layui-icon-edit"></i> 编辑</a>
         <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del"><i class="layui-icon layui-icon-delete"></i> 删除</a>
@@ -57,8 +61,10 @@
     <script type="text/html" id="statusTpl">
         {{# if(d.jobStatus === '1') { }}
             <span class="layui-badge layui-bg-green">运行中</span>
-        {{# } else { }}
+        {{# } else if(d.jobStatus === '2') { }}
             <span class="layui-badge layui-bg-gray">已停止</span>
+        {{# } else { }}
+            <span class="layui-badge layui-bg-red">异常</span>
         {{# } }}
     </script>
     
@@ -75,15 +81,16 @@
             var tableIns = TableRender.init({
                 elem: '#tableList',
                 url: Config.api.timersJob.page,
+                page: { curr: 1, limit: 10, limits: [10, 20, 30, 50] },
                 cols: [[
-                    { field: 'jobName', title: '任务名称', width: 180 },
-                    { field: 'jobGroup', title: '任务分组', width: 120 },
+                    { field: 'timerName', title: '任务名称', width: 180 },
                     { field: 'actionClass', title: '执行类', width: 250 },
                     { field: 'cron', title: 'CRON表达式', width: 150 },
                     { field: 'jobStatus', title: '状态', width: 100, templet: '#statusTpl' },
+                    { field: 'lastTime', title: '上次执行', width: 170, templet: function(d) { return Common.formatDate(d.lastTime); } },
                     { field: 'createTime', title: '创建时间', width: 170, templet: function(d) { return Common.formatDate(d.createTime); } },
                     { field: 'remark', title: '备注' },
-                    { fixed: 'right', title: '操作', width: 220, align: 'center', toolbar: '#toolbar' }
+                    { fixed: 'right', title: '操作', width: 250, align: 'center', toolbar: '#toolbar' }
                 ]]
             });
             
@@ -107,7 +114,7 @@
                 var event = obj.event;
                 
                 if (event === 'start') {
-                    Common.confirm('确定要启动任务 【' + data.jobName + '】 吗?', function() {
+                    Common.confirm('确定要启动任务 【' + data.timerName + '】 吗?', function() {
                         Common.post(Config.api.timersJob.start + '/' + data.id, {}, function(res) {
                             Common.success('启动成功', function() {
                                 table.reload('tableList');
@@ -115,7 +122,7 @@
                         });
                     });
                 } else if (event === 'stop') {
-                    Common.confirm('确定要暂停任务 【' + data.jobName + '】 吗?', function() {
+                    Common.confirm('确定要暂停任务 【' + data.timerName + '】 吗?', function() {
                         Common.post(Config.api.timersJob.stop + '/' + data.id, {}, function(res) {
                             Common.success('暂停成功', function() {
                                 table.reload('tableList');
@@ -129,7 +136,7 @@
                         content: 'job_form.html?id=' + data.id
                     });
                 } else if (event === 'del') {
-                    Common.confirm('确定要删除任务 【' + data.jobName + '】 吗?', function() {
+                    Common.confirm('确定要删除任务 【' + data.timerName + '】 吗?', function() {
                         Common.del(Config.api.timersJob.remove + '/' + data.id, function(res) {
                             Common.success('删除成功', function() {
                                 obj.del();

+ 28 - 6
ema-admin/src/main/resources/web/pages/menu/menu_form.html

@@ -128,8 +128,18 @@
                             $('#iconInput').val(data.icon);
                             $('#iconPreview').addClass(data.icon);
                         }
+                        // 编辑时设置上级菜单选中
+                        if (data.parentId !== undefined && data.parentId !== null) {
+                            loadMenuTreeAndSetParent(data.parentId);
+                        }
                     }
                 });
+            } else if (parentId) {
+                loadMenuTree();
+                $('#parentSelect').val(parentId);
+                form.render('select');
+            } else {
+                loadMenuTree();
             }
             
             function loadMenuTree() {
@@ -144,12 +154,24 @@
                         });
                         $('#parentSelect').html(html);
                         form.render('select');
-                        
-                        // 设置上级菜单默认值(添加下级时)
-                        if (parentId) {
-                            $('#parentSelect').val(parentId);
-                            form.render('select');
-                        }
+                    }
+                });
+            }
+            
+            function loadMenuTreeAndSetParent(parentId) {
+                Common.get(Config.api.menu.list, function(res) {
+                    if (res.code === 200) {
+                        var menus = res.data || [];
+                        var html = '<option value="0">顶级菜单</option>';
+                        menus.forEach(function(menu) {
+                            if (menu.id !== id) {
+                                html += '<option value="' + menu.id + '">' + menu.menuName + '</option>';
+                            }
+                        });
+                        $('#parentSelect').html(html);
+                        // 设置上级菜单选中值
+                        $('#parentSelect').val(parentId);
+                        form.render('select');
                     }
                 });
             }