浏览代码

527中英文,功能完善

tangjunhao 3 月之前
父节点
当前提交
a6afa71f93

+ 78 - 0
package-lock.json

@@ -41,6 +41,7 @@
         "vite-plugin-monaco-editor": "^1.1.0",
         "vite-plugin-remove-console": "^2.2.0",
         "vue": "^3.3.4",
+        "vue-i18n": "^12.0.0-alpha.2",
         "vue-monaco-editor": "^0.0.19",
         "vue-pdf-embed": "^2.1.2",
         "vue-router": "4.0",
@@ -2496,6 +2497,63 @@
       "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
       "dev": true
     },
+    "node_modules/@intlify/core-base": {
+      "version": "12.0.0-alpha.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-12.0.0-alpha.2.tgz",
+      "integrity": "sha512-sPWvQ1Z4Wyw9Kp8xqjAk2sMOeZ4pO7p/NL3Eol8l9a7iPyMTuHyJ2DZVbOBG6zDnCupvLAqnRMAT1LAgvx0QRQ==",
+      "dependencies": {
+        "@intlify/message-compiler": "12.0.0-alpha.2",
+        "@intlify/shared": "12.0.0-alpha.2"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/@intlify/message-compiler": {
+      "version": "12.0.0-alpha.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-12.0.0-alpha.2.tgz",
+      "integrity": "sha512-PD9C+oQbb7BF52hec0+vLnScaFkvnfX+R7zSbODYuRo/E2niAtGmHd0wPvEMsDhf9Z9b8f/qyDsVeZnD/ya9Ug==",
+      "dependencies": {
+        "@intlify/shared": "12.0.0-alpha.2",
+        "source-map-js": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/@intlify/shared": {
+      "version": "12.0.0-alpha.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-12.0.0-alpha.2.tgz",
+      "integrity": "sha512-P2DULVX9nz3y8zKNqLw9Es1aAgQ1JGC+kgpx5q7yLmrnAKkPR5MybQWoEhxanefNJgUY5ehsgo+GKif59SrncA==",
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      }
+    },
+    "node_modules/@intlify/vue-i18n-core": {
+      "version": "12.0.0-alpha.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/vue-i18n-core/-/vue-i18n-core-12.0.0-alpha.2.tgz",
+      "integrity": "sha512-y1NPEcPbD8xqWGiaEREkA9WxxWbxmd8IurN176w39MenZKEf5P20rYyk0w4r718+w/9jjm0m5zR1ed6uMaZT2Q==",
+      "dependencies": {
+        "@intlify/core-base": "12.0.0-alpha.2",
+        "@intlify/shared": "12.0.0-alpha.2",
+        "@vue/devtools-api": "^6.5.0"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
     "node_modules/@jridgewell/gen-mapping": {
       "version": "0.3.4",
       "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.4.tgz",
@@ -7832,6 +7890,26 @@
         "eslint": ">=6.0.0"
       }
     },
+    "node_modules/vue-i18n": {
+      "version": "12.0.0-alpha.2",
+      "resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-12.0.0-alpha.2.tgz",
+      "integrity": "sha512-ZSaZrDV/PhD4hLVybo84bkaNRnkGDF7GpI0Fcmn9Yj+2Kq5C3nE7I5iRbo+DiHyRGrwZ/IV1VP3naFAhcNJGsg==",
+      "dependencies": {
+        "@intlify/core-base": "12.0.0-alpha.2",
+        "@intlify/shared": "12.0.0-alpha.2",
+        "@intlify/vue-i18n-core": "12.0.0-alpha.2",
+        "@vue/devtools-api": "^6.5.0"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
     "node_modules/vue-monaco-editor": {
       "version": "0.0.19",
       "resolved": "https://registry.npmmirror.com/vue-monaco-editor/-/vue-monaco-editor-0.0.19.tgz",

+ 1 - 0
package.json

@@ -43,6 +43,7 @@
     "vite-plugin-monaco-editor": "^1.1.0",
     "vite-plugin-remove-console": "^2.2.0",
     "vue": "^3.3.4",
+    "vue-i18n": "^12.0.0-alpha.2",
     "vue-monaco-editor": "^0.0.19",
     "vue-pdf-embed": "^2.1.2",
     "vue-router": "4.0",

+ 2 - 2
src/components/layout/header.vue

@@ -8,7 +8,7 @@
                 ></el-image>
             </div>
             <h3 class="ve_logo_title">
-                {{ title }}
+                {{ $t("index.subtitle") }}
             </h3>
         </div>
 
@@ -17,7 +17,7 @@
 import { ref, onMounted, reactive, } from "vue";
 import logo from "@/assets/img/eslogo.png";
 import { ElMessage, ElButton, ElDialog, ElSelect } from 'element-plus'
-let title=ref("Web-Based Engine System-Level Simulation Software");
+
 </script>
 <style lang="scss" scoped>
 .ve_menu_logo {

+ 78 - 8
src/components/layout/openpage.vue

@@ -7,19 +7,42 @@
       <div>
         <div class="open-page-header">
           <div class="open-page-header-title">
-            PLATFORM
+            {{ $t('index.headetag') }}
           </div>
-          <div>
+          <div class="open-page-header-icons">
             <el-button class="custom-icon-button">
               <img :src="zk" style="width: 22px;"/>
             </el-button>
             <el-button class="custom-icon-button">
               <img :src="help" style="width: 22px;"/>
             </el-button>
-            <el-button class="custom-icon-button">
-              <img :src="user" style="width: 22px;"/>
-              <span>user</span>
-            </el-button>
+            <el-avatar :src="user" :size="22"/>
+            <el-dropdown>
+              <div class="user-dropdown-trigger">
+                <span class="nickname">{{ nickName }}</span>
+                <el-icon class="el-icon--right"><arrow-down /></el-icon>
+              </div>
+
+             <template #dropdown>
+                <el-dropdown-menu>
+                <!-- 上半部分:个人信息和修改密码 -->
+                  <el-dropdown-item @click="handleProfile">
+                    <el-icon><User /></el-icon>
+                    <span>{{ $t('index.userinfo') }}</span>
+                  </el-dropdown-item>
+                  <el-dropdown-item @click="handleChangePassword">
+                    <el-icon><Key /></el-icon>
+                    <span>{{ $t('index.changePassword') }}</span>
+                  </el-dropdown-item>
+
+                  <!-- 下半部分:退出登录 -->
+                  <el-dropdown-item divided @click="handleLogout" style="color: #F56C6C;">
+                    <el-icon><SwitchButton /></el-icon>
+                    <span>{{ $t('index.logout') }}</span>
+                  </el-dropdown-item>
+                  </el-dropdown-menu>
+              </template>
+            </el-dropdown>
           </div>
         </div>
         <div class="open-page-content">
@@ -31,11 +54,11 @@
             >
               <el-menu-item index="/project">
                 <img :src="ceng" style="width: 18px; margin-right: 5px;" />
-                <span>Project</span>
+                <span>{{ $t('index.project') }}</span>
               </el-menu-item>
               <el-menu-item index="/preference">
                 <img :src="set" style="width: 18px; margin-right: 5px;" />
-                <span>Preference</span>
+                <span>{{ $t('index.preferences') }}</span>
               </el-menu-item>
             </el-menu>
           </div>
@@ -52,6 +75,13 @@
 import myheader from '@/components/layout/header.vue'
 import { RouterView, RouterLink,useRouter } from "vue-router"
 import { request, uploadFile } from "@/utils/request";
+import { ElMessage } from 'element-plus'
+import { useUserStore } from '@/store/user'
+import { removeToken,removeUserId} from "@/utils/token";
+import { ArrowDown,User,Key,SwitchButton } from '@element-plus/icons-vue'
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 
 import set from '@/assets/img/set.png'
 import ceng from '@/assets/img/ceng.png'
@@ -60,6 +90,9 @@ import help from '@/assets/img/help.png'
 import user from '@/assets/img/user.png'
 
 const router = useRouter();
+const userStore = useUserStore();
+
+const nickName = userStore.userInfo.nickName || 'User';
 
 const activeIndex = computed(() => {
   // 获取当前路由的所有匹配路径
@@ -68,6 +101,23 @@ const activeIndex = computed(() => {
   return matched[matched.length - 1]?.path || '/project'
 })
 
+const handleProfile = () => {
+  // router.push('/profile');
+}
+
+const handleChangePassword = () => {
+  // router.push('/change-password');
+}
+
+const handleLogout = () => {
+  userStore.clearUserInfo();
+  removeToken();
+  removeUserId();
+  // 跳转到登录页面
+  ElMessage.success(t('message.logoutSuccess'));
+  router.push('/login');
+}
+
 </script>
 
 <style scoped>
@@ -85,10 +135,24 @@ const activeIndex = computed(() => {
 }
 
 .open-page-header-title{
+  margin-left: 10px;
   font-size: 15px;
   color: #333333;
 }
 
+.open-page-header-icons{
+  width: 150px;
+  display: flex;
+  align-items: center;
+  justify-content: space-around;
+}
+
+.open-page-header-icons .el-button {
+  padding: 0;
+  border: none;
+  margin: 0;
+}
+
 .open-page-content{
   display: flex;
   width: 100%;
@@ -127,4 +191,10 @@ const activeIndex = computed(() => {
   text-transform: none;
 }
 
+.user-dropdown-trigger {
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+}
+
 </style>

+ 75 - 0
src/locales/en.json

@@ -0,0 +1,75 @@
+{
+  "message": {
+    "hello": "Hello World",
+    "welcome": "Welcome to our app",
+    "confirmDelete": "Are you sure to delete the selected items?",
+    "deleteSuccess": "Delete successful",
+    "deleteFailed": "Delete failed",
+    "deleteCancelled": "Deletion cancelled",
+    "logoutSuccess": "Logout successful"
+  },
+  "common": {
+    "tip": "Tip",
+    "confirm": "Confirm",
+    "ok":"OK",
+    "cancel": "Cancel"
+  },
+  "preferences": {
+    "title": "Preferences",
+    "theme": "Theme",
+    "language": "Language",
+    "notifications": "Notifications",
+    "privacy": "Privacy Settings"
+  },
+  "login": {
+    "title": "Login",
+    "subtitle": "Web-Based Engine System-Level Simulation Software",
+    "username": "Username",
+    "password": "Password",
+    "rememberMe": "Remember Me",
+    "loginButton": "Login",
+    "forgotPassword": "Forgot Password?",
+    "register": "Register"
+  },
+  "index": {
+    "title": "Home",
+    "subtitle": "Web-Based Engine System-Level Simulation Software",
+    "headetag": "PLATFORM",
+    "project": "Project",
+    "preferences": "Preferences",
+    "userinfo": "User Info",
+    "changePassword": "Change Password",
+    "logout": "Logout"
+  },
+  "project": {
+    "open": "OPEN",
+    "create": "New Project",
+    "newcompound": "New Compound",
+    "newlibrary": "New Library",
+    "projectlist": "Project List",
+    "number": "Number",
+    "name": "Project Name",
+    "dirsize": "Size",
+    "updateTime": "Last Modified",
+    "uname": "Author",
+    "keywords": "Keywords",
+    "description": "Description",
+    "total": "Total",
+    "selectAll": "Select All",
+    "delete": "Delete",
+    "ok": "OK"
+  },
+  "dialog": {
+    "ok": "OK",
+    "cancel": "Cancel",
+    "error": "Error",
+    "success": "Success",
+    "warning": "Warning",
+    "info": "Info",
+    "loading": "Loading",
+    "new": "New",
+    "edit": "Edit",
+    "delete": "Delete",
+    "save": "Save"
+  }
+}

+ 76 - 0
src/locales/zh-CN.json

@@ -0,0 +1,76 @@
+{
+  "message": {
+    "hello": "你好世界",
+    "welcome": "欢迎使用我们的应用",
+    "confirmDelete": "您确定要删除选中的项目吗?",
+    "deleteSuccess": "删除成功",
+    "deleteFailed": "删除失败",
+    "deleteCancelled": "已取消删除",
+    "logoutSuccess": "退出登录成功"
+  },
+  "common": {
+    "tip": "提示",
+    "confirm": "确认",
+    "ok": "确定",
+    "cancel": "取消"
+  },
+  "preferences": {
+    "title": "偏好设置",
+    "theme": "主题",
+    "language": "语言",
+    "notifications": "通知",
+    "privacy": "隐私设置"
+  },
+  "login": {
+    "title": "登录",
+    "subtitle": "基于Web的引擎系统级仿真软件",
+    "username": "用户名",
+    "password": "密码",
+    "rememberMe": "记住密码",
+    "loginButton": "登录",
+    "forgotPassword": "忘记密码?",
+    "register": "注册"
+  },
+  "index": {
+    "title": "首页",
+    "subtitle": "基于Web的引擎系统级仿真软件",
+    "headetag": "平台",
+    "project": "项目",
+    "preferences": "偏好设置",
+    "userinfo": "个人信息",
+    "changePassword": "修改密码",
+    "logout": "退出登录"
+  },
+  "project": {
+    "open": "打开",
+    "create": "新建项目",
+    "newcompound": "新建化合物",
+    "newlibrary": "新建库",
+    "projectlist": "项目列表",
+    "number": "编号",
+    "name": "项目名",
+    "dirsize": "大小",
+    "updateTime": "最后修改",
+    "uname": "作者",
+    "keywords": "关键词",
+    "description": "描述",
+    "total": "总计",
+    "selectAll": "全选",
+    "delete": "删除",
+    "ok": "确定"
+  },
+  "dialog": {
+    "ok": "确认",
+    "cancel": "取消",
+    "error": "错误",
+    "success": "成功",
+    "warning": "警告",
+    "info": "提示",
+    "loading": "加载中",
+    "new": "新建",
+    "edit": "编辑",
+    "delete": "删除",
+    "save": "保存"
+  }
+
+}

+ 42 - 15
src/main.ts

@@ -1,35 +1,62 @@
 import { createApp } from 'vue'
 import './style.css'
 import App from './App.vue'
-//1、route
+
+// 1. 路由
 import router from '@/router/index'
-//2、pinia
+// 2. Pinia
 import pinia from '@/store'
-//3、element-plus
+// 3. Element Plus
 import ElementPlus from 'element-plus'
-import "normalize.css/normalize.css";//重置样式
+import 'normalize.css/normalize.css'
 import 'element-plus/dist/index.css'
-import './style/index.css' // 
-//import './style/style.css'
+import './style/index.css'
 
+// 其他 JS 库
 import '@/js/lindex.js'
 import '@/js/jquery.min.js'
 import '@/js/webuploader.min.js'
-//4、引入echarts
+
+// ECharts
 import * as echarts from 'echarts'
+
+// 字体
 import './assets/fonts/fonts.css'
+
+// i18n
+import i18n from './utils/i18n'
+
+// Element Plus 语言包
+import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
+import en from 'element-plus/dist/locale/en.mjs'
+
+// 获取用户上次选择的语言
+const savedLocale = localStorage.getItem('locale') || 'zh-CN'
+
+// 设置 Element Plus 对应语言
+const elementLocale = savedLocale === 'zh-CN' ? zhCn : en
+
+// 设置 vue-i18n 的语言(注意:必须在挂载前设置)
+i18n.global.locale.value = savedLocale
+
+// 创建 App 实例
 const app = createApp(App)
 
-//1、route
+// 注册插件
 app.use(router)
-//2、pinia
 app.use(pinia)
-//3、element-plus
-app.use(ElementPlus)
-//4、放入全局
+app.use(i18n)
+app.use(ElementPlus, {
+  locale: elementLocale
+})
+
+// 设置全局属性
 app.config.globalProperties.$echarts = echarts
+
+// 挂载
 app.mount('#app')
+
+// 错误处理
 app.config.errorHandler = function (err, vm, info) {
-    // 处理错误,例如记录日志
-    console.error('Vue error:', err, info);
-};
+  console.error('Vue error:', err, info)
+}

+ 9 - 0
src/router/index.js

@@ -23,6 +23,15 @@ const router = createRouter({
                 title: '项目'
               }
             },
+            {
+              path: '/preference',
+              name: 'preference',
+              component: () => import('@/views/preference/index.vue'),
+              meta: {
+                keepAlive: false,
+                title: '偏好设置'
+              }
+            },
           ]
       },
       {

+ 35 - 0
src/store/user.js

@@ -0,0 +1,35 @@
+import { defineStore } from 'pinia'
+import { ref } from 'vue'
+
+export const useUserStore = defineStore('user', () => {
+  // 用户信息
+  const userInfo = ref(null)
+  // token
+  const token = ref('')
+  
+  // 设置用户信息
+  const setUserInfo = (info) => {
+    userInfo.value = info
+  }
+  
+  // 设置token
+  const setToken = (newToken) => {
+    token.value = newToken
+  }
+  
+  // 清除用户信息
+  const clearUserInfo = () => {
+    userInfo.value = null
+    token.value = ''
+  }
+  
+  return {
+    userInfo,
+    token,
+    setUserInfo,
+    setToken,
+    clearUserInfo
+  }
+},{
+  persist: true // 启用持久化
+})

+ 66 - 0
src/style/index.css

@@ -65,6 +65,15 @@ img{
   color: white !important;
 }
 
+.el-message-box__btns button:nth-child(2) {
+  background-color: #12739E !important;
+  color: white !important;
+}
+
+.el-form-item__label {
+  justify-content: flex-start;
+}
+
 .custom-pagination .el-pagination .el-pager li {
   font-size: 12px;
   color: #1A1A1A;
@@ -110,3 +119,60 @@ img{
   padding: 0;
   border: none;
 }
+
+/* 在全局或组件样式添加 */
+.el-dropdown-menu {
+  outline: none !important;
+}
+
+.user-dropdown-trigger:focus {
+  outline: none !important;
+}
+
+.el-dialog__headerbtn:focus {
+  outline: none;
+}
+
+.dialog_class {
+  padding: 0px ;
+  color: #333333;
+  font-size: 16px;
+}
+
+.dialog_class .el-dialog__header {
+  height: 33px;
+  background: #12739E;
+  margin-right: 0;
+  padding: 5px 20px;
+}
+
+.dialog_class .el-dialog__header .el-dialog__title {
+  color: #FFFFFF;
+  font-weight: 400;
+  font-size: 14px;
+  color: #FFFFFF;
+  text-align: left;
+  font-style: normal;
+  text-transform: none;
+}
+
+.dialog_class .el-dialog__header .el-dialog__headerbtn {
+  top: 2px;
+  right: 10px;
+  width: 33px;
+  height: 33px;
+}
+
+/* 弹窗关闭按钮X大小 */
+.dialog_class .el-dialog__header .el-dialog__headerbtn .el-icon svg {
+  width: 24px;
+  height: 24px;
+}
+
+.dialog_class .el-dialog__body {
+  padding: 20px 10px 10px 20px;
+}
+
+.dialog_class .el-dialog__footer {
+  padding: 0px 10px 10px 10px;
+}

+ 18 - 0
src/utils/i18n.js

@@ -0,0 +1,18 @@
+//中英文国际化配置
+import { createI18n } from 'vue-i18n'
+import en from '../locales/en.json'
+import zhCN from '../locales/zh-CN.json'
+
+const messages = {
+  en,
+  'zh-CN': zhCN
+}
+
+const i18n = createI18n({
+  legacy: false, // 使用Composition API模式
+  locale: 'zh-CN', // 默认语言
+  fallbackLocale: 'en', // 备用语言
+  messages
+})
+
+export default i18n

+ 22 - 6
src/views/login/index.vue

@@ -12,7 +12,7 @@
           <template #header>
             <div class="login-header">
               <img :src="loginLogo" alt="logo" class="login-logo"/>
-              <h2>Web-Based Engine System-Level Simulation Software</h2>
+              <h2>{{ $t('login.subtitle') }}</h2>
             </div>
           </template>
 
@@ -24,7 +24,7 @@
             hide-required-asterisk
             label-position="left"
           >
-            <el-form-item prop="username" label="Username:" style="margin-bottom: 20px;" label-width="100px">
+            <el-form-item prop="username" :label="`${$t('login.username')}:`" style="margin-bottom: 20px;" label-width="100px">
               <el-input
                 v-model="loginForm.username"
                 placeholder=""
@@ -33,7 +33,7 @@
               />
             </el-form-item>
 
-            <el-form-item prop="password" label="Password:" style="margin-bottom: 20px;" label-width="100px">
+            <el-form-item prop="password" :label="`${$t('login.password')}:`" style="margin-bottom: 20px;" label-width="100px">
               <el-input
                 v-model="loginForm.password"
                 placeholder=""
@@ -45,8 +45,8 @@
           
             <el-form-item prop="remember" style="margin-bottom: 20px;">
               <div style="width: 100%; display: flex; justify-content: space-between;">
-                <el-checkbox v-model="loginForm.remember" class="remember-me">Remember me</el-checkbox>
-                <el-link type="info" class="forgot-password">Forgot password?</el-link>
+                <el-checkbox v-model="loginForm.remember" class="remember-me">{{ $t('login.rememberMe') }}</el-checkbox>
+                <el-link type="info" class="forgot-password">{{ $t('login.forgotPassword') }}</el-link>
               </div>
             </el-form-item>
 
@@ -58,7 +58,7 @@
                 @click="handleLogin"
                 
               >
-                Login
+                {{ $t('login.loginButton') }}
               </el-button>
             </el-form-item>
           </el-form>
@@ -81,12 +81,16 @@ import { request,enPassword } from "@/utils/request"
 import secureStorage from '@/utils/secureStorage'
 
 import { getToken, setToken, setUserId} from "@/utils/token"
+import { useUserStore } from '@/store/user'
 
 import loginbg from '@/assets/img/login-bg.png'
 import loginLogo from '@/assets/img/login-logo.png'
 // 表单引用
 const loginFormRef = ref(null)
 
+// 获取userStore实例
+const userStore = useUserStore()
+
 // 登录表单数据
 const loginForm = reactive({
   username: secureStorage.get('savedUsername') || '',
@@ -134,6 +138,18 @@ const handleLogin = () => {
     .then((res) => {
       setToken(res.clientToken)
       setUserId(res.userId)
+
+      userStore.setToken(res.clientToken)
+      // 存储用户信息到pinia
+      userStore.setUserInfo({
+        userId: res.userId,
+        userName: res.userName,
+        nickName: res.nickName,
+        userType: res.userType,
+        regTime: res.regTime,
+        mobileNo: res.mobileNo,
+      })
+
       router.push({ path: "/" })
     })
     .catch((err) => {

+ 76 - 0
src/views/preference/index.vue

@@ -0,0 +1,76 @@
+<template>
+  <div class="preference">
+    <div class="preference-container">
+      <el-card class="preference-card" shadow="never">
+        <template #header>
+          <div class="preference-header">
+            <h3>{{ $t('preferences.title') }}</h3>
+          </div>
+        </template>
+        <div class="preference-content">
+          <el-form label-width="80px">
+            <el-form-item :label="`${$t('preferences.language')}:`">
+              <el-select v-model="currentLang" placeholder="Select Language">
+                <el-option
+                  v-for="lang in languages"
+                  :key="lang.value"
+                  :label="lang.label"
+                  :value="lang.value"
+                />
+              </el-select>
+            </el-form-item>
+          </el-form>
+        </div>
+      </el-card>
+    </div>
+
+  </div>
+</template>
+
+<script setup>
+
+import { getCurrentInstance } from 'vue'
+import { useI18n } from 'vue-i18n'
+import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
+import en from 'element-plus/dist/locale/en.mjs'
+
+const { appContext } = getCurrentInstance()
+const { locale } = useI18n()
+const currentLang = ref(locale.value)
+
+const languages = [
+  { value: 'zh-CN', label: '中文' },
+  { value: 'en', label: 'English' }
+]
+
+watch(currentLang, (newVal) => {
+  locale.value = newVal
+  localStorage.setItem('locale', newVal)
+  
+  // 设置 Element Plus 的语言
+  const elementLocale = newVal === 'zh-CN' ? zhCn : en
+  appContext.config.globalProperties.$ELEMENT = { locale: elementLocale }
+})
+
+
+</script>
+
+<style lang="scss" scoped>
+.preference {
+  width: 100%;
+  height: 100%;
+  background: #EEEEEE;
+  padding: 8px 8px 0 8px;
+  overflow: auto;
+}
+
+.preference-container {
+  width: 100%;
+  height: 100%;
+  background: #FFFFFF;
+  border-radius: 0px 0px 0px 0px;
+  border: 1px solid #EEEEEE;
+  padding: 16px;
+}
+
+</style>

+ 183 - 27
src/views/project/index.vue

@@ -4,19 +4,19 @@
       <div class="boxcontainer">
         <el-button class="box">
           <img :src="getImgPath('open.png')" style="width: 20px; margin-right: 5px;" />
-          OPEN
+          {{ $t('project.open') }}
         </el-button>
-        <el-button class="box">
+        <el-button class="box" @click="addProjectdialog = true">
           <img :src="getImgPath('blueplus.png')" style="width: 20px; margin-right: 5px;" />
-          New Project
+          {{ $t('project.create') }}
         </el-button>
         <el-button class="box">
           <img :src="getImgPath('blueplus.png')" style="width: 20px; margin-right: 5px;" />
-          New Compound
+          {{ $t('project.newcompound') }}
         </el-button>
         <el-button class="box">
           <img :src="getImgPath('blueplus.png')" style="width: 20px; margin-right: 5px;" />
-          New Library
+          {{ $t('project.newlibrary') }}
         </el-button>
       </div>
       
@@ -25,12 +25,12 @@
     <el-main class="project-main">
       <div class="project-main-header">
         <div class="project-main-header-title">
-            Project List
+            <span style="margin-right: 10px;">{{ $t('project.projectlist') }}</span>
+            <el-input v-model="gd.searchtag" style="width: 200px;margin-right: 5px;">
+            </el-input>
+            <el-button @click="getprojectlist" :icon="Search" style="width: 22px;" class="custom-icon-button"/>
         </div>
         <div class="project-main-header-actions">
-          <el-select v-model="select1" style="width: 120px;margin-right: 5px;">
-            <el-option label="Save" value="save"></el-option>
-          </el-select>
           <el-button class="custom-icon-button">
             <img :src="getImgPath('daohang.png')" style="width: 22px;"/>
           </el-button>
@@ -44,18 +44,24 @@
       </div>  
       <div class="project-main-content custom-table">
         <el-table
+          ref="tableRef"
           :data="projectlists"
-          style="width: 100%;height: 540px;"
+          style="width: 100%;height: 540px; overflow: auto;"
+          @row-click="handleRowClick"
+          :row-class-name="tableRowClassName"
         >
-          <el-table-column type="index" label="Number" width="100"></el-table-column>
-          <el-table-column prop="name" label="File name" ></el-table-column>
-          <el-table-column prop="dirsize" label="Size" ></el-table-column>
-          <el-table-column prop="updateTime" label="Last modified" ></el-table-column>
-          <el-table-column prop="uname" label="Author" ></el-table-column>
-          <el-table-column prop="keywords" label="Keywords" ></el-table-column>
-          <el-table-column prop="description" label="Description" ></el-table-column>
+          <el-table-column type="index" :label="$t('project.number')" width="100"></el-table-column>
+          <el-table-column prop="name" :label="$t('project.name')" ></el-table-column>
+          <el-table-column prop="dirsize" :label="$t('project.dirsize')" ></el-table-column>
+          <el-table-column prop="updateTime" :label="$t('project.updateTime')" ></el-table-column>
+          <el-table-column prop="uname" :label="$t('project.uname')" ></el-table-column>
+          <el-table-column prop="keywords" :label="$t('project.keywords')" ></el-table-column>
+          <el-table-column prop="description" :label="$t('project.description')" ></el-table-column>
         </el-table>
-        <div class="custom-pagination" style="margin-top: 14px;">
+        
+      </div>
+      <div class="project-main-pagination">
+        <div class="custom-pagination" >
             <el-pagination
                 v-model:current-page="gd.currentPage4"
                 v-model:page-size="gd.pageSize4"
@@ -67,27 +73,67 @@
                 @current-change="handleCurrentChange2"
             >
               <template #default>
-                <span>总计 {{ gd.total }}</span>
+                <span>{{ $t('project.total') }} {{ gd.total }}</span>
               </template>
             </el-pagination>
         </div>
       </div>
       <div class="project-main-footer lastbtn">
-        <el-button >All Select</el-button>
-        <el-button >Delete</el-button>
-        <el-button >OK</el-button>
+        <el-button @click="selectAll">{{ $t('project.selectAll') }}</el-button>
+        <el-button @click="deleteSelected" :disabled="selectedRows.length === 0">{{ $t('project.delete') }}</el-button>
+        <el-button @click="confirmSelected" :disabled="selectedRows.length === 0">{{ $t('project.ok') }}</el-button>
       </div>
     </el-main>
 
   </div>
+
+  <el-dialog v-model="addProjectdialog" align-center :append-to-body="true" width="500" class="dialog_class">
+
+    <template #header="{ titleId, titleClass }">
+    <div class="my-header ">
+        <!-- <el-image :src="icon" fit="contain"></el-image> -->
+        <h4 :id="titleId" :class="titleClass">{{ $t('dialog.new') }}</h4>
+
+    </div>
+    </template>
+    <div>
+    <el-form-item :label="`${$t('project.name')}: `" label-width="120px">
+        <el-input v-model="newproject.name" class="w-50 m-2" placeholder="请输入" maxlength="100"/>
+    </el-form-item>
+    <el-form-item :label="`${$t('project.description')}: `" label-width="120px">
+        <el-input v-model="newproject.description" class="w-50 m-2" placeholder="请输入" maxlength="500"/>
+    </el-form-item>
+    </div>
+    <template #footer>
+      <span class="lastbtn">
+        <el-button @click="addProjectdialog = false">{{ $t('dialog.cancel') }}</el-button>
+        <el-button type="primary" @click="addProject();">
+          {{ $t('dialog.ok') }}
+        </el-button>
+      </span>
+    </template>
+
+  </el-dialog>
   
 </template>
 
 <script setup>
 import { request } from "@/utils/request";
 import { ElMessage, ElButton, ElDialog, ElSelect, ElMessageBox } from 'element-plus'
+import {Search} from '@element-plus/icons-vue'
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 
 let projectlists = ref([]);
+let selectedRows = ref([]);
+let tableRef = ref();
+let addProjectdialog = ref(false);
+
+let newproject = ref({
+    name: '',
+    description: ''
+});
 
 let gd = ref({
     total: 1,
@@ -96,8 +142,6 @@ let gd = ref({
     searchtag: ''
 })
 
-let select1 = ref('save');
-
 const getImgPath = (url) => {
   return new URL(`../../assets/img/${url}`, import.meta.url).href
 }
@@ -111,7 +155,7 @@ const getprojectlist = () => {
     }
     request(params)
         .then((res) => {
-            console.log(res);
+            // console.log(res);
             gd.value.total = res.total;
             projectlists.value=res.rows;
         })
@@ -120,6 +164,90 @@ const getprojectlist = () => {
         })
 };
 
+const handleCurrentChange2=(val)=>{
+    getprojectlist();
+}
+
+const addProject = () => {
+  const params = {
+    transCode: 'ES0002',
+    name: newproject.value.name,
+    description: newproject.value.description
+  };
+  request(params)
+    .then((res) => {
+      console.log(res);
+      ElMessage.success('添加成功');
+      addProjectdialog.value = false;
+      getprojectlist();
+    })
+    .catch((err) => {
+      ElMessage.error(err.returnMsg);
+    });
+};
+
+// 处理行点击事件
+const handleRowClick = (row) => {
+  const index = selectedRows.value.findIndex(item => item.pid === row.pid);
+  if (index === -1) {
+    selectedRows.value = [row]; // 单选模式,只保留一个选中项
+    // selectedRows.value.push(row); // 如果是多选模式,使用 push
+  } else {
+    selectedRows.value.splice(index, 1); // 如果已选中,则取消选中
+  }
+};
+
+// 为行添加类名
+const tableRowClassName = ({ row }) => {
+  return selectedRows.value.some(item => item.pid === row.pid) ? 'selected-row' : '';
+};
+
+// 全选/取消全选
+const selectAll = () => {
+  if (selectedRows.value.length === projectlists.value.length) {
+    selectedRows.value = [];
+  } else {
+    selectedRows.value = [...projectlists.value];
+  }
+};
+
+// 删除选中行
+const deleteSelected = () => {
+  ElMessageBox.confirm(
+    t('message.confirmDelete'), // "确定要删除选中的项目吗?"
+    t('common.tip'),           // "提示"
+    {
+      confirmButtonText: t('common.ok'), // "确定"
+      cancelButtonText: t('common.cancel'),   // "取消"
+      type: 'warning'
+    }
+  ).then(() => {
+    const selectedIds = selectedRows.value.map(row => row.pid)
+    // console.log('删除的项目ID:', selectedIds)
+    const params = {
+      transCode: 'ES0003',
+      pid: selectedIds.join(',')
+    }
+
+    request(params)
+      .then((res) => {
+        ElMessage.success(t('message.deleteSuccess')) // "删除成功"
+        selectedRows.value = []
+        getprojectlist()
+      })
+      .catch((err) => {
+        ElMessage.error(err.returnMsg || t('message.deleteFailed')) // "删除失败"
+      })
+  }).catch(() => {
+    ElMessage.info(t('message.deleteCancelled')) // "已取消删除"
+  })
+}
+
+// 确认选中行
+const confirmSelected = () => {
+  
+};
+
 onMounted(() => {
     getprojectlist();
 });
@@ -197,9 +325,15 @@ onMounted(() => {
 
 .project-main-content{
   width: 100%;
-  height: 80%;
-  
+  height: 75%;
+  overflow: auto;
+}
+
+.project-main-pagination{
+  width: 100%;
+  height: 5%;
 }
+
 .project-main-footer {
   width: 100%;
   height: 10%;
@@ -220,4 +354,26 @@ onMounted(() => {
   font-size: 14px;
   color: #12739E;
 }
+
+
+/* 选中行的样式 */
+:deep(.el-table .selected-row) {
+  background-color: #F3F8FB !important;
+  position: relative;
+}
+
+:deep(.el-table .selected-row td:first-child) {
+  border-left: 4px solid #12739E !important;
+}
+
+:deep(.el-table .selected-row td) {
+  border-top: 1px solid #12739E !important;
+  /* border-right: 1px solid #12739E !important; */
+  border-bottom: 1px solid #12739E !important;
+}
+
+:deep(.el-table .selected-row td:last-child) {
+  border-right: 1px solid #12739E !important;
+}
+
 </style>