feat/design: 优化矩阵题样式和功能

- 新增矩阵题类型的支持和样式
- 修复矩阵题选项保存逻辑
-优化矩阵题输入框样式
- 新增项目名称保存功能
- 调整问卷创建页面逻辑
This commit is contained in:
陈昱达
2025-03-18 18:25:09 +08:00
parent 36ea47bf9c
commit 5371f41a2f
14 changed files with 166 additions and 73 deletions

1
components.d.ts vendored
View File

@@ -40,6 +40,7 @@ declare module 'vue' {
VanRadioGroup: typeof import('vant/es')['RadioGroup']
VanRow: typeof import('vant/es')['Row']
VanSearch: typeof import('vant/es')['Search']
VanStep: typeof import('vant/es')['Step']
VanStepper: typeof import('vant/es')['Stepper']
VanSwitch: typeof import('vant/es')['Switch']
VanTab: typeof import('vant/es')['Tab']

View File

@@ -43,3 +43,10 @@ export function saveSettings(params) {
data: params
});
}
export function saveProjectName(sn, params) {
return request({
url: `/console/surveys/${sn}/project_name`,
method: 'PATCH',
data: params
});
}

View File

@@ -224,7 +224,7 @@
}
input {
outline-color: transparent;
outline: none;
}
.el-input__wrapper,

View File

