Compare commits

...

2 Commits

Author SHA1 Message Date
5f4858e991 feat(web): 初始化Web模块并重构登录页面
- 将模块名从 web-link 更改为 web
- 新增全局样式文件 index.scss 和 layout.scss,包含颜色、间距、布局等工具类
-重构登录页面,使用 Vue 组件语法替换原有 PHP 模板语法- 移除奶昔登录选项,保留 Linux.Do 登录方式- 更新静态资源路径,统一添加前导斜杠以确保正确加载
- 在 main.js 中调整应用挂载顺序,并增加配置信息打印
- 引入 axios 依赖用于后续 HTTP 请求处理- 修改 favicon 图标引用路径为 /favicon.ico- 调整背景图和表单元素样式,优化视觉效果与用户体验
2025-10-12 01:49:33 +08:00
6a412b3567 feat(project): 配置项目别名和turbo工作流- 添加 @ 别名指向 src 目录
- 配置 turbo 工作流支持
- 更新 package.json 脚本和依赖
- 调整路由和组件导入路径
- 格式化模板代码和样式
- 新增模块配置文件和工作区设置
2025-10-12 01:25:39 +08:00
16 changed files with 331 additions and 33 deletions

View File

@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8"/>
<link rel="icon" type="image/svg+xml" href="/vite.svg"/>
<link rel="icon" type="image/svg+xml" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta charset="utf-8">
<meta name="viewport"
@@ -12,8 +12,8 @@
<meta name="screen-orientation" content="portrait"/>
<meta name="x5-fullscreen" content="true"/>
<meta name="360-fullscreen" content="true"/>
<script src="static/js/md5.js"></script>
<script src="static/js/common.js?v=1"></script>
<script src="/static/js/md5.js"></script>
<script src="/static/js/common.js?v=1"></script>
<title>神临苍月</title>
</head>
<body oncontextmenu="return false" ondragstart="return false">

View File

@@ -0,0 +1,3 @@
export default {
}

8
module/web/package.json Normal file
View File

@@ -0,0 +1,8 @@
{
"name": "web",
"private": true,
"scripts": {
"dev": "cd ../.. && pnpm dev",
"build": "cd ../.. && pnpm build"
}
}

View File

@@ -8,19 +8,23 @@
"dev:server": "pnpm --filter chuanqi-server dev",
"start:server": "pnpm --filter chuanqi-server start",
"build": "vite build",
"preview": "vite preview"
"preview": "vite preview",
"turbo": "turbo run dev"
},
"dependencies": {
"axios": "^1.12.2",
"vue": "^3.5.21",
"vue-router": "^4.5.1"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.1",
"turbo": "^2.5.8",
"vite": "^7.1.7"
},
"pnpm": {
"workspace": [
"server"
]
}
},
"packageManager": "pnpm@10.18.2"
}

View File

@@ -1,2 +1,2 @@
packages:
- server
- module/*

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

108
src/assets/css/index.scss Normal file
View File

@@ -0,0 +1,108 @@
@use "layout";
// 1. 变量(可改)
$colors: (
"slate": #64748b,
"sky": #0ea5e9,
"emerald":#10b981,
"rose": #f43f5e
);
$border-color: #DCDFE6;
//$spacer: 0.25rem; // 1 单位 = 4px
$spacer: 1px; // 1 单位 = 1px
// 2. 工具函数
@function size($n) { @return $n * $spacer; }
// 3. 生成 margin / padding
@each $k, $v in $colors {
.bg-#{$k} { background-color: $v; }
.text-#{$k}{ color: $v; }
}
@for $i from 0 through 40 {
.m-#{$i} { margin: size($i); }
.mt-#{$i} { margin-top: size($i); }
.mr-#{$i} { margin-right: size($i); }
.mb-#{$i} { margin-bottom: size($i); }
.ml-#{$i} { margin-left: size($i); }
.mx-#{$i} { margin-left: size($i); margin-right: size($i); }
.my-#{$i} { margin-top: size($i); margin-bottom: size($i); }
.p-#{$i} { padding: size($i); }
.pt-#{$i} { padding-top: size($i); }
.pr-#{$i} { padding-right: size($i); }
.pb-#{$i} { padding-bottom: size($i); }
.pl-#{$i} { padding-left: size($i); }
.px-#{$i} { padding-left: size($i); padding-right: size($i); }
.py-#{$i} { padding-top: size($i); padding-bottom: size($i); }
.font-#{$i} { font-size: size($i); }
.round-#{$i} { border-radius: size($i); overflow: hidden}
.lh-#{$i} { line-height: size($i)!important; }
}
.rounded { border-radius: 0.25rem; }
.shadow { box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.shadow-l { box-shadow: -1px 0 3px rgba(0,0,0,0.2); }
.shadow-r { box-shadow: 1px 0 3px rgba(0,0,0,0.2); }
.shadow-b { box-shadow: 0 1px 3px rgba(0,0,0,0.2); }
.shadow-t { box-shadow: 0 -1px 3px rgba(0,0,0,0.2); }
.border { border: 1px solid #{$border-color}; }
$directions: (
"t": top,
"r": right,
"b": bottom,
"l": left
);
@each $d, $v in $directions {
.border-#{$d} { border-#{$v}: 1px solid #{$border-color}; }
}
@each $v in (60,80,100,120,140,160,180,200,220,240,260,280,300,320,340,360,380) {
.w-#{$v}{width: size($v)!important;}
.h-#{$v}{height: size($v)!important;}
}
.w100{width: 100%!important;}
.h100{height: 100%!important;}
.w50{width: 50%!important;}
.h50{height: 50%!important;}
.w100v{width: 100vw!important;}
.h100v{height: 100vh!important;}
.w50v{width: 50vw!important;}
.h50v{height: 50vh!important;}
.no-devices{
background-image: url("../assets/no-devices.png");
background-repeat: no-repeat;
background-position: 50% 38.2%; /* 纵轴38.2% */
background-size: auto 40%;
text-align: center;
color: #888;
display: flex;
justify-content: center; /* 水平居中 */
padding-top: calc( 45% + 16px);
}
/* Chrome, Edge, Safari, Opera */
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type="number"] {
-moz-appearance: textfield;
}
.el-form-item__label,.el-descriptions-item__label{
word-break: break-word;
}
.el-table__header-wrapper{
.cell{
word-break: break-word;
}
}

