|
|
|
|
@ -4,12 +4,373 @@
|
|
|
|
|
* user:sa0ChunLuyu
|
|
|
|
|
* date:2024年10月12日 16:29:26
|
|
|
|
|
*/
|
|
|
|
|
import {ref, onMounted, nextTick} from 'vue';
|
|
|
|
|
import $router from '@/router'
|
|
|
|
|
import {onBeforeRouteUpdate} from "vue-router";
|
|
|
|
|
import {ElMessage, ElMessageBox} from 'element-plus'
|
|
|
|
|
import {
|
|
|
|
|
QuestionQuestionSelectAction,
|
|
|
|
|
QuestionItemSelectAction,
|
|
|
|
|
QuestionQuestionCreateAction,
|
|
|
|
|
QuestionQuestionUpdateAction,
|
|
|
|
|
QuestionQuestionDeleteAction,
|
|
|
|
|
QuestionQuestionListAction
|
|
|
|
|
} from '@/api/api.js'
|
|
|
|
|
|
|
|
|
|
const default_page_options = {
|
|
|
|
|
search: '',
|
|
|
|
|
page: 1,
|
|
|
|
|
}
|
|
|
|
|
const page_options = ref(JSON.parse(JSON.stringify(default_page_options)))
|
|
|
|
|
onBeforeRouteUpdate((to) => {
|
|
|
|
|
routerChange(to.query)
|
|
|
|
|
})
|
|
|
|
|
const table_list = ref([])
|
|
|
|
|
const last_page = ref(0)
|
|
|
|
|
const QuestionQuestionList = async () => {
|
|
|
|
|
const response = await QuestionQuestionListAction(page_options.value)
|
|
|
|
|
if (response.status) {
|
|
|
|
|
table_list.value = response.data.list.data.map((item) => {
|
|
|
|
|
return {
|
|
|
|
|
...item,
|
|
|
|
|
option: JSON.parse(item.option)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
last_page.value = response.data.list.last_page
|
|
|
|
|
} else {
|
|
|
|
|
ElMessage.error(response.msg)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const routerChange = (query) => {
|
|
|
|
|
page_options.value = {
|
|
|
|
|
search: query.search || default_page_options.search,
|
|
|
|
|
page: Number(query.page) || default_page_options.page,
|
|
|
|
|
}
|
|
|
|
|
QuestionQuestionList()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const searchClick = (page = 1) => {
|
|
|
|
|
page_options.value.page = page
|
|
|
|
|
$router.push({
|
|
|
|
|
query: JSON.parse(JSON.stringify(page_options.value))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
const select_option = {
|
|
|
|
|
value: [
|
|
|
|
|
{content: 'A. ', type: 'items', items: [], questions: []},
|
|
|
|
|
{content: 'B. ', type: 'items', items: [], questions: []},
|
|
|
|
|
{content: 'C. ', type: 'items', items: [], questions: []},
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
const input_option = {
|
|
|
|
|
value: '',
|
|
|
|
|
placeholder: '请输入选项内容'
|
|
|
|
|
}
|
|
|
|
|
const edit_data_default = {
|
|
|
|
|
id: 0,
|
|
|
|
|
question: '',
|
|
|
|
|
type: 'input',
|
|
|
|
|
option: {
|
|
|
|
|
input: JSON.parse(JSON.stringify(input_option)),
|
|
|
|
|
select: JSON.parse(JSON.stringify(select_option)),
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
const edit_data = ref(JSON.parse(JSON.stringify(edit_data_default)))
|
|
|
|
|
const edit_show = ref(false)
|
|
|
|
|
const createClick = () => {
|
|
|
|
|
updateClick(edit_data_default)
|
|
|
|
|
}
|
|
|
|
|
const updateClick = (row) => {
|
|
|
|
|
edit_data.value = JSON.parse(JSON.stringify(row))
|
|
|
|
|
edit_show.value = true
|
|
|
|
|
}
|
|
|
|
|
const editDoneClick = async () => {
|
|
|
|
|
const $func = edit_data.value.id !== 0 ? QuestionQuestionUpdateAction : QuestionQuestionCreateAction
|
|
|
|
|
const response = await $func({
|
|
|
|
|
...edit_data.value,
|
|
|
|
|
option: JSON.stringify(edit_data.value.option),
|
|
|
|
|
})
|
|
|
|
|
if (response.status) {
|
|
|
|
|
edit_show.value = false
|
|
|
|
|
await QuestionQuestionList()
|
|
|
|
|
} else {
|
|
|
|
|
ElMessage.error(response.msg)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QuestionQuestionDelete = async (id) => {
|
|
|
|
|
const response = await QuestionQuestionDeleteAction({id})
|
|
|
|
|
if (response.status) {
|
|
|
|
|
await QuestionQuestionList()
|
|
|
|
|
} else {
|
|
|
|
|
ElMessage.error(response.msg)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const deleteClick = (id) => {
|
|
|
|
|
ElMessageBox.confirm(
|
|
|
|
|
'是否确认删除该题目?',
|
|
|
|
|
'提示',
|
|
|
|
|
{
|
|
|
|
|
confirmButtonText: '确认',
|
|
|
|
|
cancelButtonText: '取消',
|
|
|
|
|
type: 'warning',
|
|
|
|
|
}
|
|
|
|
|
).then(() => {
|
|
|
|
|
QuestionQuestionDelete(id)
|
|
|
|
|
}).catch(() => {
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
const edit_item_data = ref({
|
|
|
|
|
active: 0,
|
|
|
|
|
items: []
|
|
|
|
|
})
|
|
|
|
|
const edit_item_show = ref(false)
|
|
|
|
|
const question_list = ref([])
|
|
|
|
|
const QuestionQuestionSelect = async () => {
|
|
|
|
|
const response = await QuestionQuestionSelectAction()
|
|
|
|
|
if (response.status) {
|
|
|
|
|
question_list.value = response.data.list
|
|
|
|
|
} else {
|
|
|
|
|
ElMessage.error(response.msg)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const editItemClick = async (index) => {
|
|
|
|
|
const type = edit_data.value.option.select.value[index].type
|
|
|
|
|
if (type === 'questions') {
|
|
|
|
|
await QuestionQuestionSelect()
|
|
|
|
|
}
|
|
|
|
|
await nextTick(() => {
|
|
|
|
|
edit_item_data.value = {
|
|
|
|
|
active: index,
|
|
|
|
|
items: JSON.parse(JSON.stringify(edit_data.value.option.select.value[index][type]))
|
|
|
|
|
}
|
|
|
|
|
edit_item_show.value = true
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
const editItemDoneClick = () => {
|
|
|
|
|
const type = edit_data.value.option.select.value[edit_item_data.value.active].type
|
|
|
|
|
edit_data.value.option.select.value[edit_item_data.value.active][type] = JSON.parse(JSON.stringify(edit_item_data.value.items))
|
|
|
|
|
edit_item_show.value = false
|
|
|
|
|
}
|
|
|
|
|
const filterMethod = (query, item) => {
|
|
|
|
|
const type = edit_data.value.option.select.value[edit_item_data.value.active].type
|
|
|
|
|
if (type === 'items') {
|
|
|
|
|
return item.name.toLowerCase().includes(query.toLowerCase())
|
|
|
|
|
} else {
|
|
|
|
|
return item.question.toLowerCase().includes(query.toLowerCase())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const item_list = ref([])
|
|
|
|
|
const QuestionItemSelect = async () => {
|
|
|
|
|
const response = await QuestionItemSelectAction()
|
|
|
|
|
if (response.status) {
|
|
|
|
|
item_list.value = response.data.list
|
|
|
|
|
} else {
|
|
|
|
|
ElMessage.error(response.msg)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const moveQuestionClick = (index, type) => {
|
|
|
|
|
if (edit_data.value.option.select.value.length - 1 >= index + type) {
|
|
|
|
|
const question = edit_data.value.option.select.value[index]
|
|
|
|
|
edit_data.value.option.select.value[index] = JSON.parse(JSON.stringify(edit_data.value.option.select.value[index + type]))
|
|
|
|
|
edit_data.value.option.select.value[index + type] = JSON.parse(JSON.stringify(question))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const delQuestionClick = (index) => {
|
|
|
|
|
edit_data.value.option.select.value.splice(index, 1)
|
|
|
|
|
}
|
|
|
|
|
const addQuestionClick = () => {
|
|
|
|
|
edit_data.value.option.select.value.push({content: '', type: 'items', items: [], questions: []})
|
|
|
|
|
}
|
|
|
|
|
const question_type_map = {
|
|
|
|
|
select: '选择题',
|
|
|
|
|
input: '输入框'
|
|
|
|
|
}
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
routerChange($router.currentRoute.value.query)
|
|
|
|
|
QuestionItemSelect()
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
<template>
|
|
|
|
|
<div>
|
|
|
|
|
<el-dialog v-model="edit_item_show" title="关联管理" width="610px"
|
|
|
|
|
:close-on-click-modal="false"
|
|
|
|
|
:close-on-press-escape="false"
|
|
|
|
|
:show-close="false">
|
|
|
|
|
<div>
|
|
|
|
|
<el-transfer
|
|
|
|
|
:titles="['可选', '已选']"
|
|
|
|
|
:props="{key: 'id', label: edit_data.option.select.value[edit_item_data.active].type === 'items' ? 'name' : 'question'}"
|
|
|
|
|
v-model="edit_item_data.items"
|
|
|
|
|
filterable
|
|
|
|
|
:filter-method="filterMethod"
|
|
|
|
|
filter-placeholder="搜索"
|
|
|
|
|
:data="edit_data.option.select.value[edit_item_data.active].type === 'items' ? item_list : question_list"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<template #footer>
|
|
|
|
|
<div class="dialog-footer">
|
|
|
|
|
<el-button @click="editItemDoneClick()" type="primary">确定</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
|
|
|
<el-dialog v-model="edit_show" :title="`${!!edit_data.id ? '编辑' : '新建'}`" width="1000px">
|
|
|
|
|
<div>
|
|
|
|
|
<el-form label-width="150">
|
|
|
|
|
<el-form-item label="题目">
|
|
|
|
|
<el-input v-model="edit_data.question" placeholder="请输入题目"></el-input>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="题目类型">
|
|
|
|
|
<el-select v-model="edit_data.type" placeholder="请选择题目类型">
|
|
|
|
|
<el-option label="选择题" value="select"></el-option>
|
|
|
|
|
<el-option label="输入框" value="input"></el-option>
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<template v-if="edit_data.type === 'input'">
|
|
|
|
|
<el-form-item label="题目配置">
|
|
|
|
|
<div>
|
|
|
|
|
<div class="select_item_wrapper">
|
|
|
|
|
<el-input class="select_input_wrapper" v-model="edit_data.option.input.value"
|
|
|
|
|
placeholder="请输入默认值"></el-input>
|
|
|
|
|
<el-input class="select_input_wrapper" v-model="edit_data.option.input.placeholder"
|
|
|
|
|
placeholder="请输入占位符"></el-input>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-else-if="edit_data.type === 'select'">
|
|
|
|
|
<el-form-item label="题目配置">
|
|
|
|
|
<div>
|
|
|
|
|
<div class="select_item_wrapper" v-for="(_,k) in edit_data.option.select.value" :key="k">
|
|
|
|
|
<el-input class="select_input_wrapper" v-model="edit_data.option.select.value[k].content"
|
|
|
|
|
placeholder="请输入"></el-input>
|
|
|
|
|
<el-select class="select_select_wrapper" v-model="edit_data.option.select.value[k].type"
|
|
|
|
|
placeholder="请选择题目类型">
|
|
|
|
|
<el-option label="项目" value="items"></el-option>
|
|
|
|
|
<el-option label="题目" value="questions"></el-option>
|
|
|
|
|
</el-select>
|
|
|
|
|
<span style="margin-right: 10px;">{{
|
|
|
|
|
edit_data.option.select.value[k].type === 'items'
|
|
|
|
|
? edit_data.option.select.value[k].items.length
|
|
|
|
|
: edit_data.option.select.value[k].questions.length
|
|
|
|
|
}}个{{ edit_data.option.select.value[k].type === 'items' ? '项目' : '题目' }}</span>
|
|
|
|
|
<el-button type="primary" size="small" @click="editItemClick(k)">
|
|
|
|
|
编辑{{ edit_data.option.select.value[k].type === 'items' ? '项目' : '题目' }}
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button :disabled="k===0" type="primary" size="small" @click="moveQuestionClick(k, -1)">↑
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button :disabled="k===edit_data.option.select.value.length - 1" type="primary" size="small"
|
|
|
|
|
@click="moveQuestionClick(k, 1)">↓
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button :disabled="edit_data.option.select.value.length === 1" type="primary" size="small"
|
|
|
|
|
@click="delQuestionClick(k)">X
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<el-button type="primary" size="small" @click="addQuestionClick()">添加选项</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</template>
|
|
|
|
|
</el-form>
|
|
|
|
|
</div>
|
|
|
|
|
<template #footer>
|
|
|
|
|
<span class="dialog-footer">
|
|
|
|
|
<el-button @click="edit_show = false">取消</el-button>
|
|
|
|
|
<el-button type="primary" @click="editDoneClick()">确定</el-button>
|
|
|
|
|
</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
|
|
|
<div class="head">
|
|
|
|
|
<div class="head">
|
|
|
|
|
<el-row>
|
|
|
|
|
<el-form-item>
|
|
|
|
|
<el-input v-model="page_options.search" placeholder="搜索题目"/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-button type="primary" @click="searchClick()" style="margin-left: 10px;">查询</el-button>
|
|
|
|
|
<el-button type="success" @click="createClick()" style="margin-left: 10px;">添加</el-button>
|
|
|
|
|
</el-row>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<el-table :data="table_list" style="width: 100%;" row-key="id" :tooltip-options="{
|
|
|
|
|
popperClass: 'popper_class_wrapper'
|
|
|
|
|
}">
|
|
|
|
|
<el-table-column prop="id" label="ID" width="100"/>
|
|
|
|
|
<el-table-column prop="question" label="题目" width="200" show-overflow-tooltip/>
|
|
|
|
|
<el-table-column label="类型" width="100">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
{{ question_type_map[scope.row.type] }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="题目配置">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<div v-if="scope.row.type === 'input'" class="select_item_wrapper">
|
|
|
|
|
<span class="title_wrapper">占位符:</span><span
|
|
|
|
|
style="margin: 0 10px;">{{ scope.row.option.input.placeholder }}</span>
|
|
|
|
|
<span v-if="!!scope.row.option.input.value" class="title_wrapper">默认值:</span><span style="margin: 0 10px;">{{
|
|
|
|
|
scope.row.option.input.value
|
|
|
|
|
}}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-else-if="scope.row.type === 'select'">
|
|
|
|
|
<div class="select_item_wrapper" v-for="(i,k) in scope.row.option.select.value" :key="k">
|
|
|
|
|
<span style="margin-right: 10px;width: 400px;">{{ i.content }}</span>
|
|
|
|
|
<span class="title_wrapper">带出:</span>
|
|
|
|
|
<span>{{
|
|
|
|
|
scope.row.option.select.value[k].type === 'items' ? '项目' : '题目'
|
|
|
|
|
}}</span>
|
|
|
|
|
<span style="margin: 0 5px;">{{ scope.row.option.select.value[k][scope.row.option.select.value[k].type].length }}</span>
|
|
|
|
|
<span>个</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="操作" width="140">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-button type="primary" @click="updateClick(scope.row)" size="small">修改</el-button>
|
|
|
|
|
<el-button type="danger" @click="deleteClick(scope.row.id)" size="small">删除</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
|
|
|
|
|
<div class="page">
|
|
|
|
|
<el-pagination v-if="last_page > 0" :current-page="page_options.page" mt-2 background layout="prev, pager, next"
|
|
|
|
|
:page-count="last_page" @update:current-page="searchClick"/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<style>
|
|
|
|
|
.popper_class_wrapper {
|
|
|
|
|
width: 300px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
<style scoped>
|
|
|
|
|
.title_wrapper {
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.select_input_wrapper {
|
|
|
|
|
width: 300px;
|
|
|
|
|
margin-right: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.select_select_wrapper {
|
|
|
|
|
width: 80px;
|
|
|
|
|
margin-right: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.select_item_wrapper {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.page {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
margin-top: 10px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|