@@ -235,7 +235,6 @@ const questionMove = (action) => {
const temp = questions.value[props.questionIndex];
questions.value.splice(props.questionIndex, 1);
questions.value.splice(props.questionIndex + 1, 0, temp);
emit('move', 'down');
} else if (action.action === 'up') {
if (props.questionIndex === 0) {
@@ -247,14 +246,12 @@ const questionMove = (action) => {
emit('move', 'up');
} else {
// 复制 题目 生成新的id 更新最新的 last index
const temp = questions.value[props.questionIndex];
const temp = JSON.parse(JSON.stringify(questions.value[props.questionIndex]));
const newQuestion = {
...temp,
id: uuidv4(),
question_index: questionsInfo.value.survey.last_question_index + 1
};
questions.value.splice(props.questionIndex + 1, 0, newQuestion);
questionsInfo.value.survey.last_question_index += 1;
emit('copy', newQuestion);

View File

@@ -12,10 +12,13 @@
@blur="emit('saveOption')"
@update:model-value="
(value) => {
actionQuestion.config.min_select = Number(value);
actionQuestion.config.min_select = value;
}
"
>
<!-- <template #input>-->
<!-- <van-step></van-step>-->
<!-- </template>-->
<template #right-icon>
<span>个</span>
</template>
@@ -31,7 +34,7 @@
@blur="emit('saveOption')"
@update:model-value="
(value) => {
actionQuestion.config.max_select = Number(value);
actionQuestion.config.max_select = value;
}
"
>

View File

@@ -29,7 +29,7 @@
@blur="emit('saveOption')"
@update:model-value="
(value) => {
actionQuestion.config.line_height = Number(value);
actionQuestion.config.line_height = value;
}
"
>
@@ -52,7 +52,7 @@
@blur="emit('saveOption')"
@update:model-value="
(value) => {
actionQuestion.config.line_height = Number(value);
actionQuestion.config.line_height = value;
}
"
>
@@ -90,7 +90,7 @@
@blur="emit('saveOption')"
@update:model-value="
(value) => {
actionQuestion.config.min = Number(value);
actionQuestion.config.min = value;
}
"
>
@@ -110,7 +110,7 @@
@blur="emit('saveOption')"
@update:model-value="
(value) => {
actionQuestion.config.max = Number(value);
actionQuestion.config.max = value;
}
"
>
@@ -151,7 +151,7 @@
@blur="emit('saveOption')"
@update:model-value="
(value) => {
actionQuestion.config.line_height = Number(value);
actionQuestion.config.line_height = value;
}
"
>
@@ -170,7 +170,7 @@
@blur="emit('saveOption')"
@update:model-value="
(value) => {
actionQuestion.config.line_height = Number(value);
actionQuestion.config.line_height = value;
}
"
>

View File

@@ -12,7 +12,7 @@
@blur="emit('saveOption')"
@update:model-value="
(value) => {
actionQuestion.config.min_number = Number(value);
actionQuestion.config.min_number = value;
}
"
>
@@ -31,7 +31,7 @@
@blur="emit('saveOption')"
@update:model-value="
(value) => {
actionQuestion.config.max_number = Number(value);
actionQuestion.config.max_number = value;
}
"
>
@@ -53,7 +53,7 @@
@blur="emit('saveOption')"
@update:model-value="
(value) => {
actionQuestion.config.min_size = Number(value);
actionQuestion.config.min_size = value;
}
"
>
@@ -72,7 +72,7 @@
@blur="emit('saveOption')"
@update:model-value="
(value) => {
actionQuestion.config.max_size = Number(value);
actionQuestion.config.max_size = value;
}
"
>

View File

@@ -25,7 +25,7 @@
@blur="emit('saveOption')"
@update:model-value="
(value) => {
actionQuestion.config.min_select = Number(value);
actionQuestion.config.min_select = value;
}
"
>
@@ -95,7 +95,7 @@
@blur="emit('saveOption')"
@update:model-value="
(value) => {
actionQuestion.config.min_select = Number(value);
actionQuestion.config.min_select = value;
}
"
>
@@ -114,7 +114,7 @@
@blur="emit('saveOption')"
@update:model-value="
(value) => {
actionQuestion.config.max_select = Number(value);
actionQuestion.config.max_select = value;
}
"
>

View File

@@ -120,14 +120,14 @@ function emitInfo() {
}
function intervalChange(value) {
localConfig.value.score_interval = Number(value) === 0 ? 1 : Number(value);
localConfig.value.score_interval = value === 0 ? 1 : value;
setDefaultMax();
emitInfo();
}
const minChange = (value) => {
const oldMax = Number(props.config.min);
localConfig.value.min = Number(value);
localConfig.value.min = value;
if (localConfig.value.min > localConfig.value.max) {
localConfig.value.min = oldMax;
}
@@ -137,7 +137,7 @@ const minChange = (value) => {
function maxChange(value) {
const oldMax = Number(props.config.max);
localConfig.value.max = Number(value);
localConfig.value.max = value;
if (!isSurplus()) {
localConfig.value.max = oldMax;
}

View File

@@ -4,7 +4,12 @@ import MatrixCheckbox from '@/views/Design/components/Questions/MatrixCheckbox.v
import MatrixText from '@/views/Design/components/Questions/MatrixText.vue';
import MatrixRadio from '@/views/Design/components/Questions/MatrixRadio.vue';
const question = defineModel<question>('element', { default: () => ({}), required: false });
const question = defineModel<question>('element', {
type: Object,
default: () => {
return {};
}
});
// eslint-disable-next-line
const activeComponent = computed<Component>(() => {
switch (question.value.question_type) {
@@ -33,30 +38,33 @@ console.log(rows.value, cols.value);
active: boolean;
}>();
const emit = defineEmits(['update:element']);
const emitValue = () => {
emit('update:element', element.value);
console.log(question.value);
emit('update:element', question.value);
};
</script>
<template>
<van-field
v-model="element.stem"
:label="element.stem"
:required="element.config.is_required === 1"
v-model="question.stem"
:label="question.stem"
:required="question.config.is_required === 1"
label-align="top"
class="contenteditable-question-title"
>
<template #left-icon>
{{ index + 1 }}
</template>
<!-- 使用 title 插槽来自定义标题 -->
<template #label>
<h1>
<contenteditable v-model="element.stem" :active="active" @blur="emitValue" />
</h1>
<contenteditable v-model="question.stem" :active="active" @blur="emitValue" />
</template>
<template #input>
<!-- <div style="width: 1000px; overflow: scroll">-->
<Component :is="activeComponent" v-model:rows="rows" v-model:cols="cols" />
<!-- </div>-->
</template>
<!-- 使用 label 插槽来自定义标题 -->
<!-- <template #input>-->
@@ -104,7 +112,7 @@ const emitValue = () => {
<!-- </template>-->
</van-field>
</template>
<style lang="scss">
<style lang="scss" scoped>
.matrix-table {
width: 100%;
border-collapse: collapse;
@@ -116,8 +124,6 @@ const emitValue = () => {
th,
td {
//min-width: 80px;
//padding: 8px;
border-width: 0 0 1px;
text-align: center;
}
@@ -126,24 +132,4 @@ const emitValue = () => {
.td-input {
text-align: center;
}
input[type='text'] {
width: 85%;
border: none;
border-radius: 5px;
outline: 1px solid #ddd;
}
input[type='checkbox'] {
border: 1px solid #ddd;
border-radius: 5px;
background-color: red;
outline: none;
}
input[type='radio'] {
border: 1px solid #ddd;
border-radius: 5px;
outline: none;
}
</style>

View File

@@ -26,6 +26,7 @@
<th v-for="(col, colIndex) in cols" :key="colIndex">
<input
class="van-icon matrix-checkbox"
type="checkbox"
:name="`R${rowIndex + 1}`"
:value="`${rowIndex + 1}_${colIndex + 1}`"
@@ -114,4 +115,38 @@ function handleMatrixRadioChange(row: number, col: number) {
// };
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
@import '@/assets/css/theme';
.matrix-table {
text-align: left;
}
.matrix-checkbox {
position: relative;
overflow: hidden;
width: 15px;
height: 15px;
border: 1px solid #f4f4f4;
border-radius: 4px;
outline: none;
cursor: pointer;
appearance: none; /* 去除默认样式 */
//transition: border-color 0.3s;
}
.matrix-checkbox:checked {
border-color: transparent; /* 选中时边框颜色 */
}
.matrix-checkbox:checked::before {
content: '\e728';
position: absolute;
width: 15px;
height: 15px;
background-color: $theme-color; /* 选中颜色 */
color: #fff;
line-height: 15px;
text-align: center;
}
</style>

View File

@@ -24,6 +24,7 @@
<td v-for="(col, colIndex) in cols" :key="colIndex">
<input
type="radio"
class="van-icon matrix-radio"
:name="`R${rowIndex + 1}`"
:value="`${rowIndex + 1}_${colIndex + 1}`"
:checked="isOptionChecked(rowIndex, colIndex)"
@@ -97,4 +98,38 @@ const emitValue = () => {
};
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
@import '@/assets/css/theme';
.matrix-table {
text-align: left;
}
.matrix-radio {
position: relative;
overflow: hidden;
width: 15px;
height: 15px;
border: 1px solid #f4f4f4;
border-radius: 50%;
outline: none;
cursor: pointer;
appearance: none; /* 去除默认样式 */
//transition: border-color 0.3s;
}
.matrix-radio:checked {
border-color: transparent; /* 选中时边框颜色 */
}
.matrix-radio:checked::before {
content: '\e728';
position: absolute;
width: 15px;
height: 15px;
background-color: $theme-color; /* 选中颜色 */
color: #fff;
line-height: 15px;
text-align: center;
}
</style>

View File

@@ -26,6 +26,7 @@
<td v-for="(col, colIndex) in cols" :key="colIndex">
<input
type="text"
placeholder="请输入"
:name="`R${rowIndex + 1}`"
:value="getInputValue(rowIndex, colIndex)"
@change="handleMatrixTextChange(rowIndex, colIndex, $event)"
@@ -95,4 +96,27 @@ function handleMatrixTextChange(row: number, col: number, e: Event) {
// };
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
@import '@/assets/css/theme';
.matrix-table {
text-align: left;
tr,
td {
//width: 200px;
}
}
input {
width: 70%;
padding: 0 5px;
border: none;
border-radius: 4px;
outline: 1px solid #f4f4f4;
&:focus {
outline: 1px solid $theme-color;
}
}
</style>

View File

@@ -350,7 +350,8 @@ import {
saveQuestion,
snQuestions,
sync,
saveSettings
saveSettings,
saveProjectName
} from '@/api/design/index';
import Design from '@/views/Design/Index.vue';
import { useCounterStore } from '@/stores/counter';
@@ -419,6 +420,10 @@ const saveTitle = () => {
sn: route.query.sn,
title: questionInfo.value.survey.title,
introduction: questionInfo.value.survey.introduction
}).then((res) => {
if (res.data) {
saveProjectName(route.query.sn, { project_name: questionInfo.value.survey.title });
}
});
};
@@ -580,7 +585,7 @@ const previewQuestion = () => {
router.push({ name: 'preview', query: { ...route.query } });
};
onMounted(async() => {
onMounted(async () => {
await getQuestionDetail();
});
</script>