parent
95630f1507
commit
9e8048be97
@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div class="course-answer-field">
|
||||
<el-input v-if="field.readonly" :value="field.value" type="textarea" disabled />
|
||||
<el-select v-else-if="['radio', 'checkbox'].includes(field.edit_input)" :disabled="!!field.readonly" :value="selection" :multiple="field.edit_input === 'checkbox'" :multiple-limit="field.config.max_choices || 0" clearable filterable :placeholder="field.help || '请选择'" @change="choose">
|
||||
<el-option v-for="option in field.select_item" :key="option.key" :value="String(option.key)" :label="String(option.value)" />
|
||||
</el-select>
|
||||
<el-input v-else-if="field.edit_input !== 'date' && field.edit_input !== 'datetime'" v-model="field.value" :disabled="!!field.readonly" :type="field.edit_input === 'textarea' ? 'textarea' : 'text'" :placeholder="field.help || '请输入'" />
|
||||
<el-date-picker v-else v-model="field.value" :disabled="!!field.readonly" :type="field.edit_input" :value-format="field.edit_input === 'date' ? 'yyyy-MM-dd' : 'yyyy-MM-dd HH:mm:ss'" :placeholder="field.help || '请选择'" />
|
||||
<div v-if="field.edit_input === 'checkbox'" class="hint">{{ hint(field) }}</div>
|
||||
<div v-for="option in notes(field)" :key="option.key" class="note">
|
||||
<div>{{ option.value }} · 补充说明<span v-if="field.config.option_notes[option.key].required" class="required"> *</span></div>
|
||||
<el-input v-model="field.option_texts[option.key]" type="textarea" :placeholder="field.config.option_notes[option.key].placeholder || '请填写补充说明'" />
|
||||
</div>
|
||||
<div v-if="hasOther" class="note"><div>补充说明<span v-if="field.config.other_required" class="required"> *</span></div><el-input v-model="field.other_text" type="textarea" :placeholder="field.config.other_placeholder || '请填写补充说明'" /></div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { courseSelected, courseNotes, courseChoiceHint, changeCourseChoice } from '@/utils/courseForm'
|
||||
export default {
|
||||
props: { field: { type: Object, required: true }},
|
||||
computed: {
|
||||
selection() { const keys = courseSelected(this.field); return this.field.edit_input === 'checkbox' ? keys : (keys[0] || '') },
|
||||
hasOther() { return this.field.config.other_key && courseSelected(this.field).includes(String(this.field.config.other_key)) }
|
||||
},
|
||||
methods: {
|
||||
hint: courseChoiceHint, notes: courseNotes,
|
||||
choose(value) {
|
||||
const keys = (Array.isArray(value) ? value : [value]).map(String)
|
||||
changeCourseChoice(this.field, this.field.select_item.filter(option => keys.includes(String(option.key))).map(option => option.value))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.el-select, .el-date-editor { width: 100%; }
|
||||
.hint { font-size: 12px; color: #909399; margin-top: 6px; }
|
||||
.note { padding-top: 12px; white-space: normal; }
|
||||
.required { color: #f56c6c; }
|
||||
</style>
|
||||
@ -0,0 +1,115 @@
|
||||
import { shallowMount, createLocalVue } from '@vue/test-utils'
|
||||
import ElementUI from 'element-ui'
|
||||
import Rules from '@/views/course/components/CourseFieldRules.vue'
|
||||
import Answer from '@/views/course/components/CourseAnswerField.vue'
|
||||
import SurveySettings from '@/views/survey/components/SurveyFieldSettings.vue'
|
||||
import CourseEditor from '@/views/course/components/editPane.vue'
|
||||
import { save as saveCourseField } from '@/api/course/form'
|
||||
import { prepareCourseFields, visibleCourseFields, courseFormError, courseSubmission } from '@/utils/courseForm'
|
||||
|
||||
jest.mock('@/api/course/form', () => ({ save: jest.fn(() => Promise.resolve({ id: 7 })), destroy: jest.fn() }))
|
||||
|
||||
const localVue = createLocalVue()
|
||||
localVue.use(ElementUI)
|
||||
function mountForm(component, props, mocks = {}) {
|
||||
const state = new localVue({ data() { return { fields: props } } })
|
||||
return shallowMount(component, { localVue, propsData: state.fields, mocks })
|
||||
}
|
||||
const choices = [{ key: 'tax', value: '财税' }, { key: 'legal', value: '法律' }, { key: 'ip', value: '知识产权' }]
|
||||
|
||||
describe('Course and survey form compatibility', () => {
|
||||
it('allows new option keys and ordering after reopening a saved field, while locking only original keys', async () => {
|
||||
saveCourseField.mockClear()
|
||||
const field = { id: 7, field: 'venues', name: '会场', edit_input: 'checkbox', select_item: choices.slice(0, 2).map(option => ({ ...option })), config: { option_notes: { tax: { placeholder: '咨询内容', required: false } } } }
|
||||
const wrapper = shallowMount(CourseEditor, {
|
||||
localVue, propsData: { rules: {}, types: [] },
|
||||
methods: { getAllPara() {}, getCompanyConfig() {} },
|
||||
mocks: { base: { isNull: value => value == null || String(value).trim() === '' }, $Message: { warning: jest.fn(), success: jest.fn() } }
|
||||
})
|
||||
await wrapper.setData({ showForm: true, selectedForm: field })
|
||||
const original = wrapper.vm.selectItem[0]
|
||||
expect(wrapper.vm.isSavedOption(original)).toBe(true)
|
||||
expect(wrapper.findAll('.select-item__body').at(0).find('el-input-stub').attributes('disabled')).toBeDefined()
|
||||
wrapper.find('el-button-stub[icon="el-icon-plus"]').vm.$emit('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
const added = wrapper.vm.selectItem[2]
|
||||
expect(wrapper.vm.isSavedOption(added)).toBe(false)
|
||||
expect(wrapper.findAll('.select-item__body').at(2).find('el-input-stub').attributes('disabled')).toBeUndefined()
|
||||
added.key = 'new'; added.value = '其他'
|
||||
wrapper.vm.moveOption(2, -1)
|
||||
wrapper.vm.moveOption(0, 1)
|
||||
expect(wrapper.vm.selectItem.map(option => option.key)).toEqual(['new', 'tax', 'legal'])
|
||||
expect(wrapper.vm.isSavedOption(added)).toBe(false)
|
||||
expect(wrapper.vm.isSavedOption(original)).toBe(true)
|
||||
wrapper.vm.saveField()
|
||||
await new Promise(resolve => setTimeout(resolve, 0))
|
||||
const saved = saveCourseField.mock.calls[0][0]
|
||||
expect(saved.select_item.map(option => option.key)).toEqual(['new', 'tax', 'legal'])
|
||||
expect(saved.config.option_notes.tax.placeholder).toBe('咨询内容')
|
||||
expect(wrapper.emitted('refreshFormItem')).toBeTruthy()
|
||||
await wrapper.setData({ showForm: true, selectedForm: { ...saved, id: 7 } })
|
||||
expect(wrapper.vm.selectItem.every(option => wrapper.vm.isSavedOption(option))).toBe(true)
|
||||
wrapper.destroy()
|
||||
})
|
||||
|
||||
it('sets default minima, keeps unlimited maxima, and rejects a maximum below the minimum', () => {
|
||||
for (const component of [Rules, SurveySettings]) {
|
||||
const field = { id: 1, field: 'venues', name: '会场', edit_input: 'checkbox', config: {}, select_item: [...choices] }
|
||||
const warning = jest.fn()
|
||||
const wrapper = mountForm(component, { field }, { $message: { warning } })
|
||||
expect(wrapper.findAll('el-input-number-stub').at(0).props('min')).toBe(1)
|
||||
if (component === Rules) { wrapper.vm.minimum(2); wrapper.vm.maximum(1); wrapper.vm.maximum(0) } else { wrapper.vm.changeMinimum(2); wrapper.vm.changeMaximum(1); wrapper.vm.changeMaximum(0) }
|
||||
expect(field.config.min_choices).toBe(2)
|
||||
expect(field.config.max_choices).toBe(0)
|
||||
expect(warning).toHaveBeenCalledWith('最多选择项数不能小于最少选择项数')
|
||||
wrapper.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
it('configures multiple triggers and required overrides without changing existing field identifiers', () => {
|
||||
const parent = { id: 1, field: 'finance', sort: 1, name: '融资', edit_input: 'radio', select_item: choices }
|
||||
const field = { id: 2, field: 'legacy_field', sort: 2, name: '材料', edit_input: 'text', config: {} }
|
||||
const wrapper = mountForm(Rules, { field, fields: [parent, field] })
|
||||
wrapper.vm.toggleCondition(true)
|
||||
wrapper.vm.changeParent('finance')
|
||||
field.config.visible_when.values = ['tax', 'legal']
|
||||
wrapper.vm.toggleRequired(true)
|
||||
field.config.visible_when.required_values = ['tax']
|
||||
expect(wrapper.vm.parents).toEqual([parent])
|
||||
expect(field.field).toBe('legacy_field')
|
||||
expect(field.config.visible_when.required_values).toEqual(['tax'])
|
||||
wrapper.vm.changeParent('finance')
|
||||
expect(field.config.visible_when.values).toEqual([])
|
||||
expect(field.config.visible_when.required_values).toEqual([])
|
||||
wrapper.destroy()
|
||||
})
|
||||
|
||||
it('restores comma answers, shows selected supplements, enforces limits, and clears deselected notes', async () => {
|
||||
const fields = prepareCourseFields([{ id: 1, field: 'venues', name: '会场', edit_input: 'checkbox', value: '财税,法律', select_item: choices, config: { min_choices: 2, max_choices: 2, option_notes: { tax: { placeholder: '咨询内容', required: true } } } }])
|
||||
const wrapper = mountForm(Answer, { field: fields[0] })
|
||||
expect(wrapper.vm.selection).toEqual(['tax', 'legal'])
|
||||
expect(wrapper.find('el-input-stub').attributes('placeholder')).toBe('咨询内容')
|
||||
expect(courseFormError(fields)).toContain('请填写补充说明')
|
||||
fields[0].option_texts.tax = '问题'
|
||||
expect(courseFormError(fields)).toBe('')
|
||||
wrapper.vm.choose(['legal'])
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('el-input-stub').exists()).toBe(false)
|
||||
expect(fields[0].option_texts).toEqual({})
|
||||
expect(courseFormError(fields)).toContain('至少选择2项')
|
||||
wrapper.destroy()
|
||||
})
|
||||
|
||||
it('keeps saved identity values and zero; hidden answers never enter profile/update payloads', () => {
|
||||
const fields = prepareCourseFields([
|
||||
{ field: 'username', name: '姓名', edit_input: 'text', belong_user: true, value: '原报名姓名' },
|
||||
{ field: 'parent', name: '前置', edit_input: 'radio', value: '否', select_item: [{ key: 'no', value: '否' }] },
|
||||
{ field: 'hidden', name: '条件题', edit_input: 'checkbox', value: '财税', config: { visible_when: { field: 'parent', values: ['yes'] } }, select_item: choices, option_texts: { tax: '旧说明' } },
|
||||
{ field: 'zero', name: '数量', edit_input: 'number', value: 0 }
|
||||
], { username: '最新资料姓名' })
|
||||
expect(fields[0].value).toBe('原报名姓名')
|
||||
expect(fields[3].value).toBe(0)
|
||||
expect(visibleCourseFields(fields).map(field => field.field)).toEqual(['username', 'parent', 'zero'])
|
||||
expect(courseSubmission(fields)[2]).toMatchObject({ hidden: true, value: null, option_texts: {}, selected_keys: [] })
|
||||
})
|
||||
})
|
||||
Loading…
Reference in new issue