https 替换

This commit is contained in:
2025-12-27 17:34:19 +08:00
parent 55801fa0ec
commit 0fb7a4ffb2
18 changed files with 233 additions and 293 deletions

View File

@@ -219,7 +219,7 @@ const handleUserCommand = (command: string) => {
case 'logout':
localStorage.clear()
ElMessage.success('退出成功')
router.push('/login')
router.push('/platform/login')
break
}
}

View File

@@ -3,6 +3,7 @@ import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { resolve, dirname } from 'path'
import { fileURLToPath } from 'url'
import fs from 'fs'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
@@ -38,16 +39,16 @@ export default defineConfig(({ mode }) => ({
host: true,
cors: true,
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: {
'/api': {
target: 'http://localhost:8180',
changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/api/, '')
},
// 代理共享模块请求到 shared 服务
'/shared': {
target: 'http://localhost:7000',
changeOrigin: true
}
}
},

View File

@@ -225,7 +225,7 @@ const handleUserCommand = (command: string) => {
case 'logout':
localStorage.clear()
ElMessage.success('退出成功')
router.push('/login')
router.push('/platform/login')
break
}
}

View File

@@ -243,7 +243,7 @@ const handleUserCommand = (command: string) => {
case 'logout':
localStorage.clear()
ElMessage.success('退出成功')
router.push('/login')
router.push('/platform/login')
break
}
}

View File

