293 lines
7.7 KiB
Vue
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>
|