155
src/assets/css/layout.scss Normal file
View File

@@ -0,0 +1,155 @@
.grid {
display: grid;
$cols: 2, 3, 4;
@each $size in $cols {
&.col-#{$size} {
grid-template-columns: repeat(#{$size}, 1fr);
}
}
}
.flex {
display: flex;
align-items: center;
&.center {
justify-content: center;
}
&.between {
justify-content: space-between;
}
&.around {
justify-content: space-around;
}
&.column {
flex-direction: column;
.fill {
.el-scrollbar__wrap {
overflow-x: hidden;
}
}
}
&.wrap {
flex-wrap: wrap;
}
&.baseline {
align-items: baseline;
}
&.start {
align-items: start;
}
.fill {
flex: 1;
min-width: 0;
min-height: 0;
}
$gaps: 4, 8, 12, 16, 20, 24, 28, 32, 36, 40;
@each $size in $gaps {
&.gap-#{$size} {
gap: #{$size}px;
}
}
&.el-form {
flex-wrap: wrap;
$cols: 2, 3, 4;
@each $size in $cols {
&.col-#{$size} {
.el-form-item {
width: calc(100% / #{$size});
}
}
}
.el-form-item {
&.row {
width: 100%;
}
.el-date-editor, .el-select,.el-input-number {
width: 100% !important;
}
}
}
}
.shrink {
flex-shrink: 0;
}
.cameraGrid {
&.one {
display: flex;
justify-content: center;
align-items: center;
.video {
width: 100%;
height: 100%;
}
}
&.four {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.video {
width: 49%;
height: 49%;
}
}
&.nine {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.video {
width: calc(100% / 3);
height: calc(100% / 3);
padding: 10px;
text-align: center;
}
}
&.thirteen {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(4, calc(95% / 4));
grid-gap: 0.67708vw;
place-content: space-around;
.main {
grid-column-start: 2;
grid-column-end: 4;
grid-row-start: 2;
grid-row-end: 4;
position: relative;
}
}
&.eight {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(4, calc(95% / 4));
grid-gap: 0.67708vw;
place-content: space-around;
.main {
grid-column-start: 1;
grid-column-end: 4;
grid-row-start: 1;
grid-row-end: 4;
}
}
}

View File

@@ -13,7 +13,9 @@ document.onkeydown = document.onkeyup = document.onkeypress = function (e) {
const app = createApp(App)
fetch("/api/config", {method: "GET"}).then(res => res.json()).then(res => {
console.log(res)
app.config.globalProperties.$gameName = res.data.gameName || "神临苍月";
app.config.globalProperties.$_CONFIG = res.data;
app.use(router).mount('#app')
app.use(router)
app.mount('#app')
})

View File

@@ -10,7 +10,7 @@ const routes = [
},
{
path: '/login', name: 'Login',
component: () => import('../views/login.vue')
component: () => import('@/views/login.vue')
}
]
@@ -19,4 +19,4 @@ const router = createRouter({
routes
})
export default router
export default router

View File

@@ -1,49 +1,52 @@
<script setup>
import {reactive} from "vue";
import {defineOptions} from "vue";
defineOptions({name: 'Login'});
const servers = ref([])
const account = ref('')
const password = ref('')
function handleLogin() {
}
</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;">
<h2 class="title">{{ $gameName }}</h2>
<input type="text" id="account" v-model="account" placeholder="请输入账号" @keyup="v=>account=v.replace(/[\W]/g, '')" autocomplete="off"/>
<input type="password" id="password" v-model="password" placeholder="请输入密码"/>
<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>
<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/>
<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" >
<a id="submitButton" class="button fit" @click="handleLogin"> </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登录"/>
<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 id="bg" class="gamebg"/>
</div>
</template>
<style scoped>
.gamebg {
background-image: url("/static/img/login_bg.jpg");
}
</style>
</style>

9
turbo.json Normal file
View File

@@ -0,0 +1,9 @@
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"dev": {
"cache": false,
"persistent": true
}
}
}

View File

@@ -1,16 +1,22 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import * as path from "node:path";
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src')
}
},
server: {
proxy: {
'/api': {
target: 'http://localhost:3001',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
// rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
})
})