management.js 14 KB

1234567891011121314151617181920212223242526272829303132333435363738394041
  1. register('merchant', 'overview', 'M01', '经营概览', () => desktop(`<div class="metric-grid">${['今日订单', '售后待处理', '客户咨询', 'AI 服务用量'].map(label => `<section class="metric"><span>${label}</span><strong>—</strong><small>等待业务数据</small></section>`).join('')}</div><div class="grid">${box('经营与服务趋势', '<div class="chart-placeholder">订单 / 咨询 / 售后处理趋势</div>')}${box('待处理事项', `<div class="account-list">${link('售后工单 ›', 'after-sales')}${link('知识发布 ›', 'knowledge')}${link('费用审批 ›', 'billing')}</div>`)}</div><div class="grid">${box('店铺经营', actions(`${link('商品管理', 'products')}${link('订单管理', 'orders')}${link('店铺与员工', 'access')}`))}${box('智能客服配置', actions(`${link('知识中心', 'knowledge')}${link('售后规则', 'rules')}${link('AI 配置', 'release')}${link('渠道接入', 'channels')}`))}</div>`), ['经营首页使用指标、趋势、待办与常用操作的管理系统布局。', '商家管理员不自动获得售后审批权限或费用审批权限。', '指标使用占位显示,具体业务操作以学习范围为准。']);
  2. register('merchant', 'access', 'M02', '店铺、员工与权限范围', () => desktop(`
  3. ${table(['店铺', '状态', '操作'], [['A 商家 / 配件店', state.storeEnabled ? '启用' : '停用', button(state.storeEnabled ? '停用店铺' : '启用店铺', 'store-toggle')]])}
  4. ${table(['员工', '职责', '店铺范围', '状态 / 操作'], [
  5. ['王芳', '客服:会话查看、接管、回复、方案整理', '配件店', `${state.staffEnabled ? '启用' : '停用'} ${button(state.staffEnabled ? '停用账户' : '启用账户', 'staff-toggle')}`],
  6. ['李明', '主管:售后审批', '配件店', '启用'],
  7. ...state.invites.map(item => [escapeHtml(item.account), escapeHtml(item.permission), '配件店', '已登记,等待身份接入']),
  8. ])}
  9. ${box('邀请员工并授权', form('invite', `${field('员工账户', 'account', '', 'text', 'required')}${select('职责', 'permission', ['客服', '主管', '商家配置管理员'], '客服')}${select('授权店铺', 'store', ['配件店'], '配件店')}`, '保存邀请与授权'))}
  10. ${hint('停用会影响后续访问和执行。已登记的外部申请仍需跟踪。')}`), ['权限按商家、店铺及具体操作授予;费用权限独立管理。', '停用采用二次确认,保存后重新检查等待任务当前权限。', '邀请后显示待接入状态,账户验证完成后才启用访问。']);
  11. register('merchant', 'channels', 'M03', '渠道绑定与接入检查', () => desktop(`${table(['渠道', '店铺', '身份关联', '状态'], [['H5', '配件店', '使用客户登录身份', '启用'], ['Webhook', '配件店', '需要客户完成双身份验证', state.channelEnabled ? '启用' : '停用']])}<div class="grid">${box('接入应用', form('channel-save', `${field('应用名称', 'name', '售后渠道应用', 'text', 'required')}${field('事件回调地址', 'callback', 'https://channel.example.com/events', 'url', 'required')}${select('允许店铺', 'store', ['配件店'], '配件店')}${consent('scope', '授权会话消息、客户动作及工单结果;身份由已验证的渠道映射确定')}`, '保存接入配置'))}${box('接入检查', '<ol><li>验证应用凭据及消息签名</li><li>验证时间戳与重复消息处理</li><li>完成双身份关联</li><li>验证结果回调及重试</li></ol><p>尚未执行真实渠道接入检查。</p>')}</div>${actions(button(state.channelEnabled ? '停用 Webhook 接入' : '启用 Webhook 接入', 'channel-toggle'))}`), ['应用权限是授权范围、店铺范围和客户身份的共同结果。', '保存配置不表示渠道接入已验收;页面分别显示配置状态和接入检查。', '回调地址正式保存时需验证 HTTPS 和外部地址范围。']);
  12. register('merchant', 'rules', 'M04', '换货规则编辑与发布', () => desktop(`<div class="grid">
  13. ${box('已发布规则', `<p>配件店 · V${state.ruleVersion}</p>${tag(state.ruleActive ? '有效' : '已撤销')}<p>键盘 / 签收后 ${state.ruleDays} 天 / 功能故障 / 客户确认 + 主管审批</p>${actions(button('撤销当前规则', 'rule-revoke', state.ruleActive ? '' : 'disabled'))}`)}
  14. ${box('新版本草稿', form('rule-save', `${field('签收后申请天数', 'days', state.ruleDays, 'number', 'required min="1" max="365"')}${field('允许商品类型', 'products', '键盘', 'text', 'required')}${area('规则依据与条款', 'citation', `签收后 ${state.ruleDays} 天内,键盘功能故障可申请换货。`, 'required')}`, '保存规则草稿'))}</div>
  15. ${state.ruleSaved ? box('发布前检查', `<p>草稿 V${state.ruleVersion + 1} 已保存:${escapeHtml(state.formValues.rule || '')}</p><p>新申请使用新版本,已经确认的有效方案继续使用原版本。</p>${button('发布新版本', 'rule-publish', 'class="primary"')}`) : ''}`), ['编辑、检查、发布分成明确动作;规则始终绑定店铺与版本。', '正常发布服务新申请;明确撤销使相关方案进入待修订状态。', '撤销需要说明影响并再次确认。']);
  16. register('merchant', 'knowledge', 'M05', '知识草稿、发布与撤回', () => desktop(`<div class="grid">${box('资料目录', table(['资料', '版本', '状态'], [[escapeHtml(state.knowledgeTitle), `V${state.knowledgeVersion}`, { DRAFT: '草稿', PUBLISHED: '已发布', REVOKED: '已撤回' }[state.knowledge]]]))}${box('资料内容', form('knowledge-save', `${field('资料标题', 'title', state.knowledgeTitle, 'text', 'required')}${area('文本内容', 'text', state.formValues.knowledge || '键盘清洁前请断开电源。换货条件以当前有效售后规则为准。', 'required maxlength="16000"')}`, '保存草稿'))}</div>${hint('文件导入入口:选择原始文件后显示解析进度、失败原因、正文预览,再进入草稿。线稿以文本内容演示发布流程。')}${actions(`${button('预览检索内容', 'knowledge-preview')}${button('发布当前版本', 'knowledge-publish', state.knowledge !== 'DRAFT' ? 'disabled' : 'class="primary"')}${button('撤回当前版本', 'knowledge-revoke', state.knowledge !== 'PUBLISHED' ? 'disabled' : '')}`)}`), ['知识内容只有发布成功后对客户可见。', '修订后为新草稿,需要重新发布;撤回立即停止引用旧版本。', '文件导入、解析进度和失败重试作为后续页面组成,当前用占位说明流程。']);
  17. register('merchant', 'release', 'M06', 'Agent 版本发布', () => desktop(`${box('当前运行版本', '<p>配件店 · 发布版本 R1</p><p>所有会话中新建的助手任务使用已发布配置;进行中和等待中的任务继续使用原配置。</p>')}${table(['配置项目', '待发布内容'], [['流程模板', '换货流程 V2'], ['Prompt / 工具', 'P2 / T1'], ['规则 / 知识', `规则 V${state.ruleVersion} / 知识 V${state.knowledgeVersion}`], ['模型路由', '当前已验证模型配置']])}${hint('发布前需检查配置完整、权限及回归结果。实际回归尚未执行。')}${tag(`评审中的配置:${state.release}`)}${form('release', `${consent('checked', '演示已取得该版本的回归通过记录')}${field('发布说明', 'reason', '', 'text', 'required')}`, '检查并发布到配件店')}`), ['统一检查流程、Prompt、工具、规则、知识及模型版本。', '每次新运行读取当前店铺已发布配置,进行中和等待中的运行保留原配置。', '线稿只演示审核步骤,发布按钮不会运行测试或更改服务配置。']);
  18. register('merchant', 'billing', 'M07', '模型预算与费用审批', () => desktop(`${hint(`当前费用身份:${state.billingReviewer ? '费用审批员工乙' : '费用申请员工甲'}。每日预算按 UTC 日期计算。金额均为设计示例。`)}<div class="grid">${box('当前预算', `<p>每日金额预算:¥${state.budget}</p><p>已结算 / 待核对预占 / 可用额度:接入账本后显示</p><p>费用按已批准价格与用量计算,供应商账单核对状态单独标记。</p>`)}${box('申请调整预算', form('budget', `${field('每日预算(元)', 'amount', state.budget, 'number', 'required min="0" step="0.01"')}${area('调整原因', 'reason', '', 'required')}`, '提交费用申请', state.budgetRequest === 'PENDING' || state.billingReviewer ? 'disabled' : ''))}</div>${state.budgetRequest !== 'NONE' ? box('费用申请', `<p>申请人:员工甲 · 目标预算 ¥${state.pendingBudget}</p><p>状态:${{ PENDING: '等待审批', APPROVED: '已批准', REJECTED: '已拒绝' }[state.budgetRequest]}</p><p>申请有效期:24 小时;审批时重新检查配置版本及申请人权限。</p>${state.billingReviewer ? form('budget-decision', `${select('决定', 'decision', ['批准', '拒绝'], '批准')}${area('审批意见', 'reason', '', 'required')}`, '提交费用决定', state.budgetRequest !== 'PENDING' ? 'disabled' : '') : '<p>申请人不能审批自己的申请。评审时可通过左侧情景切换另一位费用员工。</p>'}`) : ''}${box('价格与费用核对', `<p>价格申请:模型、供应商、地域、生效日期、输入 / 缓存输入 / 输出单价、来源凭证。</p><p>费用核对:账本编号、核实金额、证据编号及原因;交另一位有权限的员工审批。</p>${actions(`${button('查看价格申请字段', 'price-fields')}${button('查看费用核对字段', 'reconcile-fields')}`)}`)}`), ['费用权限独立于售后审批权限,申请人不能自审。', '预算为零时后续模型调用暂停;未知费用保留预占,提供证据后核对。', '价格申请和费用核对沿用同一申请、另一人审批、结果记录流程。']);
  19. register('platform', 'tenants', 'P01', '商家接入与运行状态', () => desktop(`
  20. ${table(['商家', '接入状态', '服务范围', '操作'], [
  21. ['A 商家', state.tenantEnabled ? '启用' : '停用', '配件店 / H5 + Webhook', button(state.tenantEnabled ? '停用商家' : '启用商家', 'tenant-toggle')],
  22. ['B 商家', '待接入', '店铺与应用待配置', '等待接入资料'],
  23. ...state.tenantRegistrations.map(name => [escapeHtml(name), '待接入', '等待配置', '等待接入资料']),
  24. ])}
  25. ${box('登记商家', form('tenant-create', `${field('商家名称', 'name', '', 'text', 'required')}${field('接入负责人', 'contact', '', 'text', 'required')}`, '保存接入登记'))}
  26. ${hint('平台运营查看接入、额度和运行元数据。客户聊天正文、订单内容需独立业务授权。')}`), ['平台与商家后台分别提供导航和权限。', '停用前明确影响:新的访问和调用受限,已经登记的业务操作保持跟踪。']);
  27. register('platform', 'capacity', 'P02', '商家额度与资源分配', () => desktop(`${box('全局资源', '<p>设计示例:全局执行容量 8;交互预留 4,后台预留 2。</p><p>各商家预留份额合计不能超过可分配交互容量。</p>')}${form('capacity', `${select('商家', 'tenant', ['A 商家', 'B 商家'], 'A 商家')}<div class="grid">${field('交互并发上限', 'interactive', state.formValues.interactive || 4, 'number', 'required min="1" max="8"')}${field('交互预留份额', 'reserved', state.formValues.reserved || 2, 'number', 'required min="0" max="4"')}${field('后台并发上限', 'background', 2, 'number', 'required min="0" max="8"')}${field('每日 token 上限', 'tokens', 1000000, 'number', 'required min="0"')}</div>${area('修改原因', 'reason', '', 'required')}`, '检查并保存额度')}${hint('调整不终止正在执行的任务;新增预留在现有执行额度释放后生效。')}`), ['明确全局容量、分类预留与商家份额,保存前检查总量和版本。', '金额预算由租户费用权限及双人审批管理,平台额度页不直接变更金额。']);
  28. register('platform', 'deliveries', 'P03', '结果投递与失败重试', () => desktop(`${table(['投递 / 商家', '业务事件', '投递状态', '操作'], [['DL-001 / A 商家', '工单状态通知 · EV-001', { FAILED: '失败:接收端超时', QUEUED: '已排队,等待下一次投递', SENT: '接收端已确认' }[state.delivery], button('重试本次投递', 'delivery-retry', state.delivery !== 'FAILED' ? 'disabled' : '')]])}${box('投递详情', '<p>接收应用:售后渠道应用</p><p>业务事件编号与投递编号分别保留;重复发送沿用同一业务事件。</p><p>展示字段:事件版本、尝试次数、最近失败原因、下次尝试时间、接收结果。</p>')}${hint('重试只重新发送结果通知。外部售后申请保持原操作编号。')}`), ['失败、等待、已确认分别显示;点击重试后展示排队状态。', '接收方根据事件编号去重、按版本处理乱序。', '平台查看投递元数据,不显示客户对话正文。']);
  29. register('platform', 'health', 'P04', '运行监控与异常处理', () => desktop(`<div class="grid">${box('服务与队列', '<p>API / Agent / 履约 / 记忆 / 知识处理</p><div class="placeholder">健康状态、最长等待时间、失败数量<br>等待真实监控数据</div>')}${box('商家运行情况', '<p>按商家查看受理率、排队、耗时、用量与超额拒绝。</p><div class="placeholder">商家指标列表<br>等待真实监控数据</div>')}</div>${table(['异常类型', '查看内容', '处理入口'], [['消息投递失败', '投递次数、接收结果、重试时间', link('查看失败投递', 'deliveries')], ['额度不足', '商家配额与等待任务', link('查看额度', 'capacity')], ['结果未知', '原业务操作的核实状态', '授权业务员工继续查询原操作']])}${hint('告警显示发生时间、影响范围、处理负责人及恢复状态。')}`), ['监控数据使用空白占位,不将设计示例描述为真实运行数据。', '运行监控和业务正文权限独立。']);
  30. register('platform', 'audit', 'P05', '操作审计', () => desktop(state.audit.length ? table(['顺序', '本次线稿操作记录'], state.audit.map((item, index) => [state.audit.length - index, escapeHtml(item)])) : '<div class="empty">当前尚无演示操作。审批、发布、权限变更和投递重试后可以查看记录。</div>'), ['正式审计展示操作者、范围、时间、对象、变更前后版本与结果。', '这里记录当前线稿操作,刷新页面后清空。正式审计需持久保存并按权限查询。']);