{{ romInfo.title }}
40 |45 | {{ romInfo.publisher }} 46 |
47 |
4 |
5 |
6 |
9 | 基于vue3 + ts的在线FC(NES)🎮游戏项目,前端。 10 |
11 | 12 | * 框架:`vue3` 13 | * 构建工具:`vite` 14 | * FC模拟器组件:[taiyuuki/nes-vue](https://github.com/taiyuuki/nes-vue) 15 | * 组件库:`element-plus` 16 | * 类型检测:`typescript` 17 | * 前后端交互:`axios` 18 | * CSS预编译:`scss` 19 | * 代码格式:`eslint` `stylelint` 20 | * `vue3`生态 21 | * `vue-router` 22 | * `pinia` 23 | * `pinia-plugin-persistedstate`:pinia持久化插件 24 | * `vite`插件 25 | * `unocss`:CSS原子类生产 26 | * `unplugin-auto-import`:自动导入API 27 | * `unplugin-vue-components`:自动导入组件 28 | * `vite-plugin-pages`:基于文件自动创建路由 29 | * `vite-plugin-vue-layouts`:自动创建根路由 30 | * `vite-plugin-pwa`:PWA模式 31 | 32 | ## 项目运行 33 | 34 | 安装依赖 35 | 36 | ```shell 37 | yarn install 38 | ``` 39 | 40 | 运行 41 | 42 | ```shell 43 | yarn dev 44 | ``` 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /server/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { Request, Response, RequestHandler, NextFunction } from 'express' 2 | import express from 'express' 3 | import roms from './routers/rom_router' 4 | import categorys from './routers/categorys_router' 5 | import { port, getRomPath, hostIp, baseURL } from './server.config' 6 | import * as logger from './utils/logger' 7 | import banner from './routers/banner_router' 8 | 9 | const setHeaders: RequestHandler = function ( 10 | req: Request, 11 | res: Response, 12 | next: NextFunction 13 | ) { 14 | res.setHeader('Access-Control-Allow-Origin', '*') // 允许跨域 15 | res.setHeader('Access-Control-Allow-Headers', '*') // 允许客户端设置请求头 16 | res.setHeader('Access-Control-Allow-Methods', '*') // 允许客户端的请求方式 17 | if (req.method === 'OPTIONS') {return res.sendStatus(200)} // options请求快速结束 18 | next() 19 | } 20 | const app = express() 21 | 22 | app.use(express.json()) 23 | // 请求头 24 | .use(setHeaders) 25 | // 静态资源 26 | .use('/roms', express.static(getRomPath())) 27 | // 路由 28 | .use(categorys) 29 | .use(roms) 30 | .use(banner) 31 | 32 | // 开发模式下配置本地ip域名 33 | if (process.env.NODE_ENV === 'development') { 34 | app.set('host', hostIp) 35 | } 36 | 37 | app.listen(port, () => { 38 | logger.info(`server: ${baseURL}`) 39 | }) 40 | -------------------------------------------------------------------------------- /server/src/utils/response.ts: -------------------------------------------------------------------------------- 1 | import type { Response } from 'express' 2 | import * as logger from './logger' 3 | import os from 'os' 4 | 5 | function sendEmpty(res: Response, target: string) { 6 | res.send({ 7 | code: 400, 8 | message: `${target}内容不能为空`, 9 | }) 10 | } 11 | 12 | async function dispatchResponse( 13 | target: Function, 14 | res: Response, 15 | message?: string, 16 | err?: (args: any) => any 17 | ) { 18 | message = message ?? '发生错误' 19 | try { 20 | await target() 21 | } 22 | catch (e) { 23 | logger.error(`${e}`) 24 | if (err) { 25 | err(e) 26 | } 27 | res.send({ 28 | code: 500, 29 | msg: message, 30 | }) 31 | } 32 | } 33 | 34 | function getIpAddress() { 35 | const ifaces = os.networkInterfaces() 36 | for (const dev in ifaces) { 37 | const iface = ifaces[dev]! 38 | 39 | for (let i = 0; i < iface.length; i++) { 40 | const { family, address, internal } = iface[i] 41 | 42 | if (family === 'IPv4' && address !== '127.0.0.1' && !internal) { 43 | return address 44 | } 45 | } 46 | } 47 | return '127.0.0.1' 48 | } 49 | 50 | export { sendEmpty, dispatchResponse, getIpAddress } 51 | -------------------------------------------------------------------------------- /client/src/components/MainFooter.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 57 | 58 | -------------------------------------------------------------------------------- /client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "moduleResolution": "Node", 7 | "strict": true, 8 | "jsx": "preserve", 9 | "resolveJsonModule": true, 10 | "isolatedModules": true, 11 | "esModuleInterop": true, 12 | "lib": [ 13 | "ESNext", 14 | "DOM" 15 | ], 16 | "skipLibCheck": true, 17 | "noEmit": true, 18 | "baseUrl": "./", 19 | "paths": { 20 | "src/*": [ 21 | "src/*" 22 | ], 23 | "css/*": [ 24 | "*" 25 | ], 26 | "components/*": [ 27 | "src/components/*" 28 | ], 29 | "layouts/*": [ 30 | "src/layouts/*" 31 | ], 32 | "pages/*": [ 33 | "src/pages/*" 34 | ], 35 | "assets/*": [ 36 | "src/assets/*" 37 | ], 38 | "stores/*": [ 39 | "src/stores/*" 40 | ], 41 | "router/*": [ 42 | "src/router/*" 43 | ] 44 | } 45 | }, 46 | "include": [ 47 | "src/**/*.ts", 48 | "src/**/*.d.ts", 49 | "src/**/*.tsx", 50 | "src/**/*.vue" 51 | ], 52 | "references": [ 53 | { 54 | "path": "./tsconfig.node.json" 55 | } 56 | ], 57 | "types": [ 58 | "vite-plugin-pages/client", 59 | "vite-plugin-vue-layouts/client", 60 | "node" 61 | ] 62 | } -------------------------------------------------------------------------------- /client/src/pages/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 |
4 |
5 |
6 |
9 | 基于vue3 + express的在线FC(NES)🎮游戏项目。 10 |
11 | 12 | ## 功能 13 | 14 | 在线玩FC游戏,一共约400个游戏,全中文版。 15 | 16 | 前端框架:vue3,后端框架:express。 17 | 18 | 所有游戏资料、图片提取自`OfflineList`。 19 | 20 | * 设置主题色 21 | * 黑暗模式 22 | * 有限的支持移动端 23 | * 游戏分类 24 | * 搜索 25 | * 支持本地ROM 26 | * 支持双人 27 | * 支持保存和读取,每个游戏默认提供三个存档位。 28 | * 自定义按键 29 | * 支持手柄 30 | * 截图 31 | * 全屏 32 | * 支持PWA模式 33 | 34 | 35 |
36 |
>
37 |
38 |
4 |
5 |
8 | 基于vue3 + express的在线FC(NES)🎮游戏项目,服务端。 9 |
10 | 11 | * 框架:`express` 12 | * 数据库:`sqlite3` 13 | * 数据库驱动: `Sequelize` 14 | * 类型检测:`typescript` 15 | * 代码格式:`eslint` 16 | * 打包:`tsup` 17 | 18 | ## 项目运行 19 | 20 | 游戏ROM、图片等静态资源,我单独打包放在[release](../../../releases/download/v0.0.1/roms.zip)里,下载、解压后将roms文件夹放在server文件夹内即可。 21 | 22 | ### 安装依赖 23 | 24 | ```shell 25 | pnpm install 26 | ``` 27 | 28 | ### 启动服务端 29 | 30 | #### node 31 | 32 | 使用node运行`dist/index.js`即可。 33 | 34 | ```shell 35 | node dist/index.js 36 | ``` 37 | 38 | #### pm2 39 | 40 | 推荐使用`pm2` 41 | 42 | 安装pm2 43 | 44 | ```shell 45 | npm i pm2 -g 46 | ``` 47 | 48 | 启动服务 49 | 50 | ```shell 51 | pm2 start dist/index.js --watch 52 | ``` 53 | 54 | 查看日志 55 | 56 | ```shell 57 | pm2 log 58 | ``` 59 | 60 | 查看所有服务 61 | 62 | ```shell 63 | pm2 list 64 | ``` 65 | 66 | 停止服务 67 | 68 | ```shell 69 | pm2 stop id 70 | ``` 71 | 72 | 移除服务 73 | 74 | ```shell 75 | pm2 delete id 76 | ``` 77 | 78 | 查看实时状态 79 | 80 | ```shell 81 | pm2 monit 82 | ``` 83 | 84 | ### 接口 85 | 86 | 服务器默认端口为8848,默认本地启动地址:`http://localhost:8848` 87 | 88 | 地址可以在`src/server.config.ts`中进行设置(修改后记得要`pnpm build`重新编译),前端也需要修改`src/client.config.ts`中对应的设置。 89 | 90 | #### 获取游戏分类 91 | 92 | **API: `/categorys`** 93 | 94 | 示例:`http://localhost:8848/categorys` 95 | 96 | #### 获取轮播图 97 | 98 | **API: `/banner`** 99 | 100 | #### 获取游戏列表 101 | 102 | 说明:此接口用于获取游戏列表,同时可以用作搜索。 103 | 104 | **API: `/romlist`** 105 | 106 | 可选参数: 107 | 108 | * *cat*:分类,比如`ACT`、 `STG` 109 | * *keyword*:搜索关键字 110 | * *page*:分页,默认值为1 111 | * *limit*:每页数量,默认值为20 112 | 113 | 示例:`http://localhost:8848/romlist?cat=ACT&page=2&limit=10` 114 | 115 | #### 随机获取游戏列表 116 | 117 | 说明:此接口用于获取随机N个游戏。 118 | 119 | **API: `/random`** 120 | 121 | 可选参数: 122 | 123 | * *n*:游戏数量 124 | * *cat*:分类,比如比如`ACT` 、`STG` 125 | * *ignore*:不包括某个游戏的id 126 | 127 | 示例:`http://localhost:8848/random?n=10&cat=ACT&ignore=7` 128 | 129 | #### 获取单个游戏 130 | 131 | 说明:此接口可以通过游戏id获取单个游戏完整信息。 132 | 133 | **API: `/rom`** 134 | 135 | **必选参数**: 136 | 137 | * *id*:游戏id 138 | 139 | 示例:`http://localhost:8848/rom?id=7` 140 | 141 | #### 搜索建议 142 | 143 | 说明:此接口用于给定关键词列出搜索建议。 144 | 145 | **API: `/suggestions`** 146 | 147 | **必选参数**: 148 | 149 | * *keyword*:关键词 150 | 151 | 示例:`http://localhost:8848/suggestions?keyword=洛克人` 152 | 153 | #### 静态资源 154 | 155 | 静态资源的地址会包含在获取的游戏列表中。 156 | 157 | **图片资源** 158 | 159 | **API: `http://localhost:8848/roms/img/id`** 160 | 161 | 每一个游戏都有两张图片。 162 | 163 | 例如,id为472的游戏,封面: 164 | 165 | `http://localhost:8848/roms/img/472a.png` 166 | 167 | 截图: 168 | 169 | `http://localhost:8848/roms/img/472b.png` 170 | 171 | **ROM资源** 172 | 173 | **API: `http://localhost:8848/roms/游戏名`** 174 | 175 | 例如,大金刚:`http://localhost:8848/roms/大金刚 (简)` -------------------------------------------------------------------------------- /client/src/components/TBanner.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 30 |
112 |
136 |