├── .prettierignore
├── .eslintignore
├── server
└── tsconfig.json
├── assets
├── .DS_Store
├── image
│ ├── gzh.jpg
│ ├── gzh.png
│ ├── phone.png
│ ├── common_head.jpg
│ └── common_login.png
├── svg
│ ├── account
│ │ ├── user.svg
│ │ ├── course.svg
│ │ ├── mobile.svg
│ │ ├── wechat.svg
│ │ ├── collect.svg
│ │ ├── password.svg
│ │ └── order.svg
│ ├── phone.svg
│ ├── comment.svg
│ ├── gzh.svg
│ └── chapter.svg
└── styles
│ └── main.scss
├── components
├── .DS_Store
├── Zone
│ ├── Banner.vue
│ ├── Course.vue
│ └── Category.vue
├── Course
│ ├── Collect.vue
│ ├── Choose.vue
│ ├── List.vue
│ ├── Comment.vue
│ └── Chapter.vue
├── Common
│ ├── Affix.vue
│ ├── Link.vue
│ ├── Pagination.vue
│ ├── User.vue
│ ├── Footer.vue
│ ├── Header.vue
│ └── Pay.vue
├── Lecturer
│ └── List.vue
└── Account
│ ├── Wechat.vue
│ └── Order.vue
├── public
└── favicon.ico
├── distribution
├── images
│ ├── gzh.png
│ ├── admin1.png
│ ├── admin2.png
│ ├── admin3.png
│ ├── admin4.png
│ ├── logo.jpg
│ ├── web1.png
│ ├── web2.png
│ ├── web3.png
│ └── web4.png
├── bin
│ └── web.sh
├── assembly.xml
└── pom.xml
├── tsconfig.json
├── plugins
├── error-handler.js
└── element-plus.js
├── app.vue
├── .env.production
├── api
├── upload.js
├── lecturer.js
├── index.js
├── login.js
├── user.js
└── course.js
├── .env.development
├── ecosystem.config.js
├── eslint.config.cjs
├── .prettierrc.json
├── middleware
└── router.global.js
├── .gitignore
├── layouts
├── default.vue
└── account.vue
├── utils
├── login.js
├── cookie.js
├── table.js
├── polyv.js
├── storage.js
├── request.js
└── base.js
├── pages
├── 404.vue
├── index.vue
├── account
│ ├── order.vue
│ ├── collect.vue
│ ├── course.vue
│ └── user.vue
├── lecturer
│ ├── list.vue
│ └── detail.vue
├── search.vue
├── course
│ ├── list.vue
│ ├── detail.vue
│ └── study.vue
├── register.vue
├── reset.vue
└── login.vue
├── zip.mjs
├── nuxt.config.ts
├── LICENSE.txt
├── package.json
└── README.md
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .prettierrc.json
4 |
--------------------------------------------------------------------------------
/server/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../.nuxt/tsconfig.server.json"
3 | }
4 |
--------------------------------------------------------------------------------
/assets/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roncoo/roncoo-education-web/HEAD/assets/.DS_Store
--------------------------------------------------------------------------------
/assets/image/gzh.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roncoo/roncoo-education-web/HEAD/assets/image/gzh.jpg
--------------------------------------------------------------------------------
/assets/image/gzh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roncoo/roncoo-education-web/HEAD/assets/image/gzh.png
--------------------------------------------------------------------------------
/components/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roncoo/roncoo-education-web/HEAD/components/.DS_Store
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roncoo/roncoo-education-web/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/assets/image/phone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roncoo/roncoo-education-web/HEAD/assets/image/phone.png
--------------------------------------------------------------------------------
/distribution/images/gzh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roncoo/roncoo-education-web/HEAD/distribution/images/gzh.png
--------------------------------------------------------------------------------
/assets/image/common_head.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roncoo/roncoo-education-web/HEAD/assets/image/common_head.jpg
--------------------------------------------------------------------------------
/assets/image/common_login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roncoo/roncoo-education-web/HEAD/assets/image/common_login.png
--------------------------------------------------------------------------------
/distribution/images/admin1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roncoo/roncoo-education-web/HEAD/distribution/images/admin1.png
--------------------------------------------------------------------------------
/distribution/images/admin2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roncoo/roncoo-education-web/HEAD/distribution/images/admin2.png
--------------------------------------------------------------------------------
/distribution/images/admin3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roncoo/roncoo-education-web/HEAD/distribution/images/admin3.png
--------------------------------------------------------------------------------
/distribution/images/admin4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roncoo/roncoo-education-web/HEAD/distribution/images/admin4.png
--------------------------------------------------------------------------------
/distribution/images/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roncoo/roncoo-education-web/HEAD/distribution/images/logo.jpg
--------------------------------------------------------------------------------
/distribution/images/web1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roncoo/roncoo-education-web/HEAD/distribution/images/web1.png
--------------------------------------------------------------------------------
/distribution/images/web2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roncoo/roncoo-education-web/HEAD/distribution/images/web2.png
--------------------------------------------------------------------------------
/distribution/images/web3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roncoo/roncoo-education-web/HEAD/distribution/images/web3.png
--------------------------------------------------------------------------------
/distribution/images/web4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roncoo/roncoo-education-web/HEAD/distribution/images/web4.png
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | // https://nuxt.com/docs/guide/concepts/typescript
3 | "extends": "./.nuxt/tsconfig.json"
4 | }
5 |
--------------------------------------------------------------------------------
/plugins/error-handler.js:
--------------------------------------------------------------------------------
1 | export default defineNuxtPlugin((nuxtApp) => {
2 | nuxtApp.hook('vue:error', (err) => {
3 | console.error(err)
4 | // 这里可以记录错误日志
5 | })
6 | })
7 |
--------------------------------------------------------------------------------
/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
--------------------------------------------------------------------------------
/.env.production:
--------------------------------------------------------------------------------
1 | # 生产环境
2 | NODE_ENV = production
3 | # 后端接口地址
4 | VITE_BASE_URL = 'http://localhost/gateway'
5 | # 是否删除console
6 | VITE_DROP_CONSOLE = true
7 | # 是否删除debugger
8 | VITE_DROP_DEBUGGER = true
9 |
--------------------------------------------------------------------------------
/api/upload.js:
--------------------------------------------------------------------------------
1 | import { upload } from '@/utils/request'
2 |
3 | export const uploadApi = {
4 | // 上传图片
5 | uploadPic: (file) => {
6 | return upload('/system/auth/upload/pic', file)
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/.env.development:
--------------------------------------------------------------------------------
1 | # 开发环境
2 | NODE_ENV = development
3 | # 后端接口地址
4 | VITE_BASE_URL = 'http://localhost:7700'
5 | # 是否删除console
6 | VITE_DROP_CONSOLE = false
7 | # 是否删除debugger
8 | VITE_DROP_DEBUGGER = false
9 |
--------------------------------------------------------------------------------
/ecosystem.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | apps: [
3 | {
4 | name: 'web', // 名称
5 | script: '.output/server/index.mjs',
6 | instances: '1' // 进程数,若为:max,指当前服务器cpu核数
7 | }
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/eslint.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | rules: {
3 | 'linebreak-style': 0,
4 | 'space-before-function-paren': 0,
5 | 'vue/no-multiple-template-root': 'off',
6 | 'vue/multi-word-component-names': 'off'
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/plugins/element-plus.js:
--------------------------------------------------------------------------------
1 | import ElementPlus from 'element-plus'
2 | import zhCn from 'element-plus/dist/locale/zh-cn.js'
3 | export default defineNuxtPlugin((nuxtApp) => {
4 | nuxtApp.vueApp.use(ElementPlus, { locale: zhCn })
5 | })
6 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/prettierrc",
3 | "semi": false,
4 | "tabWidth": 2,
5 | "singleQuote": true,
6 | "printWidth": 200,
7 | "trailingComma": "none",
8 | "vueIndentScriptAndStyle": true
9 | }
10 |
--------------------------------------------------------------------------------
/middleware/router.global.js:
--------------------------------------------------------------------------------
1 | export default defineNuxtRouteMiddleware((to) => {
2 | // 首页重定向
3 | if (to.path === '/index') {
4 | return navigateTo('/')
5 | }
6 |
7 | // 404页面
8 | if (to.matched.length === 0) {
9 | return navigateTo('/404')
10 | }
11 | })
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | npm-debug.log*
4 | yarn-debug.log*
5 | web.zip
6 |
7 | .nuxt
8 | .output
9 | .DS_Store
10 | .idea
11 | .vscode
12 |
13 | *.suo
14 | *.ntvs*
15 | *.njsproj
16 | *.sln
17 | *.classpath
18 | *.project
19 | *.settings
20 | *.mvn
21 | *.springBeans
22 | *.factorypath
23 |
24 | distribution/target
25 | package-lock.json
26 | pnpm-lock.yaml
27 |
--------------------------------------------------------------------------------
/layouts/default.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
18 |
--------------------------------------------------------------------------------
/api/lecturer.js:
--------------------------------------------------------------------------------
1 | import { getRequest, postRequest } from '@/utils/request'
2 |
3 | export const lecturerApi = {
4 | // 讲师列表(搜索)
5 | lecturerList: (params = {}) => {
6 | return postRequest('/user/api/lecturer/search', params)
7 | },
8 |
9 | // 讲师详情
10 | lecturerDetail: (params = {}) => {
11 | return getRequest('/user/api/lecturer/view?id=' + params.id)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/utils/login.js:
--------------------------------------------------------------------------------
1 | import { setToken, removeToken } from '@/utils/cookie'
2 | import { setStorage, getStorage } from '@/utils/storage'
3 |
4 | export const login = (token) => {
5 | setToken(token)
6 | const history = getStorage('history')
7 | if (history) {
8 | window.location.href = history
9 | setStorage('history', '')
10 | } else {
11 | window.location.href = '/'
12 | }
13 | }
14 |
15 | export const logout = () => {
16 | removeToken()
17 | }
18 |
--------------------------------------------------------------------------------
/distribution/bin/web.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Copyright 2016-现在 LingKe, Co., Ltd.
3 | export NODE_HOME=/opt/node
4 | export PM2="$NODE_HOME/bin/pm2"
5 | export BASE_DIR=`cd $(dirname $0)/..; pwd`
6 | export SERVER="web"
7 |
8 | case "$1" in
9 | start)
10 | cd ${BASE_DIR}
11 | if [ ! -d "node_modules" ];then
12 | npm install
13 | fi
14 | ${PM2} start
15 | echo "${SERVER} start success"
16 | ;;
17 | stop)
18 | ${PM2} stop ${SERVER}
19 | ${PM2} delete ${SERVER}
20 | echo "${SERVER} stop success"
21 | ;;
22 | *)
23 | $0 stop
24 | sleep 1
25 | $0 start
26 | ;;
27 | esac
28 |
--------------------------------------------------------------------------------
/pages/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 前往首页
6 | 返回上一页
7 |
8 |
9 |
10 |
11 |
22 |
27 |
--------------------------------------------------------------------------------
/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
25 |
26 |
--------------------------------------------------------------------------------
/assets/svg/account/user.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/zip.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * 打包工具,方便将打包后的dist进行管理,打包后的文件为:web.zip
3 | */
4 | import { createWriteStream } from 'fs'
5 | import path from 'path'
6 | import archiver from 'archiver'
7 |
8 | const output = createWriteStream(path.resolve('./web.zip'))
9 | const archive = archiver('zip', { zlib: { level: 9 } })
10 |
11 | archive.pipe(output)
12 | archive.directory(path.resolve('node_modules'), 'node_modules', null)
13 | archive.directory(path.resolve('.output'), '.output', null)
14 | archive.directory(path.resolve('distribution', 'bin'), 'bin', null)
15 | archive.file(path.resolve('ecosystem.config.js'), { name: 'ecosystem.config.js' })
16 | archive.file(path.resolve('nuxt.config.ts'), { name: 'nuxt.config.ts' })
17 | archive.file(path.resolve('package.json'), { name: 'package.json' })
18 |
19 | archive.finalize().then(() => {
20 | console.log('打包成功,请查看根目录下的:web.zip')
21 | })
22 |
--------------------------------------------------------------------------------
/pages/account/order.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
31 |
32 |
--------------------------------------------------------------------------------
/api/index.js:
--------------------------------------------------------------------------------
1 | import { getRequest, postRequest } from '@/utils/request'
2 |
3 | export const indexApi = {
4 | // 首页轮播图
5 | carouselList: () => {
6 | return getRequest('/system/api/website/carousel/list')
7 | },
8 | // 分类列表
9 | categoryList: () => {
10 | return getRequest('/course/api/category/list')
11 | },
12 | // 专区课程
13 | zoneList: (params = {}) => {
14 | return postRequest('/course/api/zone/list', params)
15 | },
16 | // 首页导航栏
17 | websiteNav: () => {
18 | return getRequest('/system/api/website/nav/list')
19 | },
20 | // 站点信息
21 | websiteInfo: () => {
22 | return getRequest('/system/api/sys/config/website')
23 | },
24 | // 友情链接
25 | websiteLink: () => {
26 | return getRequest('/system/api/website/link/list')
27 | },
28 | // 获取枚举
29 | getEnum: (data) => {
30 | return postRequest('/system/api/common/enum', data)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/assets/svg/account/course.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/utils/cookie.js:
--------------------------------------------------------------------------------
1 | import Cookies from 'js-cookie'
2 |
3 | /**
4 | * token的key
5 | */
6 | const TOKEN_KEY = 'EDU_OS_TOKEN'
7 | /**
8 | * 单位:天
9 | */
10 | const TokenExpiresTime = 1
11 |
12 | export function setToken(token) {
13 | return Cookies.set(TOKEN_KEY, token, {
14 | expires: TokenExpiresTime
15 | })
16 | }
17 |
18 | export function getToken() {
19 | if (process.server) {
20 | const nuxtApp = useNuxtApp()
21 | return getTokenForServer(nuxtApp.ssrContext.event.node.req)
22 | }
23 | return Cookies.get(TOKEN_KEY)
24 | }
25 |
26 | export function removeToken() {
27 | return Cookies.remove(TOKEN_KEY, {
28 | expires: 0
29 | })
30 | }
31 |
32 | function getTokenForServer(req) {
33 | const serviceCookie = {}
34 | req &&
35 | req.headers.cookie &&
36 | req.headers.cookie.split(';').forEach(function (val) {
37 | const parts = val.split('=')
38 | serviceCookie[parts[0].trim()] = decodeURIComponent((parts[1] || '').trim())
39 | })
40 | return serviceCookie.EDU_OS_TOKEN
41 | }
42 |
--------------------------------------------------------------------------------
/nuxt.config.ts:
--------------------------------------------------------------------------------
1 | // https://nuxt.com/docs/api/configuration/nuxt-config
2 | // eslint-disable-next-line no-undef
3 | export default defineNuxtConfig({
4 | experimental: {
5 | asyncContext: true
6 | },
7 | css: ['~/assets/styles/main.scss'],
8 | modules: ['@element-plus/nuxt', '@vueuse/nuxt'],
9 | nitro: {
10 | devProxy: {
11 | '/gateway': {
12 | target: import.meta.env.VITE_BASE_URL,
13 | changeOrigin: true
14 | }
15 | }
16 | },
17 | vite: {
18 | build: {
19 | minify: 'terser',
20 | emptyOutDir: true,
21 | chunkSizeWarningLimit: 1500,
22 | terserOptions: {
23 | compress: {
24 | drop_console: import.meta.env.VITE_DROP_CONSOLE,
25 | drop_debugger: import.meta.env.VITE_DROP_DEBUGGER
26 | }
27 | }
28 | }
29 | },
30 | sourcemap: {
31 | server: import.meta.env.NODE_ENV === 'development',
32 | client: import.meta.env.NODE_ENV === 'development'
33 | },
34 | telemetry: true,
35 | compatibilityDate: '2025-08-25'
36 | })
37 |
--------------------------------------------------------------------------------
/api/login.js:
--------------------------------------------------------------------------------
1 | import { postRequest, getRequest } from '@/utils/request'
2 |
3 | export const loginApi = {
4 | // 密码登录
5 | userLogin: (params = {}) => {
6 | return postRequest('/user/api/users/login', params)
7 | },
8 | // 微信登录
9 | wxLogin: (params = {}) => {
10 | return postRequest('/user/api/users/wx/login', params)
11 | },
12 | // 微信登录
13 | wxCode: (params = {}) => {
14 | return postRequest('/user/api/users/wx/code', params)
15 | },
16 | // 微信登录
17 | wxBinding: (params = {}) => {
18 | return postRequest('/user/api/users/wx/binding', params)
19 | },
20 | getCodeImg: () => {
21 | return getRequest('/system/api/common/code')
22 | },
23 | // 发送注册验证码
24 | getMobileCode: (params) => {
25 | return postRequest('/user/api/users/send/code', params)
26 | },
27 | // 用户注册
28 | register: (params = {}) => {
29 | return postRequest('/user/api/users/register', params)
30 | },
31 | // 修改密码
32 | updatePassword: (params = {}) => {
33 | return postRequest('/user/api/users/password', params)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright © 2016 LingKe, Co., Ltd.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/assets/styles/main.scss:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-size: 12px;
5 | background-color: #f6f8fb;
6 | height: 100vh;
7 | overflow: hidden;
8 | word-break: break-all;
9 | }
10 | a {
11 | text-decoration: none;
12 | color: inherit;
13 | }
14 | .main {
15 | width: 1200px;
16 | margin: 0 auto;
17 | }
18 | .pagination {
19 | display: flex;
20 | justify-content: center;
21 | margin: 20px 0;
22 | }
23 | .clearfix {
24 | clear: both;
25 | }
26 | .cursor {
27 | cursor: pointer;
28 | }
29 | .fl {
30 | float: left;
31 | }
32 | .fr {
33 | float: right;
34 | }
35 | .title {
36 | overflow: hidden;
37 | text-overflow: ellipsis;
38 | -webkit-line-clamp: 2;
39 | -webkit-box-orient: vertical;
40 | }
41 | //
42 | .no-data {
43 | margin: calc((100vh - 346px) / 2) auto;
44 | text-align: center;
45 | font-size: 20px;
46 | }
47 |
48 | // 滚动条样式
49 | ::-webkit-scrollbar {
50 | width: 5px;
51 | height: 5px;
52 | border-radius: 5px;
53 | }
54 |
55 | ::-webkit-scrollbar-thumb {
56 | border-radius: 5px;
57 | background: rgba(0, 0, 0, 0.2);
58 | }
59 |
60 | ::-webkit-scrollbar-track {
61 | border-radius: 0;
62 | background: #fff;
63 | }
64 |
--------------------------------------------------------------------------------
/components/Zone/Banner.vue:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
22 |
41 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "roncoo-education-web",
3 | "version": "25.0.0-RELEASE",
4 | "license": "MIT",
5 | "type": "commonjs",
6 | "scripts": {
7 | "dev": "nuxt dev --dotenv .env.development",
8 | "build": "nuxt build --dotenv .env.production",
9 | "lint": "eslint . --fix --ext .vue,.js",
10 | "prettier": "prettier --write .",
11 | "zip": "node zip.mjs"
12 | },
13 | "engines": {
14 | "node": ">=20.0.0"
15 | },
16 | "dependencies": {
17 | "archiver": "7.0.1",
18 | "axios": "1.11.0",
19 | "dayjs": "1.11.3",
20 | "element-plus": "2.9.9",
21 | "js-cookie": "3.0.5",
22 | "node-forge": "1.3.1",
23 | "nuxt": "3.17.2",
24 | "qrcode": "1.5.3",
25 | "vue": "3.5.20",
26 | "vue-router": "4.5.1"
27 | },
28 | "devDependencies": {
29 | "@element-plus/nuxt": "1.0.8",
30 | "@nuxt/eslint-config": "1.3.0",
31 | "@vueuse/nuxt": "10.9.0",
32 | "eslint": "9.26.0",
33 | "eslint-config-prettier": "10.1.2",
34 | "eslint-plugin-prettier": "5.4.0",
35 | "eslint-plugin-vue": "10.1.0",
36 | "prettier": "3.2.5",
37 | "sass": "1.75.0",
38 | "terser": "5.31.0",
39 | "vite": "6.3.4"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/assets/svg/phone.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pages/lecturer/list.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
35 |
40 |
--------------------------------------------------------------------------------
/assets/svg/account/mobile.svg:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/api/user.js:
--------------------------------------------------------------------------------
1 | import { getRequest, postRequest, putRequest } from '@/utils/request'
2 |
3 | export const userApi = {
4 | // 我的课程
5 | userCoursePage: (params = {}) => {
6 | return postRequest('/course/auth/user/course/page', params)
7 | },
8 |
9 | // 我的订单
10 | orderPage: (params = {}) => {
11 | return postRequest('/user/auth/order/info/page', params)
12 | },
13 |
14 | // 继续支付
15 | continuePay: (params = {}) => {
16 | return postRequest('/user/auth/order/pay/continue', params)
17 | },
18 |
19 | // 取消支付
20 | cancelOrder: (params = {}) => {
21 | return putRequest('/user/auth/order/pay/cancel', params)
22 | },
23 |
24 | // 用户信息修改
25 | usersUpdate: (params = {}) => {
26 | return postRequest('/user/auth/users/edit', params)
27 | },
28 |
29 | // 用户信息
30 | getUserInfo: () => {
31 | return getRequest('/user/auth/users/view')
32 | },
33 |
34 | // 用户绑定微信
35 | userBinding: (params = {}) => {
36 | return postRequest('/user/auth/users/binding', params)
37 | },
38 |
39 | // 用户解绑微信
40 | userUnbind: () => {
41 | return getRequest('/user/auth/users/unbind')
42 | },
43 |
44 | // 课程收藏列出
45 | userCourseCollectPage: (params = {}) => {
46 | return postRequest('/course/auth/user/course/collect/page', params)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/assets/svg/comment.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/utils/table.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 表单封装
3 | */
4 | import { onMounted, reactive } from 'vue'
5 |
6 | export default function useTable(apis, paras = {}) {
7 | // 分页对象
8 | const page = reactive({
9 | pageCurrent: 1,
10 | pageSize: 20,
11 | totalCount: 0,
12 | list: [],
13 | loading: true
14 | })
15 |
16 | // 分页查询
17 | const handlePage = async () => {
18 | if (apis.page) {
19 | page.loading = true
20 | try {
21 | const res = await apis.page({
22 | pageCurrent: page.pageCurrent,
23 | pageSize: page.pageSize,
24 | ...paras,
25 | ...query
26 | })
27 | if (res) {
28 | page.list = res.list || res || []
29 | page.totalCount = res.totalCount || 0
30 | }
31 | } finally {
32 | page.loading = false
33 | }
34 | }
35 | }
36 |
37 | // 查询对象
38 | const query = reactive({})
39 |
40 | // 查询
41 | const handleQuery = () => {
42 | page.pageCurrent = 1
43 | // 分页查询
44 | handlePage().then(() => {
45 | //ElMessage.success('查询成功')
46 | })
47 | }
48 |
49 | // 重置
50 | const resetQuery = () => {
51 | for (let i in query) {
52 | query[i] = ''
53 | }
54 | handleQuery()
55 | }
56 |
57 | // 获取数据
58 | onMounted(handlePage)
59 |
60 | return {
61 | page,
62 | handlePage,
63 | query,
64 | handleQuery,
65 | resetQuery
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/components/Course/Collect.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |

5 |
收藏
6 |
7 |
8 |
9 |
47 |
48 |
63 |
--------------------------------------------------------------------------------
/components/Common/Affix.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |

8 |
商业版
9 |
10 |
11 |
12 |
13 | 扫码咨询商业版
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |

22 |
公众号
23 |
24 |
25 |
26 |
27 | 扫码关注微信公众号
28 |
29 |
30 |
31 |
32 |
33 |
34 |
50 |
--------------------------------------------------------------------------------
/components/Common/Link.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
26 |
60 |
--------------------------------------------------------------------------------
/components/Lecturer/List.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
![]()
7 |
8 |
9 |
{{ item.lecturerPosition }}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
24 |
54 |
--------------------------------------------------------------------------------
/components/Zone/Course.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
24 |
56 |
--------------------------------------------------------------------------------
/api/course.js:
--------------------------------------------------------------------------------
1 | import { getRequest, postRequest } from '@/utils/request'
2 |
3 | export const courseApi = {
4 | categoryList: () => {
5 | return getRequest('/course/api/category/list')
6 | },
7 |
8 | // 课程列表(搜索)
9 | courseList: (params = {}) => {
10 | return postRequest('/course/api/course/search', params)
11 | },
12 |
13 | // 课程详情
14 | courseDetail: (params = {}) => {
15 | return postRequest('/course/api/course/view', params)
16 | },
17 |
18 | // 课程评论列出
19 | courseCommentPage: (params = {}) => {
20 | return postRequest('/course/api/course/comment', params)
21 | },
22 |
23 | // 课程详情(登录后)
24 | userCourseDetail: (params = {}) => {
25 | return postRequest('/course/auth/course/view', params)
26 | },
27 |
28 | // 获取播放sign
29 | studySign: (params = {}) => {
30 | return postRequest('/course/auth/course/sign', params)
31 | },
32 |
33 | // 同步学习进度
34 | studyProgress: (params = {}) => {
35 | return postRequest('/course/api/user/study/progress', params)
36 | },
37 |
38 | // 创建订单
39 | createOrder: (params = {}) => {
40 | return postRequest('/user/auth/order/pay/create', params)
41 | },
42 |
43 | // 订单信息
44 | orderInfoView: (orderNo) => {
45 | return getRequest('/user/auth/order/info/view?orderNo=' + orderNo)
46 | },
47 |
48 | // 课程评论添加
49 | courseCommentAdd: (params = {}) => {
50 | return postRequest('/course/auth/user/course/comment/add', params)
51 | },
52 |
53 | // 课程收藏添加
54 | courseCollectAdd: (params = {}) => {
55 | return postRequest('/course/auth/user/course/collect/add', params)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/utils/polyv.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 获取播放器实例
3 | */
4 | export function getClient(playRes, speedDouble, speedDrag) {
5 | const params = JSON.parse(playRes.vodPlayConfig)
6 |
7 | // 当speed参数值为boolean类型时,代表是否显示倍速切换的按钮。当参数值为数组时,则代表倍速切换的可选速率。最多可设置6个速率,取值范围:(0,3]。PC端默认值为:[2, 1.5, 1.2, 0.5],移动端默认值为:[1, 1.5, 2]。
8 | const speed = speedDouble === 0 ? false : true
9 | // 是否禁止拖拽进度条,取值:{on,off}。
10 | const banSeek = speedDrag === 0 ? 'on' : 'off'
11 |
12 | return window.polyvPlayer({
13 | wrap: '#player',
14 | height: '100%',
15 | width: '100%',
16 | autoplay: true,
17 | hideSwitchPlayer: true,
18 | showLine: 'off',
19 | history_video_duration: 1,
20 | speed: speed,
21 | ban_seek: banSeek,
22 | watchStartTime: playRes.currentDuration,
23 | playsafe: params.token,
24 | ...params
25 | })
26 | }
27 |
28 | /**
29 | * 获取播放器实例(这里领课云使用保利威播放器,因为领课云使用的是标准的视频播放格式,支持通用的播放器)
30 | * @param playRes
31 | * @param speedDouble
32 | * @param speedDrag
33 | * @returns {*}
34 | */
35 | export function getClientForPri(playRes, speedDouble, speedDrag) {
36 | const params = JSON.parse(playRes.vodPlayConfig)
37 |
38 | // 当speed参数值为boolean类型时,代表是否显示倍速切换的按钮。当参数值为数组时,则代表倍速切换的可选速率。最多可设置6个速率,取值范围:(0,3]。PC端默认值为:[2, 1.5, 1.2, 0.5],移动端默认值为:[1, 1.5, 2]。
39 | const speed = speedDouble === 0 ? false : true
40 | // 是否禁止拖拽进度条,取值:{on,off}。
41 | const banSeek = speedDrag === 0 ? 'on' : 'off'
42 |
43 | return window.polyvPlayer({
44 | wrap: '#player',
45 | height: '100%',
46 | width: '100%',
47 | hideSwitchPlayer: true,
48 | autoplay: false,
49 | showLine: 'off',
50 | url: params.hdUrl,
51 | speed: speed,
52 | ban_seek: banSeek,
53 | watchStartTime: playRes.currentDuration
54 | })
55 | }
56 |
--------------------------------------------------------------------------------
/components/Common/Pagination.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
65 |
--------------------------------------------------------------------------------
/assets/svg/gzh.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/svg/chapter.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/components/Common/User.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | 我的课程
12 |
13 |
14 | 我的收藏
15 |
16 |
17 | 我的订单
18 |
19 |
20 | 个人信息
21 |
22 | 安全退出
23 |
24 |
25 |
26 |
27 |
28 |
49 |
55 |
--------------------------------------------------------------------------------
/distribution/assembly.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 | ${project.version}
19 | true
20 |
21 | dir
22 |
23 | tar.gz
24 |
25 |
26 |
27 | ${project.basedir}/bin
28 | bin
29 | 0755
30 | 0755
31 |
32 |
33 | ${project.basedir}/../.output
34 |
35 |
36 | ${project.basedir}/../node_modules
37 |
38 |
39 |
40 |
41 | ${project.basedir}/../nuxt.config.ts
42 |
43 |
44 | ${project.basedir}/../ecosystem.config.js
45 |
46 |
47 | ${project.basedir}/../package.json
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/assets/svg/account/wechat.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/utils/storage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 存储localStorage
3 | * @param expireTime 单位:分
4 | */
5 | export function setStorage(name, content, expireTime) {
6 | if (!name) return
7 | let params = JSON.stringify(content)
8 | if (expireTime) {
9 | expireTime = Date.now() + expireTime * 1000 * 60
10 | params = JSON.stringify({ content: content, expireTime: expireTime })
11 | }
12 | window.localStorage.setItem(name, params)
13 | }
14 |
15 | /**
16 | * 获取localStorage
17 | */
18 | export function getStorage(name) {
19 | if (!name) return
20 | let data = window.localStorage.getItem(name)
21 | if (!data) return null
22 | data = JSON.parse(data)
23 | if (data) {
24 | if (data.expireTime && data.expireTime > 0) {
25 | if (data.expireTime > Date.now()) {
26 | return data.content
27 | }
28 | window.localStorage.removeItem(name)
29 | return null
30 | }
31 | return data
32 | }
33 | return null
34 | }
35 |
36 | /**
37 | *
38 | * @param name
39 | * @param expireTime 单位:分
40 | */
41 | export function setSessionStorage(name, content, expireTime) {
42 | if (!name) return
43 | let params = JSON.stringify(content)
44 | if (expireTime) {
45 | expireTime = Date.now() + expireTime * 1000 * 60
46 | params = JSON.stringify({ content: content, expireTime: expireTime })
47 | }
48 | window.sessionStorage.setItem(name, params)
49 | }
50 |
51 | /**
52 | * 获取sessionStorage
53 | */
54 | export function getSessionStorage(name) {
55 | if (!name) return null
56 | let data = window.sessionStorage.getItem(name)
57 | if (!data) return null
58 | data = JSON.parse(data)
59 | if (data) {
60 | if (data.expireTime && data.expireTime > 0) {
61 | if (data.expireTime > Date.now()) {
62 | return data.content
63 | }
64 | window.sessionStorage.removeItem(name)
65 | return null
66 | }
67 | return data
68 | }
69 | return null
70 | }
71 |
--------------------------------------------------------------------------------
/components/Account/Wechat.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
56 |
57 |
73 |
--------------------------------------------------------------------------------
/distribution/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.roncoo
8 | distribution
9 | 25.0.0-RELEASE
10 | pom
11 | distribution
12 |
13 |
14 |
15 | release-education
16 |
17 | true
18 |
19 |
20 |
21 |
22 | org.apache.maven.plugins
23 | maven-assembly-plugin
24 | 3.0.0
25 |
26 | false
27 |
28 | assembly.xml
29 |
30 |
31 |
32 |
33 | make-assembly
34 | package
35 |
36 | single
37 |
38 |
39 |
40 |
41 |
42 | web
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/components/Common/Footer.vue:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
35 |
74 |
--------------------------------------------------------------------------------
/pages/lecturer/detail.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
![]()
6 |
7 |
{{ lecturerInfo.lecturerName }} {{ lecturerInfo.lecturerPosition }}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
暂无数据
18 |
19 |
20 |
21 |
35 |
68 |
--------------------------------------------------------------------------------
/assets/svg/account/collect.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/components/Course/Choose.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
{{ changeNumToHan(index + 1) }}级分类:
5 |
6 |
7 | {{ item.categoryName }}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
52 |
53 |
89 |
--------------------------------------------------------------------------------
/assets/svg/account/password.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/pages/account/collect.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
{{ scope.row.courseResp?.courseName }}【免费课】
9 |
购买人数:{{ scope.row.courseResp?.countBuy }}
10 |
学习人数:{{ scope.row.courseResp?.countStudy }}
11 |
12 |
13 |
14 |
15 |
16 |
17 | 马上学习
18 |
19 |
20 |
21 |
22 |
25 |
26 |
27 |
35 |
58 |
--------------------------------------------------------------------------------
/assets/svg/account/order.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/layouts/account.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
{{ item.title }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
60 |
99 |
--------------------------------------------------------------------------------
/pages/account/course.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
{{ scope.row.courseResp?.courseName }}【免费课】
10 |
学习至:{{ scope.row.periodName }}({{ scope.row.periodProgress }}%)| {{ scope.row.periodTime }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | 继续学习
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
39 |
60 |
--------------------------------------------------------------------------------
/components/Course/List.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 暂无数据
4 |
5 |
6 |
7 |
8 |
9 |
10 |
免费
11 |
12 | ¥{{ course.coursePrice }} ¥{{ course.rulingPrice }}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
30 |
94 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
17 |
18 |
19 | ### 使用须知
20 |
21 | 1. 允许用于个人学习、毕业设计、教学案例、公益事业等。
22 | 2. 限制商用,若需要商业使用请咨询作者:18302045627(微信可加)。
23 | 3. 禁止将本项目的相关代码和相关资料进行任何形式任何名义的出售。
24 |
25 | ### 项目介绍
26 |
27 | 领课教育系统(roncoo-education)是基于领课网络多年的在线教育平台开发和运营经验打造出来的产品,致力于打造一个各行业都适用的分布式在线教育系统。系统采用前后端分离模式,前台采用vue.js为核心框架,后台采用Spring
28 | Cloud为核心框架。系统目前主要功能有课程点播功能,支持多家视频云的接入,课程附件管理功能,支持多家存储云的接入,可以帮助个人或者企业快速搭建一个轻量级的在线教育平台。
29 |
30 |
31 |
32 |  |
33 |  |
34 |
35 |
36 |  |
37 |  |
38 |
39 |
40 |  |
41 |  |
42 |
43 |
44 |  |
45 |  |
46 |
47 |
48 |
49 | ### 演示地址
50 |
51 | * 门户系统:[https://eduos.roncoos.com/](https://eduos.roncoos.com/)
52 | * 管理系统:[https://eduos.roncoos.com/admin/](https://eduos.roncoos.com/admin/)
53 | * 前端技术体系:Vue3 + Nuxt3 + Vite6 + Vue-Router + Element-Plus + Pinia + Axios
54 | * 后端技术体系:Spring Cloud Alibaba2023 + MySQL8 + Nacos + Seata + Mybatis + Druid
55 |
56 | ### 源码地址
57 |
58 | * 后端系统:roncoo-education(核心框架:Spring Cloud Alibaba):[码云](https://gitee.com/roncoocom/roncoo-education) | [Github](https://github.com/roncoo/roncoo-education) | [Gitcode](https://gitcode.com/roncoocom/roncoo-education)
59 | * 门户系统:roncoo-education-web(核心框架:Nuxt3):[码云](https://gitee.com/roncoocom/roncoo-education-web) | [Github](https://github.com/roncoo/roncoo-education-web) | [Gitcode](https://gitcode.com/roncoocom/roncoo-education-web)
60 | * 管理系统:roncoo-education-admin(核心框架:Vue3):[码云](https://gitee.com/roncoocom/roncoo-education-admin) | [Github](https://github.com/roncoo/roncoo-education-admin) | [Gitcode](https://gitcode.com/roncoocom/roncoo-education-admin)
61 |
62 | ---
63 | 关注微信公众号可获取更多学习资料(SQL脚本、部署教程、常见问题等)
64 |
65 |
--------------------------------------------------------------------------------
/pages/search.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 搜索
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
搜索结果:{{ page.totalCount }} 个
18 |
19 |
20 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |
74 |
89 |
--------------------------------------------------------------------------------
/components/Course/Comment.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
31 |
32 |
33 |
34 |
70 |
71 |
112 |
--------------------------------------------------------------------------------
/utils/request.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import { ElMessage, ElMessageBox } from 'element-plus'
3 | import { getToken, removeToken } from '@/utils/cookie.js'
4 | import { setStorage } from '@/utils/storage.js'
5 |
6 | // create an axios instance
7 | const request = axios.create({
8 | baseURL: process.client ? '/gateway' : import.meta.env.VITE_BASE_URL,
9 | timeout: 60000 // request timeout
10 | })
11 |
12 | // request interceptor
13 | request.interceptors.request.use(
14 | (config) => {
15 | if (config.url.indexOf('/auth') === -1) {
16 | return config
17 | }
18 | // 需要token的请求
19 | const token = getToken()
20 | if (token) {
21 | config.headers['token'] = token
22 | return config
23 | }
24 |
25 | // 登录拦截
26 | ElMessageBox.confirm('请先登录', '提示', {
27 | confirmButtonText: '立即登录',
28 | showCancelButton: false,
29 | type: 'warning'
30 | }).then(() => {
31 | window.location.href = '/login'
32 | })
33 | setStorage('history', window.location.href, 5)
34 | return Promise.reject(config)
35 | },
36 | (error) => {
37 | console.error(error)
38 | return Promise.reject(error)
39 | }
40 | )
41 |
42 | // response interceptor
43 | request.interceptors.response.use(
44 | (response) => {
45 | const res = response.data
46 | //console.log('res', res)
47 | if (res.code && res.code === 200) {
48 | // 返回数据
49 | return Promise.resolve(res.data)
50 | }
51 |
52 | if (res.code === 301) {
53 | // token过期
54 | removeToken()
55 | return Promise.reject(response)
56 | }
57 |
58 | if (res.code === 302 || res.code === 303 || res.code === 304 || res.code === 305) {
59 | // 302token异常,303登录异常,304异地登录,305菜单过期
60 | ElMessageBox.confirm('异地登录', '确定登出', {
61 | confirmButtonText: '重新登录',
62 | showCancelButton: false,
63 | type: 'warning'
64 | }).then(() => {
65 | removeToken()
66 | location.reload()
67 | })
68 | return Promise.reject(response)
69 | }
70 |
71 | // 其他异常
72 | console.error(response)
73 | ElMessage.error({ message: res.msg, duration: 5 * 1000 })
74 | return Promise.reject(response)
75 | },
76 | (error) => {
77 | if (error.response && error.response.status === 500 && error.response.data.msg) {
78 | ElMessage.error({ message: error.response.data.msg, duration: 5 * 1000 })
79 | } else {
80 | console.error(error)
81 | }
82 | return Promise.reject(error)
83 | }
84 | )
85 |
86 | /**
87 | * post请求
88 | */
89 | export const postRequest = (url, data = {}) => {
90 | return request({ url: url, data: data ? data : {}, method: 'post' })
91 | }
92 |
93 | /**
94 | * get请求
95 | */
96 | export const getRequest = (url) => {
97 | return request({ url: url, method: 'get' })
98 | }
99 |
100 | /**
101 | * put请求
102 | */
103 | export const putRequest = (url, data = {}) => {
104 | return request({ url: url, data: data ? data : {}, method: 'put' })
105 | }
106 |
107 | /**
108 | * delete请求
109 | */
110 | export const deleteRequest = (url) => {
111 | return request({ url: url, method: 'delete' })
112 | }
113 |
114 | export const upload = (url, file, fileName, cb, cancelFun) => {
115 | const formData = new FormData()
116 | formData.append(fileName, file)
117 | const config = {
118 | onUploadProgress: (progressEvent) => {
119 | const percent = Number(((progressEvent.loaded / progressEvent.total) * 100).toFixed(2))
120 | // 计算上传进度
121 | if (cb) {
122 | cb(percent)
123 | }
124 | }
125 | }
126 | if (cancelFun) {
127 | config.cancelToken = new axios.CancelToken(function excutor(c) {
128 | cancelFun.cancel = c
129 | })
130 | }
131 |
132 | return request.post(url, formData, config)
133 | }
134 |
--------------------------------------------------------------------------------
/components/Common/Header.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
![]()
6 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | 登录
26 | 注册
27 |
28 |
29 |
30 |
31 |
32 |
77 |
151 |
--------------------------------------------------------------------------------
/components/Account/Order.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 订单号:{{ scope.row.orderNo }}
7 |
8 | 支付状态:
9 | 待支付
10 | 已支付
11 | 支付失败
12 | 已关闭
13 |
14 |
15 | 支付方式:
16 | 微信支付
17 | 支付宝支付
18 | 余额支付
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | {{ scope.row.courseName }}
28 | 支付金额:¥{{ scope.row.coursePrice }}
29 |
30 |
31 |
32 |
33 | 继续支付
34 |
35 |
36 | 取消订单
37 |
38 |
39 |
40 |
41 |
42 |
43 |
46 |
47 |
48 |
49 |
88 |
125 |
--------------------------------------------------------------------------------
/utils/base.js:
--------------------------------------------------------------------------------
1 | import forge from 'node-forge'
2 |
3 | export function getResourceTypeName(resourceType) {
4 | if (resourceType === 1) {
5 | return '视频'
6 | } else if (resourceType === 2) {
7 | return '音频'
8 | } else if (resourceType === 3) {
9 | return '文档'
10 | } else if (resourceType === 4) {
11 | return '图片'
12 | } else if (resourceType === 5) {
13 | return '压缩包'
14 | } else {
15 | return '未知'
16 | }
17 | }
18 |
19 | /**
20 | * 判断是否是外部链接
21 | * @param path
22 | * @returns {boolean}
23 | */
24 | export function isExternalUrl(path) {
25 | return path.indexOf('http') !== -1
26 | }
27 |
28 | /**
29 | * 格式化时长
30 | * @param time
31 | */
32 | export function formatTime(time) {
33 | let a = ~~(time / 3600)
34 | let b = ~~(time / 60) - a * 60
35 | let c = time % 60
36 | a = String(a).padStart(2, '0')
37 | b = String(b).padStart(2, '0')
38 | c = String(c).padStart(2, '0')
39 | if (a === '00') {
40 | return `${b}:${c}`
41 | } else {
42 | return `${a}:${b}:${c}`
43 | }
44 | }
45 |
46 | /**
47 | * 数字转换为中文
48 | * @param num
49 | * @returns {string}
50 | */
51 | export function changeNumToHan(num) {
52 | const arr1 = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
53 | const arr2 = ['', '十', '百', '千', '万', '十', '百', '千', '亿', '十', '百', '千', '万', '十', '百', '千', '亿']
54 | if (!num || isNaN(num)) return '零'
55 | const english = num.toString().split('')
56 | let result = ''
57 | for (let i = 0; i < english.length; i++) {
58 | const des_i = english.length - 1 - i // 倒序排列设值
59 | result = arr2[i] + result
60 | const arr1_index = english[des_i]
61 | result = arr1[arr1_index] + result
62 | }
63 | result = result.replace(/零([千百十])/g, '零').replace(/十零/g, '十') // 将【零千、零百】换成【零】 【十零】换成【十】
64 | result = result.replace(/零+/g, '零') // 合并中间多个零为一个零
65 | result = result.replace(/零亿/g, '亿').replace(/零万/g, '万') // 将【零亿】换成【亿】【零万】换成【万】
66 | result = result.replace(/亿万/g, '亿') // 将【亿万】换成【亿】
67 | result = result.replace(/零+$/, '') // 移除末尾的零
68 | // 将【一十】换成【十】
69 | result = result.replace(/^一十/g, '十')
70 | return result
71 | }
72 |
73 | // 获取操作系统信息
74 | export function getOsInfo() {
75 | const userAgent = navigator.userAgent
76 | let name = 'unknown'
77 | if (userAgent.indexOf('Windows Phone') > -1) {
78 | name = 'Windows Phone'
79 | } else if (userAgent.indexOf('Windows NT') > -1) {
80 | name = 'Windows'
81 | } else if (userAgent.indexOf('Mac') > -1) {
82 | name = 'Mac'
83 | } else if (userAgent.indexOf('X11') > -1) {
84 | name = 'UNIX'
85 | } else if (userAgent.indexOf('Linux') > -1) {
86 | name = 'Linux'
87 | } else if (userAgent.indexOf('iPhone') > -1) {
88 | name = 'iOS'
89 | } else if (userAgent.indexOf('iPad') > -1) {
90 | name = 'iOS'
91 | } else if (userAgent.indexOf('iPod') > -1) {
92 | name = 'iOS'
93 | } else if (userAgent.indexOf('Android') > -1) {
94 | name = 'Android'
95 | } else if (userAgent.indexOf('BlackBerry') > -1) {
96 | name = 'BlackBerry'
97 | } else if (userAgent.indexOf('webOS') > -1) {
98 | name = 'webOS'
99 | }
100 | return name
101 | }
102 |
103 | /**
104 | * 获取浏览器信息
105 | * @returns {{name: string, version: string}}
106 | */
107 | export function getBrowserInfo() {
108 | const ua = navigator.userAgent.toLowerCase()
109 | const browserPatterns = {
110 | ie: /rv:([\d.]+)\) like gecko/,
111 | edge: /edge\/([\d.]+)/,
112 | firefox: /firefox\/([\d.]+)/,
113 | opera: /(?:opera|opr).([\d.]+)/,
114 | chrome: /chrome\/([\d.]+)/,
115 | safari: /version\/([\d.]+).*safari/
116 | }
117 | for (const [browserName, pattern] of Object.entries(browserPatterns)) {
118 | const match = pattern.exec(ua)
119 | if (match) {
120 | const version = match[1]
121 | const capitalizedName = browserName.charAt(0).toUpperCase() + browserName.slice(1)
122 | return { name: capitalizedName === 'Ie' ? 'IE' : capitalizedName, version }
123 | }
124 | }
125 | return { name: 'Unknown', version: '0.0.0' }
126 | }
127 |
128 | export function encrypt(password, publicKey) {
129 | publicKey = `-----BEGIN PUBLIC KEY-----\n` + publicKey + `\n-----END PUBLIC KEY-----`
130 | publicKey = forge.pki.publicKeyFromPem(publicKey)
131 | return forge.util.encode64(publicKey.encrypt(password))
132 | }
133 |
--------------------------------------------------------------------------------
/components/Course/Chapter.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 第{{ index + 1 }}章 {{ one.chapterName }}
7 |
8 |
9 |
10 | 第{{ num + 1 }}讲
11 |
12 | {{ getResourceTypeName(two.resourceResp?.resourceType) }}
13 |
14 | (未更新)
15 | {{ two.periodName }}
16 | {{ formatTime(two.resourceResp.videoLength) }}
17 | {{ two.resourceResp.docPage }}页
18 | (试看)
19 |
20 | {{ two.periodProgress }}%
21 |
22 |
23 |
24 |
25 |
26 |
27 |
51 |
52 |
151 |
--------------------------------------------------------------------------------
/pages/course/list.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
16 |
17 |
139 |
144 |
--------------------------------------------------------------------------------
/pages/register.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
账号注册
8 |
已有账号, 立即登录
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | 获取验证码
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | 注 册
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
111 |
158 |
--------------------------------------------------------------------------------
/pages/reset.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
重置密码
8 |
9 | 返回账号登录
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | 获取验证码
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | 重 置
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
115 |
162 |
--------------------------------------------------------------------------------
/pages/account/user.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | {{ userInfo.mobile }}
10 | 不可修改
11 |
12 |
13 | ¥{{ userInfo.availableAmount }}元
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | 保存设置
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |

43 |
44 | 重置登录密码
45 |
46 | ********
47 |
48 |
49 |
50 | 密码重置
51 |
52 |
53 |
54 |
55 |
56 |
57 |

58 |
59 | 微信账号绑定
60 |
61 | 已绑定
62 | 未绑定
63 |
64 |
65 |
66 | 解除绑定
67 | 绑定微信
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
155 |
183 |
--------------------------------------------------------------------------------
/components/Zone/Category.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
13 | {{ item.categoryName }}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | {{ item1.categoryName }}
23 |
24 |
25 |
26 | {{ item2.categoryName }}
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
58 |
276 |
--------------------------------------------------------------------------------
/components/Common/Pay.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
![]()
7 |
8 | {{ courseInfo.courseName }}
9 |
10 |
¥{{ courseInfo.coursePrice }}
11 |
12 |
13 |
14 |
15 | {{ item.desc }}
16 |
17 |
18 |
19 | ¥{{ availableAmount }}元
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | 正在使用
28 |
29 | {{ item.desc }}
30 |
31 | 支付:¥{{ orderInfo.coursePrice.toFixed(2) }}
32 |
33 |
34 |
35 |
36 |

37 |
支付成功
38 |
39 |
40 |

41 |
支付失败
42 |
43 |
44 |
请扫描二维码完成订单
45 |
46 | 提示:
47 | 支付成功前请勿手动关闭页面
48 | 二维码两小时内有效,请及时扫码支付
49 |
50 |
51 |
52 |
56 |
57 |
58 |
59 |
60 |
162 |
218 |
--------------------------------------------------------------------------------
/pages/login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |

7 |
8 |
9 |
10 |

11 |
12 |

13 |
40 |
60 |
61 |
62 |
63 |
64 |
65 |
240 |
336 |
--------------------------------------------------------------------------------
/pages/course/detail.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
18 |
19 |
20 |
![]()
21 |
22 |
23 |
24 | {{ courseInfo.courseName }}
25 |
26 |
27 |
28 | 价格:免费
29 |
30 | ¥{{ courseInfo.coursePrice }} ¥{{ courseInfo.rulingPrice }}
31 |
32 |
33 |
34 |
35 | 讲师名称:{{ courseInfo.lecturerResp.lecturerName }}({{ courseInfo.lecturerResp.lecturerPosition }})
36 |
37 |
购买人数:{{ courseInfo.countBuy }} 人
38 |
学习人数:{{ courseInfo.countStudy }} 人
39 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
讲师简介
73 |
74 |
75 |
76 |
![]()
77 |

78 |
79 | {{ courseInfo.lecturerResp.lecturerName }}
80 |
81 | {{ courseInfo.lecturerResp.lecturerPosition }}
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
153 |
314 |
--------------------------------------------------------------------------------
/pages/course/study.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | 下一节:{{ nextPeriod?.periodName }}
27 | 马上学习
28 |
29 |
30 |
31 |
32 |
36 |
37 |
38 |
39 |
第{{ index + 1 }}章:{{ one.chapterName }}
40 |
41 |
42 |
43 |
{{ getResourceTypeName(two.resourceResp?.resourceType) }}:
44 | {{ index + 1 }}-{{ num + 1 }} {{ two.periodName }}
45 |
(未更新)
46 |
(试看)
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
336 |
475 |
--------------------------------------------------------------------------------