Files
broccoliandClaude Fable 5 edfb216561 激光材料数据平台 1.3.0
公开数据看板、四位 Key 录入面板、管理后台与三级权限、动态字段配置、
PostgreSQL/SQLite 双支持、数据库备份,以及本版新增的成品图上传与预览。

图片相关:
- 录入表单支持相册选图与手机端直接拍照,每条记录最多 6 张
- 浏览器内压缩到最长边 1600 并剥离 EXIF,入库统一为 JPG/PNG
- 二进制直接入库,现有备份自动覆盖图片,部署无需新增卷
- 详情页缩略图宫格与全屏 lightbox,公开看板同样可见

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 22:06:02 +08:00

21 lines
3.4 KiB
JavaScript

(function(){
const escapeHtml=value=>{const node=document.createElement('div');node.textContent=value??'';return node.innerHTML;};
async function load(){const response=await fetch('/api/form-fields');const data=await response.json();if(!response.ok)throw new Error(data.message||'扩展字段加载失败');return data.data;}
function inputHtml(definition,raw,disabled,preserveIfEmpty=false){
const name=escapeHtml(definition.field_key),value=raw??'',required=definition.is_required&&!preserveIfEmpty?'required':'',off=disabled?'disabled':'',preserve=preserveIfEmpty?'data-preserve-if-empty="true"':'',hint=preserveIfEmpty?`placeholder="原值:${escapeHtml(raw)}(输入新值可替换)"`:'';
if(definition.input_type==='long_text')return `<textarea data-custom-key="${name}" rows="4" ${required} ${off} ${preserve} ${hint}>${escapeHtml(value)}</textarea>`;
if(definition.input_type==='boolean')return `<select data-custom-key="${name}" ${required} ${off} ${preserve}><option value="">请选择</option><option value="true" ${String(value)==='true'?'selected':''}>是</option><option value="false" ${String(value)==='false'?'selected':''}>否</option></select>`;
if(definition.input_type==='select')return `<select data-custom-key="${name}" ${required} ${off} ${preserve}><option value="">请选择</option>${definition.options.map(option=>`<option value="${escapeHtml(option)}" ${String(value)===option?'selected':''}>${escapeHtml(option)}</option>`).join('')}</select>`;
const types={number:'number',integer:'number',date:'date',text:'text'};const step=definition.input_type==='number'?'step="any"':definition.input_type==='integer'?'step="1"':'';
return `<input type="${types[definition.input_type]||'text'}" data-custom-key="${name}" value="${escapeHtml(value)}" ${step} ${required} ${off} ${preserve} ${hint}>`;
}
function render(container,definitions,values={},disabled=false){
const section=container.closest('.custom-fields-section'),activeKeys=new Set(definitions.map(item=>item.field_key));
const active=definitions.map(definition=>{const saved=values[definition.field_key],incompatible=saved&&typeof saved==='object'&&saved.compatible===false,raw=saved&&typeof saved==='object'?saved.raw_value:saved,inputValue=incompatible?'':raw,warning=incompatible?`<span class="field-format-warning">历史值“${escapeHtml(raw)}”与当前类型不兼容;留空会保留原值,填写新值后替换</span>`:'';return `<label>${escapeHtml(definition.label)}${definition.unit?` (${escapeHtml(definition.unit)})`:''} ${definition.is_required?'<em>*</em>':'<small>可选</small>'}${inputHtml(definition,inputValue,disabled,incompatible)}${warning}</label>`;}).join('');
const historical=Object.entries(values).filter(([key,value])=>!activeKeys.has(key)&&value&&typeof value==='object').map(([,value])=>`<div class="historical-custom-value"><small>已停用字段 · ${escapeHtml(value.label)}</small><b>${escapeHtml(value.display_value||value.raw_value||'未填写')}</b>${value.compatible===false?'<span>历史格式</span>':''}</div>`).join('');
container.innerHTML=active+historical;section.hidden=!definitions.length&&!historical;
}
function collect(form){const result={};form.querySelectorAll('[data-custom-key]').forEach(input=>{if(input.value===''&&input.dataset.preserveIfEmpty==='true')return;result[input.dataset.customKey]=input.value===''?null:input.value;});return result;}
window.DynamicFields={load,render,collect};
})();