Sun, 18 Apr 2021 22:00:56 GMT
parent
8477ee9fcf
commit
b575aa3c9e
@ -0,0 +1,9 @@
|
||||
{
|
||||
"presets": [
|
||||
"@parcel/babel-preset-env"
|
||||
],
|
||||
"plugins": [
|
||||
"@parcel/babel-plugin-transform-runtime",
|
||||
"@babel/plugin-transform-typescript"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,148 @@
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,node,vuejs
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,node,vuejs
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
.env*.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Storybook build outputs
|
||||
.out
|
||||
.storybook-out
|
||||
storybook-static
|
||||
|
||||
# rollup.js default build output
|
||||
dist/
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# Temporary folders
|
||||
tmp/
|
||||
temp/
|
||||
|
||||
### VisualStudioCode ###
|
||||
.vscode/*
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
*.code-workspace
|
||||
|
||||
### VisualStudioCode Patch ###
|
||||
# Ignore all local history of files
|
||||
.history
|
||||
.ionide
|
||||
|
||||
### Vuejs ###
|
||||
# Recommended template: Node.gitignore
|
||||
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,node,vuejs
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "cugoj-ng-client",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "parcel src/index.html",
|
||||
"build": "parcel build --no-scope-hoist --no-cache --no-source-maps --public-url . src/index.html --dist-dir release",
|
||||
"clean": "rimraf *cache dist release"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@babel/preset-typescript": "^7.13.0",
|
||||
"@parcel/babel-plugin-transform-runtime": "*",
|
||||
"bootstrap": "^5.0.0-beta3",
|
||||
"element-plus": "*",
|
||||
"mathjax": "^3.1.2",
|
||||
"monaco-editor": "^0.23.0",
|
||||
"vue": "^3.0.11",
|
||||
"vue-router": "^4.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@parcel/transformer-vue": "2.0.0-beta.2",
|
||||
"parcel": "*",
|
||||
"rimraf": "^3.0.2"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id='app'>
|
||||
<router-view />
|
||||
</div>
|
||||
<script src="index.ts" async></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -0,0 +1,14 @@
|
||||
import { createApp } from 'vue'
|
||||
import ElementPlus from 'element-plus';
|
||||
|
||||
import 'bootstrap/dist/css/bootstrap-grid.css'
|
||||
import 'bootstrap/dist/css/bootstrap-reboot.css'
|
||||
import 'bootstrap/dist/css/bootstrap-utilities.css'
|
||||
import 'element-plus/lib/theme-chalk/index.css';
|
||||
|
||||
import router from './router'
|
||||
|
||||
createApp({})
|
||||
.use(router)
|
||||
.use(ElementPlus)
|
||||
.mount('#app');
|
||||
@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<header>
|
||||
<el-menu :default-active="current" mode="horizontal" router class="d-flex">
|
||||
<el-menu-item index="/"> CUGOJ </el-menu-item>
|
||||
<el-menu-item index="/problemlist">题目列表 </el-menu-item>
|
||||
<div id="placeholder"></div>
|
||||
<el-menu-item index="/login"> {{ user_display }} </el-menu-item>
|
||||
</el-menu>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { currentUser } from "../views/user";
|
||||
export default {
|
||||
props: ["current"],
|
||||
data() {
|
||||
return {
|
||||
user_display: null,
|
||||
};
|
||||
},
|
||||
async created() {
|
||||
this.user_display = (await currentUser()) ?? "登录";
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
header {
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
#placeholder {
|
||||
flex: 1;
|
||||
visibility: hidden;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,36 @@
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
|
||||
export default createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
component: () => import('./views/Home.vue')
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
component: () => import('./views/Login.vue')
|
||||
},
|
||||
{
|
||||
path: '/problemlist',
|
||||
redirect: '/problemlist/1'
|
||||
},
|
||||
{
|
||||
path: '/problemlist/:page',
|
||||
component: () => import('./views/ProblemList.vue')
|
||||
},
|
||||
{
|
||||
path: '/problem/:pid',
|
||||
component: () => import('./views/Problem.vue')
|
||||
},
|
||||
{
|
||||
path: '/submit/:pid',
|
||||
component: () => import('./views/Submit.vue')
|
||||
},
|
||||
{
|
||||
path: '/submit/:cid/:pid',
|
||||
component: () => import('./views/Submit.vue')
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
/* eslint-disable */
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import "mathjax/es5/tex-svg-full";
|
||||
export function mathjax_load() {
|
||||
if (!window.MathJax.config.tex.inlineMath) {
|
||||
window.MathJax.config.tex.inlineMath = [
|
||||
["$", "$"],
|
||||
["\\(", "\\)"],
|
||||
];
|
||||
window.MathJax.startup.getComponents();
|
||||
}
|
||||
}
|
||||
export function mathjax_typeset() {
|
||||
window.MathJax.typeset();
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
interface Window {
|
||||
MonacoEnvironment: any;
|
||||
MathJax: any;
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<el-container direction="vertical">
|
||||
<navi :current="nav_path"></navi>
|
||||
<div class="container">
|
||||
<el-skeleton :rows="15" animated v-if="!ready" />
|
||||
<el-main v-else>
|
||||
<div v-if="loggedin">
|
||||
<h1>{{ user }}</h1>
|
||||
</div>
|
||||
<div v-else>
|
||||
<h1>You are not logged in</h1>
|
||||
</div>
|
||||
<div>
|
||||
<el-card
|
||||
v-for="(news, idx) in news_list"
|
||||
:key="idx"
|
||||
style="margin-bottom: 2rem"
|
||||
>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
{{ news.title }}
|
||||
<span style="font-size: 50%; margin-left: 1rem">
|
||||
- [{{ news.author }}]
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<div v-html="news.content"></div>
|
||||
</el-card>
|
||||
</div>
|
||||
</el-main>
|
||||
</div>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import navi from "../parts/nav.vue";
|
||||
export default {
|
||||
components: { navi },
|
||||
data() {
|
||||
return {
|
||||
nav_path: "/",
|
||||
ready: false,
|
||||
loggedin: false,
|
||||
user: null,
|
||||
news_list: [],
|
||||
};
|
||||
},
|
||||
async created() {
|
||||
document.title = "CUGOJ - Home";
|
||||
let news = fetch("/api/News/List");
|
||||
let res = await fetch("/api/User/WhoAmI").then((x) => x.json());
|
||||
if (res.user) {
|
||||
this.user = res.user;
|
||||
this.loggedin = true;
|
||||
}
|
||||
this.news_list = await news.then((x) => x.json());
|
||||
|
||||
this.ready = true;
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#lst {
|
||||
width: fit-content;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<navi :current="nav_path"></navi>
|
||||
<div class="container">
|
||||
<el-form label-width="80px" id="loginForm">
|
||||
<el-form-item label="用户名">
|
||||
<el-input v-model="user.name" name="username"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="密码">
|
||||
<el-input
|
||||
v-model="user.pass"
|
||||
name="password"
|
||||
type="password"
|
||||
show-password
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="login">登录</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import navi from "../parts/nav.vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
export default {
|
||||
components: { navi },
|
||||
data() {
|
||||
return {
|
||||
nav_path: "/login",
|
||||
user: {},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async login() {
|
||||
let loginForm = document.querySelector<HTMLFormElement>("#loginForm")!;
|
||||
let postData = new FormData(loginForm);
|
||||
let res = await fetch("/api/User/Login", {
|
||||
method: "POST",
|
||||
body: postData,
|
||||
});
|
||||
if (res.status == 200) {
|
||||
ElMessage({
|
||||
message: "登录成功",
|
||||
type: "success",
|
||||
duration: 2000,
|
||||
onClose: () => this.$router.back(),
|
||||
});
|
||||
} else {
|
||||
ElMessage.error(await res.text());
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<navi :current="nav_path"></navi>
|
||||
<div class="container">
|
||||
<el-pagination
|
||||
class="d-flex justify-content-center"
|
||||
layout="prev, pager, next"
|
||||
:page-count="totalPages"
|
||||
:current-page="curPage"
|
||||
@current-change="changePage"
|
||||
>
|
||||
</el-pagination>
|
||||
<el-table :data="problemList" stripe>
|
||||
<el-table-column
|
||||
prop="problem_id"
|
||||
label="题目编号"
|
||||
width="80"
|
||||
align="right"
|
||||
>
|
||||
</el-table-column>
|
||||
<el-table-column label="标题">
|
||||
<template #default="scope">
|
||||
<router-link
|
||||
custom
|
||||
:to="`/problem/${scope.row.problem_id}`"
|
||||
v-slot="{ href }"
|
||||
>
|
||||
<el-link type="primary" target="_blank" :href="href">
|
||||
{{ scope.row.title }}
|
||||
</el-link>
|
||||
</router-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="submit" label="提交" width="60"> </el-table-column>
|
||||
<el-table-column prop="accepted" label="通过" width="60">
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="m-4"></div>
|
||||
<el-pagination
|
||||
class="d-flex justify-content-center"
|
||||
layout="prev, pager, next"
|
||||
:page-count="totalPages"
|
||||
:current-page="curPage"
|
||||
@current-change="changePage"
|
||||
>
|
||||
</el-pagination>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import navi from "../parts/nav.vue";
|
||||
export default {
|
||||
components: { navi },
|
||||
data() {
|
||||
return {
|
||||
nav_path: "/problemlist",
|
||||
ready: false,
|
||||
problemList: [],
|
||||
totalPages: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
curPage() {
|
||||
return parseInt(this.$route.params.page as string);
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
document.title = "CUGOJ - ProblemList";
|
||||
},
|
||||
watch: {
|
||||
"$route.params": {
|
||||
handler({ page }) {
|
||||
page && this.fetchProblemList(parseInt(page));
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async fetchProblemList(page: number) {
|
||||
this.ready = false;
|
||||
let problemListRequest = fetch(`/api/Problem/List/${page}`);
|
||||
let res = await problemListRequest.then((x) => x.json());
|
||||
this.problemList = res.problemList;
|
||||
this.totalPages = res.totalPages;
|
||||
this.ready = true;
|
||||
},
|
||||
changePage(newPage: number) {
|
||||
this.$router.push({ params: { page: newPage } });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@ -0,0 +1,4 @@
|
||||
export async function currentUser() {
|
||||
let res = await fetch("/api/User/WhoAmI").then(x => x.json());
|
||||
return res.user;
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "ESNext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
|
||||
"module": "ESNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
|
||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
// "outDir": "./", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "composite": true, /* Enable project compilation */
|
||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
|
||||
/* Advanced Options */
|
||||
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue