nx-sk 0.8.0 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{u as R,r as j,a as h,j as e,T as N,E as T,C as I,M as U}from"./index-DzD8wu27.js";const P=["text","textarea","number","date","month","bool","select","tags","secret","file"];function M(){const{reload:d,toast:n,guard:o,dialog:u}=R(),[t,b]=j.useState(null),[f,r]=j.useState([]),[x,i]=j.useState(null),[c,m]=j.useState(null),a=j.useCallback(async()=>{try{const[s,l]=await Promise.all([h("/api/sections"),h("/api/sections/templates")]);b(s),r(l.templates),i(null)}catch(s){i(s.message)}},[]);j.useEffect(()=>{a()},[a]);const p=o(async s=>{const l=await h(`/api/sections/${encodeURIComponent(s.id)}/dump`);m({kind:"dump",title:`${s.title}(${s.id})的全部信息`,body:e.jsx("pre",{className:"block",children:JSON.stringify(l,null,2)})})}),C=o(async s=>{const l=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({kind:"edit",id:s.id,form:{title:l.title,description:l.description,order:String(l.order),titleLabel:l.titleLabel,titleField:l.titleField||""},full:l})}),w=o(async s=>{const l={id:s.id.trim(),title:s.title.trim()||s.id.trim(),template:s.template||void 0,description:s.description,order:Number(s.order)||50};await h("/api/sections",{method:"POST",body:l}),n("栏目已创建"),m(null),await a(),await d()}),$=o(async s=>{const l={title:s.form.title,description:s.form.description,order:Number(s.form.order)||50,titleLabel:s.form.titleLabel,titleField:s.form.titleField};await h(`/api/sections/${encodeURIComponent(s.id)}`,{method:"PATCH",body:l}),n("栏目已更新");const v=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({...s,full:v}),await a(),await d()}),S=o(async(s,l)=>{await h(`/api/sections/${encodeURIComponent(s.id)}`,{method:"PATCH",body:{"add-field":l}}),n(`已新增字段 ${l.key}`);const v=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({...s,full:v}),await a()}),F=o(async s=>{var y,k;const l=await h(`/api/sections/${encodeURIComponent(s.id)}/sync`,{method:"POST"});if(l.upToDate){n("已是最新,模板里没有需要补的字段");return}const v=[(y=l.addedGroups)!=null&&y.length?`分组 ${l.addedGroups.join("、")}`:"",(k=l.addedFields)!=null&&k.length?`字段 ${l.addedFields.join("、")}`:""].filter(Boolean).join(";");n(`已按模板补齐:${v||"无变化"}`);const g=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({...s,full:g}),await a(),await d()}),E=o(async(s,l)=>{if(!await u({message:`从字段字典里删掉「${l}」?已填的值会留在条目里(只是不再显示/校验)。`,danger:!0,confirmText:"删除字段"}))return;await h(`/api/sections/${encodeURIComponent(s.id)}`,{method:"PATCH",body:{"remove-field":l}}),n(`已删字段 ${l}`);const g=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({...s,full:g}),await a()}),L=o(async s=>{await u({message:`删除栏目「${s.title}」,连带其中 ${s.entries} 条条目?删除前会自动留快照。`,danger:!0,confirmText:"连带删除"})&&(await h(`/api/sections/${encodeURIComponent(s.id)}`,{method:"DELETE",body:{force:!0}}),n("栏目已删除"),await a(),await d())});return x?e.jsxs("div",{className:"pad stack",children:[e.jsx("div",{className:"bad",children:e.jsx("strong",{children:"读不到栏目"})}),e.jsx("pre",{className:"block",children:x})]}):t?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"toolbar rowgap pad",children:[e.jsx("strong",{children:"栏目"}),e.jsxs("span",{className:"muted",children:["共 ",t.count," 个 · 条目 ",t.totalEntries," 条"]}),e.jsx("span",{className:"grow"}),e.jsx("button",{className:"btn small ghost",onClick:a,children:"刷新"}),e.jsx("button",{className:"btn small",onClick:()=>{var s;return m({kind:"create",form:{id:"",title:"",template:((s=f[0])==null?void 0:s.id)||"",description:"",order:"50"}})},children:"新建栏目"})]}),e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"colhead",children:"全部栏目"}),e.jsx("div",{className:"colbody",children:t.sections.length?t.sections.map(s=>e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"name",children:s.title}),e.jsx("span",{className:"mono muted",children:s.id}),s.template?e.jsx(N,{children:s.template}):e.jsx(N,{children:"自定义"}),e.jsx("span",{className:"desc",children:s.description}),e.jsxs("span",{className:"acts",children:[e.jsxs("span",{className:"tag",children:[s.fields," 字段"]}),e.jsxs("span",{className:"tag",children:[s.entries," 条目"]}),e.jsx("button",{className:"btn small ghost",onClick:()=>p(s),children:"全部信息"}),e.jsx("button",{className:"btn small ghost",onClick:()=>C(s),children:"编辑"}),e.jsx("button",{className:"btn small ghost",onClick:()=>L(s),children:"删除"})]})]},s.id)):e.jsx(T,{children:"还没有栏目。点「新建栏目」,用内置模板(求职 / 密钥)起步。"})})]}),e.jsx("div",{className:"pad hint",children:"栏目 = 一组同构条目 + 一份字段字典。字典是数据,所以这里加一个字段,CLI 与面板立刻就都能填它。"}),e.jsx(I,{module:"sections"}),c?e.jsxs(U,{title:z(c),onClose:()=>m(null),children:[c.kind==="dump"?c.body:null,c.kind==="create"?e.jsx(A,{templates:f,form:c.form,onChange:s=>m(l=>({...l,form:{...l.form,...s}})),onSubmit:()=>{if(!/^[a-z][a-z0-9_-]{0,63}$/.test(c.form.id.trim())){n("栏目 id 必须是小写字母开头,仅含 a-z 0-9 _ -","bad");return}w(c.form)}}):null,c.kind==="edit"?e.jsx(D,{modal:c,onChange:s=>m(l=>({...l,form:{...l.form,...s}})),onSave:()=>$(c),onSync:()=>F(c),onAddField:s=>S(c,s),onRemoveField:s=>E(c,s),onClose:()=>m(null)}):null]}):null]}):e.jsx("div",{className:"pad muted",children:"加载中…"})}function z(d){return d.kind==="dump"?d.title:d.kind==="create"?"新建栏目":`编辑栏目 ${d.id}`}function A({templates:d,form:n,onChange:o,onSubmit:u}){return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{className:"grid",children:[e.jsxs("div",{className:"field",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"栏目 id"}),e.jsx("span",{className:"fkey",children:"小写字母开头,仅 a-z 0-9 _ -"})]}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:n.id,onChange:t=>o({id:t.target.value}),placeholder:"例如 projects"})})]}),e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"标题"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:n.title,onChange:t=>o({title:t.target.value}),placeholder:"例如 项目档案"})})]}),e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"从模板起步"})}),e.jsx("div",{className:"fctrl",children:e.jsxs("select",{value:n.template,onChange:t=>o({template:t.target.value}),children:[e.jsx("option",{value:"",children:"(空栏目,自定义字段)"}),d.map(t=>e.jsxs("option",{value:t.id,children:[t.label,"(",t.fields," 字段)"]},t.id))]})})]}),e.jsxs("div",{className:"field",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"排序"}),e.jsx("span",{className:"fkey",children:"越小越靠前"})]}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{type:"number",value:n.order,onChange:t=>o({order:t.target.value})})})]})]}),e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"描述"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:n.description,onChange:t=>o({description:t.target.value})})})]}),e.jsxs("div",{className:"rowgap",children:[e.jsx("button",{className:"btn",onClick:u,children:"创建"}),e.jsx("span",{className:"hint",children:"同 id 已存在会直接报冲突,不会静默覆盖。"})]})]})}function D({modal:d,onChange:n,onSave:o,onSync:u,onAddField:t,onRemoveField:b,onClose:f}){var m;const[r,x]=j.useState({key:"",label:"",type:"text",group:""}),i=d.full,c=(i==null?void 0:i.groupList)||[];return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{className:"grid",children:[e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"标题"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:d.form.title,onChange:a=>n({title:a.target.value})})})]}),e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"排序"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{type:"number",value:d.form.order,onChange:a=>n({order:a.target.value})})})]}),e.jsxs("div",{className:"field",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"条目名字段"}),e.jsx("span",{className:"fkey",children:"列表里显示哪个字段"})]}),e.jsx("div",{className:"fctrl",children:e.jsxs("select",{value:d.form.titleField,onChange:a=>n({titleField:a.target.value}),children:[e.jsx("option",{value:"",children:"(用条目 title)"}),((i==null?void 0:i.fieldList)||[]).map(a=>e.jsxs("option",{value:a.key,children:[a.label,"(",a.key,")"]},a.key))]})})]}),e.jsxs("div",{className:"field",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"名称标签"}),e.jsx("span",{className:"fkey",children:"新建条目时问什么"})]}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:d.form.titleLabel,onChange:a=>n({titleLabel:a.target.value})})})]})]}),e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"描述"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:d.form.description,onChange:a=>n({description:a.target.value})})})]}),e.jsxs("div",{className:"rowgap",children:[e.jsx("button",{className:"btn",onClick:o,children:"保存栏目属性"}),i!=null&&i.template?e.jsx("button",{className:"btn ghost",onClick:u,title:`按模板「${i.template}」补齐新增的分组与字段(只增不改不删)`,children:"同步模板字段"}):null,e.jsx("button",{className:"btn ghost",onClick:f,children:"关闭"})]}),i!=null&&i.template?e.jsxs("div",{className:"hint",children:["本栏目由模板「",i.template,"」创建。模板后来新增的字段**不会自动**出现在这里, 点「同步模板字段」补齐(例如求职模板新增的「附件资料」组 / 简历、头像等 file 字段)。"]}):null,e.jsxs("div",{className:"card",style:{border:"1px solid var(--soft-2)",borderRadius:4},children:[e.jsxs("div",{className:"colhead",children:["字段字典(",((m=i==null?void 0:i.fieldList)==null?void 0:m.length)||0,")"]}),e.jsxs("div",{className:"colbody",children:[c.map(a=>e.jsxs("div",{children:[e.jsxs("div",{className:"group-title",children:[a.title,"(",a.id,")"]}),(i.fieldList||[]).filter(p=>p.group===a.id).map(p=>e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"name",children:p.label}),e.jsx("span",{className:"mono muted",children:p.key}),e.jsx(N,{children:p.type}),p.sensitive?e.jsx(N,{kind:"bad",children:"密文"}):null,e.jsx("span",{className:"desc",children:p.hint||""}),e.jsx("span",{className:"acts",children:e.jsx("button",{className:"btn small ghost",onClick:()=>b(p.key),children:"删除"})})]},p.key))]},a.id)),((i==null?void 0:i.fieldList)||[]).filter(a=>!c.some(p=>p.id===a.group)).map(a=>e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"name",children:a.label}),e.jsx("span",{className:"mono muted",children:a.key}),e.jsx(N,{children:a.type}),e.jsxs("span",{className:"desc",children:["分组 ",a.group,"(不在 groups 列表里)"]}),e.jsx("span",{className:"acts",children:e.jsx("button",{className:"btn small ghost",onClick:()=>b(a.key),children:"删除"})})]},a.key))]})]}),e.jsxs("div",{className:"card",style:{border:"1px solid var(--soft-2)",borderRadius:4},children:[e.jsx("div",{className:"colhead",children:"新增字段"}),e.jsxs("div",{className:"pad rowgap",children:[e.jsx("input",{style:{width:150},placeholder:"key(英文字母开头)",value:r.key,onChange:a=>x({...r,key:a.target.value})}),e.jsx("input",{style:{width:150},placeholder:"标签(中文名)",value:r.label,onChange:a=>x({...r,label:a.target.value})}),e.jsx("select",{style:{width:110},value:r.type,onChange:a=>x({...r,type:a.target.value}),children:P.map(a=>e.jsx("option",{value:a,children:a},a))}),e.jsxs("select",{style:{width:150},value:r.group,onChange:a=>x({...r,group:a.target.value}),children:[e.jsx("option",{value:"",children:"(首个分组)"}),c.map(a=>e.jsx("option",{value:a.id,children:a.title},a.id))]}),e.jsx("button",{className:"btn small",onClick:()=>{const a={key:r.key.trim(),label:r.label.trim()||r.key.trim(),type:r.type};r.group&&(a.group=r.group),t(a),x({key:"",label:"",type:"text",group:r.group})},children:"加入字典"}),e.jsx("span",{className:"hint",children:"加完立刻生效:CLI 与面板都能填它。"})]})]})]})}export{M as default};
1
+ import{u as R,r as j,a as h,j as e,T as N,E as T,C as I,M as U}from"./index-LbNmGlYj.js";const P=["text","textarea","number","date","month","bool","select","tags","secret","file"];function M(){const{reload:d,toast:n,guard:o,dialog:u}=R(),[t,b]=j.useState(null),[f,r]=j.useState([]),[x,i]=j.useState(null),[c,m]=j.useState(null),a=j.useCallback(async()=>{try{const[s,l]=await Promise.all([h("/api/sections"),h("/api/sections/templates")]);b(s),r(l.templates),i(null)}catch(s){i(s.message)}},[]);j.useEffect(()=>{a()},[a]);const p=o(async s=>{const l=await h(`/api/sections/${encodeURIComponent(s.id)}/dump`);m({kind:"dump",title:`${s.title}(${s.id})的全部信息`,body:e.jsx("pre",{className:"block",children:JSON.stringify(l,null,2)})})}),C=o(async s=>{const l=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({kind:"edit",id:s.id,form:{title:l.title,description:l.description,order:String(l.order),titleLabel:l.titleLabel,titleField:l.titleField||""},full:l})}),w=o(async s=>{const l={id:s.id.trim(),title:s.title.trim()||s.id.trim(),template:s.template||void 0,description:s.description,order:Number(s.order)||50};await h("/api/sections",{method:"POST",body:l}),n("栏目已创建"),m(null),await a(),await d()}),$=o(async s=>{const l={title:s.form.title,description:s.form.description,order:Number(s.form.order)||50,titleLabel:s.form.titleLabel,titleField:s.form.titleField};await h(`/api/sections/${encodeURIComponent(s.id)}`,{method:"PATCH",body:l}),n("栏目已更新");const v=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({...s,full:v}),await a(),await d()}),S=o(async(s,l)=>{await h(`/api/sections/${encodeURIComponent(s.id)}`,{method:"PATCH",body:{"add-field":l}}),n(`已新增字段 ${l.key}`);const v=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({...s,full:v}),await a()}),F=o(async s=>{var y,k;const l=await h(`/api/sections/${encodeURIComponent(s.id)}/sync`,{method:"POST"});if(l.upToDate){n("已是最新,模板里没有需要补的字段");return}const v=[(y=l.addedGroups)!=null&&y.length?`分组 ${l.addedGroups.join("、")}`:"",(k=l.addedFields)!=null&&k.length?`字段 ${l.addedFields.join("、")}`:""].filter(Boolean).join(";");n(`已按模板补齐:${v||"无变化"}`);const g=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({...s,full:g}),await a(),await d()}),E=o(async(s,l)=>{if(!await u({message:`从字段字典里删掉「${l}」?已填的值会留在条目里(只是不再显示/校验)。`,danger:!0,confirmText:"删除字段"}))return;await h(`/api/sections/${encodeURIComponent(s.id)}`,{method:"PATCH",body:{"remove-field":l}}),n(`已删字段 ${l}`);const g=await h(`/api/sections/${encodeURIComponent(s.id)}`);m({...s,full:g}),await a()}),L=o(async s=>{await u({message:`删除栏目「${s.title}」,连带其中 ${s.entries} 条条目?删除前会自动留快照。`,danger:!0,confirmText:"连带删除"})&&(await h(`/api/sections/${encodeURIComponent(s.id)}`,{method:"DELETE",body:{force:!0}}),n("栏目已删除"),await a(),await d())});return x?e.jsxs("div",{className:"pad stack",children:[e.jsx("div",{className:"bad",children:e.jsx("strong",{children:"读不到栏目"})}),e.jsx("pre",{className:"block",children:x})]}):t?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"toolbar rowgap pad",children:[e.jsx("strong",{children:"栏目"}),e.jsxs("span",{className:"muted",children:["共 ",t.count," 个 · 条目 ",t.totalEntries," 条"]}),e.jsx("span",{className:"grow"}),e.jsx("button",{className:"btn small ghost",onClick:a,children:"刷新"}),e.jsx("button",{className:"btn small",onClick:()=>{var s;return m({kind:"create",form:{id:"",title:"",template:((s=f[0])==null?void 0:s.id)||"",description:"",order:"50"}})},children:"新建栏目"})]}),e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"colhead",children:"全部栏目"}),e.jsx("div",{className:"colbody",children:t.sections.length?t.sections.map(s=>e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"name",children:s.title}),e.jsx("span",{className:"mono muted",children:s.id}),s.template?e.jsx(N,{children:s.template}):e.jsx(N,{children:"自定义"}),e.jsx("span",{className:"desc",children:s.description}),e.jsxs("span",{className:"acts",children:[e.jsxs("span",{className:"tag",children:[s.fields," 字段"]}),e.jsxs("span",{className:"tag",children:[s.entries," 条目"]}),e.jsx("button",{className:"btn small ghost",onClick:()=>p(s),children:"全部信息"}),e.jsx("button",{className:"btn small ghost",onClick:()=>C(s),children:"编辑"}),e.jsx("button",{className:"btn small ghost",onClick:()=>L(s),children:"删除"})]})]},s.id)):e.jsx(T,{children:"还没有栏目。点「新建栏目」,用内置模板(求职 / 密钥)起步。"})})]}),e.jsx("div",{className:"pad hint",children:"栏目 = 一组同构条目 + 一份字段字典。字典是数据,所以这里加一个字段,CLI 与面板立刻就都能填它。"}),e.jsx(I,{module:"sections"}),c?e.jsxs(U,{title:z(c),onClose:()=>m(null),children:[c.kind==="dump"?c.body:null,c.kind==="create"?e.jsx(A,{templates:f,form:c.form,onChange:s=>m(l=>({...l,form:{...l.form,...s}})),onSubmit:()=>{if(!/^[a-z][a-z0-9_-]{0,63}$/.test(c.form.id.trim())){n("栏目 id 必须是小写字母开头,仅含 a-z 0-9 _ -","bad");return}w(c.form)}}):null,c.kind==="edit"?e.jsx(D,{modal:c,onChange:s=>m(l=>({...l,form:{...l.form,...s}})),onSave:()=>$(c),onSync:()=>F(c),onAddField:s=>S(c,s),onRemoveField:s=>E(c,s),onClose:()=>m(null)}):null]}):null]}):e.jsx("div",{className:"pad muted",children:"加载中…"})}function z(d){return d.kind==="dump"?d.title:d.kind==="create"?"新建栏目":`编辑栏目 ${d.id}`}function A({templates:d,form:n,onChange:o,onSubmit:u}){return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{className:"grid",children:[e.jsxs("div",{className:"field",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"栏目 id"}),e.jsx("span",{className:"fkey",children:"小写字母开头,仅 a-z 0-9 _ -"})]}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:n.id,onChange:t=>o({id:t.target.value}),placeholder:"例如 projects"})})]}),e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"标题"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:n.title,onChange:t=>o({title:t.target.value}),placeholder:"例如 项目档案"})})]}),e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"从模板起步"})}),e.jsx("div",{className:"fctrl",children:e.jsxs("select",{value:n.template,onChange:t=>o({template:t.target.value}),children:[e.jsx("option",{value:"",children:"(空栏目,自定义字段)"}),d.map(t=>e.jsxs("option",{value:t.id,children:[t.label,"(",t.fields," 字段)"]},t.id))]})})]}),e.jsxs("div",{className:"field",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"排序"}),e.jsx("span",{className:"fkey",children:"越小越靠前"})]}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{type:"number",value:n.order,onChange:t=>o({order:t.target.value})})})]})]}),e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"描述"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:n.description,onChange:t=>o({description:t.target.value})})})]}),e.jsxs("div",{className:"rowgap",children:[e.jsx("button",{className:"btn",onClick:u,children:"创建"}),e.jsx("span",{className:"hint",children:"同 id 已存在会直接报冲突,不会静默覆盖。"})]})]})}function D({modal:d,onChange:n,onSave:o,onSync:u,onAddField:t,onRemoveField:b,onClose:f}){var m;const[r,x]=j.useState({key:"",label:"",type:"text",group:""}),i=d.full,c=(i==null?void 0:i.groupList)||[];return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{className:"grid",children:[e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"标题"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:d.form.title,onChange:a=>n({title:a.target.value})})})]}),e.jsxs("div",{className:"field",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"排序"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{type:"number",value:d.form.order,onChange:a=>n({order:a.target.value})})})]}),e.jsxs("div",{className:"field",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"条目名字段"}),e.jsx("span",{className:"fkey",children:"列表里显示哪个字段"})]}),e.jsx("div",{className:"fctrl",children:e.jsxs("select",{value:d.form.titleField,onChange:a=>n({titleField:a.target.value}),children:[e.jsx("option",{value:"",children:"(用条目 title)"}),((i==null?void 0:i.fieldList)||[]).map(a=>e.jsxs("option",{value:a.key,children:[a.label,"(",a.key,")"]},a.key))]})})]}),e.jsxs("div",{className:"field",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"名称标签"}),e.jsx("span",{className:"fkey",children:"新建条目时问什么"})]}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:d.form.titleLabel,onChange:a=>n({titleLabel:a.target.value})})})]})]}),e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"描述"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:d.form.description,onChange:a=>n({description:a.target.value})})})]}),e.jsxs("div",{className:"rowgap",children:[e.jsx("button",{className:"btn",onClick:o,children:"保存栏目属性"}),i!=null&&i.template?e.jsx("button",{className:"btn ghost",onClick:u,title:`按模板「${i.template}」补齐新增的分组与字段(只增不改不删)`,children:"同步模板字段"}):null,e.jsx("button",{className:"btn ghost",onClick:f,children:"关闭"})]}),i!=null&&i.template?e.jsxs("div",{className:"hint",children:["本栏目由模板「",i.template,"」创建。模板后来新增的字段**不会自动**出现在这里, 点「同步模板字段」补齐(例如求职模板新增的「附件资料」组 / 简历、头像等 file 字段)。"]}):null,e.jsxs("div",{className:"card",style:{border:"1px solid var(--soft-2)",borderRadius:4},children:[e.jsxs("div",{className:"colhead",children:["字段字典(",((m=i==null?void 0:i.fieldList)==null?void 0:m.length)||0,")"]}),e.jsxs("div",{className:"colbody",children:[c.map(a=>e.jsxs("div",{children:[e.jsxs("div",{className:"group-title",children:[a.title,"(",a.id,")"]}),(i.fieldList||[]).filter(p=>p.group===a.id).map(p=>e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"name",children:p.label}),e.jsx("span",{className:"mono muted",children:p.key}),e.jsx(N,{children:p.type}),p.sensitive?e.jsx(N,{kind:"bad",children:"密文"}):null,e.jsx("span",{className:"desc",children:p.hint||""}),e.jsx("span",{className:"acts",children:e.jsx("button",{className:"btn small ghost",onClick:()=>b(p.key),children:"删除"})})]},p.key))]},a.id)),((i==null?void 0:i.fieldList)||[]).filter(a=>!c.some(p=>p.id===a.group)).map(a=>e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"name",children:a.label}),e.jsx("span",{className:"mono muted",children:a.key}),e.jsx(N,{children:a.type}),e.jsxs("span",{className:"desc",children:["分组 ",a.group,"(不在 groups 列表里)"]}),e.jsx("span",{className:"acts",children:e.jsx("button",{className:"btn small ghost",onClick:()=>b(a.key),children:"删除"})})]},a.key))]})]}),e.jsxs("div",{className:"card",style:{border:"1px solid var(--soft-2)",borderRadius:4},children:[e.jsx("div",{className:"colhead",children:"新增字段"}),e.jsxs("div",{className:"pad rowgap",children:[e.jsx("input",{style:{width:150},placeholder:"key(英文字母开头)",value:r.key,onChange:a=>x({...r,key:a.target.value})}),e.jsx("input",{style:{width:150},placeholder:"标签(中文名)",value:r.label,onChange:a=>x({...r,label:a.target.value})}),e.jsx("select",{style:{width:110},value:r.type,onChange:a=>x({...r,type:a.target.value}),children:P.map(a=>e.jsx("option",{value:a,children:a},a))}),e.jsxs("select",{style:{width:150},value:r.group,onChange:a=>x({...r,group:a.target.value}),children:[e.jsx("option",{value:"",children:"(首个分组)"}),c.map(a=>e.jsx("option",{value:a.id,children:a.title},a.id))]}),e.jsx("button",{className:"btn small",onClick:()=>{const a={key:r.key.trim(),label:r.label.trim()||r.key.trim(),type:r.type};r.group&&(a.group=r.group),t(a),x({key:"",label:"",type:"text",group:r.group})},children:"加入字典"}),e.jsx("span",{className:"hint",children:"加完立刻生效:CLI 与面板都能填它。"})]})]})]})}export{M as default};
@@ -1 +1 @@
1
- import{u as b,r as d,a as k,j as s,b as r,E as F,C as I,T as $}from"./index-DzD8wu27.js";function D(){var N,v,u,g,f,w,y,S;const{boot:e,reload:t,toast:n,guard:C}=b(),[o,h]=d.useState(null),[l,i]=d.useState(null),j=typeof window>"u"?"":window.location.origin,p=typeof window>"u"?"":window.location.port||"80",m=d.useCallback(async()=>{try{const a=await k("/api/settings");h(a),i({...a.settings})}catch(a){n(a.message,"bad")}},[n]);d.useEffect(()=>{m()},[m]);const E=C(async()=>{const a={exportDir:l.exportDir,exportFormat:l.exportFormat,includeSecretsInExport:!!l.includeSecretsInExport,defaultSection:l.defaultSection},x=await k("/api/settings",{method:"PATCH",body:a});n("设置已保存"),h({...o,settings:x.set}),await t()});return!o||!l?s.jsx("div",{className:"pad muted",children:"加载中…"}):s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"toolbar rowgap pad",children:[s.jsx("strong",{children:"设置"}),s.jsx("span",{className:"muted",children:"本机配置与运行状态"}),s.jsx("span",{className:"grow"}),s.jsx("button",{className:"btn small ghost",onClick:m,children:"重新读取"})]}),s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"存储与运行"}),s.jsxs("div",{className:"colbody",children:[s.jsx(c,{label:"数据目录",value:e==null?void 0:e.home,copy:e==null?void 0:e.home}),s.jsx(c,{label:"store.json",value:e==null?void 0:e.storePath,copy:e==null?void 0:e.storePathRaw}),s.jsx(c,{label:"栏目 / 条目",value:e?`${e.counts.sections} / ${e.counts.entries}`:""}),s.jsx(c,{label:"命令条数",value:e?String(e.counts.commands):""}),s.jsx(c,{label:"app / node",value:e?`v${e.app.version} · node ${e.app.node} · ${e.app.platform}`:""}),s.jsx(c,{label:"环境变量覆盖",value:e?Object.entries(e.envOverrides).filter(([,a])=>a).map(([a,x])=>`${a}=${x}`).join(" ")||"(无)":""})]})]}),s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"密钥(密文落盘)"}),s.jsxs("div",{className:"colbody",children:[s.jsx(c,{label:"密钥来源",value:((N=e==null?void 0:e.vault)==null?void 0:N.source)==="env"?"环境变量 NX_SK_PASSPHRASE(口令不落盘)":"本机密钥文件"}),s.jsx(c,{label:"密钥文件",value:((v=e==null?void 0:e.vault)==null?void 0:v.keyFile)||((u=e==null?void 0:e.vault)==null?void 0:u.saltFile)||"",copy:((g=e==null?void 0:e.vault)==null?void 0:g.keyFile)||((f=e==null?void 0:e.vault)==null?void 0:f.saltFile)}),s.jsx("div",{className:"row",children:s.jsx("span",{className:"desc",children:(w=e==null?void 0:e.vault)==null?void 0:w.note})}),s.jsx("div",{className:"row",children:s.jsx("span",{className:"desc bad",children:"删掉密钥文件或换掉口令后,已存的密文都解不开了——导出前先确认密钥可用。"})})]})]}),s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"面板与 CLI"}),s.jsxs("div",{className:"colbody",children:[s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"name",children:"面板地址"}),s.jsx("span",{className:"desc mono",children:j}),s.jsx("span",{className:"acts",children:s.jsx(r,{className:"mono",text:j,children:"复制"})})]}),s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"name",children:"重启面板"}),s.jsx("span",{className:"desc",children:"面板本身就是 serve 提供的;改代码后重启它即可"}),s.jsx("span",{className:"acts",children:s.jsx(r,{className:"mono",text:`nx-sk serve --port ${p}`,children:`nx-sk serve --port ${p}`})})]})]})]}),s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"默认行为"}),s.jsxs("div",{className:"pad",children:[s.jsxs("div",{className:"grid",children:[s.jsxs("div",{className:"field",children:[s.jsx("div",{className:"flabel",children:s.jsx("span",{children:"默认导出格式"})}),s.jsx("div",{className:"fctrl",children:s.jsxs("select",{value:l.exportFormat,onChange:a=>i({...l,exportFormat:a.target.value}),children:[s.jsx("option",{value:"both",children:"JSON + Markdown"}),s.jsx("option",{value:"json",children:"仅 JSON"}),s.jsx("option",{value:"md",children:"仅 Markdown"})]})})]}),s.jsxs("div",{className:"field",children:[s.jsxs("div",{className:"flabel",children:[s.jsx("span",{children:"默认导出目录"}),s.jsx("span",{className:"fkey",children:"留空 = ~/nx-sk/export"})]}),s.jsx("div",{className:"fctrl",children:s.jsx("input",{value:l.exportDir,onChange:a=>i({...l,exportDir:a.target.value})})})]}),s.jsxs("div",{className:"field",children:[s.jsxs("div",{className:"flabel",children:[s.jsx("span",{children:"默认栏目"}),s.jsx("span",{className:"fkey",children:"CLI 未指定 --section 时的首选"})]}),s.jsx("div",{className:"fctrl",children:s.jsx("input",{value:l.defaultSection,onChange:a=>i({...l,defaultSection:a.target.value})})})]}),s.jsxs("div",{className:"field",children:[s.jsxs("div",{className:"flabel",children:[s.jsx("span",{children:"导出默认带明文密钥"}),s.jsx("span",{className:"fkey",children:"默认开;要分享出去时关掉"})]}),s.jsx("div",{className:"fctrl",children:s.jsxs("label",{className:"inline",children:[s.jsx("input",{type:"checkbox",checked:!!l.includeSecretsInExport,onChange:a=>i({...l,includeSecretsInExport:a.target.checked})}),s.jsx("span",{className:l.includeSecretsInExport?"muted":"bad",children:l.includeSecretsInExport?"开(导出文件含真实凭据,注意别外发)":"关(凭据打码后再导出)"})]})})]})]}),s.jsx("div",{className:"rowgap",style:{marginTop:8},children:s.jsx("button",{className:"btn",onClick:E,children:"保存设置"})})]})]}),s.jsxs("div",{className:"card",children:[s.jsxs("div",{className:"colhead",children:["自动快照",s.jsx("span",{className:"right muted",children:((y=e==null?void 0:e.snapshots)==null?void 0:y.length)??0})]}),s.jsxs("div",{className:"colbody",children:[s.jsx("div",{className:"row",children:s.jsx("span",{className:"desc",children:"每次删除条目 / 栏目、修改设置前,都会在 ~/nx-sk/backup 留一份可回滚的整库快照。"})}),(S=e==null?void 0:e.snapshots)!=null&&S.length?e.snapshots.map(a=>s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"mono name",children:a.name}),s.jsx("span",{className:"desc",children:a.at}),s.jsx("span",{className:"acts",children:s.jsxs("span",{className:"tag",children:[Math.ceil(a.bytes/1024)," KB"]})})]},a.name)):s.jsx(F,{children:"还没有快照。"})]})]}),e!=null&&e.migration?s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"数据迁移"}),s.jsxs("div",{className:"colbody",children:[s.jsx("div",{className:"row",children:s.jsxs("span",{className:"desc",children:["store 已从 v",e.migration.from," 迁到 v",e.migration.to,":密钥栏目字段 ",e.migration.fieldsBefore," → ",e.migration.fieldsAfter,",",e.migration.entries," 条条目"]})}),s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"desc",children:"迁移前的原始文件"}),s.jsx("span",{className:"acts",children:s.jsx(r,{className:"mono",text:e.migration.snapshotRaw||"",children:e.migration.snapshot})})]}),e.migration.legacyValuesKept?s.jsx("div",{className:"row",children:s.jsxs("span",{className:"desc",children:["另保留了 ",e.migration.legacyValuesKept," 个旧字段的值(不再显示,但没删)"]})}):null]})]}):null,e!=null&&e.corrupt?s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"上次读取异常"}),s.jsx("div",{className:"colbody",children:s.jsx("div",{className:"row",children:s.jsxs("span",{className:"desc bad",children:["store.json 解析失败,已挪到 ",e.corrupt.to,"(原文件没有被覆盖)"]})})})]}):null,s.jsx(I,{module:"settings"})]})}function c({label:e,value:t,copy:n}){return s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"name",children:e}),s.jsx("span",{className:"desc mono",children:t||"(空)"}),n?s.jsx("span",{className:"acts",children:s.jsx(r,{className:"mono",text:n,children:"复制"})}):s.jsx($,{children:" "})]})}export{D as default};
1
+ import{u as b,r as d,a as k,j as s,b as r,E as F,C as I,T as $}from"./index-LbNmGlYj.js";function D(){var N,v,u,g,f,w,y,S;const{boot:e,reload:t,toast:n,guard:C}=b(),[o,h]=d.useState(null),[l,i]=d.useState(null),j=typeof window>"u"?"":window.location.origin,p=typeof window>"u"?"":window.location.port||"80",m=d.useCallback(async()=>{try{const a=await k("/api/settings");h(a),i({...a.settings})}catch(a){n(a.message,"bad")}},[n]);d.useEffect(()=>{m()},[m]);const E=C(async()=>{const a={exportDir:l.exportDir,exportFormat:l.exportFormat,includeSecretsInExport:!!l.includeSecretsInExport,defaultSection:l.defaultSection},x=await k("/api/settings",{method:"PATCH",body:a});n("设置已保存"),h({...o,settings:x.set}),await t()});return!o||!l?s.jsx("div",{className:"pad muted",children:"加载中…"}):s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"toolbar rowgap pad",children:[s.jsx("strong",{children:"设置"}),s.jsx("span",{className:"muted",children:"本机配置与运行状态"}),s.jsx("span",{className:"grow"}),s.jsx("button",{className:"btn small ghost",onClick:m,children:"重新读取"})]}),s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"存储与运行"}),s.jsxs("div",{className:"colbody",children:[s.jsx(c,{label:"数据目录",value:e==null?void 0:e.home,copy:e==null?void 0:e.home}),s.jsx(c,{label:"store.json",value:e==null?void 0:e.storePath,copy:e==null?void 0:e.storePathRaw}),s.jsx(c,{label:"栏目 / 条目",value:e?`${e.counts.sections} / ${e.counts.entries}`:""}),s.jsx(c,{label:"命令条数",value:e?String(e.counts.commands):""}),s.jsx(c,{label:"app / node",value:e?`v${e.app.version} · node ${e.app.node} · ${e.app.platform}`:""}),s.jsx(c,{label:"环境变量覆盖",value:e?Object.entries(e.envOverrides).filter(([,a])=>a).map(([a,x])=>`${a}=${x}`).join(" ")||"(无)":""})]})]}),s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"密钥(密文落盘)"}),s.jsxs("div",{className:"colbody",children:[s.jsx(c,{label:"密钥来源",value:((N=e==null?void 0:e.vault)==null?void 0:N.source)==="env"?"环境变量 NX_SK_PASSPHRASE(口令不落盘)":"本机密钥文件"}),s.jsx(c,{label:"密钥文件",value:((v=e==null?void 0:e.vault)==null?void 0:v.keyFile)||((u=e==null?void 0:e.vault)==null?void 0:u.saltFile)||"",copy:((g=e==null?void 0:e.vault)==null?void 0:g.keyFile)||((f=e==null?void 0:e.vault)==null?void 0:f.saltFile)}),s.jsx("div",{className:"row",children:s.jsx("span",{className:"desc",children:(w=e==null?void 0:e.vault)==null?void 0:w.note})}),s.jsx("div",{className:"row",children:s.jsx("span",{className:"desc bad",children:"删掉密钥文件或换掉口令后,已存的密文都解不开了——导出前先确认密钥可用。"})})]})]}),s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"面板与 CLI"}),s.jsxs("div",{className:"colbody",children:[s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"name",children:"面板地址"}),s.jsx("span",{className:"desc mono",children:j}),s.jsx("span",{className:"acts",children:s.jsx(r,{className:"mono",text:j,children:"复制"})})]}),s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"name",children:"重启面板"}),s.jsx("span",{className:"desc",children:"面板本身就是 serve 提供的;改代码后重启它即可"}),s.jsx("span",{className:"acts",children:s.jsx(r,{className:"mono",text:`nx-sk serve --port ${p}`,children:`nx-sk serve --port ${p}`})})]})]})]}),s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"默认行为"}),s.jsxs("div",{className:"pad",children:[s.jsxs("div",{className:"grid",children:[s.jsxs("div",{className:"field",children:[s.jsx("div",{className:"flabel",children:s.jsx("span",{children:"默认导出格式"})}),s.jsx("div",{className:"fctrl",children:s.jsxs("select",{value:l.exportFormat,onChange:a=>i({...l,exportFormat:a.target.value}),children:[s.jsx("option",{value:"both",children:"JSON + Markdown"}),s.jsx("option",{value:"json",children:"仅 JSON"}),s.jsx("option",{value:"md",children:"仅 Markdown"})]})})]}),s.jsxs("div",{className:"field",children:[s.jsxs("div",{className:"flabel",children:[s.jsx("span",{children:"默认导出目录"}),s.jsx("span",{className:"fkey",children:"留空 = ~/nx-sk/export"})]}),s.jsx("div",{className:"fctrl",children:s.jsx("input",{value:l.exportDir,onChange:a=>i({...l,exportDir:a.target.value})})})]}),s.jsxs("div",{className:"field",children:[s.jsxs("div",{className:"flabel",children:[s.jsx("span",{children:"默认栏目"}),s.jsx("span",{className:"fkey",children:"CLI 未指定 --section 时的首选"})]}),s.jsx("div",{className:"fctrl",children:s.jsx("input",{value:l.defaultSection,onChange:a=>i({...l,defaultSection:a.target.value})})})]}),s.jsxs("div",{className:"field",children:[s.jsxs("div",{className:"flabel",children:[s.jsx("span",{children:"导出默认带明文密钥"}),s.jsx("span",{className:"fkey",children:"默认开;要分享出去时关掉"})]}),s.jsx("div",{className:"fctrl",children:s.jsxs("label",{className:"inline",children:[s.jsx("input",{type:"checkbox",checked:!!l.includeSecretsInExport,onChange:a=>i({...l,includeSecretsInExport:a.target.checked})}),s.jsx("span",{className:l.includeSecretsInExport?"muted":"bad",children:l.includeSecretsInExport?"开(导出文件含真实凭据,注意别外发)":"关(凭据打码后再导出)"})]})})]})]}),s.jsx("div",{className:"rowgap",style:{marginTop:8},children:s.jsx("button",{className:"btn",onClick:E,children:"保存设置"})})]})]}),s.jsxs("div",{className:"card",children:[s.jsxs("div",{className:"colhead",children:["自动快照",s.jsx("span",{className:"right muted",children:((y=e==null?void 0:e.snapshots)==null?void 0:y.length)??0})]}),s.jsxs("div",{className:"colbody",children:[s.jsx("div",{className:"row",children:s.jsx("span",{className:"desc",children:"每次删除条目 / 栏目、修改设置前,都会在 ~/nx-sk/backup 留一份可回滚的整库快照。"})}),(S=e==null?void 0:e.snapshots)!=null&&S.length?e.snapshots.map(a=>s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"mono name",children:a.name}),s.jsx("span",{className:"desc",children:a.at}),s.jsx("span",{className:"acts",children:s.jsxs("span",{className:"tag",children:[Math.ceil(a.bytes/1024)," KB"]})})]},a.name)):s.jsx(F,{children:"还没有快照。"})]})]}),e!=null&&e.migration?s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"数据迁移"}),s.jsxs("div",{className:"colbody",children:[s.jsx("div",{className:"row",children:s.jsxs("span",{className:"desc",children:["store 已从 v",e.migration.from," 迁到 v",e.migration.to,":密钥栏目字段 ",e.migration.fieldsBefore," → ",e.migration.fieldsAfter,",",e.migration.entries," 条条目"]})}),s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"desc",children:"迁移前的原始文件"}),s.jsx("span",{className:"acts",children:s.jsx(r,{className:"mono",text:e.migration.snapshotRaw||"",children:e.migration.snapshot})})]}),e.migration.legacyValuesKept?s.jsx("div",{className:"row",children:s.jsxs("span",{className:"desc",children:["另保留了 ",e.migration.legacyValuesKept," 个旧字段的值(不再显示,但没删)"]})}):null]})]}):null,e!=null&&e.corrupt?s.jsxs("div",{className:"card",children:[s.jsx("div",{className:"colhead",children:"上次读取异常"}),s.jsx("div",{className:"colbody",children:s.jsx("div",{className:"row",children:s.jsxs("span",{className:"desc bad",children:["store.json 解析失败,已挪到 ",e.corrupt.to,"(原文件没有被覆盖)"]})})})]}):null,s.jsx(I,{module:"settings"})]})}function c({label:e,value:t,copy:n}){return s.jsxs("div",{className:"row",children:[s.jsx("span",{className:"name",children:e}),s.jsx("span",{className:"desc mono",children:t||"(空)"}),n?s.jsx("span",{className:"acts",children:s.jsx(r,{className:"mono",text:n,children:"复制"})}):s.jsx($,{children:" "})]})}export{D as default};
@@ -1 +1 @@
1
- import{u as q,r,a as p,j as e,b as w,T as A,E as H,C as _,M as z}from"./index-DzD8wu27.js";import{f as K,r as f}from"./files-2chm08H_.js";function S(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KB`:`${(t/(1024*1024)).toFixed(2)} MB`}const Q=t=>`${t.sectionTitle} / ${t.entryTitle||"(无名)"} / ${t.fieldLabel}`;function X(){const{toast:t,guard:l,dialog:m,reload:n}=q(),[i,k]=r.useState(null),[C,$]=r.useState(null),[u,N]=r.useState(""),[g,E]=r.useState(""),[D,v]=r.useState(!1),[d,h]=r.useState(null),T=r.useRef(null),x=r.useRef(0),o=r.useCallback(async()=>{try{const a=new URLSearchParams;u&&a.set("section",u),g&&a.set("q",g);const s=a.toString(),c=await p(`/api/files${s?`?${s}`:""}`);k(c),$(null)}catch(a){$(a.message),k(null)}},[u,g]);r.useEffect(()=>{o()},[o]);const b=r.useCallback(async a=>{const s=Array.from(a||[]);if(!s.length)return;let c=0;for(const j of s)try{const y=await K(j);await p("/api/files",{method:"POST",body:{content:y,name:j.name}}),c+=1}catch(y){t(`${j.name} 上传失败:${y.message}`,"bad")}c&&t(`已导入 ${c} 个附件(到栏目条目里选它即可链接)`),await f(),await o(),await n()},[o,n,t]),P=r.useCallback(a=>{a.preventDefault(),x.current=0,v(!1),b(a.dataTransfer.files)},[b]),I=l(async()=>{h({kind:"path",form:{path:"",copy:!1}})}),R=l(async a=>{if(!a.path.trim()){t("请填本机文件的绝对路径","bad");return}const s=await p("/api/files",{method:"POST",body:{path:a.path.trim(),copy:a.copy}});h(null),t(s.duplicated?"库中已有相同文件":s.moved?"已移动进附件库(可还原)":"已复制进附件库"),await f(),await o(),await n()}),B=l(async a=>{h({kind:"edit",id:a.id,form:{name:a.name,tags:a.tags.join("、"),note:a.note}})}),F=l(async(a,s)=>{await p(`/api/files/${encodeURIComponent(a)}`,{method:"PATCH",body:{name:s.name,tags:s.tags.split(/[,,、;;]+/).map(c=>c.trim()).filter(Boolean),note:s.note}}),h(null),t("附件已更新"),await f(),await o()}),U=l(async a=>{const s=await m({message:`把「${a.name}」还原到哪?`,detail:a.sourceDisplay?`默认移回原始位置并撤销台账:${a.sourceDisplay}`:"该附件没有原始位置记录,请在下一步填写目标目录。",input:a.sourceDisplay?void 0:{placeholder:"目标目录绝对路径,例如 D:\\资料"},confirmText:"移回并撤销台账",cancelText:"取消"});if(s===null)return;const c={};s&&typeof s=="string"&&(c.to=s.trim());const j=await p(`/api/files/${encodeURIComponent(a.id)}/restore`,{method:"POST",body:c});t(`已还原到 ${j.displayPath}`),await f(),await o(),await n()}),O=l(async a=>{const s=await m({message:`导出「${a.name}」的副本到哪个目录?库中原件保留。`,input:{placeholder:"目标目录绝对路径,例如 D:\\资料"},confirmText:"导出副本"});if(!s)return;const c=await p(`/api/files/${encodeURIComponent(a.id)}/restore`,{method:"POST",body:{to:String(s).trim(),copy:!0}});t(`已导出到 ${c.displayPath}`)}),L=l(async a=>{await m({message:`删除附件「${a.name}」?物理原件会一起删除,删除前自动留台账快照。`,detail:"想保留原件、只撤销台账,请改用「还原」。",danger:!0,confirmText:"删除"})&&(await p(`/api/files/${encodeURIComponent(a.id)}`,{method:"DELETE"}),t("已删除"),await f(),await o(),await n())});if(C)return e.jsxs("div",{className:"pad stack",children:[e.jsx("div",{className:"bad",children:e.jsx("strong",{children:"附件库读不到"})}),e.jsx("pre",{className:"block",children:C})]});if(!i)return e.jsx("div",{className:"pad muted",children:"加载中…"});const M=i.sections||[];return e.jsxs("div",{className:`files-page ${D?"drag-over":""}`,onDragEnter:a=>{a.preventDefault(),x.current+=1,v(!0)},onDragOver:a=>a.preventDefault(),onDragLeave:a=>{a.preventDefault(),x.current-=1,x.current<=0&&(x.current=0,v(!1))},onDrop:P,children:[e.jsxs("div",{className:"toolbar rowgap pad",children:[e.jsx("strong",{children:"附件"}),e.jsxs("span",{className:"muted",children:[i.total," 个 · ",S(i.totalBytes)]}),e.jsxs("span",{className:"hint",title:"被栏目条目的 file 字段引用的附件数",children:["被引用 ",i.total-i.unlinked,"/",i.total]}),e.jsx(w,{className:"hint",text:i.rootRaw,title:"点击复制附件库目录",children:i.root}),e.jsx("span",{className:"grow"}),e.jsx("input",{style:{width:180},placeholder:"搜索名称 / 备注 / 标签 / 引用它的条目",value:g,onChange:a=>E(a.target.value)}),e.jsx("button",{className:"btn small ghost",onClick:o,children:"刷新"}),e.jsx("button",{className:"btn small ghost",onClick:I,children:"本机路径导入"}),e.jsx("button",{className:"btn small",onClick:()=>{var a;return(a=T.current)==null?void 0:a.click()},children:"上传文件"}),e.jsx("input",{ref:T,type:"file",multiple:!0,style:{display:"none"},onChange:a=>{b(a.target.files),a.target.value=""}})]}),e.jsxs("div",{className:"rowgap pad file-filters",children:[e.jsxs("button",{className:`tab small ${u?"":"active"}`,onClick:()=>N(""),children:["全部",e.jsx("span",{className:"count",children:i.total})]}),M.map(a=>e.jsxs("button",{className:`tab small ${u===a.id?"active":""}`,onClick:()=>N(a.id),title:`被「${a.title}」栏目引用的附件`,children:[a.title,e.jsx("span",{className:"count",children:a.count})]},a.id)),e.jsxs("button",{className:`tab small ${u==="none"?"active":""}`,onClick:()=>N("none"),children:["未引用",e.jsx("span",{className:"count",children:i.unlinked})]})]}),D?e.jsx("div",{className:"drop-hint",children:"松开鼠标,文件直接导入附件库"}):null,i.files.length?e.jsx("div",{className:"file-grid pad",children:i.files.map(a=>{var s;return e.jsxs("div",{className:"file-card",children:[e.jsx("a",{className:"file-thumb",href:a.url,target:"_blank",rel:"noreferrer",title:"在浏览器中预览",children:a.isImage?e.jsx("img",{src:a.url,alt:a.name,loading:"lazy"}):e.jsx("span",{className:"ext-badge",children:a.ext?a.ext.slice(1).toUpperCase():"FILE"})}),e.jsxs("div",{className:"file-meta",children:[e.jsx("a",{className:"file-name",href:a.url,target:"_blank",rel:"noreferrer",title:a.name,children:a.name}),e.jsxs("div",{className:"rowgap",style:{gap:4},children:[e.jsx("span",{className:"hint",children:S(a.bytes)}),a.mode==="move"?e.jsx(A,{children:"移动导入"}):null]}),(s=a.usedBy)!=null&&s.length?e.jsxs("div",{className:"hint",title:"条目的 file 字段引用了本附件",children:["被引用:",a.usedBy.map(Q).join("、")]}):e.jsx("div",{className:"hint",children:"未被任何条目引用(到栏目条目里选它即可链接)"})]}),e.jsxs("div",{className:"file-acts",children:[e.jsx("a",{className:"btn small ghost",href:a.downloadUrl,children:"下载"}),e.jsx("button",{className:"btn small ghost",onClick:()=>O(a),children:"导出副本"}),e.jsx("button",{className:"btn small ghost",onClick:()=>U(a),children:"还原"}),e.jsx("button",{className:"btn small ghost",onClick:()=>B(a),children:"编辑"}),e.jsx("button",{className:"btn small ghost danger-ghost",onClick:()=>L(a),children:"删除"}),e.jsx("span",{className:"grow"}),e.jsx(w,{className:"hint",text:a.path,title:"点击复制物理路径",children:"路径"}),e.jsx(w,{className:"hint",text:a.url,title:"点击复制链接",children:"链接"})]})]},a.id)})}):e.jsxs("div",{className:"pad",children:[e.jsx(H,{children:"还没有附件。把文件拖到这里、点「上传文件」,或用「本机路径导入」把原件移动进库;随后到栏目的条目里选它。"}),e.jsx("div",{className:"hint pad",children:'CLI:nx-sk file add --path "D:\\简历\\张三-简历.pdf"(默认移动;--copy 仅复制)'})]}),e.jsx(_,{module:"files"}),d?e.jsx(z,{title:d.kind==="edit"?"编辑附件":"从本机路径导入",onClose:()=>h(null),children:d.kind==="path"?e.jsx(V,{form:d.form,onChange:a=>h(s=>({...s,form:{...s.form,...a}})),onSubmit:()=>R(d.form)}):e.jsx(G,{form:d.form,onChange:a=>h(s=>({...s,form:{...s.form,...a}})),onSubmit:()=>F(d.id,d.form)})}):null]})}function V({form:t,onChange:l,onSubmit:m}){return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"本机文件的绝对路径"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:t.path,placeholder:"例如 D:\\简历\\张三-简历.pdf",onChange:n=>l({path:n.target.value})})})]}),e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"导入方式"})}),e.jsx("div",{className:"fctrl",children:e.jsxs("label",{className:"inline",children:[e.jsx("input",{type:"checkbox",checked:t.copy,onChange:n=>l({copy:n.target.checked})}),e.jsx("span",{className:"hint",children:"仅复制、保留原件(默认把原件移动进库,可用「还原」撤销)"})]})})]}),e.jsx("div",{className:"rowgap",children:e.jsx("button",{className:"btn",onClick:m,children:"导入"})})]})}function G({form:t,onChange:l,onSubmit:m}){return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"文件名"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:t.name,onChange:n=>l({name:n.target.value})})})]}),e.jsxs("div",{className:"field wide",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"标签"}),e.jsx("span",{className:"fkey",children:"多个用「、」分隔"})]}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:t.tags,onChange:n=>l({tags:n.target.value})})})]}),e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"备注"})}),e.jsx("div",{className:"fctrl",children:e.jsx("textarea",{rows:2,value:t.note,onChange:n=>l({note:n.target.value})})})]}),e.jsxs("div",{className:"rowgap",children:[e.jsx("button",{className:"btn",onClick:m,children:"保存"}),e.jsx("span",{className:"hint",children:"改文件名时,物理原件会随之移动。要链接到条目,请在栏目条目的 file 字段里选本附件。"})]})]})}export{X as default};
1
+ import{u as q,r,a as p,j as e,b as w,T as A,E as H,C as _,M as z}from"./index-LbNmGlYj.js";import{f as K,r as f}from"./files-CHheSoYs.js";function S(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KB`:`${(t/(1024*1024)).toFixed(2)} MB`}const Q=t=>`${t.sectionTitle} / ${t.entryTitle||"(无名)"} / ${t.fieldLabel}`;function X(){const{toast:t,guard:l,dialog:m,reload:n}=q(),[i,k]=r.useState(null),[C,$]=r.useState(null),[u,N]=r.useState(""),[g,E]=r.useState(""),[D,v]=r.useState(!1),[d,h]=r.useState(null),T=r.useRef(null),x=r.useRef(0),o=r.useCallback(async()=>{try{const a=new URLSearchParams;u&&a.set("section",u),g&&a.set("q",g);const s=a.toString(),c=await p(`/api/files${s?`?${s}`:""}`);k(c),$(null)}catch(a){$(a.message),k(null)}},[u,g]);r.useEffect(()=>{o()},[o]);const b=r.useCallback(async a=>{const s=Array.from(a||[]);if(!s.length)return;let c=0;for(const j of s)try{const y=await K(j);await p("/api/files",{method:"POST",body:{content:y,name:j.name}}),c+=1}catch(y){t(`${j.name} 上传失败:${y.message}`,"bad")}c&&t(`已导入 ${c} 个附件(到栏目条目里选它即可链接)`),await f(),await o(),await n()},[o,n,t]),P=r.useCallback(a=>{a.preventDefault(),x.current=0,v(!1),b(a.dataTransfer.files)},[b]),I=l(async()=>{h({kind:"path",form:{path:"",copy:!1}})}),R=l(async a=>{if(!a.path.trim()){t("请填本机文件的绝对路径","bad");return}const s=await p("/api/files",{method:"POST",body:{path:a.path.trim(),copy:a.copy}});h(null),t(s.duplicated?"库中已有相同文件":s.moved?"已移动进附件库(可还原)":"已复制进附件库"),await f(),await o(),await n()}),B=l(async a=>{h({kind:"edit",id:a.id,form:{name:a.name,tags:a.tags.join("、"),note:a.note}})}),F=l(async(a,s)=>{await p(`/api/files/${encodeURIComponent(a)}`,{method:"PATCH",body:{name:s.name,tags:s.tags.split(/[,,、;;]+/).map(c=>c.trim()).filter(Boolean),note:s.note}}),h(null),t("附件已更新"),await f(),await o()}),U=l(async a=>{const s=await m({message:`把「${a.name}」还原到哪?`,detail:a.sourceDisplay?`默认移回原始位置并撤销台账:${a.sourceDisplay}`:"该附件没有原始位置记录,请在下一步填写目标目录。",input:a.sourceDisplay?void 0:{placeholder:"目标目录绝对路径,例如 D:\\资料"},confirmText:"移回并撤销台账",cancelText:"取消"});if(s===null)return;const c={};s&&typeof s=="string"&&(c.to=s.trim());const j=await p(`/api/files/${encodeURIComponent(a.id)}/restore`,{method:"POST",body:c});t(`已还原到 ${j.displayPath}`),await f(),await o(),await n()}),O=l(async a=>{const s=await m({message:`导出「${a.name}」的副本到哪个目录?库中原件保留。`,input:{placeholder:"目标目录绝对路径,例如 D:\\资料"},confirmText:"导出副本"});if(!s)return;const c=await p(`/api/files/${encodeURIComponent(a.id)}/restore`,{method:"POST",body:{to:String(s).trim(),copy:!0}});t(`已导出到 ${c.displayPath}`)}),L=l(async a=>{await m({message:`删除附件「${a.name}」?物理原件会一起删除,删除前自动留台账快照。`,detail:"想保留原件、只撤销台账,请改用「还原」。",danger:!0,confirmText:"删除"})&&(await p(`/api/files/${encodeURIComponent(a.id)}`,{method:"DELETE"}),t("已删除"),await f(),await o(),await n())});if(C)return e.jsxs("div",{className:"pad stack",children:[e.jsx("div",{className:"bad",children:e.jsx("strong",{children:"附件库读不到"})}),e.jsx("pre",{className:"block",children:C})]});if(!i)return e.jsx("div",{className:"pad muted",children:"加载中…"});const M=i.sections||[];return e.jsxs("div",{className:`files-page ${D?"drag-over":""}`,onDragEnter:a=>{a.preventDefault(),x.current+=1,v(!0)},onDragOver:a=>a.preventDefault(),onDragLeave:a=>{a.preventDefault(),x.current-=1,x.current<=0&&(x.current=0,v(!1))},onDrop:P,children:[e.jsxs("div",{className:"toolbar rowgap pad",children:[e.jsx("strong",{children:"附件"}),e.jsxs("span",{className:"muted",children:[i.total," 个 · ",S(i.totalBytes)]}),e.jsxs("span",{className:"hint",title:"被栏目条目的 file 字段引用的附件数",children:["被引用 ",i.total-i.unlinked,"/",i.total]}),e.jsx(w,{className:"hint",text:i.rootRaw,title:"点击复制附件库目录",children:i.root}),e.jsx("span",{className:"grow"}),e.jsx("input",{style:{width:180},placeholder:"搜索名称 / 备注 / 标签 / 引用它的条目",value:g,onChange:a=>E(a.target.value)}),e.jsx("button",{className:"btn small ghost",onClick:o,children:"刷新"}),e.jsx("button",{className:"btn small ghost",onClick:I,children:"本机路径导入"}),e.jsx("button",{className:"btn small",onClick:()=>{var a;return(a=T.current)==null?void 0:a.click()},children:"上传文件"}),e.jsx("input",{ref:T,type:"file",multiple:!0,style:{display:"none"},onChange:a=>{b(a.target.files),a.target.value=""}})]}),e.jsxs("div",{className:"rowgap pad file-filters",children:[e.jsxs("button",{className:`tab small ${u?"":"active"}`,onClick:()=>N(""),children:["全部",e.jsx("span",{className:"count",children:i.total})]}),M.map(a=>e.jsxs("button",{className:`tab small ${u===a.id?"active":""}`,onClick:()=>N(a.id),title:`被「${a.title}」栏目引用的附件`,children:[a.title,e.jsx("span",{className:"count",children:a.count})]},a.id)),e.jsxs("button",{className:`tab small ${u==="none"?"active":""}`,onClick:()=>N("none"),children:["未引用",e.jsx("span",{className:"count",children:i.unlinked})]})]}),D?e.jsx("div",{className:"drop-hint",children:"松开鼠标,文件直接导入附件库"}):null,i.files.length?e.jsx("div",{className:"file-grid pad",children:i.files.map(a=>{var s;return e.jsxs("div",{className:"file-card",children:[e.jsx("a",{className:"file-thumb",href:a.url,target:"_blank",rel:"noreferrer",title:"在浏览器中预览",children:a.isImage?e.jsx("img",{src:a.url,alt:a.name,loading:"lazy"}):e.jsx("span",{className:"ext-badge",children:a.ext?a.ext.slice(1).toUpperCase():"FILE"})}),e.jsxs("div",{className:"file-meta",children:[e.jsx("a",{className:"file-name",href:a.url,target:"_blank",rel:"noreferrer",title:a.name,children:a.name}),e.jsxs("div",{className:"rowgap",style:{gap:4},children:[e.jsx("span",{className:"hint",children:S(a.bytes)}),a.mode==="move"?e.jsx(A,{children:"移动导入"}):null]}),(s=a.usedBy)!=null&&s.length?e.jsxs("div",{className:"hint",title:"条目的 file 字段引用了本附件",children:["被引用:",a.usedBy.map(Q).join("、")]}):e.jsx("div",{className:"hint",children:"未被任何条目引用(到栏目条目里选它即可链接)"})]}),e.jsxs("div",{className:"file-acts",children:[e.jsx("a",{className:"btn small ghost",href:a.downloadUrl,children:"下载"}),e.jsx("button",{className:"btn small ghost",onClick:()=>O(a),children:"导出副本"}),e.jsx("button",{className:"btn small ghost",onClick:()=>U(a),children:"还原"}),e.jsx("button",{className:"btn small ghost",onClick:()=>B(a),children:"编辑"}),e.jsx("button",{className:"btn small ghost danger-ghost",onClick:()=>L(a),children:"删除"}),e.jsx("span",{className:"grow"}),e.jsx(w,{className:"hint",text:a.path,title:"点击复制物理路径",children:"路径"}),e.jsx(w,{className:"hint",text:a.url,title:"点击复制链接",children:"链接"})]})]},a.id)})}):e.jsxs("div",{className:"pad",children:[e.jsx(H,{children:"还没有附件。把文件拖到这里、点「上传文件」,或用「本机路径导入」把原件移动进库;随后到栏目的条目里选它。"}),e.jsx("div",{className:"hint pad",children:'CLI:nx-sk file add --path "D:\\简历\\张三-简历.pdf"(默认移动;--copy 仅复制)'})]}),e.jsx(_,{module:"files"}),d?e.jsx(z,{title:d.kind==="edit"?"编辑附件":"从本机路径导入",onClose:()=>h(null),children:d.kind==="path"?e.jsx(V,{form:d.form,onChange:a=>h(s=>({...s,form:{...s.form,...a}})),onSubmit:()=>R(d.form)}):e.jsx(G,{form:d.form,onChange:a=>h(s=>({...s,form:{...s.form,...a}})),onSubmit:()=>F(d.id,d.form)})}):null]})}function V({form:t,onChange:l,onSubmit:m}){return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"本机文件的绝对路径"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:t.path,placeholder:"例如 D:\\简历\\张三-简历.pdf",onChange:n=>l({path:n.target.value})})})]}),e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"导入方式"})}),e.jsx("div",{className:"fctrl",children:e.jsxs("label",{className:"inline",children:[e.jsx("input",{type:"checkbox",checked:t.copy,onChange:n=>l({copy:n.target.checked})}),e.jsx("span",{className:"hint",children:"仅复制、保留原件(默认把原件移动进库,可用「还原」撤销)"})]})})]}),e.jsx("div",{className:"rowgap",children:e.jsx("button",{className:"btn",onClick:m,children:"导入"})})]})}function G({form:t,onChange:l,onSubmit:m}){return e.jsxs("div",{className:"stack",children:[e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"文件名"})}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:t.name,onChange:n=>l({name:n.target.value})})})]}),e.jsxs("div",{className:"field wide",children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:"标签"}),e.jsx("span",{className:"fkey",children:"多个用「、」分隔"})]}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:t.tags,onChange:n=>l({tags:n.target.value})})})]}),e.jsxs("div",{className:"field wide",children:[e.jsx("div",{className:"flabel",children:e.jsx("span",{children:"备注"})}),e.jsx("div",{className:"fctrl",children:e.jsx("textarea",{rows:2,value:t.note,onChange:n=>l({note:n.target.value})})})]}),e.jsxs("div",{className:"rowgap",children:[e.jsx("button",{className:"btn",onClick:m,children:"保存"}),e.jsx("span",{className:"hint",children:"改文件名时,物理原件会随之移动。要链接到条目,请在栏目条目的 file 字段里选本附件。"})]})]})}export{X as default};
@@ -0,0 +1,7 @@
1
+ import{j as e,u as _,r as h,a as w,b as H,E as L,C as q,T as B,M as z}from"./index-LbNmGlYj.js";import{l as V,f as Y,r as G}from"./files-CHheSoYs.js";function Q({value:c,onChange:g,minRows:m=2,placeholder:x,disabled:l,maxLength:d}){const v=h.useRef(null);return h.useLayoutEffect(()=>{const s=v.current;s&&(s.style.height="auto",s.style.height=`${s.scrollHeight}px`)},[c]),e.jsx("textarea",{ref:v,value:String(c??""),onChange:s=>g(s.target.value),rows:m,placeholder:x,disabled:l,maxLength:d,style:{resize:"none",overflow:"hidden",width:"100%"}})}function W(c){const g=c==null?[]:Array.isArray(c)?c:[c],m=[];for(const x of g)for(const l of String(x??"").split(/[,,、;;]+/)){const d=l.trim();d&&!m.includes(d)&&m.push(d)}return m}function J({def:c,value:g,onChange:m}){const{toast:x,guard:l,patchUi:d}=_(),[v,s]=h.useState(null),[p,j]=h.useState(!1),S=h.useRef(null),C=h.useRef(0),f=W(g),r=Number(c.maxItems)||0,b=r>0&&f.length>=r,N=h.useCallback(async()=>{try{s(await V())}catch{}},[]);h.useEffect(()=>{N()},[N]);const I=(v==null?void 0:v.files)||[],T=i=>I.find(o=>o.id===i)||null,E=I.filter(i=>!f.includes(i.id)),D=l(async i=>{const o=[...i||[]];if(!o.length)return;if(b){x(`「${c.label}」最多 ${r} 个附件,先移除一个`,"bad");return}const U=[...f];let A=0;for(const F of o){const K=await Y(F),R=await w("/api/files",{method:"POST",body:{content:K,name:F.name}});if(U.includes(R.id)){A+=1;continue}U.push(R.id)}m(U);const O=o.length-A;x(A?`已上传并关联 ${O} 个;${A} 个库里已有,直接引用`:`已上传并关联 ${o.length} 个`),s(await G())}),a=i=>{i&&!f.includes(i)&&m([...f,i])},t=i=>m(f.filter(o=>o!==i)),y=l(async i=>{var o;C.current=0,j(!1),await D((o=i.dataTransfer)==null?void 0:o.files)});return e.jsxs("div",{className:`file-picker ${p?"drag":""}`,onDragEnter:i=>{i.preventDefault(),C.current+=1,j(!0)},onDragOver:i=>i.preventDefault(),onDragLeave:()=>{C.current-=1,C.current<=0&&(C.current=0,j(!1))},onDrop:y,children:[f.map(i=>{const o=T(i);return o?e.jsxs("div",{className:"file-preview",children:[o.isImage?e.jsx("img",{src:o.url,alt:o.name}):e.jsx("span",{className:"ext-badge",style:{fontSize:13,width:40,textAlign:"center"},children:o.ext?o.ext.slice(1).toUpperCase():"FILE"}),e.jsx("a",{className:"file-preview-name grow",href:o.url,target:"_blank",rel:"noreferrer",title:o.displayPath,children:o.name}),e.jsx("a",{className:"btn small ghost",href:o.downloadUrl,children:"下载"}),e.jsx(H,{className:"hint",text:o.path,title:"点击复制物理路径",children:"路径"}),e.jsx("button",{type:"button",className:"btn small ghost",onClick:()=>t(i),children:"移除"})]},i):e.jsxs("div",{className:"file-preview",children:[e.jsxs("span",{className:"hint bad grow",children:["引用的附件已不存在(",i,")—— 重新上传,或移除。"]}),e.jsx("button",{type:"button",className:"btn small ghost",onClick:()=>t(i),children:"移除"})]},i)}),f.length?null:e.jsx("span",{className:"hint",children:"还没有附件。"}),e.jsxs("div",{className:"file-pick-row",children:[e.jsxs("select",{value:"",disabled:b||!E.length,onChange:i=>a(i.target.value),children:[e.jsx("option",{value:"",children:E.length?"(从附件库选一个,选中即追加)":"(库里没有可再选的附件)"}),E.map(i=>e.jsx("option",{value:i.id,children:i.name},i.id))]}),e.jsx("button",{type:"button",className:"btn small",disabled:b,onClick:()=>{var i;return(i=S.current)==null?void 0:i.click()},children:"上传文件"}),e.jsx("button",{type:"button",className:"btn small ghost",onClick:()=>d({view:"files"}),children:"附件中心"}),e.jsx("input",{ref:S,type:"file",multiple:!0,style:{display:"none"},onChange:i=>{D(i.target.files),i.target.value=""}})]}),b?e.jsxs("span",{className:"hint",children:["已达上限(",r," 个):先移除一个,或把字段的 maxItems 调大。"]}):null,c.hint?e.jsx("span",{className:"hint",children:c.hint}):null]})}function X({def:c,value:g,onChange:m,disabled:x}){const l=c.type||"text",d=g??"";if(l==="file")return e.jsx(J,{def:c,value:d,onChange:m});if(l==="bool"){const s=d===!0||d==="true"||d==="是";return e.jsxs("label",{className:"inline",children:[e.jsx("input",{type:"checkbox",checked:s,onChange:p=>m(p.target.checked)}),e.jsx("span",{className:"muted",children:s?"是":"否"})]})}if(l==="select"){const s=c.options||[],p=d!==""&&!s.includes(d);return e.jsxs("select",{value:p?"__custom":String(d),onChange:j=>m(j.target.value==="__custom"?"":j.target.value),children:[e.jsx("option",{value:"",children:"(未填写)"}),s.map(j=>e.jsx("option",{value:j,children:j},j)),p?e.jsxs("option",{value:"__custom",children:[String(d),"(当前值,不在候选里)"]}):null]})}if(l==="textarea")return e.jsx(Q,{value:d,onChange:m});if(l==="tags"){const s=Array.isArray(d)?d.join("、"):String(d);return e.jsx("input",{value:s,placeholder:c.maxItems?`多个用「、」分隔,最多 ${c.maxItems} 个`:"多个用「、」分隔",onChange:p=>m(p.target.value.split(/[,,、;;]+/).map(j=>j.trim()).filter(Boolean))})}const v=l==="number"?"number":l==="date"?"date":l==="month"?"month":"text";return l==="secret"?e.jsx("input",{type:"text",value:String(d),placeholder:"密文落盘;原样保留占位则视为不修改",disabled:x,onChange:s=>m(s.target.value)}):e.jsx("input",{type:v,value:String(d),onChange:s=>m(s.target.value)})}function Z({title:c,fields:g,draft:m,onPatch:x}){return e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"group-title",children:c}),e.jsx("div",{className:"pad",children:e.jsx("div",{className:"grid",children:g.map(l=>e.jsxs("div",{className:`field ${l.type==="textarea"||l.type==="file"?"wide":""}`,children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:l.label}),l.sensitive?e.jsx("span",{className:"tag bad",children:"密文"}):null,l.fill&&l.fill!=="normal"?e.jsx("span",{className:"tag",children:l.fillLabel||l.fill}):null,e.jsx("span",{className:"fkey",children:l.key})]}),e.jsx("div",{className:"fctrl",children:e.jsx(X,{def:l,value:m[l.key],onChange:d=>x(l.key,d)})})]},l.key))})})]})}function ee({sectionId:c,sectionTitle:g}){const{ui:m,patchUi:x,toast:l,guard:d,dialog:v,reload:s}=_(),[p,j]=h.useState(null),[S,C]=h.useState({}),[f,r]=h.useState({name:"",value:""}),b=!!m.mask,N=h.useCallback(async()=>{try{const a=`section=${encodeURIComponent(c)}${b?"&mask=1":""}`,t=await w(`/api/keys?${a}`);j(t);const y={};for(const i of t.keys)y[i.name]=i.value??"";C(y)}catch(a){l(a.message,"bad"),j(null)}},[c,b,l]);h.useEffect(()=>{N()},[N]);const I=d(async a=>{const t=S[a.name];(a.value??"")!==t&&(await w(`/api/keys/${encodeURIComponent(a.name)}`,{method:"PUT",body:{value:t,section:c}}),l(`已保存 ${a.name}`),await N())}),T=d(async(a,t)=>{const y=String(t||"").trim();if(!(!y||y===a.name)){if(p.keys.some(i=>i.name===y)){l(`已经有叫「${y}」的键了`,"bad"),await N();return}await w(`/api/entries/${encodeURIComponent(a.id)}`,{method:"PATCH",body:{title:y}}),C(i=>{const o={};for(const[U,A]of Object.entries(i))o[U===a.name?y:U]=A;return o}),l(`已重命名为 ${y}`),await N(),await s()}}),E=d(async()=>{const a=f.name.trim();if(!a){l("先填键名,例如 OPENAI_KEY","bad");return}if(p.keys.some(t=>t.name===a)){l(`已经有叫「${a}」的键了——直接在上面那行改值即可`,"bad");return}await w(`/api/keys/${encodeURIComponent(a)}`,{method:"PUT",body:{value:f.value,section:c}}),r({name:"",value:""}),l(`已新增 ${a}`),await N(),await s()}),D=d(async a=>{await v({message:`删除「${a.name}」?删除前会自动留一份整库快照在 ~/nx-sk/backup。`,danger:!0,confirmText:"删除"})&&(await w(`/api/keys/${encodeURIComponent(a.name)}`,{method:"DELETE",body:{section:c}}),l(`已删除 ${a.name}`),await N(),await s())});return p?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"toolbar rowgap pad",children:[e.jsx("strong",{children:g}),e.jsxs("span",{className:"muted",children:[p.count," 个键 · 名称 → 值"]}),e.jsx("span",{className:"grow"}),e.jsxs("label",{className:"inline hint",children:[e.jsx("input",{type:"checkbox",checked:b,onChange:()=>x({mask:!b})}),"打码显示(投屏/截图时用)"]}),e.jsx("button",{className:"btn small ghost",onClick:N,children:"刷新"})]}),e.jsxs("div",{className:"card",children:[e.jsxs("div",{className:"colhead",children:[e.jsx("span",{style:{width:200},children:"名称"}),e.jsx("span",{className:"grow",children:"值"})]}),e.jsxs("div",{className:"colbody",children:[p.keys.length?p.keys.map(a=>e.jsxs("div",{className:"row",children:[e.jsx("input",{className:"mono",style:{width:200},defaultValue:a.name,title:"改完按回车或点开别处即重命名",onBlur:t=>T(a,t.target.value),onKeyDown:t=>{t.key==="Enter"&&t.currentTarget.blur()}}),e.jsx("input",{className:"mono grow",value:S[a.name]??"",onChange:t=>C(y=>({...y,[a.name]:t.target.value})),onBlur:()=>I(a),onKeyDown:t=>{t.key==="Enter"&&t.currentTarget.blur()}}),e.jsxs("span",{className:"acts",children:[e.jsx(H,{className:"tag",text:S[a.name]??"",title:"点击复制值",children:"复制"}),e.jsx("button",{className:"btn small ghost",onClick:()=>D(a),children:"删除"})]})]},a.id)):e.jsx(L,{children:"还没有密钥。在下面一行填「名称 / 值」按添加,或 CLI:nx-sk key set OPENAI_KEY sk-xxx"}),e.jsxs("div",{className:"row",children:[e.jsx("input",{className:"mono",style:{width:200},placeholder:"新键名,例如 OPENAI_KEY",value:f.name,onChange:a=>r(t=>({...t,name:a.target.value})),onKeyDown:a=>{a.key==="Enter"&&E()}}),e.jsx("input",{className:"mono grow",placeholder:"值(留空也行,之后再填)",value:f.value,onChange:a=>r(t=>({...t,value:a.target.value})),onKeyDown:a=>{a.key==="Enter"&&E()}}),e.jsx("span",{className:"acts",children:e.jsx("button",{className:"btn small",onClick:E,children:"添加"})})]})]})]}),e.jsx("div",{className:"pad hint",children:"改值:直接在「值」列改完按回车(或点开别处)即保存。改键名同理。 值以密文落盘,这里显示的是原文;要打码勾上方那个框。删除前自动整库快照。"}),e.jsx(q,{module:"entries"})]}):e.jsx("div",{className:"pad muted",children:"加载中…"})}const M=c=>c==null?"":Array.isArray(c)?c.join("、"):String(c);function ne({sectionId:c}){var R;const{ui:g,patchUi:m,toast:x,guard:l,dialog:d,reload:v}=_(),[s,p]=h.useState(null),[j,S]=h.useState(null),[C,f]=h.useState(!1),[r,b]=h.useState(null),[N,I]=h.useState(null),[T,E]=h.useState(""),D=!!g.mask,a=h.useCallback(async n=>{f(!0),S(null);try{const u=await w(`/api/sections/${encodeURIComponent(c)}/dump${D?"?mask=1":""}`);p(u);const $=n||g.entry,k=u.entries.find(P=>P.id===$)||u.entries[0]||null;b(k?{id:k.id,title:k.title,values:{...k.values}}:null),k&&k.id!==g.entry&&m({entry:k.id})}catch(u){S(u.message),p(null),b(null)}finally{f(!1)}},[c,D]);h.useEffect(()=>{a()},[a]);const t=h.useMemo(()=>(s==null?void 0:s.entries.find(n=>n.id===(r==null?void 0:r.id)))||null,[s,r]),y=h.useMemo(()=>s?s.groups.map(n=>({...n,fields:s.fields.filter(u=>u.group===n.id)})).filter(n=>n.fields.length):[],[s]),i=(n,u)=>b($=>$&&{...$,values:{...$.values,[n]:u}}),o=h.useMemo(()=>{if(!r||!t)return{set:{},titleChanged:!1,count:0};const n={};for(const u of s.fields)M(t.values[u.key])!==M(r.values[u.key])&&(n[u.key]=r.values[u.key]);return{set:n,titleChanged:r.title!==t.title,count:Object.keys(n).length+(r.title!==t.title?1:0)}},[r,t,s]),U=l(async()=>{if(!r||!t)return;if(!o.count){x("没有改动");return}const n={set:o.set};o.titleChanged&&(n.title=r.title),await w(`/api/entries/${encodeURIComponent(r.id)}`,{method:"PATCH",body:n}),x(`已保存 ${o.count} 处改动`),await a(r.id),await v()}),A=l(async()=>{var P;if(!r){x("先新建一条条目","bad");return}const n=T.indexOf("=");if(n<=0){x("用 字段=值 的格式,例如 微信=zhangsan","bad");return}const u=T.slice(0,n).trim(),$=T.slice(n+1),k=await w(`/api/entries/${encodeURIComponent(r.id)}`,{method:"PATCH",body:{set:{[u]:$}}});E(""),x(`已写入 ${u}${(P=k.newFields)!=null&&P.length?"(新字段已登记)":""}`),await a(r.id),await v()}),O=l(async()=>{const n=(s==null?void 0:s.section.titleLabel)||"名称",u=await d({message:`新建「${s==null?void 0:s.section.title}」条目,${n}是?`,input:{placeholder:`例如 ${(s==null?void 0:s.section.titleField)||"名称"}`}});if(!u)return;const $={section:c,title:u};s!=null&&s.section.titleField&&($.set={[s.section.titleField]:u});const k=await w("/api/entries",{method:"POST",body:$});x(`已新建:${k.title}`),await a(k.id),await v()}),F=l(async()=>{!t||!await d({message:`删除条目「${t.title}」?删除前会自动留一份快照在 ~/nx-sk/backup。`,danger:!0,confirmText:"删除"})||(await w(`/api/entries/${encodeURIComponent(t.id)}`,{method:"DELETE"}),x("已删除"),await a(""),await v())}),K=l(async()=>{const n=await w("/api/export",{method:"POST",body:{section:c,format:"both"}});I({title:"导出结果",body:e.jsx("pre",{className:"block",children:`目录: ${n.dir}
2
+ 格式: ${n.format}
3
+ 条目: ${n.entries}
4
+ 凭据: ${n.withSecrets?"含明文":"已打码"}
5
+
6
+ ${n.files.map(u=>`${u.path} ${u.bytes} 字节`).join(`
7
+ `)}`})})});return j?e.jsxs("div",{className:"pad stack",children:[e.jsx("div",{className:"bad",children:e.jsx("strong",{children:"栏目读不到"})}),e.jsx("pre",{className:"block",children:j}),e.jsx("div",{className:"hint",children:"如果这个栏目刚被删掉,切到「栏目」tab 看看现有栏目。"})]}):s?s.section.kv?e.jsx(ee,{sectionId:c,sectionTitle:s.section.title}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"toolbar rowgap pad",children:[e.jsx("strong",{children:s.section.title}),e.jsx(B,{children:s.section.id}),e.jsxs("span",{className:"muted",children:[s.count," 条 / ",s.section.fields," 字段"]}),s.section.description?e.jsx("span",{className:"desc muted nowrap grow",children:s.section.description}):e.jsx("span",{className:"grow"}),e.jsxs("label",{className:"inline hint",children:[e.jsx("input",{type:"checkbox",checked:D,onChange:()=>m({mask:!D})}),"打码显示(投屏/截图时用)"]}),e.jsx("button",{className:"btn small ghost",onClick:()=>a(),children:"刷新"}),e.jsx("button",{className:"btn small ghost",onClick:K,children:"导出本栏目"}),e.jsx("button",{className:"btn small",onClick:O,children:"新建条目"})]}),e.jsxs("div",{className:"cols",children:[e.jsx("div",{className:"col",style:{flex:"0 0 300px"},children:e.jsxs("div",{className:"card",children:[e.jsxs("div",{className:"colhead",children:["条目列表",e.jsx("span",{className:"right muted",children:s.count})]}),e.jsx("div",{className:"colbody",children:s.entries.length?s.entries.map(n=>e.jsxs("div",{className:`row selectable ${n.id===(r==null?void 0:r.id)?"selected":""}`,onClick:()=>{m({entry:n.id}),b({id:n.id,title:n.title,values:{...n.values}})},children:[e.jsx("span",{className:"name",children:n.title||"(无名)"}),e.jsx("span",{className:"acts",children:n.missing.length?e.jsxs("span",{className:"tag",children:[n.completeness.ratio,"%"]}):e.jsx("span",{className:"tag strong",children:"已填满"})})]},n.id)):e.jsxs(L,{children:["还没有条目。点右上角「新建条目」,或用 CLI:nx-sk entry add --section ",s.section.id," --set ..."]})})]})}),e.jsx("div",{className:"col",children:r?e.jsxs("div",{className:"card",children:[e.jsxs("div",{className:"colhead",children:[e.jsx("span",{className:"grow nowrap",children:(t==null?void 0:t.title)||r.title}),e.jsx("span",{className:"muted mono",children:r.id}),t?e.jsxs(B,{kind:t.missing.length?"":"strong",children:["完整度 ",t.completeness.ratio,"%"]}):null]}),e.jsxs("div",{className:"field wide pad",style:{borderBottom:"1px solid var(--soft-2)"},children:[e.jsxs("div",{className:"flabel",children:[e.jsx("span",{children:s.section.titleLabel||"名称"}),e.jsx("span",{className:"bad",children:"改动后必须保存"})]}),e.jsx("div",{className:"fctrl",children:e.jsx("input",{value:r.title,onChange:n=>b(u=>({...u,title:n.target.value}))})})]}),e.jsxs("div",{className:"rowgap",style:{padding:"6px 14px",borderBottom:"1px solid var(--soft-2)"},children:[e.jsx("input",{className:"grow",style:{minWidth:220},placeholder:"直接加字段:字段=值 (回车写入;没见过的键会自动登记)",value:T,onChange:n=>E(n.target.value),onKeyDown:n=>{n.key==="Enter"&&A()}}),e.jsx("button",{className:"btn small",onClick:A,disabled:!T.includes("="),children:"写入"})]}),y.map(n=>e.jsx(Z,{title:n.title,fields:n.fields,draft:r.values,onPatch:i},n.id)),e.jsxs("div",{className:"rowgap pad",children:[e.jsxs("button",{className:"btn",onClick:U,disabled:!o.count,children:["保存",o.count?`(${o.count} 处改动)`:""]}),e.jsx("button",{className:"btn ghost",onClick:()=>a(r.id),disabled:!o.count,children:"放弃改动"}),e.jsx("button",{className:"btn ghost",onClick:F,children:"删除条目"}),t!=null&&t.missing.length?e.jsxs("span",{className:"hint",children:["模板建议里还有 ",t.missing.length," 项没填(不影响使用):",t.missing.slice(0,5).map(n=>n.label).join("、"),t.missing.length>5?"…":""]}):null,(R=t==null?void 0:t.excluded)!=null&&R.length?e.jsxs("span",{className:"hint",children:["另有 ",t.excluded.length," 项已标不填、不计入完整度:",t.excluded.slice(0,6).map(n=>n.label).join("、"),t.excluded.length>6?"…":""]}):null]})]}):e.jsx("div",{className:"card",children:e.jsx(L,{children:"左边选一条,或新建一条"})})})]}),e.jsx(q,{module:"entries"}),N?e.jsx(z,{title:N.title,onClose:()=>I(null),children:N.body}):null]}):e.jsx("div",{className:"pad muted",children:C?"加载中…":"暂无数据"})}export{ne as default};
@@ -8,8 +8,8 @@
8
8
  <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16.png" />
9
9
  <link rel="icon" type="image/x-icon" href="/favicon.ico" />
10
10
  <link rel="apple-touch-icon" href="/logo-rounded.png" />
11
- <script type="module" crossorigin src="/assets/index-DzD8wu27.js"></script>
12
- <link rel="stylesheet" crossorigin href="/assets/index-DLtjhflt.css">
11
+ <script type="module" crossorigin src="/assets/index-LbNmGlYj.js"></script>
12
+ <link rel="stylesheet" crossorigin href="/assets/index-lfnBM8pt.css">
13
13
  </head>
14
14
  <body>
15
15
  <div id="root"></div>