fix: make CMS content live and migrate to MySQL

This commit is contained in:
Codex User
2026-09-08 13:30:54 +08:00
parent 3a99ae30c5
commit 1088871eb9
15 changed files with 401 additions and 799 deletions
+32 -19
View File
@@ -3,19 +3,20 @@ import { useState, useEffect } from 'react'
const API_BASE = '' // 同域,相对路径
const SIDEBAR = [
{ key: 'config', label: '1 全局配置' },
{ key: 'home', label: '2 首页模块' },
{ key: 'ameba', label: '3 阿美巴页面' },
{ key: 'evolution', label: '4 企业进化' },
{ key: 'methods', label: '5 方法与能力' },
{ key: 'services', label: '6 产品服务' },
{ key: 'cases', label: '7 案例管理' },
{ key: 'articles', label: '8 认知观察' },
{ key: 'selfcheck', label: '9 自我对照' },
{ key: 'diag_q', label: '10 诊断问卷' },
{ key: 'diag_config', label: '11 诊断配置' },
{ key: 'diag_reports', label: '12 诊断记录' },
{ key: 'diag_leads', label: '13 留资统计' },
{ key: 'contact_leads', label: '14 咨询线索' },
{ key: 'texts', label: '2 全站文案' },
{ key: 'home', label: '3 首页模块' },
{ key: 'ameba', label: '4 阿美巴页面' },
{ key: 'evolution', label: '5 企业进化' },
{ key: 'methods', label: '6 方法与能力' },
{ key: 'services', label: '7 产品服务' },
{ key: 'cases', label: '8 案例管理' },
{ key: 'articles', label: '9 认知观察' },
{ key: 'selfcheck', label: '10 自我对照' },
{ key: 'diag_q', label: '11 诊断问卷' },
{ key: 'diag_config', label: '12 诊断配置' },
{ key: 'diag_reports', label: '13 诊断记录' },
{ key: 'diag_leads', label: '14 留资统计' },
{ key: 'contact_leads', label: '15 咨询线索' },
{ key: 'password', label: '⚙ 修改密码' },
]
@@ -66,6 +67,7 @@ export default function App(){
<div className="main">
<div className="topbar"><h2>{SIDEBAR.find(s=>s.key===cur)?.label}</h2><span style={{fontSize:12,color:'#94a3b8'}}> · </span></div>
{cur==='config' && <Config token={token} />}
{cur==='texts' && <Texts token={token} />}
{cur==='home' && <Home token={token} />}
{cur==='ameba' && <Ameba token={token} />}
{cur==='evolution' && <Evolution token={token} />}
@@ -85,6 +87,17 @@ export default function App(){
)
}
function Texts({token}:{token:string}){
const [list,setList]=useState<any[]>([])
useEffect(()=>{api('/api/cms/texts',token).then(j=>setList(j.data||[]))},[])
const update=(i:number,key:string,value:any)=>{const n=[...list]; n[i]={...n[i],[key]:value}; setList(n)}
const save=()=>api('/api/cms/texts',token,{method:'PUT',body:JSON.stringify(list)}).then(j=>alert(j.msg||'保存成功'))
return <div className="card"><h3></h3><p className="hint"></p>
{list.map((it,i)=><div key={it.id} className="field"><label>{it.label} <small>({it.id})</small></label><textarea value={it.value||''} onChange={e=>update(i,'value',e.target.value)} /></div>)}
<button className="btn" onClick={save}></button>
</div>
}
function ContactLeads({token}:{token:string}){
const [list,setList]=useState<any[]>([])
useEffect(()=>{api('/api/cms/contact-leads',token).then(j=> setList(j.data||[]))},[])
@@ -164,7 +177,7 @@ function Methods({token}:{token:string}){
</>
}
function Services({token}:{token:string}){
const [list,setList]=useState<any[]>([]); const [f,setF]=useState<any>({})
const [list,setList]=useState<any[]>([]); const [f,setF]=useState<any>({status:'published',type:'custom'})
useEffect(()=>{api('/api/cms/services',token).then(j=> setList(j.data||[]))},[])
return <>
<div className="card"><h3></h3><table className="table"><thead><tr><th></th><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody>{list.map(s=> <tr key={s.id}><td>{s.name}</td><td>{s.type}</td><td>{s.price||'-'}</td><td>{s.status}</td><td>{s.sort_weight}</td><td><button onClick={()=>{if(confirm('删除?')) api('/api/cms/services/'+s.id,token,{method:'DELETE'}).then(()=> location.reload())}}></button></td></tr>)}</tbody></table></div>
@@ -172,13 +185,13 @@ function Services({token}:{token:string}){
<div className="form-row"><div className="field"><label>*</label><input value={f.name||''} onChange={e=> setF({...f,name:e.target.value})}/></div><div className="field"><label>slug</label><input value={f.slug||''} onChange={e=> setF({...f,slug:e.target.value})}/></div></div>
<div className="form-row"><div className="field"><label></label><select value={f.type||'custom'} onChange={e=> setF({...f,type:e.target.value})}><option value="standard"></option><option value="custom">/</option></select></div><div className="field"><label></label><input value={f.price||''} onChange={e=> setF({...f,price:e.target.value})} placeholder="仅标准化需要"/></div></div>
<div className="field"><label></label><textarea value={f.intro||''} onChange={e=> setF({...f,intro:e.target.value})}/></div>
<div className="form-row"><div className="field"><label></label><select value={f.status||'draft'} onChange={e=> setF({...f,status:e.target.value})}><option value="published">published</option><option value="draft">draft</option></select></div><div className="field"><label></label><input type="number" value={f.sort_weight||0} onChange={e=> setF({...f,sort_weight:Number(e.target.value)})}/></div></div>
<div className="form-row"><div className="field"><label></label><select value={f.status||'published'} onChange={e=> setF({...f,status:e.target.value})}><option value="published">published</option><option value="draft">draft</option></select></div><div className="field"><label></label><input type="number" value={f.sort_weight||0} onChange={e=> setF({...f,sort_weight:Number(e.target.value)})}/></div></div>
<button className="btn" onClick={()=> api('/api/cms/services',token,{method:'POST',body:JSON.stringify(f)}).then(j=> {if(j.code===0) location.reload(); else alert(j.msg)})}></button>
</div>
</>
}
function Cases({token}:{token:string}){
const [list,setList]=useState<any[]>([]); const [f,setF]=useState<any>({})
const [list,setList]=useState<any[]>([]); const [f,setF]=useState<any>({status:'published'})
useEffect(()=>{api('/api/cms/cases',token).then(j=> setList(j.data||[]))},[])
return <>
<div className="card"><h3>9</h3><table className="table"><thead><tr><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody>{list.map(c=> <tr key={c.id}><td>{c.title}</td><td>{c.industry}</td><td>{c.status}</td><td>{c.sort_weight}</td><td><button onClick={()=>{if(confirm('删除?')) api('/api/cms/cases/'+c.id,token,{method:'DELETE'}).then(()=> location.reload())}}></button></td></tr>)}</tbody></table></div>
@@ -187,13 +200,13 @@ function Cases({token}:{token:string}){
{['original_state','goal','gap','root_cause','ameba_intervention','evolution_path','actions','phased_result','continuous_iteration'].map(k=>(
<div key={k} className="field"><label>{k}</label><textarea value={f[k]||''} onChange={e=> setF({...f,[k]:e.target.value})} /></div>
))}
<div className="form-row"><div className="field"><label></label><select value={f.status||'draft'} onChange={e=> setF({...f,status:e.target.value})}><option value="published">published</option><option value="draft">draft</option></select></div><div className="field"><label></label><input type="number" value={f.sort_weight||0} onChange={e=> setF({...f,sort_weight:Number(e.target.value)})}/></div></div>
<div className="form-row"><div className="field"><label></label><select value={f.status||'published'} onChange={e=> setF({...f,status:e.target.value})}><option value="published">published</option><option value="draft">draft</option></select></div><div className="field"><label></label><input type="number" value={f.sort_weight||0} onChange={e=> setF({...f,sort_weight:Number(e.target.value)})}/></div></div>
<button className="btn" onClick={()=> api('/api/cms/cases',token,{method:'POST',body:JSON.stringify(f)}).then(j=> {if(j.code===0) location.reload(); else alert(j.msg)})}></button>
</div>
</>
}
function Articles({token}:{token:string}){
const [list,setList]=useState<any[]>([]); const [f,setF]=useState<any>({})
const [list,setList]=useState<any[]>([]); const [f,setF]=useState<any>({status:'published'})
useEffect(()=>{api('/api/cms/articles',token).then(j=> setList(j.data||[]))},[])
return <>
<div className="card"><h3></h3><table className="table"><thead><tr><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody>{list.map(a=> <tr key={a.id}><td>{a.title}</td><td>{a.category}</td><td>{a.recommend_weight}</td><td>{a.status}</td><td><button onClick={()=>{if(confirm('删除?')) api('/api/cms/articles/'+a.id,token,{method:'DELETE'}).then(()=> location.reload())}}></button></td></tr>)}</tbody></table></div>
@@ -202,7 +215,7 @@ function Articles({token}:{token:string}){
<div className="field"><label> HTML</label><textarea value={f.content_html||''} onChange={e=> setF({...f,content_html:e.target.value})} /></div>
<Upload token={token} onUrl={url=> setF({...f,cover_url:url})} />
{f.cover_url && <img src={f.cover_url} style={{width:120,height:80,objectFit:'cover'}}/>}
<div className="form-row"><div className="field"><label></label><select value={f.status||'draft'} onChange={e=> setF({...f,status:e.target.value})}><option value="published">published</option><option value="draft">draft</option></select></div><div className="field"><label></label><input type="number" value={f.recommend_weight||0} onChange={e=> setF({...f,recommend_weight:Number(e.target.value)})}/></div></div>
<div className="form-row"><div className="field"><label></label><select value={f.status||'published'} onChange={e=> setF({...f,status:e.target.value})}><option value="published">published</option><option value="draft">draft</option></select></div><div className="field"><label></label><input type="number" value={f.recommend_weight||0} onChange={e=> setF({...f,recommend_weight:Number(e.target.value)})}/></div></div>
<button className="btn" onClick={()=> api('/api/cms/articles',token,{method:'POST',body:JSON.stringify(f)}).then(j=> {if(j.code===0) location.reload(); else alert(j.msg)})}></button>
</div>
</>