题库分类及题库联调

master
yujialong 4 months ago
parent 3e9b0a4a52
commit 15897f24e5
  1. 6
      public/index.html
  2. 3
      public/static/ueditor/ueditor.config.js
  3. 13
      src/api/index.js
  4. 13
      src/components/ueditor/index.vue
  5. 2
      src/layouts/home/index.vue
  6. 13
      src/pages/ques/index.vue
  7. 347
      src/pages/quesBank/index.vue
  8. 104
      src/pages/quesBankType/index.vue
  9. 92
      src/plugins/requests/index.js
  10. 2
      src/setting.js

@ -16,8 +16,8 @@
<script src='./static/ueditor/ueditor.config.js?v=3'></script> <script src='./static/ueditor/ueditor.config.js?v=3'></script>
<script src='./static/ueditor/ueditor.all.js?v=3'></script> <script src='./static/ueditor/ueditor.all.js?v=3'></script>
<script src='./static/ueditor/lang/zh-cn/zh-cn.js'></script> <script src='./static/ueditor/lang/zh-cn/zh-cn.js'></script>
<script src='./static/ueditor/kityformula-plugin/addKityFormulaDialog.js'></script> <!-- <script src='./static/ueditor1/kityformula-plugin/addKityFormulaDialog.js'></script>
<script src='./static/ueditor/kityformula-plugin/getKfContent.js'></script> <script src='./static/ueditor1/kityformula-plugin/getKfContent.js'></script>
<script src='./static/ueditor/kityformula-plugin/defaultFilterFix.js'></script> <script src='./static/ueditor1/kityformula-plugin/defaultFilterFix.js'></script> -->
</body> </body>
</html> </html>

