diff --git a/.gitignore b/.gitignore index 399c822..62db2a4 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ dist/ */dist/ build/ */build/ +**/pnpm-lock.yaml # pnpm .pnpm-debug.log* @@ -31,7 +32,6 @@ build/ Thumbs.db # Logs -/logs/ *.log npm-debug.log* yarn-debug.log* @@ -45,4 +45,4 @@ coverage/ backend/prisma/migrations/ tmpclaude-* -.claude/worktrees/ +.claude/worktrees/ diff --git a/java-backend/src/main/java/com/lesingle/creation/service/impl/AuthServiceImpl.java b/java-backend/src/main/java/com/lesingle/creation/service/impl/AuthServiceImpl.java index a847d87..71e3a3e 100644 --- a/java-backend/src/main/java/com/lesingle/creation/service/impl/AuthServiceImpl.java +++ b/java-backend/src/main/java/com/lesingle/creation/service/impl/AuthServiceImpl.java @@ -46,34 +46,13 @@ public class AuthServiceImpl implements AuthService { log.info("登录请求:username={}, tenantCode={}", username, tenantCode); - // 1. 根据用户名查询用户 - User user = userMapper.selectOne(new LambdaQueryWrapper() - .eq(User::getUsername, username) - .eq(User::getDeleted, 0)); - - if (user == null) { - log.warn("用户不存在:{}", username); - throw new BusinessException("用户名或密码错误"); - } - - log.info("用户查询成功,ID={}, tenantId={}", user.getId(), user.getTenantId()); - log.info("数据库密码哈希:{}", user.getPassword()); - log.info("输入密码:{}", password); - - // 2. 验证密码 - boolean passwordMatches = passwordEncoder.matches(password, user.getPassword()); - log.info("密码验证结果:{}", passwordMatches); - if (!passwordMatches) { - log.warn("密码不匹配,用户名:{}", username); - throw new BusinessException("用户名或密码错误"); - } - - // 3. 验证租户 - Long tenantId; + // 1. 确定租户(优先使用 tenantCode) + Tenant tenant = null; + Long tenantId = null; if (tenantCode != null && !tenantCode.isEmpty()) { - // 根据租户编码查询租户 - Tenant tenant = tenantMapper.selectOne(new LambdaQueryWrapper() - .eq(Tenant::getCode, tenantCode)); + tenant = tenantMapper.selectOne(new LambdaQueryWrapper() + .eq(Tenant::getCode, tenantCode) + .eq(Tenant::getDeleted, 0)); if (tenant == null) { throw new BusinessException("租户不存在"); } @@ -81,16 +60,49 @@ public class AuthServiceImpl implements AuthService { throw new BusinessException("租户已失效"); } tenantId = tenant.getId(); - } else { - tenantId = user.getTenantId(); - } - if (tenantId == null) { - throw new BusinessException("请指定租户编码"); } - // 4. 验证租户是否有效 - Tenant tenant = tenantMapper.selectById(tenantId); + // 2. 根据租户 + 用户名查询用户(避免跨租户同名导致 selectOne 返回多条) + User user; + if (tenantId != null) { + user = userMapper.selectOne(new LambdaQueryWrapper() + .eq(User::getTenantId, tenantId) + .eq(User::getUsername, username) + .eq(User::getDeleted, 0)); + } else { + // 未指定租户时,先查出所有同名用户 + List users = userMapper.selectList(new LambdaQueryWrapper() + .eq(User::getUsername, username) + .eq(User::getDeleted, 0)); + if (users == null || users.isEmpty()) { + log.warn("用户不存在:{}", username); + throw new BusinessException("用户名或密码错误"); + } + if (users.size() > 1) { + throw new BusinessException("存在同名用户,请指定租户编码"); + } + user = users.get(0); + tenantId = user.getTenantId(); + } + + if (user == null) { + log.warn("用户不存在:{}", username); + throw new BusinessException("用户名或密码错误"); + } + + log.info("用户查询成功,ID={}, tenantId={}", user.getId(), user.getTenantId()); + + // 3. 验证密码 + if (!passwordEncoder.matches(password, user.getPassword())) { + log.warn("密码不匹配,用户名:{}", username); + throw new BusinessException("用户名或密码错误"); + } + + // 4. 校验租户有效性(tenantCode 未传时也要校验一次) if (tenant == null) { + tenant = tenantMapper.selectById(tenantId); + } + if (tenant == null || tenant.getDeleted() != 0) { throw new BusinessException("租户不存在"); } if (tenant.getValidState() != 1) { diff --git a/java-backend/src/main/java/com/lesingle/creation/service/impl/MenuServiceImpl.java b/java-backend/src/main/java/com/lesingle/creation/service/impl/MenuServiceImpl.java index 9d741a5..8edad6b 100644 --- a/java-backend/src/main/java/com/lesingle/creation/service/impl/MenuServiceImpl.java +++ b/java-backend/src/main/java/com/lesingle/creation/service/impl/MenuServiceImpl.java @@ -215,7 +215,14 @@ public class MenuServiceImpl extends ServiceImpl implements Me */ private List buildTree(List menus, Long parentId) { return menus.stream() - .filter(menu -> menu.getParentId().equals(parentId)) + .filter(menu -> { + Long pid = menu.getParentId(); + // 兼容历史数据:parent_id 为空视为根节点(0) + if (pid == null) { + pid = 0L; + } + return pid.equals(parentId); + }) .map(this::convertToTreeVO) .peek(vo -> vo.setChildren(buildTree(menus, vo.getId()))) .collect(Collectors.toList()); diff --git a/java-backend/src/main/java/com/lesingle/creation/service/impl/UserDetailsServiceImpl.java b/java-backend/src/main/java/com/lesingle/creation/service/impl/UserDetailsServiceImpl.java index ed38fa6..3460de2 100644 --- a/java-backend/src/main/java/com/lesingle/creation/service/impl/UserDetailsServiceImpl.java +++ b/java-backend/src/main/java/com/lesingle/creation/service/impl/UserDetailsServiceImpl.java @@ -55,16 +55,24 @@ public class UserDetailsServiceImpl implements UserDetailsService { log.debug("通过用户 ID 找到用户:{}, username: {}", userId, user.getUsername()); } else { // 如果通过 ID 没找到,尝试通过用户名查询 - user = userMapper.selectOne(new LambdaQueryWrapper() + List users = userMapper.selectList(new LambdaQueryWrapper() .eq(User::getUsername, username) .eq(User::getDeleted, 0)); + if (users.size() > 1) { + throw new UsernameNotFoundException("存在同名用户,请使用用户ID进行认证"); + } + user = users.isEmpty() ? null : users.get(0); log.debug("通过用户名查询用户:{}, 结果:{}", username, user != null ? "找到" : "未找到"); } } catch (NumberFormatException e) { // 不是数字,通过用户名查询 - user = userMapper.selectOne(new LambdaQueryWrapper() + List users = userMapper.selectList(new LambdaQueryWrapper() .eq(User::getUsername, username) .eq(User::getDeleted, 0)); + if (users.size() > 1) { + throw new UsernameNotFoundException("存在同名用户,请使用用户ID进行认证"); + } + user = users.isEmpty() ? null : users.get(0); log.debug("通过用户名查询用户:{}, 结果:{}", username, user != null ? "找到" : "未找到"); } diff --git a/java-backend/src/main/resources/db/migration/V18__add_missing_permissions.sql b/java-backend/src/main/resources/db/migration/V18__add_missing_permissions.sql deleted file mode 100644 index 1b895e5..0000000 --- a/java-backend/src/main/resources/db/migration/V18__add_missing_permissions.sql +++ /dev/null @@ -1,70 +0,0 @@ --- ============================================ --- 添加缺失的权限(菜单、活动、评审等) --- ============================================ - --- 添加菜单管理权限 -INSERT INTO t_auth_permission (tenant_id, name, code, resource, action, description, valid_state) VALUES -(1, '菜单创建', 'menu:create', 'menu', 'create', '创建菜单权限', 1), -(1, '菜单查询', 'menu:read', 'menu', 'read', '查询菜单权限', 1), -(1, '菜单更新', 'menu:update', 'menu', 'update', '更新菜单权限', 1), -(1, '菜单删除', 'menu:delete', 'menu', 'delete', '删除菜单权限', 1) -ON DUPLICATE KEY UPDATE `name` = VALUES(`name`); - --- 添加活动管理权限 -INSERT INTO t_auth_permission (tenant_id, name, code, resource, action, description, valid_state) VALUES -(1, '活动创建', 'contest:create', 'contest', 'create', '创建活动权限', 1), -(1, '活动查询', 'contest:read', 'contest', 'read', '查询活动权限', 1), -(1, '活动更新', 'contest:update', 'contest', 'update', '更新活动权限', 1), -(1, '活动删除', 'contest:delete', 'contest', 'delete', '删除活动权限', 1), -(1, '活动报名', 'contest:register', 'contest', 'register', '活动报名权限', 1), -(1, '活动评审', 'contest:review', 'contest', 'review', '活动评审权限', 1), -(1, '活动作品', 'contest:work', 'contest', 'work', '活动作品权限', 1), -(1, '活动评委', 'contest:judge', 'contest', 'judge', '活动评委权限', 1), -(1, '活动结果', 'contest:result', 'contest', 'result', '活动结果权限', 1), -(1, '活动规则', 'contest:rule', 'contest', 'rule', '活动规则权限', 1), -(1, '活动公告', 'contest:notice', 'contest', 'notice', '活动公告权限', 1), -(1, '活动评审分配', 'contest:review:assign', 'contest', 'review:assign', '活动评审分配权限', 1), -(1, '活动评审评分', 'contest:review:score', 'contest', 'review:score', '活动评审评分权限', 1) -ON DUPLICATE KEY UPDATE `name` = VALUES(`name`); - --- 添加学校管理权限 -INSERT INTO t_auth_permission (tenant_id, name, code, resource, action, description, valid_state) VALUES -(1, '学校查询', 'school:read', 'school', 'read', '查询学校权限', 1), -(1, '部门查询', 'department:read', 'department', 'read', '查询部门权限', 1), -(1, '年级查询', 'grade:read', 'grade', 'read', '查询年级权限', 1), -(1, '班级查询', 'class:read', 'class', 'read', '查询班级权限', 1), -(1, '教师查询', 'teacher:read', 'teacher', 'read', '查询教师权限', 1), -(1, '学生查询', 'student:read', 'student', 'read', '查询学生权限', 1) -ON DUPLICATE KEY UPDATE `name` = VALUES(`name`); - --- 添加系统管理权限 -INSERT INTO t_auth_permission (tenant_id, name, code, resource, action, description, valid_state) VALUES -(1, '字典查询', 'dict:read', 'dict', 'read', '查询字典权限', 1), -(1, '配置查询', 'config:read', 'config', 'read', '查询配置权限', 1), -(1, '日志查询', 'log:read', 'log', 'read', '查询日志权限', 1), -(1, '权限查询', 'permission:read', 'permission', 'read', '查询权限权限', 1), -(1, '权限创建', 'permission:create', 'permission', 'create', '创建权限权限', 1), -(1, '权限更新', 'permission:update', 'permission', 'update', '更新权限权限', 1), -(1, '权限删除', 'permission:delete', 'permission', 'delete', '删除权限权限', 1) -ON DUPLICATE KEY UPDATE `name` = VALUES(`name`); - --- 获取刚插入的权限 ID 并关联到 super_admin 角色 (id=1) --- 菜单管理权限 -INSERT INTO t_auth_role_permission (role_id, permission_id) -SELECT 1, id FROM t_auth_permission WHERE code IN ('menu:create', 'menu:read', 'menu:update', 'menu:delete') -ON DUPLICATE KEY UPDATE `role_id` = VALUES(`role_id`); - --- 活动管理权限 -INSERT INTO t_auth_role_permission (role_id, permission_id) -SELECT 1, id FROM t_auth_permission WHERE code LIKE 'contest%' -ON DUPLICATE KEY UPDATE `role_id` = VALUES(`role_id`); - --- 学校管理权限 -INSERT INTO t_auth_role_permission (role_id, permission_id) -SELECT 1, id FROM t_auth_permission WHERE code IN ('school:read', 'department:read', 'grade:read', 'class:read', 'teacher:read', 'student:read') -ON DUPLICATE KEY UPDATE `role_id` = VALUES(`role_id`); - --- 系统管理权限 -INSERT INTO t_auth_role_permission (role_id, permission_id) -SELECT 1, id FROM t_auth_permission WHERE code IN ('dict:read', 'config:read', 'log:read', 'permission:read', 'permission:create', 'permission:update', 'permission:delete') -ON DUPLICATE KEY UPDATE `role_id` = VALUES(`role_id`); diff --git a/java-frontend/.env.development b/java-frontend/.env.development index 02f1c27..b0f0980 100644 --- a/java-frontend/.env.development +++ b/java-frontend/.env.development @@ -1,2 +1,2 @@ # 开发环境 -VITE_API_BASE_URL=/api +VITE_API_BASE_URL= diff --git a/java-frontend/.env.production b/java-frontend/.env.production index 0de5427..816b600 100644 --- a/java-frontend/.env.production +++ b/java-frontend/.env.production @@ -1,5 +1,5 @@ # 生产环境 -VITE_API_BASE_URL=/api +VITE_API_BASE_URL= # 如果后端部署在不同域名,可以改成完整地址: # VITE_API_BASE_URL=https://api.your-domain.com diff --git a/java-frontend/src/utils/menu.ts b/java-frontend/src/utils/menu.ts index 3f0aaef..5769553 100644 --- a/java-frontend/src/utils/menu.ts +++ b/java-frontend/src/utils/menu.ts @@ -75,6 +75,11 @@ const componentMap: Record Promise> = { import("@/views/system/public-users/Index.vue"), } +// 用于兜底加载未在 componentMap 中声明的视图组件(避免 Vite 动态 import 分析警告) +const viewComponentModules = import.meta.glob( + "@/views/**/*.vue" +) as Record Promise> + /** * 获取图标组件 */ @@ -89,7 +94,7 @@ export function getIconComponent(iconName: string | null | undefined) { /** * 从菜单路径生成路由名称(与 convertMenusToRoutes 中的逻辑一致) - * 如果路径相同,使用菜单ID来区分,避免路由名称冲突 + * 使用菜单ID来区分,避免路由名称冲突(同一路径可能出现在不同菜单树分支) */ function getRouteNameFromPath( path: string | null | undefined, @@ -101,8 +106,8 @@ function getRouteNameFromPath( .split("/") .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) .join("") - // 如果是子菜单,添加后缀以避免与父菜单路由名称冲突 - return isChild ? `${baseName}${menuId}` : baseName + // 统一添加 menuId,避免顶级菜单同名(例如多个“/contests”菜单)导致路由被覆盖 + return `${baseName}${menuId}` } return `Menu${menuId}` } @@ -255,13 +260,21 @@ export function convertMenusToRoutes( console.warn( `组件路径 "${componentPath}" (key: "${componentKey}") 未在 componentMap 中定义,请添加到 menu.ts 的 componentMap 中` ) - // 如果找不到映射,尝试直接导入(可能会失败,但至少不会阻塞) - componentLoader = () => - import( - /* @vite-ignore */ componentPath.startsWith("@/") - ? componentPath - : `@/views/${componentPath}.vue` - ) + // 如果找不到映射,使用 import.meta.glob 的模块映射进行兜底加载(可被 Vite 分析) + let normalizedPath = componentPath.startsWith("@/") + ? componentPath + : `@/views/${componentPath}` + if (!normalizedPath.endsWith(".vue")) { + normalizedPath = `${normalizedPath}.vue` + } + + const fallbackLoader = viewComponentModules[normalizedPath] + if (fallbackLoader) { + componentLoader = fallbackLoader + } else { + console.warn(`组件路径 "${normalizedPath}" 未找到对应的视图文件,已跳过渲染该菜单`) + componentLoader = undefined + } } } else if (menu.children && menu.children.length > 0) { // 如果没有 component 但有子菜单,使用空布局组件来渲染子路由 diff --git a/java-frontend/src/utils/request.ts b/java-frontend/src/utils/request.ts index 1311ea6..08a95ae 100644 --- a/java-frontend/src/utils/request.ts +++ b/java-frontend/src/utils/request.ts @@ -2,35 +2,47 @@ import axios, { InternalAxiosRequestConfig, type AxiosInstance, type AxiosResponse, -} from "axios" -import { message } from "ant-design-vue" -import { getToken, removeToken, getTenantCode } from "./auth" -import { useAuthStore } from "@/stores/auth" -import router from "@/router" +} from "axios"; +import { message } from "ant-design-vue"; +import { getToken, removeToken, getTenantCode } from "./auth"; +import { useAuthStore } from "@/stores/auth"; +import router from "@/router"; // 扩展 AxiosInstance 类型,添加 postForm 和 putForm 方法 interface ExtendedAxiosInstance extends AxiosInstance { - postForm(url: string, data?: any, config?: InternalAxiosRequestConfig): Promise - putForm(url: string, data?: any, config?: InternalAxiosRequestConfig): Promise + postForm( + url: string, + data?: any, + config?: InternalAxiosRequestConfig, + ): Promise; + putForm( + url: string, + data?: any, + config?: InternalAxiosRequestConfig, + ): Promise; } const service: ExtendedAxiosInstance = axios.create({ - baseURL: import.meta.env.VITE_API_BASE_URL || "/api", + baseURL: import.meta.env.VITE_API_BASE_URL || "", timeout: 30000, headers: { "Content-Type": "application/json", }, -}) +}); // 添加表单提交的便捷方法 -service.postForm = function (url: string, data?: any, config?: InternalAxiosRequestConfig) { - const formData = new URLSearchParams() +service.postForm = function ( + url: string, + data?: any, + config?: InternalAxiosRequestConfig, +) { + const formData = new URLSearchParams(); if (data) { Object.entries(data).forEach(([key, value]) => { if (value != null) { - formData.append(key, String(value)) + formData.append(key, String(value)); } - }) + }); } return this.post(url, formData.toString(), { ...config, @@ -38,17 +50,21 @@ service.postForm = function (url: string, data?: any, config?: InternalAxiosRequ ...config?.headers, "Content-Type": "application/x-www-form-urlencoded", }, - }) -} + }); +}; -service.putForm = function (url: string, data?: any, config?: InternalAxiosRequestConfig) { - const formData = new URLSearchParams() +service.putForm = function ( + url: string, + data?: any, + config?: InternalAxiosRequestConfig, +) { + const formData = new URLSearchParams(); if (data) { Object.entries(data).forEach(([key, value]) => { if (value != null) { - formData.append(key, String(value)) + formData.append(key, String(value)); } - }) + }); } return this.put(url, formData.toString(), { ...config, @@ -56,97 +72,97 @@ service.putForm = function (url: string, data?: any, config?: InternalAxiosReque ...config?.headers, "Content-Type": "application/x-www-form-urlencoded", }, - }) -} + }); +}; // Request interceptor service.interceptors.request.use( (config: InternalAxiosRequestConfig) => { - const token = getToken() + const token = getToken(); if (token && config.headers) { - config.headers.Authorization = `Bearer ${token}` + config.headers.Authorization = `Bearer ${token}`; } // 添加租户信息到请求头 // 租户编码从 URL 获取,租户ID 从用户信息获取 - const tenantCode = getTenantCode() - const authStore = useAuthStore() - const tenantId = authStore.user?.tenantId + const tenantCode = getTenantCode(); + const authStore = useAuthStore(); + const tenantId = authStore.user?.tenantId; if (config.headers) { if (tenantCode) { - config.headers["X-Tenant-Code"] = tenantCode + config.headers["X-Tenant-Code"] = tenantCode; } if (tenantId) { - config.headers["X-Tenant-Id"] = String(tenantId) + config.headers["X-Tenant-Id"] = String(tenantId); } } - return config + return config; }, (error) => { - return Promise.reject(error) - } -) + return Promise.reject(error); + }, +); // Response interceptor service.interceptors.response.use( (response: AxiosResponse) => { - const res = response.data + const res = response.data; // 如果响应已经是统一格式 { code, message, data } if (res && typeof res === "object" && "code" in res) { if (res.code !== 200 && res.code !== 0) { - message.error(res.message || "请求失败") + message.error(res.message || "请求失败"); if (res.code === 401) { - removeToken() + removeToken(); // 从 URL 获取租户编码,跳转到对应的登录页 - const path = window.location.pathname - const match = path.match(/^\/([^/]+)/) - const tenantCode = match ? match[1] : null + const path = window.location.pathname; + const match = path.match(/^\/([^/]+)/); + const tenantCode = match ? match[1] : null; if (tenantCode && tenantCode !== "login") { - router.push(`/${tenantCode}/login`) + router.push(`/${tenantCode}/login`); } else { - router.push("/login") + router.push("/login"); } } - return Promise.reject(new Error(res.message || "请求失败")) + return Promise.reject(new Error(res.message || "请求失败")); } - return res.data !== undefined ? res.data : res + return res.data !== undefined ? res.data : res; } // 如果响应是直接的数据,直接返回 - return res + return res; }, (error) => { const errorMessage = - error.response?.data?.message || error.message || "网络错误" - + error.response?.data?.message || error.message || "网络错误"; + // 403 权限错误显示更友好的提示 if (error.response?.status === 403) { - message.error(errorMessage || "您没有权限执行此操作") + message.error(errorMessage || "您没有权限执行此操作"); } else { - message.error(errorMessage) + message.error(errorMessage); } if (error.response?.status === 401) { - removeToken() + removeToken(); // 从 URL 获取租户编码,跳转到对应的登录页 - const path = window.location.pathname - const match = path.match(/^\/([^/]+)/) - const tenantCode = match ? match[1] : null + const path = window.location.pathname; + const match = path.match(/^\/([^/]+)/); + const tenantCode = match ? match[1] : null; if (tenantCode && tenantCode !== "login") { - router.push(`/${tenantCode}/login`) + router.push(`/${tenantCode}/login`); } else { - router.push("/login") + router.push("/login"); } } - return Promise.reject(error) - } -) + return Promise.reject(error); + }, +); -export default service +export default service; diff --git a/java-frontend/src/views/auth/Login.vue b/java-frontend/src/views/auth/Login.vue index 46e47f9..317112a 100644 --- a/java-frontend/src/views/auth/Login.vue +++ b/java-frontend/src/views/auth/Login.vue @@ -17,31 +17,16 @@
-
+
{{ tab.name }}
-
- @@ -118,7 +91,7 @@ const isDev = import.meta.env.DEV // 开发环境快捷切换 — 按新架构设计 const tenantTabs = [ - { code: "super", name: "平台超管", icon: SafetyOutlined, username: "admin", password: "admin123" }, + { code: "super", name: "平台超管", icon: SafetyOutlined, username: "admin", password: "admin@super" }, { code: "gdlib", name: "广东省图", icon: BankOutlined, username: "admin", password: "admin@gdlib" }, { code: "judge", name: "评委端", icon: TrophyOutlined, username: "admin", password: "admin@judge" }, ] @@ -302,15 +275,20 @@ $teal: #14b8a6; } @keyframes float { - 0%, 100% { + + 0%, + 100% { transform: translate(0, 0) scale(1); } + 25% { transform: translate(25px, -25px) scale(1.04); } + 50% { transform: translate(-15px, 15px) scale(0.96); } + 75% { transform: translate(-25px, -15px) scale(1.02); } diff --git a/java-frontend/vite.config.js b/java-frontend/vite.config.js deleted file mode 100644 index 6113cb7..0000000 --- a/java-frontend/vite.config.js +++ /dev/null @@ -1,37 +0,0 @@ -import { defineConfig } from "vite"; -import vue from "@vitejs/plugin-vue"; -import { resolve } from "path"; -// 根据环境设置 base 路径 -var getBase = function (mode) { - switch (mode) { - case "test": - return "/web-test/"; - case "production": - return "/web/"; - default: - return "/"; - } -}; -// https://vitejs.dev/config/ -export default defineConfig(function (_a) { - var mode = _a.mode; - return { - base: getBase(mode), - plugins: [vue()], - resolve: { - alias: { - "@": resolve(__dirname, "src"), - }, - }, - server: { - port: 3000, - proxy: { - "/api": { - target: "http://localhost:8580", - changeOrigin: true, - rewrite: function (path) { return path.replace(/^\/api/, ''); }, - }, - }, - }, - }; -}); diff --git a/java-frontend/vite.config.ts b/java-frontend/vite.config.ts index 3578627..ae3f233 100644 --- a/java-frontend/vite.config.ts +++ b/java-frontend/vite.config.ts @@ -1,18 +1,18 @@ -import { defineConfig } from "vite" -import vue from "@vitejs/plugin-vue" -import { resolve } from "path" +import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; +import { resolve } from "path"; // 根据环境设置 base 路径 const getBase = (mode: string) => { switch (mode) { case "test": - return "/web-test/" + return "/web-test/"; case "production": - return "/web/" + return "/web/"; default: - return "/" + return "/"; } -} +}; // https://vitejs.dev/config/ export default defineConfig(({ mode }) => { @@ -28,11 +28,11 @@ export default defineConfig(({ mode }) => { port: 3000, proxy: { "/api": { - target: "http://localhost:3234", + target: "http://localhost:8580", changeOrigin: true, // rewrite: (path) => path.replace(/^\/api/, ''), }, }, }, - } -}) + }; +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c48cdd9..6131874 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -235,88 +235,6 @@ importers: specifier: ^3.2.2 version: 3.2.2(typescript@5.9.3) - java-frontend: - dependencies: - '@ant-design/icons-vue': - specifier: ^7.0.1 - version: 7.0.1(vue@3.5.24(typescript@5.9.3)) - '@vee-validate/zod': - specifier: ^4.12.4 - version: 4.15.1(vue@3.5.24(typescript@5.9.3))(zod@3.25.76) - '@wangeditor/editor': - specifier: ^5.1.23 - version: 5.1.23 - '@wangeditor/editor-for-vue': - specifier: ^5.1.12 - version: 5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.24(typescript@5.9.3)) - ant-design-vue: - specifier: ^4.1.1 - version: 4.2.6(vue@3.5.24(typescript@5.9.3)) - axios: - specifier: ^1.6.7 - version: 1.13.2 - dayjs: - specifier: ^1.11.10 - version: 1.11.19 - pinia: - specifier: ^2.1.7 - version: 2.3.1(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3)) - three: - specifier: ^0.182.0 - version: 0.182.0 - vee-validate: - specifier: ^4.12.4 - version: 4.15.1(vue@3.5.24(typescript@5.9.3)) - vue: - specifier: ^3.4.21 - version: 3.5.24(typescript@5.9.3) - vue-router: - specifier: ^4.3.0 - version: 4.6.3(vue@3.5.24(typescript@5.9.3)) - zod: - specifier: ^3.22.4 - version: 3.25.76 - devDependencies: - '@playwright/test': - specifier: ^1.58.2 - version: 1.58.2 - '@types/multer': - specifier: ^2.0.0 - version: 2.0.0 - '@vitejs/plugin-vue': - specifier: ^5.0.4 - version: 5.2.4(vite@5.4.21(@types/node@20.19.25)(sass@1.94.1)(terser@5.44.1))(vue@3.5.24(typescript@5.9.3)) - '@vue/eslint-config-typescript': - specifier: ^13.0.0 - version: 13.0.0(eslint-plugin-vue@9.33.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3) - autoprefixer: - specifier: ^10.4.18 - version: 10.4.22(postcss@8.5.6) - eslint: - specifier: ^8.57.0 - version: 8.57.1 - eslint-plugin-vue: - specifier: ^9.22.0 - version: 9.33.0(eslint@8.57.1) - postcss: - specifier: ^8.4.35 - version: 8.5.6 - sass: - specifier: ^1.71.1 - version: 1.94.1 - tailwindcss: - specifier: ^3.4.1 - version: 3.4.18 - typescript: - specifier: ^5.4.3 - version: 5.9.3 - vite: - specifier: ^5.1.6 - version: 5.4.21(@types/node@20.19.25)(sass@1.94.1)(terser@5.44.1) - vue-tsc: - specifier: ^3.2.2 - version: 3.2.2(typescript@5.9.3) - packages: '@alloc/quick-lru@5.2.0': @@ -1029,11 +947,6 @@ packages: resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@playwright/test@1.58.2': - resolution: {integrity: sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==} - engines: {node: '>=18'} - hasBin: true - '@prisma/client@6.19.0': resolution: {integrity: sha512-QXFT+N/bva/QI2qoXmjBzL7D6aliPffIwP+81AdTGq0FXDoLxLkWivGMawG8iM5B9BKfxLIXxfWWAF6wbuJU6g==} engines: {node: '>=18.18'} @@ -2706,11 +2619,6 @@ packages: fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -3721,16 +3629,6 @@ packages: resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} engines: {node: '>=8'} - playwright-core@1.58.2: - resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==} - engines: {node: '>=18'} - hasBin: true - - playwright@1.58.2: - resolution: {integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==} - engines: {node: '>=18'} - hasBin: true - pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} @@ -5435,10 +5333,6 @@ snapshots: '@pkgr/core@0.2.9': {} - '@playwright/test@1.58.2': - dependencies: - playwright: 1.58.2 - '@prisma/client@6.19.0(prisma@6.19.0(typescript@5.9.3))(typescript@5.9.3)': optionalDependencies: prisma: 6.19.0(typescript@5.9.3) @@ -7374,9 +7268,6 @@ snapshots: fs.realpath@1.0.0: {} - fsevents@2.3.2: - optional: true - fsevents@2.3.3: optional: true @@ -8511,14 +8402,6 @@ snapshots: dependencies: find-up: 3.0.0 - playwright-core@1.58.2: {} - - playwright@1.58.2: - dependencies: - playwright-core: 1.58.2 - optionalDependencies: - fsevents: 2.3.2 - pluralize@8.0.0: {} postcss-import@15.1.0(postcss@8.5.6):