lion 1 year ago
parent 2e2bb5e706
commit b1bd9ffc97

@ -2,5 +2,10 @@
ENV = 'production'
# base api
VUE_APP_BASE_API = https://wx.sstbc.com
VUE_APP_UPLOAD_API = https://wx.sstbc.com/api/admin/upload-file
VUE_APP_BASE_API=https://suzhoukeji-test.ali251.langye.net
VUE_APP_UPLOAD_API=https://suzhoukeji-test.ali251.langye.net/api/admin/upload-file
#VUE_APP_BASE_API = https://wx.sstbc.com
#VUE_APP_UPLOAD_API = https://wx.sstbc.com/api/admin/upload-file

@ -0,0 +1,60 @@
import request from "@/utils/request";
function customParamsSerializer(params) {
let result = '';
for (let key in params) {
if (params.hasOwnProperty(key)) {
if (Array.isArray(params[key])) {
params[key].forEach((item,index) => {
if(item.key){
result += `${key}[${index}][key]=${item.key}&${key}[${index}][op]=${item.op}&${key}[${index}][value]=${item.value}&`;
}else{
result +=`${key}[${index}]=${item}&`
}
});
} else {
result += `${key}=${params[key]}&`;
}
}
}
return result.slice(0, -1);
}
export function index(params,isLoading = false) {
return request({
method: "get",
url: "/api/admin/course-content-evaluations/index",
params,
paramsSerializer: customParamsSerializer,
isLoading
})
}
export function show(params, isLoading = true) {
return request({
method: "get",
url: "/api/admin/course-content-evaluations/show",
params,
isLoading
})
}
export function save(data) {
return request({
method: "post",
url: "/api/admin/course-content-evaluations/save",
data
})
}
export function destroy(params) {
return request({
method: "get",
url: "/api/admin/course-content-evaluations/destroy",
params
})
}

@ -0,0 +1,60 @@
import request from "@/utils/request";
function customParamsSerializer(params) {
let result = '';
for (let key in params) {
if (params.hasOwnProperty(key)) {
if (Array.isArray(params[key])) {
params[key].forEach((item,index) => {
if(item.key){
result += `${key}[${index}][key]=${item.key}&${key}[${index}][op]=${item.op}&${key}[${index}][value]=${item.value}&`;
}else{
result +=`${key}[${index}]=${item}&`
}
});
} else {
result += `${key}=${params[key]}&`;
}
}
}
return result.slice(0, -1);
}
export function index(params,isLoading = false) {
return request({
method: "get",
url: "/api/admin/course-content-evaluation-ask/index",
params,
paramsSerializer: customParamsSerializer,
isLoading
})
}
export function show(params, isLoading = true) {
return request({
method: "get",
url: "/api/admin/course-content-evaluation-ask/show",
params,
isLoading
})
}
export function save(data) {
return request({
method: "post",
url: "/api/admin/course-content-evaluation-ask/save",
data
})
}
export function destroy(params) {
return request({
method: "get",
url: "/api/admin/course-content-evaluation-ask/destroy",
params
})
}

