feat(web):重构前端路由和配置管理

- 移除旧版全局变量定义,采用Vue Router管理页面跳转
- 新增配置文件统一管理游戏名称和注册码设置
-优化加载条逻辑,使用onMounted确保DOM元素正确获取
- 添加服务端配置接口,动态加载游戏配置信息
- 升级依赖包,引入vue-router支持单页应用
- 调整项目结构,分离服务器端代码至独立目录
- 配置Vite代理转发API请求到本地开发服务器
- 更新package.json脚本命令,支持前后端联合调试
- 引入pnpm workspace管理模式,提升多包协作效率
This commit is contained in:
kubbo
2025-09-30 18:39:51 +08:00
parent 39f7598b02
commit 5d20a7def3
14 changed files with 249 additions and 100 deletions

View File

@@ -5,14 +5,22 @@
"type": "module",
"scripts": {
"dev": "vite",
"dev:server": "pnpm --filter chuanqi-server dev",
"start:server": "pnpm --filter chuanqi-server start",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.5.21"
"vue": "^3.5.21",
"vue-router": "^4.5.1"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.1",
"vite": "^7.1.7"
},
"pnpm": {
"workspace": [
"server"
]
}
}

2
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,2 @@
packages:
- server

4
public/config.json Normal file
View File

@@ -0,0 +1,4 @@
{
"reg_code_open": true,
"code_type": ""
}

View File

@@ -1,17 +1,7 @@
/**
* 冰雪传奇H5
* 2022 XX信息科技有限公司
*
* @author 123456
* @wx 123456
* @qq 123456
*/
let host = window.location.hostname || '100.88.157.105';
let port = 80;
const webUrl = getHttp() + host + (port && 80 != port ? ':' + port : ''),
account = getQueryString('account'),
const account = getQueryString('account'),
token = getQueryString('token'),
noLogin = !account || !token;
@@ -20,7 +10,7 @@ if (noLogin) {
loadBarClear();
loadBarFull();
setTimeout(function () {
window.location.href = webUrl + '/login';
window.location.href = '/login';
}, randomRange(250, 500));
}, 1e3);
}
@@ -79,19 +69,18 @@ window['loginView'] = 'app.MainLoginView';
// 相关URL
window['webHost'] = host;
window['webUrl'] = webUrl;
window['serviceListdUrl'] = webUrl + '/server';
window['setServiceListdUrl'] = webUrl + '/server';
window['payUrl'] = webUrl + '/pay';
window['apiUrl'] = webUrl + '/api';
window['orderUrl'] = webUrl + '/api?act=order';
window['reportUrl'] = webUrl + '/api?act=report'; // 上报接口
window['errorReportUrl'] = webUrl + '/api?act=report&do=error';// 错误上报接口
window['checkUrl'] = webUrl + '/api?act=check'; // 验证URL
window['versionUrl'] = webUrl + '/api?act=version'; // 请求客户端版本
window['getActorInfoUrl'] = webUrl + '/api?act=actor';
window['roleInfoUrl'] = webUrl + '/api?act=role';
window['gongGaoUrl'] = webUrl + '/notice.txt';
window['serviceListdUrl'] = '/server';
window['setServiceListdUrl'] = '/server';
window['payUrl'] = '/pay';
window['apiUrl'] = '/api';
window['orderUrl'] = '/api?act=order';
window['reportUrl'] = '/api?act=report'; // 上报接口
window['errorReportUrl'] = '/api?act=report&do=error';// 错误上报接口
window['checkUrl'] = '/api?act=check'; // 验证URL
window['versionUrl'] = '/api?act=version'; // 请求客户端版本
window['getActorInfoUrl'] = '/api?act=actor';
window['roleInfoUrl'] = '/api?act=role';
window['gongGaoUrl'] = '/notice.txt';
// 客服信息
window['kfQQ'] = '123456';
@@ -341,7 +330,7 @@ function feedbackFunction(info) {
param += key + '=' + msgInfo[key] + '&'
}
param = param.substring(0, param.length - 1)
let srcStr = webUrl + '/api?' + param;
let srcStr = '/api?' + param;
const div = document.createElement('div');
div.id = 'iframDiv';
div.innerHTML = '<iframe id="main" scrolling="no" noresize="true" frameborder="0" style="width: 90%;height: 90%;padding-left:5%;padding-top:2%;" src=' + srcStr + '></iframe>';
@@ -369,7 +358,7 @@ function feedbackFunction(info) {
// 防沉迷
function IdCardFunction() {
window.open(webUrl);
window.open();
}
function addQQGrp() {
@@ -378,17 +367,17 @@ function addQQGrp() {
//下载YY游戏大厅
function downYYGameHallFun() {
window.open(webUrl);
window.open();
}
//开通会员
function openYYVip() {
window.open(webUrl);
window.open();
}
//开超玩会员
function openChaoWanVip() {
window.open(webUrl);
window.open();
}
function removeIfram() {

0
server/config/index.js Normal file
View File

27
server/index.js Normal file
View File

@@ -0,0 +1,27 @@
import Koa from 'koa';
import Router from 'koa-router';
import config from "./config/index.js"
const app = new Koa();
const router = new Router();
// 简单的路由示例
router.get('/', (ctx) => {
ctx.body = {message: 'Hello from Koa server!'};
});
router.get('/api/test', (ctx) => {
ctx.body = {message: 'This is a test API endpoint'};
});
router.get('/api/config', (ctx) => {
ctx.body = {data: config}
})
app.use(router.routes());
app.use(router.allowedMethods());
const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.log(`Koa server is running on port ${PORT}`);
});

15
server/package.json Normal file
View File

@@ -0,0 +1,15 @@
{
"name": "chuanqi-server",
"version": "1.0.0",
"description": "A simple Koa server for chuanqi web",
"main": "index.js",
"type": "module",
"scripts": {
"start": "node index.js",
"dev": "node index.js"
},
"dependencies": {
"koa": "^2.15.0",
"koa-router": "^12.0.0"
}
}

View File

@@ -1,6 +1,7 @@
<script setup>
import {RouterView} from 'vue-router'
import Loading from "./components/loading.vue";
import {reactive} from "vue";
import {onMounted, reactive} from "vue";
const mainDiv = reactive({
dataOrientation: "auto",
@@ -13,83 +14,84 @@ let loadBar1Width = 0, loadBar2Width = 0, setIntervalId = 0;
const loadBar2MaxWidth = 200;
let loadError = false;
const loadBox = document.getElementById('loadBox'),
logoImg = document.getElementById('logoImg'),
loadBar1 = document.getElementById('loadBar1'),
loadBar2 = document.getElementById('loadBar2');
onMounted(() => {
const loadBox = document.getElementById('loadBox'),
logoImg = document.getElementById('logoImg'),
loadBar1 = document.getElementById('loadBar1'),
loadBar2 = document.getElementById('loadBar2');
function updateLoadBar() {
if (loadError) {
return;
}
const screenWidth = document.documentElement.scrollWidth || document.body.scrollWidth,
screenHeight = document.documentElement.scrollHeight || document.body.scrollHeight,
smallScreen = screenWidth <= 0,
isHorizontal = isMobile() && screenHeight <= 590;
if (loadBox) loadBox.style.paddingTop = (!isHorizontal ? (screenHeight / 4) : 50) + 'px';
if (logoImg) logoImg.width = isHorizontal || smallScreen ? 300 : 500;
if (loadBar1) {
loadBar1Width += 20;
if (loadBar1Width > 100) loadBar1Width = 0;
loadBar1.style.width = loadBar1Width + '%';
}
if (loadBar2) {
loadBar2Width += 3;
if ((loadBar2Width / loadBar2MaxWidth * 100) > 100) {
loadBarFull();
} else {
loadBar2.style.width = loadBar2Width + 'px';
function updateLoadBar() {
if (loadError) {
return;
}
const screenWidth = document.documentElement.scrollWidth || document.body.scrollWidth,
screenHeight = document.documentElement.scrollHeight || document.body.scrollHeight,
smallScreen = screenWidth <= 0,
isHorizontal = isMobile() && screenHeight <= 590;
if (loadBox) loadBox.style.paddingTop = (!isHorizontal ? (screenHeight / 4) : 50) + 'px';
if (logoImg) logoImg.width = isHorizontal || smallScreen ? 300 : 500;
if (loadBar1) {
loadBar1Width += 20;
if (loadBar1Width > 100) loadBar1Width = 0;
loadBar1.style.width = loadBar1Width + '%';
}
if (loadBar2) {
loadBar2Width += 3;
if ((loadBar2Width / loadBar2MaxWidth * 100) > 100) {
loadBarFull();
} else {
loadBar2.style.width = loadBar2Width + 'px';
}
}
}
}
function startLoadBar() {
setIntervalId = self.setInterval(updateLoadBar, 100);
}
function startLoadBar() {
setIntervalId = self.setInterval(updateLoadBar, 100);
}
window.loadBarFull = function () {
loadBar2.style.width = loadBar2MaxWidth + 'px';
}
window.onload = function () {
startLoadBar();
if ((typeof (Worker) !== 'undefined')) {
const s = document.createElement('script');
s.type = 'text/javascript';
s.async = false;
s.addEventListener('load', function (e) {
s.parentNode.removeChild(s);
s.removeEventListener('load', e, false);
}, false);
s.src = 'js/index.js?v=' + Math.random();
document.body.appendChild(s);
} else {
loadError = true;
const errorMsg = `抱歉!您的浏览器不支持本游戏,请更换浏览器或前往官网 <span class="font_small"><a href="${getHttp() + location.host}" target="_blank" class="link_color">下载${isMobile() ? 'APP' : '微端'}</a></span> 进行游戏!`,
label = document.getElementById('label');
if (label) {
label.innerHTML = errorMsg;
window.loadBarFull = function () {
if (loadBar2) loadBar2.style.width = loadBar2MaxWidth + 'px';
}
window.onload = function () {
startLoadBar();
if ((typeof (Worker) !== 'undefined')) {
const s = document.createElement('script');
s.type = 'text/javascript';
s.async = false;
s.addEventListener('load', function (e) {
s.parentNode.removeChild(s);
s.removeEventListener('load', e, false);
}, false);
s.src = 'js/index.js?v=' + Math.random();
document.body.appendChild(s);
} else {
alert(filterHTML(errorMsg));
loadError = true;
const errorMsg = `抱歉!您的浏览器不支持本游戏,请更换浏览器或前往官网 <span class="font_small"><a href="${getHttp() + location.host}" target="_blank" class="link_color">下载${isMobile() ? 'APP' : '微端'}</a></span> 进行游戏!`,
label = document.getElementById('label');
if (label) {
label.innerHTML = errorMsg;
} else {
alert(filterHTML(errorMsg));
}
}
}
}
window.loadBarClear = function () {
window.clearInterval(setIntervalId);
}
if (isMobile()) {
mainDiv.dataOrientation = "landscape";
mainDiv.dataScaleMode = "fixedHeight";
mainDiv.dataFrameRate = 30;
mainDiv.dataContentWidth = 1334;
mainDiv.dataContentHeight = 750;
}
window.loadBarClear = function () {
window.clearInterval(setIntervalId);
}
if (isMobile()) {
mainDiv.dataOrientation = "landscape";
mainDiv.dataScaleMode = "fixedHeight";
mainDiv.dataFrameRate = 30;
mainDiv.dataContentWidth = 1334;
mainDiv.dataContentHeight = 750;
}
})
</script>
<template>
<div id="mainDiv" v-bind="mainDiv" data-multi-fingered="2" data-show-fps="false" data-show-log="false" data-show-fps-style="x:0,y:0,size:12,textColor:0xffffff,bgAlpha:0.9">
<loading/>
<RouterView/>
<!-- <loading/> -->
</div>
</template>

5
src/config.js Normal file
View File

@@ -0,0 +1,5 @@
export default {
gameName: "神临苍月",
reg_code_open: true,
code_type: "email",
}

View File

@@ -1,6 +1,7 @@
import {createApp} from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
// window.external?.OpenGameWindowNew(window.location.href, '', '', '', false);
document.onkeydown = document.onkeyup = document.onkeypress = function (e) {
@@ -9,4 +10,10 @@ document.onkeydown = document.onkeyup = document.onkeypress = function (e) {
return false;
}
}
createApp(App).mount('#app')
const app = createApp(App)
fetch("/api/config", {method: "GET"}).then(res => res.json()).then(res => {
app.config.globalProperties.$gameName = res.data.gameName || "神临苍月";
app.config.globalProperties.$_CONFIG = res.data;
app.use(router).mount('#app')
})

22
src/router/index.js Normal file
View File

@@ -0,0 +1,22 @@
import {createRouter, createWebHistory} from 'vue-router'
import Index from "../views/index.vue";
// 示例路由配置
const routes = [
{
path: '/',
name: 'Home',
component: Index
},
{
path: '/login', name: 'Login',
component: () => import('../views/login.vue')
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router

10
src/views/index.vue Normal file
View File

@@ -0,0 +1,10 @@
<script setup>
import Loading from "../components/loading.vue";
</script>
<template>
<loading/>
</template>
<style scoped>
</style>

49
src/views/login.vue Normal file
View File

@@ -0,0 +1,49 @@
<script setup>
import {reactive} from "vue";
const servers = ref([])
</script>
<template>
<div class="wrapper pagebg">
<div class="dialog account" id="account-login">
<h2 class="title">{{$gameName}}</h2>
<input type="text" id="account" value="<?=isset($_COOKIE['account']) ? $_COOKIE['account'] : ''?>" placeholder="请输入<?=$_CONFIG['account_name']?><?=$_CONFIG['account_name_suffix']?>" onKeyUp="value = value.replace(/[\W]/g, '')" autocomplete="off" disableautocomplete>
<input type="password" id="password" value="<?=isset($_COOKIE['password']) ? $_COOKIE['password'] : ''?>" placeholder="请输入<?=$_CONFIG['account_name']?><?=$_CONFIG['password_name_suffix']?>">
<input type="password" id="password2" placeholder="请再次输入<?=$_CONFIG['account_name']?><?=$_CONFIG['password_name_suffix']?>" style="display: none;">
<select id="serverId" style="border: none; display: none; margin-bottom: 10px;">
<option value="0">请选择区服</option>
<option v-for="item in servers" :value="item.id">{{ item.name}}</option>
</select>
<input type="text" id="email" placeholder="<?php if($_CONFIG['reg_code_open']): ?>请输入<?php if('email' == $_CONFIG['code_type']): ?>邮箱地址<?php else: ?>手机号<?php endif; ?><?php else: ?><?php if('email' == $_CONFIG['code_type']): ?>邮箱地址<?php else: ?>手机号<?php endif; ?>(选填)<?php endif; ?>,用于找回密码" style="display: none;" title="用于找回密码">
<div class="code" id="codeBox" style="display: none;">
<input type="text" id="code" placeholder="请输入验证码">
<a href="javascript:void(0);" id="getCode">获取验证码</a>
</div>
<div id="agree" class="agree">
<span><img data-v-427e1e01="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAAVFBMVEWeICicICedISidISibIiadISeeISiOOTmdISeeISj///+aGSGcHiT68vOYFByhKjHRmZzqz9CzVlvx4OG4YWXJh4qpPUPkw8WkMjjdsrS/cXatR00P5JiiAAAACnRSTlPuTT//Gq+6A9iEOAr7KAAAAbBJREFUSMeVlte2gyAQRTHYcBBQiu3///OOMdZALvCUrHX2mYYAqao8Y2VN/l11ybK8qkiVv1hR04hVF+yVVwT1NaFRi9RIkIzVNHrVLCOsIPEAKRgpEwJgiJIk6ZEgUQlxAP5JKhLgnCYAHOg4ygQAwBnjEIsDAEDOSvUgooHRTHowkQCseqWbLh546wPA2e6r/4T6xp8SP/t9+M9vfQCQEtt9MnDqfSlxLpfe9OMVcLveB6x2StllG9D6n5/6dvqeg4BFaT3M46eQm76zywPgHAMMTaOVkQAf/6Hd9QpTvW8N4LJf+41ETwEbzJ296uVzewtwtnsLMDoVgi53PcADAGmmTdAO1gnxpb9H4HtCW0dmF/A/AOz4ocAyJqv8/geALbXdrm9a3Wm//xlh7Xl7EvvPp/+1hgWndCIB/+ukpTOXMgL+90nLxd6CePyvEfDjoc6orv3l//ge8Hjo7aB/+D8BgWnN2wD9/l+HAO65cU2rDfh7ANy1WHs3+P19x8y6sWdrzejz9wOCusWN1OcfOMg4B786CGC7QgRJv7KSL8Xkazf5Yk9+OiQ/TlKfP3/iYTk/HuYxLgAAAABJRU5ErkJggg=="></span>
我已阅读并同意 <a href="javascript:void(0);" id="agree_btn">用户协议及隐私协议<a/>
</div>
<a href="javascript:process_login();" id="submitButton" class="button fit"> </a>
<div style="display:flex;justify-content:center;gap:8px;font-size:12px" >
<div style="display:flex;align-items:center;flex-direction:column;gap:4px;cursor:pointer" id="linuxdoConnect">
<img src="static/img/linuxdo_logo.png" style="width:60px;height:60px" alt="Linux.Do登录"/>
<div>Linux.do</div>
</div>
<div style="display:flex;align-items:center;flex-direction:column;gap:4px;cursor:pointer" id="naixiConnect">
<img src="https://forum.naixi.net/favicon.ico" style="width:60px;height:60px" alt="奶昔登录"/>
<div>奶昔登录</div>
</div>
</div>
<div class="forget_password">
<a href="javascript:void(0);" id="forgetPassword" data-type="2">忘记密码?</a>
<a href="javascript:void(0);" class="pull-right" id="switchBtn" data-type="1">注册</a>
</div>
</div>
<div id="bg" class="gamebg" style="background-image: url(static/img/login_bg.jpg);"></div>
</div>
</template>
<style scoped>
</style>

View File

@@ -4,4 +4,13 @@ import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
server: {
proxy: {
'/api': {
target: 'http://localhost:3001',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
})