├── .eslintrc.js
├── .gitignore
├── LICENSE
├── README.md
├── babel.config.js
├── import-resolver-webpack.conf.js
├── jsconfig.json
├── package-lock.json
├── package.json
├── public
├── index.html
├── robots.txt
└── static
│ └── icons
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ ├── apple-touch-icon-152x152.png
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon.ico
│ ├── manifest.json
│ ├── msapplication-icon-144x144.png
│ └── safari-pinned-tab.svg
├── src
├── App.vue
├── assets
│ ├── logo.png
│ └── social
│ │ ├── github.svg
│ │ ├── linkedin.svg
│ │ ├── logo.svg
│ │ ├── weibo.svg
│ │ └── zhihu.svg
├── components
│ ├── about
│ │ ├── about.vue
│ │ ├── card.vue
│ │ ├── contributor.vue
│ │ ├── copyright.vue
│ │ ├── dev.vue
│ │ ├── introduction.vue
│ │ └── why.vue
│ ├── article
│ │ ├── basic
│ │ │ ├── app.vue
│ │ │ ├── edit.vue
│ │ │ ├── toggle-article-vote.js
│ │ │ └── update-record.js
│ │ ├── detail
│ │ │ └── app.vue
│ │ ├── edit
│ │ │ ├── home_article_setting.vue
│ │ │ └── user_article_setting.vue
│ │ ├── home
│ │ │ ├── create.vue
│ │ │ ├── detail.vue
│ │ │ ├── edit.vue
│ │ │ └── tile.vue
│ │ ├── list
│ │ │ ├── app.vue
│ │ │ └── list.vue
│ │ └── user
│ │ │ ├── create.vue
│ │ │ ├── detail.vue
│ │ │ ├── edit.vue
│ │ │ └── tile.vue
│ ├── async
│ │ ├── code-mirror
│ │ │ ├── app.vue
│ │ │ ├── loading-wrapper.vue
│ │ │ └── settings.vue
│ │ └── mixrend
│ │ │ ├── app.vue
│ │ │ └── index.js
│ ├── basic
│ │ └── texteditor.vue
│ ├── chart
│ │ ├── bar.vue
│ │ ├── doughnut.vue
│ │ └── line.vue
│ ├── comments
│ │ ├── app.vue
│ │ ├── comment.vue
│ │ ├── comments.vue
│ │ ├── editor.vue
│ │ ├── reply-comment.vue
│ │ ├── toggle-reply-vote.js
│ │ └── update.vue
│ ├── contest
│ │ ├── detail
│ │ │ ├── app.vue
│ │ │ ├── clarification.vue
│ │ │ ├── problem.vue
│ │ │ ├── rank.vue
│ │ │ ├── submission.vue
│ │ │ ├── submit.vue
│ │ │ └── summary.vue
│ │ ├── edit
│ │ │ ├── create.vue
│ │ │ ├── preview.vue
│ │ │ └── update.vue
│ │ ├── list
│ │ │ ├── app.vue
│ │ │ └── list.vue
│ │ ├── review
│ │ │ ├── app.vue
│ │ │ ├── dialog.vue
│ │ │ ├── mine.vue
│ │ │ ├── overall.vue
│ │ │ ├── query-dialog.vue
│ │ │ ├── team-preview.vue
│ │ │ ├── team-tile.vue
│ │ │ └── update-team.vue
│ │ ├── submission
│ │ │ ├── detail.vue
│ │ │ └── summary.vue
│ │ └── utils.js
│ ├── footer
│ │ └── app.vue
│ ├── global
│ │ └── 404.vue
│ ├── home
│ │ ├── announcement.vue
│ │ ├── blog.vue
│ │ ├── curtain.vue
│ │ ├── develop.vue
│ │ ├── guide.vue
│ │ ├── home.vue
│ │ ├── honor.vue
│ │ └── recently.vue
│ ├── language
│ │ └── utils
│ │ │ └── select.vue
│ ├── navigation
│ │ ├── app.vue
│ │ ├── drawer.vue
│ │ ├── toolbar.vue
│ │ ├── user-menu-mobile.vue
│ │ └── user-menu.vue
│ ├── problem
│ │ ├── create
│ │ │ └── app.vue
│ │ ├── detail
│ │ │ ├── app.vue
│ │ │ ├── description.vue
│ │ │ ├── editor.vue
│ │ │ └── preview.vue
│ │ ├── edit
│ │ │ ├── app.vue
│ │ │ └── setting.vue
│ │ ├── list
│ │ │ ├── app.vue
│ │ │ └── list.vue
│ │ └── utils
│ │ │ ├── auto-complete.vue
│ │ │ ├── fetch.js
│ │ │ └── preview.vue
│ ├── signin
│ │ ├── login.vue
│ │ ├── signout.vue
│ │ └── signup.vue
│ ├── snackbar
│ │ └── app.vue
│ ├── status
│ │ ├── detail
│ │ │ ├── app.vue
│ │ │ ├── code.vue
│ │ │ ├── connection.js
│ │ │ ├── progress.vue
│ │ │ └── summary.vue
│ │ └── list
│ │ │ ├── app.vue
│ │ │ └── list.vue
│ ├── user
│ │ ├── detail
│ │ │ ├── app.vue
│ │ │ ├── layout.vue
│ │ │ ├── profile.vue
│ │ │ ├── solved.vue
│ │ │ └── summary.vue
│ │ ├── list
│ │ │ ├── app.vue
│ │ │ └── list.vue
│ │ ├── settings
│ │ │ └── app.vue
│ │ └── utils
│ │ │ └── auto-complete.vue
│ ├── utils
│ │ ├── code-mirror.vue
│ │ ├── error-spinner.vue
│ │ ├── loading-spinner.vue
│ │ ├── random-color.js
│ │ └── search-bar.vue
│ └── verdict
│ │ └── utils
│ │ └── select.vue
├── docs
│ ├── develop.md
│ └── honor.md
├── graphql
│ ├── article
│ │ ├── create.gql
│ │ ├── detail.gql
│ │ ├── discussion.gql
│ │ └── list.gql
│ ├── home
│ │ ├── home-article-list.gql
│ │ ├── topuser.gql
│ │ └── user-article-list.gql
│ ├── language
│ │ └── languagelist.gql
│ ├── problem
│ │ ├── detail.gql
│ │ ├── edit-detail.gql
│ │ ├── edit.gql
│ │ ├── list.gql
│ │ └── search.gql
│ ├── signin
│ │ ├── register.gql
│ │ └── token.gql
│ ├── submission
│ │ ├── list.gql
│ │ └── submit.gql
│ ├── user
│ │ ├── list.gql
│ │ ├── profile.gql
│ │ ├── search.gql
│ │ └── settings.gql
│ ├── utils
│ │ └── uploadimage.gql
│ └── votediscussion
│ │ └── vote.gql
├── main.js
├── modules
│ ├── code-mirror
│ │ ├── keymap.js
│ │ └── theme.js
│ ├── language
│ │ └── main.js
│ └── verdict
│ │ └── main.js
├── npm-debug.log
├── plugins
│ ├── essential
│ │ ├── apollo-provider.js
│ │ ├── auth.js
│ │ ├── filters.js
│ │ ├── global-components.js
│ │ ├── graphql-tag.js
│ │ ├── router.js
│ │ ├── store.js
│ │ ├── vue-line-clamp.js
│ │ ├── vue-moment.js
│ │ ├── vue.js
│ │ └── vuetify.js
│ ├── external
│ │ ├── code-mirror.js
│ │ ├── markdown-it-katex.js
│ │ └── markdown-it-vue.js
│ ├── index.js
│ └── npm-debug.log
├── registerServiceWorker.js
├── router
│ ├── about
│ │ └── router.js
│ ├── article
│ │ └── router.js
│ ├── contest
│ │ └── router.js
│ ├── home
│ │ └── router.js
│ ├── index.js
│ ├── problem
│ │ └── router.js
│ ├── sign
│ │ └── router.js
│ ├── status
│ │ └── router.js
│ ├── user
│ │ └── router.js
│ └── utils.js
├── store
│ ├── actions.js
│ ├── index.js
│ ├── modules
│ │ ├── editor.js
│ │ ├── footer.js
│ │ ├── navbar.js
│ │ ├── snackbar.js
│ │ └── user.js
│ └── mutations.js
├── stylus
│ └── main.styl
└── utils.js
└── vue.config.js
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parserOptions: {
4 | parser: 'babel-eslint',
5 | },
6 | env: {
7 | browser: true,
8 | },
9 | // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
10 | // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended`
11 | // for stricter rules.
12 | extends: [
13 | 'plugin:vue/recommended',
14 | '@vue/airbnb',
15 | ],
16 | plugins: [
17 | 'graphql',
18 | ],
19 | // check if imports actually resolve
20 | settings: {
21 | 'import/resolver': {
22 | webpack: {
23 | config: 'import-resolver-webpack.conf.js',
24 | },
25 | },
26 | },
27 | // add your custom rules here
28 | rules: {
29 | // don't require .vue extension when importing
30 | 'import/extensions': ['error', 'always', {
31 | js: 'never',
32 | mjs: 'never',
33 | vue: 'never',
34 | }],
35 | // disallow reassignment of function parameters
36 | // disallow parameter object manipulation except for specific exclusions
37 | 'no-param-reassign': ['error', {
38 | props: true,
39 | ignorePropertyModificationsFor: [
40 | 'state', // for vuex state
41 | 'acc', // for reduce accumulators
42 | 'e', // for e.returnvalue
43 | ],
44 | }],
45 | // allow optionalDependencies
46 | 'import/no-extraneous-dependencies': ['error', {
47 | optionalDependencies: ['test/unit/index.js'],
48 | }],
49 | // allow debugger during development
50 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
51 | 'vue/html-indent': [
52 | 'error',
53 | 'tab',
54 | ],
55 | indent: [
56 | 'error',
57 | 'tab',
58 | ],
59 | 'no-tabs': 0,
60 | 'no-unused-vars': [
61 | 'error',
62 | { argsIgnorePattern: '^_' },
63 | ],
64 | },
65 | };
66 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # ignore dist file
2 | dist/
3 |
4 | # ignore node_modules
5 | node_modules/
6 |
7 | # ignore vscode setting file
8 | .vscode/
9 |
10 | # ignore idea setting file
11 | .idea/
12 |
13 | *nohup.out
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # lutece-frontend
2 |
3 | ## Project setup
4 | ```
5 | npm install
6 | ```
7 |
8 | ### Compiles and hot-reloads for development
9 | ```
10 | npm run serve
11 | ```
12 |
13 | ### Compiles and minifies for production
14 | ```
15 | npm run build
16 | ```
17 |
18 | ### Lints and fixes files
19 | ```
20 | npm run lint
21 | ```
22 |
23 | ## Advanced
24 |
25 | You can run scripts with additional features using the GUI.
26 |
27 | ### Install Vue CLI
28 | ```
29 | npm install -g @vue/cli
30 | ```
31 |
32 | ### Run Vue CLI GUI
33 | ```
34 | vue ui
35 | ```
36 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | /* eslint no-template-curly-in-string: "off" */
2 | module.exports = {
3 | presets: [
4 | [
5 | '@vue/app',
6 | {
7 | useBuiltIns: 'entry',
8 | },
9 | ],
10 | ],
11 | plugins: [
12 | [
13 | 'transform-imports',
14 | {
15 | vuetify: {
16 | transform: 'vuetify/es5/components/${member}',
17 | preventFullImport: true,
18 | },
19 | },
20 | ],
21 | ],
22 | };
23 |
--------------------------------------------------------------------------------
/import-resolver-webpack.conf.js:
--------------------------------------------------------------------------------
1 | require('babel-polyfill');
2 |
3 | const path = require('path');
4 |
5 | module.exports = {
6 | entry: ['babel-polyfill', './src/main.js'],
7 |
8 | resolve: {
9 | extensions: ['.js', '.vue', '.json'],
10 | alias: {
11 | vue$: 'vue/dist/vue.runtime.esm.js',
12 | '@': path.resolve(__dirname, 'src'),
13 | },
14 | },
15 |
16 | module: {
17 | rules: [
18 | {
19 | test: /\.styl(us)?$/,
20 | use: ['vue-style-loader', 'css-loader', 'stylus-loader'],
21 | },
22 | {
23 | test: /.md$/,
24 | use: 'text-loader',
25 | },
26 | ],
27 | },
28 | };
29 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6",
4 | "baseUrl": ".",
5 | "paths": {
6 | "@/*": [
7 | "src/*"
8 | ]
9 | },
10 | "sourceMap": true
11 | },
12 | "include": [
13 | "./src/**/*"
14 | ],
15 | "exclude": [
16 | "node_modules",
17 | "dist"
18 | ]
19 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lutece-frontend",
3 | "version": "0.3.1",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "@babel/polyfill": "^7.4.0",
12 | "@mdi/font": "^3.5.95",
13 | "apollo-cache-inmemory": "^1.5.1",
14 | "apollo-client": "^2.5.1",
15 | "apollo-link-context": "^1.0.17",
16 | "apollo-upload-client": "^10.0.0",
17 | "babel-polyfill": "^6.26.0",
18 | "chart.js": "^2.8.0",
19 | "codemirror": "^5.45.0",
20 | "katex": "^0.10.1",
21 | "lodash.debounce": "^4.0.8",
22 | "markdown-it": "^8.4.2",
23 | "markdown-it-texmath": "^0.5.5",
24 | "register-service-worker": "^1.6.2",
25 | "unfetch": "^4.1.0",
26 | "vue": "^2.6.10",
27 | "vue-apollo": "^3.0.0-beta.28",
28 | "vue-chartjs": "^3.4.2",
29 | "vue-codemirror": "^4.0.6",
30 | "vue-line-clamp": "^1.3.2",
31 | "vue-meta": "^1.5.8",
32 | "vue-moment": "^4.1.0",
33 | "vue-router": "^3.0.2",
34 | "vuetify": "^1.5.7",
35 | "vuetify-datetime-picker": "^1.0.13",
36 | "vuex": "^3.1.0"
37 | },
38 | "devDependencies": {
39 | "@vue/cli-plugin-babel": "^3.5.0",
40 | "@vue/cli-plugin-eslint": "^3.5.0",
41 | "@vue/cli-plugin-pwa": "^3.5.0",
42 | "@vue/cli-service": "^3.5.3",
43 | "@vue/eslint-config-airbnb": "^4.0.0",
44 | "babel-plugin-transform-imports": "^1.5.1",
45 | "eslint-plugin-graphql": "^3.0.3",
46 | "fontmin-webpack": "^2.0.3",
47 | "glob-all": "^3.1.0",
48 | "graphql-tag": "^2.10.1",
49 | "lint-staged": "^8.1.5",
50 | "lodash": "^4.17.11",
51 | "purgecss-webpack-plugin": "^1.4.0",
52 | "raw-loader": "^1.0.0",
53 | "stylus": "^0.54.5",
54 | "stylus-loader": "^3.0.2",
55 | "vue-cli-plugin-apollo": "^0.19.1",
56 | "vue-cli-plugin-vuetify": "^0.4.6",
57 | "vue-cli-plugin-webpack-bundle-analyzer": "^1.2.0",
58 | "vue-template-compiler": "^2.6.10",
59 | "webpack-cli": "^3.3.0"
60 | },
61 | "gitHooks": {
62 | "pre-commit": "lint-staged"
63 | },
64 | "lint-staged": {
65 | "*.js": [
66 | "vue-cli-service lint",
67 | "git add"
68 | ],
69 | "*.vue": [
70 | "vue-cli-service lint",
71 | "git add"
72 | ]
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Lutece
10 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/public/static/icons/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lutece-awesome/lutece-frontend/27126931178a87e2a73e209b476b0c96517fc09e/public/static/icons/android-chrome-192x192.png
--------------------------------------------------------------------------------
/public/static/icons/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lutece-awesome/lutece-frontend/27126931178a87e2a73e209b476b0c96517fc09e/public/static/icons/android-chrome-512x512.png
--------------------------------------------------------------------------------
/public/static/icons/apple-touch-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lutece-awesome/lutece-frontend/27126931178a87e2a73e209b476b0c96517fc09e/public/static/icons/apple-touch-icon-152x152.png
--------------------------------------------------------------------------------
/public/static/icons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lutece-awesome/lutece-frontend/27126931178a87e2a73e209b476b0c96517fc09e/public/static/icons/favicon-16x16.png
--------------------------------------------------------------------------------
/public/static/icons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lutece-awesome/lutece-frontend/27126931178a87e2a73e209b476b0c96517fc09e/public/static/icons/favicon-32x32.png
--------------------------------------------------------------------------------
/public/static/icons/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lutece-awesome/lutece-frontend/27126931178a87e2a73e209b476b0c96517fc09e/public/static/icons/favicon.ico
--------------------------------------------------------------------------------
/public/static/icons/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Lutece",
3 | "short_name": "Lutece",
4 | "icons": [
5 | {
6 | "src": "/static/icons/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/static/icons/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "start_url": "/home",
17 | "display": "standalone",
18 | "background_color": "#FFFFFF",
19 | "theme_color": "#1E88E5"
20 | }
21 |
--------------------------------------------------------------------------------
/public/static/icons/msapplication-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lutece-awesome/lutece-frontend/27126931178a87e2a73e209b476b0c96517fc09e/public/static/icons/msapplication-icon-144x144.png
--------------------------------------------------------------------------------
/public/static/icons/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
37 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
13 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
46 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lutece-awesome/lutece-frontend/27126931178a87e2a73e209b476b0c96517fc09e/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/social/github.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/social/linkedin.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/social/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/social/weibo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/social/zhihu.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/about/about.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
46 |
--------------------------------------------------------------------------------
/src/components/about/card.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
14 |
19 |
20 |
21 |
22 |
23 |
27 |
28 |
29 | mdi-emoticon-cool |
30 | {{ user.name }} |
31 |
32 |
33 | mdi-octagram |
34 | {{ user.company }} |
35 |
36 |
37 | mdi-lighthouse-on |
38 | {{ user.title }} |
39 |
40 |
41 | mdi-map-marker |
42 | {{ user.location }} |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
62 |
--------------------------------------------------------------------------------
/src/components/about/contributor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
11 |
12 | Contributor
13 |
14 |
18 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
64 |
--------------------------------------------------------------------------------
/src/components/about/copyright.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
11 |
12 | Copyright
13 |
14 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/src/components/about/dev.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
11 |
12 | Dev Team
13 |
14 |
18 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
63 |
--------------------------------------------------------------------------------
/src/components/about/introduction.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
11 |
12 | Introduction
13 |
14 | Lutece(卢特斯)是一个基于GNU General Public License Version 3协议开源的Online Judge.
15 | Lutece的名字取自BioShock Infinite
16 | 中的双胞胎科学家(事实上她们是同一个人)Robert Lutece和Rosalind Lutece的姓氏.
17 | Lutece不仅将支持传统算法,还将计划支持并行计算和机器学习.
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
32 |
--------------------------------------------------------------------------------
/src/components/about/why.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
13 |
14 | Why Lutece?
15 |
16 |
20 |
26 |
27 |
28 |
29 |
30 |
34 | {{ each.icon }}
35 |
36 |
37 |
41 |
{{ each.title }}
42 |
43 |
44 | {{ each.text }}
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
79 |
--------------------------------------------------------------------------------
/src/components/article/basic/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
13 | Submit
19 |
20 |
23 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
68 |
--------------------------------------------------------------------------------
/src/components/article/basic/edit.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
12 |
13 |
14 |
15 |
28 |
--------------------------------------------------------------------------------
/src/components/article/basic/toggle-article-vote.js:
--------------------------------------------------------------------------------
1 | import ApolloProvider from '@/plugins/essential/apollo-provider';
2 | import gql from '@/plugins/essential/graphql-tag';
3 |
4 | const mutation = gql`
5 | mutation ToggleArticleVote($pk: ID!){
6 | toggleArticleVote(pk:$pk){
7 | state
8 | }
9 | }
10 | `;
11 |
12 | const toggleArticleVote = pk => ApolloProvider.defaultClient.mutate({
13 | mutation,
14 | variables: { pk },
15 | });
16 |
17 | export default toggleArticleVote;
18 |
--------------------------------------------------------------------------------
/src/components/article/basic/update-record.js:
--------------------------------------------------------------------------------
1 | import ApolloProvider from '@/plugins/essential/apollo-provider';
2 | import gql from '@/plugins/essential/graphql-tag';
3 |
4 | const mutate = gql`
5 | mutation UpdateArticleRecord($pk: ID!){
6 | updateArticleRecord(pk:$pk){
7 | state
8 | }
9 | }
10 | `;
11 |
12 | const updateArticleRecord = pk => ApolloProvider.defaultClient.mutate({
13 | mutation: mutate,
14 | variables: { pk },
15 | });
16 |
17 | export default updateArticleRecord;
18 |
--------------------------------------------------------------------------------
/src/components/article/home/create.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
12 |
17 |
29 |
30 |
31 |
32 |
33 |
34 |
85 |
--------------------------------------------------------------------------------
/src/components/article/home/tile.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 | {{ title }}
12 |
13 |
14 |
15 |
16 |
20 | {{ preview }}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
36 | {{ username }}
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | {{ getThoundNumberic(count) }} views
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
96 |
97 |
102 |
--------------------------------------------------------------------------------
/src/components/article/list/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
11 |
15 |
17 |
18 |
19 |
23 |
24 |
25 |
28 |
32 |
33 |
42 | mdi-pencil
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
77 |
--------------------------------------------------------------------------------
/src/components/article/list/list.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
15 |
16 |
19 |
20 |
27 |
28 |
29 |
30 |
31 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
82 |
83 |
104 |
--------------------------------------------------------------------------------
/src/components/article/user/create.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
12 |
17 |
25 |
26 |
27 |
28 |
29 |
30 |
76 |
--------------------------------------------------------------------------------
/src/components/article/user/tile.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 | {{ title }}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
25 |
26 |
27 | {{ username }}
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | {{ vote }} stars
38 |
44 | mdi-thumb-up-outline
45 |
46 |
47 |
48 | |
49 |
50 | {{ getThoundNumberic(count) }} views
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
101 |
102 |
107 |
--------------------------------------------------------------------------------
/src/components/async/code-mirror/loading-wrapper.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/src/components/async/code-mirror/settings.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 | Theme
11 |
21 |
25 |
26 | {{ item.name }}
27 |
28 |
29 |
30 |
31 |
32 | Keymap
33 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
93 |
--------------------------------------------------------------------------------
/src/components/async/mixrend/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
36 |
37 |
45 |
--------------------------------------------------------------------------------
/src/components/async/mixrend/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/prefer-default-export */
2 |
3 | import LoadingSpinner from '@/components/utils/loading-spinner';
4 |
5 | const AsyncMixrendComponent = () => ({
6 | component: import('./app'),
7 | loading: null,
8 | error: null,
9 | });
10 |
11 | const AsyncMixrendComponentWithLoadingSpinner = () => ({
12 | component: import('./app'),
13 | loading: LoadingSpinner,
14 | error: null,
15 | delay: 0,
16 | });
17 |
18 | export { AsyncMixrendComponent, AsyncMixrendComponentWithLoadingSpinner };
19 |
--------------------------------------------------------------------------------
/src/components/chart/bar.vue:
--------------------------------------------------------------------------------
1 |
21 |
--------------------------------------------------------------------------------
/src/components/chart/doughnut.vue:
--------------------------------------------------------------------------------
1 |
2 |
22 |
--------------------------------------------------------------------------------
/src/components/chart/line.vue:
--------------------------------------------------------------------------------
1 |
21 |
--------------------------------------------------------------------------------
/src/components/comments/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | mdi-comment
8 | Comments:
9 |
10 |
14 |
15 |
16 |
23 |
24 |
25 |
26 |
27 |
28 |
78 |
--------------------------------------------------------------------------------
/src/components/comments/comments.vue:
--------------------------------------------------------------------------------
1 |
2 |
23 |
24 |
25 |
86 |
--------------------------------------------------------------------------------
/src/components/comments/editor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
24 |
31 |
37 | Submit
38 |
39 |
40 |
41 |
42 |
43 |
44 |
99 |
--------------------------------------------------------------------------------
/src/components/comments/toggle-reply-vote.js:
--------------------------------------------------------------------------------
1 | import ApolloProvider from '@/plugins/essential/apollo-provider';
2 | import gql from '@/plugins/essential/graphql-tag';
3 |
4 | const mutation = gql`
5 | mutation ToggleReplyVote($pk: ID!){
6 | toggleReplyVote(pk:$pk){
7 | state
8 | }
9 | }
10 | `;
11 |
12 | const toggleReplyVote = pk => ApolloProvider.defaultClient.mutate({
13 | mutation,
14 | variables: { pk },
15 | });
16 |
17 | export default toggleReplyVote;
18 |
--------------------------------------------------------------------------------
/src/components/comments/update.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
18 |
21 |
22 | Edit Comment
23 |
24 |
25 |
26 |
31 |
38 |
39 |
40 |
41 |
45 |
52 |
58 | Submit
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
138 |
--------------------------------------------------------------------------------
/src/components/contest/detail/clarification.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
14 |
15 |
16 |
17 |
107 |
--------------------------------------------------------------------------------
/src/components/contest/detail/problem.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
16 |
17 |
18 |
22 |
23 | {{ getIcon(each) }}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
34 |
40 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
119 |
120 |
130 |
--------------------------------------------------------------------------------
/src/components/contest/detail/submit.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
25 |
26 |
27 |
28 |
29 |
110 |
--------------------------------------------------------------------------------
/src/components/contest/list/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
15 |
20 |
21 |
25 |
26 |
27 |
32 |
33 |
34 |
35 |
47 | mdi-pencil
48 |
49 |
50 |
51 |
52 |
53 |
139 |
--------------------------------------------------------------------------------
/src/components/contest/list/list.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
13 |
16 |
21 | {{ props.item.pk }} |
22 |
23 | {{ props.item.title }}
24 | |
25 |
26 | {{ props.item.settings.startTime | moment('Y-MM-DD HH:mm') }}
27 | |
28 |
29 | {{ getTimeLength(props.item.settings.startTime, props.item.settings.endTime) }}
30 | |
31 |
32 |
33 | {{ getRunningStatus( props.item.settings.startTime , props.item.settings.endTime ) }}
34 |
35 | |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
108 |
--------------------------------------------------------------------------------
/src/components/contest/review/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
12 |
17 |
20 |
26 |
30 | Overall
31 |
32 |
37 | Mine
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
69 |
--------------------------------------------------------------------------------
/src/components/contest/review/dialog.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
15 | {{ btnText }}
16 |
17 |
18 |
19 |
23 | {{ title }}
24 |
25 |
26 | {{ text }}
27 |
28 |
29 |
30 |
31 |
36 | OK
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
85 |
--------------------------------------------------------------------------------
/src/components/contest/review/team-tile.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 | {{ team.name }}
12 |
13 |
14 |
15 |
19 |
24 |
27 |
28 |
31 |
32 |
33 | {{ each.user.username }}
34 |
35 |
39 | {{ each.confirmed ? 'mdi-check' : 'mdi-help' }}
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
56 | Join
63 |
64 |
65 |
66 |
67 |
68 |
116 |
--------------------------------------------------------------------------------
/src/components/contest/review/update-team.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
21 |
22 |
23 |
24 |
106 |
--------------------------------------------------------------------------------
/src/components/contest/submission/summary.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 | {{ result.full }}
10 |
11 |
12 |
13 |
19 |
20 |
21 |
22 |
23 |
24 | Problem:
25 |
26 | |
27 |
28 |
29 | {{ problemTitle }}
30 |
31 | |
32 |
33 |
34 |
35 |
36 | User:
37 |
38 | |
39 |
40 |
48 | {{ username }}
49 |
50 | |
51 |
52 |
53 |
54 |
55 | Time:
56 |
57 | |
58 |
59 |
60 | {{ submitTime }}
61 |
62 | |
63 |
64 |
65 |
66 |
67 | Lang:
68 |
69 | |
70 |
72 |
73 | {{ language.full }}
74 |
75 | |
76 |
77 |
78 |
79 |
80 | Case:
81 |
82 | |
83 |
85 |
86 | {{ caseList.length }} / {{ caseNumber }}
87 |
88 | |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
144 |
--------------------------------------------------------------------------------
/src/components/contest/utils.js:
--------------------------------------------------------------------------------
1 | import Vue from '@/plugins/essential/vue';
2 |
3 | const getRunningStatus = (st, ed) => {
4 | const cur = Vue.moment();
5 | const stTime = Vue.moment(st);
6 | const edTime = Vue.moment(ed);
7 | if (cur < stTime) {
8 | return 'Pending';
9 | }
10 | if (cur < edTime) {
11 | return 'Running';
12 | }
13 | if (cur >= edTime) {
14 | return 'Finished';
15 | }
16 | return 'Unknown';
17 | };
18 |
19 | const getMinutesBetweenTwo = (st, ed) => Math.round((new Date(ed) - new Date(st)) / 1000 / 60);
20 |
21 | const getTimeLength = (st, ed) => {
22 | const len = getMinutesBetweenTwo(st, ed);
23 | const hour = String(Math.round(len / 60));
24 | let minute = String(len % 60);
25 | if (minute.length === 1) {
26 | minute = `0${minute}`;
27 | }
28 | return `${hour}:${minute}`;
29 | };
30 |
31 |
32 | export { getRunningStatus, getTimeLength, getMinutesBetweenTwo };
33 |
--------------------------------------------------------------------------------
/src/components/footer/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
18 |
28 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
60 |
--------------------------------------------------------------------------------
/src/components/global/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/src/components/home/announcement.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
15 |
21 |
26 |
28 |
33 |
36 |
39 |
43 |
44 |
55 |
56 |
57 |
61 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
84 | mdi-pencil
85 |
86 |
87 |
88 |
89 |
128 |
--------------------------------------------------------------------------------
/src/components/home/blog.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
20 |
25 |
27 |
32 |
35 |
38 |
42 |
43 |
54 |
55 |
56 |
60 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
82 | mdi-pencil
83 |
84 |
85 |
86 |
87 |
126 |
--------------------------------------------------------------------------------
/src/components/home/curtain.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
17 |
18 |
19 |
20 |
24 |
25 | - Develop better Online Judge
26 |
27 |
28 | - Learn computer science
29 |
30 |
31 | - Make programming contest easy
32 |
33 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
104 |
105 |
116 |
--------------------------------------------------------------------------------
/src/components/home/develop.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
15 |
16 | {{ title }}
17 |
18 |
23 |
24 |
25 |
26 |
27 |
28 |
43 |
44 |
49 |
--------------------------------------------------------------------------------
/src/components/home/guide.vue:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lutece-awesome/lutece-frontend/27126931178a87e2a73e209b476b0c96517fc09e/src/components/home/guide.vue
--------------------------------------------------------------------------------
/src/components/home/home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
17 |
--------------------------------------------------------------------------------
/src/components/home/honor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
15 |
16 | {{ title }}
17 |
18 |
24 |
25 |
26 |
27 |
28 |
29 |
44 |
45 |
69 |
--------------------------------------------------------------------------------
/src/components/home/recently.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 | Article
9 |
10 |
14 | Guide
15 |
16 |
20 | Honor
21 |
22 |
23 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
54 |
55 |
60 |
--------------------------------------------------------------------------------
/src/components/language/utils/select.vue:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
61 |
--------------------------------------------------------------------------------
/src/components/navigation/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
13 |
14 |
15 |
16 |
17 |
46 |
--------------------------------------------------------------------------------
/src/components/navigation/drawer.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
15 |
16 |
17 |
18 |
19 | {{ title || 'Lutece' }}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
36 |
37 | {{ item.icon }}
38 |
39 |
40 | {{ item.title }}
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
116 |
--------------------------------------------------------------------------------
/src/components/navigation/toolbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
12 |
13 |
14 | {{ title || 'Lutece' }}
15 |
16 |
17 |
18 |
19 |
20 |
25 | mdi-login
26 | SIGN IN
27 |
28 |
32 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
80 |
--------------------------------------------------------------------------------
/src/components/navigation/user-menu-mobile.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
12 |
13 |
14 |
18 | mdi-logout
19 |
20 |
21 |
22 |
23 |
24 |
56 |
--------------------------------------------------------------------------------
/src/components/navigation/user-menu.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
11 |
15 |
16 |
17 |
21 | {{ profile.username }}
22 |
23 |
24 |
25 |
26 |
29 |
33 | mdi-account
34 |
35 |
36 |
37 | Profile
38 |
39 |
40 |
41 |
46 |
50 | {{ item.icon }}
51 |
52 |
53 |
54 | {{ item.label }}
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
94 |
--------------------------------------------------------------------------------
/src/components/problem/detail/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
12 |
17 |
18 |
22 |
26 | Description
27 |
28 |
32 | Editor
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
55 | mdi-pencil
56 |
57 |
58 |
59 |
60 |
97 |
--------------------------------------------------------------------------------
/src/components/problem/detail/description.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
9 |
13 |
14 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
39 |
--------------------------------------------------------------------------------
/src/components/problem/detail/editor.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
79 |
--------------------------------------------------------------------------------
/src/components/problem/detail/preview.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
Standard Input
9 |
12 |
Standard Output
15 |
18 |
Samples
19 |
20 | Input | Output |
21 |
24 | {{ sample.inputContent }} |
25 | {{ sample.outputContent }} |
26 |
27 |
28 |
Constraints
31 |
34 |
Note
37 |
40 |
41 |
42 | Problem ID |
43 | {{ problem.pk }} |
44 |
45 |
46 | Problem Title |
47 | {{ problem.title }} |
48 |
49 |
50 | Time Limit |
51 | {{ problem.limitation.timeLimit }} ms |
52 |
53 |
54 | Memory Limit |
55 | {{ problem.limitation.memoryLimit }} MiB |
56 |
57 |
58 | Output Limit |
59 | {{ problem.limitation.outputLimit }} MiB |
60 |
61 |
62 | Source |
63 | {{ problem.resources }} |
64 |
65 |
66 |
67 |
68 |
69 |
85 |
--------------------------------------------------------------------------------
/src/components/problem/list/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
15 |
20 |
22 |
27 |
28 |
43 |
44 |
45 |
57 | mdi-pencil
58 |
59 |
60 |
61 |
62 |
63 |
64 |
111 |
--------------------------------------------------------------------------------
/src/components/problem/list/list.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
13 |
16 |
21 | {{ props.item.pk }} |
22 |
23 | {{ props.item.title }}
24 | |
25 | {{ props.item.accept }}/{{ props.item.submit }} |
26 |
27 | {{ (props.item.submit ? (props.item.accept / props.item.submit) * 100 : 0).toFixed(2) }}%
28 | |
29 |
30 |
31 |
32 |
33 |
34 |
82 |
--------------------------------------------------------------------------------
/src/components/problem/utils/auto-complete.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
28 |
32 | {{ item.pk + ' - ' + item.title }}
33 |
34 |
35 |
36 |
37 |
38 |
39 |
78 |
--------------------------------------------------------------------------------
/src/components/problem/utils/fetch.js:
--------------------------------------------------------------------------------
1 | import Apollo from '@/plugins/essential/apollo-provider';
2 |
3 |
4 | const fetchProblemData = ({ slug, gql }) => new Promise((resolve, reject) => {
5 | Apollo.defaultClient.query({
6 | query: gql,
7 | variables: {
8 | slug,
9 | },
10 | })
11 | .then(response => resolve(response))
12 | .catch(error => reject(error));
13 | });
14 |
15 |
16 | export default fetchProblemData;
17 |
--------------------------------------------------------------------------------
/src/components/problem/utils/preview.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 | Standard Input
11 |
14 | Standard Output
17 |
20 | Samples
21 |
22 | Input | Output |
23 |
26 | {{ sample.inputContent }} |
27 | {{ sample.outputContent }} |
28 |
29 |
30 | Constraints
33 |
36 | Note
39 |
42 |
43 |
44 | Problem ID |
45 | {{ problem.pk }} |
46 |
47 |
48 | Problem Title |
49 | {{ problem.title }} |
50 |
51 |
52 | Time Limit |
53 | {{ problem.limitation.timeLimit }} ms |
54 |
55 |
56 | Memory Limit |
57 | {{ problem.limitation.memoryLimit }} MiB |
58 |
59 |
60 | Output Limit |
61 | {{ problem.limitation.outputLimit }} MiB |
62 |
63 |
64 | Source |
65 | {{ problem.resources }} |
66 |
67 |
68 |
69 |
70 |
71 |
86 |
--------------------------------------------------------------------------------
/src/components/signin/login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
13 |
23 |
24 |
25 |
32 |
33 |
34 | Do not have account?
35 |
36 |
39 | Login
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
99 |
--------------------------------------------------------------------------------
/src/components/signin/signout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
18 |
--------------------------------------------------------------------------------
/src/components/snackbar/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ message }}
4 | Close
8 |
9 |
10 |
11 |
31 |
--------------------------------------------------------------------------------
/src/components/status/detail/code.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
13 |
14 | Error Info
15 |
16 |
17 |
{{ errorInfo }}
18 |
19 |
22 |
23 | Compile Info
24 |
25 |
26 |
{{ compileInfo }}
27 |
28 |
29 |
30 |
31 |
32 |
33 |
63 |
--------------------------------------------------------------------------------
/src/components/status/detail/connection.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | import { getWebSocketUri } from '@/utils';
3 | import store from '@/store';
4 |
5 | const establishWebSocketConnection = pk => new Promise(
6 | (resolve, reject) => {
7 | const token = store.getters['user/token'];
8 | const ws = new WebSocket(`${getWebSocketUri()}/status/${String(pk)}/?${token}`);
9 | ws.onerror = error => reject(error);
10 | ws.onopen = resolve(ws);
11 | },
12 | );
13 |
14 | const closeWebSocketConnection = ws => new Promise(
15 | (resolve) => {
16 | if (ws) {
17 | ws.close();
18 | }
19 | resolve();
20 | },
21 | );
22 |
23 | const closeWebSocketConnectionSync = ws => {
24 | if( ws ){
25 | ws.close();
26 | }
27 | };
28 |
29 |
30 | const waitingInitializing = ws => new Promise(
31 | (resolve) => {
32 | ws.onmessage = (data) => {
33 | ws.onmessage = null;
34 | resolve(data);
35 | };
36 | },
37 | );
38 |
39 | export { establishWebSocketConnection, closeWebSocketConnection, waitingInitializing, closeWebSocketConnectionSync };
40 |
--------------------------------------------------------------------------------
/src/components/status/detail/progress.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
13 |
14 | {{ props.item.case }} |
15 |
18 | {{ props.item.result.full }}
19 | |
20 | {{ props.item.timeCost }} ms |
21 | {{ props.item.memoryCost }} KiB |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
67 |
--------------------------------------------------------------------------------
/src/components/status/detail/summary.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 | {{ result.full }}
10 |
11 |
12 |
13 |
19 |
20 |
21 |
22 |
23 |
24 | Problem:
25 |
26 | |
27 |
28 |
37 | {{ problemTitle }}
38 |
39 | |
40 |
41 |
42 |
43 |
44 | User:
45 |
46 | |
47 |
48 |
56 | {{ username }}
57 |
58 | |
59 |
60 |
61 |
62 |
63 | Time:
64 |
65 | |
66 |
67 |
68 | {{ submitTime }}
69 |
70 | |
71 |
72 |
73 |
74 |
75 | Lang:
76 |
77 | |
78 |
80 |
81 | {{ language.full }}
82 |
83 | |
84 |
85 |
86 |
87 |
88 | Case:
89 |
90 | |
91 |
93 |
94 | {{ caseList.length }} / {{ caseNumber }}
95 |
96 | |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
152 |
--------------------------------------------------------------------------------
/src/components/status/list/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
15 |
16 |
22 |
24 |
25 |
26 |
28 |
37 |
38 |
41 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
106 |
--------------------------------------------------------------------------------
/src/components/user/detail/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 | mdi-pencil
13 |
14 |
15 |
19 |
24 |
28 |
30 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
74 |
--------------------------------------------------------------------------------
/src/components/user/detail/layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
14 |
15 |
18 |
21 |
22 |
23 |
24 |
25 |
26 |
47 |
--------------------------------------------------------------------------------
/src/components/user/detail/solved.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
15 |
16 |
17 | mdi-chart-gantt
18 | Solved
19 |
20 |
21 |
22 |
25 |
26 |
33 | {{ each.pk }}
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
55 |
--------------------------------------------------------------------------------
/src/components/user/list/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
10 |
15 |
20 |
22 |
27 |
28 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
84 |
--------------------------------------------------------------------------------
/src/components/user/list/list.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
15 |
18 |
24 | {{ formatRank( props.item.rank.position ) }} |
25 |
26 |
27 |
31 |
32 |
33 | {{ props.item.username }}
34 |
35 | |
36 |
37 | {{ props.item.solved }}
38 | /
39 | {{ props.item.tried }}
40 | |
41 |
42 | {{ ( props.item.statistics.ratio * 100 ).toFixed( 2 ) }} %
43 | |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
101 |
--------------------------------------------------------------------------------
/src/components/user/utils/auto-complete.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
23 |
24 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
121 |
--------------------------------------------------------------------------------
/src/components/utils/code-mirror.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
12 |
87 |
--------------------------------------------------------------------------------
/src/components/utils/error-spinner.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
14 |
15 | {{ msg }}
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
33 |
--------------------------------------------------------------------------------
/src/components/utils/loading-spinner.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
19 | {{ text }}
20 |
21 |
22 |
23 |
24 |
25 |
48 |
--------------------------------------------------------------------------------
/src/components/utils/random-color.js:
--------------------------------------------------------------------------------
1 |
2 | const ColorList = [
3 | 'red', 'pink', 'purple', 'deep-purple', 'indigo', 'blue', 'light-blue', 'cyan', 'teal', 'green', 'yellow', 'amber', 'orange', 'brown',
4 | ];
5 |
6 | const getRandomColor = () => {
7 | const sz = ColorList.length;
8 | return ColorList[Math.floor(Math.random() * sz)];
9 | };
10 |
11 | export default getRandomColor;
12 |
--------------------------------------------------------------------------------
/src/components/utils/search-bar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
19 |
20 |
21 |
22 |
23 |
41 |
--------------------------------------------------------------------------------
/src/components/verdict/utils/select.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 |
50 |
--------------------------------------------------------------------------------
/src/docs/develop.md:
--------------------------------------------------------------------------------
1 | ## Introduction
2 | [Lutece](https://github.com/lutece-awesome) is a fast iterative developing Online Judge platform, the Dev team still bring many amazing features on it, as open source project, you can also join the building of this 👏awesome project, this simple doc would guide you:
3 | - Architecture introduction
4 | - How to run dev server in localhost
5 | - Dev environment
6 | - How to submit PR to Lutece? 😎
7 | ## Part 1 - Architecture introduction
8 | There are three mainly parts of Lutece:
9 | * [Frontend](https://github.com/lutece-awesome/lutece-frontend)(Interact with user)
10 | + [Vue.js](https://vuejs.org/) as Javascript framework.
11 | + [Vuetify](https://vuetifyjs.com/en/) as UI framework.
12 | * [Backend](https://github.com/lutece-awesome/lutece-backend)(Business logic and database processing)
13 | + [Django](https://www.djangoproject.com/): Server framework with magic Python.
14 | + [Celery](http://www.celeryproject.org/): Distributed task queue framework.
15 | * [JudgeCore](https://github.com/lutece-awesome/osiris)(User submission judging)
16 | + [Docker](https://www.docker.com/): Sandbox solution.
17 | + [cgroups](https://en.wikipedia.org/wiki/Cgroups): (Still WIP)Runtime resources limitation.
18 |
19 | Lutece use the [GraphQL](https://graphql.org/) query language to process the data exchange between the server and client, for this, the client use [VueApollo](https://vue-apollo.netlify.com/) framework and server use [Graphene](https://graphene-python.org/) framework.Please attentain that this only list the most important frameworks using in Lutece, but still others.
20 | ## Part 2 - How to run dev server in localhost
21 | To easy start, we do not consider run local judge osiris core.
22 |
23 | Run dev frontend server in localhost is quite easy, please follow the [README](https://github.com/lutece-awesome/lutece-frontend/blob/master/README.md).
24 |
25 | Run dev backend server is more complexity, please follow these steps:
26 | + pip3 install -r requirements/requirements.txt
27 | + cp Lutece/config.py.tempalte Lutece/config.py
28 | + [make migrations](https://github.com/lutece-awesome/lutece-backend/blob/master/.travis.yml#L23)
29 | + [migrate](https://github.com/lutece-awesome/lutece-backend/blob/master/.travis.yml#L24)
30 | + python3 manage.py runserver
31 | ## Part 3 - Dev environment
32 | Frontend:
33 | + Use [VSCode](https://code.visualstudio.com/).
34 |
35 | Backend:
36 | + Use [Pycharm](https://www.jetbrains.com/pycharm/) professional version(if you are student, you can apply [student license](https://www.jetbrains.com/student/)).
37 | ## Part 4 - How to submit PR
38 | Please follow the github PR [guide](https://help.github.com/articles/creating-a-pull-request/).
39 |
40 | And for all PRs, please specify [lutece-awesome/review](https://github.com/orgs/lutece-awesome/teams/review/members) as [reviewers](https://help.github.com/articles/requesting-a-pull-request-review/).
41 |
42 | As osiris core planning to entire refactor, so any PRs to osiris would not be accepted for now.
--------------------------------------------------------------------------------
/src/graphql/article/create.gql:
--------------------------------------------------------------------------------
1 | mutation CreateArticleGQL($title: String!, $content: String!){
2 | createArticle(title: $title, content: $content) {
3 | state
4 | }
5 | }
--------------------------------------------------------------------------------
/src/graphql/article/detail.gql:
--------------------------------------------------------------------------------
1 | query ArticleGQL( $slug: String! ){
2 | article( slug: $slug ){
3 | title
4 | content
5 | view
6 | vote
7 | createTime
8 | user{
9 | username
10 | displayName
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/graphql/article/discussion.gql:
--------------------------------------------------------------------------------
1 | query ComemntsGQL( $page: Int! , $slug: String! , $time: Int ){
2 | articleDiscussionList( page: $page , slug: $slug , time: $time ){
3 | maxPage
4 | discussionList{
5 | user{
6 | username
7 | displayName
8 | gravataremail
9 | }
10 | content
11 | submitTime
12 | vote
13 | pk
14 | attitude
15 | reply{
16 | pk
17 | submitTime
18 | content
19 | user{
20 | username
21 | displayName
22 | gravataremail
23 | }
24 | vote
25 | attitude
26 | }
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/src/graphql/article/list.gql:
--------------------------------------------------------------------------------
1 | query ArticleListGQL($page: Int!) {
2 | articleList(page: $page) {
3 | maxPage
4 | ArticleList {
5 | title
6 | vote
7 | view
8 | user{
9 | displayName
10 | username
11 | gravataremail
12 | }
13 | slug
14 | createTime
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/graphql/home/home-article-list.gql:
--------------------------------------------------------------------------------
1 | query HomeArticleListGQL($page: Int!, $filter: String) {
2 | homeArticleList(page: $page, filter: $filter) {
3 | maxPage
4 | homeArticleList {
5 | title
6 | slug
7 | preview
8 | author {
9 | username
10 | attachInfo{
11 | gravatar
12 | }
13 | }
14 | record {
15 | count
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/graphql/home/topuser.gql:
--------------------------------------------------------------------------------
1 | query TopUserGQL($number: Int!) {
2 | topUser(number: $number) {
3 | username
4 | displayName
5 | gravataremail
6 | solved
7 | tried
8 | rank
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/graphql/home/user-article-list.gql:
--------------------------------------------------------------------------------
1 | query UserArticleListGQL($page: Int!, $filter: String) {
2 | userArticleList(page: $page, filter: $filter) {
3 | maxPage
4 | userArticleList {
5 | pk
6 | title
7 | author {
8 | username
9 | attachInfo{
10 | gravatar
11 | }
12 | }
13 | record {
14 | count
15 | }
16 | vote
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/graphql/language/languagelist.gql:
--------------------------------------------------------------------------------
1 | query LanguageList{
2 | allLanguage
3 | }
--------------------------------------------------------------------------------
/src/graphql/problem/detail.gql:
--------------------------------------------------------------------------------
1 | query ProblemDetailGQL($slug: String!) {
2 | problem(slug: $slug) {
3 | pk
4 | title
5 | content
6 | standardInput
7 | standardOutput
8 | constraints
9 | resources
10 | note
11 | limitation{
12 | timeLimit
13 | memoryLimit
14 | outputLimit
15 | cpuLimit
16 | }
17 | samples {
18 | sampleList{
19 | inputContent
20 | outputContent
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/graphql/problem/edit-detail.gql:
--------------------------------------------------------------------------------
1 | query ProblemEditDetailGQL($slug: String!) {
2 | problem(slug: $slug) {
3 | pk
4 | title
5 | content
6 | standardInput
7 | standardOutput
8 | constraints
9 | resources
10 | note
11 | limitation{
12 | timeLimit
13 | memoryLimit
14 | outputLimit
15 | cpuLimit
16 | }
17 | disable
18 | samples {
19 | sampleList{
20 | inputContent
21 | outputContent
22 | }
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/graphql/problem/edit.gql:
--------------------------------------------------------------------------------
1 | mutation UpdateProblemGQL($title: String!, $content: String!, $note: String!, $timeLimit: Int!, $memoryLimit: Int!, $constraints: String!, $resources: String!, $standardInput: String! , $standardOutput: String! , $slug: String! , $samples: String! , $disable: Boolean! , $outputLimit: Int! , $cpuLimit: Int! ) {
2 | updateProblem(
3 | title: $title,
4 | content: $content,
5 | note: $note,
6 | timeLimit: $timeLimit,
7 | memoryLimit: $memoryLimit,
8 | outputLimit: $outputLimit,
9 | cpuLimit: $cpuLimit,
10 | constraints: $constraints,
11 | resources: $resources,
12 | standardInput: $standardInput,
13 | standardOutput: $standardOutput,
14 | slug: $slug,
15 | samples: $samples,
16 | disable: $disable
17 | ) {
18 | slug
19 | }
20 | }
--------------------------------------------------------------------------------
/src/graphql/problem/list.gql:
--------------------------------------------------------------------------------
1 | query ProblemListGQL($page: Int!, $filter: String) {
2 | problemList(page: $page, filter: $filter) {
3 | maxPage
4 | problemList {
5 | pk
6 | title
7 | submit
8 | accept
9 | slug
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/graphql/problem/search.gql:
--------------------------------------------------------------------------------
1 | query ProblemSearchGQL($filter: String) {
2 | problemSearch(filter: $filter) {
3 | problemList {
4 | pk
5 | title
6 | slug
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/graphql/signin/register.gql:
--------------------------------------------------------------------------------
1 | mutation RegisterGQL(
2 | $username: String!
3 | $password: String!
4 | $email: String!
5 | $about: String!
6 | $school: String
7 | $company: String
8 | $location: String
9 | $codeforces: String
10 | $atcoder: String
11 | $studentid: String
12 | $gender: Boolean
13 | ) {
14 | userRegister(
15 | username: $username
16 | password: $password
17 | email: $email
18 | about: $about
19 | school: $school
20 | company: $company
21 | location: $location
22 | codeforces: $codeforces
23 | atcoder: $atcoder
24 | studentid: $studentid
25 | gender: $gender
26 | ) {
27 | token
28 | payload
29 | permission
30 | user{
31 | username
32 | attachInfo{
33 | gravatar
34 | school
35 | company
36 | location
37 | about
38 | codeforces
39 | atcoder
40 | studentid
41 | gender
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/graphql/signin/token.gql:
--------------------------------------------------------------------------------
1 | mutation UserLogin($username: String!, $password: String!) {
2 | userLogin(username: $username, password: $password) {
3 | token
4 | payload
5 | permission
6 | user{
7 | username
8 | attachInfo{
9 | gravatar
10 | school
11 | company
12 | location
13 | about
14 | codeforces
15 | atcoder
16 | studentid
17 | gender
18 | }
19 | }
20 | }
21 | }
22 |
23 | mutation VerifyToken($token: String!) {
24 | verifyToken(token: $token) {
25 | payload
26 | }
27 | }
28 |
29 | mutation RefreshToken($token: String!) {
30 | userTokenRefresh(token: $token) {
31 | token
32 | payload
33 | permission
34 | user{
35 | username
36 | attachInfo{
37 | gravatar
38 | school
39 | company
40 | location
41 | about
42 | codeforces
43 | atcoder
44 | studentid
45 | gender
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/graphql/submission/list.gql:
--------------------------------------------------------------------------------
1 | query SubmissionListGQL(
2 | $page: Int!
3 | $pk: ID
4 | $user: String
5 | $problem: String
6 | $judgeStatus: String
7 | $language: String
8 | ) {
9 | submissionList(
10 | page: $page
11 | pk: $pk
12 | user: $user
13 | problem: $problem
14 | judgeStatus: $judgeStatus
15 | language: $language
16 | ) {
17 | maxPage
18 | submissionList {
19 | pk
20 | failedCase
21 | createTime
22 | user {
23 | username
24 | attachInfo{
25 | gravatar
26 | }
27 | }
28 | problem {
29 | title
30 | slug
31 | }
32 | result{
33 | status
34 | color
35 | }
36 | language
37 | attachInfo{
38 | timeCost
39 | memoryCost
40 | }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/graphql/submission/submit.gql:
--------------------------------------------------------------------------------
1 | mutation SubmitSubmission($code: String!, $problemSlug: String! , $language: String!) {
2 | submitSubmission(code: $code, problemSlug: $problemSlug , language: $language) {
3 | pk
4 | }
5 | }
--------------------------------------------------------------------------------
/src/graphql/user/list.gql:
--------------------------------------------------------------------------------
1 | query UserListGQL($page: Int!, $filter: String) {
2 | userList(page: $page, filter: $filter) {
3 | maxPage
4 | userList {
5 | username
6 | attachInfo{
7 | gravatar
8 | }
9 | solved
10 | tried
11 | statistics{
12 | ratio
13 | }
14 | rank{
15 | position
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/graphql/user/profile.gql:
--------------------------------------------------------------------------------
1 | query ProfileGQL($username: String!) {
2 | user( username: $username){
3 | username
4 | attachInfo{
5 | school
6 | company
7 | location
8 | about
9 | gravatar
10 | codeforces
11 | atcoder
12 | studentid
13 | gender
14 | }
15 | solved
16 | tried
17 | joinedDate
18 | lastLoginDate
19 | rank{
20 | position
21 | count
22 | }
23 | statistics{
24 | ac
25 | tle
26 | ce
27 | wa
28 | re
29 | ole
30 | mle
31 | solve{
32 | pk
33 | slug
34 | status
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/graphql/user/search.gql:
--------------------------------------------------------------------------------
1 | query UserSearchGQL($filter: String) {
2 | userSearch(filter: $filter) {
3 | userList {
4 | username
5 | attachInfo{
6 | gravatar
7 | }
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/graphql/user/settings.gql:
--------------------------------------------------------------------------------
1 | mutation UserInfoUpdateGQL(
2 | $about: String!,
3 | $company: String!,
4 | $school: String!,
5 | $location: String!,
6 | $codeforces: String!,
7 | $atcoder: String!,
8 | $studentid: String!,
9 | $gender: Boolean!
10 | ){
11 | userAttachInfoUpdate(
12 | company: $company,
13 | about: $about,
14 | school: $school,
15 | location: $location,
16 | codeforces: $codeforces,
17 | atcoder: $atcoder,
18 | studentid: $studentid,
19 | gender: $gender
20 | ){
21 | state
22 | }
23 | }
--------------------------------------------------------------------------------
/src/graphql/utils/uploadimage.gql:
--------------------------------------------------------------------------------
1 | mutation UploadImageGQL($file: Upload!){
2 | UploadImage(file:$file){
3 | path
4 | }
5 | }
--------------------------------------------------------------------------------
/src/graphql/votediscussion/vote.gql:
--------------------------------------------------------------------------------
1 | mutation VoteDiscussionGQL(
2 | $pk: ID!
3 | $attitude: Boolean!
4 | ) {
5 | UpdateDiscussionVote(
6 | pk: $pk
7 | attitude: $attitude
8 | ) {
9 | vote
10 | result
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import '@babel/polyfill';
2 |
3 | import loadingPlugins from './plugins/index';
4 | import Vue from './plugins/essential/vue';
5 | import router from './plugins/essential/router';
6 | import store from './plugins/essential/store';
7 | import apolloProvider from './plugins/essential/apollo-provider';
8 | import App from './App';
9 |
10 | loadingPlugins.loadingEssential().then(() => {
11 | loadingPlugins.loadingExternal();
12 | new Vue({
13 | router,
14 | store,
15 | apolloProvider,
16 | render: h => h(App),
17 | }).$mount('#app');
18 | });
19 |
--------------------------------------------------------------------------------
/src/modules/code-mirror/keymap.js:
--------------------------------------------------------------------------------
1 | const keymapPlugins = [
2 | {
3 | name: 'default',
4 | import: () => Promise.resolve(),
5 | },
6 | {
7 | name: 'sublime',
8 | import: () => import('codemirror/keymap/sublime'),
9 | },
10 | {
11 | name: 'emacs',
12 | import: () => import('codemirror/keymap/emacs'),
13 | },
14 | {
15 | name: 'vim',
16 | import: () => import('codemirror/keymap/vim'),
17 | },
18 | ];
19 |
20 | export default {
21 | all: () => keymapPlugins,
22 | valueOf: result => keymapPlugins.find(element => element.name === result),
23 | first: () => keymapPlugins[0],
24 | };
25 |
--------------------------------------------------------------------------------
/src/modules/language/main.js:
--------------------------------------------------------------------------------
1 | const LanguageList = [
2 | {
3 | full: 'GNU G++',
4 | info: 'GNU G++17',
5 | codeMirror: 'text/x-c++src',
6 | codeMirrorImport: () => import('codemirror/mode/clike/clike'),
7 | },
8 | {
9 | full: 'GNU GCC',
10 | info: 'GNU GCC 7.3',
11 | codeMirror: 'text/x-csrc',
12 | codeMirrorImport: () => import('codemirror/mode/clike/clike'),
13 | },
14 | // {
15 | // full: 'Clang',
16 | // info: 'Clang 6.0.0',
17 | // codeMirror: 'text/x-c++src',
18 | // },
19 | {
20 | full: 'Python',
21 | info: 'Python 3.6.5',
22 | codeMirror: 'text/x-python',
23 | codeMirrorImport: () => import('codemirror/mode/python/python'),
24 | },
25 | {
26 | full: 'Java',
27 | info: 'Java 10',
28 | codeMirror: 'text/x-java',
29 | // Java using clike
30 | codeMirrorImport: () => import('codemirror/mode/clike/clike'),
31 | },
32 | {
33 | full: 'Go',
34 | info: 'Go 1.10.2',
35 | codeMirror: 'text/x-go',
36 | codeMirrorImport: () => import('codemirror/mode/go/go'),
37 | },
38 | {
39 | full: 'Ruby',
40 | info: 'Ruby 2.5.1',
41 | codeMirror: 'text/x-ruby',
42 | codeMirrorImport: () => import('codemirror/mode/ruby/ruby'),
43 | },
44 | {
45 | full: 'Rust',
46 | info: 'Rust 1.26.1',
47 | codeMirror: 'text/x-rustsrc',
48 | codeMirrorImport: () => import('codemirror/mode/rust/rust'),
49 | },
50 | ];
51 |
52 | const valueOf = result => LanguageList.find(element => element.full === result
53 | || element.info === result);
54 |
55 | const Language = {
56 | valueOf: result => valueOf(result),
57 | all: () => LanguageList,
58 | first: () => LanguageList[0],
59 | };
60 |
61 | export default Language;
62 |
--------------------------------------------------------------------------------
/src/modules/verdict/main.js:
--------------------------------------------------------------------------------
1 | const VerdictList = [
2 | {
3 | full: 'Pending',
4 | short: 'pd',
5 | textColor: 'info',
6 | },
7 | {
8 | full: 'Preparing',
9 | short: 'pr',
10 | textColor: 'info',
11 | },
12 | {
13 | full: 'Accepted',
14 | short: 'ac',
15 | backgroundColor: 'rgba(0, 255, 127, 0.2)',
16 | borderColor: 'rgba(0, 255, 127, 1)',
17 | textColor: 'success',
18 | },
19 | {
20 | full: 'Running',
21 | short: 'rn',
22 | textColor: 'info',
23 | },
24 | {
25 | full: 'Compile Error',
26 | short: 'ce',
27 | backgroundColor: 'rgba(54, 162, 235, 0.2)',
28 | borderColor: 'rgba(54, 162, 235, 1)',
29 | textColor: 'warning',
30 | },
31 | {
32 | full: 'Wrong Answer',
33 | short: 'wa',
34 | backgroundColor: 'rgba(255, 99, 132, 0.2)',
35 | borderColor: 'rgba(255, 99, 132, 1)',
36 | textColor: 'error',
37 | },
38 | {
39 | full: 'Runtime Error',
40 | short: 're',
41 | backgroundColor: 'rgba(255, 206, 86, 0.2)',
42 | borderColor: 'rgba(255, 206, 86, 1)',
43 | textColor: 'error',
44 | },
45 | {
46 | full: 'Time Limit Exceeded',
47 | short: 'tle',
48 | backgroundColor: 'rgba(75, 192, 192, 0.2)',
49 | borderColor: 'rgba(75, 192, 192, 1)',
50 | textColor: 'error',
51 | },
52 | {
53 | full: 'Output Limit Exceeded',
54 | short: 'ole',
55 | backgroundColor: 'rgba(153, 102, 255, 0.2)',
56 | borderColor: 'rgba(153, 102, 255, 1)',
57 | textColor: 'error',
58 | },
59 | {
60 | full: 'Memory Limit Exceeded',
61 | short: 'mle',
62 | backgroundColor: 'rgba(255, 159, 64, 0.2)',
63 | borderColor: 'rgba(255, 159, 64, 1)',
64 | textColor: 'error',
65 | },
66 | {
67 | full: 'Judger Error',
68 | short: 'je',
69 | textColor: 'warning',
70 | },
71 | ];
72 |
73 | const valueOf = result => VerdictList.find(element => element.full === result
74 | || element.short === result);
75 |
76 | const Verdict = {
77 | valueOf: result => valueOf(result),
78 | all: () => VerdictList,
79 | pd: valueOf('pd'),
80 | pr: valueOf('pr'),
81 | ac: valueOf('ac'),
82 | rn: valueOf('rn'),
83 | ce: valueOf('ce'),
84 | wa: valueOf('wa'),
85 | re: valueOf('re'),
86 | tle: valueOf('tle'),
87 | ole: valueOf('ole'),
88 | mle: valueOf('mle'),
89 | je: valueOf('je'),
90 | };
91 |
92 |
93 | export default Verdict;
94 |
--------------------------------------------------------------------------------
/src/npm-debug.log:
--------------------------------------------------------------------------------
1 | 0 info it worked if it ends with ok
2 | 1 verbose cli [ '/usr/bin/node',
3 | 1 verbose cli '/usr/bin/npm',
4 | 1 verbose cli 'install',
5 | 1 verbose cli '--save',
6 | 1 verbose cli 'plugins/external/markdown-it-vue' ]
7 | 2 info using npm@3.5.2
8 | 3 info using node@v8.10.0
9 | 4 silly loadCurrentTree Starting
10 | 5 silly install loadCurrentTree
11 | 6 silly install readLocalPackageData
12 | 7 silly fetchPackageMetaData plugins/external/markdown-it-vue
13 | 8 silly fetchOtherPackageData plugins/external/markdown-it-vue
14 | 9 silly cache add args [ 'plugins/external/markdown-it-vue', null ]
15 | 10 verbose cache add spec plugins/external/markdown-it-vue
16 | 11 silly cache add parsed spec Result {
17 | 11 silly cache add raw: 'plugins/external/markdown-it-vue',
18 | 11 silly cache add scope: null,
19 | 11 silly cache add name: null,
20 | 11 silly cache add rawSpec: 'plugins/external/markdown-it-vue',
21 | 11 silly cache add spec: '/home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins/external/markdown-it-vue',
22 | 11 silly cache add type: 'local' }
23 | 12 error addLocal Could not install /home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins/external/markdown-it-vue
24 | 13 silly fetchPackageMetaData Error: ENOENT: no such file or directory, open '/home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins/external/markdown-it-vue'
25 | 13 silly fetchPackageMetaData error for plugins/external/markdown-it-vue { Error: ENOENT: no such file or directory, open '/home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins/external/markdown-it-vue'
26 | 13 silly fetchPackageMetaData errno: -2,
27 | 13 silly fetchPackageMetaData code: 'ENOENT',
28 | 13 silly fetchPackageMetaData syscall: 'open',
29 | 13 silly fetchPackageMetaData path: '/home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins/external/markdown-it-vue' }
30 | 14 silly rollbackFailedOptional Starting
31 | 15 silly rollbackFailedOptional Finishing
32 | 16 silly runTopLevelLifecycles Starting
33 | 17 silly runTopLevelLifecycles Finishing
34 | 18 silly install printInstalled
35 | 19 verbose stack Error: ENOENT: no such file or directory, open '/home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins/external/markdown-it-vue'
36 | 20 verbose cwd /home/hexisyztem/lutece/frontend/lutece-frontend/src
37 | 21 error Linux 5.0.0-37-generic
38 | 22 error argv "/usr/bin/node" "/usr/bin/npm" "install" "--save" "plugins/external/markdown-it-vue"
39 | 23 error node v8.10.0
40 | 24 error npm v3.5.2
41 | 25 error path /home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins/external/markdown-it-vue
42 | 26 error code ENOENT
43 | 27 error errno -2
44 | 28 error syscall open
45 | 29 error enoent ENOENT: no such file or directory, open '/home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins/external/markdown-it-vue'
46 | 30 error enoent ENOENT: no such file or directory, open '/home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins/external/markdown-it-vue'
47 | 30 error enoent This is most likely not a problem with npm itself
48 | 30 error enoent and is related to npm not being able to find a file.
49 | 31 verbose exit [ -2, true ]
50 |
--------------------------------------------------------------------------------
/src/plugins/essential/apollo-provider.js:
--------------------------------------------------------------------------------
1 | import Vue from './vue';
2 | import { ApolloClient } from 'apollo-client';
3 | import { createUploadLink } from 'apollo-upload-client';
4 | import { InMemoryCache } from 'apollo-cache-inmemory';
5 | import { setContext } from 'apollo-link-context';
6 | import VueApollo from 'vue-apollo';
7 | import fetch from 'unfetch';
8 | import { getGraphQLUri } from '@/utils';
9 |
10 | /*
11 | Only used to support native Apollo component to fetch data.
12 | */
13 |
14 | const httpLink = createUploadLink({
15 | // You should use an absolute URL here
16 | uri: getGraphQLUri(),
17 | credentials: 'same-origin',
18 | fetch,
19 | });
20 |
21 | const httpLinkAuth = setContext((_, { headers }) => {
22 | // get the authentication token from localstorage if it exists
23 | const token = localStorage.getItem('USER_TOKEN');
24 | // return the headers to the context so httpLink can read them
25 | if (token) {
26 | return {
27 | headers: {
28 | ...headers,
29 | Authorization: `JWT ${token}`,
30 | },
31 | };
32 | }
33 | return { headers };
34 | });
35 |
36 | const apolloClient = new ApolloClient({
37 | link: httpLinkAuth.concat(httpLink),
38 | cache: new InMemoryCache(),
39 | connectToDevTools: true,
40 | });
41 |
42 | Vue.use(VueApollo);
43 |
44 | const apolloProvider = new VueApollo({
45 | defaultClient: apolloClient,
46 | });
47 |
48 | export default apolloProvider;
49 |
--------------------------------------------------------------------------------
/src/plugins/essential/auth.js:
--------------------------------------------------------------------------------
1 | import Store from './store';
2 |
3 | export default Store.dispatch('user/refresh_token', true);
4 |
--------------------------------------------------------------------------------
/src/plugins/essential/filters.js:
--------------------------------------------------------------------------------
1 | import Vue from './vue';
2 |
3 | Vue.filter('nl2br', text => text.replace(/(?:\r\n|\r|\n)/g, '
'));
4 |
--------------------------------------------------------------------------------
/src/plugins/essential/global-components.js:
--------------------------------------------------------------------------------
1 | import Vue from './vue';
2 | import LoadingSpinner from '@/components/utils/loading-spinner';
3 | import ErrorSpinner from '@/components/utils/error-spinner';
4 |
5 | Vue.component('loading-spinner', LoadingSpinner);
6 | Vue.component('error-spinner', ErrorSpinner);
7 |
--------------------------------------------------------------------------------
/src/plugins/essential/graphql-tag.js:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 |
3 | export default gql;
4 |
--------------------------------------------------------------------------------
/src/plugins/essential/router.js:
--------------------------------------------------------------------------------
1 | import Router from '@/router/index';
2 |
3 | export default Router;
4 |
--------------------------------------------------------------------------------
/src/plugins/essential/store.js:
--------------------------------------------------------------------------------
1 | import store from '@/store/index';
2 |
3 | export default store;
4 |
--------------------------------------------------------------------------------
/src/plugins/essential/vue-line-clamp.js:
--------------------------------------------------------------------------------
1 | import LineClamp from 'vue-line-clamp';
2 | import Vue from './vue';
3 |
4 | Vue.use(LineClamp);
5 |
--------------------------------------------------------------------------------
/src/plugins/essential/vue-moment.js:
--------------------------------------------------------------------------------
1 | import VueMoment from 'vue-moment';
2 | import Vue from './vue';
3 |
4 | Vue.use(VueMoment);
5 |
--------------------------------------------------------------------------------
/src/plugins/essential/vue.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 |
3 | Vue.config.productionTip = false;
4 |
5 | export default Vue;
6 |
--------------------------------------------------------------------------------
/src/plugins/essential/vuetify.js:
--------------------------------------------------------------------------------
1 | import {
2 | Vuetify, // required
3 | VWindow,
4 | VItemGroup,
5 | VSlider,
6 | VTimePicker,
7 | VDatePicker,
8 | VDialog,
9 | VCheckbox,
10 | VChip,
11 | VAlert,
12 | VImg,
13 | VProgressCircular,
14 | VHover,
15 | VTooltip,
16 | VApp, // required
17 | VTextarea,
18 | VSubheader,
19 | VSwitch,
20 | VNavigationDrawer,
21 | VGrid,
22 | VToolbar,
23 | VList,
24 | VBtn,
25 | VAvatar,
26 | VCard,
27 | VMenu,
28 | VIcon,
29 | VAutocomplete,
30 | VDataTable,
31 | VPagination,
32 | VTabs,
33 | VSelect,
34 | VTextField,
35 | VForm,
36 | VDivider,
37 | VProgressLinear,
38 | VSnackbar,
39 | VDataIterator,
40 | transitions,
41 | } from 'vuetify';
42 | import { Resize } from 'vuetify/es5/directives';
43 | import colors from 'vuetify/es5/util/colors';
44 | import DatetimePicker from 'vuetify-datetime-picker';
45 | import Vue from './vue';
46 | import '@/stylus/main.styl';
47 | import '@mdi/font/css/materialdesignicons.css';
48 | import 'vuetify-datetime-picker/src/stylus/main.styl';
49 |
50 | Vue.use(DatetimePicker);
51 |
52 | Vue.use(Vuetify, {
53 | components: {
54 | VWindow,
55 | VItemGroup,
56 | VSlider,
57 | VTimePicker,
58 | VDatePicker,
59 | VDialog,
60 | VCheckbox,
61 | VChip,
62 | VAlert,
63 | VImg,
64 | VProgressCircular,
65 | VHover,
66 | VTooltip,
67 | VTextarea,
68 | VSubheader,
69 | VSwitch,
70 | VApp,
71 | VNavigationDrawer,
72 | VGrid,
73 | VToolbar,
74 | VList,
75 | VBtn,
76 | VAvatar,
77 | VCard,
78 | VMenu,
79 | VIcon,
80 | VAutocomplete,
81 | VDataTable,
82 | VPagination,
83 | VTabs,
84 | VSelect,
85 | VTextField,
86 | VForm,
87 | VDivider,
88 | VProgressLinear,
89 | VSnackbar,
90 | VDataIterator,
91 | transitions,
92 | },
93 | directives: {
94 | Resize,
95 | },
96 | iconfont: 'mdi',
97 | theme: {
98 | primary: colors.blue.darken1,
99 | secondary: colors.blue.darken2,
100 | accent: colors.pink.base,
101 | },
102 | });
103 |
--------------------------------------------------------------------------------
/src/plugins/external/code-mirror.js:
--------------------------------------------------------------------------------
1 | // require component
2 | import { codemirror } from 'vue-codemirror';
3 |
4 | // require styles
5 | import 'codemirror/lib/codemirror.css';
6 |
7 | // scrollbar
8 | import 'codemirror/addon/scroll/simplescrollbars';
9 | import 'codemirror/addon/scroll/simplescrollbars.css';
10 |
11 | // auto refresh
12 | import 'codemirror/addon/display/autorefresh';
13 |
14 | // active line
15 | import 'codemirror/addon/selection/active-line';
16 |
17 | // match brackets
18 | import 'codemirror/addon/edit/matchbrackets';
19 |
20 | // close brackets
21 | import 'codemirror/addon/edit/closebrackets';
22 |
23 | export default codemirror;
24 |
--------------------------------------------------------------------------------
/src/plugins/external/markdown-it-katex.js:
--------------------------------------------------------------------------------
1 | import MarkdownIt from 'markdown-it';
2 | import TexMathPlugin from 'markdown-it-texmath';
3 | import Katex from 'katex';
4 | import 'katex/dist/katex.css';
5 |
6 | TexMathPlugin.use(Katex);
7 |
8 | /**
9 | * For markdown it options, ref to https://github.com/markdown-it/markdown-it.
10 | * For security settings, ref to https://github.com/markdown-it/markdown-it/blob/master/docs/security.md.
11 | */
12 | const StrictMarkdownParser = MarkdownIt({
13 | html: false, // Enable HTML tags in source
14 | xhtmlOut: false, // Use '/' to close single tags (
).
15 | // This is only for full CommonMark compatibility.
16 | breaks: true, // Convert '\n' in paragraphs into
17 | // useful for external highlighters.
18 | linkify: true, // Autoconvert URL-like text to links
19 | typographer: true,
20 | quotes: '“”‘’',
21 | })
22 | .use(TexMathPlugin);
23 |
24 | // Only used for admin
25 | const DangerousMarkdownParser = MarkdownIt({
26 | html: true, // Enable HTML tags in source
27 | xhtmlOut: false, // Use '/' to close single tags (
).
28 | // This is only for full CommonMark compatibility.
29 | breaks: false, // Convert '\n' in paragraphs into
30 | // useful for external highlighters.
31 | linkify: false, // Autoconvert URL-like text to links
32 | typographer: true,
33 | quotes: '“”‘’',
34 | })
35 | .use(TexMathPlugin);
36 |
37 | export { StrictMarkdownParser, DangerousMarkdownParser };
38 |
--------------------------------------------------------------------------------
/src/plugins/external/markdown-it-vue.js:
--------------------------------------------------------------------------------
1 | import MarkdownItVue from 'markdown-it-vue';
2 | import Katex from 'katex';
3 | import 'katex/dist/katex.css';
4 |
5 | TexMathPlugin.use(Katex);
6 |
7 | /**
8 | * For markdown it options, ref to https://github.com/markdown-it/markdown-it.
9 | * For security settings, ref to https://github.com/markdown-it/markdown-it/blob/master/docs/security.md.
10 | */
11 | const StrictMarkdownParser = MarkdownIt({
12 | html: false, // Enable HTML tags in source
13 | xhtmlOut: false, // Use '/' to close single tags (
).
14 | // This is only for full CommonMark compatibility.
15 | breaks: false, // Convert '\n' in paragraphs into
16 | // useful for external highlighters.
17 | linkify: false, // Autoconvert URL-like text to links
18 | typographer: true,
19 | quotes: '“”‘’',
20 | })
21 | .use(TexMathPlugin);
22 |
23 | // Only used for admin
24 | const DangerousMarkdownParser = MarkdownIt({
25 | html: true, // Enable HTML tags in source
26 | xhtmlOut: false, // Use '/' to close single tags (
).
27 | // This is only for full CommonMark compatibility.
28 | breaks: false, // Convert '\n' in paragraphs into
29 | // useful for external highlighters.
30 | linkify: false, // Autoconvert URL-like text to links
31 | typographer: true,
32 | quotes: '“”‘’',
33 | })
34 | .use(TexMathPlugin);
35 |
36 | export { StrictMarkdownParser, DangerousMarkdownParser };
37 |
--------------------------------------------------------------------------------
/src/plugins/index.js:
--------------------------------------------------------------------------------
1 |
2 | import authPromise from './essential/auth';
3 |
4 | // essential plugins, which would block the initializing.
5 | const essentialPlugins = () => [
6 | // Pre auth
7 | authPromise,
8 | // Vue.js as JS Framework
9 | import('./essential/vue'),
10 | // Vuetify UI Framework
11 | import('./essential/vuetify'),
12 | // Vue Router
13 | import('./essential/router'),
14 | // Apollo Provider
15 | import('./essential/apollo-provider'),
16 | // Global components
17 | import('./essential/global-components'),
18 | // Line clamp
19 | import('./essential/vue-line-clamp'),
20 | // Apollo related
21 | import('./essential/apollo-provider'),
22 | // Filter plugin, support nl2br
23 | import('./essential/filters'),
24 | // Vue moment
25 | import('./essential/vue-moment'),
26 | // Graphql Tag
27 | import('./essential/graphql-tag'),
28 | ];
29 |
30 | const externalPlugins = () => [
31 | // Markdown it katex plugin
32 | import('./external/markdown-it-katex'),
33 | // Code Mirror
34 | import('./external/code-mirror'),
35 | ];
36 |
37 | export default {
38 | loadingEssential: () => Promise.all(essentialPlugins()),
39 | loadingExternal: () => Promise.all(externalPlugins()),
40 | };
41 |
--------------------------------------------------------------------------------
/src/plugins/npm-debug.log:
--------------------------------------------------------------------------------
1 | 0 info it worked if it ends with ok
2 | 1 verbose cli [ '/usr/bin/node',
3 | 1 verbose cli '/usr/bin/npm',
4 | 1 verbose cli 'install',
5 | 1 verbose cli '--save',
6 | 1 verbose cli 'plugins/external/markdown-it-vue' ]
7 | 2 info using npm@3.5.2
8 | 3 info using node@v8.10.0
9 | 4 silly loadCurrentTree Starting
10 | 5 silly install loadCurrentTree
11 | 6 silly install readLocalPackageData
12 | 7 silly fetchPackageMetaData plugins/external/markdown-it-vue
13 | 8 silly fetchOtherPackageData plugins/external/markdown-it-vue
14 | 9 silly cache add args [ 'plugins/external/markdown-it-vue', null ]
15 | 10 verbose cache add spec plugins/external/markdown-it-vue
16 | 11 silly cache add parsed spec Result {
17 | 11 silly cache add raw: 'plugins/external/markdown-it-vue',
18 | 11 silly cache add scope: null,
19 | 11 silly cache add name: null,
20 | 11 silly cache add rawSpec: 'plugins/external/markdown-it-vue',
21 | 11 silly cache add spec: '/home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins/plugins/external/markdown-it-vue',
22 | 11 silly cache add type: 'local' }
23 | 12 error addLocal Could not install /home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins/plugins/external/markdown-it-vue
24 | 13 silly fetchPackageMetaData Error: ENOENT: no such file or directory, open '/home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins/plugins/external/markdown-it-vue'
25 | 13 silly fetchPackageMetaData error for plugins/external/markdown-it-vue { Error: ENOENT: no such file or directory, open '/home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins/plugins/external/markdown-it-vue'
26 | 13 silly fetchPackageMetaData errno: -2,
27 | 13 silly fetchPackageMetaData code: 'ENOENT',
28 | 13 silly fetchPackageMetaData syscall: 'open',
29 | 13 silly fetchPackageMetaData path: '/home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins/plugins/external/markdown-it-vue' }
30 | 14 silly rollbackFailedOptional Starting
31 | 15 silly rollbackFailedOptional Finishing
32 | 16 silly runTopLevelLifecycles Starting
33 | 17 silly runTopLevelLifecycles Finishing
34 | 18 silly install printInstalled
35 | 19 verbose stack Error: ENOENT: no such file or directory, open '/home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins/plugins/external/markdown-it-vue'
36 | 20 verbose cwd /home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins
37 | 21 error Linux 5.0.0-37-generic
38 | 22 error argv "/usr/bin/node" "/usr/bin/npm" "install" "--save" "plugins/external/markdown-it-vue"
39 | 23 error node v8.10.0
40 | 24 error npm v3.5.2
41 | 25 error path /home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins/plugins/external/markdown-it-vue
42 | 26 error code ENOENT
43 | 27 error errno -2
44 | 28 error syscall open
45 | 29 error enoent ENOENT: no such file or directory, open '/home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins/plugins/external/markdown-it-vue'
46 | 30 error enoent ENOENT: no such file or directory, open '/home/hexisyztem/lutece/frontend/lutece-frontend/src/plugins/plugins/external/markdown-it-vue'
47 | 30 error enoent This is most likely not a problem with npm itself
48 | 30 error enoent and is related to npm not being able to find a file.
49 | 31 verbose exit [ -2, true ]
50 |
--------------------------------------------------------------------------------
/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 |
3 | import { register } from 'register-service-worker';
4 |
5 | if (process.env.NODE_ENV === 'production') {
6 | register(`${process.env.BASE_URL}static/js/service-worker.js`, {
7 | ready() {
8 | console.log('App is being served from cache by a service worker.\n'
9 | + 'For more details, visit https://goo.gl/AFskqB');
10 | },
11 | cached() {
12 | console.log('Content has been cached for offline use.');
13 | },
14 | updated() {
15 | console.log('New content is available; please refresh.');
16 | },
17 | offline() {
18 | console.log('No internet connection found. App is running in offline mode.');
19 | },
20 | error(error) {
21 | console.error('Error during service worker registration:', error);
22 | },
23 | });
24 | }
25 |
--------------------------------------------------------------------------------
/src/router/about/router.js:
--------------------------------------------------------------------------------
1 | import About from '@/components/about/about';
2 |
3 | const Router = [
4 | {
5 | path: '/about',
6 | name: 'About',
7 | component: About,
8 | },
9 | ];
10 |
11 | export default Router;
12 |
--------------------------------------------------------------------------------
/src/router/article/router.js:
--------------------------------------------------------------------------------
1 | import HomeArticleEdit from '@/components/article/home/edit';
2 | import HomeArticleCreate from '@/components/article/home/create';
3 | import UserArticleEdit from '@/components/article/user/edit';
4 | import UserArticleCreate from '@/components/article/user/create';
5 |
6 | const Router = [
7 | {
8 | path: '/article/home/edit/:slug',
9 | name: 'HomeArticleEdit',
10 | component: HomeArticleEdit,
11 | props: true,
12 | meta: {
13 | requirePermission: 'article.change_homearticle',
14 | },
15 | },
16 | {
17 | path: '/article/home/create',
18 | name: 'HomeArticleCreate',
19 | component: HomeArticleCreate,
20 | meta: {
21 | requirePermission: 'article.add_homearticle',
22 | },
23 | },
24 | {
25 | path: '/article/user/edit/:pk',
26 | name: 'UserArticleEdit',
27 | component: UserArticleEdit,
28 | props: true,
29 | meta: {
30 | requirePermission: 'article.change_userarticle',
31 | },
32 | },
33 | {
34 | path: '/article/user/create',
35 | name: 'UserArticleCreate',
36 | component: UserArticleCreate,
37 | meta: {
38 | requirePermission: 'article.add_userarticle',
39 | },
40 | },
41 | ];
42 |
43 | export default Router;
44 |
--------------------------------------------------------------------------------
/src/router/contest/router.js:
--------------------------------------------------------------------------------
1 | import ContestList from '@/components/contest/list/app';
2 | import ContestCreate from '@/components/contest/edit/create';
3 | import ContestDetail from '@/components/contest/detail/app';
4 | import ContestReview from '@/components/contest/review/app';
5 | import ContestRank from '@/components/contest/detail/rank';
6 | import ContestReviewOverall from '@/components/contest/review/overall';
7 | import ContestReviewMine from '@/components/contest/review/mine';
8 | import ContestUpdate from '@/components/contest/edit/update';
9 |
10 | const Router = [
11 | {
12 | path: '/contest',
13 | name: 'Contest',
14 | component: ContestList,
15 | },
16 | {
17 | path: '/contest-create',
18 | name: 'ContestCreate',
19 | component: ContestCreate,
20 | meta: {
21 | requirePermission: 'contest.add_contest',
22 | },
23 | },
24 | {
25 | path: '/contest-update/:pk',
26 | name: 'ContestUpdate',
27 | component: ContestUpdate,
28 | meta: {
29 | requirePermission: 'contest.change_contest',
30 | },
31 | props: true,
32 | },
33 | {
34 | path: '/contest/:pk',
35 | name: 'ContestDetail',
36 | component: ContestDetail,
37 | redirect: {
38 | name: 'ContestSummary',
39 | },
40 | children: [
41 | {
42 | path: 'summary',
43 | name: 'ContestSummary',
44 | },
45 | {
46 | path: 'clarification',
47 | name: 'ContestClarification',
48 | },
49 | {
50 | path: 'problem',
51 | name: 'ContestProblem',
52 | children: [{
53 | path: ':id',
54 | name: 'ContestSpecifyProblem',
55 | }],
56 | },
57 | {
58 | path: 'submit',
59 | name: 'ContestSubmissionSubmit',
60 | },
61 | {
62 | path: 'submission',
63 | name: 'ContestSubmission',
64 | },
65 | {
66 | path: 'rank',
67 | name: 'ContestRank',
68 | component: ContestRank,
69 | props: true,
70 | },
71 | ],
72 | props: true,
73 | },
74 | {
75 | path: '/contest-review/:pk',
76 | name: 'ContestReview',
77 | component: ContestReview,
78 | redirect: {
79 | name: 'ContestReviewOverall',
80 | },
81 | children: [
82 | {
83 | path: 'overall',
84 | name: 'ContestReviewOverall',
85 | component: ContestReviewOverall,
86 | props: true,
87 | },
88 | {
89 | path: 'mine',
90 | name: 'ContestReviewMine',
91 | component: ContestReviewMine,
92 | props: true,
93 | meta: {
94 | requireAuth: true,
95 | },
96 | },
97 | ],
98 | props: true,
99 | },
100 | ];
101 |
102 |
103 | export default Router;
104 |
--------------------------------------------------------------------------------
/src/router/home/router.js:
--------------------------------------------------------------------------------
1 | import Home from '@/components/home/home';
2 | import Announcement from '@/components/home/announcement';
3 | import Develop from '@/components/home/develop';
4 | import AnnouncementDetail from '@/components/article/home/detail';
5 | import Honor from '@/components/home/honor';
6 | import Blog from '@/components/home/blog';
7 | import BlogDetail from '@/components/article/user/detail';
8 |
9 | const Router = [
10 | {
11 | path: '',
12 | redirect: {
13 | name: 'Home',
14 | },
15 | },
16 | {
17 | path: '/home',
18 | name: 'Home',
19 | component: Home,
20 | },
21 | {
22 | path: '/announcement',
23 | name: 'Announcement',
24 | component: Announcement,
25 | },
26 | {
27 | path: '/announcement/:slug',
28 | name: 'AnnouncementDetail',
29 | component: AnnouncementDetail,
30 | props: true,
31 | },
32 | {
33 | path: '/develop',
34 | name: 'Develop',
35 | component: Develop,
36 | },
37 | {
38 | path: '/honor',
39 | name: 'Honor',
40 | component: Honor,
41 | },
42 | {
43 | path: '/blog',
44 | name: 'Blog',
45 | component: Blog,
46 | },
47 | {
48 | path: '/blog/:pk',
49 | name: 'BlogDetail',
50 | component: BlogDetail,
51 | props: true,
52 | },
53 | ];
54 |
55 | export default Router;
56 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Router from 'vue-router';
3 | import NotFound from '@/components/global/404';
4 | import AboutRouter from '@/router/about/router';
5 | import ArticleRouter from '@/router/article/router';
6 | import ContestRouter from '@/router/contest/router';
7 | import HomeRouter from '@/router/home/router';
8 | import ProblemRouter from '@/router/problem/router';
9 | import SignRouter from '@/router/sign/router';
10 | import StatusRouter from '@/router/status/router';
11 | import UserRouter from '@/router/user/router';
12 | import Store from '@/store/index';
13 |
14 | import { isAuthenticated, goHome, hasPermission } from './utils';
15 |
16 | /*
17 |
18 | Supporting meta:
19 |
20 | requireAuth: boolean
21 | - true: Must log in to access.
22 | - false: Must not log in to access.
23 |
24 | requirePermission: str
25 | check the permission to login the specific route.
26 |
27 | */
28 |
29 | const router = new Router({
30 | mode: 'history',
31 | routes: [
32 | ...AboutRouter,
33 | ...ArticleRouter,
34 | ...HomeRouter,
35 | ...ProblemRouter,
36 | ...SignRouter,
37 | ...StatusRouter,
38 | ...UserRouter,
39 | ...ContestRouter,
40 | {
41 | path: '*',
42 | name: '404',
43 | component: NotFound,
44 | },
45 | ],
46 | });
47 |
48 |
49 | router.beforeEach((to, from, next) => {
50 | const { matched } = to;
51 |
52 | for (let i = 0; i < matched.length; i += 1) {
53 | const { meta } = matched[i];
54 | // Check the requireAuth meta info
55 | if (Object.prototype.hasOwnProperty.call(meta, 'requireAuth')) {
56 | if (isAuthenticated() !== meta.requireAuth) {
57 | goHome();
58 | return;
59 | }
60 | }
61 |
62 | // Check the permission required or not
63 | if (Object.prototype.hasOwnProperty.call(meta, 'requirePermission')) {
64 | if (!hasPermission(meta.requirePermission)) {
65 | goHome();
66 | return;
67 | }
68 | }
69 | }
70 |
71 | // Refresh token before enter any router, any error should be ignored,
72 | // this step is only to validate token expired or not.
73 | Store.dispatch('user/refresh_token')
74 | .then(() => next())
75 | .catch(() => next());
76 | });
77 |
78 |
79 | // Reset the title
80 | router.beforeEach((to, from, next) => {
81 | Store.commit('navbar/setTitle', 'Lutece');
82 |
83 | next();
84 | });
85 |
86 | Vue.use(Router);
87 |
88 | export default router;
89 |
--------------------------------------------------------------------------------
/src/router/problem/router.js:
--------------------------------------------------------------------------------
1 | import ProblemDetail from '@/components/problem/detail/app';
2 | import ProblemList from '@/components/problem/list/app';
3 | import ProblemDescription from '@/components/problem/detail/description';
4 | import ProblemEditor from '@/components/problem/detail/editor';
5 | import ProblemChange from '@/components/problem/edit/app';
6 | import ProblemCreate from '@/components/problem/create/app';
7 |
8 | const Router = [
9 | {
10 | path: '/problem',
11 | name: 'Problem',
12 | component: ProblemList,
13 | },
14 | {
15 | path: '/problem-create',
16 | name: 'ProblemCreate',
17 | component: ProblemCreate,
18 | meta: {
19 | requirePermission: 'problem.add_problem',
20 | },
21 | },
22 | {
23 | path: '/problem/:slug',
24 | name: 'ProblemDetail',
25 | component: ProblemDetail,
26 | redirect: {
27 | name: 'ProblemDescription',
28 | },
29 | children: [
30 | {
31 | path: 'description',
32 | name: 'ProblemDescription',
33 | component: ProblemDescription,
34 | props: true,
35 | },
36 | {
37 | path: 'editor',
38 | name: 'ProblemEditor',
39 | component: ProblemEditor,
40 | props: true,
41 | },
42 | ],
43 | props: true,
44 | },
45 | {
46 | path: '/problem-edit/:slug',
47 | name: 'ProblemEdit',
48 | component: ProblemChange,
49 | props: true,
50 | meta: {
51 | requirePermission: 'problem.change_problem',
52 | },
53 | },
54 | ];
55 |
56 |
57 | export default Router;
58 |
--------------------------------------------------------------------------------
/src/router/sign/router.js:
--------------------------------------------------------------------------------
1 | import Login from '@/components/signin/login';
2 | import Signup from '@/components/signin/signup';
3 | import Signout from '@/components/signin/signout';
4 |
5 | const Router = [
6 | {
7 | path: '/login',
8 | name: 'Login',
9 | component: Login,
10 | meta: {
11 | requireAuth: false,
12 | },
13 | },
14 | {
15 | path: '/signup',
16 | name: 'Signup',
17 | component: Signup,
18 | meta: {
19 | requireAuth: false,
20 | },
21 | },
22 | {
23 | path: '/signout',
24 | name: 'Signout',
25 | component: Signout,
26 | meta: {
27 | requireAuth: true,
28 | },
29 | },
30 | ];
31 |
32 |
33 | export default Router;
34 |
--------------------------------------------------------------------------------
/src/router/status/router.js:
--------------------------------------------------------------------------------
1 | import StatusList from '@/components/status/list/app';
2 | import StatusDetail from '@/components/status/detail/app';
3 |
4 | const Router = [
5 | {
6 | path: '/status',
7 | name: 'Status',
8 | component: StatusList,
9 | },
10 | {
11 | path: '/status/:pk',
12 | name: 'StatusDetail',
13 | component: StatusDetail,
14 | props: true,
15 | },
16 | ];
17 |
18 |
19 | export default Router;
20 |
--------------------------------------------------------------------------------
/src/router/user/router.js:
--------------------------------------------------------------------------------
1 | import UserList from '@/components/user/list/app';
2 | import UserDetail from '@/components/user/detail/app';
3 | import UserSettings from '@/components/user/settings/app';
4 |
5 | const Router = [
6 | {
7 | path: '/user',
8 | name: 'User',
9 | component: UserList,
10 | },
11 | {
12 | path: '/user/settings',
13 | name: 'UserSettings',
14 | component: UserSettings,
15 | meta: {
16 | requireAuth: true,
17 | },
18 | },
19 | {
20 | path: '/user/:username',
21 | name: 'UserDetail',
22 | component: UserDetail,
23 | props: true,
24 | },
25 | ];
26 |
27 |
28 | export default Router;
29 |
--------------------------------------------------------------------------------
/src/router/utils.js:
--------------------------------------------------------------------------------
1 | import router from '@/router/index';
2 | import store from '@/store';
3 |
4 | const goBack = () => (window.history.length > 1 ? router.go(-1) : router.push('/'));
5 |
6 | const goHome = () => (router.push('/'));
7 |
8 | const isAuthenticated = () => store.getters['user/isAuthenticated'];
9 |
10 | const hasPermission = permission => store.getters['user/hasPermission'](permission);
11 |
12 | export {
13 | goHome, goBack, isAuthenticated, hasPermission,
14 | };
15 |
--------------------------------------------------------------------------------
/src/store/actions.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lutece-awesome/lutece-frontend/27126931178a87e2a73e209b476b0c96517fc09e/src/store/actions.js
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Vuex from 'vuex';
3 | import user from './modules/user';
4 | import snackbar from './modules/snackbar';
5 | import navbar from './modules/navbar';
6 | import footer from './modules/footer';
7 | import editor from './modules/editor';
8 |
9 |
10 | Vue.use(Vuex);
11 |
12 | export default new Vuex.Store({
13 | modules: {
14 | user,
15 | snackbar,
16 | navbar,
17 | footer,
18 | editor,
19 | },
20 | });
21 |
--------------------------------------------------------------------------------
/src/store/modules/editor.js:
--------------------------------------------------------------------------------
1 | /* eslint no-shadow: ["error", { "allow": ["state"] }] */
2 |
3 | import Language from '@/modules/language/main';
4 | import Keymap from '@/modules/code-mirror/keymap';
5 | import Theme from '@/modules/code-mirror/theme';
6 |
7 | export const state = () => ({
8 | currentLanguage: Language.valueOf(localStorage.getItem('editor_language')),
9 | importLanguage: null,
10 | currentKeymap: Keymap.valueOf(localStorage.getItem('editor_keymap')),
11 | importKeymap: null,
12 | currentTheme: Theme.valueOf(localStorage.getItem('editor_theme')),
13 | importTheme: null,
14 | });
15 |
16 | export const mutations = {
17 | setCurrentLanguage(state, lang) {
18 | localStorage.setItem('editor_language', lang.full);
19 | state.currentLanguage = lang;
20 | },
21 | setImportLanguage(state, lang) {
22 | if (state.currentLanguage === lang) {
23 | state.importLanguage = lang;
24 | }
25 | },
26 | setCurrentKeymap(state, keymap) {
27 | localStorage.setItem('editor_keymap', keymap.name);
28 | state.currentKeymap = keymap;
29 | },
30 | setImportKeymap(state, keymap) {
31 | if (state.currentKeymap === keymap) {
32 | state.importKeymap = keymap;
33 | }
34 | },
35 | setCurrentTheme(state, theme) {
36 | localStorage.setItem('editor_theme', theme.name);
37 | state.currentTheme = theme;
38 | },
39 | setImportTheme(state, theme) {
40 | if (state.currentTheme === theme) {
41 | state.importTheme = theme;
42 | }
43 | },
44 | };
45 |
46 | const getters = {
47 | currentLanguage: state => state.currentLanguage,
48 | importLanguage: state => state.importLanguage,
49 | currentKeymap: state => state.currentKeymap,
50 | importKeymap: state => state.importKeymap,
51 | currentTheme: state => state.currentTheme,
52 | importTheme: state => state.importTheme,
53 | };
54 |
55 | const actions = {
56 | updateLanguage({ commit }, lang) {
57 | commit('setCurrentLanguage', lang);
58 | lang.codeMirrorImport().then(() => {
59 | commit('setImportLanguage', lang);
60 | });
61 | },
62 | updateKeymap({ commit }, keymap) {
63 | commit('setCurrentKeymap', keymap);
64 | keymap.import().then(() => {
65 | commit('setImportKeymap', keymap);
66 | });
67 | },
68 | updateTheme({ commit }, theme) {
69 | commit('setCurrentTheme', theme);
70 | theme.import().then(() => {
71 | commit('setImportTheme', theme);
72 | });
73 | },
74 | };
75 |
76 | export default {
77 | namespaced: true,
78 | state,
79 | actions,
80 | getters,
81 | mutations,
82 | };
83 |
--------------------------------------------------------------------------------
/src/store/modules/footer.js:
--------------------------------------------------------------------------------
1 | /* eslint no-shadow: ["error", { "allow": ["state"] }] */
2 |
3 | export const state = () => ({
4 | visible: true,
5 | });
6 |
7 | export const mutations = {
8 | setVisible(state, visible) {
9 | state.visible = (visible === true);
10 | },
11 | };
12 |
13 | const getters = {
14 | visible: state => state.visible,
15 | };
16 |
17 | const actions = {
18 | setVisible({ flag, commit }) {
19 | commit('setVisible', flag);
20 | },
21 | };
22 |
23 | export default {
24 | namespaced: true,
25 | state,
26 | actions,
27 | getters,
28 | mutations,
29 | };
30 |
--------------------------------------------------------------------------------
/src/store/modules/navbar.js:
--------------------------------------------------------------------------------
1 | /* eslint no-shadow: ["error", { "allow": ["state"] }] */
2 |
3 | export const state = () => ({
4 | visible: 0,
5 | title: 'Lutece',
6 | });
7 |
8 | export const mutations = {
9 | setProgressVisible(state, visible) {
10 | if (visible === true) {
11 | state.visible += 1;
12 | } else if (visible === false) {
13 | state.visible -= 1;
14 | }
15 | },
16 | setTitle(state, title) {
17 | state.title = title || 'Lutece';
18 | },
19 | };
20 |
21 | const getters = {
22 | progressVisible: state => (state.visible > 0),
23 | title: state => state.title,
24 | };
25 |
26 | export default {
27 | namespaced: true,
28 | state,
29 | getters,
30 | mutations,
31 | };
32 |
--------------------------------------------------------------------------------
/src/store/modules/snackbar.js:
--------------------------------------------------------------------------------
1 | /* eslint no-shadow: ["error", { "allow": ["state"] }] */
2 |
3 | export const state = () => ({
4 | snack: '',
5 | });
6 |
7 | export const mutations = {
8 | setSnack(state, snack) {
9 | state.snack = snack;
10 | },
11 | };
12 |
13 | export default {
14 | namespaced: true,
15 | state,
16 | mutations,
17 | };
18 |
--------------------------------------------------------------------------------
/src/store/mutations.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lutece-awesome/lutece-frontend/27126931178a87e2a73e209b476b0c96517fc09e/src/store/mutations.js
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | import apolloProvider from '@/plugins/essential/apollo-provider';
2 |
3 | function getServerUri(protocol, path) {
4 | const env = process.env.NODE_ENV;
5 | const loc = window.location;
6 | const host = env === 'production' ? loc.host : `${loc.host.split(':')[0]}:8000`;
7 | let newUri = protocol;
8 | if (loc.protocol === 'https:') {
9 | newUri += 's:';
10 | } else {
11 | newUri += ':';
12 | }
13 | // newUri += ':';
14 | // const socket_ip_port = '127.0.0.1:8001';
15 | // newUri += `//${socket_ip_port}/${path}`;
16 | newUri += `//${host}/${path}`;
17 | return newUri;
18 | }
19 |
20 | function getGraphQLUri() {
21 | return getServerUri('http', 'graphql');
22 | }
23 |
24 | function getWebSocketUri() {
25 | return getServerUri('ws', 'ws');
26 | }
27 |
28 | function getThoundNumberic(number) {
29 | return number < 1000 ? String(number) : `${String((number / 1000).toFixed(1))}K`;
30 | }
31 |
32 | function formatRank(rk) {
33 | let s = String(rk);
34 | if (s.length < 2) s = `0${s}`;
35 | return s;
36 | }
37 |
38 | function clearApolloCache() {
39 | return apolloProvider.defaultClient.resetStore();
40 | }
41 |
42 | // To use this function gain the specific fied error message, the catch error must be
43 | // parsing result of function parseGraphqlError.
44 | function getErrorMessage(error, field) {
45 | if (error && Object.prototype.hasOwnProperty.call(error, field)) {
46 | return error[field][0].message;
47 | }
48 | return '';
49 | }
50 |
51 | function parseGraphqlError(error) {
52 | return JSON.parse(error.graphQLErrors[0].message);
53 | }
54 |
55 | const { version } = require('../package.json');
56 |
57 | export {
58 | getGraphQLUri, getWebSocketUri, getServerUri, getThoundNumberic,
59 | formatRank, version, clearApolloCache, getErrorMessage, parseGraphqlError,
60 | };
61 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 | const FontminPlugin = require('fontmin-webpack');
3 | const PurgecssPlugin = require('purgecss-webpack-plugin');
4 | const path = require('path');
5 | const glob = require('glob-all');
6 |
7 | class MyFontminPlugin extends FontminPlugin {
8 | findFontFiles(compilation) {
9 | const regular = this.findRegularFontFiles(compilation);
10 | const extract = this.findExtractTextFontFiles(compilation);
11 | return _.filter(
12 | _.uniqBy(regular.concat(extract), 'asset'),
13 | o => !o.asset.includes('KaTeX'),
14 | );
15 | }
16 |
17 | apply(compiler) {
18 | compiler.hooks.compilation.tap('Fontmin', (compilation) => {
19 | compilation.hooks.additionalAssets.tap('Fontmin', () => {
20 | this.onAdditionalAssets(compilation, () => {});
21 | });
22 | });
23 | }
24 | }
25 |
26 | module.exports = {
27 | pwa: {
28 | name: 'Lutece',
29 | manifestPath: 'static/icons/manifest.json',
30 | iconPaths: {
31 | favicon32: 'static/icons/favicon-32x32.png',
32 | favicon16: 'static/icons/favicon-16x16.png',
33 | appleTouchIcon: 'static/icons/apple-touch-icon-152x152.png',
34 | maskIcon: 'static/icons/safari-pinned-tab.svg',
35 | msTileImage: 'static/icons/msapplication-icon-144x144.png',
36 | },
37 | themeColor: '#1E88E5',
38 | msTileColor: '#2B5797',
39 | appleMobileWebAppCapable: 'yes',
40 | appleMobileWebAppStatusBarStyle: 'black',
41 | workboxOptions: {
42 | swDest: 'static/js/service-worker.js',
43 | importsDirectory: 'static/js/',
44 | },
45 | },
46 |
47 | baseUrl: undefined,
48 | outputDir: undefined,
49 | assetsDir: 'static',
50 | runtimeCompiler: undefined,
51 | productionSourceMap: undefined,
52 | parallel: undefined,
53 | css: undefined,
54 | chainWebpack: (_config) => {
55 | _config.module
56 | .rule('md')
57 | .test(/\.md$/)
58 | .use('raw-loader')
59 | .loader('raw-loader')
60 | .end();
61 | _config.when(process.env.NODE_ENV === 'production', (config) => {
62 | config.plugins
63 | .delete('prefetch')
64 | .end()
65 | .plugin('purgecss')
66 | .use(PurgecssPlugin, [
67 | {
68 | paths: glob.sync([
69 | path.join(__dirname, './index.html'),
70 | path.join(__dirname, './src/**/*.vue'),
71 | path.join(__dirname, './src/**/*.js'),
72 | path.join(
73 | __dirname,
74 | 'node_modules',
75 | 'vuetify',
76 | 'src',
77 | '**/*.@(js|ts)',
78 | ),
79 | ]),
80 | whitelistPatterns: [/^(?!mdi)/, /^mdi-alpha-./],
81 | },
82 | ])
83 | .after('extract-css')
84 | .end()
85 | .plugin('fontmin')
86 | .use(MyFontminPlugin)
87 | .end();
88 | });
89 | },
90 | };
91 |
--------------------------------------------------------------------------------