main
lion 1 month ago
parent 8b7567a500
commit f3cf04ad19

@ -45,6 +45,8 @@ function onTypeChange(element: FormSchemaEditorItem) {
if (element.type === SIGNUP_COMMITMENT_TYPE) {
element.key = 'commitment_accepted'
element.options = []
element.requiredWhenField = ''
element.requiredWhenValuesLines = ''
if (!element.label.trim()) {
element.label = '本人已阅读并同意《参赛承诺书》的全部内容'
}
@ -134,6 +136,23 @@ function applyOptionsText(element: FormSchemaEditorItem, raw: string) {
@input="(v: string) => applyOptionsText(element, v)"
/>
</el-col>
<el-col v-if="purpose === 'signup' && element.type !== SIGNUP_COMMITMENT_TYPE" :span="24">
<div class="field-label">条件必填可选</div>
<p class="cond-hint">
当下方依赖字段取值为列表中任一值时本字段视为必填示例企业名称依赖参赛组别创业组
</p>
<el-input
v-model="element.requiredWhenField"
class="mb-2"
placeholder="依赖字段 key如 entry_group"
/>
<el-input
v-model="element.requiredWhenValuesLines"
type="textarea"
:rows="3"
placeholder="依赖取值,每行一个,例如:&#10;创业组"
/>
</el-col>
<el-col :span="24" class="field-actions">
<el-switch v-model="element.required" active-text="" />
<el-button size="small" @click="duplicateField(index)"></el-button>
@ -169,6 +188,13 @@ function applyOptionsText(element: FormSchemaEditorItem, raw: string) {
color: var(--el-text-color-secondary);
}
.cond-hint {
margin: 0 0 8px;
font-size: 12px;
color: var(--el-text-color-secondary);
line-height: 1.5;
}
.schema-draggable-list {
display: flex;
flex-direction: column;

@ -10,6 +10,8 @@ export interface SignupFormSchemaField {
placeholder?: string
help?: string
options?: { label: string; value: string }[]
/** 当依赖字段取值命中列表时,本字段视为必填(与企业名称依赖参赛组别一致) */
required_when?: { field: string; values: string[] }
}
/**
@ -39,12 +41,24 @@ export const DEFAULT_SIGNUP_FORM_SCHEMA: SignupFormSchemaField[] = [
required: true,
placeholder: '11 位中国大陆手机号',
},
{
key: 'entry_group',
type: 'select',
label: '参赛组别',
required: true,
options: [
{ label: '创新组', value: '创新组' },
{ label: '创业组', value: '创业组' },
],
help: '选择「创业组」时须填写企业名称',
},
{
key: 'company_name',
type: 'text',
label: '企业名称',
required: false,
placeholder: '如已注册企业,请填写',
required_when: { field: 'entry_group', values: ['创业组'] },
placeholder: '创业组为必填;创新组选填',
},
{ key: 'project_name', type: 'text', label: '项目名称', required: true },
{
@ -142,6 +156,19 @@ export function normalizeSignupSchema(raw: unknown): SignupFormSchemaField[] {
const helpRaw = o.help
const help =
helpRaw != null && String(helpRaw).trim() !== '' ? String(helpRaw).trim() : undefined
let required_when: { field: string; values: string[] } | undefined
const rw = o.required_when
if (rw != null && typeof rw === 'object' && !Array.isArray(rw)) {
const rwo = rw as Record<string, unknown>
const wf = String(rwo.field ?? '').trim()
const valsRaw = rwo.values
const values = Array.isArray(valsRaw)
? valsRaw.map((x) => String(x).trim()).filter((s) => s !== '')
: []
if (wf && values.length) {
required_when = { field: wf, values }
}
}
let options: { label: string; value: string }[] | undefined
if (type === 'select' && Array.isArray(o.options)) {
options = parseSchemaOptionsArray(o.options)
@ -161,7 +188,7 @@ export function normalizeSignupSchema(raw: unknown): SignupFormSchemaField[] {
}
}
}
out.push({ key, type, label, required, placeholder, help, options })
out.push({ key, type, label, required, placeholder, help, options, required_when })
}
return out.length ? out : DEFAULT_SIGNUP_FORM_SCHEMA.map((f) => ({ ...f }))
}

@ -13,6 +13,10 @@ export interface FormSchemaEditorItem {
help?: string
/** select 等:{ label, value } */
options?: { label: string; value: string }[]
/** 报名表:条件必填依赖字段 key入库为 required_when.field */
requiredWhenField?: string
/** 报名表:依赖取值,每行一个(入库为 required_when.values */
requiredWhenValuesLines?: string
}
/** 入库时为 checkbox + key commitment_accepted仅供报名表可视化 */
@ -55,6 +59,8 @@ export function createEmptySchemaItem(
placeholder: '',
help: '',
options: [],
requiredWhenField: '',
requiredWhenValuesLines: '',
}
}
@ -85,6 +91,19 @@ export function schemaJsonToEditorItems(json: unknown, purpose: FormSchemaPurpos
purpose === 'signup' && rawKey === 'commitment_accepted' && rawType === 'checkbox'
? SIGNUP_COMMITMENT_TYPE
: rawType
let requiredWhenField = ''
let requiredWhenValuesLines = ''
if (purpose === 'signup') {
const rw = o.required_when
if (rw != null && typeof rw === 'object' && !Array.isArray(rw)) {
const rwo = rw as Record<string, unknown>
requiredWhenField = String(rwo.field ?? '').trim()
const vals = rwo.values
if (Array.isArray(vals)) {
requiredWhenValuesLines = vals.map((x) => String(x).trim()).filter(Boolean).join('\n')
}
}
}
return {
__uid: newUid(),
key: rawKey,
@ -94,12 +113,14 @@ export function schemaJsonToEditorItems(json: unknown, purpose: FormSchemaPurpos
placeholder: o.placeholder != null ? String(o.placeholder) : '',
help: o.help != null ? String(o.help) : '',
options: type === 'select' ? normalizeOptions(o.options) : [],
requiredWhenField,
requiredWhenValuesLines,
}
})
}
/** 写入接口的 schema_json数组 */
export function editorItemsToSchemaJson(items: FormSchemaEditorItem[]): unknown[] {
export function editorItemsToSchemaJson(items: FormSchemaEditorItem[], purpose: FormSchemaPurpose): unknown[] {
return items.map((item) => {
const isCommitment = item.type === SIGNUP_COMMITMENT_TYPE
const row: Record<string, unknown> = {
@ -113,11 +134,25 @@ export function editorItemsToSchemaJson(items: FormSchemaEditorItem[]): unknown[
if (item.type === 'select' && item.options?.length) {
row.options = item.options.filter((o) => o.value !== '' || o.label !== '')
}
if (
purpose === 'signup'
&& !isCommitment
&& item.requiredWhenField?.trim()
&& item.requiredWhenValuesLines?.trim()
) {
const values = item.requiredWhenValuesLines
.split('\n')
.map((s) => s.trim())
.filter(Boolean)
if (values.length) {
row.required_when = { field: item.requiredWhenField.trim(), values }
}
}
return row
})
}
export function validateEditorItems(items: FormSchemaEditorItem[]): string | null {
export function validateEditorItems(items: FormSchemaEditorItem[], purpose: FormSchemaPurpose): string | null {
const keys = new Set<string>()
for (const it of items) {
const k = it.key.trim()
@ -125,6 +160,14 @@ export function validateEditorItems(items: FormSchemaEditorItem[]): string | nul
if (keys.has(k)) return `字段 key「${k}」重复`
keys.add(k)
if (!it.label.trim()) return `字段「${k}」缺少显示标签`
if (
purpose === 'signup'
&& it.type !== SIGNUP_COMMITMENT_TYPE
&& it.requiredWhenField?.trim()
&& !it.requiredWhenValuesLines?.trim()
) {
return `字段「${k}」填写了条件必填依赖,但未填写「当取值」列表`
}
}
return null
}

@ -271,6 +271,11 @@ function effectiveRequired(field: SignupFormSchemaField): boolean {
if (field.key === 'oversea_country') {
return isOversea.value
}
const rw = field.required_when
if (rw?.field && rw.values?.length) {
const dep = String(formModel[rw.field] ?? '').trim()
if (rw.values.includes(dep)) return true
}
return field.required
}
@ -842,6 +847,7 @@ function applyServerPayload(d: {
degree?: string
contact_email?: string
contact_mobile?: string
entry_group?: string
company_name?: string
project_name?: string
track?: string
@ -861,6 +867,7 @@ function applyServerPayload(d: {
formModel.degree = d.degree || ''
formModel.contact_email = d.contact_email || ''
formModel.contact_mobile = d.contact_mobile || ''
formModel.entry_group = d.entry_group || ''
formModel.company_name = d.company_name || ''
formModel.project_name = d.project_name || ''
formModel.track = d.track || ''

@ -168,7 +168,11 @@ watch(editSchemaTab, (t) => {
})
function syncEditVisualToJson() {
editSchemaJsonText.value = JSON.stringify(editorItemsToSchemaJson(editSchemaVisualItems.value), null, 2)
editSchemaJsonText.value = JSON.stringify(
editorItemsToSchemaJson(editSchemaVisualItems.value, editSchemaPurpose.value),
null,
2,
)
}
function syncEditJsonToVisual() {
@ -646,12 +650,12 @@ async function saveEditedSchema() {
editSchemaError.value = ''
let parsed: unknown[] = []
if (editSchemaTab.value === 'visual') {
const err = validateEditorItems(editSchemaVisualItems.value)
const err = validateEditorItems(editSchemaVisualItems.value, editSchemaPurpose.value)
if (err) {
editSchemaError.value = err
return
}
parsed = editorItemsToSchemaJson(editSchemaVisualItems.value) as unknown[]
parsed = editorItemsToSchemaJson(editSchemaVisualItems.value, editSchemaPurpose.value) as unknown[]
} else {
try {
const raw = JSON.parse(editSchemaJsonText.value)

@ -40,6 +40,7 @@ interface DetailPayload {
degree: string
contact_email: string
contact_mobile: string
entry_group: string
company_name: string
track_title: string
track_code: string
@ -375,6 +376,10 @@ async function submitReviewScore() {
<label class="form-label">联系电话</label>
<input class="form-control" type="text" :value="detail.contact_mobile" disabled />
</div>
<div class="col-md-4">
<label class="form-label">参赛组别</label>
<input class="form-control" type="text" :value="detail.entry_group || '—'" disabled />
</div>
<div class="col-md-4">
<label class="form-label">企业名称</label>
<input

Loading…
Cancel
Save