<template>
|
<div
|
class="question-ratio"
|
v-loading="loading"
|
element-loading-text="正在汇总随访问卷题目..."
|
>
|
<div class="ratio-header">
|
<div class="ratio-title">问题详情占比</div>
|
<div class="ratio-header-right">
|
<span class="ratio-meta">共 {{ topics.length }} 道题</span>
|
<el-button
|
type="warning"
|
plain
|
size="mini"
|
icon="el-icon-download"
|
:disabled="!topics.length"
|
@click="exportExcel"
|
>导出</el-button
|
>
|
</div>
|
</div>
|
|
<div
|
v-if="!loading && !topics.length"
|
class="ratio-empty"
|
>
|
<el-empty description="暂无问题详情数据"></el-empty>
|
</div>
|
|
<div v-else class="ratio-list">
|
<div
|
v-for="(topic, index) in topics"
|
:key="topic.scriptid || topic.id || index"
|
class="ratio-question"
|
>
|
<div class="ratio-question-title">
|
<span>
|
<span v-if="!hideIndex">第{{ index + 1 }}题:</span
|
>{{ topic.scriptContent }}
|
</span>
|
<span v-if="!hideType" class="ratio-type"
|
>[{{ typeLabel(topic) }}]</span
|
>
|
</div>
|
<el-table :data="topic.details" border size="small">
|
<el-table-column
|
prop="optionText"
|
label="问题选项"
|
min-width="240"
|
show-overflow-tooltip
|
/>
|
<el-table-column
|
prop="chosenQuantity"
|
label="选择人数"
|
width="120"
|
align="center"
|
>
|
<template slot-scope="scope">
|
{{ scope.row.chosenQuantity || 0 }}
|
</template>
|
</el-table-column>
|
<el-table-column label="占比" width="120" align="center">
|
<template slot-scope="scope">
|
{{ formatPercentage(scope.row.chosenPercentage) }}
|
</template>
|
</el-table-column>
|
</el-table>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
import ExcelJS from "exceljs";
|
import { saveAs } from "file-saver";
|
import {
|
formatPercentage,
|
normalizeTopicList,
|
topicTypeLabel,
|
} from "./topicUtils";
|
|
export default {
|
name: "QuestionRatioTable",
|
props: {
|
topicList: {
|
type: [Array, Object],
|
default: () => [],
|
},
|
loading: {
|
type: Boolean,
|
default: false,
|
},
|
hideIndex: {
|
type: Boolean,
|
default: false,
|
},
|
hideType: {
|
type: Boolean,
|
default: false,
|
},
|
},
|
computed: {
|
topics() {
|
return normalizeTopicList(this.topicList);
|
},
|
},
|
methods: {
|
formatPercentage,
|
typeLabel: topicTypeLabel,
|
fileDate() {
|
const now = new Date();
|
const pad = (value) => String(value).padStart(2, "0");
|
return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(
|
now.getDate()
|
)}`;
|
},
|
async exportExcel() {
|
if (!this.topics.length) {
|
this.$message.warning("暂无可导出的数据");
|
return;
|
}
|
|
try {
|
const workbook = new ExcelJS.Workbook();
|
const worksheet = workbook.addWorksheet("问题详情占比");
|
const headers = ["题目", "题型", "问题选项", "选择人数", "占比"];
|
const colCount = headers.length;
|
const border = {
|
top: { style: "thin", color: { argb: "FFD0D0D0" } },
|
left: { style: "thin", color: { argb: "FFD0D0D0" } },
|
bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
|
right: { style: "thin", color: { argb: "FFD0D0D0" } },
|
};
|
|
worksheet.mergeCells(1, 1, 1, colCount);
|
const titleCell = worksheet.getCell(1, 1);
|
titleCell.value = "问题详情占比";
|
titleCell.font = { size: 14, bold: true };
|
titleCell.alignment = { horizontal: "center", vertical: "middle" };
|
worksheet.getRow(1).height = 30;
|
|
worksheet.mergeCells(2, 1, 2, colCount);
|
const metaCell = worksheet.getCell(2, 1);
|
metaCell.value = `共 ${this.topics.length} 道题`;
|
metaCell.alignment = { horizontal: "left", vertical: "middle" };
|
worksheet.getRow(2).height = 22;
|
|
const headerRow = worksheet.addRow(headers);
|
headerRow.height = 24;
|
headerRow.eachCell((cell) => {
|
cell.font = { bold: true };
|
cell.fill = {
|
type: "pattern",
|
pattern: "solid",
|
fgColor: { argb: "FFF5F7FA" },
|
};
|
cell.border = border;
|
cell.alignment = { horizontal: "center", vertical: "middle" };
|
});
|
|
this.topics.forEach((topic) => {
|
const details = topic.details || [];
|
const rows = details.length
|
? details
|
: [{ optionText: "", chosenQuantity: 0, chosenPercentage: "" }];
|
|
rows.forEach((detail) => {
|
const dataRow = worksheet.addRow([
|
topic.scriptContent || "",
|
this.hideType ? "占比统计" : this.typeLabel(topic),
|
detail.optionText || "",
|
Number(detail.chosenQuantity) || 0,
|
detail.optionText
|
? this.formatPercentage(detail.chosenPercentage)
|
: "-",
|
]);
|
dataRow.height = 22;
|
dataRow.eachCell((cell) => {
|
cell.border = border;
|
cell.alignment = {
|
horizontal: "center",
|
vertical: "middle",
|
wrapText: true,
|
};
|
});
|
});
|
});
|
|
worksheet.columns = [
|
{ width: 50 },
|
{ width: 10 },
|
{ width: 30 },
|
{ width: 12 },
|
{ width: 12 },
|
];
|
|
const buffer = await workbook.xlsx.writeBuffer();
|
const blob = new Blob([buffer], {
|
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
});
|
saveAs(blob, `问题详情占比_${this.fileDate()}.xlsx`);
|
this.$message.success("导出成功");
|
} catch (error) {
|
console.error("导出问题详情占比失败:", error);
|
this.$message.error("导出失败");
|
}
|
},
|
},
|
};
|
</script>
|
|
<style lang="scss" scoped>
|
.question-ratio {
|
padding: 4px 0 8px;
|
min-height: 240px;
|
|
.ratio-header {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
margin-bottom: 14px;
|
}
|
|
.ratio-title {
|
font-size: 15px;
|
font-weight: 600;
|
color: #303133;
|
}
|
|
.ratio-meta {
|
font-size: 13px;
|
color: #909399;
|
}
|
|
.ratio-header-right {
|
display: flex;
|
align-items: center;
|
gap: 10px;
|
}
|
|
.ratio-empty {
|
padding: 40px 0;
|
}
|
|
.ratio-list {
|
max-height: 62vh;
|
overflow-y: auto;
|
padding-right: 6px;
|
}
|
|
.ratio-question {
|
margin-bottom: 18px;
|
|
&:last-child {
|
margin-bottom: 0;
|
}
|
}
|
|
.ratio-question-title {
|
display: flex;
|
align-items: center;
|
flex-wrap: wrap;
|
gap: 8px;
|
margin-bottom: 8px;
|
font-size: 14px;
|
font-weight: 600;
|
color: #303133;
|
}
|
|
.ratio-type {
|
font-size: 12px;
|
font-weight: 400;
|
color: #909399;
|
}
|
|
::v-deep .el-table th {
|
background-color: #f8f9fa;
|
}
|
}
|
</style>
|