Files
ylst-h5/src/views/Design/components/Questions/MatrixQuestion.vue
Huangzhe bb2df81907 feat[design]: 优化矩阵显示
- 当没有行列时,取消显示第一竖行的显示
2025-03-25 10:49:09 +08:00

293 lines
7.7 KiB
Vue

<script setup lang="ts">
import { type Component, defineModel } from 'vue';
import { Io5EllipsisVerticalSharp } from 'vue-icons-plus/io5';
import MatrixCheckbox from '@/views/Design/components/Questions/MatrixCheckbox.vue';
import MatrixText from '@/views/Design/components/Questions/MatrixText.vue';
import MatrixRadio from '@/views/Design/components/Questions/MatrixRadio.vue';
// 添加激活 action 的选项
interface _questionOptionType extends questionOptionType {
showAction?: boolean;
}
// 题目
const element = defineModel<question>('element', {
type: Object,
default: () => {
return {};
}
});
// table 宽度
const tableWidth = defineModel<number | string>('tableWidth', { required: false, default: 110 });
// 行记录(相关答案的记录)
const rowRecord = defineModel<unknown[]>('rowRecord', { required: false, default: () => [] });
// 是否是预览状态
const isPreview = defineModel<boolean>('isPreview', { required: false, default: false });
// 行标签
const rows = defineModel<_questionOptionType[]>('rows', { required: false, default: () => [] });
// 列标签
const cols = defineModel<_questionOptionType[]>('cols', { required: false, default: () => [] });
// 题的序号
const index = defineModel<number | string>('index', { required: false, default: 0 });
// 是否是编辑状态
const active = defineModel<boolean>('active', { required: false, default: false });
const emit = defineEmits(['update:element']);
// 更新题
const emitValue = () => {
// console.log(question.value);
emit('update:element', element.value);
};
// 组件选择
const activeComponent = selectActiveComponent();
function selectActiveComponent(): Component {
console.log(`question_type`, element.value.question_type);
switch (element.value.question_type) {
case 8:
return MatrixText;
case 9:
return MatrixRadio;
case 10:
return MatrixCheckbox;
default:
return MatrixText;
}
}
console.log(`active component`, activeComponent);
// 增加 popover 的 actions
const popoverActions = [
{
text: '删除'
}
// 置底功能暂时屏蔽
// {
// text: '置底'
// }
];
/**
* popover 被选中的事件
* @param action { text: string }
* @param axi { option: string, showAction: boolean }
* @param type { 'row' | 'col' }
*/
function handleActionSelect(action: { text: string }, axi: any, type: 'row' | 'col') {
const data = type === 'row' ? rows : cols;
const index = data.value.indexOf(axi);
if (index < 0) return;
switch (action.text) {
case '删除':
data.value.splice(index, 1);
break;
case '置底':
data.value.push(data.value.splice(index, 1)[0]);
break;
}
}
addShowActionOption();
/**
* 给行或者列选项 添加 showAction 选项
*/
function addShowActionOption() {
rows.value.forEach((row) => {
row.showAction = true;
});
cols.value.forEach((col) => {
col.showAction = true;
});
}
const errorMessage = defineModel('errorMessage', {
type: String,
default: ''
});
</script>
<template>
<van-field
v-model="element.stem"
:label="element.stem"
:required="element.config?.is_required === 1"
label-align="top"
class="contenteditable-question-title"
>
<template #left-icon> {{ isPreview ? element.title : index + 1 }}. </template>
<!-- 使用 title 插槽来自定义标题 -->
<template #label>
<contenteditable
v-model="element.stem"
:active="active"
@blur="emitValue"
:errorMessage="errorMessage"
/>
</template>
<template #input>
<div style="border-left: 1px solid #f4f4f4">
<el-table
:data="rows"
style="width: 85vw"
row-class-name="table-row"
empty-text=""
v-if="rows.length || cols.length"
>
<el-table-column width="130px" v-if="rows.length">
<template #header>
<div :style="{ width: `${tableWidth}px`, height: '30px' }"></div>
</template>
<template #default="{ row /*, column, $index*/ }">
<div style="position: relative">
<el-text truncated>
<contenteditable v-model="row.option" :active="active" @blur="emitValue" />
</el-text>
<van-popover
v-model="row.showAction"
placement="left-end"
trigger="click"
:actions="popoverActions"
@select="handleActionSelect($event, row, 'row')"
>
<template #reference>
<div v-if="active" class="icon">
<Io5EllipsisVerticalSharp />
</div>
</template>
</van-popover>
</div>
</template>
</el-table-column>
<el-table-column
v-for="(col, colIndex) in cols"
:key="col.option"
:width="130"
style="position: relative"
>
<template #header>
<div>
<el-text truncated :style="{ width: `${tableWidth}px` }">
<contenteditable v-model="col.option" :active="active" @blur="emitValue" />
</el-text>
<van-popover
v-model="col.showAction"
placement="left-end"
trigger="click"
:actions="popoverActions"
@select="handleActionSelect($event, col, 'col')"
>
<template #reference>
<div v-if="active" class="icon">
<Io5EllipsisVerticalSharp />
</div>
</template>
</van-popover>
</div>
</template>
<template #default="{ /*row, column, */ $index: rowIndex }">
<component
:is="activeComponent"
:element="element"
:rowIndex="rowIndex"
:colIndex="colIndex"
v-model:rowRecord="rowRecord"
/>
</template>
</el-table-column>
</el-table>
</div>
</template>
</van-field>
</template>
<style lang="scss" scoped>
@import '@/assets/css/theme';
input[type='text'] {
width: 100%;
padding: 0 5px;
border: none;
border-radius: 4px;
outline: 1px solid #f4f4f4;
&:focus {
outline: 1px solid $theme-color;
}
}
.icon {
position: absolute;
top: 15%;
right: 0;
& > svg {
height: 15px;
}
}
:deep(.el-table) {
// Remove horizontal borders
.el-table__inner-wrapper::before {
display: none;
}
// Remove all borders first
.el-table__cell {
border: none;
}
// Add only vertical borders
.el-table__cell::after {
content: '';
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 1px;
background-color: #ebeef5;
height: 100%;
}
// Add left vertical border to the first cell in each row
.el-table__cell:first-child::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 1px;
background-color: #ebeef5;
height: 100%;
}
// Add horizontal line at the top of the table
.el-table__header-wrapper .el-table__cell {
border-top: 1px solid #ebeef5;
}
// Remove the border between header and body
.el-table__header-wrapper tr.el-table__row th.el-table__cell.is-leaf {
border-bottom: none;
}
.el-table__body-wrapper tr:first-child td.el-table__cell {
border-top: none;
}
// Add horizontal line at the bottom of the table
.el-table__row:last-child .el-table__cell {
border-bottom: 1px solid #ebeef5;
}
// Center all content
.cell {
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
}
</style>