https 替换
This commit is contained in:
@@ -285,12 +285,7 @@ INSERT INTO sys.tb_sys_view (
|
|||||||
'route', NULL, 'workcase', NULL, 162, '工单操作日志', 'system', now(), false),
|
'route', NULL, 'workcase', NULL, 162, '工单操作日志', 'system', now(), false),
|
||||||
|
|
||||||
('VIEW-W109', 'view_workcase_admin_log_system', '系统日志', 'view_workcase_admin_log', '/admin/log/system', 'admin/log/systemLog/SystemLogView.vue', 'Settings', 1,
|
('VIEW-W109', 'view_workcase_admin_log_system', '系统日志', 'view_workcase_admin_log', '/admin/log/system', 'admin/log/systemLog/SystemLogView.vue', 'Settings', 1,
|
||||||
'route', NULL, 'workcase', NULL, 163, '系统运行日志', 'system', now(), false),
|
'route', NULL, 'workcase', NULL, 163, '系统运行日志', 'system', now(), false);
|
||||||
|
|
||||||
-- Jitsi视频会议独立页面(支持URL参数token认证,用于小程序和外部链接访问)
|
|
||||||
('VIEW-W003', 'view_workcase_jitsi_meeting', 'Jitsi视频会议', NULL, '/meeting', 'public/JitsiMeeting/JitsiMeetingView.vue', 'Video', 1,
|
|
||||||
'route', NULL, 'workcase', 'BlankLayout', 25, 'Jitsi视频会议独立页面,支持URL参数token认证', 'system', now(), false);
|
|
||||||
|
|
||||||
-- =============================
|
-- =============================
|
||||||
-- 6. 角色权限关联(超级管理员拥有所有权限)
|
-- 6. 角色权限关联(超级管理员拥有所有权限)
|
||||||
-- =============================
|
-- =============================
|
||||||
@@ -432,9 +427,6 @@ INSERT INTO sys.tb_sys_view_permission (
|
|||||||
('VP-W108', 'view_workcase_admin_log_workcase', 'perm_workcase_log', 'system', NULL, now(), false),
|
('VP-W108', 'view_workcase_admin_log_workcase', 'perm_workcase_log', 'system', NULL, now(), false),
|
||||||
('VP-W109', 'view_workcase_admin_log_system', 'perm_workcase_log', 'system', NULL, now(), false),
|
('VP-W109', 'view_workcase_admin_log_system', 'perm_workcase_log', 'system', NULL, now(), false),
|
||||||
|
|
||||||
-- Jitsi视频会议页面关联会议权限
|
|
||||||
('VP-W003', 'view_workcase_jitsi_meeting', 'perm_meeting_join', 'system', NULL, now(), false);
|
|
||||||
|
|
||||||
-- -- 用户管理视图关联用户权限(已注释,因为view_user被注释掉了)
|
-- -- 用户管理视图关联用户权限(已注释,因为view_user被注释掉了)
|
||||||
-- -- ('VP-0001', 'view_user', 'perm_user_view', 'system', NULL, now(), false),
|
-- -- ('VP-0001', 'view_user', 'perm_user_view', 'system', NULL, now(), false),
|
||||||
-- -- ('VP-0002', 'view_user', 'perm_user_create', 'system', NULL, now(), false),
|
-- -- ('VP-0002', 'view_user', 'perm_user_create', 'system', NULL, now(), false),
|
||||||
|
|||||||
@@ -105,15 +105,15 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- urban-lifeline
|
- urban-lifeline
|
||||||
ports:
|
ports:
|
||||||
- "8280:80" # 保留原 HTTP 端口
|
- "8280:80" # HTTP 端口(通过 Nginx 反向代理)
|
||||||
- "8443:443" # 保留原 HTTPS 端口(仅保留映射,实际禁用 HTTPS)
|
- "8443:443" # HTTPS 端口
|
||||||
environment:
|
environment:
|
||||||
# 基础配置(局域网访问)
|
# 基础配置(Jitsi容器使用HTTP,由Nginx提供HTTPS)
|
||||||
TZ: Asia/Shanghai
|
TZ: Asia/Shanghai
|
||||||
# 关键:使用 http:// 协议的完整 URL
|
# PUBLIC_URL设置为独立子域名
|
||||||
PUBLIC_URL: 192.168.0.253:8280
|
PUBLIC_URL: https://org.xyzh.yslg.jitsi
|
||||||
|
|
||||||
# 关键:禁用 HTTPS,让容器生成 ws:// 而不是 wss://
|
# 禁用容器内部HTTPS(由Nginx统一处理HTTPS)
|
||||||
ENABLE_HTTPS: 0
|
ENABLE_HTTPS: 0
|
||||||
ENABLE_HTTP_REDIRECT: 0
|
ENABLE_HTTP_REDIRECT: 0
|
||||||
DISABLE_HTTPS: 1
|
DISABLE_HTTPS: 1
|
||||||
@@ -142,7 +142,7 @@ services:
|
|||||||
JWT_APP_SECRET: urbanLifeline-jitsi-secret-key-2025-production-safe-hs256
|
JWT_APP_SECRET: urbanLifeline-jitsi-secret-key-2025-production-safe-hs256
|
||||||
JWT_ACCEPTED_ISSUERS: urbanLifeline
|
JWT_ACCEPTED_ISSUERS: urbanLifeline
|
||||||
JWT_ACCEPTED_AUDIENCES: jitsi
|
JWT_ACCEPTED_AUDIENCES: jitsi
|
||||||
JWT_ASAP_KEYSERVER: https://192.168.0.253:8280/
|
JWT_ASAP_KEYSERVER: https://org.xyzh.yslg.jitsi/
|
||||||
JWT_ALLOW_EMPTY: 0
|
JWT_ALLOW_EMPTY: 0
|
||||||
JWT_AUTH_TYPE: token
|
JWT_AUTH_TYPE: token
|
||||||
JWT_TOKEN_AUTH_MODULE: token_verification
|
JWT_TOKEN_AUTH_MODULE: token_verification
|
||||||
@@ -154,9 +154,9 @@ services:
|
|||||||
ENABLE_XMPP_WEBSOCKET: 1
|
ENABLE_XMPP_WEBSOCKET: 1
|
||||||
ENABLE_SCTP: 1
|
ENABLE_SCTP: 1
|
||||||
|
|
||||||
# 日志/HTTPS 配置
|
# 不使用Let's Encrypt(使用mkcert证书)
|
||||||
ENABLE_LETSENCRYPT: 0
|
ENABLE_LETSENCRYPT: 0
|
||||||
LETSENCRYPT_DOMAIN: 192.168.0.253
|
LETSENCRYPT_DOMAIN: org.xyzh.yslg.jitsi
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- ../../../.data/docker/jitsi/web:/config
|
- ../../../.data/docker/jitsi/web:/config
|
||||||
@@ -217,7 +217,7 @@ services:
|
|||||||
LOG_LEVEL: info
|
LOG_LEVEL: info
|
||||||
|
|
||||||
# 公共URL(局域网访问)
|
# 公共URL(局域网访问)
|
||||||
PUBLIC_URL: 192.168.0.253:8280
|
PUBLIC_URL: https://org.xyzh.yslg.jitsi
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- ../../../.data/docker/jitsi/prosody/config:/config
|
- ../../../.data/docker/jitsi/prosody/config:/config
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.xyzh.api.auth.service.AuthService;
|
import org.xyzh.api.auth.service.AuthService;
|
||||||
import org.xyzh.api.system.service.SysUserService;
|
import org.xyzh.api.system.service.SysUserService;
|
||||||
import org.xyzh.api.system.service.ModulePermissionService;
|
import org.xyzh.api.system.service.ModulePermissionService;
|
||||||
|
import org.xyzh.api.system.service.GuestService;
|
||||||
import org.xyzh.api.system.vo.SysUserVO;
|
import org.xyzh.api.system.vo.SysUserVO;
|
||||||
import org.xyzh.api.system.vo.PermissionVO;
|
import org.xyzh.api.system.vo.PermissionVO;
|
||||||
import org.xyzh.api.system.vo.UserDeptRoleVO;
|
import org.xyzh.api.system.vo.UserDeptRoleVO;
|
||||||
|
import org.xyzh.common.dto.sys.TbGuestDTO;
|
||||||
import org.xyzh.auth.enums.UserStatus;
|
import org.xyzh.auth.enums.UserStatus;
|
||||||
import org.xyzh.auth.strategy.LoginStrategyFactory;
|
import org.xyzh.auth.strategy.LoginStrategyFactory;
|
||||||
import org.xyzh.auth.strategy.LoginStrategy;
|
import org.xyzh.auth.strategy.LoginStrategy;
|
||||||
@@ -64,6 +66,9 @@ public class AuthServiceImpl implements AuthService{
|
|||||||
@DubboReference(version = "1.0.0", group = "system", timeout = 5000, check = false, retries = 0)
|
@DubboReference(version = "1.0.0", group = "system", timeout = 5000, check = false, retries = 0)
|
||||||
private ModulePermissionService modulePermissionService;
|
private ModulePermissionService modulePermissionService;
|
||||||
|
|
||||||
|
@DubboReference(version = "1.0.0", group = "system", timeout = 5000, check = false, retries = 0)
|
||||||
|
private GuestService guestService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private LoginStrategyFactory loginStrategyFactory;
|
private LoginStrategyFactory loginStrategyFactory;
|
||||||
|
|
||||||
@@ -418,25 +423,55 @@ public class AuthServiceImpl implements AuthService{
|
|||||||
return ResultDomain.failure("Token已过期");
|
return ResultDomain.failure("Token已过期");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 从Redis获取登录信息
|
// 2. 从JWT中提取用户ID和登录类型
|
||||||
|
String userId = jwtTokenUtil.getUserIdFromToken(token);
|
||||||
|
String loginType = jwtTokenUtil.getClaimFromToken(token, claims -> claims.get("loginType", String.class));
|
||||||
|
|
||||||
|
if (userId == null || userId.trim().isEmpty()) {
|
||||||
|
return ResultDomain.failure("Token信息不完整");
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Token刷新请求:userId={}, loginType={}", userId, loginType);
|
||||||
|
|
||||||
|
// 3. 尝试从Redis获取登录信息(优先使用缓存)
|
||||||
String loginKey = "login:token:" + token;
|
String loginKey = "login:token:" + token;
|
||||||
String loginJson = redisService.get(loginKey, String.class);
|
String loginJson = redisService.get(loginKey, String.class);
|
||||||
if (loginJson == null) {
|
LoginDomain oldLoginDomain = null;
|
||||||
return ResultDomain.failure("登录信息已失效");
|
|
||||||
|
if (loginJson != null) {
|
||||||
|
oldLoginDomain = JSON.parseObject(loginJson, LoginDomain.class);
|
||||||
|
if (loginType == null && oldLoginDomain != null) {
|
||||||
|
loginType = oldLoginDomain.getLoginType();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LoginDomain oldLoginDomain = JSON.parseObject(loginJson, LoginDomain.class);
|
// 4. 根据登录类型获取最新用户信息
|
||||||
|
LoginDomain newLoginDomain;
|
||||||
|
|
||||||
// 3. 获取用户最新信息
|
if ("wechat_miniprogram".equals(loginType)) {
|
||||||
ResultDomain<SysUserVO> userInfoResult = userService.getUserInfo(oldLoginDomain.getUser().getUserId());
|
// 来客(小程序)用户:从guest表获取信息
|
||||||
if (!userInfoResult.getSuccess() || userInfoResult.getData() == null) {
|
TbGuestDTO guestDTO = new TbGuestDTO();
|
||||||
return ResultDomain.failure("获取用户信息失败");
|
guestDTO.setUserId(userId);
|
||||||
|
ResultDomain<TbGuestDTO> guestResult = guestService.selectGuestOne(guestDTO);
|
||||||
|
if (!guestResult.getSuccess() || guestResult.getData() == null) {
|
||||||
|
return ResultDomain.failure("获取来客信息失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
TbGuestDTO guest = guestResult.getData();
|
||||||
|
newLoginDomain = buildGuestLoginDomain(guest, loginType);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 普通用户:从sys_user表获取信息
|
||||||
|
ResultDomain<SysUserVO> userInfoResult = userService.getUserInfo(userId);
|
||||||
|
if (!userInfoResult.getSuccess() || userInfoResult.getData() == null) {
|
||||||
|
return ResultDomain.failure("获取用户信息失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
SysUserVO userInfo = userInfoResult.getData();
|
||||||
|
String effectiveLoginType = (loginType != null) ? loginType : "password";
|
||||||
|
newLoginDomain = buildLoginDomain(userInfo, effectiveLoginType, clientIp);
|
||||||
}
|
}
|
||||||
|
|
||||||
SysUserVO userInfo = userInfoResult.getData();
|
|
||||||
|
|
||||||
// 4. 重新构建LoginDomain
|
|
||||||
LoginDomain newLoginDomain = buildLoginDomain(userInfo, oldLoginDomain.getLoginType(), clientIp);
|
|
||||||
if (newLoginDomain == null) {
|
if (newLoginDomain == null) {
|
||||||
return ResultDomain.failure("构建登录信息失败");
|
return ResultDomain.failure("构建登录信息失败");
|
||||||
}
|
}
|
||||||
@@ -445,19 +480,22 @@ public class AuthServiceImpl implements AuthService{
|
|||||||
String newToken = jwtTokenUtil.generateToken(newLoginDomain);
|
String newToken = jwtTokenUtil.generateToken(newLoginDomain);
|
||||||
newLoginDomain.setToken(newToken);
|
newLoginDomain.setToken(newToken);
|
||||||
|
|
||||||
// 6. 删除旧的Token信息
|
// 6. 删除旧的Token信息(如果存在)
|
||||||
redisService.delete(loginKey);
|
if (loginJson != null) {
|
||||||
|
redisService.delete(loginKey);
|
||||||
|
}
|
||||||
|
|
||||||
// 7. 存储新的登录信息
|
// 7. 存储新的登录信息
|
||||||
String newLoginKey = "login:token:" + newToken;
|
String newLoginKey = "login:token:" + newToken;
|
||||||
redisService.set(newLoginKey, JSON.toJSONString(newLoginDomain), 24, TimeUnit.HOURS);
|
redisService.set(newLoginKey, JSON.toJSONString(newLoginDomain), 24, TimeUnit.HOURS);
|
||||||
|
|
||||||
// 8. 更新用户登录状态
|
// 8. 更新用户登录状态
|
||||||
String userLoginKey = "login:user:" + userInfo.getUserId();
|
String userLoginKey = "login:user:" + userId;
|
||||||
redisService.set(userLoginKey, newToken, 24, TimeUnit.HOURS);
|
redisService.set(userLoginKey, newToken, 24, TimeUnit.HOURS);
|
||||||
|
|
||||||
logger.info("Token刷新成功:userId={}, oldToken={}, newToken={}",
|
logger.info("Token刷新成功:userId={}, loginType={}, oldToken={}, newToken={}",
|
||||||
userInfo.getUserId(),
|
userId,
|
||||||
|
loginType,
|
||||||
token.substring(0, Math.min(10, token.length())) + "...",
|
token.substring(0, Math.min(10, token.length())) + "...",
|
||||||
newToken.substring(0, Math.min(10, newToken.length())) + "...");
|
newToken.substring(0, Math.min(10, newToken.length())) + "...");
|
||||||
|
|
||||||
@@ -469,6 +507,74 @@ public class AuthServiceImpl implements AuthService{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从来客信息构造LoginDomain(用于token刷新)
|
||||||
|
*/
|
||||||
|
private LoginDomain buildGuestLoginDomain(TbGuestDTO guest, String loginType) {
|
||||||
|
try {
|
||||||
|
LoginDomain loginDomain = new LoginDomain();
|
||||||
|
|
||||||
|
// 构造TbSysUserDTO,status设为guest
|
||||||
|
TbSysUserDTO userDTO = new TbSysUserDTO();
|
||||||
|
userDTO.setUserId(guest.getUserId());
|
||||||
|
userDTO.setPhone(guest.getPhone());
|
||||||
|
userDTO.setEmail(guest.getEmail());
|
||||||
|
userDTO.setWechatId(guest.getWechatId());
|
||||||
|
userDTO.setStatus("guest"); // 来客特殊状态
|
||||||
|
loginDomain.setUser(userDTO);
|
||||||
|
|
||||||
|
// 构造TbSysUserInfoDTO
|
||||||
|
TbSysUserInfoDTO userInfoDTO = new TbSysUserInfoDTO();
|
||||||
|
userInfoDTO.setUserId(guest.getUserId());
|
||||||
|
userInfoDTO.setUsername(guest.getName() != null ? guest.getName() : "来客");
|
||||||
|
loginDomain.setUserInfo(userInfoDTO);
|
||||||
|
|
||||||
|
// 设置角色信息
|
||||||
|
List<TbSysUserRoleDTO> userRoles = new ArrayList<>();
|
||||||
|
TbSysUserRoleDTO userRole = new TbSysUserRoleDTO();
|
||||||
|
userRole.setUserId(guest.getUserId());
|
||||||
|
userRole.setRoleId("role_guest");
|
||||||
|
userRole.setDeptId("dept_root");
|
||||||
|
userRoles.add(userRole);
|
||||||
|
loginDomain.setUserRoles(userRoles);
|
||||||
|
|
||||||
|
// 获取用户权限信息
|
||||||
|
List<TbSysPermissionDTO> userPermissions = new ArrayList<>();
|
||||||
|
List<TbSysViewDTO> userViews = new ArrayList<>();
|
||||||
|
|
||||||
|
ResultDomain<PermissionVO> permissionsResult = modulePermissionService.getUserPermissions(guest.getUserId());
|
||||||
|
if (permissionsResult.getSuccess() && permissionsResult.getDataList() != null) {
|
||||||
|
for (PermissionVO permission : permissionsResult.getDataList()) {
|
||||||
|
if (permission.getPermissionId() != null) {
|
||||||
|
TbSysPermissionDTO permissionDTO = PermissionVO.toPermissionDTO(permission);
|
||||||
|
if (permissionDTO != null) {
|
||||||
|
userPermissions.add(permissionDTO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (permission.getViewId() != null) {
|
||||||
|
TbSysViewDTO viewDTO = PermissionVO.toViewDTO(permission);
|
||||||
|
if (viewDTO != null) {
|
||||||
|
userViews.add(viewDTO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loginDomain.setUserPermissions(userPermissions);
|
||||||
|
loginDomain.setUserViews(userViews);
|
||||||
|
|
||||||
|
// 设置登录类型
|
||||||
|
loginDomain.setLoginType(loginType != null ? loginType : "wechat_miniprogram");
|
||||||
|
|
||||||
|
logger.info("构建来客LoginDomain成功:userId={}, name={}", guest.getUserId(), guest.getName());
|
||||||
|
|
||||||
|
return loginDomain;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("构建来客LoginDomain失败:userId={}", guest.getUserId(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResultDomain<LoginDomain> logout(String token) {
|
public ResultDomain<LoginDomain> logout(String token) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -104,8 +104,8 @@ jitsi:
|
|||||||
# 注意:HS256算法要求密钥长度至少32字节(256 bits)
|
# 注意:HS256算法要求密钥长度至少32字节(256 bits)
|
||||||
secret: urbanLifeline-jitsi-secret-key-2025-production-safe-hs256
|
secret: urbanLifeline-jitsi-secret-key-2025-production-safe-hs256
|
||||||
server:
|
server:
|
||||||
# Jitsi Meet服务器地址(Docker部署在本地8280端口)
|
# Jitsi Meet服务器地址(独立子域名)
|
||||||
url: http://localhost:8280
|
url: https://org.xyzh.yslg.jitsi
|
||||||
token:
|
token:
|
||||||
# JWT Token有效期(毫秒)- 默认2小时
|
# JWT Token有效期(毫秒)- 默认2小时
|
||||||
expiration: 7200000
|
expiration: 7200000
|
||||||
@@ -219,7 +219,7 @@ const handleUserCommand = (command: string) => {
|
|||||||
case 'logout':
|
case 'logout':
|
||||||
localStorage.clear()
|
localStorage.clear()
|
||||||
ElMessage.success('退出成功')
|
ElMessage.success('退出成功')
|
||||||
router.push('/login')
|
router.push('/platform/login')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import vue from '@vitejs/plugin-vue'
|
|||||||
import vueJsx from '@vitejs/plugin-vue-jsx'
|
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||||
import { resolve, dirname } from 'path'
|
import { resolve, dirname } from 'path'
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'url'
|
||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url)
|
const __filename = fileURLToPath(import.meta.url)
|
||||||
const __dirname = dirname(__filename)
|
const __dirname = dirname(__filename)
|
||||||
@@ -38,16 +39,16 @@ export default defineConfig(({ mode }) => ({
|
|||||||
host: true,
|
host: true,
|
||||||
cors: true,
|
cors: true,
|
||||||
open: '/bidding/', // 开发时自动打开到 /bidding/ 路径
|
open: '/bidding/', // 开发时自动打开到 /bidding/ 路径
|
||||||
|
// HTTPS 配置(使用 mkcert 生成的本地开发证书)
|
||||||
|
https: {
|
||||||
|
key: fs.readFileSync('C:/Users/FK05/443/localhost+3-key.pem'),
|
||||||
|
cert: fs.readFileSync('C:/Users/FK05/443/localhost+3.pem')
|
||||||
|
},
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://localhost:8180',
|
target: 'http://localhost:8180',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path: string) => path.replace(/^\/api/, '')
|
rewrite: (path: string) => path.replace(/^\/api/, '')
|
||||||
},
|
|
||||||
// 代理共享模块请求到 shared 服务
|
|
||||||
'/shared': {
|
|
||||||
target: 'http://localhost:7000',
|
|
||||||
changeOrigin: true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ const handleUserCommand = (command: string) => {
|
|||||||
case 'logout':
|
case 'logout':
|
||||||
localStorage.clear()
|
localStorage.clear()
|
||||||
ElMessage.success('退出成功')
|
ElMessage.success('退出成功')
|
||||||
router.push('/login')
|
router.push('/platform/login')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ const handleUserCommand = (command: string) => {
|
|||||||
case 'logout':
|
case 'logout':
|
||||||
localStorage.clear()
|
localStorage.clear()
|
||||||
ElMessage.success('退出成功')
|
ElMessage.success('退出成功')
|
||||||
router.push('/login')
|
router.push('/platform/login')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { loadRoutesFromStorage } from './dynamicRoute'
|
|||||||
// platform应用的动态路由会根据layout字段自动添加,不需要预定义Root布局
|
// platform应用的动态路由会根据layout字段自动添加,不需要预定义Root布局
|
||||||
const routes: RouteRecordRaw[] = [
|
const routes: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: '/platform/login',
|
||||||
name: 'Login',
|
name: 'Login',
|
||||||
component: () => import('@/views/public/Login/Login.vue'),
|
component: () => import('@/views/public/Login/Login.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
@@ -37,13 +37,13 @@ router.beforeEach((to, from, next) => {
|
|||||||
if (requiresAuth && !hasToken) {
|
if (requiresAuth && !hasToken) {
|
||||||
// 需要登录但未登录,跳转到登录页
|
// 需要登录但未登录,跳转到登录页
|
||||||
next({
|
next({
|
||||||
path: '/login',
|
path: '/platform/login',
|
||||||
query: { redirect: to.fullPath } // 保存原始路径
|
query: { redirect: to.fullPath } // 保存原始路径
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (to.path === '/login' && hasToken) {
|
if (to.path === '/platform/login' && hasToken) {
|
||||||
// 已登录但访问登录页,跳转到首页
|
// 已登录但访问登录页,跳转到首页
|
||||||
next('/')
|
next('/')
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
|
|||||||
import { federation } from '@module-federation/vite'
|
import { federation } from '@module-federation/vite'
|
||||||
import { resolve, dirname } from 'path'
|
import { resolve, dirname } from 'path'
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'url'
|
||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url)
|
const __filename = fileURLToPath(import.meta.url)
|
||||||
const __dirname = dirname(__filename)
|
const __dirname = dirname(__filename)
|
||||||
@@ -26,7 +27,7 @@ export default defineConfig({
|
|||||||
shared: {
|
shared: {
|
||||||
type: 'module',
|
type: 'module',
|
||||||
name: 'shared',
|
name: 'shared',
|
||||||
entry: 'http://localhost:7000/remoteEntry.js'
|
entry: 'https://org.xyzh.yslg/shared/remoteEntry.js'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
shared: {
|
shared: {
|
||||||
@@ -55,6 +56,11 @@ export default defineConfig({
|
|||||||
host: true,
|
host: true,
|
||||||
cors: true,
|
cors: true,
|
||||||
open: '/', // 开发时自动打开到根路径
|
open: '/', // 开发时自动打开到根路径
|
||||||
|
// HTTPS 配置(使用 mkcert 生成的本地开发证书)
|
||||||
|
https: {
|
||||||
|
key: fs.readFileSync('C:/Users/FK05/443/localhost+3-key.pem'),
|
||||||
|
cert: fs.readFileSync('C:/Users/FK05/443/localhost+3.pem')
|
||||||
|
},
|
||||||
hmr: {
|
hmr: {
|
||||||
// 修复 base 路径导致的 WebSocket 连接问题
|
// 修复 base 路径导致的 WebSocket 连接问题
|
||||||
path: '/@vite/client',
|
path: '/@vite/client',
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
|
|||||||
import { federation } from '@module-federation/vite'
|
import { federation } from '@module-federation/vite'
|
||||||
import { resolve, dirname } from 'path'
|
import { resolve, dirname } from 'path'
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'url'
|
||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url)
|
const __filename = fileURLToPath(import.meta.url)
|
||||||
const __dirname = dirname(__filename)
|
const __dirname = dirname(__filename)
|
||||||
@@ -19,6 +20,9 @@ const __dirname = dirname(__filename)
|
|||||||
* - ✅ 真正的生产可用版本
|
* - ✅ 真正的生产可用版本
|
||||||
*/
|
*/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
// Shared 模块的基础路径(通过 Nginx 代理访问)
|
||||||
|
base: '/shared/',
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
vue({
|
vue({
|
||||||
script: {
|
script: {
|
||||||
@@ -109,6 +113,11 @@ export default defineConfig({
|
|||||||
strictPort: true,
|
strictPort: true,
|
||||||
host: true,
|
host: true,
|
||||||
cors: true,
|
cors: true,
|
||||||
|
// HTTPS 配置(使用 mkcert 生成的本地开发证书)
|
||||||
|
https: {
|
||||||
|
key: fs.readFileSync('C:/Users/FK05/443/localhost+3-key.pem'),
|
||||||
|
cert: fs.readFileSync('C:/Users/FK05/443/localhost+3.pem')
|
||||||
|
},
|
||||||
headers: {
|
headers: {
|
||||||
'Access-Control-Allow-Origin': '*',
|
'Access-Control-Allow-Origin': '*',
|
||||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
||||||
@@ -120,6 +129,11 @@ export default defineConfig({
|
|||||||
port: 7000,
|
port: 7000,
|
||||||
host: true,
|
host: true,
|
||||||
cors: true,
|
cors: true,
|
||||||
|
// HTTPS 配置(使用 mkcert 生成的本地开发证书)
|
||||||
|
https: {
|
||||||
|
key: fs.readFileSync('C:/Users/FK05/443/localhost+3-key.pem'),
|
||||||
|
cert: fs.readFileSync('C:/Users/FK05/443/localhost+3.pem')
|
||||||
|
},
|
||||||
headers: {
|
headers: {
|
||||||
'Access-Control-Allow-Origin': '*',
|
'Access-Control-Allow-Origin': '*',
|
||||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
||||||
|
|||||||
@@ -4,11 +4,6 @@
|
|||||||
import {loadShare} from "@module-federation/runtime";
|
import {loadShare} from "@module-federation/runtime";
|
||||||
const importMap = {
|
const importMap = {
|
||||||
|
|
||||||
"axios": async () => {
|
|
||||||
let pkg = await import("__mf__virtual/workcase__prebuild__axios__prebuild__.js");
|
|
||||||
return pkg;
|
|
||||||
}
|
|
||||||
,
|
|
||||||
"element-plus": async () => {
|
"element-plus": async () => {
|
||||||
let pkg = await import("__mf__virtual/workcase__prebuild__element_mf_2_plus__prebuild__.js");
|
let pkg = await import("__mf__virtual/workcase__prebuild__element_mf_2_plus__prebuild__.js");
|
||||||
return pkg;
|
return pkg;
|
||||||
@@ -27,36 +22,6 @@
|
|||||||
}
|
}
|
||||||
const usedShared = {
|
const usedShared = {
|
||||||
|
|
||||||
"axios": {
|
|
||||||
name: "axios",
|
|
||||||
version: "1.13.2",
|
|
||||||
scope: ["default"],
|
|
||||||
loaded: false,
|
|
||||||
from: "workcase",
|
|
||||||
async get () {
|
|
||||||
if (false) {
|
|
||||||
throw new Error(`Shared module '${"axios"}' must be provided by host`);
|
|
||||||
}
|
|
||||||
usedShared["axios"].loaded = true
|
|
||||||
const {"axios": pkgDynamicImport} = importMap
|
|
||||||
const res = await pkgDynamicImport()
|
|
||||||
const exportModule = {...res}
|
|
||||||
// All npm packages pre-built by vite will be converted to esm
|
|
||||||
Object.defineProperty(exportModule, "__esModule", {
|
|
||||||
value: true,
|
|
||||||
enumerable: false
|
|
||||||
})
|
|
||||||
return function () {
|
|
||||||
return exportModule
|
|
||||||
}
|
|
||||||
},
|
|
||||||
shareConfig: {
|
|
||||||
singleton: false,
|
|
||||||
requiredVersion: "^1.13.2",
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
,
|
|
||||||
"element-plus": {
|
"element-plus": {
|
||||||
name: "element-plus",
|
name: "element-plus",
|
||||||
version: "2.12.0",
|
version: "2.12.0",
|
||||||
@@ -153,7 +118,7 @@
|
|||||||
entryGlobalName: "shared",
|
entryGlobalName: "shared",
|
||||||
name: "shared",
|
name: "shared",
|
||||||
type: "module",
|
type: "module",
|
||||||
entry: "http://localhost:7000/remoteEntry.js",
|
entry: "https://org.xyzh.yslg/shared/remoteEntry.js",
|
||||||
shareScope: "default",
|
shareScope: "default",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ const handleUserCommand = (command: string) => {
|
|||||||
case 'logout':
|
case 'logout':
|
||||||
localStorage.clear()
|
localStorage.clear()
|
||||||
ElMessage.success('退出成功')
|
ElMessage.success('退出成功')
|
||||||
router.push('/login')
|
router.push('/platform/login')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,18 @@ import { APP_CONFIG } from 'shared/config'
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { loadRoutesFromStorage } from './dynamicRoute'
|
import { loadRoutesFromStorage } from './dynamicRoute'
|
||||||
|
|
||||||
// workcase应用的动态路由会根据layout字段自动添加,不需要预定义Root布局
|
// 公开路由(不需要登录认证)
|
||||||
const routes: RouteRecordRaw[] = []
|
const routes: RouteRecordRaw[] = [
|
||||||
|
{
|
||||||
|
path: '/meeting',
|
||||||
|
name: 'Meeting',
|
||||||
|
component: () => import('@/views/public/JitsiMeeting/JitsiMeetingView.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '视频会议',
|
||||||
|
requiresAuth: false // 不需要登录认证,允许通过token访问
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory('/workcase'), // 与nginx保持一致,使用/workcase前缀
|
history: createWebHistory('/workcase'), // 与nginx保持一致,使用/workcase前缀
|
||||||
@@ -34,9 +44,10 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
// 检查URL参数中是否有token(用于外部链接和小程序访问)
|
// 检查URL参数中是否有token(用于外部链接和小程序访问)
|
||||||
const tokenParam = to.query.token as string | undefined
|
const tokenParam = to.query.token as string | undefined
|
||||||
|
|
||||||
// 如果URL中有token,但localStorage中没有loginDomain,使用refresh接口验证
|
// 如果URL中有token参数,使用refresh接口验证并刷新登录状态
|
||||||
if (tokenParam && !localStorage.getItem('loginDomain')) {
|
// 这样可以用新token覆盖旧的登录状态(如果有的话)
|
||||||
console.log('[Workcase Router] 检测到token参数,尝试验证登录状态...')
|
if (tokenParam) {
|
||||||
|
console.log('[Workcase Router] 检测到token参数,尝试验证并刷新登录状态...')
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/urban-lifeline/auth/refresh', {
|
const response = await fetch('/api/urban-lifeline/auth/refresh', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -52,12 +63,12 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
const loginDomain = result.data
|
const loginDomain = result.data
|
||||||
const newToken = loginDomain.token
|
const newToken = loginDomain.token
|
||||||
|
|
||||||
// 保存到localStorage
|
// 保存到localStorage(覆盖旧的登录状态)
|
||||||
localStorage.setItem('token', newToken)
|
localStorage.setItem('token', newToken)
|
||||||
localStorage.setItem('loginDomain', JSON.stringify(loginDomain))
|
localStorage.setItem('loginDomain', JSON.stringify(loginDomain))
|
||||||
TokenManager.setToken(newToken)
|
TokenManager.setToken(newToken)
|
||||||
|
|
||||||
console.log('[Workcase Router] Token验证成功,登录状态已保存')
|
console.log('[Workcase Router] Token验证成功,登录状态已刷新')
|
||||||
} else {
|
} else {
|
||||||
console.warn('[Workcase Router] Token验证失败:', result.message)
|
console.warn('[Workcase Router] Token验证失败:', result.message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import { useRoute, useRouter } from 'vue-router'
|
|||||||
import { workcaseChatAPI } from '@/api/workcase'
|
import { workcaseChatAPI } from '@/api/workcase'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { TokenManager } from 'shared/api'
|
import { TokenManager } from 'shared/api'
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -53,45 +52,6 @@ const getMeetingParams = () => {
|
|||||||
return { meetingId: meetingId.value, roomId: roomId.value, token: tokenParam }
|
return { meetingId: meetingId.value, roomId: roomId.value, token: tokenParam }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用token刷新登录状态
|
|
||||||
const refreshLoginWithToken = async (token: string) => {
|
|
||||||
try {
|
|
||||||
console.log('[JitsiMeetingView] 使用token刷新登录状态...')
|
|
||||||
|
|
||||||
const response = await axios.post(
|
|
||||||
'/api/urban-lifeline/auth/refresh',
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'Authorization': `Bearer ${token}`,
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (response.data?.success && response.data?.data) {
|
|
||||||
const loginDomain = response.data.data
|
|
||||||
const newToken = loginDomain.token
|
|
||||||
|
|
||||||
// 保存到localStorage
|
|
||||||
localStorage.setItem('token', newToken)
|
|
||||||
localStorage.setItem('loginDomain', JSON.stringify(loginDomain))
|
|
||||||
|
|
||||||
// 使用TokenManager设置token
|
|
||||||
TokenManager.setToken(newToken)
|
|
||||||
|
|
||||||
console.log('[JitsiMeetingView] 登录状态刷新成功')
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
console.error('[JitsiMeetingView] 刷新登录失败:', response.data?.message)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
console.error('[JitsiMeetingView] 刷新登录异常:', err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加载 Jitsi External API 脚本
|
// 加载 Jitsi External API 脚本
|
||||||
const loadJitsiScript = (): Promise<void> => {
|
const loadJitsiScript = (): Promise<void> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -102,7 +62,8 @@ const loadJitsiScript = (): Promise<void> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const script = document.createElement('script')
|
const script = document.createElement('script')
|
||||||
script.src = 'http://localhost:8280/external_api.js'
|
// 从 Jitsi 子域名加载 External API
|
||||||
|
script.src = 'https://org.xyzh.yslg.jitsi/external_api.js'
|
||||||
script.async = true
|
script.async = true
|
||||||
script.onload = () => {
|
script.onload = () => {
|
||||||
console.log('[JitsiMeetingView] Jitsi External API 脚本加载成功')
|
console.log('[JitsiMeetingView] Jitsi External API 脚本加载成功')
|
||||||
@@ -129,29 +90,27 @@ const initJitsiMeet = async (jitsiServerUrl: string, roomName: string, jwt: stri
|
|||||||
|
|
||||||
const JitsiMeetExternalAPI = (window as any).JitsiMeetExternalAPI
|
const JitsiMeetExternalAPI = (window as any).JitsiMeetExternalAPI
|
||||||
|
|
||||||
// 解析 URL 获取协议和域名
|
// 解析 URL 获取协议和域名(子域名模式,无需路径处理)
|
||||||
const urlObj = new URL(jitsiServerUrl)
|
const urlObj = new URL(jitsiServerUrl)
|
||||||
const domain = urlObj.host // 获取 host (localhost:8280)
|
const domain = urlObj.host // org.xyzh.yslg.jitsi
|
||||||
const useHttps = urlObj.protocol === 'https:'
|
const useHttps = urlObj.protocol === 'https:'
|
||||||
|
|
||||||
console.log('[JitsiMeetingView] 解析服务器配置:', {
|
console.log('[JitsiMeetingView] 解析服务器配置:', {
|
||||||
domain,
|
domain,
|
||||||
protocol: urlObj.protocol,
|
protocol: urlObj.protocol,
|
||||||
useHttps
|
useHttps,
|
||||||
|
roomName
|
||||||
})
|
})
|
||||||
|
|
||||||
// 配置选项 - 关键!指定是否使用 HTTPS
|
// 配置选项 - 子域名模式,直接使用域名和房间名
|
||||||
// 👇 替换你原有的 options 全部代码,保留原有逻辑,仅修改配置项
|
|
||||||
const options: any = {
|
const options: any = {
|
||||||
roomName: roomName,
|
roomName: roomName, // 直接使用房间名,无需路径前缀
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
parentNode: jitsiContainer.value,
|
parentNode: jitsiContainer.value,
|
||||||
jwt: jwt,
|
jwt: jwt,
|
||||||
// ✅ 修复1:核心!正确的顶层配置项是 useHTTPS(不是 https),强制关闭HTTPS
|
// 使用 HTTPS 协议
|
||||||
useHTTPS: false,
|
https: useHttps,
|
||||||
// ✅ 修复2:禁用WebSocket的HTTPS,彻底阻止wss://请求(局域网必加)
|
|
||||||
useWebSocket: false,
|
|
||||||
configOverwrite: {
|
configOverwrite: {
|
||||||
startWithAudioMuted: false,
|
startWithAudioMuted: false,
|
||||||
startWithVideoMuted: false,
|
startWithVideoMuted: false,
|
||||||
@@ -159,13 +118,7 @@ const initJitsiMeet = async (jitsiServerUrl: string, roomName: string, jwt: stri
|
|||||||
prejoinPageEnabled: false,
|
prejoinPageEnabled: false,
|
||||||
disableDeepLinking: true,
|
disableDeepLinking: true,
|
||||||
enableChat: true,
|
enableChat: true,
|
||||||
enableScreenSharing: true,
|
enableScreenSharing: true
|
||||||
// ✅ 修复3:叠加禁用,彻底阻断API内部的HTTPS强制逻辑(局域网核心)
|
|
||||||
useHTTPS: false,
|
|
||||||
// ✅ 修复4:禁用第三方HTTPS资源请求,避免混合内容报错
|
|
||||||
disableThirdPartyRequests: false,
|
|
||||||
// ✅ 修复5:关闭服务端的HTTPS重定向检测
|
|
||||||
disableHttpsRedirect: true
|
|
||||||
},
|
},
|
||||||
interfaceConfigOverwrite: {
|
interfaceConfigOverwrite: {
|
||||||
SHOW_JITSI_WATERMARK: false,
|
SHOW_JITSI_WATERMARK: false,
|
||||||
@@ -177,7 +130,12 @@ const initJitsiMeet = async (jitsiServerUrl: string, roomName: string, jwt: stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[JitsiMeetingView] 创建 JitsiMeetExternalAPI 实例,https=' + useHttps)
|
console.log('[JitsiMeetingView] 创建 JitsiMeetExternalAPI 实例:', {
|
||||||
|
domain,
|
||||||
|
https: useHttps,
|
||||||
|
roomName,
|
||||||
|
预期URL: `${useHttps ? 'https' : 'http'}://${domain}/${roomName}`
|
||||||
|
})
|
||||||
jitsiApi = new JitsiMeetExternalAPI(domain, options)
|
jitsiApi = new JitsiMeetExternalAPI(domain, options)
|
||||||
|
|
||||||
// 监听会议准备就绪事件
|
// 监听会议准备就绪事件
|
||||||
@@ -219,32 +177,13 @@ const joinMeeting = async () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否有loginDomain
|
// 检查登录状态(路由守卫已处理token刷新)
|
||||||
const hasLoginDomain = !!localStorage.getItem('loginDomain')
|
|
||||||
const hasToken = !!localStorage.getItem('token') || !!token
|
|
||||||
|
|
||||||
console.log('[JitsiMeetingView] 登录状态检查:', {
|
|
||||||
hasLoginDomain,
|
|
||||||
hasToken
|
|
||||||
})
|
|
||||||
|
|
||||||
// 如果没有loginDomain但有token(小程序或外部链接访问),先刷新登录
|
|
||||||
if (!hasLoginDomain && token) {
|
|
||||||
const refreshed = await refreshLoginWithToken(token)
|
|
||||||
if (!refreshed) {
|
|
||||||
error.value = '登录验证失败,请重新获取会议链接'
|
|
||||||
loading.value = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查登录状态
|
|
||||||
if (!TokenManager.hasToken()) {
|
if (!TokenManager.hasToken()) {
|
||||||
error.value = '未登录,请先登录'
|
error.value = '未登录,请先登录'
|
||||||
loading.value = false
|
loading.value = false
|
||||||
// 重定向到登录页
|
// 重定向到登录页
|
||||||
const currentUrl = window.location.href
|
const currentUrl = window.location.href
|
||||||
window.location.href = `/login?redirect=${encodeURIComponent(currentUrl)}`
|
window.location.href = `/platform/login?redirect=${encodeURIComponent(currentUrl)}`
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
|
|||||||
import { federation } from '@module-federation/vite'
|
import { federation } from '@module-federation/vite'
|
||||||
import { resolve, dirname } from 'path'
|
import { resolve, dirname } from 'path'
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'url'
|
||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url)
|
const __filename = fileURLToPath(import.meta.url)
|
||||||
const __dirname = dirname(__filename)
|
const __dirname = dirname(__filename)
|
||||||
@@ -26,7 +27,7 @@ export default defineConfig(({ mode }) => ({
|
|||||||
shared: {
|
shared: {
|
||||||
type: 'module',
|
type: 'module',
|
||||||
name: 'shared',
|
name: 'shared',
|
||||||
entry: 'http://localhost:7000/remoteEntry.js'
|
entry: 'https://org.xyzh.yslg/shared/remoteEntry.js'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
shared: {
|
shared: {
|
||||||
@@ -56,6 +57,11 @@ export default defineConfig(({ mode }) => ({
|
|||||||
host: true,
|
host: true,
|
||||||
cors: true,
|
cors: true,
|
||||||
open: '/workcase/', // 开发时自动打开到 /workcase/ 路径
|
open: '/workcase/', // 开发时自动打开到 /workcase/ 路径
|
||||||
|
// HTTPS 配置(使用 mkcert 生成的本地开发证书)
|
||||||
|
https: {
|
||||||
|
key: fs.readFileSync('C:/Users/FK05/443/localhost+3-key.pem'),
|
||||||
|
cert: fs.readFileSync('C:/Users/FK05/443/localhost+3.pem')
|
||||||
|
},
|
||||||
hmr: {
|
hmr: {
|
||||||
// 修复 base 路径导致的 WebSocket 连接问题
|
// 修复 base 路径导致的 WebSocket 连接问题
|
||||||
path: '/@vite/client',
|
path: '/@vite/client',
|
||||||
@@ -67,11 +73,6 @@ export default defineConfig(({ mode }) => ({
|
|||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
ws: true, // 启用 WebSocket 代理
|
ws: true, // 启用 WebSocket 代理
|
||||||
rewrite: (path: string) => path.replace(/^\/api/, '')
|
rewrite: (path: string) => path.replace(/^\/api/, '')
|
||||||
},
|
|
||||||
// 代理共享模块请求到 shared 服务
|
|
||||||
'/shared': {
|
|
||||||
target: 'http://localhost:7000',
|
|
||||||
changeOrigin: true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -685,9 +685,9 @@ async function handleJoinMeeting(meetingId: string) {
|
|||||||
const meetingName = meetingData.meetingName || '视频会议'
|
const meetingName = meetingData.meetingName || '视频会议'
|
||||||
console.log('[handleJoinMeeting] 获取到会议页面URL:', meetingPageUrl, '会议名称:', meetingName)
|
console.log('[handleJoinMeeting] 获取到会议页面URL:', meetingPageUrl, '会议名称:', meetingName)
|
||||||
|
|
||||||
// 构建完整的会议URL(包含域名和workcase路径)
|
// 小程序环境:直接使用固定的HTTPS域名
|
||||||
const protocol = window.location.protocol
|
const protocol = 'https:'
|
||||||
const host = window.location.host
|
const host = 'org.xyzh.yslg'
|
||||||
// 如果meetingPageUrl不包含/workcase,需要加上
|
// 如果meetingPageUrl不包含/workcase,需要加上
|
||||||
const fullPath = meetingPageUrl.startsWith('/workcase')
|
const fullPath = meetingPageUrl.startsWith('/workcase')
|
||||||
? meetingPageUrl
|
? meetingPageUrl
|
||||||
|
|||||||
@@ -3,98 +3,3 @@ lockfileVersion: '6.0'
|
|||||||
settings:
|
settings:
|
||||||
autoInstallPeers: true
|
autoInstallPeers: true
|
||||||
excludeLinksFromLockfile: false
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
dependencies:
|
|
||||||
'@stomp/stompjs':
|
|
||||||
specifier: ^7.2.1
|
|
||||||
version: 7.2.1
|
|
||||||
sockjs-client:
|
|
||||||
specifier: ^1.6.1
|
|
||||||
version: 1.6.1
|
|
||||||
|
|
||||||
packages:
|
|
||||||
|
|
||||||
/@stomp/stompjs@7.2.1:
|
|
||||||
resolution: {integrity: sha512-DLd/WeicnHS5SsWWSk3x6/pcivqchNaEvg9UEGVqAcfYEBVmS9D6980ckXjTtfpXLjdLDsd96M7IuX4w7nzq5g==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/debug@3.2.7:
|
|
||||||
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
|
|
||||||
peerDependencies:
|
|
||||||
supports-color: '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
supports-color:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
ms: 2.1.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/eventsource@2.0.2:
|
|
||||||
resolution: {integrity: sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==}
|
|
||||||
engines: {node: '>=12.0.0'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/faye-websocket@0.11.4:
|
|
||||||
resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==}
|
|
||||||
engines: {node: '>=0.8.0'}
|
|
||||||
dependencies:
|
|
||||||
websocket-driver: 0.7.4
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/http-parser-js@0.5.10:
|
|
||||||
resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/inherits@2.0.4:
|
|
||||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ms@2.1.3:
|
|
||||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/querystringify@2.2.0:
|
|
||||||
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/requires-port@1.0.0:
|
|
||||||
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/safe-buffer@5.2.1:
|
|
||||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/sockjs-client@1.6.1:
|
|
||||||
resolution: {integrity: sha512-2g0tjOR+fRs0amxENLi/q5TiJTqY+WXFOzb5UwXndlK6TO3U/mirZznpx6w34HVMoc3g7cY24yC/ZMIYnDlfkw==}
|
|
||||||
engines: {node: '>=12'}
|
|
||||||
dependencies:
|
|
||||||
debug: 3.2.7
|
|
||||||
eventsource: 2.0.2
|
|
||||||
faye-websocket: 0.11.4
|
|
||||||
inherits: 2.0.4
|
|
||||||
url-parse: 1.5.10
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/url-parse@1.5.10:
|
|
||||||
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
|
|
||||||
dependencies:
|
|
||||||
querystringify: 2.2.0
|
|
||||||
requires-port: 1.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/websocket-driver@0.7.4:
|
|
||||||
resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==}
|
|
||||||
engines: {node: '>=0.8.0'}
|
|
||||||
dependencies:
|
|
||||||
http-parser-js: 0.5.10
|
|
||||||
safe-buffer: 5.2.1
|
|
||||||
websocket-extensions: 0.1.4
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/websocket-extensions@0.1.4:
|
|
||||||
resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==}
|
|
||||||
engines: {node: '>=0.8.0'}
|
|
||||||
dev: false
|
|
||||||
|
|||||||
Reference in New Issue
Block a user