import { useState, useEffect } from 'react' const API_BASE = '' // 同域,相对路径 const SIDEBAR = [ { key: 'config', label: '1 全局配置' }, { 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: '⚙ 修改密码' }, ] function useAuth(){ const [token,setToken]=useState(localStorage.getItem('cms_token')||'') useEffect(()=>{ const onUnauthorized=()=>setToken('') window.addEventListener('cms:unauthorized',onUnauthorized) return ()=>window.removeEventListener('cms:unauthorized',onUnauthorized) },[]) const login=async(u:string,p:string)=>{ const r=await fetch('/api/auth/login',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({username:u,password:p})}) const j=await r.json() if(j.code!==0) throw new Error(j.msg) localStorage.setItem('cms_token', j.data.token) setToken(j.data.token) } const logout=()=>{localStorage.removeItem('cms_token'); setToken('')} return {token,login,logout} } async function api(path:string, token:string, opts:RequestInit={}){ const headers:any={...(opts.headers||{})} if(token) headers['Authorization']='Bearer '+token if(!(opts.body instanceof FormData)) headers['Content-Type']='application/json' const response=await fetch(path,{...opts, headers}) const result=await response.json() if(response.status===401 || result.code===401){ localStorage.removeItem('cms_token') window.dispatchEvent(new Event('cms:unauthorized')) } return result } const FIELD_LABELS:Record = { site_title:'网站标题', slogan:'网站标语', seo_title:'SEO标题', seo_desc:'SEO描述', seo_keywords:'SEO关键词', hotline:'热线电话', contact_email:'联系邮箱', contact_address:'联系地址', cta_text:'行动按钮文字', cta_link:'行动按钮链接', qr_image_url:'二维码图片', qr_target_url:'二维码跳转链接', qr_label:'二维码旁文案', brand_position:'品牌定位', mission:'使命/介绍', philosophy:'经营哲学', practice:'服务理念', steps:'步骤', scenarios:'适用场景', description:'描述', original_state:'原始状态', goal:'目标', gap:'差距', root_cause:'根因', ameba_intervention:'阿美巴介入', evolution_path:'进化路径', actions:'行动', phased_result:'阶段性结果', continuous_iteration:'持续迭代', stage:'适用阶段', intro:'简介', service_content:'服务内容', delivery:'交付内容', suitable:'适合对象', price_unit:'价格单位', advantage:'核心优势', detail_html:'详情正文', guide:'引导语', questions:'问题配置', logic_text:'结果逻辑说明', next_guide:'下一步引导', } const fieldLabel=(key:string)=>FIELD_LABELS[key] ? `${FIELD_LABELS[key]}(${key})` : key export default function App(){ const {token,login,logout}=useAuth() const [user,setUser]=useState('') const [pwd,setPwd]=useState('') const [err,setErr]=useState('') const [cur,setCur]=useState('config') if(!token) return (

CMS后台登录

setUser(e.target.value)} /> setPwd(e.target.value)} /> {err &&
{err}
}
) return (

阿美巴 CMS后台
V1.1 FINAL 内容管理

{SIDEBAR.map(s=>
setCur(s.key)}>{s.label}
)}

{SIDEBAR.find(s=>s.key===cur)?.label}

内容与布局解耦 · 只改内容不改架构
{cur==='config' && } {cur==='texts' && } {cur==='home' && } {cur==='ameba' && } {cur==='evolution' && } {cur==='methods' && } {cur==='services' && } {cur==='cases' && } {cur==='articles' && } {cur==='selfcheck' && } {cur==='diag_q' && } {cur==='diag_config' && } {cur==='diag_reports' && } {cur==='diag_leads' && } {cur==='contact_leads' && } {cur==='password' && }
) } function Texts({token}:{token:string}){ const [list,setList]=useState([]) 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||'保存成功')) const groups=list.reduce((acc:any,it:any)=>{const group=it.id.split('.')[0]; (acc[group] ||= []).push(it); return acc},{}) const groupNames:any={home:'首页(Home)',global_cta:'全站行动区(Global CTA)',ameba:'阿美巴页面(Ameba)',evolution:'企业进化页面(Evolution)',methods:'方法与能力(Methods)',services:'产品服务(Services)',cases:'实践案例(Cases)',insights:'认知观察(Insights)',contact:'联系页(Contact)',diagnosis:'诊断页(Diagnosis)',selfcheck:'轻量对照(Self Check)',footer:'页脚(Footer)'} return

全站文案

这里的内容会覆盖首页、阿美巴页和全站行动区的默认文字。保存后刷新前台即可看到。

{Object.entries(groups).map(([group,items]:any)=>

{groupNames[group] || group}

{items.map((it:any)=>