feat: 新增选项操作功能并优化问卷设计页面

- 新增 OptionAction 组件用于选项操作
- 更新 BaseSelect 组件,集成 OptionAction 功能
- 优化 Paging 组件样式
- 调整 Design 页面布局和样式
This commit is contained in:
陈昱达
2025-03-04 20:38:14 +08:00
parent 520242ec51
commit 943cc7eeca
5 changed files with 216 additions and 4 deletions

3
components.d.ts vendored
View File

@@ -14,12 +14,15 @@ declare module 'vue' {
VanCellGroup: typeof import('vant/es')['CellGroup'] VanCellGroup: typeof import('vant/es')['CellGroup']
VanCheckbox: typeof import('vant/es')['Checkbox'] VanCheckbox: typeof import('vant/es')['Checkbox']
VanCheckboxGroup: typeof import('vant/es')['CheckboxGroup'] VanCheckboxGroup: typeof import('vant/es')['CheckboxGroup']
VanDialog: typeof import('vant/es')['Dialog']
VanDivider: typeof import('vant/es')['Divider'] VanDivider: typeof import('vant/es')['Divider']
VanField: typeof import('vant/es')['Field'] VanField: typeof import('vant/es')['Field']
VanIcon: typeof import('vant/es')['Icon'] VanIcon: typeof import('vant/es')['Icon']
VanPopup: typeof import('vant/es')['Popup']
VanSearch: typeof import('vant/es')['Search'] VanSearch: typeof import('vant/es')['Search']
VanSwitch: typeof import('vant/es')['Switch'] VanSwitch: typeof import('vant/es')['Switch']
VanTabbar: typeof import('vant/es')['Tabbar'] VanTabbar: typeof import('vant/es')['Tabbar']
VanTabbarItem: typeof import('vant/es')['TabbarItem'] VanTabbarItem: typeof import('vant/es')['TabbarItem']
YLSelect: typeof import('./src/components/YLSelect.vue')['default']
} }
} }

View File

@@ -0,0 +1,97 @@
<template>
<el-select v-model="selectedValue" class="yl-select-wrapper" @change="handleChange">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
:disabled="item.disabled"
/>
</el-select>
</template>
<script>
import { defineComponent, ref, watch } from 'vue';
import 'element-plus/dist/index.css';
import { ElSelect, ElOption } from 'element-plus';
export default defineComponent({
name: 'YLSelect',
components: {
ElSelect,
ElOption
},
props: {
modelValue: {
type: [String, Number],
default: ''
},
options: {
type: Array,
default: () => []
},
placeholder: {
type: String,
default: '请选择'
},
disabled: {
type: Boolean,
default: false
}
},
setup(props, { emit }) {
const selectedValue = ref(props.modelValue);
watch(
() => props.modelValue,
(newVal) => {
selectedValue.value = newVal;
}
);
const handleChange = (event) => {
emit('update:modelValue', event);
emit('change', event);
};
return {
selectedValue,
handleChange
};
}
});
</script>
<style scoped lang="scss">
.yl-select-wrapper {
position: relative;
display: inline-block;
font-size: 16px; /* 增加字体大小 */
line-height: 1.5; /* 增加行高 */
}
.yl-select {
height: 44px; /* 增加高度以适应触摸 */
padding: 10px; /* 增加内边距 */
border: 1px solid #ccc;
border-radius: 5px;
background-color: #fff;
outline: none;
cursor: pointer;
appearance: none;
&:disabled {
background-color: #f5f5f5;
cursor: not-allowed;
}
&::after {
content: '\25BC'; /* 添加下拉箭头 */
position: absolute;
top: 50%;
right: 10px;
color: #999;
pointer-events: none;
transform: translateY(-50%);
}
}
</style>

View File

@@ -181,6 +181,50 @@ export const useCommonStore = defineStore('common', {
cell_indexs: [] cell_indexs: []
}, },
hide_option_index: [] hide_option_index: []
},
{
id: 472152,
question_index: 24,
sample_number: 0,
skip_question_index: 0,
skip_type: 1,
question_id: '17852294',
logic: [
{
value: '',
location: 0,
date: '',
time: '',
type: 0,
row_type: 0,
cell_type: 0,
logic: 'if',
operator: '=',
is_answer: 1,
is_select: 0,
row_index: 0,
cell_index: 0,
question_type: 1,
question_index: 0,
relation_question_index: 0,
relation_question_row_index: 0,
relation_question_cell_index: 0,
is_option_group: 0,
option_index: 0,
skip_type: null,
question_id: null
}
],
autofill: {
value: '',
date: '',
time: '',
question_type: 1,
option_indexs: [],
row_indexs: [],
cell_indexs: []
},
hide_option_index: []
} }
], ],
questions: [ questions: [

View File

@@ -1,5 +1,5 @@
<template> <template>
<div> <div class="design-create">
<draggable <draggable
v-model:data="questionInfo.questions" v-model:data="questionInfo.questions"
item-key="id" item-key="id"
@@ -115,4 +115,10 @@ onMounted(() => {
questionInfo.value = store.questionsInfo.value; questionInfo.value = store.questionsInfo.value;
}); });
</script> </script>
<style scoped lang="scss"></style> <style scoped lang="scss">
.design-create {
min-height: calc(100vh);
background-color: #e9eef3;
color: #333;
}
</style>

