<template>
|
<div class="questionnaire" :class="'survey-type-' + surveyType">
|
<!-- 加载状态 -->
|
<div v-if="loading" class="loading-state">
|
<div class="loading-spinner">
|
<i class="el-icon-loading"></i>
|
<p>问卷加载中...</p>
|
</div>
|
</div>
|
|
<!-- 无数据状态 -->
|
<div v-else-if="isEmptyData" class="empty-state">
|
<div class="empty-content">
|
<i class="el-icon-document" style="font-size: 64px; color: #909399"></i>
|
<h3>暂无问卷数据</h3>
|
<p>当前没有可用的问卷,请联系管理员或稍后重试</p>
|
<el-button
|
type="primary"
|
@click="loadSurveyData"
|
icon="el-icon-refresh"
|
>
|
重新加载
|
</el-button>
|
</div>
|
</div>
|
|
<!-- 错误状态 -->
|
<div v-else-if="hasError" class="error-state">
|
<div class="error-content">
|
<i class="el-icon-warning" style="font-size: 64px; color: #f56c6c"></i>
|
<h3>数据加载失败</h3>
|
<p>{{ errorMessage }}</p>
|
<el-button
|
type="primary"
|
@click="loadSurveyData"
|
icon="el-icon-refresh"
|
>
|
重新尝试
|
</el-button>
|
</div>
|
</div>
|
<div class="CONTENT" v-if="!accomplish">
|
<div class="preview-left">
|
<div class="toptitle">
|
<div class="title">{{ surveyTitle }}</div>
|
<div style="font-size: 22px; margin-bottom: 20px; line-height: 1.5">
|
{{ surveyDescription }}
|
</div>
|
</div>
|
<div v-if="showDeptSelect" class="dept-select-container">
|
<el-form>
|
<el-form>
|
<el-form-item label="选择科室">
|
<el-select
|
v-model="selectedDept"
|
filterable
|
clearable
|
placeholder="请选择科室或输入关键词搜索"
|
@change="handleDeptChange"
|
popper-class="dept-select-dropdown"
|
>
|
<el-option
|
v-for="dept in filteredDeptList"
|
:key="dept.code"
|
:label="`${dept.name} (${dept.code})`"
|
:value="dept.name"
|
>
|
<span>{{ dept.name }}</span>
|
</el-option>
|
</el-select>
|
</el-form-item>
|
</el-form>
|
</el-form>
|
</div>
|
<el-divider></el-divider>
|
|
<!-- 单选题 -->
|
<div
|
class="topic-dev"
|
v-for="(item, index) in questionList"
|
:key="item.scriptId"
|
>
|
<div class="scriptTopic-dev" :key="index" v-if="item.scriptType == 1">
|
<div class="dev-text">
|
{{ index + 1 }}、<span style="line-height: 1.5"
|
>{{ item.scriptContent }}
|
<span style="color: #3ba2f7">[单选]</span></span
|
>
|
</div>
|
<div class="dev-xx">
|
<el-radio-group
|
class="custom-radio"
|
v-model="item.scriptResult"
|
@change="handleOptionChange($event, index, item)"
|
>
|
<el-radio
|
border
|
v-for="(option, optIndex) in item.svyLibTemplateTargetoptions"
|
:class="
|
option.isabnormal &&
|
item.scriptResult == option.optioncontent
|
? 'red-star'
|
: ''
|
"
|
:key="optIndex"
|
:label="option.optioncontent"
|
>{{ option.optioncontent }}</el-radio
|
>
|
</el-radio-group>
|
</div>
|
<div v-show="item.prompt">
|
<el-alert :title="item.prompt" type="warning"> </el-alert>
|
</div>
|
</div>
|
|
<!-- 多选题 -->
|
<div class="scriptTopic-dev" :key="index" v-if="item.scriptType == 2">
|
<div class="dev-text">
|
{{ index + 1 }}、<span style="line-height: 1.5"
|
>{{ item.scriptContent }}
|
<span style="color: #3ba2f7">[多选]</span></span
|
>
|
</div>
|
<div class="dev-xx">
|
<el-checkbox-group
|
class="custom-radio"
|
v-model="item.scriptResult"
|
>
|
<el-checkbox
|
border
|
@change="$forceUpdate()"
|
v-for="(option, optIndex) in item.svyLibTemplateTargetoptions"
|
:key="optIndex"
|
:label="option.optioncontent"
|
>
|
{{ option.optioncontent }}
|
</el-checkbox>
|
</el-checkbox-group>
|
</div>
|
<div v-show="item.prompt && item.scriptResult[0]">
|
<el-alert :title="item.prompt" type="warning"> </el-alert>
|
</div>
|
</div>
|
|
<!-- 填空题 -->
|
<div class="scriptTopic-dev" :key="index" v-if="item.scriptType == 4">
|
<div class="dev-text">
|
{{ index + 1 }}、<span style="line-height: 1.5"
|
>{{ item.scriptContent
|
}}<span style="color: #3ba2f7">[问答]</span></span
|
>
|
</div>
|
<div class="dev-xx">
|
<el-input
|
type="textarea"
|
:rows="3"
|
placeholder="请输入"
|
v-model="item.scriptResult"
|
clearable
|
>
|
</el-input>
|
</div>
|
</div>
|
</div>
|
|
<div class="bottom-fixed">
|
<el-button
|
type="primary"
|
style="width: 80%; font-size: 20px"
|
@click="submitSurvey"
|
>提交问卷</el-button
|
>
|
</div>
|
</div>
|
</div>
|
|
<div class="CONTENT" v-else>
|
<div class="preview-lefts">
|
<div class="completion-message">
|
<div class="thank-you">{{ this.accomplish || "感谢您的配合!" }}</div>
|
<div class="feedback-message">{{ completionMessage }}</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
import {
|
getScriptByCondition,
|
saveMYDQuestionAnswer,
|
WLgetDept,
|
} from "@/api/AiCentre/index";
|
|
export default {
|
data() {
|
return {
|
surveyType: null, // 'outpatient', 'inpatient'
|
surveyTitle: "",
|
surveyDescription: "",
|
questionList: [],
|
deptList: [],
|
completionMessage: "",
|
accomplish: false,
|
showDeptSelect: false,
|
selectedDept: null,
|
deptSearchText: "", // 保留用于本地过滤
|
deptList: [],
|
filteredDeptList: [],
|
|
// 加密后的参数
|
encryptedParams: {
|
param1: "",
|
param2: "",
|
param3: "",
|
param4: "",
|
param5: "",
|
param6: "30001002",
|
},
|
isEmptyData: false, // 新增:无数据状态
|
hasError: false, // 新增:错误状态
|
loading: false, // 新增:加载状态
|
errorMessage: "", // 新增:错误信息
|
// 测试数据
|
testData: {
|
1: {
|
title: "门诊满意度调查",
|
description:
|
"亲爱的患者,感谢您选择我们的医疗服务。为了不断提升服务质量,请您花几分钟时间填写此问卷。",
|
questions: [
|
{
|
scriptId: 1,
|
scriptType: 1,
|
scriptContent: "您对门诊医生的诊疗水平是否满意?",
|
scriptResult: null,
|
svyLibTemplateTargetoptions: [
|
{ optioncontent: "非常满意", value: "5", isabnormal: false },
|
{ optioncontent: "满意", value: "4", isabnormal: false },
|
{ optioncontent: "一般", value: "3", isabnormal: true },
|
{ optioncontent: "不满意", value: "2", isabnormal: true },
|
{ optioncontent: "非常不满意", value: "1", isabnormal: true },
|
],
|
},
|
{
|
scriptId: 2,
|
scriptType: 1,
|
scriptContent: "您对门诊护士的服务态度是否满意?",
|
scriptResult: null,
|
svyLibTemplateTargetoptions: [
|
{ optioncontent: "非常满意", value: "5", isabnormal: false },
|
{ optioncontent: "满意", value: "4", isabnormal: false },
|
{ optioncontent: "一般", value: "3", isabnormal: true },
|
{ optioncontent: "不满意", value: "2", isabnormal: true },
|
{ optioncontent: "非常不满意", value: "1", isabnormal: true },
|
],
|
},
|
{
|
scriptId: 3,
|
scriptType: 2,
|
scriptContent: "您认为门诊哪些方面需要改进?(可多选)",
|
scriptResult: [],
|
svyLibTemplateTargetoptions: [
|
{
|
optioncontent: "排队等候时间",
|
value: "waiting_time",
|
isabnormal: false,
|
},
|
{
|
optioncontent: "医生沟通方式",
|
value: "communication",
|
isabnormal: false,
|
},
|
{
|
optioncontent: "就诊环境",
|
value: "environment",
|
isabnormal: false,
|
},
|
{
|
optioncontent: "医疗设备",
|
value: "equipment",
|
isabnormal: false,
|
},
|
{ optioncontent: "其他", value: "other", isabnormal: false },
|
],
|
},
|
{
|
scriptId: 4,
|
scriptType: 4,
|
scriptContent: "您对门诊服务还有什么其他建议?",
|
scriptResult: null,
|
},
|
],
|
completionMessage:
|
"感谢您宝贵的意见!我们将不断改进门诊服务质量,为您提供更好的医疗服务体验。",
|
},
|
2: {
|
title: "住院满意度调查",
|
description:
|
"亲爱的患者及家属,感谢您选择在我院住院治疗。为了提升住院服务质量,请您填写此问卷。",
|
questions: [
|
{
|
scriptId: 1,
|
scriptType: 1,
|
scriptContent: "您对住院期间医生的诊疗水平是否满意?",
|
scriptResult: null,
|
svyLibTemplateTargetoptions: [
|
{ optioncontent: "非常满意", value: "5", isabnormal: false },
|
{ optioncontent: "满意", value: "4", isabnormal: false },
|
{ optioncontent: "一般", value: "3", isabnormal: true },
|
{ optioncontent: "不满意", value: "2", isabnormal: true },
|
{ optioncontent: "非常不满意", value: "1", isabnormal: true },
|
],
|
},
|
{
|
scriptId: 2,
|
scriptType: 1,
|
scriptContent: "您对住院期间护士的护理服务是否满意?",
|
scriptResult: null,
|
svyLibTemplateTargetoptions: [
|
{ optioncontent: "非常满意", value: "5", isabnormal: false },
|
{ optioncontent: "满意", value: "4", isabnormal: false },
|
{ optioncontent: "一般", value: "3", isabnormal: true },
|
{ optioncontent: "不满意", value: "2", isabnormal: true },
|
{ optioncontent: "非常不满意", value: "1", isabnormal: true },
|
],
|
},
|
{
|
scriptId: 3,
|
scriptType: 1,
|
scriptContent: "您对住院病房的环境和卫生是否满意?",
|
scriptResult: null,
|
svyLibTemplateTargetoptions: [
|
{ optioncontent: "非常满意", value: "5", isabnormal: false },
|
{ optioncontent: "满意", value: "4", isabnormal: false },
|
{ optioncontent: "一般", value: "3", isabnormal: true },
|
{ optioncontent: "不满意", value: "2", isabnormal: true },
|
{ optioncontent: "非常不满意", value: "1", isabnormal: true },
|
],
|
},
|
{
|
scriptId: 4,
|
scriptType: 4,
|
scriptContent: "您对住院服务还有什么其他建议?",
|
scriptResult: null,
|
},
|
],
|
completionMessage:
|
"感谢您对我们工作的支持!我们将根据您的反馈持续改进住院服务质量,祝您早日康复!",
|
},
|
},
|
};
|
},
|
created() {
|
this.initSurveyData();
|
},
|
methods: {
|
// 初始化调查数据
|
initSurveyData() {
|
// 从路由参数获取加密后的参数
|
this.encryptedParams.param1 =
|
this.$route.query.param1 ||
|
"WOAq2QZd43E-qg-96SvuIFsn-sdRVxQNH4M82XhpXp_Ux4PFrPaqSFXcKaeA6oxEgNhPisA86LvU9kTAEz4xvQ==";
|
this.encryptedParams.param2 =
|
this.$route.query.param2 ||
|
"XWeBh42RLYlNsMcomgw9UXhUPySkRP5EneWSueSq8F84qwYznU9heXuSx4tUMUtDvRnuJ86moJivy-kWQX12Rg==";
|
this.encryptedParams.param5 = this.$route.query.param3 || "2"; // 1=住院, 2=门诊, 3=投诉建议
|
this.encryptedParams.param6 = this.$route.query.param4 || "30001002";
|
|
this.surveyType = parseInt(this.encryptedParams.param5) || 2;
|
|
// 加载问卷数据
|
this.loadSurveyData();
|
// 获取科室列表
|
this.WLgetDept();
|
},
|
WLgetDept() {
|
// 调用接口获取科室数据
|
WLgetDept(this.encryptedParams.param6).then((res) => {
|
this.deptList = Object.entries(res.data).map(([code, name]) => ({
|
code,
|
name,
|
}));
|
this.filteredDeptList = [...this.deptList];
|
|
if (this.surveyType === 3) {
|
this.showDeptSelect = true;
|
}
|
});
|
},
|
filterDeptList() {
|
if (!this.deptSearchText) {
|
this.filteredDeptList = [...this.deptList];
|
return;
|
}
|
|
const searchText = this.deptSearchText.toLowerCase();
|
this.filteredDeptList = this.deptList.filter(
|
(dept) =>
|
dept.name.toLowerCase().includes(searchText) ||
|
dept.code.toLowerCase().includes(searchText)
|
);
|
},
|
// 加载调查数据
|
loadSurveyData() {
|
this.loading = true;
|
this.isEmptyData = false;
|
this.hasError = false;
|
this.errorMessage = "";
|
// 调用接口获取问卷数据
|
// 根据问卷类型设置不同的参数
|
let encryptedParams = {
|
param1: this.encryptedParams.param1,
|
};
|
|
// 根据surveyType设置不同的参数
|
switch (this.surveyType) {
|
case 1: // 住院
|
encryptedParams.param2 = this.encryptedParams.param2;
|
break;
|
case 2: // 门诊
|
encryptedParams.param3 = this.encryptedParams.param2;
|
break;
|
case 3: // 投诉建议
|
encryptedParams.param4 = this.encryptedParams.param2;
|
break;
|
default:
|
encryptedParams.param3 = this.encryptedParams.param2;
|
}
|
getScriptByCondition(encryptedParams)
|
.then((res) => {
|
if (res.code === 200) {
|
if (res.data.result) {
|
this.accomplish = res.data.result;
|
return;
|
}
|
if (
|
!res.data.svyLibTemplateScriptVOS ||
|
res.data.svyLibTemplateScriptVOS.length === 0
|
) {
|
this.isEmptyData = true;
|
this.$message.warning("暂无问卷数据");
|
return;
|
}
|
// 处理接口返回的数据
|
this.questionList = res.data.svyLibTemplateScriptVOS.map((item) => {
|
return {
|
...item,
|
scriptResult: item.scriptType === 2 ? [] : null,
|
};
|
});
|
|
// 根据surveyType设置标题和描述
|
switch (this.surveyType) {
|
case 2: // 门诊
|
this.surveyTitle = "门诊满意度调查";
|
this.surveyDescription =
|
"亲爱的患者,感谢您选择我们的医疗服务。为了不断提升服务质量,请您花几分钟时间填写此问卷。";
|
this.completionMessage =
|
"感谢您宝贵的意见!我们将不断改进门诊服务质量,为您提供更好的医疗服务体验。";
|
break;
|
case 1: // 住院
|
this.surveyTitle = "住院满意度调查";
|
this.surveyDescription =
|
"亲爱的患者及家属,感谢您选择在我院住院治疗。为了提升住院服务质量,请您填写此问卷。";
|
this.completionMessage =
|
"感谢您对我们工作的支持!我们将根据您的反馈持续改进住院服务质量,祝您早日康复!";
|
break;
|
case 3: // 投诉建议
|
this.surveyTitle = "投诉建议反馈";
|
this.surveyDescription =
|
"尊敬的客户,感谢您抽出宝贵时间提供反馈。您的意见对我们改进服务非常重要。";
|
this.completionMessage =
|
"感谢您的反馈!我们已收到您的投诉/建议,将尽快处理并与您联系。";
|
break;
|
default:
|
this.useTestData(1); // 默认使用门诊数据
|
}
|
} else {
|
// 接口无数据或失败,使用测试数据
|
// this.useTestData(this.surveyType);
|
}
|
})
|
.catch(() => {
|
console.error("数据获取失败:", error);
|
this.hasError = true;
|
this.errorMessage =
|
error.message || "问卷数据加载失败,请检查网络连接后重试";
|
this.$message.error("数据加载失败");
|
// 接口调用失败,使用测试数据
|
// this.useTestData(this.surveyType);
|
})
|
.finally(() => {
|
this.loading = false;
|
});
|
},
|
handleDeptChange(value) {
|
this.selectedDept = value;
|
// 可以在这里添加其他处理逻辑
|
},
|
// 使用测试数据
|
useTestData(surveyType) {
|
const type = [1, 2, 3].includes(surveyType) ? surveyType : 1;
|
const testData = this.testData[type];
|
|
this.surveyTitle = testData.title;
|
this.surveyDescription = testData.description;
|
this.questionList = testData.questions;
|
this.completionMessage = testData.completionMessage;
|
},
|
|
// 提交调查问卷(直接提交,不经过缓存)
|
async submitSurvey() {
|
// 验证必填项
|
if (this.hasUnansweredRequiredQuestions()) {
|
this.$message.error("请完成所有必填问题后再提交");
|
return;
|
}
|
|
try {
|
const submitData = this.prepareSubmitData();
|
const res = await saveMYDQuestionAnswer(submitData);
|
|
if (res.code === 200) {
|
this.accomplish = "问卷已提交";
|
this.$message.success("提交成功!感谢您的反馈。");
|
} else {
|
this.$message.error(res.msg || "提交失败,请稍后再试");
|
}
|
} catch (error) {
|
this.$message.error("网络错误,提交失败");
|
console.error("提交失败:", error);
|
}
|
},
|
|
// 检查是否有未回答的必填问题
|
hasUnansweredRequiredQuestions() {
|
return this.questionList.some((question) => {
|
return (
|
question.required &&
|
(question.scriptResult === null ||
|
question.scriptResult === "" ||
|
(Array.isArray(question.scriptResult) &&
|
question.scriptResult.length === 0))
|
);
|
});
|
},
|
|
// 准备提交数据
|
prepareSubmitData() {
|
// 创建科室选择问题对象
|
const deptQuestion = {
|
scriptId: "dept_selection", // 自定义ID
|
scriptType: 4, // 4表示问答类型
|
scriptContent: "选择的科室",
|
scriptResult: this.selectedDept || "", // 存储选择的科室名称
|
required: false, // 非必填
|
sort: 999,
|
nextScriptno: "1",
|
};
|
|
return {
|
taskId: this.encryptedParams.param1,
|
serialnum: this.encryptedParams.param2 || this.encryptedParams.param3,
|
mzzy: this.surveyType,
|
svyLibTemplateScriptVOS: [
|
deptQuestion, // 将科室选择作为第一个问题
|
...this.questionList.map((item) => {
|
return {
|
scriptId: item.scriptId,
|
scriptType: item.scriptType,
|
scriptResult:
|
item.scriptType === 2
|
? (item.scriptResult || []).join("&")
|
: item.scriptResult || "",
|
nextScriptno: item.nextScriptno,
|
score: item.score,
|
prompt: item.prompt,
|
...item,
|
};
|
}),
|
],
|
excep: this.checkAbnormalOptions() ? 1 : 0,
|
};
|
},
|
|
// 检查异常选项
|
checkAbnormalOptions() {
|
return this.questionList.some((question) => {
|
if (!question.scriptResult) return false;
|
|
if (question.scriptType === 1) {
|
// 单选题异常检查
|
const selectedOption = question.svyLibTemplateTargetoptions.find(
|
(opt) => opt.optioncontent === question.scriptResult
|
);
|
return selectedOption?.isabnormal;
|
} else if (question.scriptType === 2) {
|
// 多选题异常检查
|
return question.scriptResult.some((answer) => {
|
const option = question.svyLibTemplateTargetoptions.find(
|
(opt) => opt.optioncontent === answer
|
);
|
return option?.isabnormal;
|
});
|
}
|
return false;
|
});
|
},
|
|
// 处理单选选项变化
|
handleOptionChange(selectedValue, index, question) {
|
const selectedOption = question.svyLibTemplateTargetoptions.find(
|
(option) => option.optioncontent === selectedValue
|
);
|
|
if (selectedOption) {
|
this.questionList[index].nextScriptno = selectedOption.nextQuestion;
|
this.questionList[index].score = selectedOption.score;
|
this.questionList[index].prompt = selectedOption.prompt;
|
}
|
},
|
},
|
};
|
</script>
|
|
<style lang="scss" scoped>
|
/* 基础样式变量 */
|
:root {
|
--primary-color: #1a73e8; /* 默认蓝色 */
|
--secondary-color: #34d399; /* 默认绿色 */
|
--alert-color: #ed8936; /* 默认橙色 */
|
}
|
|
/* 门诊样式变量 */
|
.survey-type-1 {
|
--primary-color: #1a73e8; /* 医疗蓝 */
|
--secondary-color: #34d399; /* 健康绿 */
|
--alert-color: #ed8936; /* 警示橙 */
|
}
|
|
/* 住院样式变量 */
|
.survey-type-2 {
|
--primary-color: #5a67d8; /* 深蓝紫 */
|
--secondary-color: #667eea; /* 浅蓝紫 */
|
--alert-color: #f56565; /* 警示红 */
|
}
|
|
/* 投诉建议样式变量 */
|
.survey-type-3 {
|
--primary-color: #e53e3e; /* 紧急红 */
|
--secondary-color: #f6ad55; /* 警示黄 */
|
--alert-color: #f56565; /* 警示红 */
|
}
|
|
.questionnaire {
|
font-family: "PingFang SC", "Helvetica Neue", Arial, sans-serif;
|
min-height: 100vh;
|
margin: 0;
|
padding: 0;
|
color: #333;
|
transition: all 0.3s ease;
|
|
/* 根据surveyType应用不同的主题 */
|
&.survey-type-1 {
|
background-color: #f5f9fc;
|
--theme-gradient: linear-gradient(135deg, #1a73e8, #34d399);
|
}
|
|
&.survey-type-2 {
|
background-color: #f8f9ff;
|
--theme-gradient: linear-gradient(135deg, #5a67d8, #667eea);
|
}
|
|
&.survey-type-3 {
|
background-color: #fff5f5;
|
--theme-gradient: linear-gradient(135deg, #e53e3e, #f6ad55);
|
}
|
}
|
.dept-select-container {
|
margin: 20px 0;
|
padding: 20px;
|
background-color: #f8fafc;
|
border-radius: 8px;
|
border: 1px solid #e2e8f0;
|
}
|
|
/* 调整下拉选项样式 */
|
.el-select-dropdown__item {
|
display: flex;
|
justify-content: space-between;
|
}
|
/* 下拉框样式调整 */
|
::v-deep .dept-select-dropdown {
|
max-height: 400px; /* 限制最大高度 */
|
overflow-y: auto; /* 添加滚动条 */
|
|
.el-select-dropdown__item {
|
display: flex;
|
justify-content: space-between;
|
padding: 0 20px;
|
height: auto;
|
line-height: 36px;
|
|
span {
|
display: inline-block;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
white-space: nowrap;
|
}
|
|
/* 名称部分 */
|
span:first-child {
|
width: 60%;
|
text-align: left;
|
}
|
|
/* 编码部分 */
|
span:last-child {
|
width: 40%;
|
text-align: right;
|
}
|
}
|
}
|
|
/* 移动端适配 */
|
@media (max-width: 768px) {
|
::v-deep .dept-select-dropdown {
|
max-width: 100vw; /* 限制最大宽度为视口宽度 */
|
width: auto !important;
|
left: 10px !important;
|
right: 10px !important;
|
|
.el-select-dropdown__item {
|
span:first-child {
|
width: 50%;
|
}
|
span:last-child {
|
width: 50%;
|
}
|
}
|
}
|
}
|
.CONTENT {
|
max-width: 900px;
|
margin: 0 auto;
|
padding: 20px;
|
|
.title {
|
color: var(--primary-color);
|
font-size: 28px;
|
font-weight: 600;
|
margin-bottom: 15px;
|
text-align: center;
|
letter-spacing: 0.5px;
|
position: relative;
|
|
&::after {
|
content: "";
|
position: absolute;
|
bottom: -8px;
|
left: 50%;
|
transform: translateX(-50%);
|
width: 60px;
|
height: 3px;
|
background: var(--theme-gradient);
|
border-radius: 3px;
|
}
|
}
|
}
|
|
.preview-left {
|
margin: 20px 0;
|
margin-bottom: 100px;
|
background-color: #fff;
|
border-radius: 12px;
|
padding: 30px;
|
border: none;
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
transition: all 0.3s ease;
|
|
&:hover {
|
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1);
|
}
|
|
.toptitle {
|
margin-bottom: 25px;
|
|
div {
|
color: #4a5568;
|
font-size: 18px;
|
line-height: 1.6;
|
text-align: center;
|
}
|
}
|
|
.el-divider {
|
background-color: #e2e8f0;
|
margin: 25px 0;
|
}
|
|
.topic-dev {
|
margin-bottom: 30px;
|
font-size: 17px;
|
background-color: #f8fafc;
|
border-radius: 10px;
|
padding: 20px;
|
transition: all 0.3s ease;
|
position: relative;
|
overflow: hidden;
|
|
&:hover {
|
background-color: #f1f5f9;
|
}
|
|
/* 添加类型标识小标签 */
|
&::before {
|
content: "";
|
position: absolute;
|
top: 0;
|
left: 0;
|
width: 4px;
|
height: 100%;
|
background: var(--primary-color);
|
}
|
|
.dev-text {
|
margin-bottom: 18px;
|
font-weight: 500;
|
color: #2d3748;
|
font-size: 18px;
|
line-height: 1.6;
|
|
span[style*="color: #3ba2f7"] {
|
font-size: 14px;
|
margin-left: 8px;
|
color: var(--primary-color) !important;
|
}
|
}
|
}
|
}
|
|
.preview-lefts {
|
margin: 20px 0;
|
background-color: #fff;
|
border-radius: 12px;
|
padding: 40px;
|
min-height: 400px;
|
border: none;
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
text-align: center;
|
|
.completion-message {
|
padding: 40px;
|
max-width: 600px;
|
|
.thank-you {
|
font-size: 32px;
|
color: var(--primary-color);
|
font-weight: 600;
|
margin-bottom: 25px;
|
position: relative;
|
display: inline-block;
|
|
&::after {
|
content: "";
|
position: absolute;
|
bottom: -10px;
|
left: 50%;
|
transform: translateX(-50%);
|
width: 80px;
|
height: 3px;
|
background: var(--theme-gradient);
|
border-radius: 3px;
|
}
|
}
|
|
.feedback-message {
|
font-size: 20px;
|
line-height: 1.7;
|
color: #4a5568;
|
margin: 0 auto;
|
}
|
}
|
}
|
|
.red-star {
|
::v-deep .el-radio__label,
|
::v-deep .el-checkbox__label {
|
position: relative;
|
padding-right: 20px;
|
|
&::after {
|
content: "*";
|
color: #ef4444;
|
position: absolute;
|
right: 0;
|
top: 0;
|
font-size: 16px;
|
}
|
}
|
}
|
.loading-state {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
min-height: 60vh;
|
padding: 40px;
|
|
.loading-spinner {
|
text-align: center;
|
|
.el-icon-loading {
|
font-size: 48px;
|
color: var(--primary-color);
|
margin-bottom: 16px;
|
animation: rotating 2s linear infinite;
|
}
|
|
p {
|
color: #666;
|
font-size: 16px;
|
}
|
}
|
}
|
|
.empty-state,
|
.error-state {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
min-height: 60vh;
|
padding: 40px;
|
|
.empty-content,
|
.error-content {
|
text-align: center;
|
max-width: 400px;
|
|
h3 {
|
color: #666;
|
font-size: 20px;
|
margin: 16px 0 12px;
|
font-weight: 500;
|
}
|
|
p {
|
color: #999;
|
font-size: 14px;
|
margin-bottom: 24px;
|
line-height: 1.6;
|
}
|
}
|
}
|
|
.error-state {
|
.error-content {
|
h3 {
|
color: #f56c6c;
|
}
|
}
|
}
|
|
@keyframes rotating {
|
from {
|
transform: rotate(0deg);
|
}
|
to {
|
transform: rotate(360deg);
|
}
|
}
|
|
/* 响应式调整 */
|
@media (max-width: 768px) {
|
.loading-state,
|
.empty-state,
|
.error-state {
|
padding: 20px;
|
min-height: 50vh;
|
|
.el-icon-loading,
|
.el-icon-document,
|
.el-icon-warning {
|
font-size: 40px !important;
|
}
|
|
h3 {
|
font-size: 18px !important;
|
}
|
|
p {
|
font-size: 13px !important;
|
}
|
}
|
}
|
::v-deep {
|
.el-checkbox-group {
|
display: flex;
|
flex-direction: column;
|
margin: 15px 0;
|
gap: 12px;
|
}
|
|
.el-radio-group {
|
display: flex;
|
flex-direction: column;
|
margin: 15px 0;
|
gap: 12px;
|
}
|
|
.el-radio.is-bordered,
|
.el-checkbox.is-bordered {
|
width: 100%;
|
margin-right: 0;
|
margin-bottom: 10px;
|
max-width: 400px;
|
padding: 14px 20px 14px 15px;
|
border-radius: 8px;
|
height: auto;
|
min-height: 50px;
|
border: 1px solid #e2e8f0;
|
transition: all 0.3s ease;
|
margin-left: 0 !important;
|
margin-top: 0 !important;
|
.el-radio-group,
|
.el-checkbox-group {
|
align-items: center;
|
}
|
&:hover {
|
border-color: var(--primary-color);
|
box-shadow: 0 2px 8px rgba(var(--primary-color), 0.15);
|
}
|
|
&.is-checked {
|
border-color: var(--primary-color);
|
background-color: rgba(var(--primary-color), 0.05);
|
}
|
}
|
|
.el-radio__label,
|
.el-checkbox__label {
|
font-size: 16px;
|
color: #2d3748;
|
}
|
|
.el-alert--warning.is-light {
|
background-color: #fff8f0;
|
color: var(--alert-color);
|
margin-top: 15px;
|
border-radius: 8px;
|
border-left: 4px solid var(--alert-color);
|
|
.el-alert__title {
|
font-size: 15px;
|
line-height: 1.6;
|
color: var(--alert-color);
|
}
|
|
.el-alert__closebtn {
|
color: var(--alert-color);
|
}
|
}
|
|
.el-textarea__inner {
|
font-size: 16px;
|
border-radius: 8px;
|
border: 1px solid #e2e8f0;
|
padding: 12px 15px;
|
transition: all 0.3s ease;
|
min-height: 100px;
|
|
&:focus {
|
border-color: var(--primary-color);
|
box-shadow: 0 0 0 2px rgba(var(--primary-color), 0.2);
|
}
|
|
&::placeholder {
|
color: #a0aec0;
|
}
|
}
|
|
.el-radio__input.is-checked .el-radio__inner {
|
background-color: var(--primary-color);
|
border-color: var(--primary-color);
|
}
|
|
.el-checkbox__input.is-checked .el-checkbox__inner {
|
background-color: var(--primary-color);
|
border-color: var(--primary-color);
|
}
|
|
.el-radio__inner,
|
.el-checkbox__inner {
|
width: 18px;
|
height: 18px;
|
}
|
}
|
|
.bottom-fixed {
|
position: fixed;
|
bottom: 0;
|
left: 0;
|
width: 100%;
|
text-align: center;
|
padding: 10px 0;
|
background: var(--theme-gradient);
|
box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.1);
|
z-index: 1000;
|
transition: all 0.3s ease;
|
|
.el-button {
|
height: 56px;
|
font-size: 18px;
|
font-weight: 500;
|
letter-spacing: 0.5px;
|
border-radius: 8px;
|
background-color: #fff;
|
color: var(--primary-color);
|
border: none;
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
transition: all 0.3s ease;
|
width: 80%;
|
max-width: 400px;
|
position: relative;
|
overflow: hidden;
|
|
&::before {
|
content: "";
|
position: absolute;
|
top: 0;
|
left: -100%;
|
width: 100%;
|
height: 100%;
|
background: linear-gradient(
|
90deg,
|
transparent,
|
rgba(255, 255, 255, 0.4),
|
transparent
|
);
|
transition: all 0.5s ease;
|
}
|
|
&:hover {
|
transform: translateY(-2px);
|
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
|
|
&::before {
|
left: 100%;
|
}
|
}
|
|
&:active {
|
transform: translateY(0);
|
}
|
}
|
}
|
|
/* 响应式设计 */
|
@media (max-width: 768px) {
|
.questionnaire {
|
.CONTENT {
|
padding: 15px;
|
}
|
|
.preview-left,
|
.preview-lefts {
|
padding: 20px;
|
margin-bottom: 80px;
|
}
|
|
.title {
|
font-size: 24px !important;
|
}
|
|
.dev-text {
|
font-size: 16px !important;
|
}
|
|
.bottom-fixed .el-button {
|
height: 50px;
|
font-size: 16px;
|
width: 90%;
|
}
|
}
|
}
|
</style>
|