提交 c0d423ac authored 作者: 王鹏飞's avatar 王鹏飞

v2

上级 df502330
...@@ -4,7 +4,9 @@ module.exports = { ...@@ -4,7 +4,9 @@ module.exports = {
}, },
extends: ['plugin:vue/essential', 'standard'], extends: ['plugin:vue/essential', 'standard'],
rules: { rules: {
'vue/no-mutating-props': 'off', // 暂时关闭
'vue/comment-directive': 'off', 'vue/comment-directive': 'off',
'vue/multi-word-component-names': 'off',
'space-before-function-paren': 'off' 'space-before-function-paren': 'off'
} }
} }
This source diff could not be displayed because it is too large. You can view the blob instead.
{ {
"version": "0.0.0", "version": "0.0.0",
"scripts": { "scripts": {
"dev": "vite --mode dev", "dev": "vite",
"build": "cross-env BUILD_ENV=prod vite build && npm run deploy", "build": "vite build --mode prod && npm run deploy",
"build:pre": "vite build", "build:pre": "vite build --mode pre",
"build:test": "vite build --mode test", "build:test": "vite build --mode test",
"preview": "vite preview", "preview": "vite preview",
"deploy": "node ./deploy.js", "deploy": "node ./deploy.js",
"lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src" "lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src"
}, },
"dependencies": { "dependencies": {
"axios": "^0.24.0", "@tinymce/tinymce-vue": "^3.2.8",
"axios": "^0.26.0",
"blueimp-md5": "^2.19.0", "blueimp-md5": "^2.19.0",
"echarts": "^5.2.2", "echarts": "^5.3.0",
"element-ui": "^2.15.6", "element-ui": "^2.15.6",
"js-base64": "^3.7.2", "js-base64": "^3.7.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"query-string": "^7.0.1", "query-string": "^7.1.1",
"vue": "^2.6.14", "vue": "^2.6.14",
"vue-html2pdf": "^1.8.0", "vue-html2pdf": "^1.8.0",
"vue-router": "^3.5.3", "vue-router": "^3.5.3",
"vuex": "^3.6.2" "vuex": "^3.6.2"
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-eslint": "^8.0.1", "ali-oss": "^6.17.1",
"ali-oss": "^6.16.0",
"chalk": "^4.1.2", "chalk": "^4.1.2",
"cross-env": "^7.0.3",
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-config-standard": "^16.0.3", "eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.25.3", "eslint-plugin-import": "^2.25.4",
"eslint-plugin-node": "^11.1.0", "eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.1", "eslint-plugin-promise": "^5.2.0",
"eslint-plugin-vue": "^7.20.0", "eslint-plugin-vue": "^8.5.0",
"sass": "1.43.5", "sass": "1.49.9",
"vite": "^2.6.14", "vite": "^2.8.6",
"vite-plugin-vue2": "^1.9.0", "vite-plugin-checker": "^0.4.2",
"vite-plugin-vue2": "^1.9.3",
"vue-template-compiler": "^2.6.14" "vue-template-compiler": "^2.6.14"
} }
} }
import httpRequest from '@/utils/axios'
// 获取用户信息
export function getUser() {
return httpRequest.get('/api/passport/account/get-user-info')
}
// 退出登录
export function logout() {
return httpRequest.get('/api/passport/rest/logout')
}
// 获取oss token
export function getToken() {
return httpRequest.get('/api/usercenter/aliyun/assume-role')
}
// 获取oss signature
export function getSignature() {
return httpRequest.get('/api/usercenter/aliyun/get-signature')
}
// 图片上传
export function uploadFile(data) {
return httpRequest.post('https://webapp-pub.oss-cn-beijing.aliyuncs.com', data, {
withCredentials: false,
headers: { 'Content-Type': 'multipart/form-data' }
})
}
/**
* 获取权限列表
*/
export function getPermissions(params) {
return httpRequest.get('/api/permissions/api/v1/user/permissions', { params })
}
$--color-primary: #c01540; $--color-primary: #c01540;
$--color-info: #3c4043; $--color-info: #3c4043;
// border // border
$--border-radius-small: 8px !default; $--border-radius-small: 8px !default;
// checkbox
$--checkbox-border-radius: 2px !default;
// dialog // dialog
$--message-close-size: 20px !default; $--message-close-size: 20px !default;
......
...@@ -39,8 +39,8 @@ export default { ...@@ -39,8 +39,8 @@ export default {
this.dispatch('ElFormItem', 'el.form.blur', editor.getContent()) this.dispatch('ElFormItem', 'el.form.blur', editor.getContent())
}, },
dispatch(componentName, eventName, params) { dispatch(componentName, eventName, params) {
var parent = this.$parent || this.$root let parent = this.$parent || this.$root
var name = parent.$options.componentName let name = parent.$options.componentName
while (parent && (!name || name !== componentName)) { while (parent && (!name || name !== componentName)) {
parent = parent.$parent parent = parent.$parent
......
<template>
<div class="editor">
<textarea name="editor" :id="textareaElementId" :disabled="disabled"></textarea>
</div>
</template>
<script>
import { uniqueId } from 'lodash'
export default {
name: 'VEditor',
props: {
value: { type: String },
disabled: { type: Boolean, default: false }
},
data() {
return {
textareaElementId: uniqueId('editor_'),
ckEditor: null
}
},
watch: {
value(val) {
if (this.ckEditor && this.ckEditor.getData() !== val) {
this.ckEditor.setData(val)
}
},
disabled(val) {
if (this.ckEditor && this.ckEditor.instanceReady) {
this.ckEditor.setReadOnly(val)
}
}
},
methods: {
createEditor() {
const config = {
height: 400,
uiColor: '#eeeeee',
filebrowserImageUploadUrl: '/api/ck/form/ckeditor-upload',
fileTools_requestHeaders: { tenant: 'sofia' },
// resize_enabled: typeof this.props.resizable === 'boolean' ? this.props.resizable : true,
toolbar: [
// { name: 'document', items: ['Source', '-', 'Save', 'NewPage', 'Preview'] },
{ name: 'styles', items: ['Styles', 'Format', 'Font', 'FontSize'] },
{ name: 'colors', items: ['TextColor', 'BGColor'] },
{ name: 'tools', items: ['Maximize', 'ShowBlocks'] },
// { name: 'clipboard', items: ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo'] },
{ name: 'editing', items: ['Find', 'Replace'] },
// { name: 'forms', items: ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField'] },
'/',
{
name: 'basicstyles',
items: ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat']
},
{
name: 'paragraph',
items: [
'NumberedList',
'BulletedList',
'-',
'Outdent',
'Indent',
'-',
'Blockquote',
'CreateDiv',
'-',
'JustifyLeft',
'JustifyCenter',
'JustifyRight',
'JustifyBlock',
'-',
'BidiLtr',
'BidiRtl'
]
},
{ name: 'links', items: ['Link', 'Unlink', 'Anchor'] },
{ name: 'insert', items: ['Image', 'Table', 'HorizontalRule'] }
]
}
// if (this.disabled !== null) {
// console.log(this.disabled)
// config.readOnly = this.disabled
// }
const editor = (this.ckEditor = CKEDITOR.replace(this.textareaElementId, config))
editor.on('instanceReady', () => {
const data = this.value
editor.fire('lockSnapshot')
editor.setData(data, {
callback: () => {
this.bindEvent()
const newData = editor.getData()
// Locking the snapshot prevents the 'change' event.
// Trigger it manually to update the bound data.
if (data !== newData) {
this.$once('input', () => {
this.$emit('ready', editor)
})
this.$emit('input', newData)
} else {
this.$emit('ready', editor)
}
editor.fire('unlockSnapshot')
}
})
editor.setReadOnly(this.disabled)
})
},
bindEvent() {
const editor = this.ckEditor
editor.on('change', evt => {
const data = editor.getData()
if (this.value !== data) {
this.$emit('input', data, evt, editor)
}
})
editor.on('focus', evt => {
this.$emit('focus', evt, editor)
})
editor.on('blur', evt => {
this.$emit('blur', evt, editor)
})
}
},
mounted() {
this.createEditor()
},
beforeDestroy() {
this.ckEditor && this.ckEditor.destroy()
this.ckEditor = null
}
}
</script>
<style lang="scss" scoped>
* {
margin: 0;
padding: 0;
}
</style>
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
<script> <script>
// libs // libs
import { shuffle } from 'lodash' import { shuffle } from 'lodash'
import { Base64 } from 'js-base64'
// components // components
import Container from '../common/container.vue' import Container from '../common/container.vue'
import ExamItem from './examItem.vue' import ExamItem from './examItem.vue'
......
...@@ -79,7 +79,7 @@ ...@@ -79,7 +79,7 @@
<script> <script>
// componets // componets
import Container from '../common/container.vue' import Container from '../common/container.vue'
import VEditor from '../common/editor.vue' import VEditor from '@/components/tinymce/Index.vue'
import VUpload from '../common/upload.vue' import VUpload from '../common/upload.vue'
// api // api
import * as api from '../../api' import * as api from '../../api'
......
...@@ -67,7 +67,7 @@ ...@@ -67,7 +67,7 @@
<script> <script>
// components // components
import VEditor from '../common/editor.vue' import VEditor from '@/components/tinymce/Index.vue'
import VUpload from '../common/upload.vue' import VUpload from '../common/upload.vue'
export default { export default {
......
...@@ -42,10 +42,10 @@ export default { ...@@ -42,10 +42,10 @@ export default {
methods: { methods: {
// 获取考卷 // 获取考卷
getTopic() { getTopic() {
const isCreate = const query = this.$route.query
this.$route.query.id || this.$route.query.is_create === undefined ? 0 : this.$route.query.is_create const isCreate = query.id || query.is_create === undefined ? 0 : query.is_create
const param = { const param = {
type: 2, // 1:能力自测, 2:模拟考试 type: query.type || 2, // 1:能力自测, 2:模拟考试, 3:新的考试
paper_id: this.examId, paper_id: this.examId,
is_create: isCreate // 是否重新测试: 1:是,否则不是 is_create: isCreate // 是否重新测试: 1:是,否则不是
} }
...@@ -116,7 +116,10 @@ export default { ...@@ -116,7 +116,10 @@ export default {
api.setCache(param).then(response => { api.setCache(param).then(response => {
if (isCache) { if (isCache) {
clearInterval(this.cacheAnswerTime) clearInterval(this.cacheAnswerTime)
this.$router.replace({ path: '/exam/exam/result', query: { exam_id: this.examId } }) this.$router.replace({
path: '/exam/exam/result',
query: { exam_id: this.examId, type: this.$route.query.type || 2 }
})
} }
}) })
} }
......
...@@ -9,15 +9,19 @@ ...@@ -9,15 +9,19 @@
<template v-for="item in examList"> <template v-for="item in examList">
<el-card shadow="hover" class="exam-item" :key="item.id" @click.native="startExam(item)"> <el-card shadow="hover" class="exam-item" :key="item.id" @click.native="startExam(item)">
<h3>{{ item.paper_title }}</h3> <h3>{{ item.paper_title }}</h3>
<p v-if="[1, 2].includes(item.status)">已完成</p> <!-- <p>{{ item.start_time }} ~ {{ item.end_time }}</p> -->
<p v-if="[0, 3].includes(item.status)">待完成</p> <p>{{ getExamStatusName(item.status) }}</p>
<!-- <p>重新考试</p> -->
</el-card> </el-card>
</template> </template>
</div> </div>
<!-- <template #footer> <el-dialog title="考前须知" :visible.sync="dialogVisible">
<div class="app-container-ft"><el-button type="primary" @click="getExamStatus">开始考试</el-button></div> <div v-html="acitveItem.config.welcome_message" v-if="acitveItem && acitveItem.config"></div>
</template> --> <template #footer>
<div style="text-align: center">
<el-button type="primary" @click="enterExam">进入考试</el-button>
</div>
</template>
</el-dialog>
</app-container> </app-container>
</template> </template>
...@@ -28,7 +32,9 @@ export default { ...@@ -28,7 +32,9 @@ export default {
return { return {
courseId: '', courseId: '',
courses: [], courses: [],
examList: [] examList: [],
dialogVisible: false,
acitveItem: {} // 当前激活的考试
} }
}, },
computed: { computed: {
...@@ -40,22 +46,65 @@ export default { ...@@ -40,22 +46,65 @@ export default {
this.getExamList() this.getExamList()
}, },
methods: { methods: {
getExamStatusName(status) {
const map = {
0: '待完成', // 已缓存
1: '已完成', // 已提交
2: '已完成', // 已评阅
3: '待完成', // 已缓存
100: '未做',
101: '未开始',
102: '已结束'
}
return map[status] || status
},
getExamList() { getExamList() {
api.getExamList({ course_id: this.courseId }).then(response => { api.getExamList({ course_id: this.courseId }).then(response => {
// 考试状态(0,3:已缓存,1:已提交,2:已评阅,100:未做) // 考试状态(0,3:已缓存,1:已提交,2:已评阅,100:未做 101:未开始 102:已结束)
this.examList = response.papers this.examList = response.papers.map(item => {
const nowTime = new Date().getTime()
item.config = item.config ? JSON.parse(item.config) : {}
if (item.paper_type === 3 && ![1, 2].includes(item.status)) {
const startTime = new Date(item.start_time)
if (startTime > nowTime) {
item.status = 101
}
const endTime = new Date(item.end_time)
if (endTime < nowTime) {
item.status = 102
}
}
return item
})
this.courses = response.courses this.courses = response.courses
}) })
}, },
startExam(item) { startExam(item) {
this.acitveItem = item
// 考试未开始或者已结束
if ([101, 102].includes(item.status)) {
this.$message.error(`考试${this.getExamStatusName(item.status)}${item.start_time} ~ ${item.end_time})`)
return
}
// 未做的新试卷
if (item.paper_type === 3 && item.status === 100) {
// 考前须知
if (item.config.welcome_message) {
this.dialogVisible = true
return
}
}
if ([0, 3].includes(item.status)) { if ([0, 3].includes(item.status)) {
this.open(item) this.open(item)
} else if ([1, 2].includes(item.status)) { } else if ([1, 2].includes(item.status)) {
// 已完成 // 已完成
this.$router.push({ path: '/exam/exam/result', query: { exam_id: item.id } }) this.$router.push({ path: '/exam/exam/result', query: { exam_id: item.id, type: item.paper_type } })
} else { } else {
// 未做 // 未做
this.$router.push({ path: '/exam/exam/exam', query: { exam_id: item.id, is_create: 0 } }) this.$router.push({
path: '/exam/exam/exam',
query: { exam_id: item.id, type: item.paper_type, is_create: 0 }
})
} }
}, },
open(item) { open(item) {
...@@ -68,13 +117,27 @@ export default { ...@@ -68,13 +117,27 @@ export default {
distinguishCancelAndClose: true distinguishCancelAndClose: true
}) })
.then(() => { .then(() => {
this.$router.push({ path: '/exam/exam/exam', query: { exam_id: item.id, is_create: 0 } }) this.$router.push({
path: '/exam/exam/exam',
query: { exam_id: item.id, type: item.paper_type, is_create: 0 }
})
}) })
.catch(action => { .catch(action => {
if (action === 'cancel') { if (action === 'cancel') {
this.$router.push({ path: '/exam/exam/exam', query: { exam_id: item.id, is_create: 1 } }) this.$router.push({
path: '/exam/exam/exam',
query: { exam_id: item.id, type: item.paper_type, is_create: 1 }
})
} }
}) })
},
// 进入考试
enterExam() {
const item = this.acitveItem
this.$router.push({
path: '/exam/exam/exam',
query: { exam_id: item.id, type: item.paper_type, is_create: 0 }
})
} }
} }
} }
...@@ -91,10 +154,12 @@ export default { ...@@ -91,10 +154,12 @@ export default {
cursor: pointer; cursor: pointer;
h3 { h3 {
line-height: 30px; line-height: 30px;
min-height: 60px;
margin-bottom: 10px;
} }
p { p {
height: 24px;
line-height: 24px; line-height: 24px;
color: #c01540;
} }
} }
</style> </style>
...@@ -108,12 +108,12 @@ export default { ...@@ -108,12 +108,12 @@ export default {
goPage(param) { goPage(param) {
this.$router.push({ this.$router.push({
path: '/exam/exam/exam', path: '/exam/exam/exam',
query: { exam_id: this.examId, id: param } query: { exam_id: this.examId, id: param, type: this.$route.query.type }
}) })
}, },
getExamPapers() { getExamPapers() {
const param = { const param = {
type: 2, type: this.$route.query.type || 2,
paper_id: this.examId, paper_id: this.examId,
is_create: 0 is_create: 0
} }
......
...@@ -3,7 +3,7 @@ import qs from 'qs' ...@@ -3,7 +3,7 @@ import qs from 'qs'
import { Message } from 'element-ui' import { Message } from 'element-ui'
import router from '@/router' import router from '@/router'
const httpRequest = axios.create({ const httpRequest = axios.create({
baseURL: import.meta.env.VITE_BASE_URL, // baseURL: import.meta.env.VITE_BASE_URL,
timeout: 60000, timeout: 60000,
withCredentials: true, withCredentials: true,
headers: { headers: {
......
...@@ -2,10 +2,17 @@ import fs from 'fs' ...@@ -2,10 +2,17 @@ import fs from 'fs'
import path from 'path' import path from 'path'
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import { createVuePlugin } from 'vite-plugin-vue2' import { createVuePlugin } from 'vite-plugin-vue2'
import eslint from '@rollup/plugin-eslint' import checker from 'vite-plugin-checker'
export default defineConfig({
base: process.env.BUILD_ENV === 'prod' ? 'https://webapp-pub.ezijing.com/website/prod/x-learn/' : '/', export default defineConfig(({ mode }) => {
plugins: [eslint({ include: '**/*.+(vue|js|jsx|ts|tsx)' }), createVuePlugin()], return {
base: mode === 'prod' ? 'https://webapp-pub.ezijing.com/website/prod/x-learn/' : '/',
plugins: [
checker({
eslint: { lintCommand: 'eslint "./src/**/*.{vue,js,jsx,ts,tsx}"' }
}),
createVuePlugin()
],
server: { server: {
open: true, open: true,
host: 'dev.ezijing.com', host: 'dev.ezijing.com',
...@@ -14,12 +21,12 @@ export default defineConfig({ ...@@ -14,12 +21,12 @@ export default defineConfig({
cert: fs.readFileSync(path.join(__dirname, './https/dev.ezijing.com.pem')) cert: fs.readFileSync(path.join(__dirname, './https/dev.ezijing.com.pem'))
}, },
proxy: { proxy: {
'/api/zy': {
target: 'https://pre-xlms-api.ezijing.com',
changeOrigin: true,
rewrite: path => path.replace(/^\/api\/zy/, '')
},
'/api': 'https://project-api.ezijing.com' '/api': 'https://project-api.ezijing.com'
// '/api': {
// target: 'http://localhost-zy-api.ezijing.com',
// changeOrigin: true,
// rewrite: path => path.replace(/^\/api\/zy/, '')
// }
} }
}, },
resolve: { resolve: {
...@@ -32,6 +39,7 @@ export default defineConfig({ ...@@ -32,6 +39,7 @@ export default defineConfig({
}, },
css: { css: {
// 禁用SASS警告提醒 // 禁用SASS警告提醒
preprocessorOptions: { scss: { quietDeps: true } } preprocessorOptions: { scss: { quietDeps: true, charset: false } }
}
} }
}) })
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论