@ -0,0 +1,209 @@
<template>
<div class="course-qr-admin">
<!-- 顶部标题 -->
<div class="admin-header">
<h1 class="admin-title">
<i class="el-icon-qr-code"></i>
课程二维码管理
</h1>
</div>
<div class="admin-main">
<!-- 课程列表面板 -->
<div class="course-list-panel">
<div class="panel-title">
<i class="el-icon-menu"></i>
选择课程
</div>
<el-scrollbar style="height: 600px;">
<div v-for="course in courses" :key="course.id" :class="['course-item', {active: selectedCourse && selectedCourse.id === course.id}]" @click="selectCourse(course)">
<div class="course-name">{{ course.name }}</div>
<div class="course-info">
<span><i class="el-icon-date"></i> {{ course.time }}</span>
<span><i class="el-icon-location"></i> {{ course.location }}</span>
<span><i class="el-icon-user"></i> {{ course.teacher }}</span>
</div>
</div>
</el-scrollbar>
</div>
<!-- 二维码展示面板 -->
<div class="qr-display-panel">
<div class="panel-title">
<i class="el-icon-qr-code"></i>
课程签到二维码
</div>
<div v-if="!selectedCourse" class="empty-state">
<i class="el-icon-qr-code" style="font-size:64px;opacity:0.5;"></i>
<h5>请选择课程生成二维码</h5>
<p class="text-muted">选择左侧课程后将自动生成对应的签到二维码</p>
</div>
<div v-else>
<div class="qr-container">
<h4 style="margin-bottom: 20px; color: #2c3e50;">{{ selectedCourse.name }}</h4>
<div class="qr-code">
<img :src="qrImgUrl" alt="二维码" style="width:200px;height:200px;" />
</div>
<div class="qr-info">
<h6><i class="el-icon-info"></i> 课程信息</h6>
<div class="qr-info-grid">
<div><strong>时间</strong>{{ selectedCourse.time }}</div>
<div><strong>地点</strong>{{ selectedCourse.location }}</div>
<div><strong>主讲</strong>{{ selectedCourse.teacher }}</div>
<div><strong>类型</strong>{{ getTypeName(selectedCourse.type) }}</div>
</div>
</div>
<div class="qr-actions">
<el-button type="primary" icon="el-icon-download" class="btn-qr" @click="downloadQR"></el-button>
<el-button type="success" icon="el-icon-view" class="btn-qr" @click="previewCheckin"></el-button>
<el-button type="info" icon="el-icon-document-copy" class="btn-qr" @click="copyUrl"></el-button>
</div>
</div>
<div class="usage-tips">
<h6><i class="el-icon-lightning"></i> 使用说明</h6>
<ul>
<li>学员扫描此二维码可直接进入课程签到页面</li>
<li>二维码包含课程信息自动识别对应课程</li>
<li>支持微信扫一扫</li>
<li>建议在课程开始前30分钟展示二维码</li>
<li>可下载二维码图片用于打印或分享</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'CourseQr',
data() {
return {
courses: [
{
id: 1,
name: '2025产业加速营 | 智能制造专题',
time: '2025-06-04 09:00-17:00',
location: '商学院A101',
teacher: '王教授',
type: 'course'
},
{
id: 2,
name: '校友企业参访 | 新能源企业',
time: '2025-06-10 13:30-17:00',
location: '苏州高新区',
teacher: '李总监',
type: 'activity'
},
{
id: 3,
name: '移动课堂 | AI商业落地',
time: '2025-06-15 09:00-16:00',
location: '创新实验室B202',
teacher: '张教授',
type: 'workshop'
},
{
id: 4,
name: '2025校友论坛 | 数字经济新机遇',
time: '2025-06-20 14:00-17:30',
location: '报告厅',
teacher: '特邀嘉宾',
type: 'activity'
},
{
id: 5,
name: '2025暑期创新营 | 项目路演',
time: '2025-06-27 09:00-12:00',
location: '多功能厅',
teacher: '创业导师团',
type: 'course'
}
],
selectedCourse: null
}
},
watch: {
selectedCourse(val) {
if (val) this.$nextTick(this.renderQRCode)
}
},
methods: {
selectCourse(course) {
this.selectedCourse = course
},
renderQRCode() {
const checkinUrl = `${window.location.origin}/course-checkin.html?courseId=${this.selectedCourse.id}&courseName=${encodeURIComponent(this.selectedCourse.name)}`
const qrDiv = this.$refs.qrcode
qrDiv.innerHTML = ''
//
//
this.qrImgUrl = checkinUrl
},
getTypeName(type) {
const types = { course: '课程', activity: '活动', workshop: '移动课堂' }
return types[type] || type
},
downloadQR() {
if (!this.selectedCourse) return
//
const link = document.createElement('a')
link.download = `${this.selectedCourse.name}-签到二维码.png`
link.href = this.qrImgUrl
link.click()
},
previewCheckin() {
if (!this.selectedCourse) return
const checkinUrl = `${window.location.origin}/course-checkin.html?courseId=${this.selectedCourse.id}&courseName=${encodeURIComponent(this.selectedCourse.name)}`
window.open(checkinUrl, '_blank')
},
copyUrl() {
if (!this.selectedCourse) return
const checkinUrl = `${window.location.origin}/course-checkin.html?courseId=${this.selectedCourse.id}&courseName=${encodeURIComponent(this.selectedCourse.name)}`
if (navigator.clipboard) {
navigator.clipboard.writeText(checkinUrl).then(() => {
this.$message.success('签到链接已复制到剪贴板')
}).catch(() => {
this.$prompt('请复制以下链接:', '复制签到链接', { inputValue: checkinUrl })
})
} else {
this.$prompt('请复制以下链接:', '复制签到链接', { inputValue: checkinUrl })
}
}
},
computed: {
qrImgUrl() {
if (!this.selectedCourse) return ''
// /api/qr/course?id=xxx
return `/api/qr/course?id=${this.selectedCourse.id}`
}
}
}
</script>
<style scoped>
.course-qr-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 { display: flex; gap: 24px; padding: 30px; min-height: calc(100vh - 100px); }
.course-list-panel { flex: 1; background: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); padding: 24px; max-width: 400px; }
.qr-display-panel { flex: 2; background: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); padding: 24px; display: flex; flex-direction: column; align-items: center; text-align: center; }
.panel-title { font-size: 18px; font-weight: 600; margin-bottom: 20px; color: #3498db; display: flex; align-items: center; gap: 8px; }
.course-item { border: 1px solid #e9ecef; border-radius: 8px; padding: 16px; margin-bottom: 12px; cursor: pointer; transition: all 0.3s ease; }
.course-item:hover { border-color: #3498db; box-shadow: 0 2px 8px rgba(52, 152, 219, 0.2); }
.course-item.active { border-color: #3498db; background-color: #e3f2fd; }
.course-name { font-weight: 600; font-size: 16px; margin-bottom: 8px; color: #2c3e50; }
.course-info { font-size: 14px; color: #6c757d; display: flex; flex-direction: column; gap: 4px; }
.qr-container { background: white; border: 2px solid #e9ecef; border-radius: 16px; padding: 30px; margin: 20px 0; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
.qr-code { margin: 0 auto; }
.qr-info { margin-top: 20px; padding: 20px; background: #f8f9fa; border-radius: 12px; text-align: left; }
.qr-info-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; margin-top: 12px; }
.qr-actions { display: flex; gap: 12px; margin-top: 20px; justify-content: center; }
.btn-qr { padding: 12px 24px; border-radius: 8px; font-weight: 500; display: flex; align-items: center; gap: 8px; transition: all 0.3s ease; }
.btn-qr:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.2); }
.empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 400px; color: #6c757d; }
.usage-tips { background: #e8f5e9; border: 1px solid #c8e6c9; border-radius: 8px; padding: 16px; margin-top: 20px; text-align: left; }
.usage-tips h6 { color: #2e7d32; margin-bottom: 12px; }
.usage-tips ul { margin: 0; padding-left: 20px; color: #2e7d32; }
@media (max-width: 768px) { .admin-main { flex-direction: column; padding: 15px; gap: 15px; } .course-list-panel { max-width: none; } .qr-container { padding: 20px; } .qr-actions { flex-direction: column; } .btn-qr { width: 100%; justify-content: center; } }
</style>

@ -64,21 +64,23 @@ export default {
name: 'SurveyCreateDialog',
components: { draggable },
props: {
visible: Boolean,
surveyData: Object
},
data() {
return {
visible:false,
course_content_id:'',
localSurvey: {
title: '',
description: '',
questions: []
},
questionTypes: [
{ label: '单选题', value: 'single' },
{ label: '多选题', value: 'multi' },
{ label: '单选题', value: 'radio' },
{ label: '多选题', value: 'checkbox' },
{ label: '文本题', value: 'text' },
{ label: '评分题', value: 'rate' }
{ label: '评分题', value: 'number' },
{ label: '评分题', value: 'number' }
]
}
},

@ -0,0 +1,461 @@
<template>
<div>
<xy-dialog ref="dialog" :width="70" :is-show.sync="isShow" :type="'form'" :title="type === 'add' ? '新增问卷' : '编辑问卷'"
:form="form" :rules='rules' :hasIscheck="true" @mysubmit="mysubmit">
<template v-slot:step>
<div class="xy-table-item">
<div class="xy-table-item-label" style="font-weight: bold;display:none">
<span style="color: red;font-weight: bold;padding-right: 4px;"></span>
</div>
<div class="xy-table-item-content">
<el-steps :active="active" finish-status="success" simple>
<el-step title="问卷信息"></el-step>
<el-step title="问题信息"></el-step>
</el-steps>
</div>
</div>
</template>
<template v-slot:course_id v-if="active===0">
<div class="xy-table-item">
<div class="xy-table-item-label" style="font-weight: bold">
<span style="color: red;font-weight: bold;padding-right: 4px;">*</span>课程
</div>
<div class="xy-table-item-content">
<el-select v-model="form.course_id" @change="changeCourse" placeholder="请选择课程" clearable style="width: 100%;">
<el-option v-for="item in courseList" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
</div>
</div>
</template>
<template v-slot:course_content_id v-if="active===0">
<div class="xy-table-item">
<div class="xy-table-item-label" style="font-weight: bold">
<span style="color: red;font-weight: bold;padding-right: 4px;">*</span>课表
</div>
<div class="xy-table-item-content">
<el-select v-model="form.course_content_id" placeholder="请选择课表" clearable style="width: 100%;">
<el-option v-for="item in courseContentList" :key="item.id" :label="item.theme" :value="item.id">
</el-option>
</el-select>
</div>
</div>
</template>
<template v-slot:title v-if="active===0">
<div class="xy-table-item">
<div class="xy-table-item-label" style="font-weight: bold">
<span style="color: red;font-weight: bold;padding-right: 4px;">*</span>问卷标题
</div>
<div class="xy-table-item-content">
<el-input v-model="form.title" placeholder="请输入问卷标题" clearable style="width: 100%;"></el-input>
</div>
</div>
</template>
<template v-slot:desc v-if="active===0">
<div class="xy-table-item">
<div class="xy-table-item-label" style="font-weight: bold">
<span style="color: red;font-weight: bold;padding-right: 4px;"></span>问卷描述
</div>
<div class="xy-table-item-content">
<el-input v-model="form.desc" placeholder="请输入问卷描述" clearable style="width: 100%;"></el-input>
</div>
</div>
</template>
<template v-slot:type_id v-if="active===0">
<div class="xy-table-item">
<div class="xy-table-item-label" style="font-weight: bold">
<span style="color: red;font-weight: bold;padding-right: 4px;"></span>问卷类型
</div>
<div class="xy-table-item-content">
<el-select v-model="form.type_id" placeholder="请选择问卷类型" clearable style="width: 100%;">
<el-option v-for="item in courseEvaluationList" :key="item.id" :label="item.value" :value="item.id">
</el-option>
</el-select>
</div>
</div>
</template>
<template v-slot:start_time v-if="active===0">
<div class="xy-table-item">
<div class="xy-table-item-label" style="font-weight: bold">
<span style="color: red;font-weight: bold;padding-right: 4px;"></span>开始时间
</div>
<div class="xy-table-item-content">
<el-date-picker style="width: 100%;" v-model="form.start_time" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
format="yyyy-MM-dd HH:mm:ss" placeholder="开始时间">
</el-date-picker>
</div>
</div>
</template>
<template v-slot:end_time v-if="active===0">
<div class="xy-table-item">
<div class="xy-table-item-label" style="font-weight: bold">
<span style="color: red;font-weight: bold;padding-right: 4px;"></span>截止时间
</div>
<div class="xy-table-item-content">
<el-date-picker style="width: 100%;" v-model="form.end_time" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
format="yyyy-MM-dd HH:mm:ss" placeholder="截止时间">
</el-date-picker>
</div>
</div>
</template>
<!-- 自定义报名 表单 -->
<template v-slot:question v-if="active===1">
<div class="xy-table-item">
<div class="xy-table-item-label" style="font-weight: bold;display:none">
<span style="color: red;font-weight: bold;padding-right: 4px;"></span>
</div>
<div class="xy-table-item-content" style="min-height: 300px;">
<div>
<el-dialog id="template-dialog" :title="'问卷问题'"
style="position: relative; inset: 0 0 0 0; height: 100%;padding:20px 0" width="90%" top="0px"
:visible="true" :modal="false" :append-to-body="false" :show-close="false"
:modal-append-to-body="false">
<template v-slot:title>
<Button v-if="questionList.length>0" ghost type="primary" @click="editForm('editor')"></Button>
<Button v-else ghost type="primary" @click="editForm('add')"></Button>
</template>
<el-form style="min-height: 300px;display: flex;flex-wrap: wrap;justify-content: space-between;padding: 0 4%;" label-width="80px"
label-position="top" size="small">
<el-divider>问卷问题详情</el-divider>
<el-form-item style="flex-basis: 45%;" :label="i.name || '问题名称'" :required="
i.rule ? !!i.rule.includes('required') : false
" v-for="(i, index) in questionList">
<formSlotRender :config="i" :index="index"></formSlotRender>
</el-form-item>
</el-form>
</el-dialog>
</div>
</div>
</div>
</template>
<template v-slot:footerContent>
<Button ghost type="primary" v-if="active===1" @click="active=0"></Button>
<!-- <Button ghost type="primary" v-if="active===0" @click="next"></Button> -->
<Button type="primary" ghost @click="checkSubmit" v-if="active===0"></Button>
<Button type="primary" ghost v-if="active===1" @click="closeSubmit(0)" >保存待发布</Button>
<Button type="primary" v-if="active===1" @click="closeSubmit(1)"></Button>
</template>
</xy-dialog>
<!-- 发布提示 -->
<el-dialog title="是否确认发布" :visible.sync="showTips" width="30%">
<div style="padding: 30px;font-size: 24px;text-indent: 2em;">请确认问卷表单无误后再发布</div>
<span slot="footer" class="dialog-footer">
<el-button @click="showTips = false"> </el-button>
<el-button type="primary" @click="saveSubmit"> </el-button>
</span>
</el-dialog>
<applyForm ref="applyForm" @refresh="getquestionList"></applyForm>
</div>
</template>
<script>
import {
show,
save
} from "@/api/survey/evaluations.js"
import {
index
} from "@/api/course/courseContent.js"
import {
index as formIndex
} from "@/api/survey/index.js";
import {
getparameter
} from "@/api/system/dictionary.js"
import {
deepCopy
} from "@/utils";
import applyForm from "../components/applyForm.vue";
import formSlotRender from "@/views/system/components/formSlotRender.vue";
export default {
components: {
applyForm,
formSlotRender
},
data() {
return {
isShow: false,
showTips:false,
type: 'add',
active: 0,
id: '',
questionLinkIds:{
course_id:'',
course_content_id:'',
course_content_evaluation_id:''
},
course_content_id:'',
course_signs_count: 0,
courseList:[],
courseContentList:[],
courseEvaluationList:[],
form: {
step: '', //
course_id:"",
course_content_id:"",
title:"",
desc:'',
type_id:'',
start_time:'',
end_time:'',
status:0,
question:''
},
rules: {
title: [{
required: true,
message: '请输入问卷标题'
}],
id: [{
required: true,
message: '请选择课表'
}],
// start_time: [{
// required: true,
// message: ''
// }],
// end_time: [{
// required: true,
// message: ''
// }],
},
//
selectForm: {
page: 1,
page_size: 999,
sort_name: 'sort',
sort_type: 'ASC'
},
questionList: []
}
},
created() {
this.getEList()
},
methods: {
changeCourse(e) {
if(e){
this.filterSurveys(e)
}
},
async getEList(){
const res = await getparameter({
number: 'course_content_evaluation_type'
})
this.courseEvaluationList = res.detail
},
//
async filterSurveys(course_id) {
const res = await index({
page:1,
page_size:999,
filter:[{
key:'course_id',
op:'like',
value:course_id
}]
})
if(res.data.length<1){
this.$Message.warning("该课程暂无课表")
return
}
this.courseContentList = res.data
},
// form
mysubmit(e) {
if (e) {
this.submit()
}
},
checkSubmit() {
this.$refs.dialog.submit(true)
},
compareTime(date, sign) {
let date1 = this.$moment(date)
let sign1 = this.$moment(sign)
return sign1.isBefore(date1)
},
submit() {
//
if (this.form.start_time && this.form.end_time) {
if (this.compareTime(this.form.start_time, this.form.end_time)) {
this.$Message.warning("截止时间不能晚于开始时间")
return
}
}
save({
...this.form
}).then(res => {
this.$message({
type: 'success',
message: '保存问卷成功',
duration: 2000
})
this.id = res.id
this.questionLinkIds = {
course_id:res.course_id,
course_content_id:res.course_content_id,
course_content_evaluation_id:res.id
}
this.$emit('refresh')
this.active = 1
// this.active = 1
})
},
//
saveSubmit(){
this.form.id = this.id
this.form.status = 1
save({
...this.form
}).then(res => {
this.$message({
type: 'success',
message: '发布成功',
duration: 2000
})
this.showTips = false
this.$emit('refresh')
this.isShow = false
})
},
closeSubmit(status) {
//
if(status==1){
if(this.questionList.length<1){
this.$message({
type:'warning',
message:'需要先创建问题列表才可以发布',
duration:2000
})
return
}
//
if(this.form.status===1){
this.$emit('refresh')
this.isShow = false
}else{
this.showTips = true
}
}else{
this.form.id = this.id
this.form.status = 0
save({
...this.form
}).then(res => {
this.$message({
type: 'success',
message: '保存成功',
duration: 2000
})
this.$emit('refresh')
this.isShow = false
})
}
},
getDetail() {
show({
id: this.id,
}).then(res => {
this.form = this.base.requestToForm(res, this.form)
this.filterSurveys(this.form.course_id)
this.form.status = res.status ? res.status : 0
this.questionLinkIds = {
course_id:res.course_id,
course_content_id:res.course_content_id,
course_content_evaluation_id:res.id
}
})
},
//
next() {
if (this.id) {
this.active = 1
} else {
this.$Message.warning("请先保存问卷信息")
}
},
//
async getquestionList() {
const res = await formIndex({
filter: [{
key: 'course_content_evaluation_id',
op: 'eq',
value: this.id
}],
...this.selectForm
})
this.questionList = res.data
},
//
editForm(type, id) {
// this.$refs.applyForm.questionList = deepCopy(this.questionList)
// this.$refs.applyForm.course_content_id = this.id
// this.$refs.applyForm.course_signs_count = this.course_signs_count
// this.$refs.applyForm.visible = true
this.$refs.applyForm.questionLinkIds = this.questionLinkIds
this.$refs.applyForm.dialogVisible = true
}
},
watch: {
isShow(newVal) {
if (newVal) {
if (this.type === 'editor') {
this.getDetail()
this.getquestionList()
}
} else {
this.id = ''
this.course_signs_count = 0
this.active = 0
this.questionList = []
this.courseList = []
this.courseContentList = []
this.courseEvaluationList = []
this.questionLinkIds = {
course_id:'',
course_content_id:'',
course_content_evaluation_id:''
}
this.form = {
step: '', //
course_id:"",
course_content_id:"",
title:"",
desc:'',
type_id:'',
start_time:'',
end_time:'',
status:0,
question:''
}
this.$refs['dialog'].reset()
}
},
}
}
</script>
<style scoped lang="scss">
::v-deep .step,
::v-deep .question {
flex-basis: 100%;
}
::v-deep .el-step.is-simple .el-step__head {
display: flex
}
</style>

@ -1,231 +1,308 @@
<template>
<el-dialog :visible.sync="visible" width="60%" :show-close="false" class="survey-dialog-mobile">
<div class="survey-mobile-bg">
<div class="survey-mobile-card">
<div class="survey-header">
<div class="survey-title">{{ surveyData.title }}</div>
<div class="survey-description">{{ surveyData.description }}</div>
<div class="survey-meta">
<span class="meta-item"><i class="el-icon-date"></i> {{ surveyData.createTime }}</span>
<span class="meta-item"><i class="el-icon-time"></i> {{ surveyData.deadline }}</span>
</div>
</div>
<div class="survey-form">
<div v-for="(q, idx) in surveyData.questions" :key="q.id" class="question-item">
<div class="question-header">
<span class="question-number">{{ idx+1 }}</span>
<span class="question-title">{{ q.title }}</span>
<span v-if="q.required" class="required-mark">*</span>
</div>
<div class="question-content">
<template v-if="q.type==='single'">
<el-radio-group v-model="answers[q.id]" :disabled="previewOnly" class="mobile-radio-group">
<el-radio v-for="(opt, oidx) in q.options" :key="oidx" :label="opt" class="mobile-radio">{{ opt }}</el-radio>
</el-radio-group>
<template>
<el-dialog :visible.sync="visible" width="60%" :show-close="false" class="survey-dialog-mobile">
<div class="survey-mobile-bg">
<div class="survey-mobile-card">
<div class="survey-header">
<div class="survey-title">{{ surveyData.title }}</div>
<div class="survey-description">{{ surveyData.desc }}</div>
<div class="survey-meta">
<span class="meta-item"><i class="el-icon-date"></i> {{ surveyData.start_time }}</span>
<span class="meta-item"><i class="el-icon-time"></i> {{ surveyData.end_time }}</span>
</div>
</div>
<div class="survey-form">
<div v-for="(q, idx) in questionList" :key="q.id" class="question-item">
<div class="question-header">
<span class="question-number">{{ idx+1 }}</span>
<span v-if="q.rule && q.rule.includes('required')" class="required-mark">*</span>
<span class="question-title">{{ q.name }}</span>
</div>
<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.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.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" />
</template>
<template v-else-if="q.edit_input==='text'">
<el-input type="text" v-model="answers[q.id]" :disabled="previewOnly" :placeholder="q.help||'请输入内容'"
class="mobile-textarea" />
</template>
<template v-else-if="q.edit_input==='textarea'">
<el-input type="textarea" v-model="answers[q.id]" :disabled="previewOnly" :placeholder="q.help||'请输入内容'"
class="mobile-textarea" />
</template>
<template v-else-if="q.edit_input==='date'">
<el-date-picker v-model="answers[q.id]" :disabled="previewOnly" type="date" :placeholder="q.help||'选择日期'">
</el-date-picker>
</template>
<template v-else-if="q.type==='multi'">
<el-checkbox-group v-model="answers[q.id]" :disabled="previewOnly" class="mobile-checkbox-group">
<el-checkbox v-for="(opt, oidx) in q.options" :key="oidx" :label="opt" class="mobile-checkbox">{{ opt }}</el-checkbox>
</el-checkbox-group>
</template>
<template v-else-if="q.type==='text'">
<el-input type="textarea" v-model="answers[q.id]" :disabled="previewOnly" :placeholder="q.placeholder||'请输入内容'" class="mobile-textarea" />
</template>
<template v-else-if="q.type==='rate'">
<el-rate v-model="answers[q.id]" :max="q.rateMax||5" :disabled="previewOnly" class="mobile-rate" />
</template>
<template v-else-if="q.type==='number'">
<el-input type="number" v-model="answers[q.id]" :min="q.min" :max="q.max" :disabled="previewOnly" class="mobile-input" />
</template>
<template v-else-if="q.type==='select'">
<el-select v-model="answers[q.id]" :disabled="previewOnly" class="mobile-select" placeholder="请选择">
<el-option v-for="(opt, oidx) in q.options" :key="oidx" :label="opt" :value="opt" />
</el-select>
</template>
<template v-else-if="q.type==='scale'">
<div class="scale-container">
<span class="scale-label">{{ q.minLabel }}</span>
<el-slider v-model="answers[q.id]" :min="q.min" :max="q.max" :disabled="previewOnly" class="mobile-slider" />
<span class="scale-label">{{ q.maxLabel }}</span>
</div>
</template>
</div>
</div>
<div style="text-align:center;margin-top:32px;">
<el-button v-if="!previewOnly" type="primary" class="mobile-btn" @click="handleSubmit"></el-button>
<el-button class="mobile-btn" @click="$emit('close')"></el-button>
</div>
</div>
</div>
</div>
</el-dialog>
</template>
<script>
export default {
name: 'SurveyFillDialog',
props: {
visible: Boolean,
surveyData: Object,
previewOnly: Boolean
},
data() {
return {
answers: {}
}
},
watch: {
surveyData: {
handler(val) {
if (val && val.questions) {
this.answers = {};
val.questions.forEach(q => {
if (q.type==='multi') this.answers[q.id] = [];
else this.answers[q.id] = '';
});
}
},
immediate: true
}
},
methods: {
handleSubmit() {
this.$emit('submit', this.answers);
}
}
}
</script>
<style scoped>
.survey-dialog-mobile >>> .el-dialog__body { padding:0; background: #f4f6fa; }
.survey-mobile-bg {
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
}
.survey-mobile-card {
width: 100vw;
max-width: 414px;
min-width: 320px;
background: #fff;
border-radius: 18px;
box-shadow: 0 4px 24px rgba(0,0,0,0.12);
margin: 24px 0;
padding: 0 0 24px 0;
overflow: hidden;
}
.survey-header {
padding: 32px 20px 16px 20px;
text-align: center;
background: #fff;
border-radius: 18px 18px 0 0;
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
}
.survey-title {
font-size: 22px;
font-weight: 700;
color: #2c3e50;
margin-bottom: 10px;
}
.survey-description {
font-size: 15px;
color: #6c757d;
margin-bottom: 10px;
}
.survey-meta {
display: flex;
justify-content: center;
gap: 16px;
flex-wrap: wrap;
font-size: 12px;
color: #888;
}
.meta-item { display: flex; align-items: center; gap: 4px; }
.survey-form {
padding: 18px 16px 0 16px;
}
.question-item {
margin-bottom: 28px;
padding-bottom: 18px;
border-bottom: 1px solid #f0f0f0;
}
.question-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
}
.question-number {
background: #3498db;
color: #fff;
width: 22px;
height: 22px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 13px;
font-weight: 600;
flex-shrink: 0;
}
.question-title {
font-size: 15px;
font-weight: 600;
color: #2c3e50;
flex: 1;
}
.required-mark {
color: #e74c3c;
margin-left: 4px;
font-size: 15px;
}
.question-content {
margin-left: 0;
margin-top: 2px;
}
.mobile-radio-group, .mobile-checkbox-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.mobile-radio, .mobile-checkbox {
font-size: 15px;
height: 32px;
line-height: 32px;
}
.mobile-textarea {
font-size: 15px;
border-radius: 10px;
}
.mobile-rate {
font-size: 22px;
}
.mobile-input, .mobile-select {
font-size: 15px;
border-radius: 10px;
width: 100%;
}
.scale-container {
display: flex;
align-items: center;
gap: 8px;
margin-top: 8px;
}
.scale-label {
font-size: 13px;
color: #888;
}
.mobile-slider {
flex: 1;
}
.mobile-btn {
width: 90%;
height: 44px;
font-size: 17px;
border-radius: 22px;
margin-bottom: 10px;
}
@media (max-width: 500px) {
.survey-mobile-card { max-width: 100vw; min-width: 0; border-radius: 0; }
}
<template v-else-if="q.edit_input==='datetime'">
<el-date-picker v-model="answers[q.id]" :disabled="previewOnly" type="datetime" :placeholder="q.help||'选择日期时间'">
</el-date-picker>
</template>
<!-- <template v-else-if="q.type==='rate'">
<el-rate v-model="answers[q.id]" :max="q.rateMax||5" :disabled="previewOnly" class="mobile-rate" />
</template>
<template v-else-if="q.type==='number'">
<el-input type="number" v-model="answers[q.id]" :min="q.min" :max="q.max" :disabled="previewOnly" class="mobile-input" />
</template>
<template v-else-if="q.type==='select'">
<el-select v-model="answers[q.id]" :disabled="previewOnly" class="mobile-select" placeholder="请选择">
<el-option v-for="(opt, oidx) in q.options" :key="oidx" :label="opt" :value="opt" />
</el-select>
</template>
<template v-else-if="q.type==='scale'">
<div class="scale-container">
<span class="scale-label">{{ q.minLabel }}</span>
<el-slider v-model="answers[q.id]" :min="q.min" :max="q.max" :disabled="previewOnly" class="mobile-slider" />
<span class="scale-label">{{ q.maxLabel }}</span>
</div>
</template> -->
</div>
</div>
<div style="text-align:center;margin-top:32px;">
<el-button v-if="!previewOnly" type="primary" class="mobile-btn" @click="handleSubmit"></el-button>
<el-button class="mobile-btn" @click="$emit('close')"></el-button>
</div>
</div>
</div>
</div>
</el-dialog>
</template>
<script>
import {
index as formIndex
} from "@/api/survey/index.js";
export default {
name: 'SurveyFillDialog',
props: {
visible: Boolean,
surveyData: Object,
previewOnly: Boolean
},
data() {
return {
questionList: [],
answers: {}
}
},
watch: {
surveyData: {
handler(val) {
if (val) {
this.answers = {};
console.log("val", val)
this.getquestionList(val.id)
} else {
this.questionList = []
}
},
immediate: true
}
},
methods: {
handleSubmit() {
this.$emit('submit', this.answers);
},
async getquestionList(id) {
const res = await formIndex({
page: 1,
page_size: 999,
sort_name: 'sort',
sort_type: 'ASC',
filter: [{
key: 'course_content_evaluation_id',
op: 'eq',
value: id
}],
})
this.questionList = res.data
},
}
}
</script>
<style scoped>
.survey-dialog-mobile>>>.el-dialog__body {
padding: 0;
background: #f4f6fa;
}
.survey-mobile-bg {
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
}
.survey-mobile-card {
width: 100vw;
max-width: 414px;
min-width: 320px;
background: #fff;
border-radius: 18px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.12);
margin: 24px 0;
padding: 0 0 24px 0;
overflow: hidden;
}
.survey-header {
padding: 32px 20px 16px 20px;
text-align: center;
background: #fff;
border-radius: 18px 18px 0 0;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.survey-title {
font-size: 22px;
font-weight: 700;
color: #2c3e50;
margin-bottom: 10px;
}
.survey-description {
font-size: 15px;
color: #6c757d;
margin-bottom: 10px;
}
.survey-meta {
display: flex;
justify-content: center;
gap: 16px;
flex-wrap: wrap;
font-size: 12px;
color: #888;
}
.meta-item {
display: flex;
align-items: center;
gap: 4px;
}
.survey-form {
padding: 18px 16px 0 16px;
}
.question-item {
margin-bottom: 28px;
padding-bottom: 18px;
border-bottom: 1px solid #f0f0f0;
}
.question-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
}
.question-number {
background: #3498db;
color: #fff;
width: 22px;
height: 22px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 13px;
font-weight: 600;
flex-shrink: 0;
}
.question-title {
font-size: 15px;
font-weight: 600;
color: #2c3e50;
flex: 1;
}
.required-mark {
color: #e74c3c;
margin-left: 4px;
font-size: 15px;
}
.question-content {
margin-left: 0;
margin-top: 2px;
}
.mobile-radio-group,
.mobile-checkbox-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.mobile-radio,
.mobile-checkbox {
font-size: 15px;
height: 32px;
line-height: 32px;
}
.mobile-textarea {
font-size: 15px;
border-radius: 10px;
}
.mobile-rate {
font-size: 22px;
}
.mobile-input,
.mobile-select {
font-size: 15px;
border-radius: 10px;
width: 100%;
}
.scale-container {
display: flex;
align-items: center;
gap: 8px;
margin-top: 8px;
}
.scale-label {
font-size: 13px;
color: #888;
}
.mobile-slider {
flex: 1;
}
.mobile-btn {
width: 90%;
height: 44px;
font-size: 17px;
border-radius: 22px;
margin-bottom: 10px;
}
@media (max-width: 500px) {
.survey-mobile-card {
max-width: 100vw;
min-width: 0;
border-radius: 0;
}
}
</style>

@ -0,0 +1,465 @@
<template>
<div>
<el-dialog title="创建报名表单" :show-close="false" :visible.sync="dialogVisible" :fullscreen="true" :modal='false'>
<template v-slot:title>
<div style="display: flex;align-items: center;justify-content: space-between;font-size: 26px;">
<div style="display: flex;align-items: center;">
<div style="width:300px">创建问题</div>
<!-- <el-select v-model="hasCourseId" @change="changeCourse" placeholder="请选择课程表单" clearable
style="width: 100%;">
<el-option v-for="item in hasCourseList" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select> -->
</div>
<div style="cursor: pointer;">
<Button type="primary" @click="dialogVisible = false">完成并关闭</Button>
</div>
</div>
</template>
<div style="height: 100%">
<Split v-model="splitL" :min="0.05">
<template #left>
<draggable v-model="types" :sort="false" :group="{ name: 'items', pull: 'clone', put: false }">
<div class="type-item" v-for="item in types">
{{ item.name }}
</div>
</draggable>
</template>
<template #right>
<Split v-model="splitR" :min="0.2">
<template #left>
<div style="height: 100%">
<el-dialog id="template-dialog" :title="'模板表单'" :show-close="false"
style="position: relative; inset: 0 0 0 0; height: 100%" width="80%" top="40px" :visible="true"
:modal="false" :append-to-body="false" :modal-append-to-body="false">
<el-form style="min-height: 500px;display: flex;flex-wrap: wrap;padding: 0 4%;" label-width="80px"
label-position="top" size="small">
<!-- <el-divider>表单字段</el-divider> -->
<!-- <el-form-item :label="i.name" :required="
i.rule ? !!i.rule.includes('required') : false
" v-for="(i, index) in defaultFormList">
<el-input :placeholder="i.help?i.help:'表单字段'" :disabled="true"></el-input>
</el-form-item> -->
<draggable :value="formList" group="items" @change="changeHandler" style="width:100%">
<el-form-item style="flex-basis: 100%;" :label="i.name || '字段名称'" :required="
i.rule ? !!i.rule.includes('required') : false
" v-for="(i, index) in formList" @click.native="selectPick(i, index)">
<formSlotRender :config="i" :index="index"></formSlotRender>
</el-form-item>
</draggable>
<!-- <el-divider>选填字段</el-divider> -->
<!-- <el-form-item style="flex-basis: 100%;" :label="i.name" :required="
i.rule ? !!i.rule.includes('required') : false
" v-for="(i, index) in selectFormList"
@click.native="selectPick(i, index)">
<el-input style="width:100%;" :placeholder="i.help?i.help:'表单字段'"></el-input>
<el-switch @change="(e)=>{changeSelect(e,i)}" v-model="i.is_open" active-color="#0077CC"
inactive-color="#ccc">
</el-switch>
</el-form-item> -->
<!-- <el-divider>自定义字段</el-divider> -->
</el-form>
<div slot="footer">
<!-- <div>
<el-button type="primary"> </el-button>
</div> -->
</div>
</el-dialog>
</div>
</template>
<template #right>
<editPane ref="editPane" @refreshFormItem="refreshItem" :rules="rules" :types="types"
@refresh="getFormList"></editPane>
</template>
</Split>
</template>
</Split>
</div>
</el-dialog>
<!-- -->
<el-dialog title="提示" :visible.sync="showTips" width="30%">
<div style="padding: 30px;font-size: 24px;text-indent: 2em;">是否确认克隆<span
style="color:red">{{hasCourseName}}</span>的报名表单克隆后将<span style="color:red">覆盖原有课程的报名表单</span>请谨慎操作</div>
<span slot="footer" class="dialog-footer">
<el-button @click="showTips = false"> </el-button>
<el-button type="primary" @click="copyForms"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import formMixins from "@/mixin/formMixin.js";
import {
config
} from "@/api/system/customFormField";
import {
index,
save,
destroy,
} from "@/api/survey/index.js";
import {
index as courseIndex,
show as courseShow
} from "@/api/course/index.js"
import {
deepCopy
} from "@/utils";
import editPane from "../components/editPane.vue";
import formSlotRender from "@/views/system/components/formSlotRender.vue";
export default {
mixins: [formMixins],
components: {
formSlotRender,
editPane
},
data() {
return {
course_id: "",
questionLinkIds:{},
course_signs_count: 0,
select: {
page: 1,
page_size: 999,
sort_name: 'sort',
sort_type: 'ASC'
},
dialogVisible: false,
showTips: false,
hasCourseId: '',
hasCourseName: '',
hasCourseList: [],
formList: [],
types: [],
rules: {},
//
splitL: 0.13,
splitR: 0.7,
selectedForm: {},
selectedIndex: 0,
}
},
created() {
this.getConfig()
// this.getCourseList()
},
methods: {
//
async getCourseList() {
const res = await courseIndex({
page: 1,
page_size: 999,
has_course_forms: 1,
sort_name: 'id',
sort_type: 'ASC',
})
this.hasCourseList = res.data.filter((item) => {
return item.id != this.course_id
})
},
changeCourse(e) {
console.log("course-e", e)
if (e) {
this.getDetail(e)
}
},
getDetail(e) {
courseShow({
id: this.course_id,
}).then(res => {
// this.course_signs_count = res.course_signs_count
if (this.course_signs_count > 0) {
this.$message({
type: 'warning',
duration: 4000,
message: '当前课程已有人员报名,不可进行报名表单克隆操作'
})
// this.$Message.warning("")
return
}
this.hasCourseList.map(item => {
if (item.id === e) {
this.hasCourseId = e
this.hasCourseName = item.name
this.showTips = true
}
})
})
},
//
copyForms() {
cloneForm({
from_course_id: this.hasCourseId,
to_course_id: this.course_id
}).then(res => {
this.$Message.success('克隆成功')
this.showTips = false
this.getFormList()
})
},
// formitem
async getConfig() {
const {
edit_to_migration,
validation_rules
} = await config(false);
this.rules = validation_rules
// item.edit_input !== 'textarea' &&
this.types = edit_to_migration.filter(item => item.edit_input !==
"richtext" &&
item.edit_input !== "file" && item.edit_input !== "files" && item.edit_input !== "map");
},
//
async getFormList() {
const res = await index({
filter: [{
key: 'course_content_evaluation_id',
op: 'eq',
value: this.questionLinkIds.course_content_evaluation_id
}],
...this.select
})
this.formList = res.data
// this.updateObjects(this.selectFormList, this.formList)
// let _arr = this.removeMatchingItems(this.selectFormList, this.formList)
// this.formList.push(..._arr)
// this.selectFormList = _arr
// console.log("_arr",_arr)
console.log("this.selectFormList", this.selectFormList, this.formList)
},
updateObjects(a, b) {
//
const updatedB = b.filter(objB => {
// afield
const match = a.find(objA => objA.field === objB.field);
if (match) {
// a
Object.assign(match, objB);
match.is_open = true
return false; //
} else {
return true; //
}
});
// b
b.length = 0; //
b.push(...updatedB); //
},
removeMatchingItems(a, b) {
// Set b filed
const bFiledValues = new Set(b.map(item => item.field));
// 使 filter a
return a.filter(item => !bFiledValues.has(item.field));
},
//
changeHandler(e) {
if (e.added) {
const {
element,
newIndex
} = e.added;
const originalData = element;
const newItem = {
id: "",
field: "",
name: "",
edit_input: originalData.edit_input,
sort: newIndex,
help: "",
rule: "",
allow_input:0,
select_item: [],
...this.questionLinkIds
};
this.formList.splice(newIndex, 0, newItem)
}
if (e.moved) {
const {
element,
newIndex,
oldIndex
} = e.moved;
element.sort = newIndex;
console.log(element)
if (element.id) {
this.saveSelect(element)
} else {
this.formList.splice(oldIndex, 1);
this.formList.splice(newIndex, 0, element);
}
// if (element) {
// }
}
},
//
selectPick(i, index) {
this.$refs.editPane.selectedIndex = index
this.$refs.editPane.selectedForm = deepCopy(i)
this.$refs.editPane.formList = deepCopy(this.formList)
this.$refs.editPane.showForm = true
},
// item
refreshItem(e, index) {
// id
if (e === 'save' || e === 'DelId') {
this.getFormList()
} else if (e === 'Del') {
this.formList.splice(index, 1)
}
},
//
changeSelect(e, item) {
console.log(e, item)
if (e) {
// item.is_open = true
item.need_fill = 1
item.course_id = this.course_id
this.saveSelect(item)
} else {
// item.is_open = false
item.need_fill = 0
this.delSelect(item)
}
},
//
saveSelect(item) {
save(item).then(res => {
this.$Message.success('启用成功')
this.getFormList()
})
},
delSelect(item) {
destroy({
id: item.id
}).then(res => {
item.id = ''
// console.log("del",item)
this.getFormList()
this.$Message.success('禁用成功')
})
}
},
watch: {
dialogVisible(newval) {
if (newval) {
this.getFormList()
} else {
this.course_id = ''
this.course_signs_count = 0
this.hasCourseId = ''
this.hasCourseName = ''
this.selectFormList.map(item => {
item.is_open = false
if (item.id) {
item.id = ''
}
if (item.course_id) {
item.course_id = ''
}
})
this.formList = []
// this.types = []
// this.rules = {}
this.$refs.editPane.showForm = false
this.selectedForm = {}
this.selectedIndex = 0
this.$emit("refresh")
}
}
},
}
</script>
<style scoped lang="scss">
.title {
font-weight: 600;
letter-spacing: 2px;
padding: 0 20px;
}
.type-item {
min-width: 40px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
cursor: pointer;
color: $primaryColor;
border: solid 1px $primaryColor;
border-radius: 10px;
position: relative;
transition: all 0.2s ease-out;
padding: 10px;
margin: 6px;
&:hover {
color: #fff;
background: $primaryColor;
}
}
::v-deep .el-dialog__headerbtn {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
::v-deep .el-dialog__header {
padding: 10px !important;
box-shadow: 0 2px 10px 2px rgba(0, 0, 0, 0.2);
position: relative;
}
::v-deep .el-dialog__body {
height: calc(100% - 62px);
padding: 8px 0 !important;
}
#template-dialog {
::v-deep .el-dialog__header {
box-shadow: none;
padding: 20px;
position: relative;
}
::v-deep .el-dialog__body {
padding: 30px 20px !important;
}
}
::v-deep .ivu-split-trigger-vertical {
width: 2px;
}
::v-deep .ivu-split-trigger-bar-con.vertical {
display: none;
}
::v-deep .ivu-split-horizontal>.ivu-split-trigger-con {
width: 2px;
}
::v-deep .ivu-split-pane {
height: 100%;
overflow: scroll;
&::-webkit-scrollbar {
width: 2px;
}
}
</style>

@ -0,0 +1,298 @@
<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 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="是否可自行填写">
<el-radio-group v-model="selectedForm.allow_input">
<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: [], //
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);
}
},
//
changeIsUser(e) {
console.log("e", e)
console.log(this.selectedForm)
if(e){
this.selectFormList.map(item => {
if (e === item.belong_user_table) {
this.selectedForm.name = item.name
this.selectedForm.field = item.field
this.selectedForm.help = item.help
this.selectedForm.select_item = item.select_item
this.selectedForm.rule = item.rule
this.selectedForm.belong_user = item.belong_user
this.selectedForm.edit_input = item.edit_input
this.selectItem = item.select_item
this.validation = []
if(!this.base.isNull(this.selectedForm.rule)){
this.validation = this.selectedForm.rule.split(",")
}
console.log("2", this.selectedForm,this.validation)
}
})
}else{
this.selectedForm.belong_user = 0
}
},
saveField() {
console.log("this.selectedForm.select_item",this.selectedForm)
if(this.base.isNull(this.selectedForm.name)){
this.$Message.warning("字段名称不能为空")
return
}
if(this.base.isNull(this.selectedForm.field)){
this.$Message.warning("字段标识不能为空")
return
}
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
}else{
//
let count = 0
this.selectedForm.select_item.map(item=>{
if(this.base.isNull(item.value) || this.base.isNull(item.key)){
count++
// item.value = item.key
}
})
if(count>0){
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) {
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(',')
}
}
},
showForm(newval){
if(!newval){
this.selectedForm = {}
this.selectedIndex = 0
}
}
},
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;
}
}
}
}
</style>

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save