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

v2

上级 df502330
......@@ -4,7 +4,9 @@ module.exports = {
},
extends: ['plugin:vue/essential', 'standard'],
rules: {
'vue/no-mutating-props': 'off', // 暂时关闭
'vue/comment-directive': 'off',
'vue/multi-word-component-names': '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",
"scripts": {
"dev": "vite --mode dev",
"build": "cross-env BUILD_ENV=prod vite build && npm run deploy",
"build:pre": "vite build",
"dev": "vite",
"build": "vite build --mode prod && npm run deploy",
"build:pre": "vite build --mode pre",
"build:test": "vite build --mode test",
"preview": "vite preview",
"deploy": "node ./deploy.js",
"lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src"
},
"dependencies": {
"axios": "^0.24.0",
"@tinymce/tinymce-vue": "^3.2.8",
"axios": "^0.26.0",
"blueimp-md5": "^2.19.0",
"echarts": "^5.2.2",
"echarts": "^5.3.0",
"element-ui": "^2.15.6",
"js-base64": "^3.7.2",
"lodash": "^4.17.21",
"query-string": "^7.0.1",
"query-string": "^7.1.1",
"vue": "^2.6.14",
"vue-html2pdf": "^1.8.0",
"vue-router": "^3.5.3",
"vuex": "^3.6.2"
},
"devDependencies": {
"@rollup/plugin-eslint": "^8.0.1",
"ali-oss": "^6.16.0",
"ali-oss": "^6.17.1",
"chalk": "^4.1.2",
"cross-env": "^7.0.3",
"eslint": "^7.32.0",
"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-promise": "^5.1.1",
"eslint-plugin-vue": "^7.20.0",
"sass": "1.43.5",
"vite": "^2.6.14",
"vite-plugin-vue2": "^1.9.0",
"eslint-plugin-promise": "^5.2.0",
"eslint-plugin-vue": "^8.5.0",
"sass": "1.49.9",
"vite": "^2.8.6",
"vite-plugin-checker": "^0.4.2",
"vite-plugin-vue2": "^1.9.3",
"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-info: #3c4043;
// border
$--border-radius-small: 8px !default;
// checkbox
$--checkbox-border-radius: 2px !default;
// dialog
$--message-close-size: 20px !default;
......
......@@ -39,8 +39,8 @@ export default {
this.dispatch('ElFormItem', 'el.form.blur', editor.getContent())
},
dispatch(componentName, eventName, params) {
var parent = this.$parent || this.$root
var name = parent.$options.componentName
let parent = this.$parent || this.$root
let name = parent.$options.componentName
while (parent && (!name || name !== componentName)) {
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 @@
<script>
// libs
import { shuffle } from 'lodash'
import { Base64 } from 'js-base64'
// components
import Container from '../common/container.vue'
import ExamItem from './examItem.vue'
......
......@@ -79,7 +79,7 @@
<script>
// componets
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'
// api
import * as api from '../../api'
......
......@@ -67,7 +67,7 @@
<script>
// components
import VEditor from '../common/editor.vue'
import VEditor from '@/components/tinymce/Index.vue'
import VUpload from '../common/upload.vue'
export default {
......
......@@ -42,10 +42,10 @@ export default {
methods: {
// 获取考卷
getTopic() {
const isCreate =
this.$route.query.id || this.$route.query.is_create === undefined ? 0 : this.$route.query.is_create
const query = this.$route.query
const isCreate = query.id || query.is_create === undefined ? 0 : query.is_create
const param = {
type: 2, // 1:能力自测, 2:模拟考试
type: query.type || 2, // 1:能力自测, 2:模拟考试, 3:新的考试
paper_id: this.examId,
is_create: isCreate // 是否重新测试: 1:是,否则不是
}
......@@ -116,7 +116,10 @@ export default {
api.setCache(param).then(response => {
if (isCache) {
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 @@
<template v-for="item in examList">
<el-card shadow="hover" class="exam-item" :key="item.id" @click.native="startExam(item)">
<h3>{{ item.paper_title }}</h3>
<p v-if="[1, 2].includes(item.status)">已完成</p>
<p v-if="[0, 3].includes(item.status)">待完成</p>
<!-- <p>重新考试</p> -->
<!-- <p>{{ item.start_time }} ~ {{ item.end_time }}</p> -->
<p>{{ getExamStatusName(item.status) }}</p>
</el-card>
</template>
</div>
<!-- <template #footer>
<div class="app-container-ft"><el-button type="primary" @click="getExamStatus">开始考试</el-button></div>
</template> -->
<el-dialog title="考前须知" :visible.sync="dialogVisible">
<div v-html="acitveItem.config.welcome_message" v-if="acitveItem && acitveItem.config"></div>
<template #footer>
<div style="text-align: center">
<el-button type="primary" @click="enterExam">进入考试</el-button>
</div>
</template>
</el-dialog>
</app-container>
</template>
......@@ -28,7 +32,9 @@ export default {
return {
courseId: '',
courses: [],
examList: []
examList: [],
dialogVisible: false,
acitveItem: {} // 当前激活的考试
}
},
computed: {
......@@ -40,22 +46,65 @@ export default {
this.getExamList()
},
methods: {
getExamStatusName(status) {
const map = {
0: '待完成', // 已缓存
1: '已完成', // 已提交
2: '已完成', // 已评阅
3: '待完成', // 已缓存
100: '未做',
101: '未开始',
102: '已结束'
}
return map[status] || status
},
getExamList() {
api.getExamList({ course_id: this.courseId }).then(response => {
// 考试状态(0,3:已缓存,1:已提交,2:已评阅,100:未做)
this.examList = response.papers
// 考试状态(0,3:已缓存,1:已提交,2:已评阅,100:未做 101:未开始 102:已结束)
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
})
},
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)) {
this.open(item)
} 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 {
// 未做
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) {
......@@ -68,13 +117,27 @@ export default {
distinguishCancelAndClose: true
})
.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 => {
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 {
cursor: pointer;
h3 {
line-height: 30px;
min-height: 60px;
margin-bottom: 10px;
}
p {
height: 24px;
line-height: 24px;
color: #c01540;
}
}
</style>
......@@ -108,12 +108,12 @@ export default {
goPage(param) {
this.$router.push({
path: '/exam/exam/exam',
query: { exam_id: this.examId, id: param }
query: { exam_id: this.examId, id: param, type: this.$route.query.type }
})
},
getExamPapers() {
const param = {
type: 2,
type: this.$route.query.type || 2,
paper_id: this.examId,
is_create: 0
}
......
......@@ -3,7 +3,7 @@ import qs from 'qs'
import { Message } from 'element-ui'
import router from '@/router'
const httpRequest = axios.create({
baseURL: import.meta.env.VITE_BASE_URL,
// baseURL: import.meta.env.VITE_BASE_URL,
timeout: 60000,
withCredentials: true,
headers: {
......
......@@ -2,36 +2,44 @@ import fs from 'fs'
import path from 'path'
import { defineConfig } from 'vite'
import { createVuePlugin } from 'vite-plugin-vue2'
import eslint from '@rollup/plugin-eslint'
export default defineConfig({
base: process.env.BUILD_ENV === 'prod' ? 'https://webapp-pub.ezijing.com/website/prod/x-learn/' : '/',
plugins: [eslint({ include: '**/*.+(vue|js|jsx|ts|tsx)' }), createVuePlugin()],
server: {
open: true,
host: 'dev.ezijing.com',
https: {
key: fs.readFileSync(path.join(__dirname, './https/dev.ezijing.com.key')),
cert: fs.readFileSync(path.join(__dirname, './https/dev.ezijing.com.pem'))
import checker from 'vite-plugin-checker'
export default defineConfig(({ mode }) => {
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: {
open: true,
host: 'dev.ezijing.com',
https: {
key: fs.readFileSync(path.join(__dirname, './https/dev.ezijing.com.key')),
cert: fs.readFileSync(path.join(__dirname, './https/dev.ezijing.com.pem'))
},
proxy: {
'/api/zy': {
target: 'https://pre-xlms-api.ezijing.com',
changeOrigin: true,
rewrite: path => path.replace(/^\/api\/zy/, '')
},
'/api': 'https://project-api.ezijing.com'
}
},
resolve: {
alias: [
{
find: '@',
replacement: path.resolve(__dirname, 'src')
}
]
},
proxy: {
'/api': 'https://project-api.ezijing.com'
// '/api': {
// target: 'http://localhost-zy-api.ezijing.com',
// changeOrigin: true,
// rewrite: path => path.replace(/^\/api\/zy/, '')
// }
css: {
// 禁用SASS警告提醒
preprocessorOptions: { scss: { quietDeps: true, charset: false } }
}
},
resolve: {
alias: [
{
find: '@',
replacement: path.resolve(__dirname, 'src')
}
]
},
css: {
// 禁用SASS警告提醒
preprocessorOptions: { scss: { quietDeps: true } }
}
})
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论