ys hai 2 semanas
pai
achega
7678bd1612

+ 3 - 0
yushu-backend/yushu-system/src/main/java/com/yushu/system/domain/SysFileShare.java

@@ -77,4 +77,7 @@ public class SysFileShare extends BaseEntity
     
     /** 文件列表(文件夹分享时使用,不映射到数据库) */
     private transient List<SysFileInfo> fileList;
+    
+    /** 文件夹列表(文件夹分享时使用,不映射到数据库) */
+    private transient List<SysFolder> folderList;
 }

+ 2 - 2
yushu-backend/yushu-system/src/main/java/com/yushu/system/service/ISysFileShareService.java

@@ -135,9 +135,9 @@ public interface ISysFileShareService
      * 
      * @param shareCode 分享码
      * @param folderId 文件夹ID
-     * @return 文件列表
+     * @return 包含文件列表和文件夹列表的Map
      */
-    public List<SysFileInfo> getShareFolderFiles(String shareCode, Long folderId);
+    public java.util.Map<String, Object> getShareFolderFiles(String shareCode, Long folderId);
     
     /**
      * 下载文件夹(打包成ZIP)

+ 44 - 13
yushu-backend/yushu-system/src/main/java/com/yushu/system/service/impl/SysFileShareServiceImpl.java

@@ -274,11 +274,19 @@ public class SysFileShareServiceImpl implements ISysFileShareService
         }
         else if ("1".equals(share.getResourceType()))
         {
-            // 文件夹分享 - 查询文件夹内的文件列表(不包含子文件夹的文件)
-            SysFileInfo query = new SysFileInfo();
-            query.setFolderId(share.getFolderId());
-            List<SysFileInfo> fileList = sysFileMapper.selectSysFileList(query);
+            // 文件夹分享 - 查询文件夹内的文件列表
+            SysFileInfo fileQuery = new SysFileInfo();
+            fileQuery.setFolderId(share.getFolderId());
+            fileQuery.setUserId(share.getUserId()); // 设置用户ID以匹配查询条件
+            List<SysFileInfo> fileList = sysFileMapper.selectSysFileList(fileQuery);
             share.setFileList(fileList);
+            
+            // 查询子文件夹列表
+            SysFolder folderQuery = new SysFolder();
+            folderQuery.setParentId(share.getFolderId());
+            folderQuery.setUserId(share.getUserId()); // 设置用户ID以匹配查询条件
+            List<SysFolder> folderList = sysFolderMapper.selectSysFolderList(folderQuery);
+            share.setFolderList(folderList);
         }
         
         return share;
@@ -493,7 +501,7 @@ public class SysFileShareServiceImpl implements ISysFileShareService
      * @return 文件列表
      */
     @Override
-    public List<SysFileInfo> getShareFolderFiles(String shareCode, Long folderId)
+    public java.util.Map<String, Object> getShareFolderFiles(String shareCode, Long folderId)
     {
         // 查询分享信息
         SysFileShare share = sysFileShareMapper.selectSysFileShareByShareCode(shareCode);
@@ -524,9 +532,23 @@ public class SysFileShareServiceImpl implements ISysFileShareService
         // 暂时简化处理,直接查询
         
         // 查询文件夹内的文件
-        SysFileInfo query = new SysFileInfo();
-        query.setFolderId(folderId);
-        return sysFileMapper.selectSysFileList(query);
+        SysFileInfo fileQuery = new SysFileInfo();
+        fileQuery.setFolderId(folderId);
+        fileQuery.setUserId(share.getUserId()); // 设置用户ID以匹配查询条件
+        List<SysFileInfo> fileList = sysFileMapper.selectSysFileList(fileQuery);
+        
+        // 查询子文件夹
+        SysFolder folderQuery = new SysFolder();
+        folderQuery.setParentId(folderId);
+        folderQuery.setUserId(share.getUserId()); // 设置用户ID以匹配查询条件
+        List<SysFolder> folderList = sysFolderMapper.selectSysFolderList(folderQuery);
+        
+        // 组装返回数据
+        java.util.Map<String, Object> result = new java.util.HashMap<>();
+        result.put("fileList", fileList);
+        result.put("folderList", folderList);
+        
+        return result;
     }
     
     /**
@@ -579,8 +601,8 @@ public class SysFileShareServiceImpl implements ISysFileShareService
         // 创建ZIP输出流
         try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream()))
         {
-            // 递归打包文件夹
-            packFolder(folderId, "", zos);
+            // 递归打包文件夹(传递userId)
+            packFolder(folderId, share.getUserId(), "", zos);
             zos.finish();
         }
         
@@ -591,11 +613,12 @@ public class SysFileShareServiceImpl implements ISysFileShareService
     /**
      * 递归打包文件夹
      */
-    private void packFolder(Long folderId, String parentPath, ZipOutputStream zos) throws Exception
+    private void packFolder(Long folderId, Long userId, String parentPath, ZipOutputStream zos) throws Exception
     {
         // 查询文件夹内的文件
         SysFileInfo fileQuery = new SysFileInfo();
         fileQuery.setFolderId(folderId);
+        fileQuery.setUserId(userId); // 设置userId以匹配查询条件
         List<SysFileInfo> files = sysFileMapper.selectSysFileList(fileQuery);
         
         // 获取文件存储路径
@@ -630,13 +653,21 @@ public class SysFileShareServiceImpl implements ISysFileShareService
         // 查询子文件夹
         SysFolder folderQuery = new SysFolder();
         folderQuery.setParentId(folderId);
+        folderQuery.setUserId(userId); // 设置userId以匹配查询条件
         List<SysFolder> subFolders = sysFolderMapper.selectSysFolderList(folderQuery);
         
         // 递归打包子文件夹
         for (SysFolder subFolder : subFolders)
         {
             String subPath = parentPath + subFolder.getFolderName() + "/";
-            packFolder(subFolder.getFolderId(), subPath, zos);
+            packFolder(subFolder.getFolderId(), userId, subPath, zos);
+        }
+        
+        // 如果文件夹为空,创建一个空目录条目
+        if (files.isEmpty() && subFolders.isEmpty() && !parentPath.isEmpty())
+        {
+            zos.putNextEntry(new ZipEntry(parentPath));
+            zos.closeEntry();
         }
     }
     
