- {h(
- uiTarget,
- {
- // class: ['input-box','item-border', ],
- class: ['input-box item-border', customClass],
- attrs: {
- type: this.type,
- readonly: this.readonly,
- placeholder: this.placeholder,
- name: this.name,
- value: this.value,
- maxlength: this.maxlength,
- minlength: this.minlength,
- autocomplete: 'off',
- },
- on: {
- blur: this.onBlur,
- input: this.onInput,
- focus: this.onFocus,
- change: this.onChange,
- },
- },
- [this.value]
- )}
- {this.$slots.default}
- {/* {this.$scopedSlots.default} */}
- {/* {renderSlot(this.$slots, 'default')} */}
+
- );
- },
-});
+ )
+ }
+})
diff --git a/web/src/materials/questions/widgets/BaseInput/style.scss b/web/src/materials/questions/widgets/BaseInput/style.scss
new file mode 100644
index 00000000..b6c6462b
--- /dev/null
+++ b/web/src/materials/questions/widgets/BaseInput/style.scss
@@ -0,0 +1,71 @@
+@import '@materials/questions/common/css/default.scss';
+
+.input-wrapper {
+ font-size: 0;
+
+ .input-box {
+ outline: none;
+ font-size: 0.56rem;
+ padding: 0.4rem;
+ border-radius: 0.08rem;
+ background-color: #fff;
+ vertical-align: top;
+
+ border: 1px solid $border-color;
+ width: 200%;
+ height: 1.61rem;
+
+ transform: scale(0.5, 0.5);
+ transform-origin: left top;
+ box-sizing: border-box;
+ margin-bottom: -0.81rem;
+ overflow: hidden;
+ color: $font-color;
+ z-index: 10;
+
+ &.is-focused:focus {
+ border: 1px solid $primary-color;
+ height: 3.5rem;
+ margin-bottom: -1.75rem;
+ overflow-y: scroll;
+ }
+
+ &:not([readonly]):hover {
+ border: 1px solid $primary-color;
+ }
+
+ &.print {
+ border: none;
+ border-bottom: 1px solid $border-color;
+ border-radius: 0;
+ }
+
+ &::-webkit-input-placeholder {
+ /* WebKit browsers */
+ font-size: 0.56rem;
+ font-weight: 300;
+ color: $placeholder-color;
+ }
+
+ &:-moz-placeholder {
+ /* Mozilla Firefox 4 to 18 */
+ font-size: 0.56rem;
+ font-weight: 300;
+ color: $placeholder-color;
+ }
+
+ &::-moz-placeholder {
+ /* Mozilla Firefox 19+ */
+ font-size: 0.56rem;
+ font-weight: 300;
+ color: $placeholder-color;
+ }
+
+ &:-ms-input-placeholder {
+ /* Internet Explorer 10+ */
+ font-size: 0.56rem;
+ font-weight: 300;
+ color: $placeholder-color;
+ }
+ }
+}
diff --git a/web/src/materials/questions/widgets/BaseRate/index.jsx b/web/src/materials/questions/widgets/BaseRate/index.jsx
index 51b541bb..3d6f9a9a 100644
--- a/web/src/materials/questions/widgets/BaseRate/index.jsx
+++ b/web/src/materials/questions/widgets/BaseRate/index.jsx
@@ -1,84 +1,86 @@
-import { defineComponent, computed } from 'vue';
-import '../../common/css/radioStar.scss';
+import { defineComponent, computed } from 'vue'
+import './style.scss'
+
export default defineComponent({
name: 'BaseRate',
props: {
name: {
type: String,
- default: '',
+ default: ''
},
value: {
type: [String, Number],
- default: 0,
+ default: 0
},
min: {
type: Number,
- default: 1,
+ default: 1
},
max: {
type: Number,
- default: 5,
+ default: 5
},
iconClass: {
type: String,
- default: 'number',
+ default: 'number'
},
readonly: {
type: Boolean,
- default: false,
- },
+ default: false
+ }
},
+ emits: ['change'],
setup(props, { emit }) {
const rating = computed({
get() {
- return props.value;
+ return props.value
},
set(val) {
- emit('change', val);
- },
- });
+ emit('change', val)
+ }
+ })
const range = computed(() => {
- const { min, max } = props;
+ const { min, max } = props
if (min > max) {
- return [];
+ return []
}
- const res = [];
+ const res = []
for (let i = min; i <= max; i++) {
- res.push(i);
+ res.push(i)
}
- return res;
- });
+ return res
+ })
const handleClick = (num) => {
- if (props.readonly) return;
- rating.value = num;
- };
+ if (props.readonly) return
+ rating.value = num
+ }
return {
rating,
range,
- handleClick,
- };
+ handleClick
+ }
},
render() {
- const { rating, range, iconClass } = this;
+ const { rating, range, iconClass } = this
return (
-
-
+
+
{range.map((num, index) => {
return (
{
- this.handleClick(num);
+ this.handleClick(num)
}}
>
{iconClass === 'number' ? num : ''}
- );
+ )
})}
- );
- },
-});
+ )
+ }
+})
diff --git a/web/src/materials/questions/widgets/BaseRate/style.scss b/web/src/materials/questions/widgets/BaseRate/style.scss
new file mode 100644
index 00000000..265c7591
--- /dev/null
+++ b/web/src/materials/questions/widgets/BaseRate/style.scss
@@ -0,0 +1,32 @@
+@import '../../common/css/default.scss';
+
+.rate-wrapper-main {
+ .rate-box {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 0.2rem;
+ }
+ .rate-item {
+ display: inline-block;
+ font-size: 0.42rem;
+ flex: 1;
+ text-align: center;
+ cursor: pointer;
+
+ &.number {
+ color: #fff;
+ height: 0.62rem;
+ font-size: 0.28rem;
+ line-height: 0.62rem;
+ &.on {
+ background-color: $primary-color;
+ }
+ &.off {
+ background-color: #e3e4e8;
+ }
+ &:not(:last-child) {
+ margin-right: 0.08rem;
+ }
+ }
+ }
+}
diff --git a/web/src/materials/questions/widgets/BinaryChoiceModule/index.jsx b/web/src/materials/questions/widgets/BinaryChoiceModule/index.jsx
index 5664ad0c..be41d390 100644
--- a/web/src/materials/questions/widgets/BinaryChoiceModule/index.jsx
+++ b/web/src/materials/questions/widgets/BinaryChoiceModule/index.jsx
@@ -1,69 +1,67 @@
-import baseChoice from '../BaseChoice';
-import { defineComponent } from 'vue';
-import metaConfig from './meta.js';
-export const meta = metaConfig;
+import { defineComponent } from 'vue'
+import BaseChoice from '../BaseChoice'
+import metaConfig from './meta.js'
+
+export const meta = metaConfig
/**
* 支持配置:
* 排列方式, layout
*/
export default defineComponent({
name: 'BinaryChoiceModule',
- components: { baseChoice },
props: {
type: {
type: String,
- default: '',
+ default: ''
},
field: {
type: String,
- default: '',
+ default: ''
},
value: {
type: String,
- default: '',
+ default: ''
},
layout: {
type: String,
- default: 'vertical',
+ default: 'vertical'
},
options: {
type: Array,
- default: () => [],
+ default: () => []
},
readonly: {
type: Boolean,
- default: false,
- },
+ default: false
+ }
},
+ emits: ['change'],
setup(props, { emit }) {
const onChange = (value) => {
- const key = props.field;
+ const key = props.field
emit('change', {
key,
- value,
- });
- };
+ value
+ })
+ }
return {
- onChange,
- };
+ props,
+ onChange
+ }
},
render() {
- const { readonly, field } = this;
- const props = {
- ...this.$props,
- readonly,
- name: field,
- };
return (
-
- );
- },
-});
+ type={this.type}
+ readonly={this.readonly}
+ name={this.field}
+ field={this.field}
+ value={this.value}
+ layout={this.layout}
+ options={this.options}
+ onChange={this.onChange}
+ >
+ )
+ }
+})
diff --git a/web/src/materials/questions/widgets/BinaryChoiceModule/meta.js b/web/src/materials/questions/widgets/BinaryChoiceModule/meta.js
index 75b2353d..9bdec1ec 100644
--- a/web/src/materials/questions/widgets/BinaryChoiceModule/meta.js
+++ b/web/src/materials/questions/widgets/BinaryChoiceModule/meta.js
@@ -1,51 +1,22 @@
-import basicConfig from '../../common/config/basicConfig';
+import basicConfig from '@materials/questions/common/config/basicConfig'
const meta = {
title: '判断题',
type: 'binary-choice',
componentName: 'BinaryChoiceModule',
- formConfig: [
- // {
- // name: 'fieldId',
- // label: '题目ID',
- // type: 'Input',
- // placeholder: '',
- // key: 'field',
- // tip: '请谨慎修改题目ID,并保证此ID在整个问卷中唯一',
- // relyField: 'questionIdCustomize',
- // noNeedRelyClean: true,
- // },
- // {
- // name: 'questionLabel',
- // label: '题目标签',
- // type: 'Input',
- // placeholder: '',
- // key: 'label',
- // tip: '请谨慎修改题目标签,并保证此标签在整个问卷中唯一',
- // relyField: 'questionLabelCustomize',
- // noNeedRelyClean: true,
- // },
- // {
- // name: 'titleOrigin',
- // label: '标题引用其他题目选项',
- // type: 'TitleOrigin',
- // options: [],
- // key: 'title',
- // },
- basicConfig,
- ],
+ formConfig: [basicConfig],
editConfigure: {
optionEdit: {
- show: false,
+ show: false
},
optionEditBar: {
show: false,
configure: {
showOthers: false,
- showAdvancedConfig: false,
- },
- },
- },
-};
+ showAdvancedConfig: false
+ }
+ }
+ }
+}
-export default meta;
+export default meta
diff --git a/web/src/materials/questions/widgets/CheckboxModule/index.jsx b/web/src/materials/questions/widgets/CheckboxModule/index.jsx
index 1564c612..b48c649c 100644
--- a/web/src/materials/questions/widgets/CheckboxModule/index.jsx
+++ b/web/src/materials/questions/widgets/CheckboxModule/index.jsx
@@ -1,119 +1,126 @@
-import baseChoice from '../BaseChoice';
-import { computed, defineComponent } from 'vue';
-import QuestionWithRule from '@/materials/questions/widgets/QuestionRuleContainer';
-import { includes } from 'lodash-es';
-import metaConfig from './meta.js';
-export const meta = metaConfig;
+import { computed, defineComponent, shallowRef, defineAsyncComponent } from 'vue'
+import { includes } from 'lodash-es'
+
+import BaseChoice from '../BaseChoice'
+import metaConfig from './meta.js'
+
+export const meta = metaConfig
/**
* 支持配置:
* 排列方式, layout
*/
export default defineComponent({
name: 'CheckBoxModule',
- components: { baseChoice, QuestionWithRule },
props: {
type: {
type: String,
- default: '',
+ default: ''
},
field: {
type: String,
- default: '',
+ default: ''
},
value: {
type: Array,
default: () => {
- return [];
- },
+ return []
+ }
},
layout: {
type: String,
- default: 'vertical',
+ default: 'vertical'
},
options: {
type: Array,
- default: () => [],
+ default: () => []
},
readonly: {
type: Boolean,
- default: false,
+ default: false
},
maxNum: {
type: [Number, String],
- default: 1,
- },
+ default: 1
+ }
},
+ emits: ['change'],
setup(props, { emit }) {
const disableState = computed(() => {
if (!props.maxNum) {
- return false;
+ return false
}
- return props.value.length >= +props.maxNum;
- });
+ return props.value.length >= +props.maxNum
+ })
const isDisabled = (item) => {
- const { value } = props;
- return disableState.value && !includes(value, item.value);
- };
+ const { value } = props
+ return disableState.value && !includes(value, item.value)
+ }
const myOptions = computed(() => {
- const { options } = props;
+ const { options } = props
return options.map((item) => {
return {
...item,
- disabled: isDisabled(item),
- };
- });
- });
+ disabled: isDisabled(item)
+ }
+ })
+ })
const onChange = (value) => {
- const key = props.field;
+ const key = props.field
emit('change', {
key,
- value,
- });
- };
+ value
+ })
+ }
const handleSelectMoreChange = (data) => {
- const { key, value } = data;
+ const { key, value } = data
emit('change', {
key,
- value,
- });
- };
+ value
+ })
+ }
+
+ const selectMoreView = shallowRef(null)
+ if (props.readonly) {
+ selectMoreView.value = defineAsyncComponent(
+ () => import('@materials/questions/QuestionContainerB')
+ )
+ } else {
+ selectMoreView.value = defineAsyncComponent(
+ () => import('@materials/questions/QuestionRuleContainer')
+ )
+ }
return {
onChange,
handleSelectMoreChange,
myOptions,
- };
+ selectMoreView
+ }
},
render() {
- const { readonly, field, myOptions } = this;
-
- const props = {
- ...this.$props,
- readonly,
- name: field,
- options: myOptions,
- };
+ const { readonly, field, myOptions, onChange, maxNum, value, selectMoreView } = this
return (
-
+ {{
selectMore: (scoped) => {
return (
- this.handleSelectMoreChange(e)}
- >
- );
- },
+ >
+ )
+ }
}}
- >
- );
- },
-});
+
+ )
+ }
+})
diff --git a/web/src/materials/questions/widgets/CheckboxModule/meta.js b/web/src/materials/questions/widgets/CheckboxModule/meta.js
index 2a248681..d9148884 100644
--- a/web/src/materials/questions/widgets/CheckboxModule/meta.js
+++ b/web/src/materials/questions/widgets/CheckboxModule/meta.js
@@ -1,195 +1,49 @@
-import basicConfig from '../../common/config/basicConfig';
+import basicConfig from '@materials/questions/common/config/basicConfig'
const meta = {
title: '多选',
- questExtra: ['listenMerge'],
type: 'checkbox',
componentName: 'CheckBoxModule',
formConfig: [
- // {
- // name: 'fieldId',
- // label: '题目ID',
- // type: 'Input',
- // placeholder: '',
- // key: 'field',
- // tip: '请谨慎修改题目ID,并保证此ID在整个问卷中唯一',
- // relyField: 'questionIdCustomize',
- // noNeedRelyClean: true,
- // },
- // {
- // name: 'questionLabel',
- // label: '题目标签',
- // type: 'Input',
- // placeholder: '',
- // key: 'label',
- // tip: '请谨慎修改题目标签,并保证此标签在整个问卷中唯一',
- // relyField: 'questionLabelCustomize',
- // noNeedRelyClean: true,
- // },
- // {
- // name: 'titleOrigin',
- // label: '标题引用其他题目选项',
- // type: 'TitleOrigin',
- // options: [],
- // key: 'title',
- // },
basicConfig,
- // {
- // name: 'optionOrigin',
- // label: '选项引用其他题目选项',
- // type: 'Select',
- // options: [],
- // key: 'optionOrigin',
- // tip: '支持引用多选题、单选题、矩阵单选题',
- // },
- // {
- // name: 'originType',
- // label: '',
- // type: 'Select',
- // options: [
- // {
- // label: '选中项',
- // value: 'selected',
- // },
- // {
- // label: '未选中项',
- // value: 'noSelected',
- // },
- // ],
- // key: 'originType',
- // },
- // {
- // name: 'optionsExtra',
- // label: '固定选项配置',
- // type: 'Options',
- // options: [],
- // keys: 'extraOptions',
- // hidden: true,
- // },
{
name: 'optionConfig',
- label: '选项配置',
+ title: '选项配置',
type: 'Customed',
key: 'optionConfig',
- labelStyle: {
- 'font-weight': 'bold',
- },
content: [
- // {
- // label: '选项随机排序',
- // type: 'CheckBox',
- // key: 'randomSort',
- // direction: 'horizon',
- // value: false,
- // contentPosition: 'before',
- // contentClass: 'check-box-config',
- // },
- // {
- // label: '固定最后一个选项',
- // type: 'CustomedSwitch',
- // key: 'pinLastOption',
- // direction: 'horizon',
- // value: false,
- // exclude: ['combination'],
- // contentClass: 'check-box-config',
- // tip: '开关开启后,本题的最后一个选项不参与随机,位置固定显示在最后',
- // },
- // {
- // label: '默认选中第一项',
- // type: 'CheckBox',
- // key: 'checked',
- // direction: 'horizon',
- // value: false,
- // contentPosition: 'before',
- // contentClass: 'check-box-config',
- // },
- // {
- // label: '展示选项余量',
- // type: 'CheckBox',
- // key: 'showLeftNum',
- // direction: 'horizon',
- // value: true,
- // include: ['appointment'],
- // contentPosition: 'before',
- // contentClass: 'check-box-config',
- // },
- // {
- // label: '切换题型',
- // type: 'Radio',
- // key: 'selectType',
- // direction: 'horizon',
- // value: '',
- // options: [
- // {
- // label: '单选',
- // value: 'radio',
- // },
- // {
- // label: '多选',
- // value: 'checkbox',
- // },
- // ],
- // contentClass: 'radio-config',
- // },
- // {
- // label: '排列方式',
- // type: 'Radio',
- // key: 'sortWay',
- // direction: 'horizon',
- // value: '',
- // options: [
- // {
- // label: '竖排',
- // value: 'v',
- // },
- // {
- // label: '横排',
- // value: 'h',
- // },
- // ],
- // contentClass: 'radio-config',
- // },
{
label: '至少选择数',
type: 'InputNumber',
key: 'minNum',
- direction: 'horizon',
value: '',
min: 0,
max: 'maxNum',
- contentClass: 'input-number-config',
+ contentClass: 'input-number-config'
},
{
label: '最多选择数',
type: 'InputNumber',
key: 'maxNum',
- direction: 'horizon',
value: '',
min: 'minNum',
- contentClass: 'input-number-config',
- },
- ],
- },
- // {
- // name: 'randomCheckbox',
- // label: '随机单个选项显示关联题目',
- // type: 'CustomedSwitch',
- // direction: 'horizon',
- // key: 'randomCheckbox',
- // },
+ contentClass: 'input-number-config'
+ }
+ ]
+ }
],
editConfigure: {
optionEdit: {
- show: true,
+ show: true
},
optionEditBar: {
show: true,
configure: {
showOthers: true,
- showAdvancedConfig: true,
- },
- },
- },
-};
+ showAdvancedConfig: true
+ }
+ }
+ }
+}
-export default meta;
+export default meta
diff --git a/web/src/materials/questions/widgets/EditOptions.jsx b/web/src/materials/questions/widgets/EditOptions.jsx
deleted file mode 100644
index fe6488cd..00000000
--- a/web/src/materials/questions/widgets/EditOptions.jsx
+++ /dev/null
@@ -1,156 +0,0 @@
-import { defineComponent, ref, computed, onMounted } from 'vue';
-import OptionEdit from '@/materials/questions/components/Options/OptionEdit.vue';
-import OptionEditBar from '@/materials/questions/components/Options/OptionEditBar.vue';
-import store from '@/management/store';
-import questionLoader from '@/materials/questions/questionLoader';
-import UseOptionBase from '@/materials/questions/components/Options/UseOptionBase';
-
-export default defineComponent({
- name: 'EditOptions',
- components: {
- OptionEdit,
- OptionEditBar,
- },
- provide() {
- return {
- currentEditKey: store.getters['edit/currentEditKey'],
- moduleConfig: computed(() => this.moduleConfig),
- };
- },
- props: {
- moduleConfig: {
- type: Object,
- required: true,
- },
- },
- setup(props) {
- const questionDataList = computed(() => {
- return store.state.schema.questionDataList;
- });
- const currentEditOne = computed(() => {
- return store.state?.edit?.currentEditOne;
- });
- const currentEditKey = computed(() => {
- return store.getters['edit/currentEditKey'];
- });
- const getOptions = computed(() => {
- return props.moduleConfig.options;
- });
- const { addOption, addOtherOption } = UseOptionBase(getOptions);
- const handleAddOption = (
- text = '选项',
- others = false,
- index = -1,
- field
- ) => {
- const value = addOption(text, others, index, field);
- handleOptionChange(value);
- };
- const handleAddOtherOption = () => {
- const { field } = props.moduleConfig;
- const value = addOtherOption(field);
- handleOptionChange(value);
- };
-
- const handleOptionChange = (value) => {
- const optionKey = `options`;
- const key = `${currentEditKey.value}.${optionKey}`;
- handleChange({ key, value });
- };
-
- const handleChange = ({ key, value }) => {
- store.dispatch('edit/changeSchema', { key, value });
- };
- // const questionMeta = ref({})
- const isShowOptionConfig = ref(false);
- const hasAdvancedConfig = ref(false);
- const hasAdvancedRateConfig = ref(false);
- const showOthers = ref(false);
- const showOptionEdit = ref(false);
- const showOptionEditBar = ref(true);
- onMounted(() => {
- const questionMeta = questionLoader.getMeta(props.moduleConfig.type);
- const { editConfigure } = questionMeta;
-
- if (editConfigure) {
- showOptionEdit.value = editConfigure.optionEdit.show;
- const { optionEditBar } = editConfigure;
- showOptionEditBar.value = optionEditBar.show;
- showOthers.value = optionEditBar.configure.showOthers;
- hasAdvancedConfig.value = Boolean(
- optionEditBar.configure.showAdvancedConfig
- );
- hasAdvancedRateConfig.value = Boolean(
- optionEditBar.configure.showAdvancedRateConfig
- );
- } else {
- // meta不存在的兜底程序
- if (
- ['radio-star', 'text', 'textarea'].includes(props.moduleConfig.type)
- ) {
- showOptionEdit.value = false;
- } else {
- showOptionEdit.value = true;
- }
- if (['radio-star'].includes(props.moduleConfig.type)) {
- showOthers.value = false;
- } else {
- showOthers.value = true;
- }
- if (['binary-choice'].includes(props.moduleConfig.type)) {
- showOptionEditBar.value = false;
- }
- if (!['radio-star'].includes(props.moduleConfig.type)) {
- hasAdvancedConfig.value = true;
- } else {
- hasAdvancedRateConfig.value = true;
- }
- }
- });
- return {
- questionDataList,
- currentEditOne,
- currentEditKey,
- getOptions,
- isShowOptionConfig,
- hasAdvancedConfig,
- hasAdvancedRateConfig,
- showOptionEdit,
- showOptionEditBar,
- showOthers,
- handleAddOption,
- handleAddOtherOption,
- handleOptionChange,
- handleChange,
- };
- },
- render() {
- return (
-
- {this.showOptionEdit ? (
-
- ) : (
- this.$slots.default
- )}
- {this.showOptionEditBar && (
-
- )}
-
- );
- },
-});
diff --git a/web/src/materials/questions/widgets/EditOptions/AdvancedConfig/OptionConfig.vue b/web/src/materials/questions/widgets/EditOptions/AdvancedConfig/OptionConfig.vue
new file mode 100644
index 00000000..8f9c9cac
--- /dev/null
+++ b/web/src/materials/questions/widgets/EditOptions/AdvancedConfig/OptionConfig.vue
@@ -0,0 +1,387 @@
+
+
+
+
+
+
+
+
+
+
+
+
changeOptionOthers(val, element)"
+ >
+
+
+ 必填
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 添加新选项
+
+
+
+
+
+ 其他____
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/materials/questions/components/AdvancedConfig/RateConfig.vue b/web/src/materials/questions/widgets/EditOptions/AdvancedConfig/RateConfig.vue
similarity index 64%
rename from web/src/materials/questions/components/AdvancedConfig/RateConfig.vue
rename to web/src/materials/questions/widgets/EditOptions/AdvancedConfig/RateConfig.vue
index 1cbeb6b2..4737b457 100644
--- a/web/src/materials/questions/components/AdvancedConfig/RateConfig.vue
+++ b/web/src/materials/questions/widgets/EditOptions/AdvancedConfig/RateConfig.vue
@@ -2,7 +2,7 @@
@@ -17,12 +17,7 @@
{{ item.index }}
-
+
@@ -32,117 +27,110 @@
v-model="item.text"
placeholder="提示文案"
/>
- 必填
-
+
+
+
-
diff --git a/web/src/materials/questions/widgets/EditOptions/Options/OptionEditBar.vue b/web/src/materials/questions/widgets/EditOptions/Options/OptionEditBar.vue
new file mode 100644
index 00000000..552c98e2
--- /dev/null
+++ b/web/src/materials/questions/widgets/EditOptions/Options/OptionEditBar.vue
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+ 其他____
+
+
+
+ 高级设置>
+
+
+
+ 高级评分设置>
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/materials/questions/components/Options/UseOptionBase.jsx b/web/src/materials/questions/widgets/EditOptions/Options/UseOptionBase.jsx
similarity index 51%
rename from web/src/materials/questions/components/Options/UseOptionBase.jsx
rename to web/src/materials/questions/widgets/EditOptions/Options/UseOptionBase.jsx
index 46f7ff74..02a644ef 100644
--- a/web/src/materials/questions/components/Options/UseOptionBase.jsx
+++ b/web/src/materials/questions/widgets/EditOptions/Options/UseOptionBase.jsx
@@ -1,15 +1,15 @@
-import { ref } from 'vue';
-import { cloneDeep } from 'lodash-es';
+import { ref } from 'vue'
+import { cloneDeep } from 'lodash-es'
-import GetHash from '../../common/utils/getOptionHash';
+import GetHash from '@materials/questions/common/utils/getOptionHash'
function useOptionBase(options) {
- const optionList = ref(options);
+ const optionList = ref(options)
const addOption = (text = '选项', others = false, index = -1, field) => {
// const {} = payload
- let addOne;
+ let addOne
if (optionList.value[0]) {
- addOne = cloneDeep(optionList.value[0]);
+ addOne = cloneDeep(optionList.value[0])
} else {
addOne = {
text: '',
@@ -20,41 +20,41 @@ function useOptionBase(options) {
othersKey: '',
placeholderDesc: '',
score: 0,
- limit: '',
- };
- }
- if (typeof text !== 'string') {
- text = '选项';
- }
-
- const getHash = new GetHash();
- addOne.hash = getHash.getHash();
- for (const i in addOne) {
- if (i === 'others') {
- addOne[i] = others;
- if (others) addOne.othersKey = `${field}_${addOne.hash}`;
- } else if (i === 'mustOthers') {
- addOne[i] = false;
- } else if (i === 'text') {
- addOne[i] = text;
+ limit: ''
}
}
- let myoptionList = cloneDeep(optionList.value);
- if (index < 0) {
- myoptionList.push(addOne);
- } else {
- myoptionList.splice(index + 1, 0, addOne);
+ if (typeof text !== 'string') {
+ text = '选项'
}
- return myoptionList;
- };
+
+ const getHash = new GetHash()
+ addOne.hash = getHash.getHash()
+ for (const i in addOne) {
+ if (i === 'others') {
+ addOne[i] = others
+ if (others) addOne.othersKey = `${field}_${addOne.hash}`
+ } else if (i === 'mustOthers') {
+ addOne[i] = false
+ } else if (i === 'text') {
+ addOne[i] = text
+ }
+ }
+ let myoptionList = cloneDeep(optionList.value)
+ if (index < 0) {
+ myoptionList.push(addOne)
+ } else {
+ myoptionList.splice(index + 1, 0, addOne)
+ }
+ return myoptionList
+ }
const addOtherOption = (field) => {
- return addOption('其他', true, -1, field);
- };
+ return addOption('其他', true, -1, field)
+ }
return {
addOption,
- addOtherOption,
- };
+ addOtherOption
+ }
}
-export default useOptionBase;
+export default useOptionBase
diff --git a/web/src/materials/questions/widgets/EditOptions/index.jsx b/web/src/materials/questions/widgets/EditOptions/index.jsx
new file mode 100644
index 00000000..a9c6f9c8
--- /dev/null
+++ b/web/src/materials/questions/widgets/EditOptions/index.jsx
@@ -0,0 +1,112 @@
+import { defineComponent, ref, computed, onMounted } from 'vue'
+
+import store from '@/management/store'
+
+import OptionEdit from './Options/OptionEdit.vue'
+import OptionEditBar from './Options/OptionEditBar.vue'
+import UseOptionBase from './Options/UseOptionBase'
+
+export default defineComponent({
+ name: 'EditOptions',
+ provide() {
+ return {
+ currentEditKey: store.getters['edit/currentEditKey'],
+ moduleConfig: computed(() => this.moduleConfig)
+ }
+ },
+ props: {
+ editConfigure: {
+ type: Object,
+ required: true
+ },
+ moduleConfig: {
+ type: Object,
+ required: true
+ }
+ },
+ setup(props, { slots }) {
+ const currentEditKey = computed(() => {
+ return store.getters['edit/currentEditKey']
+ })
+ const getOptions = computed(() => {
+ return props.moduleConfig.options
+ })
+ const { addOption, addOtherOption } = UseOptionBase(getOptions)
+ const handleAddOption = (text = '选项', others = false, index = -1, field) => {
+ const value = addOption(text, others, index, field)
+ handleOptionChange(value)
+ }
+ const handleAddOtherOption = () => {
+ const { field } = props.moduleConfig
+ const value = addOtherOption(field)
+ handleOptionChange(value)
+ }
+
+ const handleOptionChange = (value) => {
+ const optionKey = `options`
+ const key = `${currentEditKey.value}.${optionKey}`
+ handleChange({ key, value })
+ }
+
+ const handleChange = ({ key, value }) => {
+ store.dispatch('edit/changeSchema', { key, value })
+ }
+
+ const hasAdvancedConfig = ref(false)
+ const hasAdvancedRateConfig = ref(false)
+ const showOthers = ref(false)
+ const showOptionEdit = ref(true)
+ const showOptionEditBar = ref(true)
+ onMounted(() => {
+ const { optionEdit, optionEditBar } = props.editConfigure
+ showOptionEdit.value = optionEdit.show
+ showOptionEditBar.value = optionEditBar.show
+ showOthers.value = optionEditBar.configure.showOthers
+ hasAdvancedConfig.value = Boolean(optionEditBar.configure.showAdvancedConfig)
+ hasAdvancedRateConfig.value = Boolean(optionEditBar.configure.showAdvancedRateConfig)
+ })
+ return {
+ slots,
+ getOptions,
+ hasAdvancedConfig,
+ hasAdvancedRateConfig,
+ showOptionEdit,
+ showOptionEditBar,
+ showOthers,
+ handleAddOption,
+ handleAddOtherOption,
+ handleOptionChange,
+ handleChange
+ }
+ },
+ render() {
+ const { slots } = this
+ return (
+
+ {this.showOptionEdit ? (
+
+ ) : (
+ slots.default()
+ )}
+ {this.showOptionEditBar && (
+
+ )}
+
+ )
+ }
+})
diff --git a/web/src/materials/questions/widgets/InputModule/index.jsx b/web/src/materials/questions/widgets/InputModule/index.jsx
index 54028bfa..85d27455 100644
--- a/web/src/materials/questions/widgets/InputModule/index.jsx
+++ b/web/src/materials/questions/widgets/InputModule/index.jsx
@@ -1,9 +1,12 @@
-import baseInput from '../BaseInput';
-import { defineComponent, ref } from 'vue';
-import '../../common/css/input.scss';
-import { get } from 'lodash-es';
-import myMeta from './meta';
-export const meta = myMeta;
+import { defineComponent, ref } from 'vue'
+import { get } from 'lodash-es'
+
+import BaseInput from '../BaseInput'
+
+import './style.scss'
+import myMeta from './meta'
+
+export const meta = myMeta
/**
* 支持配置:
* 内容限制格式,valid
@@ -12,23 +15,22 @@ export const meta = myMeta;
*/
export default defineComponent({
name: 'InputModule',
- components: { baseInput },
props: {
type: {
type: String,
- default: '',
+ default: ''
},
field: {
type: String,
- default: '',
+ default: ''
},
value: {
type: String,
- default: '',
+ default: ''
},
placeholder: {
type: String,
- default: '请填写',
+ default: '请填写'
},
textRange: {
type: Object,
@@ -36,101 +38,102 @@ export default defineComponent({
return {
max: {
placeholder: '500',
- value: 500,
+ value: 500
},
min: {
placeholder: '0',
- value: 0,
- },
- };
- },
+ value: 0
+ }
+ }
+ }
},
valid: {
type: String,
- default: '',
+ default: ''
},
readonly: {
type: Boolean,
- default: false,
- },
+ default: false
+ }
},
+ emits: ['blur', 'focus', 'input', 'change'],
setup(props, { emit }) {
- const focusFlag = ref(false);
- const initial = props.textRange.max.value - props.value.length;
- const getLeftTextNumber = ref(initial);
+ const focusFlag = ref(false)
+ const initial = props.textRange.max.value - props.value.length
+ const getLeftTextNumber = ref(initial)
const onBlur = () => {
- emit('blur');
- };
+ emit('blur')
+ }
const onFocus = () => {
if (props.type !== 'mob' && (props.valid === '' || props.valid === 'n')) {
- focusFlag.value = true;
+ focusFlag.value = true
}
- emit('focus');
- };
+ emit('focus')
+ }
const onInput = (e) => {
if (props.type !== 'mob' && (props.valid === '' || props.valid === 'n')) {
- getLeftTextNumber.value =
- props.textRange.max.value - e.target.value.length;
+ getLeftTextNumber.value = props.textRange.max.value - e.target.value.length
}
- emit('input');
- };
+ emit('input')
+ }
const onChange = (e) => {
- const key = props.field;
- const maxLength = get(props, 'textRange.max.value');
+ const key = props.field
+ const maxLength = get(props, 'textRange.max.value')
if (
get(props, 'valid') === 'n' &&
maxLength &&
e.target.value.toString().length > maxLength
) {
- e.target.value = e.target.value.slice(0, props.textRange.max.value);
+ e.target.value = e.target.value.slice(0, props.textRange.max.value)
}
if (['m', 'idcard', 'e', 'licensePlate'].includes(props.valid)) {
- e.target.value = e.target.value.replace(/\s+/g, '');
+ e.target.value = e.target.value.replace(/\s+/g, '')
}
+
emit('change', {
key,
- value: e.target.value,
- });
- };
+ value: e.target.value
+ })
+ }
return {
+ props,
focusFlag,
getLeftTextNumber,
onBlur,
onFocus,
onInput,
- onChange,
- };
+ onChange
+ }
},
render() {
- const { readonly, focusFlag, getLeftTextNumber, field, valid, textRange } =
- this;
- const props = {
- ...this.$props,
- readonly,
- name: field,
- type: valid === 'n' ? 'number' : 'text',
- maxlength: textRange.max.value,
- minlength: textRange.min.value,
- };
+ const { focusFlag, getLeftTextNumber, valid, textRange, props } = this
+
return (
-
+
+
+
{focusFlag && (
)}
-
- );
- },
-});
+
+ )
+ }
+})
diff --git a/web/src/materials/questions/widgets/InputModule/meta.js b/web/src/materials/questions/widgets/InputModule/meta.js
index 95d83fd2..ef856e50 100644
--- a/web/src/materials/questions/widgets/InputModule/meta.js
+++ b/web/src/materials/questions/widgets/InputModule/meta.js
@@ -1,91 +1,47 @@
-import basicConfig from '../../common/config/basicConfig';
+import basicConfig from '@materials/questions/common/config/basicConfig'
export const meta = {
title: '单行输入框',
- questExtra: ['listenMerge'],
type: 'text',
componentName: 'InputModule',
formConfig: [
- // {
- // name: 'fieldId',
- // label: '题目ID',
- // type: 'Input',
- // placeholder: '',
- // key: 'field',
- // tip: '请谨慎修改题目ID,并保证此ID在整个问卷中唯一',
- // relyField: 'questionIdCustomize',
- // noNeedRelyClean: true,
- // },
- // {
- // name: 'questionLabel',
- // label: '题目标签',
- // type: 'Input',
- // placeholder: '',
- // key: 'label',
- // tip: '请谨慎修改题目标签,并保证此标签在整个问卷中唯一',
- // relyField: 'questionLabelCustomize',
- // noNeedRelyClean: true,
- // },
- // {
- // name: 'titleOrigin',
- // label: '标题引用其他题目选项',
- // type: 'TitleOrigin',
- // options: [],
- // key: 'title',
- // },
basicConfig,
{
name: 'valid',
- label: '内容限制格式',
- labelStyle: {
- 'font-weight': 'bold',
- },
- type: 'Select',
+ title: '内容限制格式',
+ type: 'SelectSetter',
key: 'valid',
options: [
{
label: '请选择',
- value: '',
+ value: ''
},
{
label: '手机号',
- value: 'm',
+ value: 'm'
},
{
label: '身份证',
- value: 'idcard',
+ value: 'idcard'
},
{
label: '数字',
- value: 'n',
+ value: 'n'
},
{
label: '邮箱',
- value: 'e',
+ value: 'e'
},
{
label: '车牌号',
- value: 'licensePlate',
- },
- ],
+ value: 'licensePlate'
+ }
+ ]
},
- // {
- // name: 'qQuestionLimit',
- // label: '相同填写内容提交次数',
- // type: 'QuestionLimit',
- // key: 'qQuestionLimit',
- // value: {
- // type: 0,
- // limit: 0,
- // },
- // },
{
name: 'numberRange',
- label: '数字限制',
- labelStyle: {
- 'font-weight': 'bold',
- },
- type: 'Range',
+ title: '数字限制',
+ type: 'RangeSetter',
options: [],
key: 'numberRange',
value: [],
@@ -93,65 +49,40 @@ export const meta = {
numberRange: {
min: {
placeholder: '0',
- value: 0,
+ value: 0
},
max: {
placeholder: '1000',
- value: 1000,
- },
- },
+ value: 1000
+ }
+ }
},
- relyFunc: (data) => data.valid && data.valid === 'n',
+ relyFunc: (data) => data.valid && data.valid === 'n'
},
{
name: 'textRange',
- label: '字数限制',
- labelStyle: {
- 'font-weight': 'bold',
- },
- type: 'Range',
+ title: '字数限制',
+ type: 'RangeSetter',
options: [],
key: 'textRange',
- value: [],
+ value: []
},
- // {
- // name: 'inputType',
- // label: '切换题型',
- // type: 'Radio',
- // key: 'type',
- // direction: 'horizon',
- // value: '',
- // options: [
- // {
- // label: '单行',
- // value: 'text',
- // },
- // {
- // label: '多行',
- // value: 'textarea',
- // },
- // ],
- // contentClass: 'radio-config',
- // },
{
name: 'placeholder',
- label: '引导提示文案',
- labelStyle: {
- 'font-weight': 'bold',
- },
- type: 'Input',
+ title: '引导提示文案',
+ type: 'InputSetter',
placeholder: '限制20字',
key: 'placeholder',
tip: '限制20字',
validate(value) {
if (value && value.length > 20) {
- console.warn('引导提示文案字数不能超过20个字,请修改后重新保存');
- return false;
+ console.warn('引导提示文案字数不能超过20个字,请修改后重新保存')
+ return false
}
- return true;
- },
- },
- ],
-};
+ return true
+ }
+ }
+ ]
+}
-export default meta;
+export default meta
diff --git a/web/src/materials/questions/widgets/InputModule/style.scss b/web/src/materials/questions/widgets/InputModule/style.scss
new file mode 100644
index 00000000..7e50f179
--- /dev/null
+++ b/web/src/materials/questions/widgets/InputModule/style.scss
@@ -0,0 +1,19 @@
+@import '@materials/questions/common/css/default.scss';
+
+.text-number-tip {
+ font-size: 0.24rem;
+ color: $font-color;
+ float: right;
+ margin-right: 0.1rem;
+ margin-top: 0.1rem;
+}
+
+input::-webkit-outer-spin-button,
+input::-webkit-inner-spin-button {
+ -webkit-appearance: none !important;
+ margin: 0;
+}
+
+input[type='number'] {
+ -moz-appearance: textfield;
+}
diff --git a/web/src/materials/questions/widgets/NpsModule/index.jsx b/web/src/materials/questions/widgets/NpsModule/index.jsx
new file mode 100644
index 00000000..a58780bc
--- /dev/null
+++ b/web/src/materials/questions/widgets/NpsModule/index.jsx
@@ -0,0 +1,160 @@
+import { defineComponent, computed, shallowRef, defineAsyncComponent } from 'vue'
+import BaseRate from '../BaseRate'
+import './style.scss'
+
+export default defineComponent({
+ name: 'NpsModule',
+ props: {
+ field: {
+ type: [String, Number],
+ default: ''
+ },
+ value: {
+ type: [String, Number],
+ default: ''
+ },
+ min: {
+ type: Number,
+ default: 1
+ },
+ max: {
+ type: Number,
+ default: 10
+ },
+ minMsg: {
+ type: String,
+ default: ''
+ },
+ maxMsg: {
+ type: String,
+ default: ''
+ },
+ readonly: {
+ type: Boolean,
+ default: false
+ },
+ rangeConfig: {
+ type: Object,
+ default: () => {
+ return {}
+ }
+ }
+ },
+ emits: ['change'],
+ setup(props, { emit }) {
+ const rating = computed({
+ get() {
+ return props.value
+ },
+ set(val) {
+ const key = props.field
+ emit('change', {
+ key,
+ value: val
+ })
+ }
+ })
+
+ const confirmNps = (num) => {
+ if (props.readonly) return
+ rating.value = num
+ }
+
+ const minMessage = computed(() => {
+ return props.minMsg || '极不满意'
+ })
+
+ const maxMessage = computed(() => {
+ return props.maxMsg || '十分满意'
+ })
+
+ const indexValue = computed(() => {
+ return props.value !== '' ? props.value : -1
+ })
+ const currentRangeConfig = computed(() => {
+ return props.rangeConfig[rating.value]
+ })
+ const isShowInput = computed(() => {
+ return currentRangeConfig.value?.isShowInput
+ })
+
+ const onMoreDataChange = (data) => {
+ const { key, value } = data
+ emit('change', {
+ key,
+ value
+ })
+ }
+
+ const selectMoreView = shallowRef(null)
+ if (props.readonly) {
+ selectMoreView.value = defineAsyncComponent(
+ () => import('@materials/questions/QuestionContainerB')
+ )
+ } else {
+ selectMoreView.value = defineAsyncComponent(
+ () => import('@materials/questions/QuestionRuleContainer')
+ )
+ }
+
+ return {
+ rating,
+ confirmNps,
+ minMessage,
+ maxMessage,
+ indexValue,
+ isShowInput,
+ onMoreDataChange,
+ selectMoreView
+ }
+ },
+ render() {
+ const {
+ field,
+ rating,
+ confirmNps,
+ indexValue,
+ minMessage,
+ maxMessage,
+ isShowInput,
+ min,
+ max,
+ readonly,
+ rangeConfig,
+ onMoreDataChange
+ } = this
+
+ return (
+
+
+
{minMessage}
+
{maxMessage}
+
+
+ {isShowInput && (
+ onMoreDataChange(e)}
+ >
+ )}
+
+
+ )
+ }
+})
diff --git a/web/src/materials/questions/widgets/NpsModule/index.vue b/web/src/materials/questions/widgets/NpsModule/index.vue
deleted file mode 100644
index ea4f2a2c..00000000
--- a/web/src/materials/questions/widgets/NpsModule/index.vue
+++ /dev/null
@@ -1,157 +0,0 @@
-
-
-
-
{{ minMsg }}
-
{{ maxMsg }}
-
-
-
-
-
-
-
diff --git a/web/src/materials/questions/widgets/NpsModule/meta.js b/web/src/materials/questions/widgets/NpsModule/meta.js
index 42c620fd..02c3f5bd 100644
--- a/web/src/materials/questions/widgets/NpsModule/meta.js
+++ b/web/src/materials/questions/widgets/NpsModule/meta.js
@@ -1,97 +1,81 @@
-import basicConfig from '../../common/config/basicConfig';
-import { Message } from 'element-ui';
+import { ElMessage } from 'element-plus'
+import basicConfig from '@materials/questions/common/config/basicConfig'
const meta = {
title: '评分',
- questExtra: ['listenMerge'],
type: 'radio-nps',
componentName: 'NpsModule',
formConfig: [
basicConfig,
{
name: 'min',
- label: 'NPS量表最小值',
- labelStyle: {
- 'font-weight': 'bold',
- },
+ title: 'NPS量表最小值',
contentClass: 'nps-select-config',
key: 'min',
- type: 'Select',
+ type: 'SelectSetter',
options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((v) => ({
value: v,
- label: v,
+ label: v
})),
valueSetter: (val, moduleConfig) => {
if (moduleConfig['max'] && val >= moduleConfig['max']) {
- Message({
- type: 'info',
- message: '最小值不可大于最大值',
- });
- return true;
+ ElMessage.info('最小值不可大于最大值')
+ return true
}
- },
+ }
},
{
name: 'max',
- label: 'NPS量表最大值',
- labelStyle: {
- 'font-weight': 'bold',
- },
+ title: 'NPS量表最大值',
key: 'max',
- type: 'Select',
+ type: 'SelectSetter',
contentClass: 'nps-select-config',
options: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((v) => ({
value: v,
- label: v,
+ label: v
})),
valueSetter: (val, moduleConfig) => {
if (moduleConfig['min'] && val <= moduleConfig['min']) {
- Message({
- type: 'info',
- message: '最大值不可小于最小值',
- });
- return true;
+ ElMessage.info('最大值不可小于最小值')
+ return true
}
- },
+ }
},
{
name: 'npsMsg',
- label: 'NPS两级文案',
- labelStyle: {
- 'font-weight': 'bold',
- },
+ title: 'NPS两级文案',
contentClass: 'nps-customed-config',
type: 'Customed',
content: [
{
label: '最小值文案',
- type: 'Input',
+ type: 'InputSetter',
key: 'minMsg',
- direction: 'horizon',
placeholder: '极不满意',
+ value: '极不满意'
},
{
label: '最大值文案',
- type: 'Input',
+ type: 'InputSetter',
key: 'maxMsg',
- direction: 'horizon',
placeholder: '十分满意',
- },
- ],
- },
+ value: '十分满意'
+ }
+ ]
+ }
],
editConfigure: {
optionEdit: {
- show: false,
+ show: false
},
optionEditBar: {
show: true,
configure: {
showOthers: false,
- showAdvancedRateConfig: true,
- },
- },
- },
-};
+ showAdvancedRateConfig: true
+ }
+ }
+ }
+}
-export default meta;
+export default meta
diff --git a/web/src/materials/questions/widgets/NpsModule/style.scss b/web/src/materials/questions/widgets/NpsModule/style.scss
new file mode 100644
index 00000000..ec2a82d1
--- /dev/null
+++ b/web/src/materials/questions/widgets/NpsModule/style.scss
@@ -0,0 +1,36 @@
+@import '@materials/questions/common/css/default.scss';
+
+.nps-wrapper-main {
+ .nps-row-msg {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 0.2rem;
+ .nps-msg {
+ flex: 1;
+ font-size: 0.22rem;
+ color: #92949d;
+ &.left {
+ text-align: left;
+ }
+ &.right {
+ text-align: right;
+ }
+ }
+ }
+ @media (max-width: 930px) {
+ :deep(.question-block) {
+ padding: 0;
+ }
+ }
+ .radio-nps-hover {
+ .rate-item {
+ &:hover {
+ background-color: $primary-color;
+ }
+ }
+ &:has(.rate-item:hover) .rate-item:not(:hover, :hover ~ *) {
+ background-color: $primary-color;
+ }
+ }
+}
diff --git a/web/src/materials/questions/widgets/QuestionContainer.jsx b/web/src/materials/questions/widgets/QuestionContainer.jsx
deleted file mode 100644
index a60a2282..00000000
--- a/web/src/materials/questions/widgets/QuestionContainer.jsx
+++ /dev/null
@@ -1,168 +0,0 @@
-import { computed, defineComponent, onMounted, ref } from 'vue';
-import moduleTitle from './Title.jsx';
-import EditOptions from './EditOptions.jsx';
-import moduleList from '../common/config/moduleList.js';
-import '../common/css/question.scss';
-
-import questionLoader from '@/materials/questions/questionLoader.js';
-import '../common/css/title.scss';
-
-export const getBlockComponent = async (type) => {
- const path = moduleList[type];
- const component = await questionLoader.loadComponent(type, path);
-
- return component;
-};
-
-export default defineComponent({
- name: 'QuestionContainer',
- props: {
- type: {
- type: String,
- default: 'text',
- },
- showTitle: {
- type: Boolean,
- default: true,
- },
- indexNumber: {
- type: [Number, String],
- default: 1,
- },
- moduleConfig: {
- type: Object,
- default: () => {
- return {
- field: 'quiestion01',
- type: 'text',
- component: 'InputModule',
- title: '标题1单行输入框',
- value: '123444',
- showType: 'text',
- placeholder: '请填写',
- textRange: {
- max: {
- placeholder: '500',
- value: 500,
- },
- min: {
- placeholder: '0',
- value: 0,
- },
- },
- valid: 'n',
- };
- },
- },
- readonly: {
- type: Boolean,
- default: false,
- },
- isSelected: {
- type: Boolean,
- default: false,
- },
- },
- components: {
- moduleTitle,
- },
- setup(props, { emit }) {
- const blockComponent = ref(null);
- const showEditCom = computed(() => {
- let result = false;
- if (props.isSelected) {
- if (!['text', 'textarea'].includes(props.type)) {
- result = true;
- }
- }
- return result;
- });
-
- // const isSelected = ref(false)
- onMounted(async () => {
- const { component } = await getBlockComponent(props.type);
- blockComponent.value = component;
- });
- const onBlur = () => {
- emit('blur');
- };
- const onFoucs = () => {
- emit('focus');
- };
- const onChange = (data) => {
- emit('change', data);
- };
- const onClick = () => {
- emit('select', props.indexNumber);
- };
- return {
- // isSelected,
- blockComponent,
- onClick,
- onBlur,
- onFoucs,
- onChange,
- showEditCom,
- // showOthers
- };
- },
- render(h) {
- const { readonly, isSelected } = this;
-
- const props = {
- isSelected,
- ...this.moduleConfig,
- ...this.$props,
- };
- return (
-
- {this.showTitle && (
-
- )}
-
- {this.showEditCom
- ? h(
- EditOptions,
- {
- props: {
- moduleConfig: this.moduleConfig,
- },
- },
- [
- h(
- this.blockComponent,
- {
- props: {
- readonly,
- ...props,
- },
- on: {
- blur: this.onBlur,
- focus: this.onFoucs,
- change: this.onChange,
- },
- },
- []
- ),
- ]
- )
- : h(
- this.blockComponent,
- {
- props: {
- readonly,
- ...props,
- },
- on: {
- blur: this.onBlur,
- focus: this.onFoucs,
- change: this.onChange,
- },
- },
- []
- )}
-
-
- );
- },
-});
diff --git a/web/src/materials/questions/widgets/QuestionRuleContainer.jsx b/web/src/materials/questions/widgets/QuestionRuleContainer.jsx
deleted file mode 100644
index df49e687..00000000
--- a/web/src/materials/questions/widgets/QuestionRuleContainer.jsx
+++ /dev/null
@@ -1,193 +0,0 @@
-import '../common/css/formItem.scss';
-import Vue, {
- defineComponent,
- getCurrentInstance,
- ref,
- nextTick,
- computed,
- inject,
- onMounted,
- onBeforeUnmount,
-} from 'vue';
-import QuestionContainer from '@/materials/questions/widgets/QuestionContainer.jsx';
-import ErrorTip from '../components/ErrorTip.vue';
-import { assign } from 'lodash-es';
-import AsyncValidator from 'async-validator';
-export default defineComponent({
- name: 'QuestionRuleContainer',
- components: { ErrorTip },
- props: {
- readonly: Boolean,
- showTitle: Boolean,
- moduleConfig: {
- type: Object,
- default: () => {
- return {};
- },
- },
- showSpliter: {
- type: Boolean,
- default: false,
- },
- indexNumber: {
- type: [Number, String],
- default: 1,
- },
- },
- setup(props, { emit }) {
- const validateMessage = ref('');
- const validateState = ref('');
- const { proxy: instance } = getCurrentInstance();
-
- const itemClass = computed(() => {
- const classList = [];
- classList.push(props.moduleConfig.direction);
- const type = props.moduleConfig.type;
- if (type) {
- if (type === 'section') {
- classList.push('question-type-section');
- }
- // if (includes(['radio', 'checkbox', 'vote', 'radio-star', 'binary-choice'], type)) {
- // classList.push('special')
- // }
-
- if (type === 'scroll') {
- classList.push('no-padding');
- }
- if (type === 'user-agreement') {
- classList.push('no-out-padding');
- }
- }
- return classList.join(' ');
- });
- const form = inject('Form', {
- default: {},
- type: Object,
- });
- const show = computed(() => {
- const { type } = props.moduleConfig;
- return !/hidden|mobileHidden/.test(type);
- });
- onMounted(() => {
- form.$emit && form.$emit('form.addField', instance);
- });
- onBeforeUnmount(() => {
- form.$emit && form.$emit('form.removeField', instance);
- });
- const validate = (trigger, callback = () => {}) => {
- const rules = getFilteredRule(trigger);
- if (!rules || rules.length === 0) {
- callback && callback();
- return true;
- }
-
- const { field, options } = props.moduleConfig;
-
- const descriptor = {};
- if (rules && rules.length > 0) {
- rules.forEach((rule) => {
- // eslint-disable-next-line no-param-reassign
- delete rule.trigger;
- });
- }
- // 有选项的题才需要判断这步,如果没有选项,就不校验,如果有,就要校验
- if (options && options.length) {
- const trueOptions = options.filter((i) => !i.hide);
- if (trueOptions.length === 0) {
- callback && callback();
- return true;
- }
- }
-
- descriptor[field] = rules;
- const validator = new AsyncValidator(descriptor);
- // 因为有些input的value是bind上去的,所以应该在下一帧再去校验,否则会出现第一次blur没反应
- nextTick(() => {
- // 对填空题单独设置其value
- let value = form.model[field];
- validator.validate(
- { [field]: value },
- { firstFields: true },
- (errors) => {
- validateState.value = !errors ? 'success' : 'error';
- validateMessage.value = errors ? errors[0].message : '';
- callback && callback(validateMessage.value);
- }
- );
- });
- };
- const onFieldBlur = () => {
- if (!(form && form instanceof Vue)) return;
- validate('blur');
- };
- // eslint-disable-next-line no-unused-vars
- const onFieldChange = () => {
- if (!(form && form instanceof Vue)) return;
- validate('change');
- };
- const getRules = () => {
- const { rules } = form;
- const { field } = props.moduleConfig;
- return rules[field];
- };
- const getFilteredRule = (trigger) => {
- let rules = getRules();
- if (!rules) {
- rules = [];
- }
- return rules
- .filter((rule) => !rule.trigger || rule.trigger.indexOf(trigger) !== -1)
- .map((rule) => assign({}, rule));
- };
- const handleBlur = () => {
- emit('blur');
- onFieldBlur();
- };
- const handleChange = (data) => {
- emit('change', data);
- onFieldChange();
- };
- return {
- validateMessage,
- validate,
- itemClass,
- show,
- onFieldBlur,
- onFieldChange,
- getRules,
- getFilteredRule,
- handleBlur,
- handleChange,
- };
- },
- render(h) {
- const { itemClass, validateMessage } = this;
- return (
-
- {h(QuestionContainer, {
- props: {
- type: this.moduleConfig.type,
- moduleConfig: this.moduleConfig,
- value: this.moduleConfig.value,
- indexNumber: this.indexNumber,
- showTitle: this.showTitle,
- readonly: this.readonly,
- },
- on: {
- ...this.$listeners,
- blur: this.handleBlur,
- change: this.handleChange,
- },
- })}
-
-
- );
- },
-});
diff --git a/web/src/materials/questions/widgets/RadioModule/index.jsx b/web/src/materials/questions/widgets/RadioModule/index.jsx
index 5b5e9f76..6985478c 100644
--- a/web/src/materials/questions/widgets/RadioModule/index.jsx
+++ b/web/src/materials/questions/widgets/RadioModule/index.jsx
@@ -1,6 +1,5 @@
-import baseChoice from '../BaseChoice';
-import { defineComponent } from 'vue';
-import QuestionWithRule from '@/materials/questions/widgets/QuestionRuleContainer';
+import { defineComponent, shallowRef, defineAsyncComponent } from 'vue'
+import BaseChoice from '../BaseChoice'
/**
* 支持配置:
@@ -8,100 +7,95 @@ import QuestionWithRule from '@/materials/questions/widgets/QuestionRuleContaine
*/
export default defineComponent({
name: 'RadioModule',
- components: { baseChoice, QuestionWithRule },
props: {
type: {
type: String,
- default: '',
+ default: ''
},
field: {
type: String,
- default: '',
+ default: ''
},
value: {
type: String,
- default: '',
+ default: ''
},
layout: {
type: String,
- default: 'vertical',
+ default: 'vertical'
},
options: {
type: Array,
- default: () => [],
+ default: () => []
},
readonly: {
type: Boolean,
- default: false,
- },
- // moduleConfig: {
- // type: Object,
- // default: () => {
- // return {
- // field: 'quiestion01',
- // component: 'RadioModule',
- // options: [{
- // text: '选项2',
- // hash: 'item1'
- // }, {
- // text: '选项2',
- // hash: 'item2'
- // }]
- // }
- // }
- // }
+ default: false
+ }
},
+ emits: ['change'],
setup(props, { emit }) {
const onChange = (value) => {
- const key = props.field;
+ const key = props.field
emit('change', {
key,
- value,
- });
- };
+ value
+ })
+ }
const handleSelectMoreChange = (data) => {
- const { key, value } = data;
+ const { key, value } = data
emit('change', {
key,
- value,
- });
- };
+ value
+ })
+ }
+ // 填写更多传入一个动态组件
+ const selectMoreView = shallowRef(null)
+ if (props.readonly) {
+ selectMoreView.value = defineAsyncComponent(
+ () => import('@materials/questions/QuestionContainerB')
+ )
+ } else {
+ selectMoreView.value = defineAsyncComponent(
+ () => import('@materials/questions/QuestionRuleContainer')
+ )
+ }
return {
onChange,
handleSelectMoreChange,
- };
+ selectMoreView
+ }
},
render() {
- const { readonly, field } = this;
- const props = {
- ...this.$props,
- readonly,
- name: field,
- };
+ const { selectMoreView } = this
+
return (
-
+ {{
selectMore: (scoped) => {
return (
- this.handleSelectMoreChange(e)}
- >
- );
- },
+ >
+ )
+ }
}}
- >
+
- );
- },
-});
+ )
+ }
+})
diff --git a/web/src/materials/questions/widgets/RadioModule/meta.js b/web/src/materials/questions/widgets/RadioModule/meta.js
index 045c51fb..20e8c6a9 100644
--- a/web/src/materials/questions/widgets/RadioModule/meta.js
+++ b/web/src/materials/questions/widgets/RadioModule/meta.js
@@ -1,208 +1,55 @@
-import basicConfig from '../../common/config/basicConfig';
+import basicConfig from '@materials/questions/common/config/basicConfig'
const meta = {
title: '单选',
- questExtra: ['listenMerge'],
type: 'radio',
+ componentName: 'RadioModule',
props: [
{
name: 'title',
propType: 'String',
description: '这是用于描述题目标题',
- defaultValue: '标题一',
+ defaultValue: '标题一'
},
{
name: 'type',
propType: 'String',
description: '这是用于描述题目类型',
- defaultValue: '标题一',
+ defaultValue: '标题一'
},
{
name: 'extraOptions',
propType: Array,
description: '这是用于固定选项配置',
- defaultValue: [],
- },
+ defaultValue: []
+ }
],
formConfig: [
- // {
- // name: 'fieldId',
- // label: '题目ID',
- // type: 'Input',
- // placeholder: '',
- // key: 'field',
- // tip: '请谨慎修改题目ID,并保证此ID在整个问卷中唯一',
- // relyField: 'questionIdCustomize',
- // noNeedRelyClean: true,
- // },
- // {
- // name: 'questionLabel',
- // label: '题目标签',
- // type: 'Input',
- // placeholder: '',
- // key: 'label',
- // tip: '请谨慎修改题目标签,并保证此标签在整个问卷中唯一',
- // relyField: 'questionLabelCustomize',
- // noNeedRelyClean: true,
- // },
- // {
- // name: 'titleOrigin',
- // label: '标题引用其他题目选项',
- // type: 'TitleOrigin',
- // options: [],
- // key: 'title',
- // },
basicConfig,
- // {
- // name: 'optionOrigin',
- // label: '选项引用其他题目选项',
- // type: 'Select',
- // options: [],
- // key: 'optionOrigin',
- // tip: '支持引用多选题、单选题、矩阵单选题',
- // },
- // {
- // name: 'originType',
- // label: '',
- // type: 'Select',
- // options: [
- // {
- // label: '选中项',
- // value: 'selected',
- // },
- // {
- // label: '未选中项',
- // value: 'noSelected',
- // },
- // ],
- // key: 'originType',
- // },
{
name: 'optionsExtra',
label: '固定选项配置',
labelStyle: {
- 'font-weight': 'bold',
+ 'font-weight': 'bold'
},
type: 'Options',
options: [],
keys: 'extraOptions',
- hidden: true,
- },
- // {
- // name: 'optionConfig',
- // label: '选项配置',
- // type: 'Customed',
- // key: 'optionConfig',
- // content: [
- // {
- // label: '选项随机排序',
- // type: 'CheckBox',
- // key: 'randomSort',
- // direction: 'horizon',
- // value: false,
- // contentPosition: 'before',
- // contentClass: 'check-box-config',
- // },
- // {
- // label: '固定最后一个选项',
- // type: 'CustomedSwitch',
- // key: 'pinLastOption',
- // direction: 'horizon',
- // value: false,
- // exclude: ['combination'],
- // contentClass: 'check-box-config',
- // tip: '开关开启后,本题的最后一个选项不参与随机,位置固定显示在最后',
- // },
- // {
- // label: '默认选中第一项',
- // type: 'CheckBox',
- // key: 'checked',
- // direction: 'horizon',
- // value: false,
- // contentPosition: 'before',
- // contentClass: 'check-box-config',
- // },
- // {
- // label: '展示选项余量',
- // type: 'CheckBox',
- // key: 'showLeftNum',
- // direction: 'horizon',
- // value: true,
- // include: ['appointment'],
- // contentPosition: 'before',
- // contentClass: 'check-box-config',
- // },
- // {
- // name: 'inputType',
- // label: '切换题型',
- // type: 'Radio',
- // key: 'selectType',
- // direction: 'horizon',
- // value: '',
- // options: [
- // {
- // label: '单选',
- // value: 'radio',
- // },
- // {
- // label: '多选',
- // value: 'checkbox',
- // },
- // ],
- // contentClass: 'radio-config',
- // },
- // {
- // label: '排列方式',
- // type: 'Radio',
- // key: 'sortWay',
- // direction: 'horizon',
- // value: '',
- // options: [
- // {
- // label: '竖排',
- // value: 'v',
- // },
- // {
- // label: '横排',
- // value: 'h',
- // },
- // ],
- // contentClass: 'radio-config',
- // },
- // {
- // label: '至少选择数',
- // type: 'InputNumber',
- // key: 'minNum',
- // direction: 'horizon',
- // value: '',
- // min: 0,
- // max: 'maxNum',
- // contentClass: 'input-number-config',
- // },
- // {
- // label: '最多选择数',
- // type: 'InputNumber',
- // key: 'maxNum',
- // direction: 'horizon',
- // value: '',
- // min: 'minNum',
- // contentClass: 'input-number-config',
- // },
- // ],
- // },
+ hidden: true
+ }
],
editConfigure: {
optionEdit: {
- show: true,
+ show: true
},
optionEditBar: {
show: true,
configure: {
showOthers: true,
- showAdvancedConfig: true,
- },
- },
- },
-};
+ showAdvancedConfig: true
+ }
+ }
+ }
+}
-export default meta;
+export default meta
diff --git a/web/src/materials/questions/widgets/SelectMoreModule/index.jsx b/web/src/materials/questions/widgets/SelectMoreModule/index.jsx
index 2ee24264..aaf7900b 100644
--- a/web/src/materials/questions/widgets/SelectMoreModule/index.jsx
+++ b/web/src/materials/questions/widgets/SelectMoreModule/index.jsx
@@ -1,51 +1,45 @@
-import { defineComponent } from 'vue';
-import baseInput from '../BaseInput';
+import { defineComponent } from 'vue'
+import BaseInput from '../BaseInput'
export default defineComponent({
name: 'SelectMoreData',
- components: { baseInput },
props: {
readonly: Boolean,
field: String,
placeholder: String,
- value: String,
+ value: String
},
+ emits: ['blur', 'change'],
setup(props, { emit }) {
- // const moduleConfig = ref({
- // type: 'text'
- // })
const onBlur = () => {
- emit('blur');
- };
+ emit('blur')
+ }
const onChange = (e) => {
- const key = props.field;
- const value = e.target.value;
+ const key = props.field
+ const value = e.target.value
emit('change', {
key,
- value,
- });
- };
+ value
+ })
+ }
return {
// moduleConfig,
onBlur,
- onChange,
- };
+ onChange
+ }
},
render() {
+ const props = this.$props
return (
-
- );
- },
-});
+ >
+ )
+ }
+})
diff --git a/web/src/materials/questions/widgets/StarModule/index.jsx b/web/src/materials/questions/widgets/StarModule/index.jsx
index a0e713fd..991a8179 100644
--- a/web/src/materials/questions/widgets/StarModule/index.jsx
+++ b/web/src/materials/questions/widgets/StarModule/index.jsx
@@ -1,89 +1,99 @@
-import { defineComponent, computed } from 'vue';
-import '../../common/css/radioStar.scss';
-import BaseRate from '../BaseRate';
-import QuestionWithRule from '@/materials/questions/widgets/QuestionRuleContainer';
+import { defineComponent, computed, shallowRef, defineAsyncComponent } from 'vue'
+import BaseRate from '../BaseRate'
+import './style.scss'
+
export default defineComponent({
name: 'StarModule',
- components: { BaseRate, QuestionWithRule },
props: {
type: {
type: String,
- default: '',
+ default: ''
},
field: {
type: String,
- default: '',
+ default: ''
},
value: {
type: [String, Number],
- default: 0,
+ default: 0
},
starMin: {
type: Number,
- default: 1,
+ default: 1
},
starMax: {
type: Number,
- default: 5,
+ default: 5
},
starStyle: {
type: String,
- default: 'star',
+ default: 'star'
},
readonly: {
type: Boolean,
- default: false,
+ default: false
},
rangeConfig: {
type: Object,
default: () => {
- return {};
- },
- },
+ return {}
+ }
+ }
},
+ emits: ['change'],
setup(props, { emit }) {
const rating = computed({
get() {
- return props.value;
+ return props.value
},
set(val) {
- const key = props.field;
+ const key = props.field
emit('change', {
key,
- value: val,
- });
- },
- });
+ value: val
+ })
+ }
+ })
const currentRangeConfig = computed(() => {
- return props.rangeConfig[rating.value];
- });
+ return props.rangeConfig[rating.value]
+ })
const isShowInput = computed(() => {
- return currentRangeConfig.value?.isShowInput;
- });
+ return currentRangeConfig.value?.isShowInput
+ })
const starClass = computed(() => {
- const { starStyle } = props;
+ const { starStyle } = props
switch (starStyle) {
case 'star':
- return 'qicon qicon-xingxing';
+ return 'qicon qicon-xingxing'
case 'love':
- return 'qicon qicon-aixin';
+ return 'qicon qicon-aixin'
case 'number':
- return 'number';
+ return 'number'
default:
- return 'qicon qicon-xingxing';
+ return 'qicon qicon-xingxing'
}
- });
+ })
const confirmStar = (num) => {
- if (props.readonly) return;
- rating.value = num;
- };
+ if (props.readonly) return
+ rating.value = num
+ }
const onMoreDataChange = (data) => {
- const { key, value } = data;
+ const { key, value } = data
emit('change', {
key,
- value,
- });
- };
+ value
+ })
+ }
+ const selectMoreView = shallowRef(null)
+ if (props.readonly) {
+ selectMoreView.value = defineAsyncComponent(
+ () => import('@materials/questions/QuestionContainerB')
+ )
+ } else {
+ selectMoreView.value = defineAsyncComponent(
+ () => import('@materials/questions/QuestionRuleContainer')
+ )
+ }
return {
rating,
currentRangeConfig,
@@ -91,7 +101,8 @@ export default defineComponent({
isShowInput,
confirmStar,
onMoreDataChange,
- };
+ selectMoreView
+ }
},
render() {
const {
@@ -104,7 +115,9 @@ export default defineComponent({
isShowInput,
onMoreDataChange,
rangeConfig,
- } = this;
+ selectMoreView,
+ confirmStar
+ } = this
return (
@@ -113,25 +126,23 @@ export default defineComponent({
value={value}
readonly={readonly}
iconClass={starClass}
- onChange={this.confirmStar}
+ onChange={confirmStar}
/>
- {currentRangeConfig && (
-
{currentRangeConfig.explain}
- )}
+ {currentRangeConfig &&
{currentRangeConfig.explain}
}
{isShowInput && (
-
onMoreDataChange(e)}
- >
+ >
)}
- );
- },
-});
+ )
+ }
+})
diff --git a/web/src/materials/questions/widgets/StarModule/meta.js b/web/src/materials/questions/widgets/StarModule/meta.js
index 19c20702..3d6ca731 100644
--- a/web/src/materials/questions/widgets/StarModule/meta.js
+++ b/web/src/materials/questions/widgets/StarModule/meta.js
@@ -1,118 +1,48 @@
-import basicConfig from '../../common/config/basicConfig';
+import basicConfig from '@materials/questions/common/config/basicConfig'
const meta = {
title: '评分',
- questExtra: ['listenMerge'],
type: 'radio-star',
componentName: 'StarModule',
formConfig: [
- // {
- // name: 'fieldId',
- // label: '题目ID',
- // type: 'Input',
- // placeholder: '',
- // key: 'field',
- // tip: '请谨慎修改题目ID,并保证此ID在整个问卷中唯一',
- // relyField: 'questionIdCustomize',
- // noNeedRelyClean: true
- // },
- // {
- // name: 'questionLabel',
- // label: '题目标签',
- // type: 'Input',
- // placeholder: '',
- // key: 'label',
- // tip: '请谨慎修改题目标签,并保证此标签在整个问卷中唯一',
- // relyField: 'questionLabelCustomize',
- // noNeedRelyClean: true
- // },
- // {
- // name: 'titleOrigin',
- // label: '标题引用其他题目选项',
- // type: 'TitleOrigin',
- // options: [],
- // key: 'title'
- // },
basicConfig,
{
name: 'starConfig',
- label: '评分显示样式',
- labelStyle: {
- 'font-weight': 'bold',
- },
+ title: '评分显示样式',
type: 'RadioGroup',
key: 'starStyle',
options: [
{
label: [1, 2, 3, 4, 5]
- .map(
- () =>
- `
`
- )
+ .map(() => `
`)
.join(''),
- value: 'star',
+ value: 'star'
},
{
label: [1, 2, 3, 4, 5]
- .map(
- () =>
- `
`
- )
+ .map(() => `
`)
.join(''),
- value: 'love',
+ value: 'love'
},
{
label: `1 2 3 4 5`,
- value: 'number',
- },
- ],
- },
- // {
- // name: 'starMin',
- // label: '评分最小值',
- // type: 'InputNumber',
- // key: 'starMin',
- // direction: 'horizon',
- // value: '',
- // min: data => Math.max(data.starMax - 10, -10),
- // max: 'starMax',
- // relyFunc: data => data.starStyle === 'number',
- // },
- // {
- // name: 'starMax',
- // label: '评分最大值',
- // type: 'InputNumber',
- // key: 'starMax',
- // direction: 'horizon',
- // value: '',
- // min: 'starMin',
- // max: data => Math.min(data.starMin + 10, 10),
- // relyFunc: data => data.starStyle === 'number',
- // },
- // {
- // name: 'starMaxSingle',
- // label: '评分最大值',
- // type: 'InputNumber',
- // key: 'starMax',
- // direction: 'horizon',
- // value: 5,
- // min: 1,
- // max: 10,
- // relyFunc: data => data.starStyle !== 'number',
- // },
+ value: 'number'
+ }
+ ]
+ }
],
editConfigure: {
optionEdit: {
- show: false,
+ show: false
},
optionEditBar: {
show: true,
configure: {
showOthers: false,
- showAdvancedRateConfig: true,
- },
- },
- },
-};
+ showAdvancedRateConfig: true
+ }
+ }
+ }
+}
-export default meta;
+export default meta
diff --git a/web/src/materials/questions/widgets/StarModule/style.scss b/web/src/materials/questions/widgets/StarModule/style.scss
new file mode 100644
index 00000000..f94143f1
--- /dev/null
+++ b/web/src/materials/questions/widgets/StarModule/style.scss
@@ -0,0 +1,24 @@
+@import '@materials/questions/common/css/default.scss';
+
+.explain {
+ line-height: 0.48rem;
+ font-size: 0.24rem;
+ color: #92949d;
+ padding: 0 0.4rem;
+}
+
+.radio-star {
+ &.isPc {
+ margin-bottom: 0.2rem;
+ }
+}
+
+.qicon {
+ // color: #e3e4e8;
+ &.on {
+ color: $primary-color;
+ }
+ &.off {
+ color: #e3e4e8;
+ }
+}
diff --git a/web/src/materials/questions/widgets/TextareaModule/index.jsx b/web/src/materials/questions/widgets/TextareaModule/index.jsx
index d376c48f..e8ef947c 100644
--- a/web/src/materials/questions/widgets/TextareaModule/index.jsx
+++ b/web/src/materials/questions/widgets/TextareaModule/index.jsx
@@ -1,9 +1,12 @@
-import baseInput from '../BaseInput';
-import { defineComponent, ref } from 'vue';
-import '../../common/css/input.scss';
-import { get } from 'lodash-es';
-import myMeta from './meta';
-export const meta = myMeta;
+import { defineComponent, ref } from 'vue'
+import { get } from 'lodash-es'
+
+import BaseInput from '../BaseInput'
+import myMeta from './meta'
+
+import './style.scss'
+
+export const meta = myMeta
/**
* 支持配置:
* 内容限制格式,valid
@@ -12,17 +15,16 @@ export const meta = myMeta;
*/
export default defineComponent({
name: 'TextareaModule',
- components: { baseInput },
props: {
type: {},
field: {},
value: {
type: String,
- default: '',
+ default: ''
},
placeholder: {
type: String,
- default: '请填写',
+ default: '请填写'
},
textRange: {
type: Object,
@@ -30,101 +32,101 @@ export default defineComponent({
return {
max: {
placeholder: '500',
- value: 500,
+ value: 500
},
min: {
placeholder: '0',
- value: 0,
- },
- };
- },
+ value: 0
+ }
+ }
+ }
},
valid: {
type: String,
- default: '',
+ default: ''
},
readonly: {
type: Boolean,
- default: false,
- },
+ default: false
+ }
},
+ emits: ['change', 'blur', 'focus', 'input'],
setup(props, { emit }) {
- const focusFlag = ref(false);
- const initial = props.textRange.max.value - props.value.length;
- const getLeftTextNumber = ref(initial);
+ const focusFlag = ref(false)
+ const initial = props.textRange.max.value - props.value.length
+ const getLeftTextNumber = ref(initial)
const onFocus = () => {
- if (props.readonly) return;
+ if (props.readonly) return
if (props.type !== 'mob' && (props.valid === '' || props.valid === 'n')) {
- focusFlag.value = true;
+ focusFlag.value = true
}
- emit('focus');
- };
+ emit('focus')
+ }
const onBlur = () => {
- emit('blur');
- };
+ emit('blur')
+ }
const onInput = (e) => {
if (props.type !== 'mob' && (props.valid === '' || props.valid === 'n')) {
- getLeftTextNumber.value =
- props.textRange.max.value - e.target.value.length;
+ getLeftTextNumber.value = props.textRange.max.value - e.target.value.length
}
- emit('input');
- };
+ emit('input')
+ }
const onChange = (e) => {
- const key = props.field;
- const maxLength = get(props, 'textRange.max.value');
+ const key = props.field
+ const maxLength = get(props, 'textRange.max.value')
if (
get(props, 'valid') === 'n' &&
maxLength &&
e.target.value.toString().length > maxLength
) {
- e.target.value = e.target.value.slice(0, props.textRange.max.value);
+ e.target.value = e.target.value.slice(0, props.textRange.max.value)
}
if (['m', 'idcard', 'e', 'licensePlate'].includes(props.valid)) {
- e.target.value = e.target.value.replace(/\s+/g, '');
+ e.target.value = e.target.value.replace(/\s+/g, '')
}
emit('change', {
key,
- value: e.target.value,
- });
- };
+ value: e.target.value
+ })
+ }
return {
focusFlag,
getLeftTextNumber,
onBlur,
onFocus,
onInput,
- onChange,
- };
+ onChange
+ }
},
render() {
- const { focusFlag, getLeftTextNumber, field, textRange } = this;
+ const { focusFlag, getLeftTextNumber, field, textRange } = this
const props = {
...this.$props,
value: this.value,
name: field,
maxlength: textRange.max.value,
- minlength: textRange.min.value,
- };
+ minlength: textRange.min.value
+ }
return (
-
+
+
+
{focusFlag && (
)}
-
- );
- },
-});
+
+ )
+ }
+})
diff --git a/web/src/materials/questions/widgets/TextareaModule/meta.js b/web/src/materials/questions/widgets/TextareaModule/meta.js
index 6a95858a..73f39d26 100644
--- a/web/src/materials/questions/widgets/TextareaModule/meta.js
+++ b/web/src/materials/questions/widgets/TextareaModule/meta.js
@@ -1,90 +1,47 @@
-import basicConfig from '../../common/config/basicConfig';
+import basicConfig from '@materials/questions/common/config/basicConfig'
const meta = {
title: '多行输入框',
type: 'textarea',
componentName: 'TextareaModule',
formConfig: [
- // {
- // name: 'fieldId',
- // label: '题目ID',
- // type: 'Input',
- // placeholder: '',
- // key: 'field',
- // tip: '请谨慎修改题目ID,并保证此ID在整个问卷中唯一',
- // relyField: 'questionIdCustomize',
- // noNeedRelyClean: true
- // },
- // {
- // name: 'questionLabel',
- // label: '题目标签',
- // type: 'Input',
- // placeholder: '',
- // key: 'label',
- // tip: '请谨慎修改题目标签,并保证此标签在整个问卷中唯一',
- // relyField: 'questionLabelCustomize',
- // noNeedRelyClean: true
- // },
- // {
- // name: 'titleOrigin',
- // label: '标题引用其他题目选项',
- // type: 'TitleOrigin',
- // options: [],
- // key: 'title'
- // },
basicConfig,
{
name: 'valid',
- label: '内容限制格式',
- labelStyle: {
- 'font-weight': 'bold',
- },
- type: 'Select',
+ title: '内容限制格式',
+ type: 'SelectSetter',
key: 'valid',
options: [
{
label: '请选择',
- value: '',
+ value: ''
},
{
label: '手机号',
- value: 'm',
+ value: 'm'
},
{
label: '身份证',
- value: 'idcard',
+ value: 'idcard'
},
{
label: '数字',
- value: 'n',
+ value: 'n'
},
{
label: '邮箱',
- value: 'e',
+ value: 'e'
},
{
label: '车牌号',
- value: 'licensePlate',
- },
- ],
+ value: 'licensePlate'
+ }
+ ]
},
- // {
- // name: 'qQuestionLimit',
- // label: '相同填写内容提交次数',
- // type: 'QuestionLimit',
- // key: 'qQuestionLimit',
- // value: {
- // type: 0,
- // limit: 0
- // }
- // },
{
name: 'numberRange',
- label: '数字限制',
- labelStyle: {
- 'font-weight': 'bold',
- },
- type: 'Range',
+ title: '数字限制',
+ type: 'RangeSetter',
options: [],
key: 'numberRange',
value: [],
@@ -92,63 +49,38 @@ const meta = {
numberRange: {
min: {
placeholder: '0',
- value: 0,
+ value: 0
},
max: {
placeholder: '1000',
- value: 1000,
- },
- },
+ value: 1000
+ }
+ }
},
- relyFunc: (data) => data.valid && data.valid === 'n',
+ relyFunc: (data) => data.valid && data.valid === 'n'
},
{
name: 'textRange',
- label: '字数限制',
- labelStyle: {
- 'font-weight': 'bold',
- },
- type: 'Range',
+ title: '字数限制',
+ type: 'RangeSetter',
options: [],
key: 'textRange',
- value: [],
+ value: []
},
- // {
- // name: 'inputType',
- // label: '切换题型',
- // type: 'Radio',
- // key: 'type',
- // direction: 'horizon',
- // value: '',
- // options: [
- // {
- // label: '单行',
- // value: 'text'
- // },
- // {
- // label: '多行',
- // value: 'textarea'
- // }
- // ],
- // contentClass: 'radio-config'
- // },
{
name: 'placeholder',
- label: '引导提示文案',
- labelStyle: {
- 'font-weight': 'bold',
- },
- type: 'Input',
+ title: '引导提示文案',
+ type: 'InputSetter',
placeholder: '限制20字',
key: 'placeholder',
tip: '限制20字',
validate(value) {
if (value && value.length > 20) {
- return false;
+ return false
}
- return true;
- },
- },
- ],
-};
-export default meta;
+ return true
+ }
+ }
+ ]
+}
+export default meta
diff --git a/web/src/materials/questions/widgets/TextareaModule/style.scss b/web/src/materials/questions/widgets/TextareaModule/style.scss
new file mode 100644
index 00000000..576225c9
--- /dev/null
+++ b/web/src/materials/questions/widgets/TextareaModule/style.scss
@@ -0,0 +1,11 @@
+.text-number-tip {
+ font-size: 0.24rem;
+ color: $font-color;
+ float: right;
+ margin-right: 0.1rem;
+ margin-top: 0.1rem;
+}
+
+textarea {
+ resize: none;
+}
diff --git a/web/src/materials/questions/widgets/Title.jsx b/web/src/materials/questions/widgets/Title.jsx
deleted file mode 100644
index cefa5568..00000000
--- a/web/src/materials/questions/widgets/Title.jsx
+++ /dev/null
@@ -1,127 +0,0 @@
-import { defineComponent, ref, computed, watch } from 'vue';
-import { filterXSS } from '@/common/xss';
-import '../common/css/title.scss';
-import tagList from '../common/config/tagList';
-import richEditor from '@/common/Editor/RichEditor';
-
-export default defineComponent({
- name: 'ModuleTitle',
- components: { richEditor },
- props: {
- isSelected: {
- type: Boolean,
- default: false,
- },
- showIndex: {
- type: Boolean,
- default: false,
- },
- indexNumber: {
- type: [Number, String],
- default: '',
- },
- isRequired: {
- type: Boolean,
- default: true,
- },
- title: {
- type: String,
- default: '标题',
- },
- showType: {
- type: Boolean,
- default: false,
- },
- type: {
- type: String,
- default: '',
- },
- },
- setup(props, { emit }) {
- const status = ref('');
- watch(
- () => props.isSelected,
- () => {
- status.value = 'preview';
- }
- );
- const typeName = computed(() => {
- if (!props.showType) return '';
- const types = props.showType ? [props.type] : [];
- if (!types || !types.length) return '';
- let ret = '';
- types.forEach((t) => {
- if (ret) return;
- const tv = tagList && tagList[t];
- if (tv && typeof tv === 'string') {
- ret = tv.trim();
- }
- });
- return ret;
- });
- const tagTitle = computed(() => {
- let htmlText = '';
- htmlText += filterXSS(props.title);
- htmlText = `${htmlText}`;
- if (typeName.value) {
- const index = htmlText.lastIndexOf('');
- if (index > -1) {
- htmlText =
- htmlText.slice(0, index) +
- `${typeName.value}` +
- htmlText.slice(index);
- } else {
- htmlText = htmlText + `${typeName.value}`;
- }
- }
- return htmlText;
- });
- const handleClick = () => {
- if (props.isSelected) {
- status.value = 'edit';
- }
- };
- const handleChange = (val) => {
- emit('change', {
- key: 'title',
- value: val,
- });
- };
- return {
- status,
- typeName,
- tagTitle,
- handleClick,
- handleChange,
- };
- },
- render() {
- const { isRequired, tagTitle, indexNumber } = this;
- return (
-
-
- {isRequired &&
*}
-
- {this.showIndex &&
{indexNumber}.}
- {this.status === 'edit' ? (
-
-
-
- ) : (
-
- )}
-
-
-
- );
- },
-});
diff --git a/web/src/materials/questions/widgets/TitleModules/EditTitle/index.jsx b/web/src/materials/questions/widgets/TitleModules/EditTitle/index.jsx
new file mode 100644
index 00000000..29abab89
--- /dev/null
+++ b/web/src/materials/questions/widgets/TitleModules/EditTitle/index.jsx
@@ -0,0 +1,80 @@
+import { defineComponent } from 'vue'
+import { filterXSS } from '@/common/xss'
+import RichEditor from '@/common/Editor/RichEditor.vue'
+
+import TitleContent from '../TitleContent'
+
+import './style.scss'
+
+export default defineComponent({
+ name: 'EditTitle',
+ props: {
+ isSelected: {
+ type: Boolean,
+ default: false
+ },
+ showIndex: {
+ type: Boolean,
+ default: false
+ },
+ indexNumber: {
+ type: [Number, String],
+ default: ''
+ },
+ isRequired: {
+ type: Boolean,
+ default: true
+ },
+ title: {
+ type: String,
+ default: '标题'
+ },
+ showType: {
+ type: Boolean,
+ default: false
+ },
+ type: {
+ type: String,
+ default: ''
+ }
+ },
+ emits: ['change'],
+ setup(props, { emit }) {
+ const handleChange = (val) => {
+ emit('change', {
+ key: 'title',
+ value: val
+ })
+ }
+ return {
+ handleChange
+ }
+ },
+ render() {
+ const { isRequired, indexNumber, title, showType, type, isSelected, showIndex } = this
+
+ return (
+
+ {{
+ edit: () => {
+ return (
+
+ )
+ }
+ }}
+
+ )
+ }
+})
diff --git a/web/src/materials/questions/widgets/TitleModules/EditTitle/style.scss b/web/src/materials/questions/widgets/TitleModules/EditTitle/style.scss
new file mode 100644
index 00000000..a3cd8de5
--- /dev/null
+++ b/web/src/materials/questions/widgets/TitleModules/EditTitle/style.scss
@@ -0,0 +1,9 @@
+.rich-editor {
+ position: relative;
+ cursor: pointer;
+ width: 100%;
+ margin: -10px 0;
+ flex: 1;
+ font-size: 0.32rem;
+ background-color: #fff;
+}
diff --git a/web/src/materials/questions/widgets/TitleModules/PreviewTitle/index.jsx b/web/src/materials/questions/widgets/TitleModules/PreviewTitle/index.jsx
new file mode 100644
index 00000000..80b6c487
--- /dev/null
+++ b/web/src/materials/questions/widgets/TitleModules/PreviewTitle/index.jsx
@@ -0,0 +1,48 @@
+import { defineComponent } from 'vue'
+
+import TitleContent from '../TitleContent'
+
+export default defineComponent({
+ name: 'PreviewTitle',
+ props: {
+ indexNumber: {
+ type: [Number, String],
+ default: ''
+ },
+ showIndex: {
+ type: Boolean,
+ default: false
+ },
+ isRequired: {
+ type: Boolean,
+ default: true
+ },
+ title: {
+ type: String,
+ default: ''
+ },
+ showType: {
+ type: Boolean,
+ default: false
+ },
+ type: {
+ type: String,
+ default: ''
+ }
+ },
+ setup() {},
+ render() {
+ const { isRequired, indexNumber, title, showType, type, showIndex } = this
+
+ return (
+
+ )
+ }
+})
diff --git a/web/src/materials/questions/widgets/TitleModules/TitleContent/index.jsx b/web/src/materials/questions/widgets/TitleModules/TitleContent/index.jsx
new file mode 100644
index 00000000..8bd47f3a
--- /dev/null
+++ b/web/src/materials/questions/widgets/TitleModules/TitleContent/index.jsx
@@ -0,0 +1,106 @@
+import { defineComponent, watch, ref, computed } from 'vue'
+import { filterXSS } from '@/common/xss'
+import tagList from '@materials/questions/common/config/tagList'
+
+import './style.scss'
+
+export default defineComponent({
+ name: 'TitleContent',
+ props: {
+ isSelected: {
+ type: Boolean,
+ default: false
+ },
+ showIndex: {
+ type: Boolean,
+ default: false
+ },
+ indexNumber: {
+ type: [Number, String],
+ default: ''
+ },
+ isRequired: {
+ type: Boolean,
+ default: true
+ },
+ title: {
+ type: String,
+ default: '标题'
+ },
+ showType: {
+ type: Boolean,
+ default: false
+ },
+ type: {
+ type: String,
+ default: ''
+ }
+ },
+ setup(props, { slots }) {
+ const status = ref('')
+ watch(
+ () => props.isSelected,
+ () => {
+ status.value = 'preview'
+ }
+ )
+
+ const handleClick = () => {
+ if (props.isSelected) {
+ status.value = 'edit'
+ }
+ }
+
+ const typeName = computed(() => {
+ if (!props.showType) return ''
+ const types = props.showType ? [props.type] : []
+ if (!types || !types.length) return ''
+ let ret = ''
+ types.forEach((t) => {
+ if (ret) return
+ const tv = tagList && tagList[t]
+ if (tv && typeof tv === 'string') {
+ ret = tv.trim()
+ }
+ })
+ return ret
+ })
+
+ const tagTitle = computed(() => {
+ let htmlText = ''
+ htmlText += filterXSS(props.title)
+ htmlText = `${htmlText}`
+ if (typeName.value) {
+ const index = htmlText.lastIndexOf('')
+ if (index > -1) {
+ htmlText =
+ htmlText.slice(0, index) +
+ `${typeName.value}` +
+ htmlText.slice(index)
+ } else {
+ htmlText = htmlText + `${typeName.value}`
+ }
+ }
+ return htmlText
+ })
+
+ return { slots, handleClick, status, tagTitle }
+ },
+ render() {
+ const { isRequired, indexNumber, slots, status, tagTitle } = this
+
+ return (
+
+ {isRequired &&
*}
+
+ {this.showIndex &&
{indexNumber}.}
+ {status === 'edit' && slots.edit ? (
+ slots.edit()
+ ) : (
+
+ )}
+
+
+ )
+ }
+})
diff --git a/web/src/materials/questions/widgets/TitleModules/TitleContent/style.scss b/web/src/materials/questions/widgets/TitleModules/TitleContent/style.scss
new file mode 100644
index 00000000..dea76e98
--- /dev/null
+++ b/web/src/materials/questions/widgets/TitleModules/TitleContent/style.scss
@@ -0,0 +1,59 @@
+@import '@materials/questions/common/css/default.scss';
+
+.module-title {
+ position: relative;
+ height: auto;
+ line-height: 0.45rem;
+ font-size: $title-size;
+ padding: 0 0.4rem 0.4rem;
+ color: #000000;
+ font-weight: normal;
+ &-required {
+ color: $error-color;
+ position: absolute;
+ left: 0.16rem;
+ top: 0rem;
+ font-size: 0.2rem;
+ transform-origin: left top;
+ }
+
+ .module-content {
+ zoom: 1;
+ display: flex;
+ font-size: $title-size;
+ img {
+ max-width: 100%;
+ }
+ .index {
+ float: left;
+ padding-right: 0.06rem;
+ font-size: $title-size;
+ }
+ em {
+ font-style: italic;
+ }
+ u {
+ text-decoration: underline;
+ }
+ }
+
+ .module-text {
+ font-size: $title-size;
+ p,
+ span {
+ font-size: inherit;
+ }
+
+ .m-tag {
+ border: 1px solid $primary-color;
+ font-size: 0.18rem;
+ height: 0.4rem;
+ line-height: 0.4rem;
+ display: inline-block;
+ color: $primary-color;
+ padding: 0 0.16rem;
+ margin-left: 0.16rem;
+ border-radius: 0 0.06rem 0 0.06rem;
+ }
+ }
+}
diff --git a/web/src/materials/questions/components/Progress.vue b/web/src/materials/questions/widgets/VoteModule/AnswerProcess/index.vue
similarity index 70%
rename from web/src/materials/questions/components/Progress.vue
rename to web/src/materials/questions/widgets/VoteModule/AnswerProcess/index.vue
index ddd82d5c..5ca22779 100644
--- a/web/src/materials/questions/components/Progress.vue
+++ b/web/src/materials/questions/widgets/VoteModule/AnswerProcess/index.vue
@@ -3,13 +3,13 @@
class="process-outer"
:style="{
width: processConf.width || 'auto',
- height: processConf.height || '0.03rem',
+ height: processConf.height || '0.03rem'
}"
>
@@ -24,23 +24,24 @@ export default {
*/
processConf: {
type: Object,
- required: true,
- },
+ required: true
+ }
},
computed: {},
methods: {
calcVotePercent(option) {
- const { voteCount = 0, voteTotal = 0 } = option;
+ const { voteCount = 0, voteTotal = 0 } = option
if (voteTotal === 0) {
- return '0.0';
+ return '0.0'
}
- return ((voteCount * 100) / voteTotal).toFixed(1);
- },
- },
-};
+ return ((voteCount * 100) / voteTotal).toFixed(1)
+ }
+ }
+}
-
diff --git a/web/src/materials/setters/widgets/ColorInput.vue b/web/src/materials/setters/widgets/ColorInput.vue
index 247d7bdc..0495409a 100644
--- a/web/src/materials/setters/widgets/ColorInput.vue
+++ b/web/src/materials/setters/widgets/ColorInput.vue
@@ -6,28 +6,28 @@
:maxLength="formConfig.maxlength"
@change="changeData"
>
-