lion 1 year ago
parent c6139be9c0
commit 7bf7060395

File diff suppressed because it is too large Load Diff

@ -21,16 +21,16 @@
<div class="question-content">
<template v-if="q.edit_input==='radio'">
<el-radio-group v-model="answers[q.id]" :disabled="previewOnly" class="mobile-radio-group">
<el-radio v-for="(opt, oidx) in q.select_item" :key="oidx" :label="opt"
class="mobile-radio">{{ opt }}</el-radio>
<el-radio v-for="(opt, oidx) in q.select_item" :key="oidx" :label="opt.value"
class="mobile-radio">{{ opt.value }}</el-radio>
</el-radio-group>
<el-input v-if="q.allow_input" type="text" v-model="answers[q.id]" :disabled="previewOnly"
:placeholder="q.help||'请输入内容'" class="mobile-textarea" />
</template>
<template v-else-if="q.edit_input==='checkbox'">
<el-checkbox-group v-model="answers[q.id]" :disabled="previewOnly" class="mobile-checkbox-group">
<el-checkbox v-for="(opt, oidx) in q.select_item" :key="oidx" :label="opt"
class="mobile-checkbox">{{ opt }}</el-checkbox>
<el-checkbox v-for="(opt, oidx) in q.select_item" :key="oidx" :label="opt.value"
class="mobile-checkbox">{{ opt.value }}</el-checkbox>
</el-checkbox-group>
<el-input v-if="q.allow_input" type="text" v-model="answers[q.id]" :disabled="previewOnly"
:placeholder="q.help||'请输入内容'" class="mobile-textarea" />

@ -74,7 +74,7 @@
</template>
<template #right>
<editPane ref="editPane" @refreshFormItem="refreshItem" :rules="rules" :types="types"
@refresh="getFormList"></editPane>
@refresh="getFormList" :selectedCourseSchedules="selectedCourseSchedules"></editPane>
</template>
</Split>
</template>
@ -99,11 +99,11 @@
import formMixins from "@/mixin/formMixin.js";
import {
config
} from "@/api/system/customFormField";
import {
index,
save,
destroy,
} from "@/api/system/customFormField";
import {
index,
save,
destroy,
} from "@/api/survey/index.js";
import {
index as courseIndex,
@ -122,9 +122,10 @@
},
data() {
return {
course_id: "",
course_id: "",
questionLinkIds:{},
course_signs_count: 0,
selectedCourseSchedules: [], //
select: {
page: 1,
page_size: 999,
@ -211,7 +212,7 @@
edit_to_migration,
validation_rules
} = await config(false);
this.rules = validation_rules
this.rules = validation_rules
// item.edit_input !== 'textarea' &&
this.types = edit_to_migration.filter(item => item.edit_input !==
"richtext" &&
@ -279,10 +280,13 @@
edit_input: originalData.edit_input,
sort: newIndex,
help: "",
rule: "",
rule: "",
allow_input:0,
select_item: [],
...this.questionLinkIds
select_item: [],
course_id: this.questionLinkIds.course_id,
course_content_evaluation_id: this.questionLinkIds.course_content_evaluation_id,
// course_content_id
course_content_id: ""
};
this.formList.splice(newIndex, 0, newItem)
}
@ -371,12 +375,12 @@
if (item.course_id) {
item.course_id = ''
}
})
this.formList = []
// this.types = []
// this.rules = {}
this.$refs.editPane.showForm = false
this.selectedForm = {}
})
this.formList = []
// this.types = []
// this.rules = {}
this.$refs.editPane.showForm = false
this.selectedForm = {}
this.selectedIndex = 0
this.$emit("refresh")
}
@ -462,4 +466,4 @@
width: 2px;
}
}
</style>
</style>

