commit 03a3645dff311be511feb732bb972951568a8b47 Author: jacky Date: Thu Jun 11 18:32:13 2026 +0800 feat: NCT集训报名系统 - 陈村校区 + 北滘校区(数据独立) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..456e6d2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +data/*.db diff --git a/db.js b/db.js new file mode 100644 index 0000000..1168ba0 --- /dev/null +++ b/db.js @@ -0,0 +1,112 @@ +const initSqlJs = require('sql.js'); +const fs = require('fs'); +const path = require('path'); + +const ADMIN_PASSWORD = 'admin123'; // 管理员密码,可自行修改 + +// 工厂函数:为每个校区创建独立的数据库实例 +function createCampusDB(campusName) { + const dbDir = path.join(__dirname, 'data'); + const dbPath = path.join(dbDir, campusName + '.db'); + let db = null; + + async function init() { + const SQL = await initSqlJs(); + + if (!fs.existsSync(dbDir)) { + fs.mkdirSync(dbDir, { recursive: true }); + } + + if (fs.existsSync(dbPath)) { + const buffer = fs.readFileSync(dbPath); + db = new SQL.Database(buffer); + } else { + db = new SQL.Database(); + } + + db.run(` + CREATE TABLE IF NOT EXISTS registrations ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + period TEXT NOT NULL, + teacher TEXT NOT NULL, + createdAt TEXT DEFAULT (datetime('now', 'localtime')) + ) + `); + + save(); + console.log('[db] ' + campusName + ' 数据库初始化完成'); + } + + function save() { + const data = db.export(); + const buffer = Buffer.from(data); + fs.writeFileSync(dbPath, buffer); + } + + function insertRegistration(name, period, teacher) { + db.run( + 'INSERT INTO registrations (name, period, teacher) VALUES (?, ?, ?)', + [name, period, teacher] + ); + save(); + } + + function getAllRegistrations() { + const result = db.exec('SELECT * FROM registrations ORDER BY id DESC'); + if (result.length === 0) return []; + const columns = result[0].columns; + return result[0].values.map(row => { + const obj = {}; + columns.forEach((col, i) => { obj[col] = row[i]; }); + return obj; + }); + } + + function deleteRegistration(id) { + db.run('DELETE FROM registrations WHERE id = ?', [id]); + save(); + } + + function getStats() { + const total = db.exec('SELECT COUNT(*) as count FROM registrations'); + const totalCount = total.length > 0 ? total[0].values[0][0] : 0; + + const byPeriod = db.exec( + 'SELECT period, COUNT(*) as count FROM registrations GROUP BY period' + ); + const periodStats = {}; + if (byPeriod.length > 0) { + byPeriod[0].values.forEach(row => { + periodStats[row[0]] = row[1]; + }); + } + + const byTeacher = db.exec( + 'SELECT teacher, COUNT(*) as count FROM registrations GROUP BY teacher' + ); + const teacherStats = {}; + if (byTeacher.length > 0) { + byTeacher[0].values.forEach(row => { + teacherStats[row[0]] = row[1]; + }); + } + + return { total: totalCount, byPeriod: periodStats, byTeacher: teacherStats }; + } + + function verifyPassword(password) { + return password === ADMIN_PASSWORD; + } + + return { + init, + insertRegistration, + getAllRegistrations, + deleteRegistration, + getStats, + verifyPassword + }; +} + +module.exports = { createCampusDB }; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..53a5fe5 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,835 @@ +{ + "name": "nct-registration", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "nct-registration", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "express": "^4.22.2", + "sql.js": "^1.14.1" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sql.js": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.14.1.tgz", + "integrity": "sha512-gcj8zBWU5cFsi9WUP+4bFNXAyF1iRpA3LLyS/DP5xlrNzGmPIizUeBggKa8DbDwdqaKwUcTEnChtd2grWo/x/A==", + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..2c4e7b4 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "nct-registration", + "version": "1.0.0", + "description": "NCT 集训报名系统", + "main": "server.js", + "scripts": { + "start": "node server.js", + "dev": "node --watch server.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "commonjs", + "dependencies": { + "express": "^4.22.2", + "sql.js": "^1.14.1" + } +} diff --git a/public/beijiao/admin.html b/public/beijiao/admin.html new file mode 100644 index 0000000..bfa309b --- /dev/null +++ b/public/beijiao/admin.html @@ -0,0 +1,529 @@ + + + + + + 报名管理后台 | 北滘校区 + + + + + +
+

📋 报名管理后台

+

北滘校区 · NCT 集训报名数据管理

+
+ + +
+

🔐 管理员登录

+

请输入管理员密码查看报名数据

+ + + +
+ + +
+ + +
+
+
0
+
总报名人数
+
+
+
0
+
第一期
+
+
+
0
+
第二期
+
+
+
0
+
第三期
+
+
+ + +
+ + + +
+ + + +
+ + +
+
+ + + + + + + + + + + + + +
序号姓名期数老师报名时间操作
+ +
+
+
+ + + + + + + diff --git a/public/beijiao/css/style.css b/public/beijiao/css/style.css new file mode 100644 index 0000000..174e335 --- /dev/null +++ b/public/beijiao/css/style.css @@ -0,0 +1,704 @@ +* { margin: 0; padding: 0; box-sizing: border-box; } + +:root { + --orange: #FF6B35; + --orange-light: #FF8F5E; + --orange-dark: #E85A24; + --blue: #4A6CF7; + --blue-light: #6B8CFF; + --purple: #7C5CFC; + --dark: #1A1A2E; + --text: #333; + --text-light: #666; + --bg: #F5F7FA; + --white: #fff; + --shadow: 0 4px 24px rgba(0,0,0,0.08); + --shadow-lg: 0 12px 40px rgba(0,0,0,0.12); +} + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif; + background: var(--bg); + color: var(--text); + line-height: 1.6; + -webkit-font-smoothing: antialiased; +} + +/* ====== Hero ====== */ +.hero { + background: linear-gradient(135deg, #FF6B35 0%, #FF8F5E 40%, #FFB347 100%); + color: white; + padding: 60px 24px 80px; + text-align: center; + position: relative; + overflow: hidden; +} + +.hero::before { + content: ''; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: radial-gradient(circle, rgba(255,255,255,0.08) 1px, transparent 1px); + background-size: 30px 30px; + animation: heroPattern 20s linear infinite; +} + +@keyframes heroPattern { + from { transform: translate(0, 0); } + to { transform: translate(30px, 30px); } +} + +.hero-content { + position: relative; + z-index: 2; + max-width: 700px; + margin: 0 auto; +} + +.hero-badge { + display: inline-block; + background: rgba(255,255,255,0.2); + backdrop-filter: blur(10px); + padding: 6px 20px; + border-radius: 20px; + font-size: 14px; + margin-bottom: 20px; + letter-spacing: 1px; +} + +.hero h1 { + font-size: 32px; + font-weight: 800; + margin-bottom: 12px; + text-shadow: 0 2px 10px rgba(0,0,0,0.1); +} + +.hero .subtitle { + font-size: 18px; + opacity: 0.95; + margin-bottom: 28px; +} + +.hero-stats { + display: flex; + justify-content: center; + gap: 32px; + flex-wrap: wrap; +} + +.hero-stat { + text-align: center; +} + +.hero-stat .num { + font-size: 36px; + font-weight: 800; +} + +.hero-stat .label { + font-size: 13px; + opacity: 0.85; + margin-top: 2px; +} + +/* ====== Sections ====== */ +.section { + max-width: 800px; + margin: 0 auto; + padding: 48px 20px; +} + +.section-header { + text-align: center; + margin-bottom: 36px; +} + +.section-header h2 { + font-size: 26px; + font-weight: 700; + color: var(--dark); + margin-bottom: 8px; +} + +.section-header p { + color: var(--text-light); + font-size: 15px; +} + +/* ====== Info Cards ====== */ +.info-grid { + display: grid; + grid-template-columns: 1fr; + gap: 16px; +} + +.info-card { + background: var(--white); + border-radius: 16px; + padding: 24px; + box-shadow: var(--shadow); + display: flex; + gap: 16px; + align-items: flex-start; + transition: transform 0.3s; +} + +.info-card:hover { + transform: translateY(-2px); +} + +.info-icon { + width: 52px; + height: 52px; + border-radius: 14px; + display: flex; + align-items: center; + justify-content: center; + font-size: 26px; + flex-shrink: 0; +} + +.info-icon.orange { background: #FFF0E8; } +.info-icon.blue { background: #EEF2FF; } +.info-icon.purple { background: #F3EEFF; } +.info-icon.green { background: #E8F9EF; } + +.info-card h3 { + font-size: 17px; + font-weight: 600; + color: var(--dark); + margin-bottom: 6px; +} + +.info-card p { + font-size: 14px; + color: var(--text-light); + line-height: 1.7; +} + +/* ====== Timeline ====== */ +.period-section { + background: var(--white); + border-radius: 16px; + padding: 24px; + box-shadow: var(--shadow); + margin-bottom: 24px; +} + +.period-badge { + display: inline-block; + font-size: 12px; + font-weight: 600; + padding: 3px 12px; + border-radius: 10px; + margin-bottom: 10px; +} + +.period-badge.orange { background: #FFF0E8; color: var(--orange); } +.period-badge.blue { background: #EEF2FF; color: var(--blue); } + +.period-section h3 { + font-size: 20px; + font-weight: 700; + color: var(--dark); + margin-bottom: 6px; +} + +.period-info { + font-size: 14px; + color: var(--text-light); + margin-bottom: 16px; + line-height: 1.7; +} + +/* ====== Registration Input ====== */ +.reg-input-row { + display: flex; + gap: 10px; + align-items: center; + margin-top: 12px; +} + +.reg-input { + flex: 1; + padding: 10px 14px; + border: 2px solid #e8e8e8; + border-radius: 10px; + font-size: 14px; + outline: none; + transition: border-color 0.3s; +} + +.reg-input:focus { + border-color: var(--orange); +} + +.reg-input::placeholder { + color: #bbb; +} + +.reg-btn { + padding: 10px 20px; + border: none; + border-radius: 10px; + font-size: 14px; + font-weight: 700; + color: white; + cursor: pointer; + white-space: nowrap; + transition: transform 0.2s, opacity 0.2s; +} + +.reg-btn:hover { transform: scale(1.03); } +.reg-btn:active { transform: scale(0.97); } +.reg-btn.orange { background: var(--orange); } +.reg-btn.blue { background: var(--blue); } + +/* ====== Exam Info ====== */ +.exam-card { + background: linear-gradient(135deg, var(--dark) 0%, #2D2D5E 100%); + color: white; + border-radius: 20px; + padding: 32px 28px; + text-align: center; + box-shadow: var(--shadow-lg); + position: relative; + overflow: hidden; +} + +.exam-card::after { + content: ''; + position: absolute; + top: -40%; + right: -20%; + width: 200px; + height: 200px; + background: radial-gradient(circle, rgba(255,107,53,0.15), transparent); + border-radius: 50%; +} + +.exam-card h3 { + font-size: 15px; + opacity: 0.7; + margin-bottom: 8px; + letter-spacing: 2px; + position: relative; + z-index: 2; +} + +.exam-date { + font-size: 38px; + font-weight: 800; + margin-bottom: 6px; + position: relative; + z-index: 2; + background: linear-gradient(135deg, #FF6B35, #FFB347); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.exam-note { + font-size: 14px; + opacity: 0.6; + position: relative; + z-index: 2; +} + +/* ====== Value Cards ====== */ +.value-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 14px; +} + +.value-card { + background: var(--white); + border-radius: 16px; + padding: 22px 18px; + text-align: center; + box-shadow: var(--shadow); + transition: transform 0.3s; +} + +.value-card:hover { + transform: translateY(-3px); +} + +.value-emoji { + font-size: 36px; + margin-bottom: 10px; +} + +.value-card h3 { + font-size: 16px; + font-weight: 600; + color: var(--dark); + margin-bottom: 6px; +} + +.value-card p { + font-size: 13px; + color: var(--text-light); + line-height: 1.6; +} + +/* ====== Highlights ====== */ +.highlight-strip { + background: linear-gradient(135deg, #FFF8E1, #FFF3E0); + border-radius: 16px; + padding: 20px 24px; + border-left: 5px solid #FFB347; + margin: 20px 0; +} + +.highlight-strip p { + font-size: 14px; + color: #8B6914; + line-height: 1.7; +} + +.highlight-strip strong { + color: #E85A24; +} + +/* ====== Fee Section ====== */ +.fee-table { + width: 100%; + background: var(--white); + border-radius: 16px; + overflow: hidden; + box-shadow: var(--shadow); +} + +.fee-row { + display: flex; + align-items: center; + padding: 18px 24px; + border-bottom: 1px solid #f0f0f0; +} + +.fee-row:last-child { + border-bottom: none; +} + +.fee-label { + flex: 1; + font-size: 15px; + color: var(--text); +} + +.fee-value { + font-size: 16px; + font-weight: 700; + color: var(--orange); +} + +/* ====== FAQ ====== */ +.faq-item { + background: var(--white); + border-radius: 14px; + margin-bottom: 12px; + box-shadow: 0 2px 12px rgba(0,0,0,0.05); + overflow: hidden; +} + +.faq-q { + padding: 18px 24px; + font-size: 15px; + font-weight: 600; + color: var(--dark); + cursor: pointer; + display: flex; + justify-content: space-between; + align-items: center; + user-select: none; + transition: background 0.2s; +} + +.faq-q:hover { + background: #f8f9fa; +} + +.faq-q .arrow { + font-size: 12px; + color: #999; + transition: transform 0.3s; +} + +.faq-item.open .faq-q .arrow { + transform: rotate(180deg); +} + +.faq-a { + display: none; + padding: 0 24px 18px; + font-size: 14px; + color: var(--text-light); + line-height: 1.8; +} + +.faq-item.open .faq-a { + display: block; +} + +/* ====== CTA ====== */ +.cta-section { + text-align: center; + padding: 48px 20px 24px; + max-width: 800px; + margin: 0 auto; +} + +.cta-box { + background: linear-gradient(135deg, #FF6B35, #FF8F5E, #FFB347); + border-radius: 20px; + padding: 40px 28px; + color: white; + box-shadow: 0 8px 32px rgba(255,107,53,0.3); +} + +.cta-box h2 { + font-size: 24px; + font-weight: 700; + margin-bottom: 12px; +} + +.cta-box p { + font-size: 15px; + opacity: 0.95; + margin-bottom: 24px; + line-height: 1.7; +} + +.cta-btn { + display: inline-block; + background: white; + color: var(--orange); + padding: 14px 40px; + border-radius: 30px; + font-size: 16px; + font-weight: 700; + text-decoration: none; + box-shadow: 0 4px 16px rgba(0,0,0,0.15); + transition: transform 0.3s, box-shadow 0.3s; +} + +.cta-btn:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(0,0,0,0.2); +} + +/* ====== Countdown ====== */ +.countdown-section { + background: var(--dark); + color: white; + padding: 36px 20px; + text-align: center; +} + +.countdown-title { + font-size: 14px; + opacity: 0.6; + letter-spacing: 3px; + margin-bottom: 16px; +} + +.countdown-boxes { + display: flex; + justify-content: center; + gap: 12px; +} + +.countdown-box { + background: rgba(255,255,255,0.08); + border-radius: 12px; + padding: 16px 20px; + min-width: 72px; +} + +.countdown-box .num { + font-size: 32px; + font-weight: 800; + color: var(--orange-light); +} + +.countdown-box .unit { + font-size: 12px; + opacity: 0.5; + margin-top: 2px; +} + +/* ====== Footer ====== */ +.footer { + text-align: center; + padding: 32px 20px; + color: #aaa; + font-size: 13px; +} + +/* ====== Modal ====== */ +.modal-overlay { + display: none; + position: fixed; + top: 0; left: 0; right: 0; bottom: 0; + background: rgba(0,0,0,0.4); + z-index: 1000; + justify-content: center; + align-items: center; +} + +.modal-overlay.show { + display: flex; +} + +.modal { + background: white; + border-radius: 20px; + padding: 32px; + width: 90%; + max-width: 380px; + text-align: center; + box-shadow: 0 20px 60px rgba(0,0,0,0.2); +} + +.modal h3 { + font-size: 20px; + margin-bottom: 6px; + color: var(--dark); +} + +.modal .modal-period { + font-size: 14px; + color: var(--orange); + font-weight: 600; + margin-bottom: 20px; +} + +.modal-input { + width: 100%; + padding: 12px 16px; + border: 2px solid #e8e8e8; + border-radius: 12px; + font-size: 16px; + outline: none; + transition: border-color 0.3s; + box-sizing: border-box; + text-align: center; +} + +.modal-input:focus { + border-color: var(--orange); +} + +.modal-select { + width: 100%; + padding: 12px 16px; + border: 2px solid #e8e8e8; + border-radius: 12px; + font-size: 16px; + outline: none; + background: white; + color: #999; + appearance: none; + -webkit-appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23999' stroke-width='1.5' fill='none'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 14px center; + cursor: pointer; + box-sizing: border-box; +} + +.modal-select:focus { + border-color: var(--orange); +} + +.modal-select.has-value { + color: #333; +} + +.modal-submit { + width: 100%; + padding: 14px; + background: var(--orange); + color: white; + border: none; + border-radius: 12px; + font-size: 16px; + font-weight: 700; + cursor: pointer; + margin-top: 14px; + transition: background 0.2s; +} + +.modal-submit:hover { background: var(--orange-dark); } +.modal-submit:disabled { background: #ccc; cursor: default; } + +.modal-cancel { + background: none; + border: none; + color: #999; + font-size: 14px; + margin-top: 12px; + cursor: pointer; +} + +.modal-cancel:hover { color: #666; } + +/* ====== Toast ====== */ +.toast { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(0.85); + background: rgba(0,0,0,0.82); + color: #fff; + padding: 28px 36px; + border-radius: 18px; + font-size: 15px; + line-height: 1.8; + text-align: center; + z-index: 2000; + opacity: 0; + transition: opacity 0.3s, transform 0.3s; + max-width: 340px; + box-shadow: 0 12px 40px rgba(0,0,0,0.25); + pointer-events: none; + white-space: pre-line; +} + +.toast.show { + opacity: 1; + transform: translate(-50%, -50%) scale(1); +} + +/* ====== Animations ====== */ +.fade-up { + opacity: 0; + transform: translateY(30px); + transition: opacity 0.6s ease, transform 0.6s ease; +} + +.fade-up.visible { + opacity: 1; + transform: translateY(0); +} + +/* ====== Mobile ====== */ +@media (max-width: 640px) { + .hero { padding: 48px 20px 64px; } + .hero h1 { font-size: 26px; } + .hero .subtitle { font-size: 15px; } + .hero-stats { gap: 20px; } + .hero-stat .num { font-size: 28px; } + .section { padding: 36px 16px; } + .section-header h2 { font-size: 22px; } + .value-grid { grid-template-columns: 1fr; } + .exam-date { font-size: 30px; } + .countdown-boxes { gap: 8px; } + .countdown-box { padding: 12px 16px; min-width: 60px; } + .countdown-box .num { font-size: 26px; } +} diff --git a/public/beijiao/index.html b/public/beijiao/index.html new file mode 100644 index 0000000..b5b0853 --- /dev/null +++ b/public/beijiao/index.html @@ -0,0 +1,320 @@ + + + + + + 2026 暑假 Kitten 图形化编程等级考试集训 | 北滘校区 + + + + + +
+
+ 🎯 2026 暑假集训 +

Kitten 图形化编程
等级考试集训

+

NCT 青少年编程能力等级测试 · 北滘校区

+
+
+
100%
+
集训学员通过率
+
+
+
75W+
+
全国考生累计
+
+
+
8人
+
小班精品教学
+
+
+
+
+ + +
+
距离 NCT 考试还有
+
+
+
--
+
+
+
+
--
+
+
+
+
--
+
+
+
+
--
+
+
+
+
+ + +
+
+

什么是 NCT?

+

国内最权威的青少年编程能力等级测试

+
+
+
+
🏛️
+
+

中国软件行业协会主办

+

NCT 由中国软件行业协会盖章颁发等级证书,是国家民政部认可的首批 AAA 级行业协会,唯一代表中国计算机软件产业界的全国性一级行业组织。

+
+
+
+
🌐
+
+

国内国际双认证

+

通过教育部 CELTSC 标准符合性认证,同时通过国际教育技术协会 ISTE 认证,是国内首家获得 ISTE Seal of Alignment 的编程测评项目。

+
+
+
+
🎓
+
+

联合国教科文组织合作项目

+

2021 年成为联合国教科文组织亚非地区青少年编程教育合作项目(AAYC)指定测评服务平台,获得国际认可。

+
+
+
+
👨‍🎓
+
+

全国超过 75 万考生参与

+

覆盖中国 34 个省级行政区、全球 25+ 国家及地区,3000+ 所学校和机构参与,新东方、编程猫、童程童美等知名品牌合作。

+
+
+
+
+ + +
+
+

考级证书的价值

+

不只是一张证书,更是孩子成长的里程碑

+
+
+
+
🏅
+

权威认证

+

中国软件行业协会盖章颁发,含金量高

+
+
+
📋
+

科技特长

+

可作为信息科技特长参考依据,综合素质评价加分

+
+
+
🧠
+

能力提升

+

锻炼逻辑思维、拆题能力与问题解决能力

+
+
+
🎯
+

阶段目标

+

清晰的等级体系,给孩子学习锚定方向与动力

+
+
+
+ + +
+
+

NCT 考试时间

+
2026 年 8 月 22 – 23 日
+

具体考试场次 6 月上旬公布

+
+
+ + +
+
+

集训时间安排

+

三期可选,内容一致,根据时间灵活选择

+
+
+ 第一期 +

7 月集训 · 第一期

+

2 周 · 周一至周六 · 每周日休息 · 每节课 2.5 小时

+
7 月 13 日 – 7 月 25 日
+
+ + +
+
+ +
+ 第二期 +

7–8 月集训 · 第二期

+

2 周 · 周一至周六 · 每周日休息 · 每节课 2.5 小时

+
7 月 27 日 – 8 月 8 日
+
+ + +
+
+ +
+ 第三期 +

8 月集训 · 第三期

+

2 周 · 周一至周六 · 每周日休息 · 每节课 2.5 小时

+
8 月 10 日 – 8 月 22 日
+
+ + +
+
+ +
+

💡 三期集训内容完全一致,家长可根据孩子时间安排自由选择。每周日休息。
填写孩子姓名后点击「我要报名」即可提交,老师会主动联系您确认。

+
+
+ + +
+
+

报名条件

+
+
+
+
📐
+
+

K2 阶段学员

+

报考 NCT 一级,夯实基础,建立信心

+
+
+
+
🔬
+
+

K4 阶段学员

+

报考 NCT 二级,挑战进阶,证明实力

+
+
+
+
+

📌 未考过一级的学生需先考一级,不可跳级。所有在读 Kitten 学员均可报名。

+
+
+ + +
+
+

费用说明

+
+
+
+
📝 NCT 官方报名费
+
300 元 / 人
+
+
+
🎒 集训费用
+
正常课时扣费
+
+
+
⏱️ 课时规则
+
每节扣 2 课时
+
+
+
+

🏆 奖学金激励:成绩优异的学员可获得校区颁发的奖学金,既是荣誉也是激励!

+
+
+ + +
+
+

常见问题

+
+
+
+ 孩子零基础可以参加吗? + +
+
+ 集训面向在读 Kitten 学员。K2 阶段报一级、K4 阶段报二级,难度与孩子当前学习进度匹配,老师全程指导,可以放心参加。 +
+
+
+
+ 考试形式是什么样的? + +
+
+ NCT 采用线上考试形式,支持居家和线下考点在线考试。AI 智能监考系统保证考试公正、专业,足不出户即可参加。 +
+
+
+
+ 集训会不会影响暑假出行? + +
+
+ 集训每期内容一样,选一期参加即可。每周日休息,三期覆盖整个暑假,合理安排完全不会影响家庭出行计划。 +
+
+
+
+ 通过率怎么样? + +
+
+ 参加集训的学员通过率基本达到 100%。集训内容紧扣考级知识点,系统刷题 + 模拟实操,帮助孩子充分准备。 +
+
+
+
+ 证书长什么样?有含金量吗? + +
+
+ 新版证书采用 PU 皮革材质、烫金凹印技术、圆角裁剪工艺,精美大气。由中国软件行业协会盖章颁发,通过顺丰快递寄送。该证书通过教育部 CELTSC 标准认证和国际 ISTE 认证,是目前国内外双认证的权威编程等级证书。 +
+
+
+ + +
+
+

🚀 暑假集训,助孩子高效拿证

+

小班精品教学 · 系统备考方案 · 权威等级证书
名额有限,每班仅限 8 人,先到先得!

+ 立即咨询报名 +
+
+ + + + + +
+ + + + + + + diff --git a/public/beijiao/js/app.js b/public/beijiao/js/app.js new file mode 100644 index 0000000..cb9f6f5 --- /dev/null +++ b/public/beijiao/js/app.js @@ -0,0 +1,126 @@ +// ============ 倒计时 ============ +function updateCountdown() { + var examDate = new Date('2026-08-22T09:00:00'); + var now = new Date(); + var diff = examDate - now; + if (diff <= 0) { + document.getElementById('cd-days').textContent = '0'; + document.getElementById('cd-hours').textContent = '0'; + document.getElementById('cd-mins').textContent = '0'; + document.getElementById('cd-secs').textContent = '0'; + return; + } + document.getElementById('cd-days').textContent = Math.floor(diff / 864e5); + document.getElementById('cd-hours').textContent = Math.floor((diff % 864e5) / 36e5); + document.getElementById('cd-mins').textContent = Math.floor((diff % 36e5) / 6e4); + document.getElementById('cd-secs').textContent = Math.floor((diff % 6e4) / 1e3); +} +updateCountdown(); +setInterval(updateCountdown, 1000); + +// ============ FAQ 折叠 ============ +document.querySelectorAll('.faq-q').forEach(function(q) { + q.addEventListener('click', function() { + this.parentElement.classList.toggle('open'); + }); +}); + +// ============ 滚动渐入 ============ +var observer = new IntersectionObserver(function(entries) { + entries.forEach(function(entry) { + if (entry.isIntersecting) entry.target.classList.add('visible'); + }); +}, { threshold: 0.1 }); +document.querySelectorAll('.fade-up').forEach(function(el) { + observer.observe(el); +}); + +// ============ Toast 提示 ============ +function showToast(msg, duration) { + duration = duration || 2500; + var el = document.getElementById('toast'); + el.textContent = msg; + el.classList.add('show'); + clearTimeout(el._timer); + el._timer = setTimeout(function() { + el.classList.remove('show'); + }, duration); +} + +// ============ 报名流程 ============ +var pendingPeriod = ''; + +function register(btn, period) { + var row = btn.closest('.reg-input-row'); + var name = row.querySelector('.reg-input').value.trim(); + if (!name) { + row.querySelector('.reg-input').focus(); + row.querySelector('.reg-input').style.borderColor = '#FF4444'; + setTimeout(function() { + row.querySelector('.reg-input').style.borderColor = ''; + }, 1500); + return; + } + pendingPeriod = period; + document.getElementById('modalPeriod').textContent = period; + document.getElementById('modalName').value = name; + document.getElementById('regModal').classList.add('show'); + document.getElementById('modalName').focus(); +} + +function closeModal() { + document.getElementById('regModal').classList.remove('show'); + pendingPeriod = ''; +} + +document.getElementById('regModal').addEventListener('click', function(e) { + if (e.target === this) closeModal(); +}); + +function submitReg() { + var name = document.getElementById('modalName').value.trim(); + var teacher = document.getElementById('modalTeacher').value; + var btn = document.getElementById('modalSubmit'); + + if (!name) { + document.getElementById('modalName').focus(); + return; + } + if (!teacher) { + document.getElementById('modalTeacher').style.borderColor = '#FF4444'; + setTimeout(function() { + document.getElementById('modalTeacher').style.borderColor = ''; + }, 1500); + return; + } + + btn.disabled = true; + btn.textContent = '提交中...'; + + fetch('api/register', { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: 'name=' + encodeURIComponent(name) + + '&period=' + encodeURIComponent(pendingPeriod) + + '&teacher=' + encodeURIComponent(teacher) + }) + .then(function(res) { return res.json(); }) + .then(function(data) { + closeModal(); + if (data.ok) { + showToast('✅ 报名成功!\n\n' + name + '\n' + pendingPeriod + '\n' + teacher, 3000); + document.querySelectorAll('.reg-input').forEach(function(el) { el.value = ''; }); + document.getElementById('modalTeacher').selectedIndex = 0; + document.getElementById('modalTeacher').classList.remove('has-value'); + } else { + showToast('❌ ' + (data.msg || '提交失败,请稍后再试')); + } + }) + .catch(function() { + showToast('❌ 网络错误,请稍后再试'); + }) + .finally(function() { + btn.disabled = false; + btn.textContent = '确认提交'; + }); +} diff --git a/public/chencun/admin.html b/public/chencun/admin.html new file mode 100644 index 0000000..c06704b --- /dev/null +++ b/public/chencun/admin.html @@ -0,0 +1,529 @@ + + + + + + 报名管理后台 | 陈村校区 + + + + + +
+

📋 报名管理后台

+

陈村校区 · NCT 集训报名数据管理

+
+ + +
+

🔐 管理员登录

+

请输入管理员密码查看报名数据

+ + + +
+ + +
+ + +
+
+
0
+
总报名人数
+
+
+
0
+
第一期
+
+
+
0
+
第二期
+
+
+
0
+
第三期
+
+
+ + +
+ + + +
+ + + +
+ + +
+
+ + + + + + + + + + + + + +
序号姓名期数老师报名时间操作
+ +
+
+
+ + + + + + + diff --git a/public/chencun/css/style.css b/public/chencun/css/style.css new file mode 100644 index 0000000..174e335 --- /dev/null +++ b/public/chencun/css/style.css @@ -0,0 +1,704 @@ +* { margin: 0; padding: 0; box-sizing: border-box; } + +:root { + --orange: #FF6B35; + --orange-light: #FF8F5E; + --orange-dark: #E85A24; + --blue: #4A6CF7; + --blue-light: #6B8CFF; + --purple: #7C5CFC; + --dark: #1A1A2E; + --text: #333; + --text-light: #666; + --bg: #F5F7FA; + --white: #fff; + --shadow: 0 4px 24px rgba(0,0,0,0.08); + --shadow-lg: 0 12px 40px rgba(0,0,0,0.12); +} + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif; + background: var(--bg); + color: var(--text); + line-height: 1.6; + -webkit-font-smoothing: antialiased; +} + +/* ====== Hero ====== */ +.hero { + background: linear-gradient(135deg, #FF6B35 0%, #FF8F5E 40%, #FFB347 100%); + color: white; + padding: 60px 24px 80px; + text-align: center; + position: relative; + overflow: hidden; +} + +.hero::before { + content: ''; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: radial-gradient(circle, rgba(255,255,255,0.08) 1px, transparent 1px); + background-size: 30px 30px; + animation: heroPattern 20s linear infinite; +} + +@keyframes heroPattern { + from { transform: translate(0, 0); } + to { transform: translate(30px, 30px); } +} + +.hero-content { + position: relative; + z-index: 2; + max-width: 700px; + margin: 0 auto; +} + +.hero-badge { + display: inline-block; + background: rgba(255,255,255,0.2); + backdrop-filter: blur(10px); + padding: 6px 20px; + border-radius: 20px; + font-size: 14px; + margin-bottom: 20px; + letter-spacing: 1px; +} + +.hero h1 { + font-size: 32px; + font-weight: 800; + margin-bottom: 12px; + text-shadow: 0 2px 10px rgba(0,0,0,0.1); +} + +.hero .subtitle { + font-size: 18px; + opacity: 0.95; + margin-bottom: 28px; +} + +.hero-stats { + display: flex; + justify-content: center; + gap: 32px; + flex-wrap: wrap; +} + +.hero-stat { + text-align: center; +} + +.hero-stat .num { + font-size: 36px; + font-weight: 800; +} + +.hero-stat .label { + font-size: 13px; + opacity: 0.85; + margin-top: 2px; +} + +/* ====== Sections ====== */ +.section { + max-width: 800px; + margin: 0 auto; + padding: 48px 20px; +} + +.section-header { + text-align: center; + margin-bottom: 36px; +} + +.section-header h2 { + font-size: 26px; + font-weight: 700; + color: var(--dark); + margin-bottom: 8px; +} + +.section-header p { + color: var(--text-light); + font-size: 15px; +} + +/* ====== Info Cards ====== */ +.info-grid { + display: grid; + grid-template-columns: 1fr; + gap: 16px; +} + +.info-card { + background: var(--white); + border-radius: 16px; + padding: 24px; + box-shadow: var(--shadow); + display: flex; + gap: 16px; + align-items: flex-start; + transition: transform 0.3s; +} + +.info-card:hover { + transform: translateY(-2px); +} + +.info-icon { + width: 52px; + height: 52px; + border-radius: 14px; + display: flex; + align-items: center; + justify-content: center; + font-size: 26px; + flex-shrink: 0; +} + +.info-icon.orange { background: #FFF0E8; } +.info-icon.blue { background: #EEF2FF; } +.info-icon.purple { background: #F3EEFF; } +.info-icon.green { background: #E8F9EF; } + +.info-card h3 { + font-size: 17px; + font-weight: 600; + color: var(--dark); + margin-bottom: 6px; +} + +.info-card p { + font-size: 14px; + color: var(--text-light); + line-height: 1.7; +} + +/* ====== Timeline ====== */ +.period-section { + background: var(--white); + border-radius: 16px; + padding: 24px; + box-shadow: var(--shadow); + margin-bottom: 24px; +} + +.period-badge { + display: inline-block; + font-size: 12px; + font-weight: 600; + padding: 3px 12px; + border-radius: 10px; + margin-bottom: 10px; +} + +.period-badge.orange { background: #FFF0E8; color: var(--orange); } +.period-badge.blue { background: #EEF2FF; color: var(--blue); } + +.period-section h3 { + font-size: 20px; + font-weight: 700; + color: var(--dark); + margin-bottom: 6px; +} + +.period-info { + font-size: 14px; + color: var(--text-light); + margin-bottom: 16px; + line-height: 1.7; +} + +/* ====== Registration Input ====== */ +.reg-input-row { + display: flex; + gap: 10px; + align-items: center; + margin-top: 12px; +} + +.reg-input { + flex: 1; + padding: 10px 14px; + border: 2px solid #e8e8e8; + border-radius: 10px; + font-size: 14px; + outline: none; + transition: border-color 0.3s; +} + +.reg-input:focus { + border-color: var(--orange); +} + +.reg-input::placeholder { + color: #bbb; +} + +.reg-btn { + padding: 10px 20px; + border: none; + border-radius: 10px; + font-size: 14px; + font-weight: 700; + color: white; + cursor: pointer; + white-space: nowrap; + transition: transform 0.2s, opacity 0.2s; +} + +.reg-btn:hover { transform: scale(1.03); } +.reg-btn:active { transform: scale(0.97); } +.reg-btn.orange { background: var(--orange); } +.reg-btn.blue { background: var(--blue); } + +/* ====== Exam Info ====== */ +.exam-card { + background: linear-gradient(135deg, var(--dark) 0%, #2D2D5E 100%); + color: white; + border-radius: 20px; + padding: 32px 28px; + text-align: center; + box-shadow: var(--shadow-lg); + position: relative; + overflow: hidden; +} + +.exam-card::after { + content: ''; + position: absolute; + top: -40%; + right: -20%; + width: 200px; + height: 200px; + background: radial-gradient(circle, rgba(255,107,53,0.15), transparent); + border-radius: 50%; +} + +.exam-card h3 { + font-size: 15px; + opacity: 0.7; + margin-bottom: 8px; + letter-spacing: 2px; + position: relative; + z-index: 2; +} + +.exam-date { + font-size: 38px; + font-weight: 800; + margin-bottom: 6px; + position: relative; + z-index: 2; + background: linear-gradient(135deg, #FF6B35, #FFB347); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.exam-note { + font-size: 14px; + opacity: 0.6; + position: relative; + z-index: 2; +} + +/* ====== Value Cards ====== */ +.value-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 14px; +} + +.value-card { + background: var(--white); + border-radius: 16px; + padding: 22px 18px; + text-align: center; + box-shadow: var(--shadow); + transition: transform 0.3s; +} + +.value-card:hover { + transform: translateY(-3px); +} + +.value-emoji { + font-size: 36px; + margin-bottom: 10px; +} + +.value-card h3 { + font-size: 16px; + font-weight: 600; + color: var(--dark); + margin-bottom: 6px; +} + +.value-card p { + font-size: 13px; + color: var(--text-light); + line-height: 1.6; +} + +/* ====== Highlights ====== */ +.highlight-strip { + background: linear-gradient(135deg, #FFF8E1, #FFF3E0); + border-radius: 16px; + padding: 20px 24px; + border-left: 5px solid #FFB347; + margin: 20px 0; +} + +.highlight-strip p { + font-size: 14px; + color: #8B6914; + line-height: 1.7; +} + +.highlight-strip strong { + color: #E85A24; +} + +/* ====== Fee Section ====== */ +.fee-table { + width: 100%; + background: var(--white); + border-radius: 16px; + overflow: hidden; + box-shadow: var(--shadow); +} + +.fee-row { + display: flex; + align-items: center; + padding: 18px 24px; + border-bottom: 1px solid #f0f0f0; +} + +.fee-row:last-child { + border-bottom: none; +} + +.fee-label { + flex: 1; + font-size: 15px; + color: var(--text); +} + +.fee-value { + font-size: 16px; + font-weight: 700; + color: var(--orange); +} + +/* ====== FAQ ====== */ +.faq-item { + background: var(--white); + border-radius: 14px; + margin-bottom: 12px; + box-shadow: 0 2px 12px rgba(0,0,0,0.05); + overflow: hidden; +} + +.faq-q { + padding: 18px 24px; + font-size: 15px; + font-weight: 600; + color: var(--dark); + cursor: pointer; + display: flex; + justify-content: space-between; + align-items: center; + user-select: none; + transition: background 0.2s; +} + +.faq-q:hover { + background: #f8f9fa; +} + +.faq-q .arrow { + font-size: 12px; + color: #999; + transition: transform 0.3s; +} + +.faq-item.open .faq-q .arrow { + transform: rotate(180deg); +} + +.faq-a { + display: none; + padding: 0 24px 18px; + font-size: 14px; + color: var(--text-light); + line-height: 1.8; +} + +.faq-item.open .faq-a { + display: block; +} + +/* ====== CTA ====== */ +.cta-section { + text-align: center; + padding: 48px 20px 24px; + max-width: 800px; + margin: 0 auto; +} + +.cta-box { + background: linear-gradient(135deg, #FF6B35, #FF8F5E, #FFB347); + border-radius: 20px; + padding: 40px 28px; + color: white; + box-shadow: 0 8px 32px rgba(255,107,53,0.3); +} + +.cta-box h2 { + font-size: 24px; + font-weight: 700; + margin-bottom: 12px; +} + +.cta-box p { + font-size: 15px; + opacity: 0.95; + margin-bottom: 24px; + line-height: 1.7; +} + +.cta-btn { + display: inline-block; + background: white; + color: var(--orange); + padding: 14px 40px; + border-radius: 30px; + font-size: 16px; + font-weight: 700; + text-decoration: none; + box-shadow: 0 4px 16px rgba(0,0,0,0.15); + transition: transform 0.3s, box-shadow 0.3s; +} + +.cta-btn:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(0,0,0,0.2); +} + +/* ====== Countdown ====== */ +.countdown-section { + background: var(--dark); + color: white; + padding: 36px 20px; + text-align: center; +} + +.countdown-title { + font-size: 14px; + opacity: 0.6; + letter-spacing: 3px; + margin-bottom: 16px; +} + +.countdown-boxes { + display: flex; + justify-content: center; + gap: 12px; +} + +.countdown-box { + background: rgba(255,255,255,0.08); + border-radius: 12px; + padding: 16px 20px; + min-width: 72px; +} + +.countdown-box .num { + font-size: 32px; + font-weight: 800; + color: var(--orange-light); +} + +.countdown-box .unit { + font-size: 12px; + opacity: 0.5; + margin-top: 2px; +} + +/* ====== Footer ====== */ +.footer { + text-align: center; + padding: 32px 20px; + color: #aaa; + font-size: 13px; +} + +/* ====== Modal ====== */ +.modal-overlay { + display: none; + position: fixed; + top: 0; left: 0; right: 0; bottom: 0; + background: rgba(0,0,0,0.4); + z-index: 1000; + justify-content: center; + align-items: center; +} + +.modal-overlay.show { + display: flex; +} + +.modal { + background: white; + border-radius: 20px; + padding: 32px; + width: 90%; + max-width: 380px; + text-align: center; + box-shadow: 0 20px 60px rgba(0,0,0,0.2); +} + +.modal h3 { + font-size: 20px; + margin-bottom: 6px; + color: var(--dark); +} + +.modal .modal-period { + font-size: 14px; + color: var(--orange); + font-weight: 600; + margin-bottom: 20px; +} + +.modal-input { + width: 100%; + padding: 12px 16px; + border: 2px solid #e8e8e8; + border-radius: 12px; + font-size: 16px; + outline: none; + transition: border-color 0.3s; + box-sizing: border-box; + text-align: center; +} + +.modal-input:focus { + border-color: var(--orange); +} + +.modal-select { + width: 100%; + padding: 12px 16px; + border: 2px solid #e8e8e8; + border-radius: 12px; + font-size: 16px; + outline: none; + background: white; + color: #999; + appearance: none; + -webkit-appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23999' stroke-width='1.5' fill='none'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 14px center; + cursor: pointer; + box-sizing: border-box; +} + +.modal-select:focus { + border-color: var(--orange); +} + +.modal-select.has-value { + color: #333; +} + +.modal-submit { + width: 100%; + padding: 14px; + background: var(--orange); + color: white; + border: none; + border-radius: 12px; + font-size: 16px; + font-weight: 700; + cursor: pointer; + margin-top: 14px; + transition: background 0.2s; +} + +.modal-submit:hover { background: var(--orange-dark); } +.modal-submit:disabled { background: #ccc; cursor: default; } + +.modal-cancel { + background: none; + border: none; + color: #999; + font-size: 14px; + margin-top: 12px; + cursor: pointer; +} + +.modal-cancel:hover { color: #666; } + +/* ====== Toast ====== */ +.toast { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(0.85); + background: rgba(0,0,0,0.82); + color: #fff; + padding: 28px 36px; + border-radius: 18px; + font-size: 15px; + line-height: 1.8; + text-align: center; + z-index: 2000; + opacity: 0; + transition: opacity 0.3s, transform 0.3s; + max-width: 340px; + box-shadow: 0 12px 40px rgba(0,0,0,0.25); + pointer-events: none; + white-space: pre-line; +} + +.toast.show { + opacity: 1; + transform: translate(-50%, -50%) scale(1); +} + +/* ====== Animations ====== */ +.fade-up { + opacity: 0; + transform: translateY(30px); + transition: opacity 0.6s ease, transform 0.6s ease; +} + +.fade-up.visible { + opacity: 1; + transform: translateY(0); +} + +/* ====== Mobile ====== */ +@media (max-width: 640px) { + .hero { padding: 48px 20px 64px; } + .hero h1 { font-size: 26px; } + .hero .subtitle { font-size: 15px; } + .hero-stats { gap: 20px; } + .hero-stat .num { font-size: 28px; } + .section { padding: 36px 16px; } + .section-header h2 { font-size: 22px; } + .value-grid { grid-template-columns: 1fr; } + .exam-date { font-size: 30px; } + .countdown-boxes { gap: 8px; } + .countdown-box { padding: 12px 16px; min-width: 60px; } + .countdown-box .num { font-size: 26px; } +} diff --git a/public/chencun/index.html b/public/chencun/index.html new file mode 100644 index 0000000..d3cd71b --- /dev/null +++ b/public/chencun/index.html @@ -0,0 +1,320 @@ + + + + + + 2026 暑假 Kitten 图形化编程等级考试集训 | 陈村校区 + + + + + +
+
+ 🎯 2026 暑假集训 +

Kitten 图形化编程
等级考试集训

+

NCT 青少年编程能力等级测试 · 陈村校区

+
+
+
100%
+
集训学员通过率
+
+
+
75W+
+
全国考生累计
+
+
+
8人
+
小班精品教学
+
+
+
+
+ + +
+
距离 NCT 考试还有
+
+
+
--
+
+
+
+
--
+
+
+
+
--
+
+
+
+
--
+
+
+
+
+ + +
+
+

什么是 NCT?

+

国内最权威的青少年编程能力等级测试

+
+
+
+
🏛️
+
+

中国软件行业协会主办

+

NCT 由中国软件行业协会盖章颁发等级证书,是国家民政部认可的首批 AAA 级行业协会,唯一代表中国计算机软件产业界的全国性一级行业组织。

+
+
+
+
🌐
+
+

国内国际双认证

+

通过教育部 CELTSC 标准符合性认证,同时通过国际教育技术协会 ISTE 认证,是国内首家获得 ISTE Seal of Alignment 的编程测评项目。

+
+
+
+
🎓
+
+

联合国教科文组织合作项目

+

2021 年成为联合国教科文组织亚非地区青少年编程教育合作项目(AAYC)指定测评服务平台,获得国际认可。

+
+
+
+
👨‍🎓
+
+

全国超过 75 万考生参与

+

覆盖中国 34 个省级行政区、全球 25+ 国家及地区,3000+ 所学校和机构参与,新东方、编程猫、童程童美等知名品牌合作。

+
+
+
+
+ + +
+
+

考级证书的价值

+

不只是一张证书,更是孩子成长的里程碑

+
+
+
+
🏅
+

权威认证

+

中国软件行业协会盖章颁发,含金量高

+
+
+
📋
+

科技特长

+

可作为信息科技特长参考依据,综合素质评价加分

+
+
+
🧠
+

能力提升

+

锻炼逻辑思维、拆题能力与问题解决能力

+
+
+
🎯
+

阶段目标

+

清晰的等级体系,给孩子学习锚定方向与动力

+
+
+
+ + +
+
+

NCT 考试时间

+
2026 年 8 月 22 – 23 日
+

具体考试场次 6 月上旬公布

+
+
+ + +
+
+

集训时间安排

+

三期可选,内容一致,根据时间灵活选择

+
+
+ 第一期 +

7 月集训 · 第一期

+

2 周 · 周一至周六 · 每周日休息 · 每节课 2.5 小时

+
7 月 13 日 – 7 月 25 日
+
+ + +
+
+ +
+ 第二期 +

7–8 月集训 · 第二期

+

2 周 · 周一至周六 · 每周日休息 · 每节课 2.5 小时

+
7 月 27 日 – 8 月 8 日
+
+ + +
+
+ +
+ 第三期 +

8 月集训 · 第三期

+

2 周 · 周一至周六 · 每周日休息 · 每节课 2.5 小时

+
8 月 10 日 – 8 月 22 日
+
+ + +
+
+ +
+

💡 三期集训内容完全一致,家长可根据孩子时间安排自由选择。每周日休息。
填写孩子姓名后点击「我要报名」即可提交,老师会主动联系您确认。

+
+
+ + +
+
+

报名条件

+
+
+
+
📐
+
+

K2 阶段学员

+

报考 NCT 一级,夯实基础,建立信心

+
+
+
+
🔬
+
+

K4 阶段学员

+

报考 NCT 二级,挑战进阶,证明实力

+
+
+
+
+

📌 未考过一级的学生需先考一级,不可跳级。所有在读 Kitten 学员均可报名。

+
+
+ + +
+
+

费用说明

+
+
+
+
📝 NCT 官方报名费
+
300 元 / 人
+
+
+
🎒 集训费用
+
正常课时扣费
+
+
+
⏱️ 课时规则
+
每节扣 2 课时
+
+
+
+

🏆 奖学金激励:成绩优异的学员可获得校区颁发的奖学金,既是荣誉也是激励!

+
+
+ + +
+
+

常见问题

+
+
+
+ 孩子零基础可以参加吗? + +
+
+ 集训面向在读 Kitten 学员。K2 阶段报一级、K4 阶段报二级,难度与孩子当前学习进度匹配,老师全程指导,可以放心参加。 +
+
+
+
+ 考试形式是什么样的? + +
+
+ NCT 采用线上考试形式,支持居家和线下考点在线考试。AI 智能监考系统保证考试公正、专业,足不出户即可参加。 +
+
+
+
+ 集训会不会影响暑假出行? + +
+
+ 集训每期内容一样,选一期参加即可。每周日休息,三期覆盖整个暑假,合理安排完全不会影响家庭出行计划。 +
+
+
+
+ 通过率怎么样? + +
+
+ 参加集训的学员通过率基本达到 100%。集训内容紧扣考级知识点,系统刷题 + 模拟实操,帮助孩子充分准备。 +
+
+
+
+ 证书长什么样?有含金量吗? + +
+
+ 新版证书采用 PU 皮革材质、烫金凹印技术、圆角裁剪工艺,精美大气。由中国软件行业协会盖章颁发,通过顺丰快递寄送。该证书通过教育部 CELTSC 标准认证和国际 ISTE 认证,是目前国内外双认证的权威编程等级证书。 +
+
+
+ + +
+
+

🚀 暑假集训,助孩子高效拿证

+

小班精品教学 · 系统备考方案 · 权威等级证书
名额有限,每班仅限 8 人,先到先得!

+ 立即咨询报名 +
+
+ + + + + +
+ + + + + + + diff --git a/public/chencun/js/app.js b/public/chencun/js/app.js new file mode 100644 index 0000000..cb9f6f5 --- /dev/null +++ b/public/chencun/js/app.js @@ -0,0 +1,126 @@ +// ============ 倒计时 ============ +function updateCountdown() { + var examDate = new Date('2026-08-22T09:00:00'); + var now = new Date(); + var diff = examDate - now; + if (diff <= 0) { + document.getElementById('cd-days').textContent = '0'; + document.getElementById('cd-hours').textContent = '0'; + document.getElementById('cd-mins').textContent = '0'; + document.getElementById('cd-secs').textContent = '0'; + return; + } + document.getElementById('cd-days').textContent = Math.floor(diff / 864e5); + document.getElementById('cd-hours').textContent = Math.floor((diff % 864e5) / 36e5); + document.getElementById('cd-mins').textContent = Math.floor((diff % 36e5) / 6e4); + document.getElementById('cd-secs').textContent = Math.floor((diff % 6e4) / 1e3); +} +updateCountdown(); +setInterval(updateCountdown, 1000); + +// ============ FAQ 折叠 ============ +document.querySelectorAll('.faq-q').forEach(function(q) { + q.addEventListener('click', function() { + this.parentElement.classList.toggle('open'); + }); +}); + +// ============ 滚动渐入 ============ +var observer = new IntersectionObserver(function(entries) { + entries.forEach(function(entry) { + if (entry.isIntersecting) entry.target.classList.add('visible'); + }); +}, { threshold: 0.1 }); +document.querySelectorAll('.fade-up').forEach(function(el) { + observer.observe(el); +}); + +// ============ Toast 提示 ============ +function showToast(msg, duration) { + duration = duration || 2500; + var el = document.getElementById('toast'); + el.textContent = msg; + el.classList.add('show'); + clearTimeout(el._timer); + el._timer = setTimeout(function() { + el.classList.remove('show'); + }, duration); +} + +// ============ 报名流程 ============ +var pendingPeriod = ''; + +function register(btn, period) { + var row = btn.closest('.reg-input-row'); + var name = row.querySelector('.reg-input').value.trim(); + if (!name) { + row.querySelector('.reg-input').focus(); + row.querySelector('.reg-input').style.borderColor = '#FF4444'; + setTimeout(function() { + row.querySelector('.reg-input').style.borderColor = ''; + }, 1500); + return; + } + pendingPeriod = period; + document.getElementById('modalPeriod').textContent = period; + document.getElementById('modalName').value = name; + document.getElementById('regModal').classList.add('show'); + document.getElementById('modalName').focus(); +} + +function closeModal() { + document.getElementById('regModal').classList.remove('show'); + pendingPeriod = ''; +} + +document.getElementById('regModal').addEventListener('click', function(e) { + if (e.target === this) closeModal(); +}); + +function submitReg() { + var name = document.getElementById('modalName').value.trim(); + var teacher = document.getElementById('modalTeacher').value; + var btn = document.getElementById('modalSubmit'); + + if (!name) { + document.getElementById('modalName').focus(); + return; + } + if (!teacher) { + document.getElementById('modalTeacher').style.borderColor = '#FF4444'; + setTimeout(function() { + document.getElementById('modalTeacher').style.borderColor = ''; + }, 1500); + return; + } + + btn.disabled = true; + btn.textContent = '提交中...'; + + fetch('api/register', { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: 'name=' + encodeURIComponent(name) + + '&period=' + encodeURIComponent(pendingPeriod) + + '&teacher=' + encodeURIComponent(teacher) + }) + .then(function(res) { return res.json(); }) + .then(function(data) { + closeModal(); + if (data.ok) { + showToast('✅ 报名成功!\n\n' + name + '\n' + pendingPeriod + '\n' + teacher, 3000); + document.querySelectorAll('.reg-input').forEach(function(el) { el.value = ''; }); + document.getElementById('modalTeacher').selectedIndex = 0; + document.getElementById('modalTeacher').classList.remove('has-value'); + } else { + showToast('❌ ' + (data.msg || '提交失败,请稍后再试')); + } + }) + .catch(function() { + showToast('❌ 网络错误,请稍后再试'); + }) + .finally(function() { + btn.disabled = false; + btn.textContent = '确认提交'; + }); +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..3032f46 --- /dev/null +++ b/server.js @@ -0,0 +1,143 @@ +const express = require('express'); +const path = require('path'); +const { createCampusDB } = require('./db'); + +const app = express(); +const PORT = 3000; + +// 中间件 +app.use(express.urlencoded({ extended: true })); +app.use(express.json()); + +// ============ 为每个校区创建独立路由 ============ + +function setupCampus(routePrefix, campusName, dirName) { + const router = express.Router(); + const campusDB = createCampusDB(dirName); + + // 静态文件 + router.use(express.static(path.join(__dirname, 'public', dirName))); + + // 报名接口 + router.post('/api/register', (req, res) => { + const { name, period, teacher } = req.body; + + if (!name || !period || !teacher) { + return res.json({ ok: false, msg: '请填写完整信息' }); + } + + try { + campusDB.insertRegistration(name.trim(), period, teacher); + console.log(`[报名·${campusName}] ${name} - ${period} - ${teacher}`); + res.json({ ok: true, msg: '报名成功' }); + } catch (err) { + console.error(`[报名错误·${campusName}]`, err); + res.json({ ok: false, msg: '服务器错误,请稍后再试' }); + } + }); + + // 获取报名列表(需密码) + router.get('/api/registrations', (req, res) => { + const password = req.query.password || req.headers['x-admin-password']; + if (!campusDB.verifyPassword(password)) { + return res.status(401).json({ ok: false, msg: '密码错误' }); + } + + try { + const registrations = campusDB.getAllRegistrations(); + res.json({ ok: true, data: registrations }); + } catch (err) { + console.error(`[查询错误·${campusName}]`, err); + res.status(500).json({ ok: false, msg: '服务器错误' }); + } + }); + + // 获取统计(需密码) + router.get('/api/stats', (req, res) => { + const password = req.query.password || req.headers['x-admin-password']; + if (!campusDB.verifyPassword(password)) { + return res.status(401).json({ ok: false, msg: '密码错误' }); + } + + try { + const stats = campusDB.getStats(); + res.json({ ok: true, data: stats }); + } catch (err) { + console.error(`[统计错误·${campusName}]`, err); + res.status(500).json({ ok: false, msg: '服务器错误' }); + } + }); + + // 删除报名记录(需密码) + router.delete('/api/registrations/:id', (req, res) => { + const password = req.query.password || req.headers['x-admin-password']; + if (!campusDB.verifyPassword(password)) { + return res.status(401).json({ ok: false, msg: '密码错误' }); + } + + try { + campusDB.deleteRegistration(parseInt(req.params.id)); + res.json({ ok: true, msg: '删除成功' }); + } catch (err) { + console.error(`[删除错误·${campusName}]`, err); + res.status(500).json({ ok: false, msg: '服务器错误' }); + } + }); + + app.use(routePrefix, router); + return campusDB; +} + +// ============ 注册校区 ============ + +const chencunDB = setupCampus('/chencunnct', '陈村校区', 'chencun'); +const beijiaoDB = setupCampus('/beijiaonct', '北滘校区', 'beijiao'); + +// 根路径跳转提示 +app.get('/', (req, res) => { + res.send(` + + + + 校区选择 + +
+

🏫 选择校区

+

请选择您所在的校区

+ +
+ `); +}); + +// ============ 启动服务器 ============ + +async function start() { + await chencunDB.init(); + await beijiaoDB.init(); + app.listen(PORT, () => { + console.log('\n🚀 服务器已启动: http://localhost:' + PORT); + console.log('🏫 陈村校区: http://localhost:' + PORT + '/chencunnct/'); + console.log('🏫 北滘校区: http://localhost:' + PORT + '/beijiaonct/'); + console.log('🔑 管理员密码: admin123\n'); + }); +} + +start();