From 4e87dcb849b8aced24ffb83a949acba885877d40 Mon Sep 17 00:00:00 2001 From: Codex User Date: Wed, 9 Sep 2026 10:34:10 +0800 Subject: [PATCH] feat: merge V1.3 CMS content support --- cms/admin/src/App.tsx | 5 ++- cms/server/src/db.js | 8 +++- cms/server/src/index.js | 4 +- cms/server/src/seed.js | 61 +++++++++++++++++++---------- src/components/Layout/Footer.tsx | 16 ++++---- src/components/Layout/GlobalCTA.tsx | 4 +- src/pages/Contact/index.tsx | 9 ++++- src/pages/Methods/index.tsx | 12 +++++- src/pages/SelfCheck/index.tsx | 30 +++++++------- 9 files changed, 96 insertions(+), 53 deletions(-) diff --git a/cms/admin/src/App.tsx b/cms/admin/src/App.tsx index b14546e..08fe262 100644 --- a/cms/admin/src/App.tsx +++ b/cms/admin/src/App.tsx @@ -41,7 +41,7 @@ function api(path:string, token:string, opts:RequestInit={}){ } 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:'行动按钮链接', + 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

全局配置

- {['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=>(
setF({...f,[k]:e.target.value})} />
))} +
setF({...f,qr_image_url:e.target.value})}/>setF({...f,qr_image_url:url})}/>{f.qr_image_url&&二维码预览}
} diff --git a/cms/server/src/db.js b/cms/server/src/db.js index cc076de..efb50f7 100644 --- a/cms/server/src/db.js +++ b/cms/server/src/db.js @@ -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'); } diff --git a/cms/server/src/index.js b/cms/server/src/index.js index bede7df..95b4fb3 100644 --- a/cms/server/src/index.js +++ b/cms/server/src/index.js @@ -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:'保存成功'}); }); diff --git a/cms/server/src/seed.js b/cms/server/src/seed.js index a6d4bfa..64df1ad 100644 --- a/cms/server/src/seed.js +++ b/cms/server/src/seed.js @@ -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) => { diff --git a/src/components/Layout/Footer.tsx b/src/components/Layout/Footer.tsx index 122dc83..4a051b2 100644 --- a/src/components/Layout/Footer.tsx +++ b/src/components/Layout/Footer.tsx @@ -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 (