@ -27,7 +27,7 @@ class SurveySchema
public static function question(array $q): array
{
$types = ['section', 'radio', 'checkbox', 'text', 'textarea', 'number', 'date', 'datetime', 'multi_dimension'];
$types = ['section', 'radio', 'checkbox', 'text', 'textarea', 'number', 'date', 'datetime', 'multi_dimension', 'file' ];
if (!in_array($q['edit_input'] ?? '', $types, true)) {
throw new InvalidArgumentException('不支持的题型');
}
@ -84,9 +84,27 @@ class SurveySchema
return self::field($q);
}
public const FILE_EXTENSIONS = ['zip', 'rar', 'ppt', 'pptx', 'xls', 'xlsx', 'doc', 'docx', 'png', 'gif', 'jpg', 'jpeg', 'pdf', 'mp4'];
public static function fileExtensions(array $config): array
{
$extensions = $config['allowed_extensions'] ?? self::FILE_EXTENSIONS;
if (!is_array($extensions) || !$extensions) throw new InvalidArgumentException('请至少选择一种允许上传的文件格式');
foreach ($extensions as $extension) {
if (!is_string($extension) || !in_array(strtolower($extension), self::FILE_EXTENSIONS, true)) throw new InvalidArgumentException('不支持的上传文件格式');
}
return array_values(array_unique(array_map('strtolower', $extensions)));
}
private static function field(array $field): array
{
$config = $field['config'] ?? [];
if ($field['edit_input'] === 'file') {
$limit = $config['max_files'] ?? 1;
if (filter_var($limit, FILTER_VALIDATE_INT) === false || $limit < 1 | | $ limit > 10) throw new InvalidArgumentException('上传数量须为1至10');
$field['config']['max_files'] = (int) $limit;
$field['config']['allowed_extensions'] = self::fileExtensions($config);
}
if (in_array($field['edit_input'], ['radio', 'checkbox'], true)) {
$options = self::arrayValue($field['select_item'] ?? []);
if (!$options) {
@ -118,6 +136,19 @@ class SurveySchema
if (!self::blank($config['other_key'] ?? null) & & !in_array((string) $config['other_key'], $keys, true)) {
throw new InvalidArgumentException('其他选项必须属于本题选项');
}
if ($field['edit_input'] === 'checkbox' & & isset($config['max_choices'])) {
$max = $config['max_choices'];
if (filter_var($max, FILTER_VALIDATE_INT) === false || $max < 0 | | $ max > count($options)) throw new InvalidArgumentException('最多选择项数须为0至选项数量之间的整数');
}
$notes = $config['option_notes'] ?? [];
if (!is_array($notes)) throw new InvalidArgumentException('选项附加输入框配置不正确');
foreach ($notes as $key => $note) {
if (!in_array((string) $key, $keys, true) || !is_array($note)) throw new InvalidArgumentException('附加输入框必须属于本题选项');
if ((string) $key === (string) ($config['other_key'] ?? '')) throw new InvalidArgumentException('同一选项不能重复设置补充输入框');
if (!is_string($note['placeholder'] ?? '') || mb_strlen($note['placeholder'] ?? '') > 255) throw new InvalidArgumentException('输入框提示文字不能超过255字');
$field['config']['option_notes'][$key]['required'] = !empty($note['required']);
}
if (!is_string($config['other_placeholder'] ?? '') || mb_strlen($config['other_placeholder'] ?? '') > 255) throw new InvalidArgumentException('补充输入框提示文字不能超过255字');
$field['select_item'] = $options;
}
if ($field['edit_input'] === 'number') {
@ -277,6 +308,19 @@ class SurveySchema
if ($type === 'checkbox' & & !empty($config['max_choices']) & & count($keys) > (int) $config['max_choices']) throw new InvalidArgumentException($label.':最多选择'.$config['max_choices'].'项');
return $type === 'checkbox' ? $keys : $keys[0];
}
if ($type === 'file') {
if (!is_array($value) || count($value) > ($config['max_files'] ?? 1)) throw new InvalidArgumentException($label.':上传文件数量不正确');
$files = [];
foreach ($value as $item) {
$id = is_array($item) ? ($item['id'] ?? null) : $item;
if (!is_scalar($id) || !ctype_digit((string) $id)) throw new InvalidArgumentException($label.':文件无效,请重新上传');
$file = \App\Models\Upload::whereKey($id)->where('creator_id', auth('mobile')->id())->first();
if (!$file || $file->size >= 50 * 1024 * 1024 || !in_array(strtolower($file->extension), ['zip','rar','ppt','pptx','xls','xlsx','doc','docx','png','gif','jpg','jpeg','pdf','mp4'], true)) throw new InvalidArgumentException($label.':文件无效或不属于当前用户,请重新上传');
if (!in_array(strtolower($file->extension), self::fileExtensions($config), true)) throw new InvalidArgumentException($label.':只允许上传 '.implode('、', self::fileExtensions($config)).' 格式');
$files[(string) $file->id] = ['id' => $file->id, 'original_name' => $file->original_name, 'url' => $file->url, 'extension' => $file->extension, 'size' => $file->size];
}
return array_values($files);
}
if ($type === 'number') {
if (!is_scalar($value) || !is_numeric($value) || !is_finite((float) $value)) {
throw new InvalidArgumentException($label . ':请输入有效数字');
@ -321,6 +365,7 @@ class SurveySchema
return '未填写';
}
$type = $field['edit_input'] ?? 'radio';
if ($type === 'file') return implode('、', array_column(is_array($value) ? $value : [], 'original_name'));
if (in_array($type, ['radio', 'checkbox'], true)) {
$values = is_array($value) ? $value : ($type === 'checkbox' ? explode(',', (string) $value) : [$value]);
$labels = [];