@@ -5,7 +5,7 @@ import { loadRoutesFromStorage } from './dynamicRoute'
// platform应用的动态路由会根据layout字段自动添加不需要预定义Root布局
const routes: RouteRecordRaw[] = [
{
path: '/login',
path: '/platform/login',
name: 'Login',
component: () => import('@/views/public/Login/Login.vue'),
meta: {
@@ -37,13 +37,13 @@ router.beforeEach((to, from, next) => {
if (requiresAuth && !hasToken) {
// 需要登录但未登录,跳转到登录页
next({
path: '/login',
path: '/platform/login',
query: { redirect: to.fullPath } // 保存原始路径
})
return
}
if (to.path === '/login' && hasToken) {
if (to.path === '/platform/login' && hasToken) {
// 已登录但访问登录页,跳转到首页
next('/')
return

View File

@@ -4,6 +4,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
import { federation } from '@module-federation/vite'
import { resolve, dirname } from 'path'
import { fileURLToPath } from 'url'
import fs from 'fs'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
@@ -26,7 +27,7 @@ export default defineConfig({
shared: {
type: 'module',
name: 'shared',
entry: 'http://localhost:7000/remoteEntry.js'
entry: 'https://org.xyzh.yslg/shared/remoteEntry.js'
}
},
shared: {
@@ -55,6 +56,11 @@ export default defineConfig({
host: true,
cors: true,
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: {
// 修复 base 路径导致的 WebSocket 连接问题
path: '/@vite/client',

View File

@@ -4,6 +4,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
import { federation } from '@module-federation/vite'
import { resolve, dirname } from 'path'
import { fileURLToPath } from 'url'
import fs from 'fs'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
@@ -11,7 +12,7 @@ const __dirname = dirname(__filename)
/**
* Module Federation 构建配置(@module-federation/vite
* 官方维护版本,支持 Vite 6 + 开发模式热更新
*
*
* 优势:
* - ✅ 完整支持 Vite 开发模式
* - ✅ dev 模式能生成 remoteEntry.js
@@ -19,6 +20,9 @@ const __dirname = dirname(__filename)
* - ✅ 真正的生产可用版本
*/
export default defineConfig({
// Shared 模块的基础路径(通过 Nginx 代理访问)
base: '/shared/',
plugins: [
vue({
script: {
@@ -109,17 +113,27 @@ export default defineConfig({
strictPort: true,
host: 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: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
}
},
preview: {
port: 7000,
host: 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: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',

View File

@@ -4,11 +4,6 @@
import {loadShare} from "@module-federation/runtime";
const importMap = {
"axios": async () => {
let pkg = await import("__mf__virtual/workcase__prebuild__axios__prebuild__.js");
return pkg;
}
,
"element-plus": async () => {
let pkg = await import("__mf__virtual/workcase__prebuild__element_mf_2_plus__prebuild__.js");
return pkg;
@@ -27,36 +22,6 @@
}
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": {
name: "element-plus",
version: "2.12.0",
@@ -153,7 +118,7 @@
entryGlobalName: "shared",
name: "shared",
type: "module",
entry: "http://localhost:7000/remoteEntry.js",
entry: "https://org.xyzh.yslg/shared/remoteEntry.js",
shareScope: "default",
}

View File

@@ -188,7 +188,7 @@ const handleUserCommand = (command: string) => {
case 'logout':
localStorage.clear()
ElMessage.success('退出成功')
router.push('/login')
router.push('/platform/login')
break
}
}

View File

@@ -6,8 +6,18 @@ import { APP_CONFIG } from 'shared/config'
// @ts-ignore
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({
history: createWebHistory('/workcase'), // 与nginx保持一致使用/workcase前缀
@@ -34,9 +44,10 @@ router.beforeEach(async (to, from, next) => {
// 检查URL参数中是否有token用于外部链接和小程序访问
const tokenParam = to.query.token as string | undefined
// 如果URL中有token但localStorage中没有loginDomain使用refresh接口验证
if (tokenParam && !localStorage.getItem('loginDomain')) {
console.log('[Workcase Router] 检测到token参数尝试验证登录状态...')
// 如果URL中有token参数使用refresh接口验证并刷新登录状态
// 这样可以用新token覆盖旧的登录状态如果有的话
if (tokenParam) {
console.log('[Workcase Router] 检测到token参数尝试验证并刷新登录状态...')
try {
const response = await fetch('/api/urban-lifeline/auth/refresh', {
method: 'POST',
@@ -52,12 +63,12 @@ router.beforeEach(async (to, from, next) => {
const loginDomain = result.data
const newToken = loginDomain.token
// 保存到localStorage
// 保存到localStorage(覆盖旧的登录状态)
localStorage.setItem('token', newToken)
localStorage.setItem('loginDomain', JSON.stringify(loginDomain))
TokenManager.setToken(newToken)
console.log('[Workcase Router] Token验证成功登录状态已保存')
console.log('[Workcase Router] Token验证成功登录状态已刷新')
} else {
console.warn('[Workcase Router] Token验证失败:', result.message)
}

View File

@@ -25,7 +25,6 @@ import { useRoute, useRouter } from 'vue-router'
import { workcaseChatAPI } from '@/api/workcase'
// @ts-ignore
import { TokenManager } from 'shared/api'
import axios from 'axios'
const route = useRoute()
const router = useRouter()
@@ -53,45 +52,6 @@ const getMeetingParams = () => {
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 脚本
const loadJitsiScript = (): Promise<void> => {
return new Promise((resolve, reject) => {
@@ -102,7 +62,8 @@ const loadJitsiScript = (): Promise<void> => {
}
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.onload = () => {
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
// 解析 URL 获取协议和域名
// 解析 URL 获取协议和域名(子域名模式,无需路径处理)
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:'
console.log('[JitsiMeetingView] 解析服务器配置:', {
domain,
protocol: urlObj.protocol,
useHttps
useHttps,
roomName
})
// 配置选项 - 关键!指定是否使用 HTTPS
// 👇 替换你原有的 options 全部代码,保留原有逻辑,仅修改配置项
// 配置选项 - 子域名模式,直接使用域名和房间名
const options: any = {
roomName: roomName,
roomName: roomName, // 直接使用房间名,无需路径前缀
width: '100%',
height: '100%',
parentNode: jitsiContainer.value,
jwt: jwt,
// ✅ 修复1核心正确的顶层配置项是 useHTTPS不是 https强制关闭HTTPS
useHTTPS: false,
// ✅ 修复2禁用WebSocket的HTTPS彻底阻止wss://请求(局域网必加)
useWebSocket: false,
// 使用 HTTPS 协议
https: useHttps,
configOverwrite: {
startWithAudioMuted: false,
startWithVideoMuted: false,
@@ -159,13 +118,7 @@ const initJitsiMeet = async (jitsiServerUrl: string, roomName: string, jwt: stri
prejoinPageEnabled: false,
disableDeepLinking: true,
enableChat: true,
enableScreenSharing: true,
// ✅ 修复3叠加禁用彻底阻断API内部的HTTPS强制逻辑局域网核心
useHTTPS: false,
// ✅ 修复4禁用第三方HTTPS资源请求避免混合内容报错
disableThirdPartyRequests: false,
// ✅ 修复5关闭服务端的HTTPS重定向检测
disableHttpsRedirect: true
enableScreenSharing: true
},
interfaceConfigOverwrite: {
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)
// 监听会议准备就绪事件
@@ -219,32 +177,13 @@ const joinMeeting = async () => {
return
}
// 检查是否有loginDomain
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
}
}
// 检查登录状态
// 检查登录状态路由守卫已处理token刷新
if (!TokenManager.hasToken()) {
error.value = '未登录,请先登录'
loading.value = false
// 重定向到登录页
const currentUrl = window.location.href
window.location.href = `/login?redirect=${encodeURIComponent(currentUrl)}`
window.location.href = `/platform/login?redirect=${encodeURIComponent(currentUrl)}`
return
}

View File

@@ -4,6 +4,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
import { federation } from '@module-federation/vite'
import { resolve, dirname } from 'path'
import { fileURLToPath } from 'url'
import fs from 'fs'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
@@ -26,7 +27,7 @@ export default defineConfig(({ mode }) => ({
shared: {
type: 'module',
name: 'shared',
entry: 'http://localhost:7000/remoteEntry.js'
entry: 'https://org.xyzh.yslg/shared/remoteEntry.js'
}
},
shared: {
@@ -56,6 +57,11 @@ export default defineConfig(({ mode }) => ({
host: true,
cors: true,
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: {
// 修复 base 路径导致的 WebSocket 连接问题
path: '/@vite/client',
@@ -67,11 +73,6 @@ export default defineConfig(({ mode }) => ({
changeOrigin: true,
ws: true, // 启用 WebSocket 代理
rewrite: (path: string) => path.replace(/^\/api/, '')
},
// 代理共享模块请求到 shared 服务
'/shared': {
target: 'http://localhost:7000',
changeOrigin: true
}
}
},

View File

@@ -685,9 +685,9 @@ async function handleJoinMeeting(meetingId: string) {
const meetingName = meetingData.meetingName || '视频会议'
console.log('[handleJoinMeeting] 获取到会议页面URL:', meetingPageUrl, '会议名称:', meetingName)
// 构建完整的会议URL包含域名和workcase路径
const protocol = window.location.protocol
const host = window.location.host
// 小程序环境直接使用固定的HTTPS域名
const protocol = 'https:'
const host = 'org.xyzh.yslg'
// 如果meetingPageUrl不包含/workcase需要加上
const fullPath = meetingPageUrl.startsWith('/workcase')
? meetingPageUrl

View File

@@ -3,98 +3,3 @@ lockfileVersion: '6.0'
settings:
autoInstallPeers: true
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