@ -1,27 +1,48 @@
<template>
<div style="padding: 0 10px; height: 100%; overflow: scroll">
<template v-if="showForm">
<el-form :rules="formRule" ref="form" :model="selectedForm" label-width="80px">
<!-- <el-form-item prop="belong_user_table" label="更新学员字段">
<el-select v-model="selectedForm.belong_user_table" clearable @change="changeIsUser"
:popper-append-to-body="false" placeholder="请选择学员字段" style="width: 100%">
<el-option v-for="(item, key) in selectFormList" :key="key" :label="item.name"
:value="item.belong_user_table"></el-option>
</el-select>
</el-form-item> -->
<el-form-item prop="name" label="字段名称">
<el-input v-model="selectedForm.name" placeholder="请输入字段名称" @input="inputHandler"></el-input>
</el-form-item>
<el-form-item required prop="field" label="字段标识">
<el-input :disabled="selectedForm.belong_user?true:false" v-model="selectedForm.field"></el-input>
</el-form-item>
<el-form-item prop="help" label="帮助文字">
<el-input v-model="selectedForm.help" placeholder="请输入帮助文字"></el-input>
<template>
<div style="padding: 0 10px; height: 100%; overflow: scroll">
<template v-if="showForm">
<el-form :rules="formRule" ref="form" :model="selectedForm" label-width="80px">
<!-- <el-form-item prop="belong_user_table" label="更新学员字段">
<el-select v-model="selectedForm.belong_user_table" clearable @change="changeIsUser"
:popper-append-to-body="false" placeholder="请选择学员字段" style="width: 100%">
<el-option v-for="(item, key) in selectFormList" :key="key" :label="item.name"
:value="item.belong_user_table"></el-option>
</el-select>
</el-form-item> -->
<el-form-item prop="name" label="字段名称">
<el-input v-model="selectedForm.name" placeholder="请输入字段名称" @input="inputHandler"></el-input>
</el-form-item>
<el-form-item required prop="field" label="字段标识">
<el-input :disabled="selectedForm.belong_user?true:false" v-model="selectedForm.field"></el-input>
</el-form-item>
<el-form-item prop="help" label="帮助文字">
<el-input v-model="selectedForm.help" placeholder="请输入帮助文字"></el-input>
</el-form-item>
<!-- 课表选择字段 -->
<el-form-item
v-if="selectedCourseSchedules.length > 0"
prop="course_content_id"
label="关联课表"
>
<div class="course-schedule-selection">
<div class="schedule-list">
<el-radio
v-for="schedule in selectedCourseSchedules"
:key="schedule.id"
v-model="selectedScheduleId"
:label="schedule.id"
class="schedule-item"
>
{{ schedule.date }} {{ schedule.period }} | {{ schedule.theme }}
</el-radio>
</div>
</div>
</el-form-item>
<el-form-item prop="sort" label="排序">
<el-input v-model="selectedForm.sort" placeholder="请输入排序"></el-input>
<el-form-item prop="sort" label="排序">
<el-input v-model="selectedForm.sort" placeholder="请输入排序"></el-input>
</el-form-item>
<el-form-item v-if="selectedForm.edit_input==='radio' || selectedForm.edit_input==='checkbox'"
prop="allow_input" label="是否可自行填写">
@ -29,113 +50,119 @@
<el-radio :label="0"></el-radio>
<el-radio :label="1"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item prop="select_item" label="下拉框选项"
v-if="selectedForm.edit_input==='radio' || selectedForm.edit_input==='checkbox'">
<div class="select-item">
<div class="select-item__header">
<span></span>
<span></span>
</div>
<div class="select-item__body" v-for="(i, index) in selectItem">
<div><el-input v-model="i.key" size="mini"></el-input></div>
<div><el-input v-model="i.value" size="mini"></el-input></div>
<div>
<el-button type="danger" size="mini" circle icon="el-icon-delete"
@click="selectItem.splice(index, 1)"></el-button>
</div>
</div>
<el-button type="primary" size="mini" circle icon="el-icon-plus"
@click="selectItem.push({ key: '', value: '' })"></el-button>
</div>
</el-form-item>
<el-form-item prop="validation" label="校验规则">
<el-select v-model="validation" clearable multiple collapse-tags :popper-append-to-body="false"
placeholder="请选择校验规则" style="width: 100%">
<el-option v-for="(value, key) in rules" :key="key" :label="value" :value="key"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveField"></el-button>
<el-popover trigger="hover" ref="el-popover" placement="top" width="160">
<p>确定删除字段吗</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="$refs['el-popover'].doClose()"></el-button>
<el-button type="primary" size="mini" @click="destroyField"></el-button>
</div>
<el-button type="danger" slot="reference" @click="$refs['el-popover'].doShow()"
style="margin-left: 10px">删除</el-button>
</el-popover>
</el-form-item>
</el-form>
</template>
<template v-else>
<el-empty style="position: relative; top: 50%; transform: translateY(-50%)" description="暂无选择"></el-empty>
</template>
</div>
</template>
<script>
import {
save,
destroy
} from "@/api/survey/index.js";
import {
translate
} from "@/api/system/customFormField";
import {
debounce,
deepCopy
} from "@/utils";
export default {
props: {
rules: Object,
types: Array,
},
data() {
return {
showForm: false,
debouncedInputHandler: null,
formRule: {
name: [{
required: true,
message: "请输入字段名称"
}],
field: [{
required: true,
message: "请输入字段标识"
}],
},
selectedForm: {}, // item
selectedIndex: 0, // item index
selectItem: [], //
validation: [], //
</el-form-item>
<el-form-item prop="select_item" label="下拉框选项"
v-if="selectedForm.edit_input==='radio' || selectedForm.edit_input==='checkbox'">
<div class="select-item">
<div class="select-item__header">
<span></span>
<span></span>
</div>
<div class="select-item__body" v-for="(i, index) in selectItem">
<div><el-input v-model="i.key" size="mini"></el-input></div>
<div><el-input v-model="i.value" size="mini"></el-input></div>
<div>
<el-button type="danger" size="mini" circle icon="el-icon-delete"
@click="selectItem.splice(index, 1)"></el-button>
</div>
</div>
<el-button type="primary" size="mini" circle icon="el-icon-plus"
@click="selectItem.push({ key: '', value: '' })"></el-button>
</div>
</el-form-item>
<el-form-item prop="validation" label="校验规则">
<el-select v-model="validation" clearable multiple collapse-tags :popper-append-to-body="false"
placeholder="请选择校验规则" style="width: 100%">
<el-option v-for="(value, key) in rules" :key="key" :label="value" :value="key"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveField"></el-button>
<el-popover trigger="hover" ref="el-popover" placement="top" width="160">
<p>确定删除字段吗</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="$refs['el-popover'].doClose()"></el-button>
<el-button type="primary" size="mini" @click="destroyField"></el-button>
</div>
<el-button type="danger" slot="reference" @click="$refs['el-popover'].doShow()"
style="margin-left: 10px">删除</el-button>
</el-popover>
</el-form-item>
</el-form>
</template>
<template v-else>
<el-empty style="position: relative; top: 50%; transform: translateY(-50%)" description="暂无选择"></el-empty>
</template>
</div>
</template>
<script>
import {
save,
destroy
} from "@/api/survey/index.js";
import {
translate
} from "@/api/system/customFormField";
import {
debounce,
deepCopy
} from "@/utils";
export default {
props: {
rules: Object,
types: Array,
selectedCourseSchedules: {
type: Array,
default: () => []
}
},
data() {
return {
showForm: false,
debouncedInputHandler: null,
formRule: {
name: [{
required: true,
message: "请输入字段名称"
}],
field: [{
required: true,
message: "请输入字段标识"
}],
},
selectedForm: {}, // item
selectedIndex: 0, // item index
selectItem: [], //
validation: [], //
parameters: [], //
formList:[],
forms: [], //
};
},
methods: {
checkChinese(rule, value, callback) {
const reg = /^[\u4e00-\u9fa5]+$/;
if (reg.test(value)) {
callback();
} else {
callback(new Error("请输入中文姓名"));
}
},
inputHandler(e) {
if (this.selectedForm.belong_user) {
} else {
this.debouncedInputHandler(e);
}
formList:[],
forms: [], //
//
selectedScheduleId: null, //
};
},
methods: {
checkChinese(rule, value, callback) {
const reg = /^[\u4e00-\u9fa5]+$/;
if (reg.test(value)) {
callback();
} else {
callback(new Error("请输入中文姓名"));
}
},
//
changeIsUser(e) {
console.log("e", e)
inputHandler(e) {
if (this.selectedForm.belong_user) {
} else {
this.debouncedInputHandler(e);
}
},
//
changeIsUser(e) {
console.log("e", e)
console.log(this.selectedForm)
if(e){
this.selectFormList.map(item => {
@ -157,12 +184,14 @@
})
}else{
this.selectedForm.belong_user = 0
}
},
}
},
// -
saveField() {
console.log("this.selectedForm.select_item",this.selectedForm)
console.log("this.selectedForm.select_item",this.selectedForm)
if(this.base.isNull(this.selectedForm.name)){
this.$Message.warning("字段名称不能为空")
return
@ -170,18 +199,29 @@
if(this.base.isNull(this.selectedForm.field)){
this.$Message.warning("字段标识不能为空")
return
}
this.selectedForm.select_item = []
this.selectedForm.select_item = this.selectItem
}
// - ID
if (this.selectedScheduleId) {
this.selectedForm.course_content_id = this.selectedScheduleId;
} else {
//
this.selectedForm.course_content_id = '';
}
console.log('保存的表单数据:', this.selectedForm);
this.selectedForm.select_item = []
this.selectedForm.select_item = this.selectItem
this.selectedForm.rule = this.validation.join(",")
//
//
if (this.selectedForm.edit_input === 'radio' || this.selectedForm.edit_input === 'checkbox') {
if (this.selectedForm.select_item.length === 0) {
this.$Message.warning("请输入下拉框选项")
return
//
if (this.selectedForm.edit_input === 'radio' || this.selectedForm.edit_input === 'checkbox') {
if (this.selectedForm.select_item.length === 0) {
this.$Message.warning("请输入下拉框选项")
return
}else{
//
let count = 0
@ -195,104 +235,155 @@
this.$Message.warning("下拉框选项键值不能为空")
return
}
}
}
// return
save(this.selectedForm).then(res => {
this.$Message.success('保存成功')
this.$emit("refreshFormItem", 'save')
this.showForm = false
})
},
destroyField() {
if (this.selectedForm.id) {
destroy({
id: this.selectedForm.id
}).then(res => {
this.$Message.success('删除成功')
this.showForm = false
this.$emit("refreshFormItem", 'DelId', this.selectedIndex)
})
} else {
this.$Message.success('删除成功')
this.showForm = false
this.$emit("refreshFormItem", 'Del', this.selectedIndex)
}
}
},
watch: {
selectedForm(newVal) {
}
}
// return
save(this.selectedForm).then(res => {
this.$Message.success('保存成功')
this.$emit("refreshFormItem", 'save')
this.showForm = false
})
},
destroyField() {
if (this.selectedForm.id) {
destroy({
id: this.selectedForm.id
}).then(res => {
this.$Message.success('删除成功')
this.showForm = false
this.$emit("refreshFormItem", 'DelId', this.selectedIndex)
})
} else {
this.$Message.success('删除成功')
this.showForm = false
this.$emit("refreshFormItem", 'Del', this.selectedIndex)
}
}
},
watch: {
selectedForm(newVal) {
console.log("newval", newVal)
if (newVal) {
if (newVal.select_item) {
this.selectItem = [];
this.selectItem = newVal.select_item
}
this.validation = []
if (newVal.rule) {
this.validation = newVal.rule.split(',')
}
}
if (newVal) {
if (newVal.select_item) {
this.selectItem = [];
this.selectItem = newVal.select_item
}
this.validation = []
if (newVal.rule) {
this.validation = newVal.rule.split(',')
}
//
if (newVal.course_content_id) {
//
this.selectedScheduleId = newVal.course_content_id;
console.log('编辑问题设置课表ID:', this.selectedScheduleId);
} else {
//
this.selectedScheduleId = null;
console.log('新增问题,清空课表选择');
}
}
},
showForm(newval){
if(!newval){
this.selectedForm = {}
this.selectedIndex = 0
//
this.selectedScheduleId = null
}
}
},
created() {
this.debouncedInputHandler = debounce((e) => {
if (e) {
this.checkChinese("", e, (err) => {
if (!err) {
translate({
str: e,
}).then((en) => {
this.selectedForm.field = en.pinyin;
});
}
});
}
}, 1000);
},
};
</script>
<style scoped lang="scss">
::v-deep .el-input-number.is-without-controls .el-input__inner {
text-align: left;
}
.select-item {
text-align: center;
&__header {
font-weight: 600;
display: flex;
&>span {
flex-basis: 30%;
&+span {
margin-left: 10px;
}
}
}
&__body {
display: flex;
&>div {
flex-basis: 30%;
&+div {
margin-left: 10px;
}
}
}
}
},
//
selectedCourseSchedules: {
handler(newVal) {
console.log('监听到课表数据变化:', newVal);
if (newVal && newVal.length > 0) {
//
if (!this.selectedScheduleId && this.selectedForm.course_content_id) {
this.selectedScheduleId = newVal[0].id;
console.log('编辑问题,默认选择第一个课表:', this.selectedScheduleId);
} else if (this.selectedScheduleId) {
console.log('保持现有课表选择:', this.selectedScheduleId);
} else {
console.log('新增问题,不自动选择课表');
}
} else {
//
this.selectedScheduleId = null;
}
},
immediate: true
}
},
created() {
this.debouncedInputHandler = debounce((e) => {
if (e) {
this.checkChinese("", e, (err) => {
if (!err) {
translate({
str: e,
}).then((en) => {
this.selectedForm.field = en.pinyin;
});
}
});
}
}, 1000);
},
};
</script>
<style scoped lang="scss">
::v-deep .el-input-number.is-without-controls .el-input__inner {
text-align: left;
}
.select-item {
text-align: center;
&__header {
font-weight: 600;
display: flex;
&>span {
flex-basis: 30%;
&+span {
margin-left: 10px;
}
}
}
&__body {
display: flex;
&>div {
flex-basis: 30%;
&+div {
margin-left: 10px;
}
}
}
}
.course-schedule-selection {
display: flex;
flex-direction: column;
width: 100%;
}
.schedule-list {
display: flex;
flex-direction: column;
gap: 8px;
width: 100%;
}
.schedule-item {
padding: 8px 0;
box-sizing: border-box;
}
</style>