View File

@@ -33,12 +33,12 @@
</template> </template>
</van-cell> </van-cell>
<van-divider></van-divider> <van-divider></van-divider>
<van-cell title="题前隐藏" :border="false"> <van-cell title="题前隐藏" :border="false" @click="questionSetting('before')">
<template #right-icon> <template #right-icon>
<span> {{ getSkipTypeText(0) }} <van-icon name="arrow"></van-icon></span> <span> {{ getSkipTypeText(0) }} <van-icon name="arrow"></van-icon></span>
</template> </template>
</van-cell> </van-cell>
<van-cell title="题后跳转" :border="false"> <van-cell title="题后跳转" :border="false" @click="questionSetting('after')">
<template #right-icon> <template #right-icon>
<span> {{ getSkipTypeText(1) }} <van-icon name="arrow"></van-icon></span> <span> {{ getSkipTypeText(1) }} <van-icon name="arrow"></van-icon></span>
</template> </template>
@@ -50,6 +50,30 @@
</van-action-sheet> </van-action-sheet>
<!-- 题目操作 题前 题后--> <!-- 题目操作 题前 题后-->
<van-popup
v-model:show="questionBeforeShow"
title="题前隐藏逻辑"
position="bottom"
round
:style="{ minHeight: '60%' }"
>
<template v-for="(item, index) in logics" :key="index">
<div
v-if="item.skip_type === skipType && item.question_index === activeQuestion.question_index"
class="mv10 question-setting"
>
<template v-for="(log, logIndex) in item.logic" :key="logIndex">
<yl-select
v-model="log.logic"
:options="settingIfOption"
class="question-setting-if"
></yl-select>
<yl-select v-model="log.logic" :options="settingIfOption"></yl-select>
</template>
</div>
</template>
</van-popup>
</template> </template>
<script setup> <script setup>
import { showConfirmDialog } from 'vant'; import { showConfirmDialog } from 'vant';
@@ -57,10 +81,17 @@ import { ref } from 'vue';
import { useCounterStore } from '@/stores/counter'; import { useCounterStore } from '@/stores/counter';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import YlSelect from '@/components/YLSelect.vue';
const store = useCounterStore(); const store = useCounterStore();
const { questionsInfo } = storeToRefs(store); const { questionsInfo } = storeToRefs(store);
const logics = questionsInfo.value.logics; const logics = questionsInfo.value.logics;
const settingIfOption = [
{ label: 'if', value: 'if' },
{ label: 'always', value: 'always' }
];
const props = defineProps({ const props = defineProps({
index: { index: {
type: Number, type: Number,
@@ -87,6 +118,7 @@ const questions = ref(props.questions);
const activeQuestion = ref(props.data); const activeQuestion = ref(props.data);
const show = ref(false); const show = ref(false);
const questionBeforeShow = ref(false);
const deleteQuestion = () => { const deleteQuestion = () => {
showConfirmDialog({ showConfirmDialog({
title: '提示', title: '提示',
@@ -124,6 +156,7 @@ const questionMove = (action) => {
} }
}; };
const skipType = ref(-1);
// 获取题前隐藏 和题后 跳转 文字 // 获取题前隐藏 和题后 跳转 文字
const getSkipTypeText = (skipType) => { const getSkipTypeText = (skipType) => {
const ls = []; const ls = [];
@@ -141,12 +174,41 @@ const getSkipTypeText = (skipType) => {
} }
return text; return text;
}; };
// 题前 题后逻辑配置
const questionSetting = (type) => {
switch (type) {
case 'before':
questionBeforeShow.value = true;
break;
case 'after':
questionBeforeShow.value = true;
break;
}
skipType.value = type === 'before' ? 0 : 1;
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.ml10 { .ml10 {
margin-left: 10px; margin-left: 10px;
} }
.mv10 {
margin: 10px;
}
.y-select {
min-width: 10vw;
//max-width: 90vw;
}
.question-setting-if {
width: 140px;
}
.question-action-container { .question-action-container {
font-size: 20px; font-size: 20px;