Browse Source

组件迁移

ys 2 tuần trước cách đây
mục cha
commit
d7d8e3a94d

+ 7 - 3
yushu-uivue3/src/components/SvgIcon/svgicon.js

@@ -1,10 +1,14 @@
-import * as components from '@element-plus/icons-vue'
+// 此文件已废弃,请使用 antd-icons.js
+// 保留此文件仅为了兼容性,实际使用 ant-design-vue 图标
+import * as components from '@ant-design/icons-vue'
 
 export default {
   install: (app) => {
     for (const key in components) {
-      const componentConfig = components[key]
-      app.component(componentConfig.name, componentConfig)
+      const component = components[key]
+      if (component && component.name) {
+        app.component(component.name, component)
+      }
     }
   }
 }

+ 43 - 39
yushu-uivue3/src/layout/components/Navbar.vue

@@ -11,34 +11,35 @@
         <screenfull id="screenfull" class="right-menu-item hover-effect" />
 
         <!-- 消息中心 -->
-        <el-tooltip content="消息中心" effect="dark" placement="bottom">
+        <a-tooltip title="消息中心" placement="bottom">
           <div class="right-menu-item hover-effect message-center" @click="goMessage">
-            <el-badge :value="unreadCount" :hidden="unreadCount === 0" :max="99">
-              <el-icon :size="18"><ChatDotRound /></el-icon>
-            </el-badge>
+            <a-badge :count="unreadCount" :number-style="{ display: unreadCount === 0 ? 'none' : 'block' }" :overflow-count="99">
+              <MessageOutlined :style="{ fontSize: '18px' }" />
+            </a-badge>
           </div>
-        </el-tooltip>
+        </a-tooltip>
       </template>
 
-      <el-dropdown @command="handleCommand" class="avatar-container right-menu-item hover-effect" trigger="hover">
+      <a-dropdown @click="handleCommand" class="avatar-container right-menu-item hover-effect" trigger="hover">
         <div class="avatar-wrapper">
           <img :src="userStore.avatar" class="user-avatar" />
           <span class="user-nickname"> {{ userStore.nickName }} </span>
         </div>
-        <template #dropdown>
-          <el-dropdown-menu>
-            <router-link to="/user/profile">
-              <el-dropdown-item>个人中心</el-dropdown-item>
-            </router-link>
-            <el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
-                <span>布局设置</span>
-              </el-dropdown-item>
-            <el-dropdown-item divided command="logout">
+        <template #overlay>
+          <a-menu @click="handleMenuClick">
+            <a-menu-item key="profile">
+              <router-link to="/user/profile">个人中心</router-link>
+            </a-menu-item>
+            <a-menu-item key="setLayout" v-if="settingsStore.showSettings">
+              <span>布局设置</span>
+            </a-menu-item>
+            <a-menu-divider />
+            <a-menu-item key="logout">
               <span>退出登录</span>
-            </el-dropdown-item>
-          </el-dropdown-menu>
+            </a-menu-item>
+          </a-menu>
         </template>
-      </el-dropdown>
+      </a-dropdown>
     </div>
   </div>
 </template>
@@ -46,8 +47,8 @@
 <script setup>
 import { ref, onMounted, onUnmounted } from 'vue'
 import { useRouter } from 'vue-router'
-import { ElMessageBox } from 'element-plus'
-import { ChatDotRound } from '@element-plus/icons-vue'
+import { Modal } from 'ant-design-vue'
+import { MessageOutlined } from '@ant-design/icons-vue'
 import Breadcrumb from '@/components/Breadcrumb'
 import TopNav from '@/components/TopNav'
 import Hamburger from '@/components/Hamburger'
@@ -133,16 +134,25 @@ function handleCommand(command) {
   }
 }
 
+function handleMenuClick({ key }) {
+  if (key === 'profile') {
+    return
+  }
+  handleCommand(key)
+}
+
 function logout() {
-  ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
-    confirmButtonText: '确定',
-    cancelButtonText: '取消',
-    type: 'warning'
-  }).then(() => {
-    userStore.logOut().then(() => {
-      location.href = '/index'
-    })
-  }).catch(() => { })
+  Modal.confirm({
+    title: '提示',
+    content: '确定注销并退出系统吗?',
+    okText: '确定',
+    cancelText: '取消',
+    onOk: () => {
+      userStore.logOut().then(() => {
+        location.href = '/index'
+      })
+    }
+  })
 }
 
 const emits = defineEmits(['setLayout'])
