feat: merge V1.3 CMS content support

This commit is contained in:
Codex User
2026-09-09 10:34:10 +08:00
parent 7608dc5415
commit 4e87dcb849
9 changed files with 96 additions and 53 deletions
+3 -2
View File
@@ -41,7 +41,7 @@ function api(path:string, token:string, opts:RequestInit={}){
}
const FIELD_LABELS:Record<string,string> = {
site_title:'网站标题', slogan:'网站标语', seo_title:'SEO标题', seo_desc:'SEO描述', seo_keywords:'SEO关键词', hotline:'热线电话', contact_email:'联系邮箱', contact_address:'联系地址', cta_text:'行动按钮文字', cta_link:'行动按钮链接',
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:'详情正文',
@@ -126,9 +126,10 @@ function Config({token}:{token:string}){
const save=()=> api('/api/cms/config',token,{method:'PUT',body:JSON.stringify(f)}).then(j=> alert(j.msg||'保存成功'))
return <div className="card">
<h3>全局配置</h3>
{['site_title','slogan','seo_title','seo_desc','seo_keywords','hotline','contact_email','contact_address','cta_text','cta_link'].map(k=>(
{['site_title','slogan','seo_title','seo_desc','seo_keywords','hotline','contact_email','contact_address','cta_text','cta_link','qr_target_url','qr_label'].map(k=>(
<div key={k} className="field"><label>{fieldLabel(k)}</label><input value={f[k]||''} onChange={e=> setF({...f,[k]:e.target.value})} /></div>
))}
<div className="field"><label>{fieldLabel('qr_image_url')}</label><input value={f.qr_image_url||''} onChange={e=>setF({...f,qr_image_url:e.target.value})}/><Upload token={token} onUrl={url=>setF({...f,qr_image_url:url})}/>{f.qr_image_url&&<img src={f.qr_image_url} alt="二维码预览" style={{width:120,height:120,objectFit:'contain',marginTop:8,border:'1px solid #e2e8f0',borderRadius:8}}/>}</div>
<button className="btn" onClick={save}>保存</button>
</div>
}
+7 -1
View File
@@ -31,7 +31,7 @@ export async function initDB() {
await ensureDatabase();
pool = mysql.createPool({ host: DB_HOST, port: DB_PORT, user: DB_USER, password: DB_PASSWORD, database: DB_NAME, charset: 'utf8mb4', waitForConnections: true, connectionLimit: Number(process.env.DB_CONNECTION_LIMIT || 10), timezone: 'Z' });
const tables = [
`CREATE TABLE IF NOT EXISTS global_config (id INTEGER PRIMARY KEY CHECK (id = 1), site_title TEXT, slogan TEXT, seo_title TEXT, seo_desc TEXT, seo_keywords TEXT, hotline TEXT, contact_email TEXT, contact_address TEXT, cta_text TEXT, cta_link TEXT, updated_at TEXT)`,
`CREATE TABLE IF NOT EXISTS global_config (id INTEGER PRIMARY KEY CHECK (id = 1), site_title TEXT, slogan TEXT, seo_title TEXT, seo_desc TEXT, seo_keywords TEXT, hotline TEXT, contact_email TEXT, contact_address TEXT, cta_text TEXT, cta_link TEXT, qr_image_url TEXT, qr_target_url TEXT, qr_label TEXT, updated_at TEXT)`,
`CREATE TABLE IF NOT EXISTS home_sections (id TEXT PRIMARY KEY, \`key\` TEXT UNIQUE, title TEXT, subtitle TEXT, summary TEXT, visible INTEGER DEFAULT 1, sort_order INTEGER DEFAULT 0, updated_at TEXT)`,
`CREATE TABLE IF NOT EXISTS site_texts (id VARCHAR(191) PRIMARY KEY, label TEXT, value LONGTEXT, updated_at TEXT)`,
`CREATE TABLE IF NOT EXISTS ameba_page (id INTEGER PRIMARY KEY CHECK (id = 1), brand_position TEXT, mission TEXT, philosophy TEXT, practice TEXT, updated_at TEXT)`,
@@ -54,6 +54,12 @@ export async function initDB() {
`CREATE TABLE IF NOT EXISTS admins (id TEXT PRIMARY KEY, username TEXT UNIQUE, password_hash TEXT, created_at TEXT)`,
];
for (const table of tables) await pool.query(mysqlSchema(table));
// CREATE TABLE IF NOT EXISTS does not add fields to an existing database.
// Keep small additive schema upgrades here so a normal restart is enough to migrate production.
for (const column of ['qr_image_url', 'qr_target_url', 'qr_label']) {
const [rows] = await pool.query(`SHOW COLUMNS FROM global_config LIKE ?`, [column]);
if (!rows.length) await pool.query(`ALTER TABLE global_config ADD COLUMN \`${column}\` LONGTEXT`);
}
}
function normalizeSql(sql) { return sql.replace(/INSERT OR IGNORE/gi, 'INSERT IGNORE'); }
+2 -2
View File
@@ -149,8 +149,8 @@ app.get('/api/cms/config', (req, res) => {
});
app.put('/api/cms/config', (req, res) => {
const f = req.body;
db.run(`UPDATE global_config SET site_title=?, slogan=?, seo_title=?, seo_desc=?, seo_keywords=?, hotline=?, contact_email=?, contact_address=?, cta_text=?, cta_link=?, updated_at=? WHERE id=1`,
[f.site_title, f.slogan, f.seo_title, f.seo_desc, f.seo_keywords, f.hotline, f.contact_email, f.contact_address, f.cta_text, f.cta_link, nowISO()], function(err){
db.run(`UPDATE global_config SET site_title=?, slogan=?, seo_title=?, seo_desc=?, seo_keywords=?, hotline=?, contact_email=?, contact_address=?, cta_text=?, cta_link=?, qr_image_url=?, qr_target_url=?, qr_label=?, updated_at=? WHERE id=1`,
[f.site_title, f.slogan, f.seo_title, f.seo_desc, f.seo_keywords, f.hotline, f.contact_email, f.contact_address, f.cta_text, f.cta_link, f.qr_image_url, f.qr_target_url, f.qr_label, nowISO()], function(err){
if(err) return res.status(500).json({code:500, msg: err.message});
res.json({code:0, msg:'保存成功'});
});
+40 -21
View File
@@ -16,7 +16,7 @@ db.run(`INSERT OR IGNORE INTO admins (id, username, password_hash, created_at) V
db.get(`SELECT id FROM global_config WHERE id=1`, (err, row) => {
if (!row) {
db.run(`INSERT INTO global_config (id, site_title, slogan, seo_title, seo_desc, seo_keywords, hotline, contact_email, contact_address, cta_text, cta_link, updated_at) VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
['阿美巴咨询|企业进化陪跑', '让企业进化被看见、被度量、被落地', '阿美巴咨询|企业进化陪跑 · 体系化增长落地', '以企业进化为主线,四大价值坐标与A/M/B状态坐标为观察系', '阿美巴,企业进化,四大价值,A/M/B', '400-XXX-XXXX', 'contact@aiameiba.com', '成都', '咨询获取方案', '/contact', now]);
['阿美巴|AI时代企业进化平台', '帮助企业看清位置、找到方向、持续进化', '阿美巴|AI时代企业进化平台 · 企业进化陪跑', '以四大价值坐标与A/M/B状态坐标为观察系,帮助企业看清位置、找到方向、持续进化。', '阿美巴,企业进化,四大价值,A/M/B,AI时代企业进化平台', '400-XXX-XXXX', 'contact@aiameiba.com', '成都市', '咨询获取方案', '/contact', now]);
}
});
// 首页板块
@@ -32,22 +32,22 @@ const sections = [
sections.forEach(s => db.run(`INSERT OR IGNORE INTO home_sections (id, \`key\`, title, subtitle, summary, visible, sort_order, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [...s, now]));
const siteTexts = [
['home.eyebrow', '首页首屏眉题', '阿美巴 · 企业进化陪跑'],
['home.title', '首页首屏标题', '让企业进化\n被看见、被度量、被落地'],
['home.description', '首页首屏描述', '以“企业进化”为主线,以四大价值坐标与 A/M/B 状态坐标为观察系,陪跑企业构建可复制、可持续的增长经营体系。'],
['home.trust', '首页首屏信任说明', '30秒理解主张:进化思想 → 价值坐标 → 状态坐标 → 行动路径'],
['home.eyebrow', '首页首屏眉题', '阿美巴 · AI时代企业进化平台'],
['home.title', '首页首屏标题', 'AI时代企业进化平台'],
['home.description', '首页首屏描述', '帮助企业看清位置、找到方向、持续进化'],
['home.trust', '首页首屏信任说明', '以四大价值坐标与 A/M/B 状态坐标为观察系'],
['home.primary_cta', '首页主按钮', '开始企业进化诊断'],
['home.secondary_cta', '首页咨询按钮', '咨询获取方案'],
['home.light_cta', '首页轻量对照按钮', '轻量对照'],
['home.notice', '首页提示', ''],
['home.about_title', '首页关于标题', '我们是谁'],
['home.about_text', '首页关于描述', '围绕企业进化经营哲学,陪跑企业把战略、组织、机制与人的成长转化为可执行、可复盘的经营系统。'],
['home.about_text', '首页关于描述', '企业进化经营哲学:经营即进化,进化即经营。以四大价值为观察坐标,以A/M/B为状态坐标,帮助企业持续迭代。'],
['home.about_note', '首页关于补充说明', '我们持续沉淀品牌故事、使命愿景与真实实践。'],
['home.cases_title', '首页案例标题', '实践案例'],
['home.services_title', '首页服务标题', '产品服务 · 下一步行动'],
['home.insights_title', '首页洞察标题', '认知观察'],
['global_cta.title', '全站行动标题', '下一步如何行动?'],
['global_cta.description', '全站行动描述', '任意页面均可直接发起咨询,选择轻量对照或深度陪跑路径。'],
['global_cta.description', '全站行动描述', '帮助企业看清位置、找到方向、持续进化。'],
['global_cta.primary', '全站主按钮', '咨询获取方案'],
['global_cta.secondary', '全站次按钮', '看看你的企业'],
['ameba.header_title', '阿美巴页标题', '阿美巴'],
@@ -76,6 +76,9 @@ const siteTexts = [
['methods.detail_steps_title', '方法详情步骤标题', '关键步骤'],
['methods.placeholder', '方法页补充说明', '从判断到行动,让方法真正进入经营现场。'],
['methods.detail_note', '方法详情补充说明', '围绕方法、工具与交付物,形成可复盘的行动路径。'],
['methods.process_eyebrow', '企业进化梳理眉题', '企业进化梳理'],
['methods.process_title', '企业进化梳理标题', '从看清现状到持续进化'],
['methods.process_scenario_label', '企业进化梳理适用场景标签', '适用场景'],
['services.header_description', '服务页说明', '围绕下一步行动展示标准产品与定制陪跑服务。'],
['services.title', '服务页标题', '产品服务'],
['services.standard_label', '标准服务标签', '标准产品'],
@@ -91,6 +94,23 @@ const siteTexts = [
['selfcheck.storage_note', '轻量对照结果说明', '结果仅本地展示,不存储。'],
['selfcheck.result_title', '轻量对照结果标题', '对照结果'],
['selfcheck.result_note', '轻量对照结果补充说明', '此处仅为轻量对照提示,不生成诊断报告。'],
['selfcheck.title', '自我观察页标题', '看看你的企业'],
['selfcheck.form_title', '自我观察表单标题', '企业进化自我观察'],
['selfcheck.form_description', '自我观察表单说明', '每题选择最贴近现状的状态,用于帮助你观察企业,不用于评分或排名。'],
['selfcheck.submit', '自我观察提交按钮', '查看自我观察结果'],
['selfcheck.contact_cta', '自我观察咨询按钮', '咨询获取更多观察视角'],
['selfcheck.evolution_cta', '自我观察进化按钮', '了解企业进化坐标'],
['selfcheck.path_note', '自我观察路径补充', ''],
['contact.channels_title', '联系页其他通道标题', '其他通道'],
['contact.hotline_label', '联系页电话标签', '联系电话'],
['contact.email_label', '联系页邮箱标签', '联系邮箱'],
['contact.address_label', '联系页地址标签', '联系地址'],
['contact.qr_default_label', '联系页二维码默认文案', '扫码联系我们'],
['footer.company', '页脚公司名称', '成都阿美巴企业管理咨询有限公司'],
['footer.description', '页脚公司介绍', '帮助企业看清位置、找到方向、持续进化。'],
['footer.navigation_title', '页脚导航标题', '网站导航'],
['footer.action_title', '页脚行动入口标题', '行动入口'],
['footer.copyright', '页脚版权信息', '© 2026 成都阿美巴企业管理咨询有限公司 | aiameiba.com · ameiba.cn'],
['evolution.header_title', '企业进化页标题', '企业进化'],
['evolution.header_description', '企业进化页头部说明', '内部顺序强制:企业进化思想 → 四大价值坐标 → A/M/B 状态坐标,顺序不可调换。'],
['evolution.header_note', '企业进化页头部补充', '从进化思想到价值坐标,再到状态坐标,逐层看清企业位置。'],
@@ -121,36 +141,35 @@ siteTexts.forEach(([id, label, value]) => db.run(`INSERT OR IGNORE INTO site_tex
// 阿美巴页面
db.get(`SELECT id FROM ameba_page WHERE id=1`, (e, r) => {
if (!r) db.run(`INSERT INTO ameba_page (id, brand_position, mission, philosophy, practice, updated_at) VALUES (1, ?, ?, ?, ?, ?)`,
['以企业进化经营哲学为底色', '陪跑企业体系化增长落地', '利他、务实、陪跑', '十年以上实战沉淀', now]);
['AI时代企业进化平台', '帮助企业看清位置、找到方向、持续进化', '企业进化经营哲学:经营即进化,进化即经营。以四大价值为观察坐标,以A/M/B为状态坐标,帮助企业持续迭代。', '先看企业→定路径→再配载体。教练式入企陪跑,体系化交付SOP与工具包。', now]);
});
// evolution_thought
db.get(`SELECT id FROM evolution_thought WHERE id=1`, (e, r) => {
if (!r) db.run(`INSERT INTO evolution_thought (id, content, updated_at) VALUES (1, ?, ?)`, ['进化是持续迭代,而非一次性变革', now]);
if (!r) db.run(`INSERT INTO evolution_thought (id, content, updated_at) VALUES (1, ?, ?)`, ['企业进化不是一次性的变革,而是持续的迭代过程。通过目标→现状→差距→根因→路径→推进→反馈→再进化的闭环,帮助企业在不确定环境中找到确定的进化方向。', now]);
});
// four_values
const fvs = [
['fv-1', 'value1', '战略价值坐标', '观察企业战略清晰度', '战略价值说明【占位】', 100],
['fv-2', 'value2', '组织价值坐标', '观察组织活力', '组织价值说明【占位】', 90],
['fv-3', 'value3', '经营价值坐标', '观察经营机制', '经营价值说明【占位】', 80],
['fv-4', 'value4', '人的价值坐标', '观察人的成长', '人的价值说明【占位】', 70],
['fv-1', 'value1', '价值创造', '价值创造,是企业发现、选择并形成客户价值与经营价值的过程。', '核心问题:企业为谁创造什么价值,以及这种价值能否形成持续的经营成果?', 100],
['fv-2', 'value2', '价值传递', '价值传递,是企业将所创造的价值有效传达给目标对象,使其被看见、理解、认可、信任并产生选择的过程。', '核心问题:企业创造的价值,能否被正确的人看见、理解、认可、信任并选择?注意:内部组织协同、信息、资源、机制等属于价值支持的经营支撑内容,不能替代价值传递本身。', 90],
['fv-3', 'value3', '价值交付', '价值交付,是企业将承诺的价值稳定、持续地兑现给客户的过程。', '核心问题:客户最终有没有真正获得企业承诺的价值?', 80],
['fv-4', 'value4', '价值支持', '价值支持,是为价值创造、价值传递与价值交付持续发生提供基础保障与进化动力的过程。', '核心问题:企业有没有能力让前三种价值持续发生,并不断进化?价值支持不是独立于经营之外的普通管理模块,而是贯穿价值创造、价值传递、价值交付的支撑维度。', 70],
];
fvs.forEach(f => db.run(`INSERT OR IGNORE INTO four_values (id, \`key\`, name, definition, description, sort_order, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)`, [...f, now]));
// amb_states 固定说明
const ambs = [
['amb-A', 'A', 'A 状态|自进化', '具备自驱动', 'A状态说明【占位】', 'A/M/B不绑定企业规模的说明文案固定:同一企业不同价值维度可处不同状态', 100],
['amb-M', 'M', 'M 状态|待突破', '存在卡点', 'M状态说明【占位】', '同上固定说明', 90],
['amb-B', 'B', 'B 状态|待构建', '基础待构建', 'B状态说明【占位】', '同上固定说明', 80],
['amb-A', 'A', 'A态·入态', '企业在某一具体进化维度上,开始主动面对变化,并进入观察、调整与行动的进化状态。', 'A=Action。入态意味着企业在某一具体维度上开始主动面对变化。关键词:进入。', 'A/M/B是企业进化状态坐标,不是等级,不是优劣,不是高低排序,不是规模分类,不是产品分类,不是客户等级,不是服务等级,不是评分区间,不是固定晋级路径。严禁形成A→M→B=低→中→高。', 100],
['amb-M', 'M', 'M态·优态', '企业在某一具体进化维度上已经形成一定实践基础,并持续进行优化、协同与能力提升。', 'M=Mastery。优态意味着企业已经形成一定实践基础。关键词:优化。', 'A/M/B是企业进化状态坐标,不是等级,不是优劣,不是高低排序,不是规模分类,不是产品分类,不是客户等级,不是服务等级,不是评分区间,不是固定晋级路径。严禁形成A→M→B=低→中→高。', 90],
['amb-B', 'B', 'B态·越态', '企业在某一具体进化维度上开始突破既有边界,重新思考原有方式,并进行结构性重构或新的进化尝试。', 'B=Breakthrough。越态意味着企业在某一具体进化维度上开始突破既有边界。关键词:突破。', 'A/M/B是企业进化状态坐标,不是等级,不是优劣,不是高低排序,不是规模分类,不是产品分类,不是客户等级,不是服务等级,不是评分区间,不是固定晋级路径。严禁形成A→M→B=低→中→高。', 80],
];
ambs.forEach(a => db.run(`INSERT OR IGNORE INTO amb_states (id, \`key\`, name, definition, description, fixed_note, sort_order, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [...a, now]));
// ten_systems
for (let i = 1; i <= 10; i++) {
db.run(`INSERT OR IGNORE INTO ten_systems (id, name, description, sort_order, updated_at) VALUES (?, ?, ?, ?, ?)`, [`sys-${i}`, `系统${i}`, `第${i}系统说明【占位】`, 100 - i * 5, now]);
}
const systems = [['经营哲学系统','确立企业经营理念与文化根基'],['顶层设计系统','明确企业战略方向与组织架构'],['增长战略系统','围绕市场机会制定增长策略'],['商业模式系统','梳理价值创造与盈利逻辑'],['组织重塑系统','调整组织形态与协作方式'],['独立核算系统','划分经营单元,建立内部核算'],['经营分析系统','搭建数据化经营看板'],['价值评价系统','建立岗位与经营价值评价标准'],['薪酬激励系统','设计与经营成果挂钩的薪酬激励'],['IT部署系统','搭建支撑经营管理的信息化平台']];
systems.forEach(([name, description], i) => db.run(`INSERT OR IGNORE INTO ten_systems (id, name, description, sort_order, updated_at) VALUES (?, ?, ?, ?, ?)`, [`sys-${i + 1}`, name, description, 100 - (i + 1) * 5, now]));
db.get(`SELECT id FROM evolution_process WHERE id=1`, (e, r) => {
if (!r) db.run(`INSERT INTO evolution_process (id, steps, scenarios, description, updated_at) VALUES (1, ?, ?, ?, ?)`, ['步骤1→步骤2→步骤3', '适用场景【占位】', '企业进化梳理说明', now]);
if (!r) db.run(`INSERT INTO evolution_process (id, steps, scenarios, description, updated_at) VALUES (1, ?, ?, ?, ?)`, ['目标→现状→差距→根因→路径→推进→反馈→再进化', '适用于希望系统化推进企业进化的企业', '企业进化梳理是阿美巴的核心标准化方法载体,帮助企业看清现状、找到差距、明确方向。', now]);
});
db.get(`SELECT id FROM self_check WHERE id=1`, (e, r) => {
if (!r) db.run(`INSERT INTO self_check (id, guide, questions, logic_text, next_guide, updated_at) VALUES (1, ?, ?, ?, ?, ?)`, ['轻量自我对照引导文案', JSON.stringify([{q:"战略是否清晰?",options:["A","M","B"]}]), '归位逻辑文案【占位】', '下一步引导:预约咨询', now]);
if (!r) db.run(`INSERT INTO self_check (id, guide, questions, logic_text, next_guide, updated_at) VALUES (1, ?, ?, ?, ?, ?)`, ['企业进化自我观察体验。帮助企业看见自己的企业,而不是给企业打分。', JSON.stringify([{id:'q1',dimension:'价值创造',question:'当客户需求发生变化时,你通常如何发现这种变化?'},{id:'q2',dimension:'价值传递',question:'一个第一次认识你的客户,通常需要通过什么方式理解你与其他企业有什么不同?'},{id:'q3',dimension:'价值交付',question:'客户完成一次合作后,你通常如何判断他是否真正获得了你承诺的价值?'},{id:'q4',dimension:'价值支持',question:'当团队遇到新挑战时,你通常依靠什么来支撑下一步行动?'}]), '基于你的选择,以下维度值得进一步观察:', '如果希望进一步看清企业,可以进入企业进化梳理,或咨询顾问获取更多观察视角。', now]);
});
// 诊断配置
db.get(`SELECT id FROM diagnosis_config WHERE id=1`, (e, r) => {
+9 -7
View File
@@ -1,18 +1,21 @@
import { Link } from 'react-router-dom'
import { PRIMARY_NAV } from '@/data/nav'
import { getCMSConfig, useCMS, useCMSTexts } from '@/services/cms'
export default function Footer() {
const { data: config } = useCMS(getCMSConfig, null)
const { data: texts } = useCMSTexts()
return (
<footer className="site-footer">
<div className="container footer-grid">
<div>
<div className="footer-brand">成都阿美巴企业管理咨询有限公司</div>
<div className="footer-brand">{texts['footer.company'] || '成都阿美巴企业管理咨询有限公司'}</div>
<p className="footer-desc">
以“企业进化”为核心主张,以四大价值坐标与 A/M/B 状态坐标为观察系,陪跑企业体系化增长落地。
{texts['footer.description'] || config?.slogan || '帮助企业看清位置、找到方向、持续进化。'}
</p>
</div>
<div>
<h4>一级导航(冻结6项)</h4>
<h4>{texts['footer.navigation_title'] || '网站导航'}</h4>
<ul>
{PRIMARY_NAV.map(n => (
<li key={n.key}>
@@ -22,7 +25,7 @@ export default function Footer() {
</ul>
</div>
<div>
<h4>常驻行动入口</h4>
<h4>{texts['footer.action_title'] || '行动入口'}</h4>
<ul>
<li>
<Link to="/self-check">看看你的企业(轻量对照)</Link>
@@ -31,12 +34,11 @@ export default function Footer() {
<Link to="/contact">咨询获取方案</Link>
</li>
</ul>
<h4 style={{ marginTop: 16 }}>部署</h4>
<p className="footer-desc">复用现有服务器与域名 · 旧站完整备份可回滚</p>
{(config?.hotline || config?.contact_email) && <p className="footer-desc" style={{marginTop:16}}>{[config.hotline, config.contact_email].filter(Boolean).join(' · ')}</p>}
</div>
</div>
<div className="container footer-bottom">
<p>© 2026 成都阿美巴企业管理咨询有限公司 | aiameiba.com · ameiba.cn</p>
<p>{texts['footer.copyright'] || '© 2026 成都阿美巴企业管理咨询有限公司 | aiameiba.com · ameiba.cn'}</p>
<p className="footer-links">
<Link to="/contact">联系我们</Link> · <Link to="/self-check">看看你的企业</Link>
</p>
+3 -1
View File
@@ -1,5 +1,5 @@
import { Link } from 'react-router-dom'
import { useCMSTexts } from '@/services/cms'
import { useCMS, useCMSTexts, getCMSConfig } from '@/services/cms'
interface Props {
title?: string
@@ -19,6 +19,7 @@ export default function GlobalCTA({
secondaryTo = '/self-check',
}: Props) {
const { data: texts } = useCMSTexts()
const { data: config } = useCMS(getCMSConfig, null)
title = texts['global_cta.title'] || title
desc = texts['global_cta.description'] || desc
primaryLabel = texts['global_cta.primary'] || primaryLabel
@@ -31,6 +32,7 @@ export default function GlobalCTA({
<p>{desc}</p>
</div>
<div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
{config?.qr_image_url && <a href={config.qr_target_url || config.qr_image_url} target="_blank" rel="noreferrer" title={config.qr_label || '扫码咨询'}><img src={config.qr_image_url} alt={config.qr_label || '咨询二维码'} style={{width:56,height:56,objectFit:'contain',background:'#fff',borderRadius:6,padding:3}}/></a>}
<Link to={primaryTo} className="btn" style={{ background: '#fff', color: '#163E70' }}>
{primaryLabel} →
</Link>
+7 -2
View File
@@ -1,9 +1,10 @@
import { useState } from 'react'
import { Link } from 'react-router-dom'
import { useCMSTexts } from '@/services/cms'
import { useCMS, useCMSTexts, getCMSConfig } from '@/services/cms'
export default function Contact() {
const { data: texts } = useCMSTexts()
const { data: config } = useCMS(getCMSConfig, null)
const [form, setForm] = useState({ name: '', company: '', phone: '', need: '' })
const [submitted, setSubmitted] = useState(false)
const [error, setError] = useState('')
@@ -100,7 +101,11 @@ export default function Contact() {
</div>
<div style={{ display: 'grid', gap: 16, alignContent: 'start' }}>
<div className="card">
<h3>其他通道</h3>
<h3>{texts['contact.channels_title'] || '其他通道'}</h3>
{config?.hotline && <p><strong>{texts['contact.hotline_label'] || '联系电话'}:</strong>{config.hotline}</p>}
{config?.contact_email && <p><strong>{texts['contact.email_label'] || '联系邮箱'}:</strong>{config.contact_email}</p>}
{config?.contact_address && <p><strong>{texts['contact.address_label'] || '联系地址'}:</strong>{config.contact_address}</p>}
{config?.qr_image_url && <a href={config.qr_target_url || config.qr_image_url} target="_blank" rel="noreferrer" style={{display:'inline-grid',gap:8,marginTop:8,color:'inherit'}}><img src={config.qr_image_url} alt={config.qr_label || '联系二维码'} style={{width:140,height:140,objectFit:'contain',borderRadius:8}}/><span style={{fontSize:13}}>{config.qr_label || texts['contact.qr_default_label'] || '扫码联系我们'}</span></a>}
</div>
<div className="card">
<h3>快速路径</h3>
+11 -1
View File
@@ -1,10 +1,11 @@
import { Link } from 'react-router-dom'
import { METHODS as FALLBACK_METHODS } from '@/data/mock'
import { useCMS, useCMSTexts, getCMSMethods } from '@/services/cms'
import { useCMS, useCMSTexts, getCMSMethods, getCMSEvolutionProcess } from '@/services/cms'
import GlobalCTA from '@/components/Layout/GlobalCTA'
export default function Methods() {
const { data: METHODS } = useCMS(getCMSMethods, FALLBACK_METHODS)
const { data: process } = useCMS(getCMSEvolutionProcess, null)
const { data: texts } = useCMSTexts()
return (
<div>
@@ -22,6 +23,15 @@ export default function Methods() {
</div>
<section className="section">
<div className="container">
{process && (process.description || process.steps || process.scenarios) && (
<div className="card" style={{ marginBottom: 24 }}>
<p className="eyebrow">{texts['methods.process_eyebrow'] || '企业进化梳理'}</p>
<h2>{texts['methods.process_title'] || '从看清现状到持续进化'}</h2>
{process.description && <p>{process.description}</p>}
{process.steps && <p style={{ fontWeight: 700, color: '#163E70', marginTop: 14 }}>{process.steps}</p>}
{process.scenarios && <p style={{ fontSize: 13, color: '#64748b', marginTop: 10 }}><strong>{texts['methods.process_scenario_label'] || '适用场景'}:</strong>{process.scenarios}</p>}
</div>
)}
<div className="grid-2">
{METHODS.map(m => (
<Link key={m.id} to={`/methods/${m.slug}`} className="card" style={{ display: 'block' }}>
+14 -16
View File
@@ -6,10 +6,10 @@ import { useCMS, useCMSTexts, getCMSSelfCheck } from '@/services/cms'
type CheckItem = { id: string; question: string; dimension: string; options?: string[] }
const CHECK_ITEMS: CheckItem[] = [
{ id: 'q1', question: '战略方向在团队中是否清晰一致?', dimension: '战略价值' },
{ id: 'q2', question: '组织协同是否顺畅、职责是否清晰?', dimension: '组织价值' },
{ id: 'q3', question: '经营机制(目标/复盘/数据)是否闭环?', dimension: '经营价值' },
{ id: 'q4', question: '团队是否具备经营意识与成长机制?', dimension: '人的价值' },
{ id: 'q1', question: '当客户需求发生变化时,你通常如何发现这种变化?', dimension: '价值创造' },
{ id: 'q2', question: '一个第一次认识你的客户,通常需要通过什么方式理解你与其他企业有什么不同?', dimension: '价值传递' },
{ id: 'q3', question: '客户完成一次合作后,你通常如何判断他是否真正获得了你承诺的价值?', dimension: '价值交付' },
{ id: 'q4', question: '当团队遇到新挑战时,你通常依靠什么来支撑下一步行动?', dimension: '价值支持' },
]
export default function SelfCheck() {
@@ -37,7 +37,7 @@ export default function SelfCheck() {
</Link>{' '}
/ 看看你的企业
</div>
<h1>看看你的企业</h1>
<h1>{texts['selfcheck.title'] || '看看你的企业'}</h1>
<p>
{cmsCheck?.guide || '轻量自我对照工具,帮助你快速定位在四大价值 × A/M/B 的大致位置。'}
<br />
@@ -49,22 +49,22 @@ export default function SelfCheck() {
<section className="section">
<div className="container" style={{ maxWidth: 720 }}>
<div className="card">
<h3>轻量对照({items.length} 题)</h3>
<p style={{ fontSize: 13, color: '#64748b' }}>每题选择最贴近现状的选项,结果仅作自我对照参考,不生成诊断报告。</p>
<h3>{texts['selfcheck.form_title'] || '企业进化自我观察'}({items.length} 题)</h3>
<p style={{ fontSize: 13, color: '#64748b' }}>{texts['selfcheck.form_description'] || '每题选择最贴近现状的状态,用于帮助你观察企业,不用于评分或排名。'}</p>
<div style={{ marginTop: 16, display: 'grid', gap: 16 }}>
{items.map(item => (
<div key={item.id} style={{ border: '1px solid #e2e8f0', borderRadius: 10, padding: 16 }}>
<div style={{ fontSize: 12, color: '#0f6b5b', fontWeight: 800 }}>{item.dimension}</div>
<div style={{ fontWeight: 700, marginTop: 4 }}>{item.question}</div>
<div style={{ display: 'flex', gap: 8, marginTop: 10, flexWrap: 'wrap' }}>
{(item.options || ['A 较清晰/顺畅', 'M 部分卡点', 'B 待构建']).map((opt: any) => {
{(item.options || ['A态·入态', 'M态·优态', 'B态·越态']).map((opt: any) => {
const label = typeof opt === 'string' ? opt : opt.label
return (
<label
key={label}
style={{
border: answers[item.id] === opt ? '1px solid #163E70' : '1px solid #e2e8f0',
background: answers[item.id] === opt ? '#eff6ff' : '#fff',
border: answers[item.id] === label ? '1px solid #163E70' : '1px solid #e2e8f0',
background: answers[item.id] === label ? '#eff6ff' : '#fff',
padding: '8px 12px',
borderRadius: 999,
fontSize: 13,
@@ -82,7 +82,7 @@ export default function SelfCheck() {
</div>
<button className="btn btn-primary" style={{ width: '100%', marginTop: 20 }} onClick={() => setShowResult(true)}>
查看轻量对照结果
{texts['selfcheck.submit'] || '查看自我观察结果'}
</button>
<p style={{ fontSize: 11, color: '#94a3b8', marginTop: 8, textAlign: 'center' }}>{texts['selfcheck.storage_note'] || '结果仅本地展示,不存储。'}</p>
</div>
@@ -102,18 +102,16 @@ export default function SelfCheck() {
</div>
<div style={{ display: 'flex', gap: 10, marginTop: 12, flexWrap: 'wrap' }}>
<Link to="/contact" className="btn btn-primary">
预约深度诊断
{texts['selfcheck.contact_cta'] || '咨询获取更多观察视角'}
</Link>
<Link to="/evolution" className="btn btn-ghost">
了解进化坐标
{texts['selfcheck.evolution_cta'] || '了解企业进化坐标'}
</Link>
</div>
</div>
)}
<div className="placeholder" style={{ marginTop: 16 }}>
快速路径:看看你的企业 → 轻量对照 → 预约咨询 / 深度路径:企业进化 → 四大价值 → A/M/B → 联系我们(全链路已打通)
</div>
{texts['selfcheck.path_note'] && <div className="placeholder" style={{ marginTop: 16 }}>{texts['selfcheck.path_note']}</div>}
</div>
</section>