@ -1,6 +1,6 @@
!function () { !function () {
window.UEDITOR_HOME_URL = "./static/ueditor/"; var s = window.UEDITOR_HOME_URL || l(); function l (s, l) { return function (s, l) { var t = l; /^(\/|\\\\)/.test(l) ? t = /^.+?\w(\/|\\\\)/.exec(s)[0] + l.replace(/^(\/|\\\\)/, "") : /^[a-z]+:/i.test(l) || (s = s.split("#")[0].split("?")[0].replace(/[^\\\/]+$/, ""), t = s + "" + l); return function (s) { var l = /^[a-z]+:\/\//.exec(s)[0], t = null, e = []; (s = (s = s.replace(l, "").split("?")[0].split("#")[0]).replace(/\\/g, "/").split(/\//))[s.length - 1] = ""; for (; s.length;)".." === (t = s.shift()) ? e.pop() : "." !== t && e.push(t); return l + e.join("/") }(t) }(s || self.document.URL || self.location.href, l || (t = document.getElementsByTagName("script"))[t.length - 1].src); var t } window.UEDITOR_CONFIG = { window.UEDITOR_HOME_URL = "./static/ueditor/"; var s = window.UEDITOR_HOME_URL || l(); function l (s, l) { return function (s, l) { var t = l; /^(\/|\\\\)/.test(l) ? t = /^.+?\w(\/|\\\\)/.exec(s)[0] + l.replace(/^(\/|\\\\)/, "") : /^[a-z]+:/i.test(l) || (s = s.split("#")[0].split("?")[0].replace(/[^\\\/]+$/, ""), t = s + "" + l); return function (s) { var l = /^[a-z]+:\/\//.exec(s)[0], t = null, e = []; (s = (s = s.replace(l, "").split("?")[0].split("#")[0]).replace(/\\/g, "/").split(/\//))[s.length - 1] = ""; for (; s.length;)".." === (t = s.shift()) ? e.pop() : "." !== t && e.push(t); return l + e.join("/") }(t) }(s || self.document.URL || self.location.href, l || (t = document.getElementsByTagName("script"))[t.length - 1].src); var t } window.UEDITOR_CONFIG = {
UEDITOR_HOME_URL: s, serverUrl: "http://192.168.31.51:9000/exam/upload/configAndUpload", toolbars: [[ UEDITOR_HOME_URL: s, serverHeaders: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, Authorization', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS' }, serverUrl: "http://192.168.31.51:9000/exam/exam/upload/configAndUpload", toolbars: [[
"fullscreen", "fullscreen",
"source", "source",
"|", "|",
@ -57,7 +57,6 @@
"imageright", "imageright",
"imagecenter", "imagecenter",
"|", "|",
"simpleupload",
"insertimage", "insertimage",
"emotion", "emotion",
"scrawl", "scrawl",

@ -1,6 +1,17 @@
import Setting from '@/setting' import Setting from '@/setting'
const { apiBaseURL: host } = Setting const { apiBaseURL: host } = Setting
// const host = 'http://192.168.31.217:9000/'
export default { export default {
upload: `${host}/iasf/sysFiles/upload`, categoriesDel: `/exam/question/bank/categories/batchDeletion`,
categoriesFind: `/exam/question/bank/categories/findById`,
getAllQuestionBankCategories: `/exam/question/bank/categories/getAllQuestionBankCategories`,
categoriesSave: `/exam/question/bank/categories/saveOrUpdate`,
categoriesDisable: `/exam/question/bank/categories/updateStatus`,
questionBankDel: `/exam/questionBank/batchDeletion`,
questionBankFind: `/exam/questionBank/findById`,
questionBankList: `/exam/questionBank/pagingQuery`,
questionBankSave: `/exam/questionBank/saveOrUpdate`,
questionBankDisable: `/exam/questionBank/updateStatus`,
} }

@ -43,10 +43,23 @@ export default {
}, },
methods: { methods: {
initEditor () { initEditor () {
console.log(44, UE.Editor.prototype.getActionUrl)
this.$nextTick(() => { this.$nextTick(() => {
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
this.instance = UE.getEditor(this.randomId) this.instance = UE.getEditor(this.randomId)
this.instance.addListener('ready', () => { this.instance.addListener('ready', () => {
// UE.Editor.prototype._bkGetActionUrl = UE.Editor.prototype.getActionUrl;
// UE.Editor.prototype.getActionUrl = function (action) {
// console.log("🚀 ~ initEditor ~ action:", action)
// if (action == 'uploadimage' || action == 'uploadscrawl' || action == 'uploadimage') {
// return 'http://192.168.31.51:9000/nakadai/nakadai/oss/fileUpload';
// } else {
// return this._bkGetActionUrl.call(this, action);
// }
// }
this.ready = true this.ready = true
this.$emit('ready', this.instance) this.$emit('ready', this.instance)
}) })

@ -31,7 +31,7 @@ export default {
}, },
mounted () { mounted () {
const { token } = this.$route.query const { token } = this.$route.query
Util.local.set(Setting.tokenKey, token, Setting.tokenExpires) token && Util.local.set(Setting.tokenKey, token, Setting.tokenExpires)
}, },
}; };
</script> </script>

@ -202,7 +202,7 @@
<el-upload name="file" accept=".xls,.xlsx" ref="upload" class="import-file" drag :before-upload="beforeUpload" <el-upload name="file" accept=".xls,.xlsx" ref="upload" class="import-file" drag :before-upload="beforeUpload"
:on-remove="handleRemove" :on-error="uploadError" :on-success="uploadSuccess" :before-remove="beforeRemove" :on-remove="handleRemove" :on-error="uploadError" :on-success="uploadSuccess" :before-remove="beforeRemove"
:limit="1" :data="{ :limit="1" :data="{
competitionId: id, // competitionId: id,
platformId: 2 platformId: 2
}" :disabled="uploading" :on-exceed="handleExceed" :action="this.api.batchImportPersonalData" }" :disabled="uploading" :on-exceed="handleExceed" :action="this.api.batchImportPersonalData"
:file-list="uploadList" :headers="headers"> :file-list="uploadList" :headers="headers">
@ -232,6 +232,7 @@ export default {
components: { Ueditor }, components: { Ueditor },
data () { data () {
return { return {
radio: '',
type: 1, type: 1,
orgList: [], orgList: [],
status: [ status: [
@ -524,11 +525,11 @@ export default {
this.quesVisible = true this.quesVisible = true
}, },
editorReady (instance) { editorReady (instance) {
this.richEditor.instance = instance // this.richEditor.instance = instance
let currentContent = this.richEditor.object[this.richEditor.parameterName] // let currentContent = this.richEditor.object[this.richEditor.parameterName]
this.richEditor.instance.setContent(currentContent) // this.richEditor.instance.setContent(currentContent)
// Ueditor // // Ueditor
this.richEditor.instance.focus(true) // this.richEditor.instance.focus(true)
}, },
// //
quesSubmit () { quesSubmit () {

@ -3,23 +3,24 @@
<div class="side"> <div class="side">
<div class="m-b-20"> <div class="m-b-20">
<h6 class="page-name">题库分类</h6> <h6 class="page-name">题库分类</h6>
<el-radio-group v-model="type" @change="typeChange"> <el-radio-group v-model="isNotJoin" @change="typeChange">
<div class="m-b-10"> <div class="m-b-10">
<el-radio :label="1">所有题库</el-radio> <el-radio :label="0">所有题库</el-radio>
</div> </div>
<div> <div>
<el-radio :label="2">未加入分类的题库</el-radio> <el-radio :label="1">未加入分类的题库</el-radio>
</div> </div>
</el-radio-group> </el-radio-group>
</div> </div>
<el-divider></el-divider> <el-divider></el-divider>
<div> <div>
<el-input class="m-b-10" placeholder="请输入题库分类" prefix-icon="el-icon-search" size="small" clearable></el-input> <el-input class="m-b-10" placeholder="请输入题库分类" prefix-icon="el-icon-search" size="small" clearable
v-model="keyword"></el-input>
<div style="height: 504px; max-height: 504px; overflow: auto"> <div style="height: 504px; max-height: 504px; overflow: auto">
<el-tree :data="orgList" default-expand-all ref="orgTree" node-key="id" highlight-current <el-tree :data="types" default-expand-all ref="typeTree" node-key="id" highlight-current
:expand-on-click-node="false" @node-click="handleNodeClick" :expand-on-click-node="false" @node-click="handleNodeClick"
:props="{ children: 'children', label: 'partnerClassificationName', isLeaf: 'leaf' }"></el-tree> :props="{ children: 'children', label: 'name', isLeaf: 'leaf' }"></el-tree>
</div> </div>
</div> </div>
</div> </div>
@ -36,7 +37,7 @@
</li> </li>
<li> <li>
<label>搜索</label> <label>搜索</label>
<el-input style="width: 250px;" placeholder="请输入题库名称" prefix-icon="el-icon-search" v-model="filter.keyWord" <el-input style="width: 250px;" placeholder="请输入题库名称" prefix-icon="el-icon-search" v-model="filter.name"
clearable></el-input> clearable></el-input>
</li> </li>
</ul> </ul>
@ -46,23 +47,25 @@
</div> </div>
<el-table :data="list" class="table" ref="table" stripe header-align="center" <el-table :data="list" class="table" ref="table" stripe header-align="center"
@selection-change="handleSelectionChange" row-key="id"> @selection-change="handleSelectionChange" row-key="id" @sort-change="sortChange">
<el-table-column type="selection" width="55" align="center" :reserve-selection="true"></el-table-column> <el-table-column type="selection" width="55" align="center" :reserve-selection="true"></el-table-column>
<el-table-column type="index" width="60" label="序号" align="center"></el-table-column> <el-table-column type="index" width="60" label="序号" align="center"></el-table-column>
<el-table-column prop="userName" label="题库名称" align="center" min-width="100"></el-table-column> <el-table-column prop="questionBankName" label="题库名称" align="center" min-width="120"></el-table-column>
<el-table-column prop="account" label="描述" align="center" min-width="100"></el-table-column> <el-table-column prop="questionBankDescription" label="描述" align="center" min-width="120"
<el-table-column prop="phone" label="题库分类" align="center" min-width="120"></el-table-column> show-overflow-tooltip></el-table-column>
<el-table-column prop="invitationAccount" label="题目数量" align="center" min-width="120"></el-table-column> <el-table-column prop="questionBankCategory" label="题库分类" align="center" min-width="120"></el-table-column>
<el-table-column prop="loginNumber" label="创建时间" align="center" width="120"></el-table-column> <el-table-column prop="questionsNum" label="题目数量" align="center" width="100"
<el-table-column prop="lastLoginTime" label="创建人" align="center" width="120"></el-table-column> sortable="custom"></el-table-column>
<el-table-column label="操作" align="center" width="300"> <el-table-column prop="createTime" label="创建时间" align="center" width="160" sortable="custom"></el-table-column>
<el-table-column prop="createUserName" label="创建人" align="center" width="100"></el-table-column>
<el-table-column label="操作" align="center" width="280">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="text" @click="edit(scope.row)">试题管理</el-button> <el-button type="text" @click="edit(scope.row)">试题管理</el-button>
<el-button type="text" @click="edit(scope.row)">编辑</el-button> <el-button type="text" @click="edit(scope.row, 0)">编辑</el-button>
<el-button type="text" @click="del(scope.row)">复制</el-button> <el-button type="text" @click="edit(scope.row, 1)">复制</el-button>
<el-button type="text" @click="del(scope.row)">删除</el-button> <el-button type="text" @click="del(scope.row)">删除</el-button>
<el-switch v-model="scope.row.ztOpen" :active-value="0" :inactive-value="1" style="margin: 0 10px 0 5px" <el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0" style="margin: 0 10px 0 5px"
:active-text="scope.row.ztOpen ? '关' : '开'" :active-text="scope.row.status ? '开' : '关'"
@change="switchOff($event, scope.row, scope.$index)"></el-switch> @change="switchOff($event, scope.row, scope.$index)"></el-switch>
</template> </template>
</el-table-column> </el-table-column>
@ -72,20 +75,19 @@
:total="total"></el-pagination> :total="total"></el-pagination>
</div> </div>
<el-dialog :title="!form.id ? '创建题库' : '编辑题库'" :visible.sync="quesBankVisible" width="400px" <el-dialog :title="isCopy ? '复制题库' : !form.id ? '创建题库' : '编辑题库'" :visible.sync="quesBankVisible" width="400px"
:close-on-click-modal="false"> :close-on-click-modal="false">
<el-form label-width="80px"> <p v-if="isCopy" style="margin: -10px 0 20px;font-size: 12px;color: #f00;">您将复制当前题库中的所有题目到新题库请输入新题库名称</p>
<el-form :model="form" :rules="rules" label-width="80px">
<el-form-item prop="userName" label="分类"> <el-form-item prop="userName" label="分类">
<el-select style="width: 100%" v-model="form.provinceId" placeholder="请选择题库分类"> <el-cascader placeholder="请选择题库分类" v-model="form.categoryIds" :options="enableTypes"
<el-option v-for="(item, i) in types" :key="i" :label="item.provinceName" :props="{ value: 'id', label: 'name', checkStrictly: true }" clearable></el-cascader>
:value="item.provinceId"></el-option>
</el-select>
</el-form-item> </el-form-item>
<el-form-item prop="userName" label="题库名称"> <el-form-item prop="questionBankName" label="题库名称">
<el-input placeholder="请输入题库名称" v-model="form.name"></el-input> <el-input placeholder="请输入题库名称" v-model="form.questionBankName"></el-input>
</el-form-item> </el-form-item>
<el-form-item prop="userName" label="题库描述"> <el-form-item prop="questionBankDescription" label="题库描述">
<el-input placeholder="请输入题库描述" type="textarea" :rows="3" v-model="form.name"></el-input> <el-input placeholder="请输入题库描述" type="textarea" :rows="3" v-model="form.questionBankDescription"></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
@ -103,21 +105,25 @@ import Setting from "@/setting";
export default { export default {
data () { data () {
return { return {
type: 1, loading: false,
orgList: [], keyword: '',
isNotJoin: 0,
types: [],
status: [ status: [
{ {
id: 1, id: 1,
name: '启用' name: '启用'
}, },
{ {
id: 2, id: 0,
name: '禁用' name: '禁用'
}, },
], ],
filter: { filter: {
questionNumOrderBy: '',
timeOrderBy: '',
status: '', status: '',
keyWord: '', name: '',
}, },
list: [], list: [],
page: 1, page: 1,
@ -125,136 +131,96 @@ export default {
total: 0, total: 0,
multipleSelection: [], multipleSelection: [],
types: [],
form: { form: {
userName: '', questionBankDescription: '',
provinceId: '', questionBankName: '',
cityId: '', categoryIds: [],
roleList: []
}, },
rules: { rules: {
userName: [ questionBankName: [
{ required: true, message: "请输入姓名", trigger: "blur" } { required: true, message: '请输入题库名称', trigger: 'blur' }
], ]
provinceId: [
{ required: true, message: '请选择省份', trigger: "change" }
],
cityId: [
{ required: true, message: '请选择城市', trigger: "change" }
],
roleList: [
{ required: true, message: '请选择角色', trigger: "change" }
],
}, },
enableTypes: [],
quesBankVisible: false, quesBankVisible: false,
submiting: false,
isCopy: false,
submiting: false, //
setKey: '',
transferVisible: false,
chooseVisible: false,
members: [],
choosePartnerId: '',
curRow: '',
provinces: [],
cities: [],
editVisible: false
}; };
}, },
watch: { watch: {
keyWord: function (val) { keyword: function (val) {
clearTimeout(this.searchTimer); clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(this.initData, 500); this.searchTimer = setTimeout(this.getType, 500)
},
'filter.name': function (val) {
clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(this.initData, 500)
} }
}, },
mounted () { mounted () {
this.getOrg() this.getType()
}, },
methods: { methods: {
// //
async getOrg () { async getType () {
const res = await this.$post(this.api.listParner) try {
const list = res.treeList this.loading = true
// children const { data } = await this.$post(this.api.getAllQuestionBankCategories, {
const handleLeaf = (list, ids) => { keyword: this.keyword,
list.map(e => { createSource: 1,
e.ids = ids ? [...ids, e.id] : [e.id]
if (e.children.length) {
if (e.isTeam) {
delete e.children
} else {
handleLeaf(e.children, e.ids)
}
} else {
delete e.children
}
}) })
} this.handleList(data)
handleLeaf(list) this.types = data
this.orgList = list
// //
this.setKey && this.$nextTick(() => { this.setKey && this.$nextTick(() => {
this.$refs.orgTree.setCurrentKey(this.setKey) this.$refs.typeTree.setCurrentKey(this.setKey)
}) })
this.getList() this.getList()
} finally {
this.loading = false
}
},
//
handleList (list) {
list.map(e => {
if (e.children && e.children.length) {
this.handleList(e.children)
} else {
delete e.children
}
})
}, },
// //
typeChange () { typeChange () {
this.$refs.orgTree.setCurrentKey(null) this.$refs.typeTree.setCurrentKey(null)
this.curTeamId = ''
this.initData() this.initData()
}, },
// id
getTeamId (list) {
for (const i in list) {
const e = list[i]
if (e.isTeam && !this.curTeamId) {
this.curTeamId = e.id
break
} else {
this.getTeamId(e.children)
}
}
},
// //
handleNodeClick (data) { handleNodeClick (data) {
this.type = '' this.isNotJoin = ''
this.curTeamId = ''
if (data.isTeam) {
this.curTeamId = data.id
} else {
// this.getTeamId(data.children)
}
if (!this.curTeamId) this.curTeamId = data.id
this.initData() this.initData()
this.$refs.table.clearSelection() this.$refs.table.clearSelection()
}, },
// //
getAll () { async getList () {
this.curTeamId = '' const res = await this.$post(this.api.questionBankList, {
this.getList() ...this.filter,
}, isNotJoin: this.isNotJoin || '',
//
getList () {
this.$post(this.api[this.type ? 'partnerAccountMergeList' : 'partnerAccountList'], {
type: this.type || 1,
partnerClassificationId: this.curTeamId,
pageNum: this.page, pageNum: this.page,
pageSize: this.pageSize pageSize: this.pageSize,
}).then(({ pageList }) => { questionCategoryId: this.$refs.typeTree.getCurrentKey() || '',
pageList.records.forEach((e, i) => {
e.id = i
}) })
this.list = pageList.records this.list = res.message.records
this.total = pageList.total this.total = res.message.total
}).catch(err => { })
}, },
// //
currentChange (val) { currentChange (val) {
this.page = val this.page = val
this.getList() this.getList()
}, },
handleSelectionChange (val) { // //
handleSelectionChange (val) {
this.multipleSelection = val this.multipleSelection = val
}, },
initData () { initData () {
@ -262,80 +228,93 @@ export default {
this.page = 1 this.page = 1
this.getList() this.getList()
}, },
//
sortChange (column) {
// 12
if (column.prop === 'questionNumOrderBy') this.filter.questionNumOrderBy = column.order ? column.order === 'ascending' ? 'asc' : 'desc' : ''
if (column.prop === 'createTime') this.filter.timeOrderBy = column.order ? column.order === 'ascending' ? 'asc' : 'desc' : ''
this.getList()
},
// //
del (row) { async del (row) {
this.$confirm("确定要删除吗?", "提示", { try {
type: "warning" await this.$confirm(`<p>确认要删除【${row.questionBankName}】吗?</p><p style="color: #f56c6c;">删除后,题库中的知识点框架与题目将会被删除,请谨慎操作!</p>`, '提示', {
}).then(() => { confirmButtonText: '确定',
this.$post(`${this.api.delPartnerAccount}?accountId=${row.accountId}`).then(res => { cancelButtonText: '取消',
Util.successMsg("删除成功") type: 'warning',
closeOnClickModal: false,
dangerouslyUseHTMLString: true,
})
await this.$post(this.api.questionBankDel, [row.id])
Util.successMsg('删除成功')
this.getList() this.getList()
}).catch(res => { }) } catch (e) { }
}).catch(() => { })
}, },
async switchOff (val, row) { async switchOff (val, row) {
this.$post(this.api.disabledEventsCompetition, { await this.$post(`${this.api.questionBankDisable}?id=${row.id}&status=${val}`)
competitionId: row.id, Util.successMsg(val ? '启用成功' : '禁用成功')
isOpen: val,
type: 0 // (01)
}).then(res => {
Util.successMsg(val == 1 ? '禁用成功' : '启用成功')
}).catch(err => { })
await this.$post(`${this.api.refreshPageNotification}?content=1`)
},
//
edit (row) {
if (!row.provinceId) row.provinceId = ''
if (!row.cityId) row.cityId = ''
row.roleList = row.roleId.split(',').map(e => +e)
this.editVisible = true
this.form = JSON.parse(JSON.stringify(row))
},
//
submitEdit () {
this.$refs.form.validate((valid) => {
if (valid) {
if (this.submiting) return false
this.submiting = true
const form = JSON.parse(JSON.stringify(this.form))
form.classificationId = form.partnerClassificationId
this.$post(this.api.editProvinceCity, form).then(res => {
this.getList() this.getList()
Util.successMsg("编辑成功!")
this.editVisible = false
setTimeout(() => {
this.submiting = false
}, 2000)
}).catch(res => {
setTimeout(() => {
this.submiting = false
}, 2000)
})
}
})
}, },
// //
add () { add () {
this.getEnableType()
this.isCopy = false
const type = this.$refs.typeTree.getCurrentNode()
this.form = {
questionBankDescription: '',
questionBankName: '',
categoryIds: type ? type.path.split('/').map(e => +e) : [],
}
this.quesBankVisible = true this.quesBankVisible = true
}, },
// /
async edit (row, isCopy) {
this.getEnableType()
this.isCopy = isCopy
const res = await this.$post(`${this.api.questionBankFind}?id=${row.id}`)
const type = res.message.questionBankCategoryRelations
this.quesBankVisible = true
this.form = {
id: row.id,
questionBankName: row.questionBankName,
questionBankDescription: row.questionBankDescription,
categoryIds: type && type.length ? type[0].path.split('/').map(e => +e) : [],
}
},
//
async getEnableType () {
if (!this.enableTypes.length) {
try {
const { data } = await this.$post(this.api.getAllQuestionBankCategories, {
createSource: 1,
status: 1,
})
this.handleList(data)
this.enableTypes = data
} catch (e) { }
}
},
// //
quesBankSubmit () { async quesBankSubmit () {
if (this.submiting) return false
const { form } = this
if (!form.categoryIds.length) return Util.warningMsg('请选择分类')
if (!form.questionBankName) return Util.warningMsg('请输入题库名称')
this.submiting = true
if (form.categoryIds.length) form.categoryIds = [form.categoryIds[form.categoryIds.length - 1]]
// form.systemId = this.systemId
form.createSource = 1
await this.$post(this.api.questionBankSave, form)
Util.successMsg('保存成功')
this.quesBankVisible = false
this.submiting = false
this.getList()
}, },
} }
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.org-name {
margin-right: 20px;
}
.w-100 {
width: 100%;
}
.page { .page {
display: flex; display: flex;
padding: 0 24px; padding: 0 24px;

@ -10,7 +10,7 @@
</li> </li>
<li> <li>
<label>搜索</label> <label>搜索</label>
<el-input style="width: 250px;" placeholder="请输入题库分类名称" prefix-icon="el-icon-search" v-model="filter.keyWord" <el-input style="width: 250px;" placeholder="请输入题库分类名称" prefix-icon="el-icon-search" v-model="filter.keyword"
clearable></el-input> clearable></el-input>
</li> </li>
</ul> </ul>
@ -20,19 +20,21 @@
</div> </div>
</div> </div>
<el-table v-loading="loading" :data="list" class="table" ref="table" stripe header-align="center" row-key="id" <el-table v-loading="loading" :data="list" class="table" ref="table" stripe header-align="center" row-key="id"
default-expand-all :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"> default-expand-all :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" :reserve-selection="true"></el-table-column>
<el-table-column type="index" width="100" label="序号" align="center"></el-table-column> <el-table-column type="index" width="100" label="序号" align="center"></el-table-column>
<el-table-column prop="name" label="题库分类名称" align="center"></el-table-column> <el-table-column prop="name" label="题库分类名称"></el-table-column>
<el-table-column prop="orderNum" label="已关联题库" align="center"></el-table-column> <el-table-column prop="orderNum" label="已关联题库" align="center"></el-table-column>
<el-table-column prop="createTime" label="创建时间" align="center"></el-table-column> <el-table-column prop="createTime" label="创建时间" align="center"></el-table-column>
<el-table-column prop="orderVolume" label="创建人" align="center"></el-table-column> <el-table-column prop="createUserName" label="创建人" align="center"></el-table-column>
<el-table-column label="操作" align="center" width="300"> <el-table-column label="操作" align="center" width="300">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="text" @click="add(scope.row)">新增下级</el-button> <el-button type="text" @click="add(scope.row)">新增下级</el-button>
<el-button type="text" @click="edit(scope.row)">编辑</el-button> <el-button type="text" @click="edit(scope.row)">编辑</el-button>
<el-button type="text" @click="del(scope.row)">删除</el-button> <el-button type="text" @click="del(scope.row)">删除</el-button>
<el-switch v-model="scope.row.status" :active-value="0" :inactive-value="1" style="margin: 0 10px 0 5px" <el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0" style="margin: 0 10px 0 5px"
:active-text="scope.row.ztOpen ? '关' : '开'" @change="switchOff($event, scope.row)"></el-switch> :active-text="scope.row.status ? '开' : '关'" @change="switchOff($event, scope.row)"></el-switch>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -40,18 +42,18 @@
<el-dialog :title="(!form.id ? '新增' : '编辑') + '题库分类'" :visible.sync="typeVisible" width="400px" <el-dialog :title="(!form.id ? '新增' : '编辑') + '题库分类'" :visible.sync="typeVisible" width="400px"
:close-on-click-modal="false"> :close-on-click-modal="false">
<el-form label-width="110px"> <el-form :model="form" :rules="rules" label-width="110px">
<el-form-item prop="name" label="题库分类名称" required> <el-form-item prop="name" label="题库分类名称">
<el-input placeholder="请输入分类名称" v-model="form.name" maxlength="20"></el-input> <el-input placeholder="请输入分类名称" v-model="form.name" maxlength="20"></el-input>
</el-form-item> </el-form-item>
<el-form-item prop="userName" label="设置上一级"> <el-form-item label="设置上一级">
<el-cascader v-model="parentId" :options="list" :props="{ value: 'id', label: 'name', checkStrictly: true }" <el-cascader v-model="parentId" :options="list" :props="{ value: 'id', label: 'name', checkStrictly: true }"
clearable></el-cascader> clearable></el-cascader>
</el-form-item> </el-form-item>
</el-form> </el-form>
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
<el-button @click="typeVisible = false">取消</el-button> <el-button @click="typeVisible = false">取消</el-button>
<el-button type="primary" @click="typeSubmit">确定</el-button> <el-button type="primary" v-loading="submiting" @click="typeSubmit">确定</el-button>
</span> </span>
</el-dialog> </el-dialog>
@ -62,7 +64,7 @@
<div> <div>
<p>确认要删除{{ curName }}</p> <p>确认要删除{{ curName }}</p>
<p class="tips">删除后此题库分类及其子分类将被删除关联题库将无分类</p> <p class="tips">删除后此题库分类及其子分类将被删除关联题库将无分类</p>
<el-checkbox v-model="delCheck">同时删除此分类及其子分类的题库删除后数据无法恢复请谨慎操作</el-checkbox> <el-checkbox v-model="deleteQuestions">同时删除此分类及其子分类的题库删除后数据无法恢复请谨慎操作</el-checkbox>
</div> </div>
</div> </div>
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
@ -78,75 +80,113 @@ import Util from '@/libs/util'
export default { export default {
data () { data () {
return { return {
systemId: '',
status: [ status: [
{ {
id: 1, id: 1,
name: '启用' name: '启用'
}, },
{ {
id: 2, id: 0,
name: '禁用' name: '禁用'
}, },
], ],
filter: { filter: {
createSource: 1,
status: '', status: '',
keyWord: '', keyword: '',
}, },
list: [], list: [],
multipleSelection: [],
parentId: [], parentId: [],
form: { form: {
name: '', name: '',
}, },
rules: {
name: [
{ required: true, message: '请输入题库分类名称', trigger: 'blur' }
]
},
typeVisible: false, typeVisible: false,
loading: false, loading: false,
searchTimer: null, searchTimer: null,
delVisible: false, delVisible: false,
delCheck: false, deleteQuestions: false,
curName: '', curName: '',
curId: [], curId: [],
submiting: false,
}; };
}, },
watch: { watch: {
"form.curriculumName": function (val) { 'filter.keyword': function (val) {
clearTimeout(this.searchTimer); clearTimeout(this.searchTimer);
this.searchTimer = setTimeout(() => { this.searchTimer = setTimeout(() => {
this.initData(); this.getData()
}, 500); }, 500)
} }
}, },
mounted () { mounted () {
// this.getData() this.getData()
}, },
methods: { methods: {
async getData () { async getData () {
try {
this.loading = true this.loading = true
const { data } = await this.$post(this.api.getAllCategories) const { data } = await this.$post(this.api.getAllQuestionBankCategories, {
...this.filter
})
this.handleList(data)
this.list = data this.list = data
} finally {
this.loading = false this.loading = false
}
}, },
initData () { //
this.page = 1 handleList (list) {
this.getData() list.map(e => {
if (e.children && e.children.length) {
this.handleList(e.children)
} else {
delete e.children
}
})
},
handleSelectionChange (val) {
this.multipleSelection = val
}, },
// //
add () { add (row) {
this.form = { this.form = {
name: '', name: '',
} }
if (row) this.parentId = row.path.split('/').map(e => +e)
this.typeVisible = true this.typeVisible = true
}, },
// //
edit (row) { async edit (row) {
this.$router.push(`/addcurriculum?cid=${row.cid}`); const { data } = await this.$post(`${this.api.categoriesFind}?id=${row.id}`)
this.typeVisible = true
this.form = {
id: data.id,
name: data.name,
}
this.parentId = data.path.split('/').map(e => +e)
}, },
// //
async typeSubmit () { async typeSubmit () {
const { form } = this if (this.submiting) return false
const { form, parentId } = this
if (!form.name) return Util.warningMsg('请输入题库分类名称') if (!form.name) return Util.warningMsg('请输入题库分类名称')
this.submiting = true
if (parentId.length) form.parentId = parentId[parentId.length - 1]
// form.systemId = this.systemId
form.createSource = 1
await this.$post(this.api.categoriesSave, form) await this.$post(this.api.categoriesSave, form)
Util.successMsg('保存成功') Util.successMsg('保存成功')
this.typeVisible = false this.typeVisible = false
this.submiting = false
this.getData()
}, },
// //
del (row) { del (row) {
@ -156,9 +196,12 @@ export default {
}, },
// //
async delSubmit () { async delSubmit () {
await this.$post(this.api.categoriesDel, this.curId) await this.$post(`${this.api.categoriesDel}?deleteQuestions=${this.deleteQuestions}`, this.curId)
Util.successMsg('删除成功') Util.successMsg('删除成功')
this.multipleSelection = []
this.$refs.table.clearSelection()
this.delVisible = false this.delVisible = false
this.getData()
}, },
// //
delAllSelection () { delAllSelection () {
@ -175,16 +218,19 @@ export default {
async switchOff (val, row) { async switchOff (val, row) {
try { try {
if (!val) { if (!val) {
await this.$confirm(`<p>确认要禁用${row.name}吗?</p><p style="color: #f56c6c;">禁用后,此题库分类及其子分类将被禁用</p>`, '提示', { row.status = 1
await this.$confirm(`<p>确认要禁用【${row.name}】吗?</p><p style="color: #f56c6c;">禁用后,此题库分类及其子分类将被禁用</p>`, '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning', type: 'warning',
closeOnClickModal: false, closeOnClickModal: false,
dangerouslyUseHTMLString: true, dangerouslyUseHTMLString: true,
}) })
row.status = 0
} }
await this.$post(`${this.api.categoriesDisable}?categoryId=${row.id}&status=${row.status}`) await this.$post(`${this.api.categoriesDisable}?categoryId=${row.id}&status=${row.status}`)
Util.successMsg(val == 1 ? '禁用成功' : '启用成功') Util.successMsg(val ? '启用成功' : '禁用成功')
this.getData()
} catch (e) { } } catch (e) { }
}, },
} }

@ -1,21 +1,20 @@
import axios from 'axios' import axios from "axios";
import Util from '@/libs/util' import Util from "@/libs/util";
import Setting from '@/setting' import router from "@/router/index";
import Setting from "@/setting";
import store from '@/store' import store from '@/store'
import Router from '@/router'
const service = axios.create({ const service = axios.create({
baseURL: Setting.apiBaseURL, baseURL: Setting.apiBaseURL,
timeout: 1000 * 60 * 60 * 6 timeout: 10000000
}); });
// post请求头 // post请求头
service.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8' service.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
service.defaults.headers['X-Content-Type-Options'] = 'nosniff'
service.defaults.headers['Content-Security-Policy'] = 'script-src "self"; object-src "none";style-src cdn.example.org third-party.org; child-src https:'
// 请求拦截器 // 请求拦截器
service.interceptors.request.use(config => { service.interceptors.request.use(config => {
let token = Util.local.get(Setting.tokenKey); let token = Util.local.get(Setting.tokenKey);
if (token) config.headers.token = token if (token) config.headers.token = token;
return config; return config;
}, err => { }, err => {
Util.errorMsg({ Util.errorMsg({
@ -27,67 +26,74 @@ service.interceptors.request.use(config => {
return Promise.reject(err); return Promise.reject(err);
}); });
let logouted = 0;
// 响应拦截器 // 响应拦截器
service.interceptors.response.use( service.interceptors.response.use(
response => { response => {
const res = response.data const res = response.data;
const { code, status } = res if (res.status == 200 || res.status == 10000 || res.status == 30001) {
if (code == 200 || status === 200) {
return Promise.resolve(res).catch(e => { }); return Promise.resolve(res).catch(e => { });
} else if (code === 401) { } else if (res.code === 401) {
Util.errorMsg(res.msg) // 账号互踢
if (!logouted) {
Util.local.remove(Setting.storeKey)
Util.local.remove(Setting.tokenKey)
Util.errorMsg(res.msg.includes('顶') ? '您的账号已在其他设备登录,您已被迫下线!' : '登录过期,请重新登录!')
setTimeout(() => { setTimeout(() => {
store.dispatch('user/logout') store.dispatch('user/logout')
}, 1000) logouted = 0
return Promise.reject(res) }, 2000)
} else if (code == 300) { logouted = 1
// 悬浮栏管理里的禁用启用返回300要弹询问框选择是否继续 }
} else if (!res.status) {
return Promise.resolve(res).catch(e => { }); return Promise.resolve(res).catch(e => { });
} else { } else {
Util.errorMsg(res.msg); Util.errorMsg(res.message);
return Promise.reject(res) return Promise.reject(res)
// return Promise.resolve(res).catch(e => {});
} }
}, },
// 服务器状态码不是200的情况 // 服务器状态码不是200的情况
error => { error => {
if (error.response.status) { if (error.response.status) {
const { msg, code } = error.response.data switch (error.response.status) {
// 站点id为空
if (error.response.status === 405) {
Router.replace('/site')
} else {
switch (code) {
// 401: 未登录 // 401: 未登录
// 未登录则跳转登录页面,并携带当前页面的路径
// 在登录成功后返回当前页面,这一步需要在登录页操作。
case 401: case 401:
Util.errorMsg("登录过期,请重新登录") if (!logouted) {
Util.local.remove(Setting.storeKey);
Util.local.remove(Setting.tokenKey);
Util.errorMsg("登录过期,请重新登录");
setTimeout(() => { setTimeout(() => {
store.dispatch('user/logout') store.dispatch('user/logout')
}, 1000) }, 1000);
break logouted = 1
// 403 token过期 }
case 403: break;
Util.errorMsg("登录过期,请重新登录") case 500:
setTimeout(() => { Util.errorMsg("网络错误");
store.dispatch('user/logout') break;
}, 1000) // 404请求不存在
break case 404:
Util.errorMsg("网络请求不存在!");
break;
// 其他错误,直接抛出错误提示 // 其他错误,直接抛出错误提示
default: default:
Util.errorMsg(msg) Util.errorMsg(error.response.data.message);
Promise.reject(error.response.data) Promise.reject(res);
} }
return Promise.reject(error.response) return Promise.reject(error.response);
} }
} }
} );
)
function get (url, params) { function get (url, params) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
service.get(url, { params: params }).then(res => { service.get(url, { params: params }).then(res => {
resolve(res) resolve(res);
}).catch(err => { }).catch(err => {
reject(err) reject(err);
}); });
}); });
} }
@ -105,7 +111,7 @@ function post (url, params) {
function del (url, params) { function del (url, params) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
service.delete(url, { service.delete(url, {
data: params params
}).then(res => { }).then(res => {
resolve(res); resolve(res);
}).catch(err => { }).catch(err => {

@ -4,7 +4,7 @@
const isDev = process.env.NODE_ENV === 'development' // 开发环境 const isDev = process.env.NODE_ENV === 'development' // 开发环境
let host = location.origin let host = location.origin
if (isDev) { if (isDev) {
host = 'http://192.168.31.51:10000' host = 'http://192.168.31.217:9000'
} }
const Setting = { const Setting = {

Loading…
Cancel
Save