@ -12,18 +12,43 @@
<!-- 操作栏 -->
<div class="action-bar">
<div class="search-box">
<el-input v-model="select.title" placeholder="搜索问卷..." style="width:250px;" @input="filterSurveys"
clearable />
<el-select v-model="select.status" placeholder="全部状态" style="width:150px;" @change="filterSurveys" clearable>
<el-input
v-model="select.title"
placeholder="搜索问卷..."
style="width: 250px"
@input="filterSurveys"
clearable
/>
<el-select
v-model="select.status"
placeholder="全部状态"
style="width: 150px"
@change="filterSurveys"
clearable
>
<el-option label="草稿" :value="0" />
<el-option label="已发布" :value="1" />
</el-select>
<el-select v-model="select.course_id" placeholder="全部课程" style="width:200px;" @change="filterSurveys"
clearable>
<el-option v-for="item in courseList" :label="item.name" :value="item.id" />
<el-select
v-model="select.course_id"
placeholder="全部课程"
style="width: 200px"
@change="filterSurveys"
clearable
>
<el-option
v-for="item in courseList"
:label="item.name"
:value="item.id"
/>
</el-select>
</div>
<el-button type="primary" icon="el-icon-plus" @click="openCreateModal('add')"></el-button>
<el-button
type="primary"
icon="el-icon-plus"
@click="openCreateModal('add')"
>创建问卷</el-button
>
</div>
<!-- 问卷列表 -->
<div class="survey-grid">
@ -31,42 +56,78 @@
<div v-for="survey in list" :key="survey.id" class="survey-card">
<div class="survey-header">
<div>
<div class="survey-title">{{ survey.title }}</div>
<span :class="['status-badge', statusClass(survey.status)]">{{ statusText(survey.status) }}</span>
<el-tooltip
:content="survey.title"
placement="top"
effect="light"
>
<div class="survey-title">{{ survey.title }}</div>
</el-tooltip>
<span :class="['status-badge', statusClass(survey.status)]">{{
statusText(survey.status)
}}</span>
</div>
<el-dropdown trigger="click">
<el-button size="mini" icon="el-icon-more"></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="openCreateModal('editor',survey.id)"><i class="el-icon-edit"></i>
编辑</el-dropdown-item>
<el-dropdown-item @click.native="viewResults(survey)"><i class="el-icon-data-analysis"></i>
查看结果</el-dropdown-item>
<el-dropdown-item
@click.native="openCreateModal('editor', survey.id)"
><i class="el-icon-edit"></i> 编辑</el-dropdown-item
>
<el-dropdown-item @click.native="viewResults(survey)"
><i class="el-icon-data-analysis"></i>
查看结果</el-dropdown-item
>
<!-- <el-dropdown-item @click.native="duplicateSurvey(survey.id)"><i class="el-icon-document-copy"></i>
复制</el-dropdown-item> -->
<el-dropdown-item divided @click.native="deleteSurvey(survey.id)" style="color:#F56C6C"><i
class="el-icon-delete"></i> 删除</el-dropdown-item>
<el-dropdown-item
divided
@click.native="deleteSurvey(survey.id)"
style="color: #f56c6c"
><i class="el-icon-delete"></i> 删除</el-dropdown-item
>
</el-dropdown-menu>
</el-dropdown>
</div>
<div class="survey-meta">
<div class="meta-item"><i class="el-icon-date"></i> 开始时间{{ survey.start_time }}</div>
<div class="meta-item"><i class="el-icon-time"></i> 截止时间{{ survey.end_time }}</div>
<div class="meta-item">
<i class="el-icon-date"></i> 开始时间{{ survey.start_time }}
</div>
<div class="meta-item">
<i class="el-icon-time"></i> 截止时间{{ survey.end_time }}
</div>
<div class="meta-item" v-if="survey.course">
<i class="el-icon-link"></i>
<span class="course-tag">{{ survey.course.name }}</span>
</div>
<div class="meta-item" v-if="survey.course_content">
<div class="meta-item" v-if="survey.course_content && survey.course_content.length">
<i class="el-icon-document"></i>
<span class="course-tag">{{ survey.course_content.theme }}</span>
<div class="course-content-list">
<el-tooltip
v-for="(content, index) in survey.course_content"
:key="index"
:content="`${content.date} ${content.period} | ${content.theme}`"
placement="top"
effect="light"
>
<span class="course-tag course-content-item">
{{ content.date }} {{ content.period }} | {{ content.theme }}
</span>
</el-tooltip>
</div>
</div>
</div>
<div class="survey-stats">
<div class="stat-item">
<div class="stat-number">{{ survey.course_content_evaluation_forms_count }}</div>
<div class="stat-number">
{{ survey.course_content_evaluation_forms_count }}
</div>
<div class="stat-label">回复数</div>
</div>
<div class="stat-item">
<div class="stat-number">{{ survey.course_content_evaluation_asks_count }}</div>
<div class="stat-number">
{{ survey.course_content_evaluation_asks_count }}
</div>
<div class="stat-label">题目数</div>
</div>
<!-- <div class="stat-item">
@ -75,409 +136,472 @@
</div> -->
</div>
<div class="survey-actions">
<el-button v-if="survey.status==='0'" type="success" size="mini" @click="publishSurvey(survey.id)"><i
class="el-icon-upload2"></i> 发布</el-button>
<el-button type="primary" size="mini" @click="previewSurvey(survey)"><i class="el-icon-view"></i>
预览</el-button>
<el-button
v-if="survey.status === '0'"
type="success"
size="mini"
@click="publishSurvey(survey.id)"
><i class="el-icon-upload2"></i> 发布</el-button
>
<el-button
type="primary"
size="mini"
@click="previewSurvey(survey)"
><i class="el-icon-view"></i> 预览</el-button
>
<!-- <el-button type="info" size="mini" @click="shareSurvey(survey.id)"><i class="el-icon-share"></i> 分享</el-button> -->
<!-- <el-button v-if="survey.responses>0" type="success" size="mini" @click="exportData(survey.id)"><i class="el-icon-download"></i> </el-button> -->
</div>
</div>
</template>
<el-empty v-else description="暂无问卷,点击'创建问卷'开始制作您的第一个调查问卷" />
<el-empty
v-else
description="暂无问卷,点击'创建问卷'开始制作您的第一个调查问卷"
/>
</div>
<div class="survey-pagination" v-if="list.length">
<el-pagination background layout="total, sizes, prev, pager, next, jumper" :total="total"
:page-size="select.page_size" :current-page.sync="select.page" :page-sizes="[6, 12, 24, 48]"
@size-change="onPageSizeChange" @current-change="onPageChange"
style="margin: 24px 0 0 0; text-align: right;" />
<el-pagination
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
:page-size="select.page_size"
:current-page.sync="select.page"
:page-sizes="[6, 12, 24, 48]"
@size-change="onPageSizeChange"
@current-change="onPageChange"
style="margin: 24px 0 0 0; text-align: right"
/>
</div>
</div>
<!-- 创建问卷弹窗合并版 -->
<SurveyCreateWizard ref="SurveyCreateWizard" @refresh="filterSurveys" />
<!-- 问卷结果全屏弹窗 -->
<SurveyResultsDialog :visible.sync="resultsDialogVisible" :surveyData="resultsSurveyData"
@close="resultsDialogVisible=false" />
<SurveyResultsDialog
:visible.sync="resultsDialogVisible"
:surveyData="resultsSurveyData"
@close="resultsDialogVisible = false"
/>
<!-- 问卷填写/预览全屏弹窗 -->
<SurveyFillDialog :visible.sync="fillDialogVisible" :surveyData="fillSurveyData" :previewOnly="fillPreviewOnly"
@close="fillDialogVisible=false" @submit="onFillSubmit" />
<SurveyFillDialog
:visible.sync="fillDialogVisible"
:surveyData="fillSurveyData"
:previewOnly="fillPreviewOnly"
@close="fillDialogVisible = false"
@submit="onFillSubmit"
/>
</div>
</template>
<script>
import SurveyCreateWizard from './components/SurveyCreateWizard.vue';
import SurveyResultsDialog from './components/SurveyResultsDialog.vue';
import SurveyFillDialog from './components/SurveyFillDialog.vue';
import {
index as courseApi
} from "@/api/course/index.js"
import {
index,
destroy,
save
} from "@/api/survey/evaluations.js"
import SurveyCreateWizard from "./components/SurveyCreateWizard.vue";
import SurveyResultsDialog from "./components/SurveyResultsDialog.vue";
import SurveyFillDialog from "./components/SurveyFillDialog.vue";
import { index as courseApi } from "@/api/course/index.js";
import { index, destroy, save } from "@/api/survey/evaluations.js";
export default {
components: {
SurveyCreateWizard,
SurveyResultsDialog,
SurveyFillDialog
export default {
components: {
SurveyCreateWizard,
SurveyResultsDialog,
SurveyFillDialog,
},
data() {
return {
courseList: [],
select: {
page: 1,
page_size: 6,
course_id: "",
title: "",
status: "",
},
list: [],
total: 0,
resultsDialogVisible: false,
resultsSurveyData: null,
fillDialogVisible: false,
fillSurveyData: null,
fillPreviewOnly: false,
};
},
created() {
this.getCourseList();
this.filterSurveys();
},
methods: {
//
async getCourseList() {
const res = await courseApi({
page: 1,
page_size: 999,
});
this.courseList = res.data;
},
data() {
return {
courseList: [],
select: {
page: 1,
page_size: 6,
course_id: '',
title: '',
status: '',
},
list: [],
total: 0,
resultsDialogVisible: false,
resultsSurveyData: null,
fillDialogVisible: false,
fillSurveyData: null,
fillPreviewOnly: false
}
//
async filterSurveys() {
const res = await index({
page: this.select.page,
page_size: this.select.page_size,
filter: [
{
key: "title",
op: "like",
value: this.select.title,
},
{
key: "course_id",
op: "like",
value: this.select.course_id,
},
{
key: "status",
op: "like",
value: this.select.status,
},
],
});
this.list = res.data;
this.total = res.total;
},
created() {
this.getCourseList()
onPageChange(page) {
this.select.page = page;
this.filterSurveys();
},
methods: {
//
async getCourseList() {
const res = await courseApi({
page: 1,
page_size: 999,
})
this.courseList = res.data
},
//
async filterSurveys() {
const res = await index({
page: this.select.page,
page_size: this.select.page_size,
filter: [{
key: 'title',
op: 'like',
value: this.select.title
}, {
key: 'course_id',
op: 'like',
value: this.select.course_id
}, {
key: 'status',
op: 'like',
value: this.select.status
}]
})
this.list = res.data
this.total = res.total
},
onPageChange(page) {
this.select.page = page;
this.filterSurveys()
},
onPageSizeChange(size) {
this.select.page_size = size;
this.select.page = 1;
this.filterSurveys()
},
statusText(status) {
return {
0: '草稿',
1: '已发布'
} [status] || status;
},
statusClass(status) {
return {
0: 'status-draft',
1: 'status-published',
} [status] || '';
},
openCreateModal(type, id) {
if (type === 'editor') {
this.$refs.SurveyCreateWizard.id = id
}
this.$refs.SurveyCreateWizard.type = type
this.$refs.SurveyCreateWizard.courseList = this.courseList
this.$refs.SurveyCreateWizard.isShow = true
},
onPageSizeChange(size) {
this.select.page_size = size;
this.select.page = 1;
this.filterSurveys();
},
statusText(status) {
return (
{
0: "草稿",
1: "已发布",
}[status] || status
);
},
statusClass(status) {
return (
{
0: "status-draft",
1: "status-published",
}[status] || ""
);
},
viewResults(survey) {
this.resultsSurveyData = {
id: 1,
title: '问卷1',
description: '请对本次智能制造专题课程进行评价和反馈',
status: 'published',
type: 'feedback',
bindType: 'course',
bindCourse: '2025产业加速营 | 智能制造专题',
createTime: '2025-06-01 10:00',
deadline: '2025-06-10 23:59',
responses: 1,
questions: 6,
avgScore: 4.6,
questions: [
{
id: 101,
type: 'single',
title: '单选框',
options: ['单选1', '单选2',]
},
// {
// id: 102,
// type: 'rate',
// title: '',
// rateMax: 5
// }
]
};
this.resultsDialogVisible = true;
},
previewSurvey(survey) {
this.fillSurveyData = survey;
this.fillPreviewOnly = true;
this.fillDialogVisible = true;
},
onFillSubmit(answers) {
this.$message.success('提交成功!(模拟)');
this.fillDialogVisible = false;
},
publishSurvey(id) {
openCreateModal(type, id) {
if (type === "editor") {
this.$refs.SurveyCreateWizard.id = id;
}
this.$refs.SurveyCreateWizard.type = type;
this.$refs.SurveyCreateWizard.courseList = this.courseList;
this.$refs.SurveyCreateWizard.isShow = true;
},
save({
id:id,
status:1
}).then(res => {
this.$message({
type: 'success',
message: '发布成功',
duration: 2000
viewResults(survey) {
this.resultsSurveyData = {
id: 1,
title: "问卷1",
description: "请对本次智能制造专题课程进行评价和反馈",
status: "published",
type: "feedback",
bindType: "course",
bindCourse: "2025产业加速营 | 智能制造专题",
createTime: "2025-06-01 10:00",
deadline: "2025-06-10 23:59",
responses: 1,
questions: 6,
avgScore: 4.6,
questions: [
{
id: 101,
type: "single",
title: "单选框",
options: ["单选1", "单选2"],
},
// {
// id: 102,
// type: 'rate',
// title: '',
// rateMax: 5
// }
],
};
this.resultsDialogVisible = true;
},
previewSurvey(survey) {
this.fillSurveyData = survey;
this.fillPreviewOnly = true;
this.fillDialogVisible = true;
},
onFillSubmit(answers) {
this.$message.success("提交成功!(模拟)");
this.fillDialogVisible = false;
},
publishSurvey(id) {
save({
id: id,
status: 1,
}).then((res) => {
this.$message({
type: "success",
message: "发布成功",
duration: 2000,
});
this.filterSurveys();
});
},
shareSurvey(id) {
const url = `${window.location.origin}/survey-fill.html?id=${id}`;
if (navigator.clipboard) {
navigator.clipboard
.writeText(url)
.then(() => {
this.$message.success("问卷链接已复制到剪贴板");
})
this.filterSurveys()
})
},
shareSurvey(id) {
const url = `${window.location.origin}/survey-fill.html?id=${id}`;
if (navigator.clipboard) {
navigator.clipboard.writeText(url).then(() => {
this.$message.success('问卷链接已复制到剪贴板');
}).catch(() => {
this.$prompt('请复制以下链接:', '分享问卷', {
inputValue: url
.catch(() => {
this.$prompt("请复制以下链接:", "分享问卷", {
inputValue: url,
});
});
} else {
this.$prompt('请复制以下链接:', '分享问卷', {
inputValue: url
});
}
},
exportData(id) {
this.$message.info('数据导出功能开发中...');
},
duplicateSurvey(id) {
const survey = this.surveys.find(s => s.id === id);
if (survey) {
const newSurvey = {
...survey,
id: this.surveys.length + 1,
title: survey.title + ' (副本)',
status: 'draft',
createTime: this.formatNow(),
responses: 0
};
this.surveys.unshift(newSurvey);
this.filterSurveys();
}
},
deleteSurvey(id) {
var that = this;
this.$confirm('确定要删除这个问卷吗?此操作不可恢复。', '提示', {
type: 'warning'
}).then(() => {
destroy({
id: id,
}).then(response => {
this.$Message.success('删除成功');
this.filterSurveys()
}).catch(error => {
console.log(error)
reject(error)
})
} else {
this.$prompt("请复制以下链接:", "分享问卷", {
inputValue: url,
});
},
formatNow() {
const d = new Date();
return `${d.getFullYear()}-${(d.getMonth()+1).toString().padStart(2,'0')}-${d.getDate().toString().padStart(2,'0')} ${d.getHours().toString().padStart(2,'0')}:${d.getMinutes().toString().padStart(2,'0')}`;
}
}
}
},
exportData(id) {
this.$message.info("数据导出功能开发中...");
},
duplicateSurvey(id) {
const survey = this.surveys.find((s) => s.id === id);
if (survey) {
const newSurvey = {
...survey,
id: this.surveys.length + 1,
title: survey.title + " (副本)",
status: "draft",
createTime: this.formatNow(),
responses: 0,
};
this.surveys.unshift(newSurvey);
this.filterSurveys();
}
},
deleteSurvey(id) {
var that = this;
this.$confirm("确定要删除这个问卷吗?此操作不可恢复。", "提示", {
type: "warning",
}).then(() => {
destroy({
id: id,
})
.then((response) => {
this.$Message.success("删除成功");
this.filterSurveys();
})
.catch((error) => {
console.log(error);
reject(error);
});
});
},
formatNow() {
const d = new Date();
return `${d.getFullYear()}-${(d.getMonth() + 1)
.toString()
.padStart(2, "0")}-${d.getDate().toString().padStart(2, "0")} ${d
.getHours()
.toString()
.padStart(2, "0")}:${d.getMinutes().toString().padStart(2, "0")}`;
},
},
};
</script>
<style scoped>
.survey-admin {
background: #f8f9fa;
min-height: 100vh;
}
.survey-admin {
background: #f8f9fa;
min-height: 100vh;
}
.admin-header {
background: white;
padding: 20px 30px;
border-bottom: 1px solid #e9ecef;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.admin-title {
font-size: 24px;
font-weight: 700;
color: #2c3e50;
margin: 0;
display: flex;
align-items: center;
gap: 12px;
}
.admin-main {
padding: 30px;
max-width: 1400px;
margin: 0 auto;
}
.admin-header {
background: white;
padding: 20px 30px;
border-bottom: 1px solid #e9ecef;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.action-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
flex-wrap: wrap;
gap: 16px;
}
.admin-title {
font-size: 24px;
font-weight: 700;
color: #2c3e50;
margin: 0;
display: flex;
align-items: center;
gap: 12px;
}
.search-box {
display: flex;
gap: 12px;
align-items: center;
}
.admin-main {
padding: 30px;
max-width: 1400px;
margin: 0 auto;
}
.survey-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 24px;
}
.action-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
flex-wrap: wrap;
gap: 16px;
}
.survey-card {
background: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
padding: 24px;
transition: all 0.3s ease;
border: 1px solid #e9ecef;
}
.search-box {
display: flex;
gap: 12px;
align-items: center;
}
.survey-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
.survey-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 24px;
}
.survey-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 16px;
}
.survey-card {
background: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
padding: 24px;
transition: all 0.3s ease;
border: 1px solid #e9ecef;
}
.survey-title {
font-size: 18px;
font-weight: 600;
color: #2c3e50;
margin-bottom: 8px;
line-height: 1.4;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 280px;
}
.survey-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
.survey-meta {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 16px;
}
.survey-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 16px;
}
.meta-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: #6c757d;
}
.survey-title {
font-size: 18px;
font-weight: 600;
color: #2c3e50;
margin-bottom: 8px;
line-height: 1.4;
}
.survey-stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
margin-bottom: 16px;
}
.survey-meta {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 16px;
}
.stat-item {
text-align: center;
padding: 12px;
background: #f8f9fa;
border-radius: 8px;
}
.meta-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: #6c757d;
}
.stat-number {
font-size: 20px;
font-weight: 700;
color: #3498db;
}
.survey-stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
margin-bottom: 16px;
}
.stat-label {
font-size: 12px;
color: #6c757d;
margin-top: 4px;
}
.stat-item {
text-align: center;
padding: 12px;
background: #f8f9fa;
border-radius: 8px;
}
.survey-actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.stat-number {
font-size: 20px;
font-weight: 700;
color: #3498db;
}
.status-badge {
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
}
.stat-label {
font-size: 12px;
color: #6c757d;
margin-top: 4px;
}
.status-draft {
background: #f8f9fa;
color: #6c757d;
}
.survey-actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.status-published {
background: #d4edda;
color: #155724;
}
.status-badge {
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
}
.status-closed {
background: #f8d7da;
color: #721c24;
}
.status-draft {
background: #f8f9fa;
color: #6c757d;
}
.course-tag {
display: inline-block;
background: #e3f2fd;
color: #1976d2;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
margin-right: 8px;
margin-bottom: 4px;
}
.status-published {
background: #d4edda;
color: #155724;
}
.course-content-list {
display: flex;
flex-direction: column;
gap: 4px;
}
.status-closed {
background: #f8d7da;
color: #721c24;
}
.course-content-list .course-tag {
margin-right: 0;
margin-bottom: 0;
background: #f0f8ff;
color: #0066cc;
border: 1px solid #b3d9ff;
}
.course-tag {
display: inline-block;
background: #e3f2fd;
color: #1976d2;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
margin-right: 8px;
margin-bottom: 4px;
}
</style>
.course-content-item {
display: block;
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 300px;
}
</style>

Loading…
Cancel
Save