@@ -236,24 +246,18 @@ function setLayout() {
         display: flex;
         align-items: center;
 
-        .el-badge {
+        .ant-badge {
           display: flex;
           align-items: center;
-
-          :deep(.el-badge__content) {
-            top: 0;
-            right: 0;
-            transform: translateY(-50%) translateX(50%);
-          }
         }
 
-        .el-icon {
+        .anticon {
           color: #64748b;
           transition: color 0.3s;
         }
 
-        &:hover .el-icon {
-          color: var(--el-color-primary);
+        &:hover .anticon {
+          color: #1890ff;
         }
       }
     }

+ 30 - 25
yushu-uivue3/src/layout/components/Settings/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <el-drawer v-model="showSettings" :withHeader="false" :lock-scroll="false" direction="rtl" size="300px">
+  <a-drawer v-model:open="showSettings" :headerStyle="{ display: 'none' }" :maskClosable="false" placement="right" :width="300">
     <div class="setting-drawer-title">
       <h3 class="drawer-title">主题风格设置</h3>
     </div>
@@ -28,22 +28,21 @@
     <div class="drawer-item">
       <span>主题颜色</span>
       <span class="comp-style">
-        <el-color-picker v-model="theme" :predefine="predefineColors" @change="themeChange"/>
+        <a-color-picker v-model:value="theme" :presets="predefineColors.map(c => ({ label: c, value: c }))" @change="themeChange"/>
       </span>
     </div>
-    <el-divider />
+    <a-divider />
 
     <h3 class="drawer-title">系统布局配置</h3>
 
     <div class="drawer-item">
       <span>主题模式</span>
       <span class="comp-style">
-        <el-switch
-          v-model="isDark"
+        <a-switch
+          v-model:checked="isDark"
           class="drawer-switch"
-          inline-prompt
-          :active-icon="Moon"
-          :inactive-icon="Sunny"
+          checked-children="暗"
+          un-checked-children="亮"
         />
       </span>
     </div>
@@ -51,73 +50,79 @@
     <div class="drawer-item">
       <span>布局大小</span>
       <span class="comp-style">
-        <el-select v-model="size" @change="handleSizeChange" style="width: 80px">
-          <el-option label="默认" value="default" />
-          <el-option label="大型" value="large" />
-          <el-option label="小型" value="small" />
-        </el-select>
+        <a-select v-model:value="size" @change="handleSizeChange" style="width: 80px">
+          <a-select-option value="default">默认</a-select-option>
+          <a-select-option value="large">大型</a-select-option>
+          <a-select-option value="small">小型</a-select-option>
+        </a-select>
       </span>
     </div>
 
     <div class="drawer-item">
       <span>开启 TopNav</span>
       <span class="comp-style">
-        <el-switch v-model="settingsStore.topNav" @change="topNavChange" class="drawer-switch" />
+        <a-switch v-model:checked="settingsStore.topNav" @change="topNavChange" class="drawer-switch" />
       </span>
     </div>
 
     <div class="drawer-item">
       <span>开启 Tags-Views</span>
       <span class="comp-style">
-        <el-switch v-model="settingsStore.tagsView" class="drawer-switch" />
+        <a-switch v-model:checked="settingsStore.tagsView" class="drawer-switch" />
       </span>
     </div>
 
     <div class="drawer-item">
       <span>显示页签图标</span>
       <span class="comp-style">
-        <el-switch v-model="settingsStore.tagsIcon" :disabled="!settingsStore.tagsView" class="drawer-switch" />
+        <a-switch v-model:checked="settingsStore.tagsIcon" :disabled="!settingsStore.tagsView" class="drawer-switch" />
       </span>
     </div>
 
     <div class="drawer-item">
       <span>固定 Header</span>
       <span class="comp-style">
-        <el-switch v-model="settingsStore.fixedHeader" class="drawer-switch" />
+        <a-switch v-model:checked="settingsStore.fixedHeader" class="drawer-switch" />
       </span>
     </div>
 
     <div class="drawer-item">
       <span>显示 Logo</span>
       <span class="comp-style">
-        <el-switch v-model="settingsStore.sidebarLogo" class="drawer-switch" />
+        <a-switch v-model:checked="settingsStore.sidebarLogo" class="drawer-switch" />
       </span>
     </div>
 
     <div class="drawer-item">
       <span>动态标题</span>
       <span class="comp-style">
-        <el-switch v-model="settingsStore.dynamicTitle" @change="dynamicTitleChange" class="drawer-switch" />
+        <a-switch v-model:checked="settingsStore.dynamicTitle" @change="dynamicTitleChange" class="drawer-switch" />
       </span>
     </div>
 
     <div class="drawer-item">
       <span>底部版权</span>
       <span class="comp-style">
-        <el-switch v-model="settingsStore.footerVisible" class="drawer-switch" />
+        <a-switch v-model:checked="settingsStore.footerVisible" class="drawer-switch" />
       </span>
     </div>
 
-    <el-divider />
+    <a-divider />
 
-    <el-button type="primary" plain icon="DocumentAdd" @click="saveSetting">保存配置</el-button>
-    <el-button plain icon="Refresh" @click="resetSetting">重置配置</el-button>
-  </el-drawer>
+    <a-button type="primary" ghost @click="saveSetting">
+      <template #icon><FileAddOutlined /></template>
+      保存配置
+    </a-button>
+    <a-button ghost @click="resetSetting">
+      <template #icon><ReloadOutlined /></template>
+      重置配置
+    </a-button>
+  </a-drawer>
 
 </template>
 
 <script setup>
-import { Moon, Sunny } from '@element-plus/icons-vue'
+import { FileAddOutlined, ReloadOutlined } from '@ant-design/icons-vue'
 import useAppStore from '@/store/modules/app'
 import useSettingsStore from '@/store/modules/settings'
 import usePermissionStore from '@/store/modules/permission'

+ 57 - 27
yushu-uivue3/src/plugins/modal.js

@@ -1,82 +1,112 @@
-import { ElMessage, ElMessageBox, ElNotification, ElLoading } from 'element-plus'
+import { message, Modal, notification } from 'ant-design-vue'
 
 let loadingInstance
 
 export default {
   // 消息提示
   msg(content) {
-    ElMessage.info(content)
+    message.info(content)
   },
   // 错误消息
   msgError(content) {
-    ElMessage.error(content)
+    message.error(content)
   },
   // 成功消息
   msgSuccess(content) {
-    ElMessage.success(content)
+    message.success(content)
   },
   // 警告消息
   msgWarning(content) {
-    ElMessage.warning(content)
+    message.warning(content)
   },
   // 弹出提示
   alert(content) {
-    ElMessageBox.alert(content, "系统提示")
+    Modal.info({
+      title: "系统提示",
+      content: content,
+    })
   },
   // 错误提示
   alertError(content) {
-    ElMessageBox.alert(content, "系统提示", { type: 'error' })
+    Modal.error({
+      title: "系统提示",
+      content: content,
+    })
   },
   // 成功提示
   alertSuccess(content) {
-    ElMessageBox.alert(content, "系统提示", { type: 'success' })
+    Modal.success({
+      title: "系统提示",
+      content: content,
+    })
   },
   // 警告提示
   alertWarning(content) {
-    ElMessageBox.alert(content, "系统提示", { type: 'warning' })
+    Modal.warning({
+      title: "系统提示",
+      content: content,
+    })
   },
   // 通知提示
   notify(content) {
-    ElNotification.info(content)
+    notification.info({
+      message: "系统提示",
+      description: content,
+    })
   },
   // 错误通知
   notifyError(content) {
-    ElNotification.error(content)
+    notification.error({
+      message: "系统提示",
+      description: content,
+    })
   },
   // 成功通知
   notifySuccess(content) {
-    ElNotification.success(content)
+    notification.success({
+      message: "系统提示",
+      description: content,
+    })
   },
   // 警告通知
   notifyWarning(content) {
-    ElNotification.warning(content)
+    notification.warning({
+      message: "系统提示",
+      description: content,
+    })
   },
   // 确认窗体
   confirm(content) {
-    return ElMessageBox.confirm(content, "系统提示", {
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
-      type: "warning",
+    return Modal.confirm({
+      title: "系统提示",
+      content: content,
+      okText: '确定',
+      cancelText: '取消',
     })
   },
   // 提交内容
   prompt(content) {
-    return ElMessageBox.prompt(content, "系统提示", {
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
-      type: "warning",
+    return new Promise((resolve, reject) => {
+      Modal.confirm({
+        title: "系统提示",
+        content: content,
+        okText: '确定',
+        cancelText: '取消',
+        onOk: () => resolve(),
+        onCancel: () => reject(),
+      })
     })
   },
   // 打开遮罩层
   loading(content) {
-    loadingInstance = ElLoading.service({
-      lock: true,
-      text: content,
-      background: "rgba(0, 0, 0, 0.7)",
-    })
+    // Ant Design Vue 使用 message.loading 或 Spin 组件
+    loadingInstance = message.loading(content || '加载中...', 0)
   },
   // 关闭遮罩层
   closeLoading() {
-    loadingInstance.close()
+    if (loadingInstance) {
+      loadingInstance()
+      loadingInstance = null
+    }
   }
 }

+ 106 - 108
yushu-uivue3/src/views/login.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="login">
-    <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
+    <a-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
       <div class="title-container">
         <h3 class="title">{{ title }}</h3>
       </div>
@@ -26,93 +26,91 @@
       
       <!-- 账号登录表单 -->
       <template v-if="loginType === 'account'">
-        <el-form-item prop="username">
-          <el-input
-            v-model="loginForm.username"
+        <a-form-item name="username">
+          <a-input
+            v-model:value="loginForm.username"
             type="text"
             size="large"
-            auto-complete="off"
+            autocomplete="off"
             placeholder="账号"
             class="login-input"
           >
-            <template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
-          </el-input>
-        </el-form-item>
-        <el-form-item prop="password">
-          <el-input
-            v-model="loginForm.password"
-            type="password"
+            <template #prefix><svg-icon icon-class="user" class="input-icon" /></template>
+          </a-input>
+        </a-form-item>
+        <a-form-item name="password">
+          <a-input-password
+            v-model:value="loginForm.password"
             size="large"
-            auto-complete="off"
+            autocomplete="off"
             placeholder="密码"
             @keyup.enter="handleLogin"
             class="login-input"
-            show-password
           >
-            <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
-          </el-input>
-        </el-form-item>
-        <el-form-item prop="code" v-if="captchaEnabled">
+            <template #prefix><svg-icon icon-class="password" class="input-icon" /></template>
+          </a-input-password>
+        </a-form-item>
+        <a-form-item name="code" v-if="captchaEnabled">
           <div class="captcha-container">
-            <el-input
-              v-model="loginForm.code"
+            <a-input
+              v-model:value="loginForm.code"
               size="large"
-              auto-complete="off"
+              autocomplete="off"
               placeholder="验证码"
               class="login-input captcha-input"
               @keyup.enter="handleLogin"
             >
-              <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
-            </el-input>
+              <template #prefix><svg-icon icon-class="validCode" class="input-icon" /></template>
+            </a-input>
             <div class="login-code">
               <img :src="codeUrl" @click="getCode" class="login-code-img"/>
             </div>
           </div>
-        </el-form-item>
+        </a-form-item>
       </template>
 
       <!-- 邮箱登录表单 (UI Only) -->
       <template v-else>
-        <el-form-item prop="email">
-          <el-input
-            v-model="loginForm.email"
+        <a-form-item name="email">
+          <a-input
+            v-model:value="loginForm.email"
             type="text"
             size="large"
-            auto-complete="off"
+            autocomplete="off"
             placeholder="请输入邮箱地址"
             class="login-input"
           >
-            <template #prefix><svg-icon icon-class="email" class="el-input__icon input-icon" /></template>
-          </el-input>
-        </el-form-item>
-        <el-form-item prop="emailCode">
+            <template #prefix><svg-icon icon-class="email" class="input-icon" /></template>
+          </a-input>
+        </a-form-item>
+        <a-form-item name="emailCode">
           <div class="captcha-container">
-            <el-input
-              v-model="loginForm.emailCode"
+            <a-input
+              v-model:value="loginForm.emailCode"
               size="large"
-              auto-complete="off"
+              autocomplete="off"
               placeholder="邮箱验证码"
               class="login-input captcha-input"
               @keyup.enter="handleLogin"
             >
-              <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
-            </el-input>
-            <el-button size="large" class="get-code-btn" :disabled="emailCodeDisabled" @click="handleSendEmailCode">
+              <template #prefix><svg-icon icon-class="validCode" class="input-icon" /></template>
+            </a-input>
+            <a-button size="large" class="get-code-btn" :disabled="emailCodeDisabled" @click="handleSendEmailCode">
               {{ emailCodeBtnText }}
-            </el-button>
+            </a-button>
           </div>
-        </el-form-item>
+        </a-form-item>
       </template>
       
       <div class="form-options">
-        <el-checkbox v-model="loginForm.rememberMe">记住密码</el-checkbox>
+        <a-checkbox v-model:checked="loginForm.rememberMe">记住密码</a-checkbox>
         <div class="link-group">
           <router-link v-if="register" class="link-type" :to="'/register'">立即注册</router-link>
         </div>
       </div>
 
-      <el-form-item style="width:100%;">
-        <el-button
+      <a-form-item style="width:100%;">
+        <a-button
           :loading="loading"
           size="large"
           type="primary"
@@ -121,9 +119,9 @@
         >
           <span v-if="!loading">登 录</span>
           <span v-else>登 录 中...</span>
-        </el-button>
-      </el-form-item>
-    </el-form>
+        </a-button>
+      </a-form-item>
+    </a-form>
     
     <!--  底部  -->
     <div class="el-login-footer">
@@ -134,7 +132,7 @@
 
 <script setup>
 import { getCodeImg, sendEmailCode } from "@/api/login"
-import { ElMessage } from 'element-plus'
+import { message } from 'ant-design-vue'
 import Cookies from "js-cookie"
 import { encrypt, decrypt } from "@/utils/jsencrypt"
 import useUserStore from '@/store/modules/user'
@@ -160,14 +158,14 @@ const loginForm = ref({
 })
 
 const loginRules = {
-  username: [{ required: true, trigger: "blur", message: "请输入您的账号" }],
-  password: [{ required: true, trigger: "blur", message: "请输入您的密码" }],
-  code: [{ required: true, trigger: "change", message: "请输入验证码" }],
+  username: [{ required: true, message: "请输入您的账号", trigger: "blur" }],
+  password: [{ required: true, message: "请输入您的密码", trigger: "blur" }],
+  code: [{ required: true, message: "请输入验证码", trigger: "change" }],
   email: [
-    { required: true, trigger: "blur", message: "请输入您的邮箱" },
-    { type: "email", trigger: "blur", message: "请输入正确的邮箱格式" }
+    { required: true, message: "请输入您的邮箱", trigger: "blur" },
+    { type: "email", message: "请输入正确的邮箱格式", trigger: "blur" }
   ],
-  emailCode: [{ required: true, trigger: "blur", message: "请输入邮箱验证码" }]
+  emailCode: [{ required: true, message: "请输入邮箱验证码", trigger: "blur" }]
 }
 
 // 邮箱验证码倒计时相关
@@ -195,42 +193,42 @@ function handleLogin() {
     ? ['username', 'password', ...(captchaEnabled.value ? ['code'] : [])] 
     : ['email', 'emailCode']
   
-  proxy.$refs.loginRef.validateField(fieldsToValidate, (valid) => {
-    if (valid) {
-      loading.value = true
-      
-      if (loginType.value === 'account') {
-        // 账号密码登录
-        // 勾选了需要记住密码设置在 cookie 中设置记住用户名和密码
-        if (loginForm.value.rememberMe) {
-          Cookies.set("username", loginForm.value.username, { expires: 30 })
-          Cookies.set("password", encrypt(loginForm.value.password), { expires: 30 })
-          Cookies.set("rememberMe", loginForm.value.rememberMe, { expires: 30 })
-        } else {
-          // 否则移除
-          Cookies.remove("username")
-          Cookies.remove("password")
-          Cookies.remove("rememberMe")
-        }
-        // 调用action的登录方法
-        userStore.login(loginForm.value).then(() => {
-          handleLoginSuccess()
-        }).catch(() => {
-          loading.value = false
-          // 重新获取验证码
-          if (captchaEnabled.value) {
-            getCode()
-          }
-        })
+  proxy.$refs.loginRef.validateFields(fieldsToValidate).then(() => {
+    loading.value = true
+    
+    if (loginType.value === 'account') {
+      // 账号密码登录
+      // 勾选了需要记住密码设置在 cookie 中设置记住用户名和密码
+      if (loginForm.value.rememberMe) {
+        Cookies.set("username", loginForm.value.username, { expires: 30 })
+        Cookies.set("password", encrypt(loginForm.value.password), { expires: 30 })
+        Cookies.set("rememberMe", loginForm.value.rememberMe, { expires: 30 })
       } else {
-        // 邮箱登录
-        userStore.emailLoginAction(loginForm.value).then(() => {
-          handleLoginSuccess()
-        }).catch(() => {
-          loading.value = false
-        })
+        // 否则移除
+        Cookies.remove("username")
+        Cookies.remove("password")
+        Cookies.remove("rememberMe")
       }
+      // 调用action的登录方法
+      userStore.login(loginForm.value).then(() => {
+        handleLoginSuccess()
+      }).catch(() => {
+        loading.value = false
+        // 重新获取验证码
+        if (captchaEnabled.value) {
+          getCode()
+        }
+      })
+    } else {
+      // 邮箱登录
+      userStore.emailLoginAction(loginForm.value).then(() => {
+        handleLoginSuccess()
+      }).catch(() => {
+        loading.value = false
+      })
     }
+  }).catch(() => {
+    // 验证失败,不做任何操作
   })
 }
 
@@ -249,28 +247,28 @@ function handleLoginSuccess() {
 // 发送邮箱验证码
 function handleSendEmailCode() {
   // 先验证邮箱格式
-  proxy.$refs.loginRef.validateField('email', (valid) => {
-    if (valid) {
-      emailCodeDisabled.value = true
-      sendEmailCode(loginForm.value.email).then(res => {
-        ElMessage.success(res.msg || '验证码已发送')
-        // 开始倒计时
-        let countdown = 60
-        emailCodeBtnText.value = `${countdown}s后重新获取`
-        emailCodeTimer = setInterval(() => {
-          countdown--
-          if (countdown <= 0) {
-            clearInterval(emailCodeTimer)
-            emailCodeDisabled.value = false
-            emailCodeBtnText.value = '获取验证码'
-          } else {
-            emailCodeBtnText.value = `${countdown}s后重新获取`
-          }
-        }, 1000)
-      }).catch(() => {
-        emailCodeDisabled.value = false
-      })
-    }
+  proxy.$refs.loginRef.validateFields(['email']).then(() => {
+    emailCodeDisabled.value = true
+    sendEmailCode(loginForm.value.email).then(res => {
+      message.success(res.msg || '验证码已发送')
+      // 开始倒计时
+      let countdown = 60
+      emailCodeBtnText.value = `${countdown}s后重新获取`
+      emailCodeTimer = setInterval(() => {
+        countdown--
+        if (countdown <= 0) {
+          clearInterval(emailCodeTimer)
+          emailCodeDisabled.value = false
+          emailCodeBtnText.value = '获取验证码'
+        } else {
+          emailCodeBtnText.value = `${countdown}s后重新获取`
+        }
+      }, 1000)
+    }).catch(() => {
+      emailCodeDisabled.value = false
+    })
+  }).catch(() => {
+    // 验证失败,不做任何操作
   })
 }
 

+ 42 - 44
yushu-uivue3/src/views/register.vue

@@ -4,64 +4,60 @@
       <div class="title-container">
         <h3 class="title">{{ title }}</h3>
       </div>
-      <el-form-item prop="username">
-        <el-input 
-          v-model="registerForm.username" 
+      <a-form-item name="username">
+        <a-input 
+          v-model:value="registerForm.username" 
           type="text" 
           size="large" 
-          auto-complete="off" 
+          autocomplete="off" 
           placeholder="账号"
           class="register-input"
         >
-          <template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
-        </el-input>
-      </el-form-item>
-      <el-form-item prop="password">
-        <el-input
-          v-model="registerForm.password"
-          type="password"
+          <template #prefix><svg-icon icon-class="user" class="input-icon" /></template>
+        </a-input>
+      </a-form-item>
+      <a-form-item name="password">
+        <a-input-password
+          v-model:value="registerForm.password"
           size="large" 
-          auto-complete="off"
+          autocomplete="off"
           placeholder="密码"
           class="register-input"
-          show-password
         >
-          <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
-        </el-input>
-      </el-form-item>
-      <el-form-item prop="confirmPassword">
-        <el-input
-          v-model="registerForm.confirmPassword"
-          type="password"
+          <template #prefix><svg-icon icon-class="password" class="input-icon" /></template>
+        </a-input-password>
+      </a-form-item>
+      <a-form-item name="confirmPassword">
+        <a-input-password
+          v-model:value="registerForm.confirmPassword"
           size="large" 
-          auto-complete="off"
+          autocomplete="off"
           placeholder="确认密码"
           class="register-input"
-          show-password
           @keyup.enter="handleRegister"
         >
-          <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
-        </el-input>
-      </el-form-item>
-      <el-form-item prop="code" v-if="captchaEnabled">
+          <template #prefix><svg-icon icon-class="password" class="input-icon" /></template>
+        </a-input-password>
+      </a-form-item>
+      <a-form-item name="code" v-if="captchaEnabled">
         <div class="captcha-container">
-          <el-input
+          <a-input
             size="large" 
-            v-model="registerForm.code"
-            auto-complete="off"
+            v-model:value="registerForm.code"
+            autocomplete="off"
             placeholder="验证码"
             class="register-input captcha-input"
             @keyup.enter="handleRegister"
           >
-            <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
-          </el-input>
+            <template #prefix><svg-icon icon-class="validCode" class="input-icon" /></template>
+          </a-input>
           <div class="register-code">
             <img :src="codeUrl" @click="getCode" class="register-code-img"/>
           </div>
         </div>
-      </el-form-item>
-      <el-form-item style="width:100%;">
-        <el-button
+      </a-form-item>
+      <a-form-item style="width:100%;">
+        <a-button
           :loading="loading"
           size="large" 
           type="primary"
@@ -70,12 +66,12 @@
         >
           <span v-if="!loading">注 册</span>
           <span v-else>注 册 中...</span>
-        </el-button>
-      </el-form-item>
+        </a-button>
+      </a-form-item>
       <div class="form-footer">
         <router-link class="link-type" :to="'/login'">使用已有账户登录</router-link>
       </div>
-    </el-form>
+    </a-form>
     <!--  底部  -->
     <div class="el-register-footer">
       <span>{{ footerContent }}</span>
@@ -84,7 +80,8 @@
 </template>
 
 <script setup>
-import { ElMessageBox } from "element-plus"
+import { Modal } from "ant-design-vue"
+import { h } from "vue"
 import { getCodeImg, register } from "@/api/login"
 import defaultSettings from '@/settings'
 
@@ -136,12 +133,13 @@ function handleRegister() {
       loading.value = true
       register(registerForm.value).then(res => {
         const username = registerForm.value.username
-        ElMessageBox.alert("<font color='red'>恭喜你,您的账号 " + username + " 注册成功!</font>", "系统提示", {
-          dangerouslyUseHTMLString: true,
-          type: "success",
-        }).then(() => {
-          router.push("/login")
-        }).catch(() => {})
+        Modal.success({
+          title: "系统提示",
+          content: h('div', { innerHTML: "<font color='red'>恭喜你,您的账号 " + username + " 注册成功!</font>" }),
+          onOk: () => {
+            router.push("/login")
+          }
+        })
       }).catch(() => {
         loading.value = false
         if (captchaEnabled) {

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

@@ -2,20 +2,22 @@
   <div class="app-container icon-manager">
     <!-- 搜索区域 -->
     <div class="search-header">
-      <el-input
-        v-model="searchQuery"
+      <a-input
+        v-model:value="searchQuery"
         placeholder="搜索图标名称..."
-        prefix-icon="Search"
-        clearable
+        allow-clear
         class="search-input"
-      />
+      >
+        <template #prefix><SearchOutlined /></template>
+      </a-input>
       <div class="header-right">
         <div class="icon-stats">
           共 <span class="count">{{ filteredIcons.length }}</span> 个图标
         </div>
-        <el-button v-if="isDev" type="primary" icon="Plus" @click="showUploadDialog = true" v-hasPermi="['tool:icon:add']">
+        <a-button v-if="isDev" type="primary" @click="showUploadDialog = true" v-hasPermi="['tool:icon:add']">
+          <template #icon><PlusOutlined /></template>
           上传图标
-        </el-button>
+        </a-button>
       </div>
     </div>
 
@@ -32,50 +34,49 @@
         </div>
         <div class="icon-name">{{ icon }}</div>
         <div class="icon-actions">
-          <el-tooltip content="复制名称" placement="top">
-            <el-button type="primary" link size="small" @click.stop="handleCopy(icon)">
-              <el-icon><DocumentCopy /></el-icon>
-            </el-button>
-          </el-tooltip>
-          <el-tooltip content="复制组件代码" placement="top">
-            <el-button type="primary" link size="small" @click.stop="handleCopyComponent(icon)">
-              <el-icon><CopyDocument /></el-icon>
-            </el-button>
-          </el-tooltip>
+          <a-tooltip title="复制名称" placement="top">
+            <a-button type="link" size="small" @click.stop="handleCopy(icon)">
+              <template #icon><CopyOutlined /></template>
+            </a-button>
+          </a-tooltip>
+          <a-tooltip title="复制组件代码" placement="top">
+            <a-button type="link" size="small" @click.stop="handleCopyComponent(icon)">
+              <template #icon><FileCopyOutlined /></template>
+            </a-button>
+          </a-tooltip>
         </div>
       </div>
     </div>
 
     <!-- 空状态 -->
-    <el-empty v-if="filteredIcons.length === 0" description="没有找到匹配的图标" />
+    <a-empty v-if="filteredIcons.length === 0" description="没有找到匹配的图标" />
 
     <!-- 上传对话框 -->
-    <el-dialog v-model="showUploadDialog" title="上传图标" width="500px">
-      <el-upload
+    <a-modal v-model:open="showUploadDialog" title="上传图标" :width="500">
+      <a-upload-dragger
         ref="uploadRef"
-        drag
         :auto-upload="false"
-        :limit="10"
+        :max-count="10"
         accept=".svg"
-        :on-change="handleFileChange"
+        @change="handleFileChange"
       >
-        <el-icon class="el-icon--upload"><Upload /></el-icon>
-        <div class="el-upload__text">将SVG文件拖到此处,或<em>点击上传</em></div>
-        <template #tip>
-          <div class="el-upload__tip">只能上传 SVG 格式,上传后刷新页面即可使用</div>
-        </template>
-      </el-upload>
+        <p class="ant-upload-drag-icon">
+          <UploadOutlined />
+        </p>
+        <p class="ant-upload-text">将SVG文件拖到此处,或点击上传</p>
+        <p class="ant-upload-hint">只能上传 SVG 格式,上传后刷新页面即可使用</p>
+      </a-upload-dragger>
       <template #footer>
-        <el-button @click="showUploadDialog = false">取消</el-button>
-        <el-button type="primary" @click="handleUpload" :loading="uploading">确认上传</el-button>
+        <a-button @click="showUploadDialog = false">取消</a-button>
+        <a-button type="primary" @click="handleUpload" :loading="uploading">确认上传</a-button>
       </template>
-    </el-dialog>
+    </a-modal>
   </div>
 </template>
 
 <script setup>
-import { DocumentCopy, CopyDocument, Upload } from '@element-plus/icons-vue'
-import { ElMessage } from 'element-plus'
+import { CopyOutlined, FileCopyOutlined, UploadOutlined, SearchOutlined, PlusOutlined } from '@ant-design/icons-vue'
+import { message } from 'ant-design-vue'
 import { uploadIcon } from '@/api/system/icon'
 
 const searchQuery = ref('')
@@ -104,9 +105,9 @@ const filteredIcons = computed(() => {
 // 复制图标名称
 function handleCopy(iconName) {
   navigator.clipboard.writeText(iconName).then(() => {
-    ElMessage.success(`已复制: ${iconName}`)
+    message.success(`已复制: ${iconName}`)
   }).catch(() => {
-    ElMessage.error('复制失败')
+    message.error('复制失败')
   })
 }
 
@@ -114,21 +115,24 @@ function handleCopy(iconName) {
 function handleCopyComponent(iconName) {
   const code = `<svg-icon icon-class="${iconName}" />`
   navigator.clipboard.writeText(code).then(() => {
-    ElMessage.success('已复制组件代码')
+    message.success('已复制组件代码')
   }).catch(() => {
-    ElMessage.error('复制失败')
+    message.error('复制失败')
   })
 }
 
 // 处理文件变化
-function handleFileChange(file, fileList) {
-  uploadFiles.value = fileList
+function handleFileChange(info) {
+  uploadFiles.value = info.fileList.map(file => ({
+    raw: file.originFileObj || file,
+    name: file.name
+  }))
 }
 
 // 上传图标
 async function handleUpload() {
   if (uploadFiles.value.length === 0) {
-    ElMessage.warning('请选择要上传的图标')
+    message.warning('请选择要上传的图标')
     return
   }
   
@@ -138,22 +142,22 @@ async function handleUpload() {
   for (const file of uploadFiles.value) {
     try {
       const formData = new FormData()
-      formData.append('file', file.raw)
+      formData.append('file', file.raw || file)
       const res = await uploadIcon(formData)
       if (res.code === 200) {
         successCount++
       } else {
-        ElMessage.error(res.msg || '上传失败')
+        message.error(res.msg || '上传失败')
       }
     } catch (e) {
-      ElMessage.error('上传失败')
+      message.error('上传失败')
     }
   }
   
   uploading.value = false
   
   if (successCount > 0) {
-    ElMessage.success(`成功上传 ${successCount} 个图标,即将刷新页面`)
+    message.success(`成功上传 ${successCount} 个图标,即将刷新页面`)
     showUploadDialog.value = false
     uploadRef.value?.clearFiles()
     uploadFiles.value = []