├── .dockerignore ├── .editorconfig ├── .env.development ├── .env.production ├── .env.staging ├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ ├── dev.yml │ └── prod.yml ├── .gitignore ├── .idea ├── .gitignore ├── inspectionProfiles │ └── Project_Default.xml ├── modules.xml ├── service-frontend.iml └── vcs.xml ├── .travis.yml ├── Dockerfile_dev ├── Dockerfile_prod ├── LICENSE ├── README.es.md ├── README.ja.md ├── README.md ├── README.zh-CN.md ├── babel.config.js ├── build └── index.js ├── jest.config.js ├── jsconfig.json ├── nginx_dev.conf ├── nginx_prod.conf ├── package-lock.json ├── package.json ├── plopfile.js ├── postcss.config.js ├── public ├── favicon.ico ├── index.html ├── logo.jpg ├── logo.svg ├── logo02.svg ├── logo03.svg ├── logo04.svg ├── logo05.svg ├── logo06.svg ├── logo07.svg ├── logo08.svg ├── logo2.svg └── logo3.png ├── src ├── App.vue ├── api │ ├── admin.js │ ├── client.ts │ ├── draft.js │ ├── forum.js │ ├── hot_list.js │ ├── issue.js │ ├── issue_connect.js │ ├── notify.js │ ├── send_email.js │ ├── statistics.js │ ├── subject.js │ ├── tag.js │ ├── upload.js │ ├── user.js │ └── year.js ├── assets │ ├── 401_images │ │ └── 401.gif │ ├── 404_images │ │ ├── 404.png │ │ └── 404_cloud.png │ ├── custom-theme │ │ ├── fonts │ │ │ ├── element-icons.ttf │ │ │ └── element-icons.woff │ │ └── index.css │ └── images │ │ ├── anonymous.jpg │ │ ├── login_background.jpg │ │ └── login_cover_image.png ├── components │ ├── BackToTop │ │ └── index.vue │ ├── Breadcrumb │ │ └── index.vue │ ├── Charts │ │ ├── Keyboard.vue │ │ ├── LineMarker.vue │ │ ├── MixChart.vue │ │ └── mixins │ │ │ └── resize.js │ ├── Comment │ │ └── TextBox.vue │ ├── DndList │ │ └── index.vue │ ├── DragSelect │ │ └── index.vue │ ├── Dropzone │ │ └── index.vue │ ├── ErrorLog │ │ └── index.vue │ ├── GithubCorner │ │ └── index.vue │ ├── Hamburger │ │ └── index.vue │ ├── HeaderSearch │ │ └── index.vue │ ├── ImageCropper │ │ ├── index.vue │ │ └── utils │ │ │ ├── data2blob.js │ │ │ ├── effectRipple.js │ │ │ ├── language.js │ │ │ └── mimes.js │ ├── JsonEditor │ │ └── index.vue │ ├── Kanban │ │ └── index.vue │ ├── MDeditor │ │ ├── MarkdownDisplay.vue │ │ └── MdEditor.vue │ ├── MDinput │ │ └── index.vue │ ├── MarkdownEditor │ │ ├── default-options.js │ │ └── index.vue │ ├── Notify │ │ ├── NotifyDialog.vue │ │ └── TextBox.vue │ ├── Pagination │ │ └── index.vue │ ├── PanThumb │ │ └── index.vue │ ├── RelateIssue │ │ ├── AddRelateDialog.vue │ │ └── RelateDialog.vue │ ├── RightPanel │ │ └── index.vue │ ├── Screenfull │ │ └── index.vue │ ├── Share │ │ └── DropdownMenu.vue │ ├── SizeSelect │ │ └── index.vue │ ├── Sticky │ │ └── index.vue │ ├── Suggest │ │ └── TextBox.vue │ ├── SvgIcon │ │ └── index.vue │ ├── TextHoverEffect │ │ └── Mallki.vue │ ├── ThemePicker │ │ └── index.vue │ ├── Tinymce │ │ ├── components │ │ │ └── EditorImage.vue │ │ ├── dynamicLoadScript.js │ │ ├── index.vue │ │ ├── plugins.js │ │ └── toolbar.js │ ├── Upload │ │ ├── SingleImage.vue │ │ ├── SingleImage2.vue │ │ └── SingleImage3.vue │ └── UploadExcel │ │ └── index.vue ├── directive │ ├── clipboard │ │ ├── clipboard.js │ │ └── index.js │ ├── el-drag-dialog │ │ ├── drag.js │ │ └── index.js │ ├── el-table │ │ ├── adaptive.js │ │ └── index.js │ ├── permission │ │ ├── index.js │ │ └── permission.js │ ├── sticky.js │ └── waves │ │ ├── index.js │ │ ├── waves.css │ │ └── waves.js ├── filters │ └── index.js ├── icons │ ├── index.js │ ├── svg │ │ ├── 404.svg │ │ ├── bug.svg │ │ ├── chart.svg │ │ ├── clipboard.svg │ │ ├── component.svg │ │ ├── dashboard.svg │ │ ├── documentation.svg │ │ ├── drag.svg │ │ ├── edit.svg │ │ ├── education.svg │ │ ├── email.svg │ │ ├── example.svg │ │ ├── excel.svg │ │ ├── exit-fullscreen.svg │ │ ├── eye-open.svg │ │ ├── eye.svg │ │ ├── form.svg │ │ ├── fullscreen.svg │ │ ├── guide.svg │ │ ├── icon.svg │ │ ├── international.svg │ │ ├── language.svg │ │ ├── link.svg │ │ ├── list.svg │ │ ├── lock.svg │ │ ├── message.svg │ │ ├── money.svg │ │ ├── nested.svg │ │ ├── password.svg │ │ ├── pdf.svg │ │ ├── people.svg │ │ ├── peoples.svg │ │ ├── qq.svg │ │ ├── search.svg │ │ ├── shopping.svg │ │ ├── size.svg │ │ ├── skill.svg │ │ ├── star.svg │ │ ├── tab.svg │ │ ├── table.svg │ │ ├── theme.svg │ │ ├── tree-table.svg │ │ ├── tree.svg │ │ ├── user.svg │ │ ├── wechat.svg │ │ └── zip.svg │ └── svgo.yml ├── layout │ ├── components │ │ ├── AppMain.vue │ │ ├── Navbar.vue │ │ ├── Settings │ │ │ └── index.vue │ │ ├── Sidebar │ │ │ ├── ASidebarItem.vue │ │ │ ├── FixiOSBug.js │ │ │ ├── Item.vue │ │ │ ├── Link.vue │ │ │ ├── Logo.vue │ │ │ ├── SidebarItem.vue │ │ │ └── index.vue │ │ ├── TagsView │ │ │ ├── ScrollPane.vue │ │ │ └── index.vue │ │ └── index.js │ ├── index.vue │ └── mixin │ │ └── ResizeHandler.js ├── main.js ├── permission.js ├── plugins │ └── vuetify.js ├── router │ ├── index.js │ └── modules │ │ ├── charts.js │ │ ├── components.js │ │ ├── nested.js │ │ └── table.js ├── settings.js ├── store │ ├── getters.js │ ├── index.js │ └── modules │ │ ├── app.js │ │ ├── errorLog.js │ │ ├── permission.js │ │ ├── settings.js │ │ ├── tagsView.js │ │ └── user.js ├── styles │ ├── btn.scss │ ├── element-ui.scss │ ├── element-variables.scss │ ├── index.scss │ ├── mixin.scss │ ├── sidebar.scss │ ├── transition.scss │ └── variables.scss ├── utils │ ├── auth.js │ ├── bus 2.js │ ├── bus.js │ ├── clipboard.js │ ├── error-log.js │ ├── file.js │ ├── get-page-title.js │ ├── index.js │ ├── open-window.js │ ├── permission.js │ ├── request.js │ ├── scroll-to.js │ └── validate.js ├── vendor │ ├── Export2Excel.js │ └── Export2Zip.js └── views │ ├── error-log │ ├── components │ │ ├── ErrorTestA.vue │ │ └── ErrorTestB.vue │ └── index.vue │ ├── error-page │ ├── 401.vue │ └── 404.vue │ ├── icons │ ├── element-icons.js │ ├── index.vue │ └── svg-icons.js │ ├── issueInfo │ ├── components │ │ ├── Confirm.vue │ │ └── MyRichText.vue │ └── issueInfoDetail.vue │ ├── login │ ├── auth-redirect.vue │ ├── components │ │ ├── SocialSignin.vue │ │ ├── captcha.vue │ │ └── registerPanel.vue │ └── index.vue │ ├── postIssue │ ├── components │ │ └── postIssue.vue │ └── index.vue │ ├── redirect │ └── index.vue │ ├── searchIssue │ ├── components │ │ ├── issueItem.vue │ │ ├── topIssue.vue │ │ └── topUser.vue │ └── index.vue │ ├── userInfo │ ├── userInfo.vue │ └── userIssue.vue │ └── userManage │ ├── bonusManage.vue │ ├── labelManage.vue │ ├── labelView.vue │ ├── postMessage.vue │ ├── tutorManage.vue │ ├── userCreate.vue │ ├── userList.vue │ ├── waldon.json │ └── workStat.vue ├── tests └── unit │ ├── .eslintrc.js │ ├── components │ ├── Hamburger.spec.js │ └── SvgIcon.spec.js │ └── utils │ ├── formatTime.spec.js │ ├── param2Obj.spec.js │ ├── parseTime.spec.js │ └── validate.spec.js ├── vue-element-admin.iml └── vue.config.js /.dockerignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/dist -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | insert_final_newline = false 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'development' 3 | 4 | # base api 5 | VUE_APP_BASE_API = '/dev-api' 6 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'production' 3 | 4 | # base api 5 | VUE_APP_BASE_API = '/prod-api' 6 | 7 | -------------------------------------------------------------------------------- /.env.staging: -------------------------------------------------------------------------------- 1 | NODE_ENV = production 2 | 3 | # just a flag 4 | ENV = 'staging' 5 | 6 | # base api 7 | VUE_APP_BASE_API = '/stage-api' 8 | 9 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | src/assets 3 | public 4 | dist 5 | src 6 | -------------------------------------------------------------------------------- /.github/workflows/dev.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy to Tencent Cloud 2 | on: 3 | push: 4 | branches: 5 | - dev 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@v2 12 | - name: Set up Docker Buildx 13 | uses: docker/setup-buildx-action@v1 14 | - name: Login to DockerHub 15 | uses: docker/login-action@v1 16 | with: 17 | username: ${{ secrets.DOCKER_HUB_USERNAME }} 18 | password: ${{ secrets.DOCKER_HUB_PASSWORD }} 19 | - name: Build and push Docker image 20 | uses: docker/build-push-action@v2 21 | with: 22 | file: Dockerfile_dev 23 | context: . 24 | push: true 25 | tags: ${{ secrets.DOCKER_HUB_USERNAME }}/${{ secrets.DOCKER_HUB_REPO_NAME }}:latest 26 | # tags: your-dockerhub-username/your-repo-name:latest 27 | 28 | - name: Deploy to Tencent Cloud server 29 | # 这是最后一个步骤使用的GitHub Action,这里使用appleboy/ssh-action@master来通过SSH连接到腾讯云服务器并执行命令 30 | uses: appleboy/ssh-action@master 31 | # 这是一些步骤需要的参数,这里需要提供腾讯云服务器的IP地址,SSH密钥,以及要执行的命令 32 | with: 33 | host: ${{ secrets.CLOUD_HOST}} 34 | username: ${{ secrets.CLOUD_USERNAME }} 35 | password: ${{ secrets.CLOUD_PASSWORD }} 36 | port: ${{ secrets.CLOUD_PORT }} 37 | script: | 38 | sudo docker pull ${{ secrets.DOCKER_HUB_USERNAME }}/${{ secrets.DOCKER_HUB_REPO_NAME }}:latest # 这是一个命令,用于从DockerHub拉取构建好的镜像 39 | sudo docker stop ${{ secrets.DOCKER_HUB_REPO_NAME }} || true # 这是一个命令,用于停止已经运行的容器,如果没有则忽略错误 40 | sudo docker rm ${{ secrets.DOCKER_HUB_REPO_NAME }} || true # 这是一个命令,用于删除已经停止的容器,如果没有则忽略错误 41 | sudo docker run -d --name ${{ secrets.DOCKER_HUB_REPO_NAME }} -p 9527:80 -p 9528:443 -v ${{ secrets.CLOUD_SSL}}:/usr/share/nginx/ssl/ ${{ secrets.DOCKER_HUB_USERNAME }}/${{ secrets.DOCKER_HUB_REPO_NAME }}:latest # 这是一个命令,用于运行新的容器,并将容器的80端口映射到服务器的9527端口,将容器的443端口映射到服务器的9528端口 -------------------------------------------------------------------------------- /.github/workflows/prod.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy to Tencent Cloud Production 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@v2 12 | - name: Set up Docker Buildx 13 | uses: docker/setup-buildx-action@v1 14 | - name: Login to DockerHub 15 | uses: docker/login-action@v1 16 | with: 17 | username: ${{ secrets.DOCKER_HUB_USERNAME }} 18 | password: ${{ secrets.DOCKER_HUB_PASSWORD }} 19 | - name: Build and push Docker image 20 | uses: docker/build-push-action@v2 21 | with: 22 | file: Dockerfile_prod 23 | context: . 24 | push: true 25 | tags: ${{ secrets.DOCKER_HUB_USERNAME }}/${{ secrets.DOCKER_HUB_REPO_PROD_NAME }}:latest 26 | # tags: your-dockerhub-username/your-repo-name:latest 27 | 28 | - name: Deploy to Tencent Cloud server 29 | # 这是最后一个步骤使用的GitHub Action,这里使用appleboy/ssh-action@master来通过SSH连接到腾讯云服务器并执行命令 30 | uses: appleboy/ssh-action@master 31 | # 这是一些步骤需要的参数,这里需要提供腾讯云服务器的IP地址,SSH密钥,以及要执行的命令 32 | with: 33 | host: ${{ secrets.CLOUD_HOST}} 34 | username: ${{ secrets.CLOUD_USERNAME }} 35 | password: ${{ secrets.CLOUD_PASSWORD }} 36 | port: ${{ secrets.CLOUD_PORT }} 37 | script: | 38 | sudo docker pull ${{ secrets.DOCKER_HUB_USERNAME }}/${{ secrets.DOCKER_HUB_REPO_PROD_NAME }}:latest # 这是一个命令,用于从DockerHub拉取构建好的镜像 39 | sudo docker stop ${{ secrets.DOCKER_HUB_REPO_PROD_NAME }} || true # 这是一个命令,用于停止已经运行的容器,如果没有则忽略错误 40 | sudo docker rm ${{ secrets.DOCKER_HUB_REPO_PROD_NAME }} || true # 这是一个命令,用于删除已经停止的容器,如果没有则忽略错误 41 | sudo docker run -d --name ${{ secrets.DOCKER_HUB_REPO_PROD_NAME }} -p 80:80 -p 443:443 -v ${{ secrets.CLOUD_PIC }}:/usr/share/nginx/html/pic/ -v ${{ secrets.CLOUD_SSL}}:/usr/share/nginx/ssl/ ${{ secrets.DOCKER_HUB_USERNAME }}/${{ secrets.DOCKER_HUB_REPO_PROD_NAME }}:latest # 这是一个命令,用于运行新的容器,并将容器的80端口映射到服务器的80端口,同时挂载静态资源访问 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | **/*.log 8 | 9 | tests/**/coverage/ 10 | tests/e2e/reports 11 | selenium-debug.log 12 | 13 | # Editor directories and files 14 | .idea 15 | .vscode 16 | *.suo 17 | *.ntvs* 18 | *.njsproj 19 | *.sln 20 | *.local 21 | 22 | package-lock.json 23 | yarn.lock 24 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 22 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/service-frontend.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 10 3 | script: npm run test 4 | notifications: 5 | email: false 6 | -------------------------------------------------------------------------------- /Dockerfile_dev: -------------------------------------------------------------------------------- 1 | FROM node:16 2 | WORKDIR /app 3 | COPY . . 4 | 5 | RUN npm install -g npm@9.6.4 \ 6 | && npm install \ 7 | && npm run build:prod \ 8 | && cd /usr/share/nginx/html ; mkdir pic 9 | 10 | FROM nginx 11 | RUN mkdir /app 12 | COPY --from=0 /app/dist /app 13 | COPY nginx_dev.conf /etc/nginx/nginx.conf 14 | -------------------------------------------------------------------------------- /Dockerfile_prod: -------------------------------------------------------------------------------- 1 | FROM node:16 2 | WORKDIR /app 3 | COPY . . 4 | 5 | RUN npm install -g npm@9.6.4 \ 6 | && npm install \ 7 | && npm run build:prod \ 8 | && cd /usr/share/nginx/html ; mkdir pic 9 | 10 | FROM nginx 11 | RUN mkdir /app 12 | COPY --from=0 /app/dist /app 13 | COPY nginx_prod.conf /etc/nginx/nginx.conf 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-present PanJiaChen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 初版文件框架 2 | 3 | - node_modules 已经引入了tiptap富文本编辑器插件以及上传图床插件,之后可以直接用 4 | 5 | - public 静态资源目录 6 | 7 | - src 8 | - api api接口(主要工作目录) 9 | - assets 全局资源目录 10 | - components 原框架的一些组件,之后不复用的话可以删掉(主要工作目录) 11 | - directive 原模版用到了一些,删了会引发一些报错,不删不影响 12 | - filters 原模版用到了一些,删了会引发一些报错,不删不影响 13 | - icons 14 | - layout 布局,侧边栏和导航栏的组件都在这里(主要工作目录) 15 | - plugins vuetify插件 16 | - router 路由(主要工作目录),原模版利用permission对权限和路由做了映射。在这里添加路由可以直接显示到侧边栏,不同的权限看到的侧边栏路由不一样。 17 | - store 状态管理(主要工作目录) 18 | - user.js 与用户权限设置有关 19 | - 其他的暂时不是特别重要,是原模版的内容,之后不用的话可以删掉 20 | - styles 样式 21 | - utills 一些js工具函数,原模版提供,之后不用可以删掉,内部有一些用户权限相关函数不要删,模版的权限管理用到了这些函数。 22 | - vendor 模版提供的导出excel,zip函数,之后学习资料下载或者导出志愿时长或许可以用到,没有删 23 | - views(主要工作目录) 24 | - management 后台管理页面,内部有关于图床的操作,后续可以参考,没有删 25 | - login 登陆注册页面,没有删除,相关权限管理可以参考 26 | - 其余是模版自带的跳转页面,error404页面 27 | - permission.js 权限管理操作,可以参考也可以沿用 28 | - test 原模版的单元测试文件目录,之后可以参考 29 | - 最后是模版一些介绍md文档,可以参考一下 30 | 31 | PS:一些logo和配色暂时还是之前项目的,之后可以更换 32 | 33 | 34 | 35 | 运行前:npm install 36 | 37 | 运行:npm run dev -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | // https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app 4 | '@vue/cli-plugin-babel/preset' 5 | ], 6 | 'env': { 7 | 'development': { 8 | // babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require(). 9 | // This plugin can significantly increase the speed of hot updates, when you have a large number of pages. 10 | // https://panjiachen.github.io/vue-element-admin-site/guide/advanced/lazy-loading.html 11 | 'plugins': [ 12 | 'dynamic-import-node' 13 | ] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /build/index.js: -------------------------------------------------------------------------------- 1 | const { run } = require('runjs') 2 | const chalk = require('chalk') 3 | const config = require('../vue.config.js') 4 | const rawArgv = process.argv.slice(2) 5 | const args = rawArgv.join(' ') 6 | 7 | if (process.env.npm_config_preview || rawArgv.includes('--preview')) { 8 | const report = rawArgv.includes('--report') 9 | 10 | run(`vue-cli-service build ${args}`) 11 | 12 | const port = 9526 13 | const publicPath = config.publicPath 14 | 15 | var connect = require('connect') 16 | var serveStatic = require('serve-static') 17 | const app = connect() 18 | 19 | app.use( 20 | publicPath, 21 | serveStatic('./dist', { 22 | index: ['index.html', '/'] 23 | }) 24 | ) 25 | 26 | app.listen(port, function () { 27 | console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`)) 28 | if (report) { 29 | console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`)) 30 | } 31 | 32 | }) 33 | } else { 34 | run(`vue-cli-service build ${args}`) 35 | } 36 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: ['js', 'jsx', 'json', 'vue'], 3 | transform: { 4 | '^.+\\.vue$': 'vue-jest', 5 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 6 | 'jest-transform-stub', 7 | '^.+\\.jsx?$': 'babel-jest' 8 | }, 9 | moduleNameMapper: { 10 | '^@/(.*)$': '/src/$1' 11 | }, 12 | snapshotSerializers: ['jest-serializer-vue'], 13 | testMatch: [ 14 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' 15 | ], 16 | collectCoverageFrom: ['src/utils/**/*.{js,vue}', '!src/utils/auth.js', '!src/utils/request.js', 'src/components/**/*.{js,vue}'], 17 | coverageDirectory: '/tests/unit/coverage', 18 | // 'collectCoverage': true, 19 | 'coverageReporters': [ 20 | 'lcov', 21 | 'text-summary' 22 | ], 23 | testURL: 'http://localhost/' 24 | } 25 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "paths": { 5 | "@/*": ["src/*"] 6 | } 7 | }, 8 | "exclude": ["node_modules", "dist"] 9 | } -------------------------------------------------------------------------------- /nginx_dev.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes 1; 3 | error_log /var/log/nginx/error.log warn; 4 | pid /var/run/nginx.pid; 5 | events { 6 | worker_connections 1024; 7 | } 8 | http { 9 | include /etc/nginx/mime.types; 10 | default_type application/octet-stream; 11 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 12 | '$status $body_bytes_sent "$http_referer" ' 13 | '"$http_user_agent" "$http_x_forwarded_for"'; 14 | access_log /var/log/nginx/access.log main; 15 | sendfile on; 16 | keepalive_timeout 65; 17 | client_max_body_size 5m; 18 | server { 19 | # listen 80; 20 | listen 443 ssl; 21 | server_name localhost; 22 | # 证书文件的绝对路径 23 | ssl_certificate /usr/share/nginx/ssl/shieask.com_bundle.crt; 24 | # 私钥文件的绝对路径 25 | ssl_certificate_key /usr/share/nginx/ssl/shieask.com.key; 26 | ssl_session_timeout 5m; 27 | #请按照以下协议配置 28 | ssl_protocols TLSv1.2 TLSv1.3; 29 | #请按照以下套件配置,配置加密套件,写法遵循 openssl 标准。 30 | ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; 31 | ssl_prefer_server_ciphers on; 32 | 33 | location / { 34 | root /app; 35 | index index.html; 36 | try_files $uri $uri/ /index.html; 37 | } 38 | error_page 500 502 503 504 /50x.html; 39 | location = /50x.html { 40 | root /usr/share/nginx/html; 41 | } 42 | 43 | location /prod-api { 44 | 45 | rewrite ^/prod-api/(.*)$ /$1 break; 46 | 47 | proxy_pass http://shieask.com:8080; #后台接口地址 48 | 49 | proxy_set_header X-Real-IP $remote_addr; 50 | 51 | proxy_set_header X-Forwarded-For $remote_addr; 52 | 53 | } 54 | 55 | location /pic/ { 56 | alias /usr/share/nginx/html/pic/ ; 57 | } 58 | } 59 | 60 | server { 61 | listen 80; 62 | server_name localhost; 63 | return 301 https://$host$request_uri; 64 | } 65 | } -------------------------------------------------------------------------------- /nginx_prod.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes 1; 3 | error_log /var/log/nginx/error.log warn; 4 | pid /var/run/nginx.pid; 5 | events { 6 | worker_connections 1024; 7 | } 8 | http { 9 | include /etc/nginx/mime.types; 10 | default_type application/octet-stream; 11 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 12 | '$status $body_bytes_sent "$http_referer" ' 13 | '"$http_user_agent" "$http_x_forwarded_for"'; 14 | access_log /var/log/nginx/access.log main; 15 | sendfile on; 16 | keepalive_timeout 65; 17 | client_max_body_size 5m; 18 | server { 19 | # listen 80; 20 | listen 443 ssl; 21 | # server_name localhost; 22 | server_name shieask.com www.shieask.com; 23 | # 证书文件的绝对路径 24 | ssl_certificate /usr/share/nginx/ssl/shieask.com_bundle.crt; 25 | # 私钥文件的绝对路径 26 | ssl_certificate_key /usr/share/nginx/ssl/shieask.com.key; 27 | ssl_session_timeout 5m; 28 | #请按照以下协议配置 29 | ssl_protocols TLSv1.2 TLSv1.3; 30 | #请按照以下套件配置,配置加密套件,写法遵循 openssl 标准。 31 | ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; 32 | ssl_prefer_server_ciphers on; 33 | 34 | location / { 35 | root /app; 36 | index index.html; 37 | try_files $uri $uri/ /index.html; 38 | } 39 | error_page 500 502 503 504 /50x.html; 40 | location = /50x.html { 41 | root /usr/share/nginx/html; 42 | } 43 | 44 | location /prod-api { 45 | 46 | rewrite ^/prod-api/(.*)$ /$1 break; 47 | 48 | proxy_pass http://101.43.219.110:9001; #后端生产环境接口地址 49 | 50 | proxy_set_header X-Real-IP $remote_addr; 51 | 52 | proxy_set_header X-Forwarded-For $remote_addr; 53 | 54 | } 55 | 56 | location /pic/ { 57 | alias /usr/share/nginx/html/pic/ ; 58 | } 59 | } 60 | server { 61 | listen 80; 62 | server_name shieask.com www.shieask.com; 63 | return 301 https://$host$request_uri; 64 | } 65 | } -------------------------------------------------------------------------------- /plopfile.js: -------------------------------------------------------------------------------- 1 | const viewGenerator = require('./plop-templates/view/prompt') 2 | const componentGenerator = require('./plop-templates/component/prompt') 3 | const storeGenerator = require('./plop-templates/store/prompt.js') 4 | 5 | module.exports = function(plop) { 6 | plop.setGenerator('view', viewGenerator) 7 | plop.setGenerator('component', componentGenerator) 8 | plop.setGenerator('store', storeGenerator) 9 | } 10 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MOSS2023ASE/service-frontend/b9822931ce6f3c38fe25e873e96446bf4d65f28b/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | <%= webpackConfig.name %> 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /public/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MOSS2023ASE/service-frontend/b9822931ce6f3c38fe25e873e96446bf4d65f28b/public/logo.jpg -------------------------------------------------------------------------------- /public/logo3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MOSS2023ASE/service-frontend/b9822931ce6f3c38fe25e873e96446bf4d65f28b/public/logo3.png -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /src/api/admin.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | 4 | export function batch_register( 5 | jwt, //: string, 6 | name_list, //: Array, 7 | student_id_list, //: Array, 8 | password_list, //: Array 9 | role_list 10 | ) { 11 | return request({ 12 | url: '/admins/create_user_batch', 13 | method: 'post', 14 | data: { 15 | jwt: jwt, 16 | name_list: name_list, 17 | student_id_list: student_id_list, 18 | password_list: password_list, 19 | role_list: role_list 20 | } 21 | }) 22 | } 23 | 24 | export function update_privilege( 25 | jwt, //: string, 26 | user_id, //: number, 27 | user_role, //: number 28 | ) { 29 | return request({ 30 | url: '/admins/update_privilege', 31 | method: 'post', 32 | data: { 33 | jwt: jwt, 34 | user_id: user_id, 35 | user_role: user_role 36 | } 37 | }) 38 | } 39 | 40 | export function get_users( 41 | jwt //: string 42 | ) { 43 | return request({ 44 | url: '/admins/users', 45 | method: 'post', 46 | data: { 47 | jwt: jwt 48 | } 49 | }) 50 | } 51 | 52 | export function freeze_user( 53 | jwt, //: string, 54 | user_id, //: number, 55 | frozen, //: number 56 | ) { 57 | return request({ 58 | url: '/admins/freeze_user', 59 | method: 'post', 60 | data: { 61 | jwt: jwt, 62 | user_id: user_id, 63 | frozen: frozen 64 | } 65 | }) 66 | } 67 | 68 | export function delete_issue( 69 | jwt, //: string, 70 | issue_id, //: number 71 | ) { 72 | return request({ 73 | url: '/admins/issue/delete', 74 | method: 'post', 75 | data: { 76 | jwt: jwt, 77 | issue_id: issue_id 78 | } 79 | }) 80 | } 81 | 82 | export function single_register( 83 | jwt, //: string, 84 | name, //: string, 85 | student_id, //: string, 86 | password, //: string 87 | role 88 | ) { 89 | return request({ 90 | url: '/admins/create_user', 91 | method: 'post', 92 | data: { 93 | jwt: jwt, 94 | name: name, 95 | student_id: student_id, 96 | password: password, 97 | role: role 98 | } 99 | }) 100 | } 101 | -------------------------------------------------------------------------------- /src/api/client.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | // @ts-ignore 3 | import qs from 'qs' 4 | 5 | 6 | function DELETE(url: string, options?: any) { 7 | if (options?.data && 8 | options.headers?.['Content-Type'] !== 'multipart/form-data') 9 | options.data = qs.stringify(options.data, { arrayFormat: 'brackets' }) 10 | return axios({ method: 'DELETE', url, ...options }) 11 | } 12 | 13 | function GET(url: string, options?: any) { 14 | if (options?.data && 15 | options.headers?.['Content-Type'] !== 'multipart/form-data') 16 | options.data = qs.stringify(options.data, { arrayFormat: 'brackets' }) 17 | return axios({ method: 'GET', url, ...options }) 18 | } 19 | 20 | function POST(url: string, options?: any) { 21 | if (options?.data && 22 | options.headers?.['Content-Type'] !== 'multipart/form-data') 23 | options.data = qs.stringify(options.data, { arrayFormat: 'brackets' }) 24 | return axios({ method: 'POST', url, ...options }) 25 | } 26 | 27 | function PUT(url: string, options?: any) { 28 | if (options?.data) 29 | options.data = qs.stringify(options.data, { arrayFormat: 'brackets' }) 30 | return axios({ method: 'PUT', url, ...options }) 31 | } 32 | 33 | const API = axios.create({ 34 | baseURL:'http://localhost:3000', //请求后端数据的基本地址,自定义 35 | timeout: 2000 //请求超时设置,单位ms 36 | }) 37 | 38 | export default { DELETE, GET, POST, PUT, API } 39 | -------------------------------------------------------------------------------- /src/api/draft.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function save_draft( 4 | jwt, //: string 5 | chapter_id, //: number, 6 | title, //: string, 7 | content, //: string, 8 | anonymous, //: number 9 | ) { 10 | return request({ 11 | url: '/issue/save_draft', 12 | method: 'post', 13 | data: { 14 | jwt: jwt, 15 | chapter_id: chapter_id, 16 | title: title, 17 | content: content, 18 | anonymous: anonymous 19 | } 20 | }) 21 | } 22 | 23 | export function load_draft( 24 | jwt, //: string 25 | ) { 26 | return request({ 27 | url: '/issue/load_draft', 28 | method: 'post', 29 | data: { 30 | jwt: jwt, 31 | } 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /src/api/forum.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | 4 | export function get_issue_all_comments( 5 | jwt, //: string, 6 | issue_id //: number 7 | ) { 8 | return request({ 9 | url: '/issue/comments', 10 | method: 'post', 11 | data: { 12 | jwt: jwt, 13 | issue_id: issue_id 14 | } 15 | }) 16 | } 17 | 18 | export function delete_comment( 19 | jwt, //: string, 20 | comment_id //: number 21 | ) { 22 | return request({ 23 | url: '/issue/comment', 24 | method: 'delete', 25 | data: { 26 | jwt: jwt, 27 | comment_id: comment_id 28 | } 29 | }) 30 | } 31 | 32 | export function update_comment( 33 | jwt, //: string, 34 | comment_id, //: number, 35 | content //: string 36 | ) { 37 | return request({ 38 | url: '/issue/comment/update', 39 | method: 'post', 40 | data: { 41 | jwt: jwt, 42 | comment_id: comment_id, 43 | content: content 44 | } 45 | }) 46 | } 47 | 48 | export function create_comment( 49 | jwt, //: string, 50 | issue_id, //: number, 51 | content //: string 52 | ) { 53 | return request({ 54 | url: '/issue/comment/create', 55 | method: 'post', 56 | data: { 57 | jwt: jwt, 58 | issue_id: issue_id, 59 | content: content 60 | } 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /src/api/hot_list.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 已对接 4 | export function get_active_users( 5 | jwt, //: string 6 | top_k, //: number 7 | ) { 8 | return request({ 9 | url: '/user/active_users', 10 | method: 'post', 11 | data: { 12 | jwt: jwt, 13 | top_k: top_k, 14 | } 15 | }) 16 | } 17 | 18 | export function get_popular_issues( 19 | jwt, //: string 20 | top_k, //: number 21 | ) { 22 | return request({ 23 | url: '/user/get_popular_issue', 24 | method: 'post', 25 | data: { 26 | jwt: jwt, 27 | top_k: top_k 28 | } 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /src/api/issue_connect.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function add_association( 4 | jwt, //: string 5 | issue_ids, // : array 6 | issue_associate_id, // : number 7 | ) { 8 | return request({ 9 | url: '/issue/associate', 10 | method: 'post', 11 | data: { 12 | jwt: jwt, 13 | issue_id: issue_ids, 14 | issue_associate_id: issue_associate_id 15 | } 16 | }) 17 | } 18 | 19 | export function delete_association( 20 | jwt, //: string 21 | issue_id, // : number 22 | issue_associate_id, // : number 23 | ) { 24 | return request({ 25 | url: '/issue/associate/delete', 26 | method: 'post', 27 | data: { 28 | jwt: jwt, 29 | issue_id: issue_id, 30 | issue_associate_id: issue_associate_id 31 | } 32 | }) 33 | } 34 | 35 | export function get_association( 36 | jwt, //: string 37 | issue_id, // : number 38 | ) { 39 | return request({ 40 | url: '/issue/associate/get', 41 | method: 'post', 42 | data: { 43 | jwt: jwt, 44 | issue_id: issue_id, 45 | } 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /src/api/notify.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function clear_all_notification( 4 | jwt, //: string 5 | ) { 6 | return request({ 7 | url: '/notification/clear_all', 8 | method: 'post', 9 | data: { 10 | jwt: jwt 11 | } 12 | }) 13 | } 14 | 15 | export function read_one_notification( 16 | jwt, //: string 17 | id, 18 | ) { 19 | return request({ 20 | url: '/notification/get', 21 | method: 'post', 22 | data: { 23 | jwt: jwt, 24 | notification_id: id 25 | } 26 | }) 27 | } 28 | 29 | export function get_all_notification( 30 | jwt, //: string 31 | ) { 32 | return request({ 33 | url: '/notification/user_receive', 34 | method: 'post', 35 | data: { 36 | jwt: jwt, 37 | } 38 | }) 39 | } 40 | 41 | export function admin_broadcast( 42 | jwt, //: string 43 | title, //: string 44 | content, //: string 45 | category, //: number 46 | ) { 47 | return request({ 48 | url: '/notification/broadcast', 49 | method: 'post', 50 | data: { 51 | jwt: jwt, 52 | title: title, 53 | content: content, 54 | category: category 55 | } 56 | }) 57 | } 58 | -------------------------------------------------------------------------------- /src/api/send_email.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function send_mail( 4 | mail, // : string 5 | ) { 6 | return request({ 7 | url: '/mail/send', 8 | method: 'post', 9 | data: { 10 | mail: mail, 11 | } 12 | }) 13 | } 14 | 15 | export function confirm_mail( 16 | mail, // : string 17 | student_id, // : string 18 | password, // : string 19 | v_code, // : string 20 | ) { 21 | return request({ 22 | url: '/mail/confirm', 23 | method: 'post', 24 | data: { 25 | mail: mail, 26 | student_id: student_id, 27 | password: password, 28 | v_code: v_code 29 | } 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /src/api/statistics.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function get_statistics( 4 | jwt, //: string 5 | type, // : number 6 | indicator, // : number 7 | begin_date, // : string 8 | end_date, // : string 9 | ) { 10 | return request({ 11 | url: '/admins/statistics', 12 | method: 'post', 13 | data: { 14 | jwt: jwt, 15 | type: type, 16 | indicator: indicator, 17 | begin_date: begin_date, 18 | end_date: end_date 19 | } 20 | }) 21 | } 22 | 23 | export function get_markdown( 24 | jwt, //: string 25 | begin_date, // : string 26 | end_date, // : string 27 | ) { 28 | return request({ 29 | url: '/markdown', 30 | method: 'post', 31 | data: { 32 | jwt: jwt, 33 | begin_date: begin_date, 34 | end_date: end_date 35 | } 36 | }) 37 | } 38 | 39 | export function get_tutor_bonus( 40 | jwt, //: string 41 | bonus_per_counsel, //: float 42 | bonus_per_review, //: float 43 | begin_date, //: string 44 | end_date, //: string 45 | min_bonus, //: float 46 | max_bonus, //: float 47 | ) { 48 | return request({ 49 | url: '/admins/tutor_bonus', 50 | method: 'post', 51 | data: { 52 | jwt: jwt, 53 | bonus_per_counsel: bonus_per_counsel, 54 | bonus_per_review: bonus_per_review, 55 | begin_date: begin_date, 56 | end_date: end_date, 57 | min_bonus: min_bonus, 58 | max_bonus: max_bonus 59 | } 60 | }) 61 | } 62 | 63 | export function get_student_bonus( 64 | jwt, //: string 65 | bonus_per_issue, //: float 66 | begin_date, //: string 67 | end_date, //: string 68 | min_bonus, //: float 69 | max_bonus, //: float 70 | ) { 71 | return request({ 72 | url: '/admins/student_bonus', 73 | method: 'post', 74 | data: { 75 | jwt: jwt, 76 | bonus_per_issue: bonus_per_issue, 77 | begin_date: begin_date, 78 | end_date: end_date, 79 | min_bonus: min_bonus, 80 | max_bonus: max_bonus 81 | } 82 | }) 83 | } 84 | -------------------------------------------------------------------------------- /src/api/tag.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function update_tag( 4 | jwt, //: string, 5 | tag_id, //: number, 6 | content //: string 7 | ) { 8 | return request({ 9 | url: '/tag/update', 10 | method: 'post', 11 | data: { 12 | jwt: jwt, 13 | tag_id: tag_id, 14 | content: content 15 | } 16 | }) 17 | } 18 | 19 | export function delete_tag( 20 | jwt, //: string, 21 | tag_id //: number 22 | ) { 23 | return request({ 24 | url: '/tag/delete', 25 | method: 'delete', 26 | data: { 27 | jwt: jwt, 28 | tag_id: tag_id, 29 | } 30 | }) 31 | } 32 | 33 | export function create_tag( 34 | jwt, //: string, 35 | content //: string 36 | ) { 37 | return request({ 38 | url: '/tag/create', 39 | method: 'post', 40 | data: { 41 | jwt: jwt, 42 | content: content 43 | } 44 | }) 45 | } 46 | 47 | export function get_all_tags( 48 | jwt, //: string, 49 | ) { 50 | return request({ 51 | url: '/tag/', 52 | method: 'get', 53 | data: { 54 | jwt: jwt 55 | } 56 | }) 57 | } 58 | 59 | -------------------------------------------------------------------------------- /src/api/upload.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | 4 | export function upload_public( 5 | formData 6 | ) { 7 | return request({ 8 | url: '/images/upload/', 9 | method: 'post', 10 | data: formData, 11 | }) 12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/api/year.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function get_all_years( 4 | jwt, //: string 5 | ) { 6 | return request({ 7 | url: '/year/', 8 | method: 'get', 9 | data: { 10 | jwt: jwt, 11 | } 12 | }) 13 | } 14 | 15 | export function update_year_info( 16 | jwt, //: string 17 | year_id, //: number 18 | content, //: string 19 | ) { 20 | return request({ 21 | url: '/year/update', 22 | method: 'post', 23 | data: { 24 | jwt: jwt, 25 | year_id: year_id, 26 | content: content 27 | } 28 | }) 29 | } 30 | 31 | export function create_year( 32 | jwt, //: string 33 | content, //: string 34 | ) { 35 | return request({ 36 | url: '/year/create', 37 | method: 'post', 38 | data: { 39 | jwt: jwt, 40 | content: content 41 | } 42 | }) 43 | } 44 | 45 | export function update_current_year( 46 | jwt, //: string 47 | year_id, //: number 48 | ) { 49 | return request({ 50 | url: '/year/update_current', 51 | method: 'post', 52 | data: { 53 | jwt: jwt, 54 | year_id: year_id, 55 | } 56 | }) 57 | } 58 | -------------------------------------------------------------------------------- /src/assets/401_images/401.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MOSS2023ASE/service-frontend/b9822931ce6f3c38fe25e873e96446bf4d65f28b/src/assets/401_images/401.gif -------------------------------------------------------------------------------- /src/assets/404_images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MOSS2023ASE/service-frontend/b9822931ce6f3c38fe25e873e96446bf4d65f28b/src/assets/404_images/404.png -------------------------------------------------------------------------------- /src/assets/404_images/404_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MOSS2023ASE/service-frontend/b9822931ce6f3c38fe25e873e96446bf4d65f28b/src/assets/404_images/404_cloud.png -------------------------------------------------------------------------------- /src/assets/custom-theme/fonts/element-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MOSS2023ASE/service-frontend/b9822931ce6f3c38fe25e873e96446bf4d65f28b/src/assets/custom-theme/fonts/element-icons.ttf -------------------------------------------------------------------------------- /src/assets/custom-theme/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MOSS2023ASE/service-frontend/b9822931ce6f3c38fe25e873e96446bf4d65f28b/src/assets/custom-theme/fonts/element-icons.woff -------------------------------------------------------------------------------- /src/assets/images/anonymous.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MOSS2023ASE/service-frontend/b9822931ce6f3c38fe25e873e96446bf4d65f28b/src/assets/images/anonymous.jpg -------------------------------------------------------------------------------- /src/assets/images/login_background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MOSS2023ASE/service-frontend/b9822931ce6f3c38fe25e873e96446bf4d65f28b/src/assets/images/login_background.jpg -------------------------------------------------------------------------------- /src/assets/images/login_cover_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MOSS2023ASE/service-frontend/b9822931ce6f3c38fe25e873e96446bf4d65f28b/src/assets/images/login_cover_image.png -------------------------------------------------------------------------------- /src/components/Breadcrumb/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 69 | 70 | 83 | -------------------------------------------------------------------------------- /src/components/Charts/mixins/resize.js: -------------------------------------------------------------------------------- 1 | import { debounce } from '@/utils' 2 | 3 | export default { 4 | data() { 5 | return { 6 | $_sidebarElm: null, 7 | $_resizeHandler: null 8 | } 9 | }, 10 | mounted() { 11 | this.initListener() 12 | }, 13 | activated() { 14 | if (!this.$_resizeHandler) { 15 | // avoid duplication init 16 | this.initListener() 17 | } 18 | 19 | // when keep-alive chart activated, auto resize 20 | this.resize() 21 | }, 22 | beforeDestroy() { 23 | this.destroyListener() 24 | }, 25 | deactivated() { 26 | this.destroyListener() 27 | }, 28 | methods: { 29 | // use $_ for mixins properties 30 | // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential 31 | $_sidebarResizeHandler(e) { 32 | if (e.propertyName === 'width') { 33 | this.$_resizeHandler() 34 | } 35 | }, 36 | initListener() { 37 | this.$_resizeHandler = debounce(() => { 38 | this.resize() 39 | }, 100) 40 | window.addEventListener('resize', this.$_resizeHandler) 41 | 42 | this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0] 43 | this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler) 44 | }, 45 | destroyListener() { 46 | window.removeEventListener('resize', this.$_resizeHandler) 47 | this.$_resizeHandler = null 48 | 49 | this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler) 50 | }, 51 | resize() { 52 | const { chart } = this 53 | chart && chart.resize() 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/components/Comment/TextBox.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 108 | -------------------------------------------------------------------------------- /src/components/DragSelect/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 50 | 51 | 66 | -------------------------------------------------------------------------------- /src/components/ErrorLog/index.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 70 | 71 | 79 | -------------------------------------------------------------------------------- /src/components/GithubCorner/index.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 55 | -------------------------------------------------------------------------------- /src/components/Hamburger/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 32 | 33 | 45 | -------------------------------------------------------------------------------- /src/components/ImageCropper/utils/data2blob.js: -------------------------------------------------------------------------------- 1 | /** 2 | * database64文件格式转换为2进制 3 | * 4 | * @param {[String]} data dataURL 的格式为 “data:image/png;base64,****”,逗号之前都是一些说明性的文字,我们只需要逗号之后的就行了 5 | * @param {[String]} mime [description] 6 | * @return {[blob]} [description] 7 | */ 8 | export default function(data, mime) { 9 | data = data.split(',')[1] 10 | data = window.atob(data) 11 | var ia = new Uint8Array(data.length) 12 | for (var i = 0; i < data.length; i++) { 13 | ia[i] = data.charCodeAt(i) 14 | } 15 | // canvas.toDataURL 返回的默认格式就是 image/png 16 | return new Blob([ia], { 17 | type: mime 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /src/components/ImageCropper/utils/effectRipple.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 点击波纹效果 3 | * 4 | * @param {[event]} e [description] 5 | * @param {[Object]} arg_opts [description] 6 | * @return {[bollean]} [description] 7 | */ 8 | export default function(e, arg_opts) { 9 | var opts = Object.assign({ 10 | ele: e.target, // 波纹作用元素 11 | type: 'hit', // hit点击位置扩散center中心点扩展 12 | bgc: 'rgba(0, 0, 0, 0.15)' // 波纹颜色 13 | }, arg_opts) 14 | var target = opts.ele 15 | if (target) { 16 | var rect = target.getBoundingClientRect() 17 | var ripple = target.querySelector('.e-ripple') 18 | if (!ripple) { 19 | ripple = document.createElement('span') 20 | ripple.className = 'e-ripple' 21 | ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px' 22 | target.appendChild(ripple) 23 | } else { 24 | ripple.className = 'e-ripple' 25 | } 26 | switch (opts.type) { 27 | case 'center': 28 | ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px' 29 | ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px' 30 | break 31 | default: 32 | ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px' 33 | ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px' 34 | } 35 | ripple.style.backgroundColor = opts.bgc 36 | ripple.className = 'e-ripple z-active' 37 | return false 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/components/ImageCropper/utils/mimes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'jpg': 'image/jpeg', 3 | 'png': 'image/png', 4 | 'gif': 'image/gif', 5 | 'svg': 'image/svg+xml', 6 | 'psd': 'image/photoshop' 7 | } 8 | -------------------------------------------------------------------------------- /src/components/JsonEditor/index.vue: -------------------------------------------------------------------------------- 1 |