feat: 新增选项操作功能并优化问卷设计页面
- 新增 OptionAction 组件用于选项操作 - 更新 BaseSelect 组件,集成 OptionAction 功能 - 优化 Paging 组件样式 - 调整 Design 页面布局和样式
This commit is contained in:
8
components.d.ts
vendored
8
components.d.ts
vendored
@@ -2,15 +2,11 @@
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
export {}
|
||||
export {};
|
||||
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElContainer: typeof import('element-plus/es')['ElContainer']
|
||||
ElHeader: typeof import('element-plus/es')['ElHeader']
|
||||
ElMain: typeof import('element-plus/es')['ElMain']
|
||||
Index: typeof import('./src/components/VanCellModel/Index.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
@@ -24,5 +20,7 @@ declare module 'vue' {
|
||||
VanDivider: typeof import('vant/es')['Divider']
|
||||
VanField: typeof import('vant/es')['Field']
|
||||
VanIcon: typeof import('vant/es')['Icon']
|
||||
VanSearch: typeof import('vant/es')['Search']
|
||||
VanSwitch: typeof import('vant/es')['Switch']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
.van-cell {
|
||||
padding: 8px !important;
|
||||
}
|
||||
|
||||
.van-divider {
|
||||
margin: 5px 0 !important;
|
||||
}
|
||||
|
||||
.van-popup--bottom.van-popup--round {
|
||||
border-radius: 10px 10px 0 0 !important;
|
||||
}
|
||||
|
||||
@@ -9,12 +9,11 @@ import { RouterView } from 'vue-router';
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
.common-layout {
|
||||
min-height: calc(100vh);
|
||||
background-color: #E9EEF3;
|
||||
background-color: #e9eef3;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
//text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,6 +4,8 @@ import { createApp } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
// 2. 引入组件样式
|
||||
import 'vant/lib/index.css';
|
||||
const app = createApp(App);
|
||||
app.use(createPinia());
|
||||
app.use(router);
|
||||
|
||||
@@ -93,7 +93,96 @@ export const useCommonStore = defineStore('common', {
|
||||
}
|
||||
]
|
||||
},
|
||||
logics: [],
|
||||
logics: [
|
||||
{
|
||||
id: 472148,
|
||||
question_index: 24,
|
||||
sample_number: 0,
|
||||
skip_question_index: 27,
|
||||
skip_type: 0,
|
||||
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: 24,
|
||||
relation_question_index: 0,
|
||||
relation_question_row_index: 0,
|
||||
relation_question_cell_index: 0,
|
||||
is_option_group: 0,
|
||||
option_index: 1,
|
||||
skip_type: null,
|
||||
question_id: null
|
||||
}
|
||||
],
|
||||
autofill: {
|
||||
value: '',
|
||||
date: '',
|
||||
time: '',
|
||||
question_type: 1,
|
||||
option_indexs: [],
|
||||
row_indexs: [],
|
||||
cell_indexs: []
|
||||
},
|
||||
hide_option_index: []
|
||||
},
|
||||
{
|
||||
id: 472149,
|
||||
question_index: 24,
|
||||
sample_number: 0,
|
||||
skip_question_index: 0,
|
||||
skip_type: 0,
|
||||
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: 24,
|
||||
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: [
|
||||
{
|
||||
id: '17852294',
|
||||
@@ -274,7 +363,7 @@ export const useCommonStore = defineStore('common', {
|
||||
id: '1048968',
|
||||
type: 0,
|
||||
is_other: 0,
|
||||
is_fixed: 0,
|
||||
is_fixed: 1,
|
||||
is_remove_other: 0,
|
||||
created_at: null,
|
||||
created_user_id: null,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<template #item="{ element, index }">
|
||||
<choose-question
|
||||
:element="element"
|
||||
:questions="questionInfo.questions"
|
||||
:index="index"
|
||||
:chooseQuestionId="chooseQuestionId"
|
||||
@get-choose-question-id="getChooseQuestionId"
|
||||
|
||||
@@ -1,30 +1,53 @@
|
||||
<template>
|
||||
<div class="option-action">
|
||||
<template v-for="(item, index) in data" :key="item.id">
|
||||
<template v-for="(item, index) in data" :key="index">
|
||||
<div class="flex align-center option-action-container">
|
||||
<slot name="item" :element="item" :index="index"></slot>
|
||||
<span v-if="active" class="flex">
|
||||
<van-icon name="close"></van-icon>
|
||||
<van-icon name="more-o" @click="openOptionActionModel"></van-icon>
|
||||
<van-icon name="close" @click="deleteOption(index)"></van-icon>
|
||||
<van-icon name="more-o" @click="openOptionActionModel(item, index)"></van-icon>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<!-- 操作项弹窗-->
|
||||
<van-action-sheet v-model:show="show">
|
||||
<template #description>
|
||||
<div class="flex flex-start">操作选项</div>
|
||||
</template>
|
||||
<div class="">
|
||||
<van-cell title="固定置底"></van-cell>
|
||||
<van-cell title="固定置底"></van-cell>
|
||||
<van-cell title="固定置底"></van-cell>
|
||||
<van-cell title="固定置底"></van-cell>
|
||||
</div>
|
||||
<van-cell-group :border="false" class="ml10">
|
||||
<van-cell title="固定置底" :border="false">
|
||||
<template #right-icon>
|
||||
<van-switch
|
||||
v-model="activeOption.is_fixed"
|
||||
class="option-action-sheet-switch"
|
||||
size="0.5rem"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
></van-switch>
|
||||
</template>
|
||||
</van-cell>
|
||||
<van-cell title="设为其他项" :border="false">
|
||||
<template #right-icon>
|
||||
<van-switch
|
||||
v-model="activeOption.is_other"
|
||||
class="option-action-sheet-switch"
|
||||
size="0.5rem"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
></van-switch>
|
||||
</template>
|
||||
</van-cell>
|
||||
<van-divider></van-divider>
|
||||
<van-cell title="下移选项" :border="false" @click="optionMove('down')"></van-cell>
|
||||
<van-cell title="上移选项" :border="false" @click="optionMove('up')"></van-cell>
|
||||
</van-cell-group>
|
||||
</van-action-sheet>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
defineProps({
|
||||
import { showConfirmDialog } from 'vant';
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
@@ -40,17 +63,66 @@ defineProps({
|
||||
});
|
||||
// const emit = defineEmits(['update:data']);
|
||||
const show = ref(false);
|
||||
const activeOption = ref({});
|
||||
const activeIndex = ref(-1);
|
||||
const element = ref(props.data);
|
||||
|
||||
// emit('update:data');
|
||||
/**
|
||||
* @name 打开model弹窗
|
||||
* @created_date 2025/3/4
|
||||
* @description
|
||||
**/
|
||||
const openOptionActionModel = () => {
|
||||
const openOptionActionModel = (item, index) => {
|
||||
show.value = true;
|
||||
activeOption.value = item;
|
||||
activeIndex.value = index;
|
||||
};
|
||||
|
||||
// 上下移动
|
||||
const optionMove = (action) => {
|
||||
switch (action) {
|
||||
case 'up':
|
||||
if (activeIndex.value === 0) {
|
||||
show.value = false;
|
||||
return;
|
||||
}
|
||||
// 向上移动
|
||||
element.value.splice(activeIndex.value - 1, 0, element.value.splice(activeIndex.value, 1)[0]);
|
||||
activeIndex.value -= 1;
|
||||
break;
|
||||
case 'down':
|
||||
if (activeIndex.value === element.value.length) {
|
||||
show.value = false;
|
||||
return;
|
||||
}
|
||||
element.value.splice(activeIndex.value + 1, 0, element.value.splice(activeIndex.value, 1)[0]);
|
||||
activeIndex.value += 1;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// 删除当前选项
|
||||
|
||||
const deleteOption = (index) => {
|
||||
showConfirmDialog({
|
||||
title: '提示',
|
||||
message: '是否删除选项?'
|
||||
})
|
||||
.then(() => {
|
||||
// on confirm
|
||||
element.value.splice(index, 1);
|
||||
})
|
||||
.catch(() => {
|
||||
// on cancel
|
||||
});
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.ml10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.option-action {
|
||||
font-size: 20px;
|
||||
|
||||
|
||||
@@ -1,11 +1,139 @@
|
||||
<template>
|
||||
<div class="container question-action-container flex">
|
||||
<van-icon name="delete"></van-icon>
|
||||
<van-icon name="more"></van-icon>
|
||||
<van-icon name="delete" @click="deleteQuestion"></van-icon>
|
||||
<van-icon name="more" @click="openQuestionActionModel"></van-icon>
|
||||
</div>
|
||||
|
||||
<!-- 操作项弹窗-->
|
||||
<van-action-sheet v-model:show="show">
|
||||
<template #description>
|
||||
<div class="flex flex-start">操作选项</div>
|
||||
</template>
|
||||
<van-cell-group :border="false" class="ml10">
|
||||
<van-cell title="此题必答" :border="false" label-align="left">
|
||||
<template #right-icon>
|
||||
<van-switch
|
||||
v-model="activeQuestion.config.is_required"
|
||||
class="option-action-sheet-switch"
|
||||
size="0.5rem"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
></van-switch>
|
||||
</template>
|
||||
</van-cell>
|
||||
<van-cell title="选项随机" :border="false">
|
||||
<template #right-icon>
|
||||
<van-switch
|
||||
v-model="activeQuestion.config.select_random"
|
||||
class="option-action-sheet-switch"
|
||||
size="0.5rem"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
></van-switch>
|
||||
</template>
|
||||
</van-cell>
|
||||
<van-divider></van-divider>
|
||||
<van-cell title="题前隐藏" :border="false">
|
||||
<template #right-icon>
|
||||
<span> {{ getSkipTypeText() }} <van-icon name="arrow"></van-icon></span>
|
||||
</template>
|
||||
</van-cell>
|
||||
<van-cell title="题后跳转" :border="false">
|
||||
<template #right-icon>
|
||||
<span> 设置 <van-icon name="arrow"></van-icon></span>
|
||||
</template>
|
||||
</van-cell>
|
||||
<van-divider></van-divider>
|
||||
<van-cell title="下移选项" :border="false" @click="questionMove('down')"></van-cell>
|
||||
<van-cell title="上移选项" :border="false" @click="questionMove('up')"></van-cell>
|
||||
</van-cell-group>
|
||||
</van-action-sheet>
|
||||
</template>
|
||||
<script setup></script>
|
||||
<script setup>
|
||||
import { showConfirmDialog } from 'vant';
|
||||
import { ref } from 'vue';
|
||||
import { useCounterStore } from '@/stores/counter';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const store = useCounterStore();
|
||||
const { questionsInfo } = storeToRefs(store);
|
||||
const logics = questionsInfo.value.logics;
|
||||
|
||||
const props = defineProps({
|
||||
index: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
// 传递
|
||||
}
|
||||
},
|
||||
questions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
questionIndex: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
});
|
||||
const questions = ref(props.questions);
|
||||
// 当前题目
|
||||
const activeQuestion = ref(props.data);
|
||||
|
||||
const show = ref(false);
|
||||
const deleteQuestion = () => {
|
||||
showConfirmDialog({
|
||||
title: '提示',
|
||||
message: '是否删除问题?'
|
||||
})
|
||||
.then(() => {
|
||||
// on confirm
|
||||
questions.value.splice(props.questionIndex, 1);
|
||||
})
|
||||
.catch(() => {
|
||||
// on cancel
|
||||
});
|
||||
};
|
||||
|
||||
// 打开题目弹窗
|
||||
const openQuestionActionModel = () => {
|
||||
show.value = true;
|
||||
};
|
||||
// 题目上下移动
|
||||
const questionMove = (action) => {
|
||||
if (action === 'down') {
|
||||
if (props.questionIndex === questions.value.length - 1) {
|
||||
return;
|
||||
}
|
||||
const temp = questions.value[props.questionIndex];
|
||||
questions.value.splice(props.questionIndex, 1);
|
||||
questions.value.splice(props.questionIndex + 1, 0, temp);
|
||||
} else {
|
||||
if (props.questionIndex === 0) {
|
||||
return;
|
||||
}
|
||||
const temp = questions.value[props.questionIndex];
|
||||
questions.value.splice(props.questionIndex, 1);
|
||||
questions.value.splice(props.questionIndex - 1, 0, temp);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取题前隐藏 和题后 跳转 文字
|
||||
const getSkipTypeText = () => {
|
||||
setTimeout(() => {
|
||||
logics[0].id = 123;
|
||||
}, 2000);
|
||||
return logics[0].id;
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.ml10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.question-action-container {
|
||||
font-size: 20px;
|
||||
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
</template>
|
||||
<template #title>{{}}</template>
|
||||
<template #right-icon>
|
||||
<question-action></question-action>
|
||||
<question-action
|
||||
v-model:data="element"
|
||||
:questions="questions"
|
||||
:questionIndex="index"
|
||||
></question-action>
|
||||
</template>
|
||||
<!-- <div-->
|
||||
<!-- v-for="item in questionAction"-->
|
||||
@@ -51,6 +55,10 @@ const props = defineProps({
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
questions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
chooseQuestionId: {
|
||||
type: String,
|
||||
default: '0'
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</template>
|
||||
<template #input>
|
||||
<van-checkbox-group>
|
||||
<template v-for="(item, index) in element.options" :key="item.id">
|
||||
<template v-for="(item, index) in element.options" :key="index">
|
||||
<option-action v-model:data="element.options[index]" :active="active">
|
||||
<template #item="{ element: it, index: itIndex }">
|
||||
<van-checkbox
|
||||
@@ -27,13 +27,16 @@
|
||||
icon-size="0.45rem"
|
||||
>
|
||||
<template #default>
|
||||
<div class="flex align-center space-between">
|
||||
<div class="flex align-center van-cell">
|
||||
<div
|
||||
class="van-cell van-cell--borderless"
|
||||
class="van-cell--borderless"
|
||||
:contenteditable="active"
|
||||
@blur="saveOption($event, it)"
|
||||
v-html="it.option"
|
||||
></div>
|
||||
<div v-if="it.is_other">
|
||||
<input class="other-input" type="text" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</van-checkbox>
|
||||
@@ -83,5 +86,15 @@ const saveStem = (e, ele) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& .other-input {
|
||||
width: 100px;
|
||||
height: 20px;
|
||||
margin-left: 20px;
|
||||
padding: 3px 5px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user