@@ -724,7 +755,7 @@ public class SysFileShareServiceImpl implements ISysFileShareService
                     if (folder != null)
                     {
                         String folderPath = folder.getFolderName() + "/";
-                        packFolder(folderId, folderPath, zos);
+                        packFolder(folderId, share.getUserId(), folderPath, zos);
                         fileCount++;
                     }
                 }

+ 20 - 0
yushu-uivue3/index.html

@@ -131,9 +131,29 @@
   </div>
 
   <script>
+    // 检查是否为分享页面,如果是则立即隐藏加载动画
+    (function() {
+      const isSharePage = window.location.pathname.startsWith('/share/');
+      if (isSharePage) {
+        const loader = document.querySelector('.app-loading');
+        const app = document.getElementById('app');
+        if (loader) {
+          loader.style.display = 'none';
+          loader.remove();
+        }
+        if (app) {
+          app.style.opacity = '1';
+          app.classList.add('app-loaded');
+        }
+        return; // 分享页面不执行星空动画
+      }
+    })();
+    
     // 沉浸式星空背景
     (function() {
       const canvas = document.getElementById('loading-canvas');
+      if (!canvas) return; // 如果 canvas 不存在(分享页面已移除),直接返回
+      
       const ctx = canvas.getContext('2d');
       let width, height;
       let stars = [];

+ 28 - 5
yushu-uivue3/src/App.vue

@@ -20,13 +20,16 @@ onMounted(() => {
       document.documentElement.classList.remove('dark')
     }
     
+    // 检查是否为分享页面
+    const isSharePage = window.location.pathname.startsWith('/share/')
+    
     // 移除 Loading 动画并显示 App
     const loader = document.querySelector('.app-loading')
     const app = document.getElementById('app')
     
     if (loader) {
-      // 强制延迟 1.5s 再开始淡出,让用户看清星空
-      setTimeout(() => {
+      if (isSharePage) {
+        // 分享页面立即移除加载动画,不延迟
         loader.style.opacity = '0'
         loader.style.visibility = 'hidden'
         
@@ -35,15 +38,35 @@ onMounted(() => {
           window.stopLoadingAnimation()
         }
         
-        // 动画结束后移除节点
+        // 立即移除节点
         setTimeout(() => {
           loader.remove()
-        }, 1200) // 对应 CSS 中的 1.2s transition
+        }, 100)
         
         if (app) {
           app.classList.add('app-loaded')
         }
-      }, 1500)
+      } else {
+        // 其他页面保持原有逻辑:强制延迟 1.5s 再开始淡出,让用户看清星空
+        setTimeout(() => {
+          loader.style.opacity = '0'
+          loader.style.visibility = 'hidden'
+          
+          // 停止星空动画,释放性能
+          if (window.stopLoadingAnimation) {
+            window.stopLoadingAnimation()
+          }
+          
+          // 动画结束后移除节点
+          setTimeout(() => {
+            loader.remove()
+          }, 1200) // 对应 CSS 中的 1.2s transition
+          
+          if (app) {
+            app.classList.add('app-loaded')
+          }
+        }, 1500)
+      }
     } else {
       // 如果没有 loader(比如热更新),直接显示 app
       if (app) {

+ 18 - 5
yushu-uivue3/src/permission.js

@@ -18,13 +18,20 @@ const isWhiteList = (path) => {
 }
 
 router.beforeEach((to, from, next) => {
-  NProgress.start()
+  // 分享页面不显示系统加载动画
+  const isSharePage = to.path.startsWith('/share/')
+  if (!isSharePage) {
+    NProgress.start()
+  }
+  
   if (getToken()) {
     to.meta.title && useSettingsStore().setTitle(to.meta.title)
     /* has token*/
     if (to.path === '/login') {
       next({ path: '/' })
-      NProgress.done()
+      if (!isSharePage) {
+        NProgress.done()
+      }
     } else if (isWhiteList(to.path)) {
       next()
     } else {
@@ -61,11 +68,17 @@ router.beforeEach((to, from, next) => {
       next()
     } else {
       next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
-      NProgress.done()
+      if (!isSharePage) {
+        NProgress.done()
+      }
     }
   }
 })
 
-router.afterEach(() => {
-  NProgress.done()
+router.afterEach((to) => {
+  // 分享页面不显示系统加载动画
+  const isSharePage = to.path.startsWith('/share/')
+  if (!isSharePage) {
+    NProgress.done()
+  }
 })

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 458 - 272
yushu-uivue3/src/views/system/file/index.vue


+ 546 - 407
yushu-uivue3/src/views/system/file/share.vue

@@ -1,165 +1,184 @@
 <template>
   <div class="share-page">
     <div class="share-container">
-      <div class="share-box" :class="{ 'full-width': verified && fileInfo && fileInfo.fileList }">
+      <transition name="fade" mode="out-in">
         <!-- 加载中 -->
-        <div v-if="loading" class="loading-box">
-          <LoadingOutlined :spin="true" :style="{ fontSize: '48px', color: '#1890ff' }" />
-          <p style="margin-top: 20px;">正在加载分享文件...</p>
+        <div v-if="loading" class="loading-box" key="loading">
+          <a-spin size="large" tip="正在加载分享..." />
         </div>
         
         <!-- 需要密码 -->
-        <div v-else-if="needPassword && !verified" class="password-box">
-          <div class="share-header">
-            <FolderOpenOutlined :style="{ fontSize: '64px', color: '#1890ff' }" />
-            <h1>文件分享</h1>
-            <p>请输入提取码访问,如无提取码请直接点击确定</p>
-          </div>
-          
-          <div class="password-input-box">
-            <a-input
-              v-model:value="password"
-              placeholder="请输入提取码"
-              size="large"
-              :maxlength="4"
-              class="password-input"
-              @pressEnter="verifyPassword"
-            />
-            <a-button type="primary" size="large" @click="verifyPassword" class="submit-btn">
-              访问文件
-            </a-button>
+        <div v-else-if="needPassword && !verified" class="password-box" key="password">
+          <div class="password-card">
+            <div class="icon-wrapper">
+              <FolderOpenOutlined />
+            </div>
+            <h1 class="title">文件分享</h1>
+            <p class="subtitle">请输入提取码访问,如无提取码请直接点击确定</p>
+            
+            <div class="input-group">
+              <a-input
+                v-model:value="password"
+                placeholder="请输入4位提取码"
+                size="large"
+                :maxlength="4"
+                class="code-input"
+                @pressEnter="verifyPassword"
+              >
+                <template #prefix>
+                  <LockOutlined style="color: #bfbfbf" />
+                </template>
+              </a-input>
+              <a-button type="primary" size="large" @click="verifyPassword" class="submit-btn" :loading="verifying">
+                访问文件
+              </a-button>
+            </div>
           </div>
         </div>
 
         <!-- 文件信息 -->
-        <div v-else-if="fileInfo" class="file-info-box">
-          <!-- 单文件分享 -->
-          <div v-if="!fileInfo.fileList">
-            <div class="file-icon">
-              <FileOutlined :style="{ fontSize: '80px', color: '#1890ff' }" />
-            </div>
-            <h2>{{ fileInfo.fileName }}</h2>
-            <div class="file-meta">
-              <span><FileOutlined /> {{ formatFileSize(fileInfo.fileSize) }}</span>
-              <span style="margin-left: 20px;"><ClockCircleOutlined /> {{ parseTime(fileInfo.createTime) }}</span>
+        <div v-else-if="fileInfo" class="content-box" key="content">
+          <!-- 头部信息 -->
+          <div class="content-header">
+            <div class="header-left">
+              <div class="file-icon">
+                <FolderOpenOutlined v-if="fileInfo.fileList" />
+                <FileOutlined v-else />
+              </div>
+              <div class="file-details">
+                <h2 class="file-name" :title="fileInfo.fileName">{{ fileInfo.fileName }}</h2>
+                <div class="file-meta">
+                  <span v-if="shareInfo.expireTime" class="meta-tag expire">
+                    <ClockCircleOutlined /> {{ parseTime(shareInfo.expireTime) }} 到期
+                  </span>
+                  <span class="meta-tag size" v-if="!fileInfo.fileList">
+                    {{ formatFileSize(fileInfo.fileSize) }}
+                  </span>
+                </div>
+              </div>
             </div>
-            <div class="action-buttons">
-              <a-button type="primary" @click="downloadFile">
+            <div class="header-right">
+              <a-button v-if="fileInfo.fileList" type="primary" @click="downloadAll">
+                <template #icon><DownloadOutlined /></template>
+                下载全部
+              </a-button>
+              <a-button v-else type="primary" size="large" @click="downloadFile">
                 <template #icon><DownloadOutlined /></template>
                 下载文件
               </a-button>
             </div>
           </div>
-          
-          <!-- 文件夹分享 -->
-          <div v-else class="folder-share">
-            <div class="folder-header">
-              <FolderOpenOutlined :style="{ fontSize: '48px', color: '#1890ff' }" />
-              <h2>{{ fileInfo.fileName }}</h2>
-              <p>共 {{ folderList.length }} 个文件夹,{{ fileInfo.fileList.length }} 个文件</p>
+
+          <!-- 单文件展示区 -->
+          <div v-if="!fileInfo.fileList" class="single-file-preview">
+            <div class="preview-icon">
+              <FileOutlined />
             </div>
-            
-            <!-- 工具栏 -->
-            <div class="toolbar">
-              <div class="toolbar-left">
-                <a-breadcrumb separator="/" v-if="breadcrumb.length > 0">
-                  <a-breadcrumb-item @click="goToFolder(-1)" style="cursor: pointer;">根目录</a-breadcrumb-item>
-                  <a-breadcrumb-item 
-                    v-for="(item, index) in breadcrumb" 
-                    :key="index"
-                    @click="goToFolder(index)"
-                    style="cursor: pointer;">
+            <p class="preview-tip">该文件暂不支持在线预览,请下载后查看</p>
+          </div>
+          
+          <!-- 文件夹列表区 -->
+          <div v-else class="folder-content">
+            <!-- 面包屑导航 -->
+            <div class="breadcrumb-bar">
+              <a-breadcrumb separator="/">
+                <a-breadcrumb-item>
+                  <a @click="goToRoot" :class="{ 'active': breadcrumb.length === 0 }">
+                    <HomeOutlined /> 根目录
+                  </a>
+                </a-breadcrumb-item>
+                <a-breadcrumb-item v-for="(item, index) in breadcrumb" :key="index">
+                  <a @click="goToFolder(index)" :class="{ 'active': index === breadcrumb.length - 1 }">
                     {{ item.folderName }}
-                  </a-breadcrumb-item>
-                </a-breadcrumb>
-                <span v-else style="color: #999;">根目录</span>
-              </div>
-              <div class="toolbar-right">
-                <a-button type="primary" @click="downloadAll">
-                  <template #icon><DownloadOutlined /></template>
-                  下载全部
-                </a-button>
-                <a-button 
-                  v-if="selectedItems.length > 0"
-                  @click="downloadSelected">
-                  <template #icon><DownloadOutlined /></template>
-                  下载选中 ({{ selectedItems.length }})
-                </a-button>
-              </div>
+                  </a>
+                </a-breadcrumb-item>
+              </a-breadcrumb>
+              <span class="file-count">共 {{ allItems.length }} 项</span>
             </div>
             
-            <!-- 文件列表 -->
-            <a-table 
-              :dataSource="allItems" 
-              style="width: 100%; margin-top: 20px;" 
-              :row-selection="{ selectedRowKeys: selectedRowKeys, onChange: handleSelectionChange }"
-              :pagination="false"
-              rowKey="id"
-            >
-              <a-table-column title="名称" dataIndex="name" key="name" :min-width="300">
-                <template #default="{ record }">
-                  <div v-if="record.isFolder" @click="enterFolder(record)" style="cursor: pointer; display: flex; align-items: center;">
-                    <FolderOutlined style="color: #1890ff; margin-right: 8px;" />
-                    <span style="color: #1890ff;">{{ record.folderName }}</span>
-                  </div>
-                  <div v-else style="display: flex; align-items: center;">
-                    <FileOutlined style="margin-right: 8px;" />
-                    <span>{{ record.fileName }}</span>
-                  </div>
-                </template>
-              </a-table-column>
-              <a-table-column title="大小" width="150">
-                <template #default="{ record }">
-                  <span v-if="!record.isFolder">{{ formatFileSize(record.fileSize) }}</span>
-                  <span v-else style="color: #999;">-</span>
-                </template>
-              </a-table-column>
-              <a-table-column title="上传时间" width="200">
-                <template #default="{ record }">
-                  {{ parseTime(record.createTime) }}
-                </template>
-              </a-table-column>
-              <a-table-column title="操作" width="120">
-                <template #default="{ record }">
-                  <a-button 
-                    type="link"
-                    @click="record.isFolder ? downloadFolder(record) : downloadFileById(record.fileId)">
-                    <template #icon><DownloadOutlined /></template>
-                    下载
-                  </a-button>
-                </template>
-              </a-table-column>
-            </a-table>
-          </div>
-          
-          <div class="share-info">
-            <p>
-              <WarningOutlined /> 
-              分享有效期至: {{ parseTime(shareInfo.expireTime) }}
-            </p>
+            <!-- 文件列表表格 -->
+            <div class="table-wrapper">
+              <a-table 
+                :dataSource="allItems" 
+                :pagination="false"
+                :childrenColumnName="null"
+                rowKey="id"
+                :scroll="{ y: 500 }"
+                size="middle"
+              >
+                <a-table-column title="名称" key="name" :width="400">
+                  <template #default="{ record }">
+                    <div class="name-cell" @click="handleItemClick(record)">
+                      <div class="icon">
+                        <FolderFilled v-if="record.isFolder" style="color: #ffc107; font-size: 20px;" />
+                        <FileTextOutlined v-else style="color: #8c8c8c; font-size: 18px;" />
+                      </div>
+                      <span class="text">{{ record.isFolder ? record.folderName : record.fileName }}</span>
+                    </div>
+                  </template>
+                </a-table-column>
+                <a-table-column title="大小" width="120" align="right">
+                  <template #default="{ record }">
+                    <span class="size-text">{{ record.isFolder ? '-' : formatFileSize(record.fileSize) }}</span>
+                  </template>
+                </a-table-column>
+                <a-table-column title="修改时间" width="180" align="right">
+                  <template #default="{ record }">
+                    <span class="time-text">{{ parseTime(record.createTime) }}</span>
+                  </template>
+                </a-table-column>
+                <a-table-column title="操作" width="100" align="center">
+                  <template #default="{ record }">
+                    <a-tooltip title="下载">
+                      <a-button type="text" shape="circle" @click.stop="downloadItem(record)">
+                        <DownloadOutlined />
+                      </a-button>
+                    </a-tooltip>
+                  </template>
+                </a-table-column>
+              </a-table>
+            </div>
           </div>
         </div>
 
         <!-- 错误提示 -->
-        <div v-else class="error-box">
-          <WarningOutlined :style="{ fontSize: '64px', color: '#f5222d' }" />
-          <h2>{{ errorMessage }}</h2>
-          <p>{{ errorDetail }}</p>
+        <div v-else class="error-box" key="error">
+          <div class="error-card">
+            <FrownOutlined class="error-icon" />
+            <h2>{{ errorMessage }}</h2>
+            <p>{{ errorDetail }}</p>
+            <a-button type="primary" @click="refreshPage">刷新页面</a-button>
+          </div>
         </div>
-      </div>
+      </transition>
+    </div>
+    
+    <!-- 底部版权 -->
+    <div class="footer">
+      <p>© {{ new Date().getFullYear() }} Yushu File Share. All rights reserved.</p>
     </div>
   </div>
 </template>
 
 <script setup name="FileShare">
-import { LoadingOutlined, FolderOpenOutlined, FileOutlined, ClockCircleOutlined, FolderOutlined, WarningOutlined, DownloadOutlined } from '@ant-design/icons-vue'
-import { verifyFileShare, getShareFolderFiles, listFolder } from '@/api/system/file'
+import { ref, computed, onMounted } from 'vue'
+import { useRoute } from 'vue-router'
+import { getCurrentInstance } from 'vue'
+import { Modal } from 'ant-design-vue'
+import { 
+  LoadingOutlined, FolderOpenOutlined, FileOutlined, FileTextOutlined,
+  ClockCircleOutlined, WarningOutlined, DownloadOutlined, LockOutlined,
+  HomeOutlined, FolderFilled, FrownOutlined
+} from '@ant-design/icons-vue'
+import { verifyFileShare, getShareFolderFiles } from '@/api/system/file'
 import { parseTime } from '@/utils/yushu'
 
 const { proxy } = getCurrentInstance()
 const route = useRoute()
 
+// 状态管理
 const loading = ref(true)
+const verifying = ref(false)
 const needPassword = ref(false)
 const verified = ref(false)
 const password = ref('')
@@ -171,29 +190,53 @@ const errorDetail = ref('')
 const currentFolderId = ref(null)
 const folderList = ref([])
 const breadcrumb = ref([])
-const selectedItems = ref([])
-const selectedRowKeys = ref([])
 
+// 计算属性:合并文件和文件夹
 const allItems = computed(() => {
-  const folders = folderList.value.map(folder => ({ ...folder, isFolder: true, id: `folder_${folder.folderId}` }))
-  const files = fileInfo.value?.fileList?.map(file => ({ ...file, isFolder: false, id: `file_${file.fileId}` })) || []
+  const folders = (folderList.value || []).map(folder => {
+    const item = {
+      ...folder,
+      isFolder: true,
+      id: `folder_${folder.folderId}`
+    }
+    // 删除 children 属性,防止表格显示展开按钮
+    delete item.children
+    return item
+  })
+  
+  const files = (fileInfo.value?.fileList || []).map(file => {
+    const item = {
+      ...file,
+      isFolder: false,
+      id: `file_${file.fileId}`
+    }
+    // 删除 children 属性
+    delete item.children
+    return item
+  })
+  
   return [...folders, ...files]
 })
 
+// 初始化
 onMounted(() => {
   shareCode.value = route.params.shareCode
   if (shareCode.value) {
     const code = route.query.code
     if (code) password.value = code
-    loading.value = false
-    needPassword.value = true
+    // 自动尝试验证
+    verifyPassword()
   } else {
-    showError('分享链接无效', '请检查链接是否正确')
+    showError('链接无效', '请检查您的分享链接是否正确')
   }
 })
 
-async function loadShare() {
+// 验证密码并加载数据
+async function verifyPassword() {
+  if (!shareCode.value) return
+  verifying.value = true
   loading.value = true
+  
   try {
     const response = await verifyFileShare(shareCode.value, password.value || '')
     
@@ -201,122 +244,155 @@ async function loadShare() {
       shareInfo.value = response.data
       
       if (response.data.resourceType === '0') {
+        // 单文件
         fileInfo.value = response.data.fileInfo
       } else {
+        // 文件夹
         currentFolderId.value = response.data.folderId
         fileInfo.value = {
           fileName: '文件夹分享',
           fileList: response.data.fileList || []
         }
-        loadFolderList(response.data.folderId)
+        folderList.value = response.data.folderList || []
       }
       
       verified.value = true
       needPassword.value = false
       if (password.value) proxy.$modal.msgSuccess('验证成功')
     } else {
-      proxy.$modal.msgError(response.msg || '验证失败')
-      needPassword.value = true
-      verified.value = false
+      // 需要密码或密码错误
+      if (response.msg.includes('提取码')) {
+        needPassword.value = true
+        verified.value = false
+        if (password.value) proxy.$modal.msgError('提取码错误')
+      } else {
+        showError('验证失败', response.msg)
+      }
     }
   } catch (error) {
-    proxy.$modal.msgError('加载失败,请重试')
-    needPassword.value = true
-    verified.value = false
+    console.error(error)
+    // 如果是401/403等错误,通常意味着需要密码或出错
+    if (!verified.value) {
+      needPassword.value = true
+    } else {
+      showError('加载失败', '请稍后重试')
+    }
   } finally {
     loading.value = false
+    verifying.value = false
   }
 }
 
-async function verifyPassword() {
-  await loadShare()
-}
-
-function downloadFile() {
-  if (!fileInfo.value) return
-  const link = document.createElement('a')
-  link.href = `${import.meta.env.VITE_APP_BASE_API}/system/file/share/download/${shareCode.value}`
-  link.download = fileInfo.value.fileName
-  link.click()
-  proxy.$modal.msgSuccess('开始下载')
-}
-
-function downloadFileById(fileId) {
-  const link = document.createElement('a')
-  link.href = `${import.meta.env.VITE_APP_BASE_API}/system/file/share/download/${shareCode.value}/${fileId}`
-  link.click()
-  proxy.$modal.msgSuccess('开始下载')
-}
-
-async function loadFolderList(folderId) {
-  try {
-    const response = await listFolder({ parentId: folderId })
-    if (response.code === 200) {
-      folderList.value = response.rows || []
-    }
-  } catch (error) {
-    console.error('加载文件夹列表失败:', error)
-  }
-}
-
+// 文件夹下钻
 async function enterFolder(folder) {
+  if (!folder || !folder.folderId) return
+  
   loading.value = true
+  
   try {
     const response = await getShareFolderFiles(shareCode.value, folder.folderId)
+    
     if (response.code === 200) {
+      const data = response.data || {}
+      
       currentFolderId.value = folder.folderId
-      fileInfo.value.fileList = response.data || []
-      loadFolderList(folder.folderId)
+      
       breadcrumb.value.push({
         folderId: folder.folderId,
         folderName: folder.folderName
       })
+      
+      folderList.value = data.folderList || []
+      fileInfo.value.fileList = data.fileList || []
+    } else {
+      proxy.$modal.msgError(response.msg || '加载失败')
     }
   } catch (error) {
-    proxy.$modal.msgError('加载文件夹失败')
+    proxy.$modal.msgError('网络错误,请重试')
   } finally {
     loading.value = false
   }
 }
 
-async function goToFolder(index) {
-  if (index === -1) {
-    breadcrumb.value = []
-    await loadShare()
-  } else {
-    const folder = breadcrumb.value[index]
-    breadcrumb.value = breadcrumb.value.slice(0, index + 1)
-    await enterFolder(folder)
+// 点击项目(文件夹则进入)
+function handleItemClick(record) {
+  if (record.isFolder) {
+    enterFolder(record)
   }
 }
 
-function handleSelectionChange(keys, rows) {
-  selectedRowKeys.value = keys
-  selectedItems.value = rows
+// 导航回根目录
+async function goToRoot() {
+  if (breadcrumb.value.length === 0) return
+  breadcrumb.value = []
+  // 重新加载初始数据
+  verifyPassword()
 }
 
-function downloadAll() {
-  proxy.$modal.confirm('确定要下载全部内容吗?').then(() => {
-    const files = fileInfo.value.fileList || []
-    const folders = folderList.value || []
-    if (files.length === 0 && folders.length === 0) {
-      proxy.$modal.msgWarning('没有可下载的内容')
-      return
-    }
-    downloadBatch(files.map(f => f.fileId), folders.map(f => f.folderId))
-  }).catch(() => {})
+// 导航到指定层级
+async function goToFolder(index) {
+  if (index === breadcrumb.value.length - 1) return
+  
+  const folder = breadcrumb.value[index]
+  // 截取面包屑到当前层级之前(因为enterFolder会添加当前层级)
+  breadcrumb.value = breadcrumb.value.slice(0, index) 
+  await enterFolder(folder)
 }
 
-function downloadSelected() {
-  if (selectedItems.value.length === 0) {
-    proxy.$modal.msgWarning('请选择要下载的项目')
-    return
+// 下载单文件
+function downloadFile() {
+  if (!fileInfo.value) return
+  const link = document.createElement('a')
+  link.href = `${import.meta.env.VITE_APP_BASE_API}/system/file/share/download/${shareCode.value}`
+  link.download = fileInfo.value.fileName
+  link.click()
+}
+
+// 下载列表项
+async function downloadItem(item) {
+  if (item.isFolder) {
+    // 先检查文件夹是否为空
+    Modal.confirm({
+      title: '下载文件夹',
+      content: `确定要下载文件夹 "${item.folderName}" 吗?`,
+      okText: '确定',
+      cancelText: '取消',
+      onOk: () => {
+        const link = document.createElement('a')
+        link.href = `${import.meta.env.VITE_APP_BASE_API}/system/file/share/downloadFolder/${shareCode.value}/${item.folderId}`
+        link.click()
+        proxy.$modal.msgSuccess('开始打包下载文件夹,请稍候...')
+      }
+    })
+  } else {
+    const link = document.createElement('a')
+    link.href = `${import.meta.env.VITE_APP_BASE_API}/system/file/share/download/${shareCode.value}/${item.fileId}`
+    link.click()
   }
-  const files = selectedItems.value.filter(item => !item.isFolder)
-  const folders = selectedItems.value.filter(item => item.isFolder)
-  downloadBatch(files.map(f => f.fileId), folders.map(f => f.folderId))
 }
 
+// 下载全部
+function downloadAll() {
+  Modal.confirm({
+    title: '确认下载',
+    content: '确定要将当前目录下的所有内容打包下载吗?',
+    okText: '确定',
+    cancelText: '取消',
+    onOk: () => {
+      const files = (fileInfo.value.fileList || []).map(f => f.fileId).filter(id => id)
+      const folders = (folderList.value || []).map(f => f.folderId).filter(id => id)
+      
+      if (files.length === 0 && folders.length === 0) {
+        proxy.$modal.msgWarning('当前目录没有可下载的内容')
+        return
+      }
+      
+      downloadBatch(files, folders)
+    }
+  })
+}
+
+// 批量下载
 async function downloadBatch(fileIds, folderIds) {
   try {
     const response = await fetch(`${import.meta.env.VITE_APP_BASE_API}/system/file/share/downloadBatch/${shareCode.value}`, {
@@ -338,23 +414,23 @@ async function downloadBatch(fileIds, folderIds) {
       proxy.$modal.msgError('下载失败')
     }
   } catch (error) {
-    proxy.$modal.msgError('下载失败')
+    proxy.$modal.msgError('下载请求失败')
   }
 }
 
-function downloadFolder(folder) {
-  const link = document.createElement('a')
-  link.href = `${import.meta.env.VITE_APP_BASE_API}/system/file/share/downloadFolder/${shareCode.value}/${folder.folderId}`
-  link.click()
-  proxy.$modal.msgSuccess(`开始下载文件夹: ${folder.folderName}`)
-}
-
+// 显示错误
 function showError(message, detail) {
   loading.value = false
   errorMessage.value = message
   errorDetail.value = detail
 }
 
+// 刷新页面
+function refreshPage() {
+  window.location.reload()
+}
+
+// 格式化文件大小
 function formatFileSize(bytes) {
   if (!bytes) return '0 B'
   const k = 1024
@@ -364,250 +440,313 @@ function formatFileSize(bytes) {
 }
 </script>
 
-<style lang="scss" scoped>
+<style scoped>
+/* 全局变量模拟 */
 .share-page {
+  --primary-color: #1890ff;
+  --bg-color: #f5f7fa;
+  --card-bg: #ffffff;
+  --text-main: #333333;
+  --text-secondary: #666666;
+  --border-color: #f0f0f0;
+  
   min-height: 100vh;
-  background: #f0f2f5 url("data:image/svg+xml,%3Csvg width='64' height='64' viewBox='0 0 64 64' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8 16c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8 3.582 8 8 8zm0-2c3.314 0 6-2.686 6-6s-2.686-6-6-6-6 2.686-6 6 2.686 6 6 6zm33.414 0c5.955 0 10.791-4.836 10.791-10.791 0-5.955-4.836-10.791-10.791-10.791-5.955 0-10.791 4.836-10.791 10.791 0 5.955 4.836 10.791 10.791 10.791zm0-2c4.85 0 8.791-3.941 8.791-8.791 0-4.85-3.941-8.791-8.791-8.791-4.85 0-8.791 3.941-8.791 8.791 0 4.85 3.941 8.791 8.791 8.791zM16 48c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8 3.582 8 8 8zm0-2c3.314 0 6-2.686 6-6s-2.686-6-6-6-6 2.686-6 6 2.686 6 6 6zM8 32c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8 3.582 8 8 8zm0-2c3.314 0 6-2.686 6-6s-2.686-6-6-6-6 2.686-6 6 2.686 6 6 6zm33.414 32c5.955 0 10.791-4.836 10.791-10.791 0-5.955-4.836-10.791-10.791-10.791-5.955 0-10.791 4.836-10.791 10.791 0 5.955 4.836 10.791 10.791 10.791zm0-2c4.85 0 8.791-3.941 8.791-8.791 0-4.85-3.941-8.791-8.791-8.791-4.85 0-8.791 3.941-8.791 8.791 0 4.85 3.941 8.791 8.791 8.791zM63.5 0h-3.2c-1.036 0-1.884.848-1.884 1.884v3.2c0 1.036.848 1.884 1.884 1.884h3.2c1.036 0 1.884-.848 1.884-1.884v-3.2c0-1.036-.848-1.884-1.884-1.884zM61 5h-2V3h2v2zm-28 0h-3.2c-1.036 0-1.884.848-1.884 1.884v3.2c0 1.036.848 1.884 1.884 1.884h3.2c1.036 0 1.884-.848 1.884-1.884v-3.2c0-1.036-.848-1.884-1.884-1.884zM33 5h-2V3h2v2zM6.5 0H3.3c-1.036 0-1.884.848-1.884 1.884v3.2c0 1.036.848 1.884 1.884 1.884h3.2c1.036 0 1.884-.848 1.884-1.884v-3.2c0-1.036-.848-1.884-1.884-1.884zM4 5H2V3h2v2z' fill='%239C92AC' fill-opacity='0.05' fill-rule='evenodd'/%3E%3C/svg%3E");
+  background-color: var(--bg-color);
   display: flex;
+  flex-direction: column;
   align-items: center;
-  justify-content: center;
-  padding: 20px;
+  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
 }
 
 .share-container {
   width: 100%;
+  max-width: 1000px;
+  flex: 1;
   display: flex;
-  align-items: center;
   justify-content: center;
+  align-items: flex-start;
+  padding: 60px 20px;
 }
 
-.share-box {
-  background: rgba(255, 255, 255, 0.95);
-  backdrop-filter: blur(10px);
-  border-radius: 16px;
-  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.08);
-  padding: 48px;
-  max-width: 520px;
+/* 卡片通用样式 */
+.password-box, .content-box, .error-box, .loading-box {
   width: 100%;
+  background: var(--card-bg);
+  border-radius: 12px;
+  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
+  overflow: hidden;
+  transition: all 0.3s ease;
+}
+
+/* 加载中 */
+.loading-box {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  min-height: 400px;
+}
+
+/* 密码框 */
+.password-box {
+  max-width: 480px;
+  margin: 40px auto;
+  padding: 40px;
+}
+
+.password-card {
   text-align: center;
-  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
-  
-  &.full-width {
-    max-width: 1200px;
-    padding: 32px;
-  }
 }
 
-.loading-box,
-.password-box,
-.file-info-box,
-.error-box {
-  padding: 20px 0;
-  animation: fadeIn 0.5s ease-out;
+.password-card .icon-wrapper {
+  font-size: 48px;
+  color: var(--primary-color);
+  margin-bottom: 24px;
+}
+
+.password-card .title {
+  font-size: 24px;
+  font-weight: 600;
+  color: var(--text-main);
+  margin-bottom: 12px;
+}
+
+.password-card .subtitle {
+  color: var(--text-secondary);
+  margin-bottom: 32px;
 }
 
-@keyframes fadeIn {
-  from { opacity: 0; transform: translateY(10px); }
-  to { opacity: 1; transform: translateY(0); }
+.password-card .input-group {
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
 }
 
-.share-header {
+.password-card .code-input {
   text-align: center;
-  margin-bottom: 40px;
-  
-  .anticon {
-    margin-bottom: 16px;
-    padding: 16px;
-    background: #e6f7ff;
-    border-radius: 50%;
-    color: #1890ff;
-  }
-  
-  h1 {
-    font-size: 28px;
-    font-weight: 700;
-    color: #303133;
-    margin: 8px 0;
-    letter-spacing: -0.5px;
-  }
-  
-  p {
-    color: #999;
-    font-size: 15px;
-    margin-top: 8px;
-  }
+  font-size: 18px;
+  letter-spacing: 2px;
 }
 
-.password-input-box {
-  max-width: 320px;
-  margin: 0 auto;
-  
-  .password-input {
-    :deep(.ant-input) {
-      text-align: center;
-      font-size: 24px;
-      font-weight: 600;
-      letter-spacing: 12px;
-      height: 52px;
-      color: #303133;
-      
-      &::placeholder {
-        font-size: 16px;
-        letter-spacing: 1px;
-        font-weight: 400;
-      }
-    }
-  }
-  
-  .submit-btn {
-    width: 100%;
-    margin-top: 24px;
-    height: 48px;
-    border-radius: 12px;
-    font-size: 16px;
-    font-weight: 600;
-    letter-spacing: 1px;
-    box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);
-    transition: all 0.2s;
-    
-    &:hover {
-      transform: translateY(-1px);
-      box-shadow: 0 6px 16px rgba(24, 144, 255, 0.4);
-    }
-    
-    &:active {
-      transform: translateY(0);
-    }
-  }
+.password-card .submit-btn {
+  height: 44px;
+  font-size: 16px;
+  border-radius: 6px;
 }
 
-.file-icon {
-  margin-bottom: 24px;
-  
-  .anticon {
-    padding: 24px;
-    background: #f0f2f5;
-    border-radius: 20px;
-    box-shadow: inset 0 0 0 1px rgba(0,0,0,0.05);
-  }
+/* 内容区域 */
+.content-box {
+  display: flex;
+  flex-direction: column;
+  min-height: 600px;
+}
+
+.content-header {
+  padding: 24px 32px;
+  border-bottom: 1px solid var(--border-color);
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  background-color: #fafafa;
+}
+
+.content-header .header-left {
+  display: flex;
+  align-items: center;
+  gap: 16px;
 }
 
-h2 {
+.content-header .file-icon {
+  width: 48px;
+  height: 48px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background: #e6f7ff;
+  border-radius: 8px;
   font-size: 24px;
+  color: var(--primary-color);
+}
+
+.content-header .file-details .file-name {
+  font-size: 18px;
   font-weight: 600;
-  margin-bottom: 12px;
-  color: #303133;
+  color: var(--text-main);
+  margin-bottom: 4px;
+  max-width: 400px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
 }
 
-.file-meta {
-  color: #606266;
-  margin: 24px 0;
-  font-size: 14px;
+.content-header .file-details .file-meta {
+  display: flex;
+  gap: 12px;
+  font-size: 13px;
+  color: var(--text-secondary);
+}
+
+.content-header .file-details .meta-tag {
   display: flex;
   align-items: center;
+  gap: 4px;
+}
+
+.content-header .file-details .meta-tag.expire {
+  color: #ff4d4f;
+  background: #fff1f0;
+  padding: 2px 8px;
+  border-radius: 4px;
+}
+
+/* 单文件预览 */
+.single-file-preview {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
   justify-content: center;
-  gap: 24px;
-  
-  span {
-    display: flex;
-    align-items: center;
-    gap: 6px;
-    background: #f5f7fa;
-    padding: 6px 12px;
-    border-radius: 20px;
-  }
+  padding: 60px;
 }
 
-.folder-share {
-  width: 100%;
-  text-align: left;
-  
-  .folder-header {
-    text-align: center;
-    padding: 20px 0 40px;
-    border-bottom: 1px solid #ebeef5;
-    margin-bottom: 20px;
-    
-    .anticon {
-      padding: 16px;
-      background: #e6f7ff;
-      border-radius: 16px;
-      margin-bottom: 16px;
-    }
-    
-    h2 {
-      margin: 0;
-      font-size: 24px;
-    }
-    
-    p {
-      color: #999;
-      margin-top: 8px;
-      font-size: 14px;
-    }
-  }
-  
-  .toolbar {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    padding: 12px 0;
-    
-    .toolbar-left {
-      flex: 1;
-      overflow: hidden;
-    }
-    
-    .toolbar-right {
-      display: flex;
-      gap: 12px;
-      flex-shrink: 0;
-      margin-left: 20px;
-    }
-  }
+.single-file-preview .preview-icon {
+  font-size: 80px;
+  color: #d9d9d9;
+  margin-bottom: 24px;
 }
 
-.action-buttons {
-  margin: 40px 0;
-  
-  .ant-btn {
-    padding: 12px 32px;
-    font-size: 16px;
-    border-radius: 8px;
-    height: auto;
-  }
+.single-file-preview .preview-tip {
+  color: var(--text-secondary);
+  font-size: 16px;
 }
 
-.share-info {
-  margin-top: 32px;
-  padding-top: 24px;
-  border-top: 1px solid #ebeef5;
-  
-  p {
-    color: #999;
-    font-size: 13px;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    gap: 6px;
-  }
+/* 文件夹内容 */
+.folder-content {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+}
+
+.folder-content .breadcrumb-bar {
+  padding: 16px 32px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  border-bottom: 1px solid var(--border-color);
 }
 
+.folder-content .breadcrumb-bar .file-count {
+  color: var(--text-secondary);
+  font-size: 13px;
+}
+
+.folder-content .breadcrumb-bar a {
+  color: var(--text-secondary);
+  transition: color 0.2s;
+}
+
+.folder-content .breadcrumb-bar a:hover,
+.folder-content .breadcrumb-bar a.active {
+  color: var(--primary-color);
+}
+
+.folder-content .table-wrapper {
+  padding: 0 16px;
+}
+
+.folder-content .name-cell {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  cursor: pointer;
+  padding: 8px 0;
+}
+
+.folder-content .name-cell:hover .text {
+  color: var(--primary-color);
+}
+
+.folder-content .name-cell .icon {
+  display: flex;
+  align-items: center;
+}
+
+.folder-content .name-cell .text {
+  font-weight: 500;
+  color: var(--text-main);
+  transition: color 0.2s;
+}
+
+.folder-content .size-text,
+.folder-content .time-text {
+  color: var(--text-secondary);
+  font-size: 13px;
+}
+
+/* 错误提示 */
 .error-box {
-  .anticon {
-    background: #fff1f0;
-    padding: 20px;
-    border-radius: 50%;
-    margin-bottom: 24px;
+  max-width: 480px;
+  margin: 40px auto;
+  text-align: center;
+  padding: 60px 40px;
+}
+
+.error-box .error-icon {
+  font-size: 64px;
+  color: #ff4d4f;
+  margin-bottom: 24px;
+}
+
+.error-box h2 {
+  font-size: 20px;
+  color: var(--text-main);
+  margin-bottom: 12px;
+}
+
+.error-box p {
+  color: var(--text-secondary);
+  margin-bottom: 32px;
+}
+
+.footer {
+  text-align: center;
+  padding: 24px;
+  color: #999;
+  font-size: 13px;
+}
+
+/* 过渡动画 */
+.fade-enter-active,
+.fade-leave-active {
+  transition: opacity 0.3s ease, transform 0.3s ease;
+}
+
+.fade-enter-from,
+.fade-leave-to {
+  opacity: 0;
+  transform: translateY(10px);
+}
+
+/* 响应式调整 */
+@media (max-width: 768px) {
+  .share-container {
+    padding: 20px 10px;
   }
   
-  h2 {
-    color: #303133;
-    margin: 0 0 8px;
+  .content-box .content-header {
+    padding: 16px;
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 16px;
+  }
+  
+  .content-box .content-header .header-right {
+    width: 100%;
+    display: flex;
+    justify-content: flex-end;
   }
   
-  p {
-    color: #999;
+  .folder-content .breadcrumb-bar {
+    padding: 12px 16px;
   }
 }
 </style>

+ 2 - 2
yushu-uivue3/src/views/system/icon/index.vue

@@ -44,7 +44,7 @@
           </a-tooltip>
           <a-tooltip title="复制组件代码" placement="top">
             <a-button type="link" size="small" @click.stop="handleCopyComponent(icon)">
-              <template #icon><FileCopyOutlined /></template>
+              <template #icon><FileTextOutlined /></template>
             </a-button>
           </a-tooltip>
         </div>
@@ -87,7 +87,7 @@
 </template>
 
 <script setup>
-import { CopyOutlined, FileCopyOutlined, UploadOutlined, SearchOutlined, PlusOutlined } from '@ant-design/icons-vue'
+import { CopyOutlined, FileTextOutlined, UploadOutlined, SearchOutlined, PlusOutlined } from '@ant-design/icons-vue'
 import { message } from 'ant-design-vue'
 import { uploadIcon } from '@/api/system/icon'
 

+ 9 - 13
yushu-uivue3/src/views/tool/gen/genInfoForm.vue

@@ -13,8 +13,9 @@
 
       <a-col :span="12">
         <a-form-item name="tplWebType" label="前端类型">
-          <a-select v-model:value="info.tplWebType" disabled>
-            <a-select-option value="antdv">Ant Design Vue 模版</a-select-option>
+          <a-select v-model:value="info.tplWebType">
+            <a-select-option value="element-ui">Vue2 Element UI 模版</a-select-option>
+            <a-select-option value="element-plus">Vue3 Element Plus 模版</a-select-option>
           </a-select>
         </a-form-item>
       </a-col>
@@ -285,22 +286,17 @@ function getMenuTreeselect() {
   })
 }
 
-// 确保前端类型始终为 antdv(Ant Design Vue)
-watch(() => props.info.tplWebType, val => {
-  if (!val || val === '' || val === 'element-ui' || val === 'element-plus') {
-    props.info.tplWebType = "antdv"
-  }
-}, { immediate: true })
+onMounted(() => {
+  getMenuTreeselect()
+})
 
 watch(() => props.info.subTableName, val => {
   setSubTableColumns(val)
 })
 
-onMounted(() => {
-  getMenuTreeselect()
-  // 初始化时确保前端类型为 antdv
-  if (!props.info.tplWebType || props.info.tplWebType === '' || props.info.tplWebType === 'element-ui' || props.info.tplWebType === 'element-plus') {
-    props.info.tplWebType = "antdv"
+watch(() => props.info.tplWebType, val => {
+  if (val === '') {
+    props.info.tplWebType = "element-plus"
   }
 })
 

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio