feat(web):重构前端路由和配置管理
- 移除旧版全局变量定义,采用Vue Router管理页面跳转 - 新增配置文件统一管理游戏名称和注册码设置 -优化加载条逻辑,使用onMounted确保DOM元素正确获取 - 添加服务端配置接口,动态加载游戏配置信息 - 升级依赖包,引入vue-router支持单页应用 - 调整项目结构,分离服务器端代码至独立目录 - 配置Vite代理转发API请求到本地开发服务器 - 更新package.json脚本命令,支持前后端联合调试 - 引入pnpm workspace管理模式,提升多包协作效率
This commit is contained in:
10
package.json
10
package.json
@@ -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
2
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
packages:
|
||||
- server
|
||||
4
public/config.json
Normal file
4
public/config.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"reg_code_open": true,
|
||||
"code_type": ""
|
||||
}
|
||||
@@ -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
0
server/config/index.js
Normal file
27
server/index.js
Normal file
27
server/index.js
Normal 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
15
server/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
134
src/App.vue
134
src/App.vue
@@ -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
5
src/config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
gameName: "神临苍月",
|
||||
reg_code_open: true,
|
||||
code_type: "email",
|
||||
}
|
||||
@@ -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
22
src/router/index.js
Normal 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
10
src/views/index.vue
Normal 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
49
src/views/login.vue
Normal 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>
|
||||
@@ -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/, '')
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user