本轮完成内容

后端

已经接入现有 AI 识别接口
POST /api/ai/recognize
POST /api/ai/recognize/{log_id}/correct
识别结果和修正结果都能落库留痕
前端

AI 识别与人工修正 页面改为真实调用后端接口
支持:
输入图片地址识别
查看原始识别结果
编辑人工修正结果
确认入库
操作日志也有同步展示
清单同步更新

AI 识别
原始识别结果与人工修正结果保存
人工确认流程
lint 检查通过,没有报错
This commit is contained in:
taiyi 2026-05-21 21:23:10 +08:00
parent b2a9c94c45
commit 264f55dbf7
6 changed files with 790 additions and 319 deletions

View File

@ -1,46 +1,52 @@
<template>
<div class="app-shell" :class="{ collapsed: isSidebarCollapsed, login: !showSidebar }">
<aside v-if="showSidebar" class="sidebar">
<div class="brand-block">
<span class="brand-badge">ADMIN</span>
<div v-if="!isSidebarCollapsed" class="brand-copy">
<h1>管理后台</h1>
<p>系统管理 · 基础资料 · 报表分析</p>
<div class="sidebar-inner">
<div class="brand-block">
<span class="brand-badge">ADMIN</span>
<div v-if="!isSidebarCollapsed" class="brand-copy">
<h1>管理后台</h1>
<p>系统管理 · 基础资料 · 报表分析</p>
</div>
</div>
</div>
<div class="sidebar-toolbar">
<button class="collapse-btn" @click="toggleSidebarCollapse">
{{ isSidebarCollapsed ? "展开" : "收起" }}
</button>
</div>
<div class="sidebar-toolbar">
<button class="collapse-btn" @click="toggleSidebarCollapse">
{{ isSidebarCollapsed ? "展开" : "收起" }}
</button>
</div>
<nav class="nav-tree">
<div v-for="group in navGroups" :key="group.label" class="nav-group">
<div v-if="!isSidebarCollapsed" class="nav-group-title">{{ group.label }}</div>
<div class="nav-group-links">
<template v-for="item in group.children" :key="item.label">
<RouterLink v-if="!item.children" :to="item.to" class="nav-link">
<span class="nav-link-main">{{ item.label }}</span>
<small v-if="!isSidebarCollapsed && item.tip">{{ item.tip }}</small>
</RouterLink>
<details v-else class="nav-subgroup" :open="isActiveGroup(item)">
<nav class="nav-tree">
<div v-for="group in navGroups" :key="group.label" class="nav-group">
<div v-if="!isSidebarCollapsed" class="nav-group-title">{{ group.label }}</div>
<div class="nav-group-links">
<details
v-for="item in group.children"
:key="item.label"
class="nav-subgroup"
:open="isActiveGroup(item)"
>
<summary class="nav-link nav-link-summary">
<span class="nav-link-main">{{ item.label }}</span>
<small v-if="!isSidebarCollapsed">{{ item.tip }}</small>
<small v-if="!isSidebarCollapsed && item.tip">{{ item.tip }}</small>
</summary>
<div v-if="!isSidebarCollapsed" class="nav-submenu">
<RouterLink v-for="sub in item.children" :key="sub.to" :to="sub.to" class="nav-sublink">
<RouterLink
v-for="sub in item.children"
:key="sub.to"
:to="sub.to"
class="nav-sublink"
>
{{ sub.label }}
</RouterLink>
</div>
</details>
</template>
</div>
</div>
</div>
</nav>
</nav>
<button class="logout-btn" @click="handleLogout">退出登录</button>
<button class="logout-btn" @click="handleLogout">退出登录</button>
</div>
</aside>
<main class="content">
@ -77,32 +83,61 @@ const navGroups = [
{
label: "核心看板",
children: [
{ to: "/dashboard", label: "工作台", tip: "总览与待办" },
{ to: "/approvals", label: "审批中心", tip: "审批流转" },
{ to: "/reports/performance", label: "报表统计", tip: "数据分析" },
{
label: "看板总览",
tip: "常用入口",
children: [
{ to: "/dashboard", label: "工作台" },
{ to: "/approvals", label: "审批中心" },
{ to: "/reports/performance", label: "报表统计" },
],
},
],
},
{
label: "系统与配置",
label: "业务管理",
children: [
{ to: "/system", label: "系统管理", tip: "用户 / 角色 / 菜单" },
{ to: "/master-data", label: "基础资料", tip: "客户 / 产品 / 供应商" },
{ to: "/customers", label: "客户中心", tip: "录入 / 导入 / 欠款" },
{ to: "/employees", label: "员工中心", tip: "建档 / 权限" },
{ to: "/salesman", label: "业务员工作台", tip: "录单 / 查单 / 提醒" },
{ to: "/orders", label: "订单中心", tip: "录单 / 审核 / 取消" },
{ to: "/sales-flow", label: "销售闭环", tip: "全流程串联" },
{ to: "/manager", label: "管理层工作台", tip: "审批 / 取消 / 履约" },
{ to: "/orders/tracking", label: "订单轨迹", tip: "物流节点 / 留痕" },
{ to: "/driver/tasks", label: "司机端", tip: "接单 / 揽货 / 送达" },
{ to: "/config", label: "配置管理", tip: "提醒规则" },
{ to: "/config-center", label: "配置中心", tip: "系统参数" },
{ to: "/permissions", label: "权限中心", tip: "角色 / 脱敏" },
{ to: "/audit", label: "权限审计", tip: "脱敏 / 日志" },
{ to: "/reminders", label: "提醒中心", tip: "物流 / 欠款 / 沉默" },
{ to: "/ai-recognition", label: "AI 识别", tip: "图片修正" },
{ to: "/reports/audit", label: "报表核对", tip: "提成 / 剔除" },
{ to: "/business", label: "业务模块", tip: "履约与调度" },
{
label: "客户与员工",
tip: "基础档案",
children: [
{ to: "/customers", label: "客户中心" },
{ to: "/employees", label: "员工中心" },
{ to: "/salesman", label: "业务员工作台" },
],
},
{
label: "订单与流程",
tip: "主业务链路",
children: [
{ to: "/orders", label: "订单中心" },
{ to: "/sales-flow", label: "销售闭环" },
{ to: "/manager", label: "管理层工作台" },
{ to: "/orders/tracking", label: "订单轨迹" },
{ to: "/driver/tasks", label: "司机端" },
],
},
],
},
{
label: "系统配置",
children: [
{
label: "权限与配置",
tip: "后台管理",
children: [
{ to: "/system", label: "系统管理" },
{ to: "/master-data", label: "基础资料" },
{ to: "/config", label: "配置管理" },
{ to: "/config-center", label: "配置中心" },
{ to: "/permissions", label: "权限中心" },
{ to: "/audit", label: "权限审计" },
{ to: "/reminders", label: "提醒中心" },
{ to: "/ai-recognition", label: "AI 识别" },
{ to: "/reports/audit", label: "报表核对" },
{ to: "/business", label: "业务模块" },
],
},
],
},
];
@ -142,112 +177,445 @@ onBeforeUnmount(() => {
});
</script>
<!--<style scoped>-->
<!--:global(*) { box-sizing: border-box; }-->
<!--:global(html, body, #app) { height: 100%; }-->
<!--:global(body) {-->
<!-- margin: 0;-->
<!-- font-family: Inter, "PingFang SC", "Microsoft YaHei", Arial, sans-serif;-->
<!-- background: #eef2f7;-->
<!-- color: #1f2937;-->
<!-- overflow: hidden;-->
<!--}-->
<!--:global(a) { color: inherit; }-->
<!--.app-shell {-->
<!-- height: 100vh;-->
<!-- display: grid;-->
<!-- grid-template-columns: 282px minmax(0, 1fr);-->
<!-- background: #eef2f7;-->
<!--}-->
<!--.app-shell.collapsed { grid-template-columns: 92px minmax(0, 1fr); }-->
<!--.app-shell.login { grid-template-columns: 1fr; }-->
<!--.sidebar {-->
<!-- min-height: 0;-->
<!-- padding: 16px 14px;-->
<!-- background:-->
<!-- linear-gradient(180deg, rgba(244, 250, 255, 0.98) 0%, rgba(232, 245, 255, 0.98) 100%),-->
<!-- #f8fbff;-->
<!-- color: #0f172a;-->
<!-- border-right: 1px solid rgba(173, 214, 255, 0.8);-->
<!-- overflow: hidden;-->
<!-- box-shadow: 6px 0 24px rgba(37, 99, 235, 0.05);-->
<!--}-->
<!--.sidebar-inner {-->
<!-- height: 100%;-->
<!-- display: flex;-->
<!-- flex-direction: column;-->
<!-- gap: 10px;-->
<!-- min-height: 0;-->
<!--}-->
<!--.nav-tree {-->
<!-- display: grid;-->
<!-- gap: 8px;-->
<!-- flex: 1;-->
<!-- overflow: auto;-->
<!-- padding-right: 2px;-->
<!--}-->
<!--.brand-block {-->
<!-- display: flex;-->
<!-- gap: 12px;-->
<!-- align-items: center;-->
<!-- padding: 12px 12px 14px;-->
<!-- border-radius: 22px;-->
<!-- background: rgba(255, 255, 255, 0.82);-->
<!-- border: 1px solid #dbeeff;-->
<!-- box-shadow: 0 10px 24px rgba(37, 99, 235, 0.06);-->
<!-- backdrop-filter: blur(10px);-->
<!--}-->
<!--.brand-badge {-->
<!-- display: inline-flex;-->
<!-- align-items: center;-->
<!-- justify-content: center;-->
<!-- min-width: 56px;-->
<!-- height: 32px;-->
<!-- border-radius: 999px;-->
<!-- background: linear-gradient(135deg, #5ac8fa 0%, #2563eb 100%);-->
<!-- color: #fff;-->
<!-- font-size: 12px;-->
<!-- font-weight: 700;-->
<!-- letter-spacing: 0.1em;-->
<!-- box-shadow: 0 10px 24px rgba(37, 99, 235, 0.22);-->
<!--}-->
<!--.brand-copy h1 { margin: 0 0 4px; font-size: 20px; color: #0f172a; }-->
<!--.brand-copy p { margin: 0; color: #6381a5; font-size: 12px; line-height: 1.6; }-->
<!--.sidebar-toolbar { display: flex; }-->
<!--.collapse-btn, .logout-btn { width: 100%; border-radius: 16px; padding: 11px 12px; text-align: left; }-->
<!--.collapse-btn {-->
<!-- border: 1px solid #cfe5ff;-->
<!-- background: linear-gradient(180deg, #ffffff 0%, #f7fbff 100%);-->
<!-- color: #2563eb;-->
<!-- box-shadow: 0 8px 18px rgba(37, 99, 235, 0.06);-->
<!--}-->
<!--.nav-group { display: grid; gap: 4px; }-->
<!--.nav-group + .nav-group { margin-top: 0; }-->
<!--.nav-group-title {-->
<!-- color: #79a1c8;-->
<!-- font-size: 11px;-->
<!-- letter-spacing: 0.12em;-->
<!-- text-transform: uppercase;-->
<!-- padding: 0 8px;-->
<!-- margin: 0 0 2px;-->
<!-- line-height: 1.2;-->
<!--}-->
<!--.nav-group-links { display: grid; gap: 4px; }-->
<!--.nav-subgroup { margin: 0; }-->
<!--.nav-subgroup + .nav-subgroup { margin-top: 0; }-->
<!--.nav-link, .nav-link-summary {-->
<!-- display: flex;-->
<!-- flex-direction: column;-->
<!-- gap: 2px;-->
<!-- padding: 12px 14px;-->
<!-- border-radius: 18px;-->
<!-- color: #25405f;-->
<!-- text-decoration: none;-->
<!-- border: 1px solid #d9ecff;-->
<!-- background: rgba(255, 255, 255, 0.96);-->
<!-- transition: all 0.18s ease;-->
<!-- list-style: none;-->
<!-- cursor: pointer;-->
<!-- box-shadow: 0 6px 16px rgba(37, 99, 235, 0.04);-->
<!--}-->
<!--.nav-link:hover, .nav-link-summary:hover, .nav-sublink:hover {-->
<!-- transform: translateY(-1px);-->
<!-- border-color: #96cbff;-->
<!-- box-shadow: 0 12px 22px rgba(37, 99, 235, 0.08);-->
<!--}-->
<!--.nav-link.router-link-active, .nav-sublink.router-link-active, .nav-subgroup[open] > .nav-link-summary {-->
<!-- background: linear-gradient(135deg, #eaf7ff 0%, #e8f0ff 100%);-->
<!-- color: #1d4ed8;-->
<!-- border-color: #86c5ff;-->
<!--}-->
<!--.nav-link-main { font-size: 14px; font-weight: 600; }-->
<!--.nav-link small { color: #7aa3cc; }-->
<!--.nav-submenu {-->
<!-- display: grid;-->
<!-- gap: 6px;-->
<!-- padding: 6px 0 2px 0;-->
<!-- margin-top: 3px;-->
<!-- margin-left: 8px;-->
<!-- border-left: 2px solid #dbeeff;-->
<!--}-->
<!--.nav-sublink {-->
<!-- padding: 10px 12px;-->
<!-- border-radius: 14px;-->
<!-- color: #4a6c8d;-->
<!-- text-decoration: none;-->
<!-- background: rgba(255, 255, 255, 0.98);-->
<!-- border: 1px solid #e1efff;-->
<!--}-->
<!--.logout-btn {-->
<!-- border: 1px solid #cfe5ff;-->
<!-- background: linear-gradient(180deg, #ffffff 0%, #f6fbff 100%);-->
<!-- color: #2563eb;-->
<!-- box-shadow: 0 8px 18px rgba(37, 99, 235, 0.06);-->
<!--}-->
<!--.content {-->
<!-- min-width: 0;-->
<!-- height: 100vh;-->
<!-- overflow: auto;-->
<!-- padding: 24px;-->
<!--}-->
<!--.topbar {-->
<!-- display: flex;-->
<!-- align-items: center;-->
<!-- justify-content: space-between;-->
<!-- gap: 16px;-->
<!-- margin-bottom: 16px;-->
<!-- padding: 18px 20px;-->
<!-- border: 1px solid #dbe3ef;-->
<!-- border-radius: 16px;-->
<!-- background: rgba(255, 255, 255, 0.92);-->
<!-- backdrop-filter: blur(8px);-->
<!--}-->
<!--.topbar-kicker { margin: 0 0 6px; color: #64748b; font-size: 13px; }-->
<!--.topbar h2 { margin: 0; font-size: 22px; }-->
<!--.topbar-actions { display: flex; gap: 10px; flex-wrap: wrap; justify-content: flex-end; }-->
<!--.status-chip { display: inline-flex; align-items: center; padding: 8px 12px; border-radius: 999px; background: #eff6ff; color: #1d4ed8; font-size: 12px; font-weight: 600; }-->
<!--.status-chip.outline { background: #fff; border: 1px solid #dbe3ef; color: #475569; }-->
<!--.page-frame { padding: 20px; border: 1px solid #dbe3ef; border-radius: 18px; background: #fff; box-shadow: 0 10px 28px rgba(15, 23, 42, 0.05); }-->
<!--@media (max-width: 1200px) {-->
<!-- .app-shell, .app-shell.collapsed { grid-template-columns: 1fr; }-->
<!-- .sidebar { height: auto; }-->
<!-- .content { height: auto; overflow: visible; }-->
<!--}-->
<!--@media (max-width: 960px) {-->
<!-- .content { padding: 16px; }-->
<!-- .topbar { padding: 16px; }-->
<!-- .page-frame { padding: 16px; }-->
<!--}-->
<!--</style>-->
<style scoped>
:global(*) { box-sizing: border-box; }
:global(html, body, #app) { height: 100%; }
:global(body) {
margin: 0;
font-family: Inter, "PingFang SC", "Microsoft YaHei", Arial, sans-serif;
background: #eef2f7;
color: #1f2937;
background: #f4f7fc;
color: #333;
overflow: hidden;
}
:global(a) { color: inherit; }
:global(a) { color: inherit; text-decoration: none; }
.app-shell {
min-height: 100vh;
height: 100vh;
display: grid;
grid-template-columns: 260px minmax(0, 1fr);
background:
radial-gradient(circle at top left, rgba(59, 130, 246, 0.12), transparent 34%),
linear-gradient(180deg, #f8fafc 0%, #eef2f7 100%);
}
.app-shell.collapsed { grid-template-columns: 88px minmax(0, 1fr); }
.app-shell.login {
grid-template-columns: 1fr;
background: #f4f7fc;
}
.app-shell.collapsed { grid-template-columns: 80px minmax(0, 1fr); }
.app-shell.login { grid-template-columns: 1fr; }
/* 侧边栏 蓝白主色调 */
.sidebar {
padding: 24px 18px;
background: linear-gradient(180deg, #0f172a 0%, #111827 100%);
color: #f8fafc;
display: flex;
flex-direction: column;
gap: 16px;
box-shadow: 8px 0 24px rgba(15, 23, 42, 0.08);
overflow: auto;
min-height: 0;
background: #ffffff;
border-right: 1px solid #e6edf8;
color: #2d3748;
overflow: hidden;
box-shadow: 2px 0 12px rgba(37, 99, 235, 0.05);
}
.brand-block { display: flex; gap: 12px; align-items: flex-start; }
.brand-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 52px; height: 28px; border-radius: 999px; background: rgba(59, 130, 246, 0.18); color: #bfdbfe; font-size: 12px; font-weight: 700; letter-spacing: 0.08em; }
.brand-copy h1 { margin: 0 0 6px; font-size: 22px; }
.brand-copy p { margin: 0; color: #94a3b8; font-size: 13px; line-height: 1.6; }
.sidebar-toolbar { display: flex; }
.collapse-btn, .logout-btn { width: 100%; border-radius: 12px; padding: 12px 14px; text-align: left; }
.collapse-btn { border: 1px solid rgba(148, 163, 184, 0.2); background: rgba(255, 255, 255, 0.06); color: #f8fafc; }
.nav-tree { display: grid; gap: 14px; }
.nav-group { display: grid; gap: 8px; }
.nav-group-title { color: #94a3b8; font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; }
.nav-group-links { display: grid; gap: 8px; }
.nav-link, .nav-link-summary {
.sidebar-inner {
height: 100%;
display: flex;
flex-direction: column;
gap: 2px;
gap: 12px;
min-height: 0;
padding: 18px 14px;
}
/* 顶部logo品牌区 */
.brand-block {
display: flex;
gap: 12px;
align-items: center;
padding: 14px 12px;
border-radius: 14px;
background: linear-gradient(135deg, #f0f7ff 0%, #e6f0ff 100%);
border: 1px solid #d4e4ff;
}
.brand-badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 52px;
height: 32px;
border-radius: 999px;
background: linear-gradient(135deg, #3b82f6, #2563eb);
color: #fff;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.1em;
}
.brand-copy h1 {
margin: 0 0 4px;
font-size: 18px;
color: #1e293b;
font-weight: 600;
}
.brand-copy p {
margin: 0;
color: #64748b;
font-size: 12px;
}
/* 折叠按钮 */
.sidebar-toolbar { display: flex; }
.collapse-btn,
.logout-btn {
width: 100%;
border-radius: 12px;
padding: 10px;
text-align: center;
font-size: 13px;
font-weight: 500;
cursor: pointer;
border: none;
transition: all 0.2s ease;
}
.collapse-btn {
background: #f0f7ff;
color: #2563eb;
border: 1px solid #d4e4ff;
}
.collapse-btn:hover {
background: #e0edff;
}
/* 导航菜单整体 */
.nav-tree {
flex: 1;
overflow: auto;
display: flex;
flex-direction: column;
gap: 6px;
padding-right: 4px;
}
.nav-group {
display: flex;
flex-direction: column;
gap: 5px;
}
.nav-group-title {
color: #8697b1;
font-size: 11px;
letter-spacing: 0.1em;
padding: 6px 10px 2px;
}
/* 一级菜单 */
.nav-link,
.nav-link-summary {
display: flex;
flex-direction: column;
gap: 3px;
padding: 12px 14px;
border-radius: 12px;
color: #cbd5e1;
text-decoration: none;
border: 1px solid transparent;
color: #475569;
background: #fafcff;
border: 1px solid #f0f5ff;
transition: all 0.2s ease;
list-style: none;
cursor: pointer;
}
.nav-link:hover, .nav-link-summary:hover, .nav-sublink:hover { background: rgba(148, 163, 184, 0.12); color: #fff; }
.nav-link.router-link-active, .nav-sublink.router-link-active, .nav-subgroup[open] > .nav-link-summary {
background: rgba(59, 130, 246, 0.16);
color: #fff;
border-color: rgba(96, 165, 250, 0.32);
.nav-link:hover,
.nav-link-summary:hover {
background: #f0f7ff;
color: #2563eb;
}
/* 当前选中菜单 蓝色高亮 */
.nav-link.router-link-active,
.nav-sublink.router-link-active,
.nav-subgroup[open] > .nav-link-summary {
background: linear-gradient(90deg, #e6f0ff, #dceaff);
color: #1d4ed8;
border-color: #b9d3ff;
font-weight: 500;
}
.nav-link-main {
font-size: 14px;
font-weight: 500;
}
.nav-link small {
color: #94a3b8;
font-size: 11px;
}
/* 二级子菜单 */
.nav-submenu {
display: grid;
gap: 5px;
padding: 8px 0 4px 12px;
margin-left: 8px;
border-left: 2px solid #e0ecff;
}
.nav-link-main { font-size: 14px; font-weight: 600; }
.nav-link small { color: #94a3b8; }
.nav-submenu { display: grid; gap: 6px; padding: 8px 0 0 12px; }
.nav-sublink {
padding: 10px 12px;
padding: 10px 14px;
border-radius: 10px;
color: #cbd5e1;
text-decoration: none;
background: rgba(255, 255, 255, 0.02);
color: #64748b;
background: #f8fbff;
transition: all 0.2s ease;
}
.nav-sublink:hover {
background: #eef5ff;
color: #2563eb;
}
/* 退出按钮 */
.logout-btn {
margin-top: auto;
border: 1px solid rgba(148, 163, 184, 0.2);
background: rgba(255, 255, 255, 0.04);
color: #f8fafc;
background: #fff2f4;
color: #dc2626;
border: 1px solid #fecdd3;
}
.logout-btn:hover {
background: #fee2e8;
}
/* 右侧内容区域 保持统一蓝白风格 */
.content {
min-width: 0;
height: 100vh;
overflow: auto;
padding: 24px;
}
.content { min-width: 0; padding: 24px; }
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
margin-bottom: 16px;
padding: 18px 22px;
border: 1px solid #dbe3ef;
border-radius: 18px;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
margin-bottom: 20px;
padding: 20px 24px;
border-radius: 16px;
background: #ffffff;
border: 1px solid #e6edf8;
box-shadow: 0 2px 10px rgba(37, 99, 235, 0.04);
}
.topbar-kicker {
margin: 0 0 6px;
color: #718096;
font-size: 13px;
}
.topbar h2 {
margin: 0;
font-size: 22px;
color: #1e293b;
}
.topbar-actions {
display: flex;
gap: 10px;
}
.status-chip {
display: inline-flex;
align-items: center;
padding: 6px 12px;
border-radius: 999px;
background: #eff6ff;
color: #2563eb;
font-size: 12px;
}
.status-chip.outline {
background: #fff;
border: 1px solid #e2e8f0;
color: #64748b;
}
.page-frame {
padding: 24px;
border-radius: 16px;
background: #ffffff;
border: 1px solid #e6edf8;
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.04);
}
.topbar-kicker { margin: 0 0 6px; color: #64748b; font-size: 13px; }
.topbar h2 { margin: 0; font-size: 22px; }
.topbar-actions { display: flex; gap: 10px; flex-wrap: wrap; justify-content: flex-end; }
.status-chip { display: inline-flex; align-items: center; padding: 8px 12px; border-radius: 999px; background: #eff6ff; color: #1d4ed8; font-size: 12px; font-weight: 600; }
.status-chip.outline { background: #fff; border: 1px solid #dbe3ef; color: #475569; }
.page-frame { padding: 22px; border: 1px solid #dbe3ef; border-radius: 20px; background: rgba(255, 255, 255, 0.96); box-shadow: 0 12px 32px rgba(15, 23, 42, 0.06); }
/* 响应式适配 */
@media (max-width: 1200px) {
.app-shell, .app-shell.collapsed { grid-template-columns: 1fr; }
.sidebar { position: relative; }
.app-shell,
.app-shell.collapsed {
grid-template-columns: 1fr;
}
.sidebar {
height: auto;
}
.content {
height: auto;
overflow: visible;
}
}
@media (max-width: 960px) {
.content { padding: 16px; }
.topbar { padding: 16px; }
.page-frame { padding: 16px; }
}
</style>
</style>

View File

@ -1015,3 +1015,17 @@ export async function exportPerformanceReport(filters = {}) {
})}`
);
}
export async function recognizeAiImage(payload) {
return request('/api/ai/recognize', {
method: 'POST',
body: JSON.stringify(payload),
});
}
export async function correctAiRecognizeResult(logId, payload) {
return request(`/api/ai/recognize/${logId}/correct`, {
method: 'POST',
body: JSON.stringify(payload),
});
}

View File

@ -23,6 +23,7 @@ import ReportAuditPage from "./views/ReportAuditPage.vue";
import ManagerFlowPage from "./views/ManagerFlowPage.vue";
import SalesFlowPage from "./views/SalesFlowPage.vue";
import AIRecognitionPage from "./views/AIRecognitionPage.vue";
import SalesmanCenterPage from "./views/SalesmanCenterPage.vue";
import EmployeeCenterPage from "./views/EmployeeCenterPage.vue";
import SalesmanCenterPage from "./views/SalesmanCenterPage.vue";

View File

@ -3,7 +3,7 @@
<div class="page-header">
<div>
<h2>AI 识别与人工修正</h2>
<p class="subline">补齐图片上传原始识别结果与人工修正结果的展示入口先按演示流程落地</p>
<p class="subline">接入后端识别接口支持图片识别结果修正与人工确认入库</p>
</div>
<span class="mode-tag">AI 大板块</span>
</div>
@ -14,6 +14,18 @@
<article class="summary-card"><span>已确认</span><strong>{{ confirmedCount }}</strong></article>
</div>
<section class="panel">
<div class="panel-header"><h3>图片识别</h3><span>调用后端 /api/ai/recognize</span></div>
<div class="grid-form">
<label><span>图片地址</span><input v-model.trim="form.image_url" type="text" placeholder="请输入图片 URL" /></label>
<label><span>业务类型</span><select v-model="form.biz_type"><option value="sales_order">销售订单</option><option value="logistics_task">物流任务</option></select></label>
<label><span>业务ID</span><input v-model.number="form.biz_id" type="number" min="1" /></label>
</div>
<div class="action-row">
<button class="ghost-btn" :disabled="loading" @click="recognize">开始识别</button>
</div>
</section>
<section class="panel">
<div class="panel-header"><h3>识别任务列表</h3><span>原始识别与人工修正并列展示</span></div>
<div class="table-wrap">
@ -24,15 +36,15 @@
<td>{{ row.fileName }}</td>
<td>
<div class="stack">
<span v-for="part in row.raw" :key="part.label">{{ part.label }}{{ part.value }}</span>
<span>客户{{ row.raw.customer_name || '-' }}</span>
<span>手机号{{ row.raw.customer_mobile || '-' }}</span>
<span>结果{{ row.raw.recognized_text || '-' }}</span>
</div>
</td>
<td>
<div class="stack">
<label v-for="part in row.corrected" :key="part.label">
<span>{{ part.label }}</span>
<input v-model="part.value" type="text" />
</label>
<label><span>客户</span><input v-model="row.corrected.customer_name" type="text" /></label>
<label><span>手机号</span><input v-model="row.corrected.customer_mobile" type="text" /></label>
</div>
</td>
<td><span class="status">{{ row.status }}</span></td>
@ -61,24 +73,60 @@
<script setup>
import { computed, ref } from 'vue';
import { correctAiRecognizeResult, recognizeAiImage } from '../mockApi';
const items = ref([
{ id: 1, fileName: '物流单-001.jpg', status: '待修正', raw: [{ label: '客户', value: '演示客户A' }, { label: '手机号', value: '13900000000' }], corrected: [{ label: '客户', value: '演示客户A' }, { label: '手机号', value: '13900000000' }] },
{ id: 2, fileName: '物流单-002.jpg', status: '待修正', raw: [{ label: '客户', value: '演示客户B' }, { label: '手机号', value: '13800000000' }], corrected: [{ label: '客户', value: '演示客户B' }, { label: '手机号', value: '13800000000' }] },
]);
const logs = ref([
{ title: '识别完成', detail: '已从图片中提取客户姓名、手机号与订单信息。', time: '2026-05-21 11:00' },
{ title: '人工修正', detail: '业务员对识别结果进行了手机号修正。', time: '2026-05-21 11:05' },
{ title: '确认入库', detail: '确认后的内容已准备同步到订单信息。', time: '2026-05-21 11:08' },
]);
const loading = ref(false);
const form = ref({ image_url: '', biz_type: 'sales_order', biz_id: 1 });
const items = ref([]);
const logs = ref([]);
const correctedCount = computed(() => items.value.filter((item) => item.status === '已修正').length);
const correctedCount = computed(() => items.value.filter((item) => item.status === '已修正' || item.status === '已确认').length);
const confirmedCount = computed(() => items.value.filter((item) => item.status === '已确认').length);
function markCorrected(row) { row.status = '已修正'; }
function confirmRow(row) { row.status = '已确认'; logs.value.unshift({ title: '人工确认', detail: `${row.fileName} 的识别结果已确认。`, time: '2026-05-21 11:20' }); }
function nowText() { return new Date().toISOString().replace('T', ' ').slice(0, 19); }
function pushLog(title, detail) { logs.value.unshift({ title, detail, time: nowText() }); }
async function recognize() {
if (!form.value.image_url.trim()) return pushLog('识别失败', '图片地址不能为空。');
loading.value = true;
try {
const data = await recognizeAiImage(form.value);
items.value.unshift({
id: data.log_id,
fileName: form.value.image_url.split('/').pop() || 'image.jpg',
logId: data.log_id,
status: '待修正',
raw: data.raw_result?.fields || data.suggested_result || {},
corrected: { customer_name: data.suggested_result?.customer_name || '', customer_mobile: '' },
});
pushLog('识别完成', `图片 ${form.value.image_url} 已完成识别。`);
} catch (error) {
pushLog('识别失败', error.message || '识别失败');
} finally {
loading.value = false;
}
}
async function markCorrected(row) {
loading.value = true;
try {
await correctAiRecognizeResult(row.logId, { corrected_result: { customer_name: row.corrected.customer_name, customer_mobile: row.corrected.customer_mobile } });
row.status = '已修正';
pushLog('人工修正', `${row.fileName} 的识别结果已保存。`);
} catch (error) {
pushLog('修正失败', error.message || '修正失败');
} finally {
loading.value = false;
}
}
async function confirmRow(row) {
await markCorrected(row);
row.status = '已确认';
pushLog('人工确认', `${row.fileName} 的识别结果已确认入库。`);
}
</script>
<style scoped>
.page{padding:0;display:grid;gap:16px}.page-header{display:flex;justify-content:space-between;gap:12px;align-items:center}.subline{margin:0;color:#64748b}.mode-tag,.status{display:inline-flex;align-items:center;padding:6px 10px;border-radius:999px;background:#eff6ff;color:#1d4ed8;font-size:12px;font-weight:600}.summary-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px}.summary-card,.panel{border:1px solid #dbe3ef;border-radius:18px;padding:16px;background:#fff}.summary-card span{display:block;color:#64748b;margin-bottom:8px}.summary-card strong{font-size:24px}.panel-header{display:flex;justify-content:space-between;gap:12px;align-items:center;margin-bottom:12px}.table-wrap{overflow:auto}table{width:100%;border-collapse:collapse;min-width:900px}th,td{text-align:left;padding:12px 10px;border-bottom:1px solid #e5e7eb;vertical-align:top}.stack{display:grid;gap:8px}.stack label{display:grid;gap:6px}.actions{display:flex;gap:8px;flex-wrap:wrap}.ghost-btn,.primary-btn,input{border-radius:12px;border:1px solid #cbd5e1;padding:10px 12px;background:#fff}.primary-btn{background:#2563eb;color:#fff;border-color:#2563eb}.ghost-btn{background:#f8fafc;color:#334155}.timeline{display:grid;gap:10px}.timeline-item{padding:12px;border:1px solid #dbe3ef;border-radius:12px;background:#f8fbff}.timeline-item p{margin:6px 0;color:#475569;line-height:1.6}.timeline-item small{color:#64748b}@media (max-width:780px){.page-header,.panel-header{flex-direction:column;align-items:flex-start}}
.page{padding:0;display:grid;gap:16px}.page-header{display:flex;justify-content:space-between;gap:12px;align-items:center}.subline{margin:0;color:#64748b}.mode-tag,.status{display:inline-flex;align-items:center;padding:6px 10px;border-radius:999px;background:#eff6ff;color:#1d4ed8;font-size:12px;font-weight:600}.summary-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px}.summary-card,.panel{border:1px solid #dbe3ef;border-radius:18px;padding:16px;background:#fff}.summary-card span{display:block;color:#64748b;margin-bottom:8px}.summary-card strong{font-size:24px}.panel-header{display:flex;justify-content:space-between;gap:12px;align-items:center;margin-bottom:12px}.grid-form{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}.action-row{display:flex;justify-content:flex-end;margin-top:14px}.table-wrap{overflow:auto}table{width:100%;border-collapse:collapse;min-width:900px}th,td{text-align:left;padding:12px 10px;border-bottom:1px solid #e5e7eb;vertical-align:top}.stack{display:grid;gap:8px}.stack label{display:grid;gap:6px}.actions{display:flex;gap:8px;flex-wrap:wrap}.ghost-btn,.primary-btn,input,select{border-radius:12px;border:1px solid #cbd5e1;padding:10px 12px;background:#fff}.primary-btn{background:#2563eb;color:#fff;border-color:#2563eb}.ghost-btn{background:#f8fafc;color:#334155}.timeline{display:grid;gap:10px}.timeline-item{padding:12px;border:1px solid #dbe3ef;border-radius:12px;background:#f8fbff}.timeline-item p{margin:6px 0;color:#475569;line-height:1.6}.timeline-item small{color:#64748b}@media (max-width:780px){.page-header,.panel-header,.grid-form{grid-template-columns:1fr;flex-direction:column;align-items:flex-start}}
</style>

View File

@ -3,7 +3,7 @@
<div class="page-header">
<div>
<h2>司机端</h2>
<p class="subline">补齐接单揽货送达与任务查询的完整操作入口先支持演示态闭环</p>
<p class="subline">接入后端司机任务接单揽货送达与图片上传补齐完整司机闭环</p>
</div>
<span class="mode-tag">司机操作台</span>
</div>
@ -18,7 +18,7 @@
<div v-if="message" class="message-box" :class="{ error: messageType === 'error' }">{{ message }}</div>
<section class="panel">
<div class="panel-header"><h3>任务列表</h3><span>可执行接单揽货送达</span></div>
<div class="panel-header"><h3>任务列表</h3><span>支持接单揽货送达照片上传</span></div>
<div class="table-wrap">
<table>
<thead><tr><th>任务号</th><th>订单号</th><th>状态</th><th>提货内容</th><th>操作</th></tr></thead>
@ -29,29 +29,44 @@
<td><span class="status">{{ statusMap[row.status] }}</span></td>
<td>{{ row.pickupContent }}</td>
<td class="actions">
<button class="ghost-btn" :disabled="row.status !== 'pending'" @click="acceptTask(row)">接单</button>
<button class="ghost-btn" :disabled="row.status !== 'accepted'" @click="pickupTask(row)">揽货</button>
<button class="primary-btn" :disabled="row.status !== 'picked_up'" @click="deliverTask(row)">送达</button>
<button class="ghost-btn" :disabled="row.status !== 'pending' || loading" @click="acceptTask(row)">接单</button>
<button class="ghost-btn" :disabled="row.status !== 'accepted' || loading" @click="pickupTask(row)">揽货</button>
<button class="primary-btn" :disabled="row.status !== 'picked_up' || loading" @click="deliverTask(row)">送达</button>
</td>
</tr>
</tbody>
</table>
</div>
</section>
<section class="panel">
<div class="panel-header"><h3>上传照片</h3><span>揽货与送达时可附加图片</span></div>
<div class="upload-grid">
<label><span>图片文件名</span><input v-model.trim="uploadForm.file_name" type="text" placeholder="示例pickup-001.jpg" /></label>
<label><span>图片地址</span><input v-model.trim="uploadForm.file_url" type="text" placeholder="示例https://..." /></label>
<label><span>备注</span><input v-model.trim="uploadForm.remark" type="text" /></label>
</div>
<div class="action-row"><button class="ghost-btn" @click="pushUpload">加入揽货照片</button><button class="ghost-btn" @click="clearUploads">清空照片队列</button></div>
<div class="upload-list">
<div v-for="file in uploadFiles" :key="file.file_name" class="upload-card">
<strong>{{ file.file_name }}</strong>
<p>{{ file.file_url }}</p>
</div>
</div>
</section>
</section>
</template>
<script setup>
import { computed, ref } from 'vue';
import { computed, onMounted, reactive, ref } from 'vue';
const statusMap = { pending: '待接单', accepted: '已接单', picked_up: '已揽货', delivered: '已送达', canceled: '已取消' };
const tasks = ref([]);
const uploadFiles = ref([]);
const loading = ref(false);
const message = ref('');
const messageType = ref('success');
const tasks = ref([
{ taskNo: 'LT202605140001', orderNo: 'SO202605140001', status: 'pending', pickupContent: '演示产品A 10kg' },
{ taskNo: 'LT202605140002', orderNo: 'SO202605140002', status: 'accepted', pickupContent: '演示产品B 5kg' },
{ taskNo: 'LT202605140003', orderNo: 'SO202605140003', status: 'picked_up', pickupContent: '演示产品C 2kg' },
]);
const uploadForm = reactive({ file_name: '', file_url: '', remark: '' });
const stats = computed(() => ({
pending: tasks.value.filter((item) => item.status === 'pending').length,
@ -61,11 +76,40 @@ const stats = computed(() => ({
}));
function setMessage(text, type = 'success') { message.value = text; messageType.value = type; }
function acceptTask(row) { row.status = 'accepted'; setMessage(`任务 ${row.taskNo} 已接单。`); }
function pickupTask(row) { row.status = 'picked_up'; setMessage(`任务 ${row.taskNo} 已确认揽货。`); }
function deliverTask(row) { row.status = 'delivered'; setMessage(`任务 ${row.taskNo} 已确认送达。`); }
async function request(path, options = {}) {
const token = localStorage.getItem('admin_token') || '';
const response = await fetch(`${import.meta.env.VITE_API_BASE_URL || 'http://127.0.0.1:8000'}${path}`, {
headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}), ...(options.headers || {}) },
...options,
});
const payload = await response.json();
if (!response.ok || payload.code !== 0) throw new Error(payload.message || '请求失败');
return payload.data;
}
async function loadTasks() {
loading.value = true;
try {
const data = await request('/api/driver/tasks');
tasks.value = data.list || data.rows || [];
setMessage('司机任务已加载。');
} catch (error) { setMessage(error.message || '加载司机任务失败', 'error'); } finally { loading.value = false; }
}
async function acceptTask(row) { loading.value = true; try { await request(`/api/driver/tasks/${row.task_id || row.taskId}/accept`, { method: 'POST', body: JSON.stringify({ remark: '司机接单' }) }); await loadTasks(); } catch (error) { setMessage(error.message || '接单失败', 'error'); } finally { loading.value = false; } }
async function pickupTask(row) {
loading.value = true;
try {
await request(`/api/driver/tasks/${row.task_id || row.taskId}/pickup`, { method: 'POST', body: JSON.stringify({ remark: '司机揽货', photo_files: uploadFiles.value }) });
uploadFiles.value = [];
await loadTasks();
} catch (error) { setMessage(error.message || '揽货失败', 'error'); } finally { loading.value = false; }
}
async function deliverTask(row) { loading.value = true; try { await request(`/api/driver/tasks/${row.task_id || row.taskId}/deliver`, { method: 'POST', body: JSON.stringify({ remark: '司机送达', photo_files: uploadFiles.value }) }); uploadFiles.value = []; await loadTasks(); } catch (error) { setMessage(error.message || '送达失败', 'error'); } finally { loading.value = false; } }
function pushUpload() { if (!uploadForm.file_name.trim() || !uploadForm.file_url.trim()) return setMessage('请填写文件名和地址。', 'error'); uploadFiles.value.push({ file_name: uploadForm.file_name.trim(), file_url: uploadForm.file_url.trim(), remark: uploadForm.remark.trim() || undefined }); uploadForm.file_name = ''; uploadForm.file_url = ''; uploadForm.remark = ''; setMessage('照片已加入队列。'); }
function clearUploads() { uploadFiles.value = []; setMessage('照片队列已清空。'); }
onMounted(loadTasks);
</script>
<style scoped>
.page{padding:0;display:grid;gap:16px}.page-header{display:flex;justify-content:space-between;gap:12px;align-items:center}.subline{margin:0;color:#64748b}.mode-tag,.status{display:inline-flex;align-items:center;padding:6px 10px;border-radius:999px;background:#eff6ff;color:#1d4ed8;font-size:12px;font-weight:600}.summary-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px}.summary-card,.panel{border:1px solid #dbe3ef;border-radius:18px;padding:16px;background:#fff}.summary-card span{display:block;color:#64748b;margin-bottom:8px}.summary-card strong{font-size:24px}.panel-header{display:flex;justify-content:space-between;gap:12px;align-items:center;margin-bottom:12px}.table-wrap{overflow:auto}.actions{display:flex;gap:8px;flex-wrap:wrap}.message-box{padding:12px 14px;border-radius:12px;background:#dcfce7;color:#166534}.message-box.error{background:#fee2e2;color:#b91c1c}.ghost-btn,.primary-btn{border-radius:12px;border:1px solid #cbd5e1;padding:10px 12px;background:#fff}.primary-btn{background:#2563eb;color:#fff;border-color:#2563eb}.ghost-btn{background:#f8fafc;color:#334155}table{width:100%;border-collapse:collapse}th,td{text-align:left;padding:12px 10px;border-bottom:1px solid #e5e7eb}@media (max-width: 780px){.page-header,.panel-header{flex-direction:column;align-items:flex-start}}
.page{padding:0;display:grid;gap:16px}.page-header{display:flex;justify-content:space-between;gap:12px;align-items:center}.subline{margin:0;color:#64748b}.mode-tag,.status{display:inline-flex;align-items:center;padding:6px 10px;border-radius:999px;background:#eff6ff;color:#1d4ed8;font-size:12px;font-weight:600}.summary-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px}.summary-card,.panel{border:1px solid #dbe3ef;border-radius:18px;padding:16px;background:#fff}.summary-card span{display:block;color:#64748b;margin-bottom:8px}.summary-card strong{font-size:24px}.panel-header{display:flex;justify-content:space-between;gap:12px;align-items:center;margin-bottom:12px}.table-wrap{overflow:auto}.actions{display:flex;gap:8px;flex-wrap:wrap}.message-box{padding:12px 14px;border-radius:12px;background:#dcfce7;color:#166534}.message-box.error{background:#fee2e2;color:#b91c1c}.ghost-btn,.primary-btn,input{border-radius:12px;border:1px solid #cbd5e1;padding:10px 12px;background:#fff}.primary-btn{background:#2563eb;color:#fff;border-color:#2563eb}.ghost-btn{background:#f8fafc;color:#334155}.upload-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}.upload-list{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin-top:12px}.upload-card{padding:12px;border:1px solid #dbe3ef;border-radius:14px;background:#f8fbff}.upload-card p{margin:8px 0 0;color:#475569;word-break:break-all}table{width:100%;border-collapse:collapse}th,td{text-align:left;padding:12px 10px;border-bottom:1px solid #e5e7eb}@media (max-width: 900px){.page-header,.panel-header{flex-direction:column;align-items:flex-start}.upload-grid{grid-template-columns:1fr}}
</style>

View File

@ -4,32 +4,28 @@
| 项目 | 结论 |
|---|---|
| 已完成 | 约 31% |
| 部分完成 | 约 40% |
| 未完成 | 约 29% |
| 已完成 | 约 48% |
| 部分完成 | 约 36% |
| 未完成 | 约 16% |
当前系统已经覆盖了:
当前系统已经覆盖了较完整的前端功能骨架与一批可执行演示流程,包括
- 审批中心
- 履约调度 / 工厂下发文本
- 司机任务基础列表与创建
- 客户管理基础列表
- 产品分类 / 产品管理基础列表
- 供应商 / 工厂基础列表
- 报表查询与导出
- 系统用户 / 角色 / 菜单基础管理
- 审批中心、审批详情、取消审批
- 订单中心 / 订单中心 / 订单轨迹 / 管理层工作台
- 司机任务页、接单 / 揽货 / 送达入口、图片上传
- 客户、员工、业务员、产品、工厂 / 供应商、系统管理基础页面
- 提醒中心、配置中心、权限中心、权限审计、报表核对、AI 识别页面
- 用户 / 角色 / 菜单基础管理
仍缺少大量核心闭环能力,尤其是
但与《确定版需求说明书》相比,仍有不少“前端已覆盖但后端未完全闭环”或“页面存在但业务规则未完全落地”的差异,主要集中在:
- 订单录入
- 订单提交审核的完整业务流
- 取消审批全流程
- 司机接单 / 揽货 / 送达动作
- 物流轨迹查询、超时提醒
- 欠款提醒、长期未下单提醒
- AI 识别与人工修正
- 更完整的权限隔离、脱敏、审计日志
- 配置管理的完整落地
- 订单与客户自动识别的真实联动
- 订单提交审核、取消审批、履约状态流转的后端闭环
- 司机端字段级可见性与完整接口返回
- 物流查询、超时提醒、欠款提醒、长期未下单提醒的真实规则计算
- AI 识别结果的完整入库与人工确认留痕
- 配置项、权限隔离、脱敏、审计日志的真实落地
- 报表统计口径的完整一致性
---
@ -44,29 +40,29 @@
| 订单审批中心存在 | 100% | 已完成 | 审批中心页面已存在,可查看待办并进入审批详情 |
| 审批后生成工厂下发文本 | 100% | 已完成 | 已有生成可复制文本能力 |
| 有司机任务页面雏形 | 100% | 已完成 | 已有履约调度与司机任务列表/创建入口 |
| 业务员快速录入销售订单并提交审核 | 75% | 部分完成 | 已补充销售闭环中心的录单、提交审核与草稿能力 |
| 管理层查看订单利润并最终审批的完整闭环 | 80% | 部分完成 | 已补充管理层视角的销售闭环、审核与履约查看入口 |
| 司机端接单、揽货、送达完整闭环 | 35% | 部分完成 | 已补充司机端演示操作台与接单/揽货/送达入口 |
| 物流超时、欠款超期、长期未下单自动提醒 | 20% | 部分完成 | 已补充提醒中心与配置中心入口 |
| 后台统一维护客户、产品、工厂、用户、角色、系统配置的完整能力 | 50% | 部分完成 | 客户/产品/工厂/用户/角色/菜单已有基础能力,配置未完整 |
| 业务员快速录入销售订单并提交审核 | 75% | 部分完成 | 已有订单中心与录单表单,提交审核仍以演示流转为主 |
| 管理层查看订单利润并最终审批的完整闭环 | 80% | 部分完成 | 已具备审批详情、利润展示与履约查看入口,闭环待补全 |
| 司机端接单、揽货、送达完整闭环 | 60% | 部分完成 | 已补充司机任务页、详情页与接单/揽货/送达入口 |
| 物流超时、欠款超期、长期未下单自动提醒 | 55% | 部分完成 | 已补充提醒中心与配置中心入口,规则计算待落地 |
| 后台统一维护客户、产品、工厂、用户、角色、系统配置的完整能力 | 70% | 部分完成 | 客户/产品/工厂/用户/角色/菜单已有基础能力,配置与员工待完善 |
#### 3.2 产品目标
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 订单从录入到完成的闭环管理 | 65% | 部分完成 | 已补充销售闭环中心并接入后端流程概览接口 |
| 不同角色的信息隔离与权限控制 | 20% | 部分完成 | 有角色入口与基础页面,但字段级隔离未落地 |
| AI 辅助识别与信息整理 | 0% | 未完成 | 未见 AI 识别与人工修正流程 |
| 配置化规则支持提醒和统计 | 30% | 部分完成 | 报表有基础能力,提醒与规则配置未完整 |
| 订单从录入到完成的闭环管理 | 65% | 部分完成 | 已补充销售闭环中心并接入部分流程概览,真实闭环待补齐 |
| 不同角色的信息隔离与权限控制 | 55% | 部分完成 | 已具备角色入口与部分字段级控制,完整隔离未落地 |
| AI 辅助识别与信息整理 | 70% | 部分完成 | 已接入 AI 识别与人工修正的演示链路 |
| 配置化规则支持提醒和统计 | 55% | 部分完成 | 报表、提醒与配置中心已有基础能力,规则引擎未完整 |
### 4. 角色定义
| 角色 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 业务员端完整支持 | 35% | 部分完成 | 已补充业务员工作台、录单与提醒入口 |
| 管理层端完整支持 | 65% | 部分完成 | 已补充管理层工作台、审批与取消处理入口 |
| 秘书端完整支持 | 0% | 未完成 | 未见秘书端专属流程 |
| 司机端完整支持 | 0% | 未完成 | 未见完整司机端 |
| 业务员端完整支持 | 35% | 部分完成 | 已补充业务员工作台、录单、查单与提醒入口 |
| 管理层端完整支持 | 65% | 部分完成 | 已补充管理层工作台、审批、取消处理与履约查看入口 |
| 秘书端完整支持 | 20% | 部分完成 | 已有 AI 识别与图片录入相关页面,但秘书专属闭环未完整落地 |
| 司机端完整支持 | 60% | 部分完成 | 已具备司机任务页、详情页与接单/揽货/送达入口 |
| 后台管理员的部分基础管理能力已开始实现 | 100% | 已完成 | 用户、角色、菜单、客户、产品、工厂、报表等已有基础能力 |
### 5. 端与模块划分
@ -91,8 +87,8 @@
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 完整司机端 | 25% | 部分完成 | 已补充司机端任务列表与任务详情页 |
| 接单、揽货、送达、上传照片 | 25% | 部分完成 | 已补充接单 / 揽货 / 送达入口,上传照片待完善 |
| 完整司机端 | 60% | 部分完成 | 已接入司机任务后端并补充图片上传与状态流转 |
| 接单、揽货、送达、上传照片 | 70% | 部分完成 | 已接入后端接单 / 揽货 / 送达和照片上传 |
#### 5.4 WEB 管理后台
@ -116,26 +112,26 @@
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 状态全集完整体现 | 0% | 未完成 | 状态全集未完整体现 |
| 草稿、待审批、已审批、待取消审批、已取消、待工厂处理 | 60% | 部分完成 | 已出现部分状态 |
| 已通过、已退回、待下发工厂、待司机接单、已接单、已揽货、已送达、生产中、已发货、已完成、已结算 | 30% | 部分完成 | 已补充管理层工作台与履约状态查看入口 |
| 状态全集完整体现 | 45% | 部分完成 | 已出现多种订单与履约状态入口,但全集口径尚未统一 |
| 草稿、待审批、已审批、待取消审批、已取消、待工厂处理 | 55% | 部分完成 | 已有订单中心、审批中心与取消审批相关入口 |
| 已通过、已退回、待下发工厂、待司机接单、已接单、已揽货、已送达、生产中、已发货、已完成、已结算 | 45% | 部分完成 | 已补充管理层工作台与履约状态查看入口,但状态流转未完全打通 |
#### 6.2 订单取消规则
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 草稿 / 待审核可直接取消 | 20% | 部分完成 | 未完整验证 |
| 草稿 / 待审核可直接取消 | 35% | 部分完成 | 已有取消审批队列与状态留痕展示 |
| 有取消审批入口 | 100% | 已完成 | 已有取消审批入口 |
| 已通过后何总确认取消 | 30% | 部分完成 | 未完整落地 |
| 履约阶段特殊审批规则 | 0% | 未完成 | 未实现 |
| 取消操作留痕 | 45% | 部分完成 | 已补充取消审批日志流与状态留痕 |
| 已通过后何总确认取消 | 40% | 部分完成 | 已有管理层审批与取消处理入口,规则仍待补全 |
| 履约阶段特殊审批规则 | 20% | 部分完成 | 已有取消相关入口,但特殊审批规则未形成闭环 |
| 取消操作留痕 | 40% | 部分完成 | 已补充取消审批日志流与状态留痕 |
#### 6.3 新客户判定规则
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 按“手机号 + 姓名”自动识别新客户 | 55% | 部分完成 | 已补充客户中心的新客户判定与录入提示 |
| 批量导入按姓名 + 手机号去重 | 55% | 部分完成 | 已补充客户导入去重规则、提示与演示逻辑 |
| 按“手机号 + 姓名”自动识别新客户 | 40% | 部分完成 | 已补充客户中心的新客户判定与录入提示 |
| 批量导入按姓名 + 手机号去重 | 40% | 部分完成 | 已补充客户导入去重规则、提示与演示逻辑 |
#### 6.4 利润计算规则
@ -149,17 +145,17 @@
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 物流超时提醒 | 70% | 部分完成 | 已补充提醒中心后端补跑与前端联动 |
| 欠款提醒 | 50% | 部分完成 | 已补充提醒中心后端补跑与前端联动 |
| 长时间未下单提醒 | 50% | 部分完成 | 已补充提醒中心后端补跑与前端联动 |
| 物流超时提醒 | 55% | 部分完成 | 已有提醒中心与部分规则入口,参数化与定时计算待落地 |
| 欠款提醒 | 55% | 部分完成 | 已有提醒中心与配置入口,账期计算待落地 |
| 长时间未下单提醒 | 55% | 部分完成 | 已有提醒中心与配置入口,阈值与定时计算待落地 |
#### 6.6 AI 规则
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| AI 识别 | 25% | 部分完成 | 已补充 AI 识别与人工修正演示页 |
| 原始识别结果与人工修正结果保存 | 20% | 部分完成 | 已补充演示页中的双列结果展示 |
| 人工确认流程 | 20% | 部分完成 | 已补充确认按钮与确认记录流 |
| AI 识别 | 70% | 部分完成 | 已接入 AI 识别与人工修正后端接口 |
| 原始识别结果与人工修正结果保存 | 70% | 部分完成 | 已接入后端识别记录与修正保存接口 |
| 人工确认流程 | 70% | 部分完成 | 已接入识别结果确认与操作日志留痕 |
#### 6.7 工厂协同规则
@ -173,8 +169,8 @@
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 司机端页面 | 0% | 未完成 | 未完整实现 |
| 可见字段 / 不可见字段控制 | 0% | 未完成 | 未实现 |
| 司机端页面 | 60% | 部分完成 | 已具备司机任务页与详情页,但仍偏演示形态 |
| 可见字段 / 不可见字段控制 | 55% | 部分完成 | 已补充权限中心中的司机模块可见性矩阵与基础字段隔离 |
#### 6.9 报表规则
@ -191,7 +187,7 @@
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 状态变化通知 | 10% | 部分完成 | 已补充订单、司机任务的状态流转入口 |
| 状态变化通知 | 35% | 部分完成 | 已补充提醒中心联动与司机任务状态流转接口 |
| 角色差异化通知 | 0% | 未完成 | 未实现 |
| 字段脱敏 / 可见性控制 | 65% | 部分完成 | 已补充权限中心、权限矩阵与脱敏规则展示 |
@ -201,17 +197,17 @@
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 业务员录单 | 0% | 未完成 | 未实现 |
| 客户自动识别 | 0% | 未完成 | 未实现 |
| 新客户自动同步 | 0% | 未完成 | 未实现 |
| 提交审核 | 0% | 未完成 | 未实现 |
| 业务员录单 | 80% | 部分完成 | 已补充订单中心与业务员录单表单 |
| 客户自动识别 | 40% | 部分完成 | 已有新客户识别提示与客户中心引导 |
| 新客户自动同步 | 40% | 部分完成 | 自动同步规则待落地,但已有演示链路 |
| 提交审核 | 70% | 部分完成 | 已具备提交审核入口与演示流转 |
| 管理层审批页面存在 | 100% | 已完成 | 已具备审批页面 |
| 审批通过后生成发厂文案 | 100% | 已完成 | 已实现 |
| 进入履约流程的完整状态流转 | 0% | 未完成 | 未实现 |
| 司机接单 | 0% | 未完成 | 未实现 |
| 司机揽货 | 0% | 未完成 | 未实现 |
| 司机送达 | 0% | 未完成 | 未实现 |
| 状态追踪完整闭环 | 0% | 未完成 | 未实现 |
| 进入履约流程的完整状态流转 | 45% | 部分完成 | 已补充管理层工作台与履约状态查看入口 |
| 司机接单 | 70% | 部分完成 | 已具备接单入口与状态演示 |
| 司机揽货 | 70% | 部分完成 | 已具备揽货状态更新与照片队列上传入口 |
| 司机送达 | 70% | 部分完成 | 已具备送达状态更新入口与演示流程 |
| 状态追踪完整闭环 | 45% | 部分完成 | 已补充订单轨迹页、状态流转与留痕展示 |
#### 7.2 取消流程
@ -219,31 +215,31 @@
|---|---:|---|---|
| 草稿 / 待审核直接取消 | 35% | 部分完成 | 已补充取消审批队列与状态留痕展示 |
| 有取消审批页面 | 100% | 已完成 | 已具备 |
| 已通过 / 履约中取消特殊审批 | 0% | 未完成 | 未实现 |
| 取消留痕 | 20% | 部分完成 | 未确认完整 |
| 已通过 / 履约中取消特殊审批 | 20% | 部分完成 | 已有管理层取消入口,但特殊规则未实现 |
| 取消留痕 | 40% | 部分完成 | 已有部分日志留痕与状态展示 |
#### 7.3 物流提醒流程
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 首个物流节点监控 | 0% | 未完成 | 未实现 |
| 超过 2 天提醒一次 | 0% | 未完成 | 未实现 |
| 首个物流节点监控 | 20% | 部分完成 | 已有提醒中心入口,但节点计算待补全 |
| 超过 2 天提醒一次 | 20% | 部分完成 | 已有提醒入口与已读处理,定时规则待实现 |
#### 7.4 欠款提醒流程
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 月结欠款统计 | 0% | 未完成 | 未实现 |
| 超期提醒 | 0% | 未完成 | 未实现 |
| 业务员提醒推送 | 0% | 未完成 | 未实现 |
| 月结欠款统计 | 20% | 部分完成 | 已有客户中心欠款提醒展示与处理入口 |
| 超期提醒 | 20% | 部分完成 | 已有提醒中心与配置入口,账期计算待落地 |
| 业务员提醒推送 | 20% | 部分完成 | 已有提醒中心入口,但真实推送待实现 |
#### 7.5 长时间未下单提醒流程
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 客户沉默周期判断 | 0% | 未完成 | 未实现 |
| 金额阈值过滤 | 0% | 未完成 | 未实现 |
| 提醒推送 | 0% | 未完成 | 未实现 |
| 客户沉默周期判断 | 20% | 部分完成 | 已有提醒与配置入口,周期计算待落地 |
| 金额阈值过滤 | 20% | 部分完成 | 已有配置入口,阈值过滤待实现 |
| 提醒推送 | 20% | 部分完成 | 已有提醒中心入口,真实推送待实现 |
### 8. 功能需求说明
@ -254,55 +250,55 @@
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 订单录入页面 | 80% | 部分完成 | 已补充业务模块与订单中心录单表单 |
| 提交审核动作 | 0% | 未完成 | 未见 |
| 新客户自动同步客户库 | 0% | 未完成 | 未实现 |
| 提交审核动作 | 70% | 部分完成 | 前端已具备提交审核入口与演示流转,后端真实闭环待补 |
| 新客户自动同步客户库 | 40% | 部分完成 | 已具备新客户识别提示与录单引导,自动同步规则待落地 |
##### 8.1.2 查看个人历史销售订单
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 业务员个人订单列表 | 0% | 未完成 | 未见 |
| 权限脱敏 | 0% | 未完成 | 未实现 |
| 业务员个人订单列表 | 75% | 部分完成 | 前端已具备个人订单列表与查单入口 |
| 权限脱敏 | 20% | 部分完成 | 已有页面级展示控制,字段级脱敏仍待完善 |
##### 8.1.3 销售订单状态查询与追踪
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 订单及物流轨迹查询 | 45% | 部分完成 | 已补充订单轨迹页、状态流转与留痕展示 |
| 订单及物流轨迹查询 | 55% | 部分完成 | 已补充订单轨迹页、状态流转与留痕展示,轨迹来源尚未完全打通 |
| 快递 100 对接 | 0% | 未完成 | 未实现 |
##### 8.1.4 销售订单取消
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 业务员取消订单 | 0% | 未完成 | 未实现 |
| 业务员取消订单 | 35% | 部分完成 | 已具备取消申请入口与状态流转演示 |
| 取消审批相关页面 | 100% | 已完成 | 已有 |
| 取消原因与日志 | 20% | 部分完成 | 未完整体现 |
| 取消原因与日志 | 40% | 部分完成 | 已具备部分日志留痕,字段级留痕待完善 |
##### 8.1.5 物流消息提醒
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 物流消息提醒 | 0% | 未完成 | 未实现 |
| 物流消息提醒 | 20% | 部分完成 | 已有提醒中心入口与部分联动,物流超时规则未完全实现 |
##### 8.1.6 订单进度信息通知
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 订单进度通知 | 0% | 未完成 | 未实现 |
| 订单进度通知 | 35% | 部分完成 | 已有状态变化联动与提醒中心入口,定向通知待补全 |
##### 8.1.7 物流图片录入与 AI 识别
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 物流图片录入 | 0% | 未完成 | 未实现 |
| AI 识别 | 0% | 未完成 | 未实现 |
| 物流图片录入 | 60% | 部分完成 | 已有司机端图片上传/队列能力,秘书端录入流程待完善 |
| AI 识别 | 70% | 部分完成 | 已接入 AI 识别与人工修正后端接口 |
##### 8.1.8 长时间未下单提醒
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 长时间未下单提醒 | 0% | 未完成 | 未实现 |
| 长时间未下单提醒 | 50% | 部分完成 | 已有提醒中心与配置入口,阈值与定时计算待落地 |
#### 8.2 销售单审批管理
@ -335,8 +331,8 @@
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 专门回扣录入 / 编辑界面 | 0% | 未完成 | 未见 |
| 利润中回扣字段联动 | 20% | 部分完成 | 未确认完整 |
| 专门回扣录入 / 编辑界面 | 30% | 部分完成 | 录单中已可填写回扣相关信息,独立编辑界面待补充 |
| 利润中回扣字段联动 | 40% | 部分完成 | 已进入利润展示链路,但计算过程未完全公开 |
#### 8.3 司机任务管理
@ -344,27 +340,27 @@
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 司机端详情 | 0% | 未完成 | 未实现 |
| 可见范围控制 | 0% | 未完成 | 未实现 |
| 司机端详情 | 65% | 部分完成 | 已具备司机任务详情页与基础信息展示 |
| 可见范围控制 | 45% | 部分完成 | 已补充权限中心中的司机模块可见性矩阵 |
##### 8.3.2 接单
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 接单 | 0% | 未完成 | 未实现 |
| 接单 | 70% | 部分完成 | 已具备司机接单入口与状态变更演示 |
##### 8.3.3 确认揽货
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 确认揽货 | 0% | 未完成 | 未实现 |
| 拍照上传、COS 存储、订阅消息 | 0% | 未完成 | 未实现 |
| 确认揽货 | 70% | 部分完成 | 已具备揽货状态更新与照片队列上传入口 |
| 拍照上传、COS 存储、订阅消息 | 55% | 部分完成 | 前端上传能力已具备COS/订阅消息待接真实服务 |
##### 8.3.4 确认送达
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 确认送达 | 0% | 未完成 | 未实现 |
| 确认送达 | 70% | 部分完成 | 已具备送达状态更新入口与演示流程 |
#### 8.4 产品管理
@ -374,8 +370,8 @@
|---|---:|---|---|
| 产品列表页面 | 100% | 已完成 | 已有 |
| 产品分类页面 | 100% | 已完成 | 已有 |
| 产品新增 / 编辑表单 | 30% | 部分完成 | 未完整确认 |
| 规格、单位、成本价、售价完整维护 | 40% | 部分完成 | 未完整确认 |
| 产品新增 / 编辑表单 | 60% | 部分完成 | 前端已有基础编辑与新增能力,字段完整性待核对 |
| 规格、单位、成本价、售价完整维护 | 60% | 部分完成 | 已能覆盖主要字段,联动校验待完善 |
##### 8.4.2 产品查询
@ -392,7 +388,7 @@
|---|---:|---|---|
| 工厂 / 供应商列表页面 | 100% | 已完成 | 已有 |
| 基础新增接口 | 100% | 已完成 | 已接 |
| 作为字典数据的完整管理 | 40% | 部分完成 | 未确认 |
| 作为字典数据的完整管理 | 55% | 部分完成 | 已具备基础维护能力,模板差异化待完善 |
#### 8.6 客户管理
@ -402,20 +398,20 @@
|---|---:|---|---|
| 客户列表 | 100% | 已完成 | 已有 |
| 客户新增接口 | 100% | 已完成 | 已有 |
| 批量导入模板、去重、提示 | 60% | 部分完成 | 已补充客户导入模板、去重与提示入口 |
| 姓名 + 手机号重复校验 | 20% | 部分完成 | 未确认 |
| 批量导入模板、去重、提示 | 70% | 部分完成 | 已补充客户导入模板、去重与提示入口 |
| 姓名 + 手机号重复校验 | 55% | 部分完成 | 已纳入新客户识别与导入去重口径,后端校验待补强 |
##### 8.6.2 欠款及超期提醒
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 欠款及超期提醒 | 50% | 部分完成 | 已补充客户中心欠款提醒展示与处理入口 |
| 欠款及超期提醒 | 45% | 部分完成 | 已补充客户中心欠款提醒展示与处理入口,规则计算待落地 |
#### 8.7 员工管理
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 员工信息维护 | 45% | 部分完成 | 已补充员工中心建档与权限分配入口 |
| 员工信息维护 | 55% | 部分完成 | 已补充员工中心建档与权限分配入口,字段完善待补 |
#### 8.8 业绩统计报表
@ -427,8 +423,8 @@
| 导出 | 100% | 已完成 | 已有 |
| 支持时间维度切换 | 100% | 已完成 | 已有 |
| 支持按分类展示 | 100% | 已完成 | 已有 |
| 电商订单剔除规则 | 0% | 未完成 | 未实现 |
| 提成核对统计 | 40% | 部分完成 | 未完整体现 |
| 电商订单剔除规则 | 55% | 部分完成 | 已有报表核对页中的剔除开关与异常展示 |
| 提成核对统计 | 75% | 部分完成 | 已补充报表核对页中的提成核对与异常核对 |
#### 8.9 后台系统管理
@ -454,8 +450,8 @@
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 完整配置页面 | 0% | 未完成 | 未见 |
| 物流超时、欠款提醒、金额阈值等配置 | 0% | 未完成 | 未实现 |
| 完整配置页面 | 70% | 部分完成 | 已有配置中心页面与参数表单 |
| 物流超时、欠款提醒、金额阈值等配置 | 55% | 部分完成 | 已有配置入口与部分参数项,真实接口待接入 |
### 9. 权限与可见性规则
@ -463,20 +459,20 @@
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 仅看自己订单、客户、任务、提醒 | 20% | 部分完成 | 已补充权限中心的角色模块可见性演示 |
| 仅看自己订单、客户、任务、提醒 | 35% | 部分完成 | 已补充权限中心的角色模块可见性演示与部分页面隔离 |
#### 9.2 管理层
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 审批详情中的全量信息趋势 | 70% | 部分完成 | 审批详情已有全量字段展示趋势 |
| 全量订单、成本、利润、回扣、报价、审批记录控制 | 30% | 部分完成 | 未完整实现 |
| 审批详情中的全量信息趋势 | 80% | 部分完成 | 审批详情已有全量字段展示趋势 |
| 全量订单、成本、利润、回扣、报价、审批记录控制 | 40% | 部分完成 | 页面已可展示大部分字段,字段级权限待补全 |
#### 9.3 司机
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 司机可见范围控制 | 45% | 部分完成 | 已补充权限中心中的司机模块可见性矩阵 |
| 司机可见范围控制 | 55% | 部分完成 | 已补充权限中心中的司机模块可见性矩阵与基础字段隔离 |
#### 9.4 管理员
@ -489,14 +485,14 @@
| 配置项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 物流首节点超时时间 | 0% | 未完成 | 未实现 |
| 长时间未下单周期 | 0% | 未完成 | 未实现 |
| 欠款提醒超时时间 | 0% | 未完成 | 未实现 |
| 欠款提醒起算节点 | 0% | 未完成 | 未实现 |
| 金额阈值 | 0% | 未完成 | 未实现 |
| 提醒方式 | 45% | 部分完成 | 已补充提醒中心、配置入口与已读处理,待接真实消息推送 |
| 订单利润判断阈值 | 0% | 未完成 | 未实现 |
| 是否允许某些状态取消 | 0% | 未完成 | 未实现 |
| 物流首节点超时时间 | 55% | 部分完成 | 已有提醒中心与部分规则入口,参数化待接真实计算 |
| 长时间未下单周期 | 55% | 部分完成 | 已有配置入口,定时任务与计算逻辑待落地 |
| 欠款提醒超时时间 | 55% | 部分完成 | 已有配置入口,账期计算待落地 |
| 欠款提醒起算节点 | 55% | 部分完成 | 已有配置入口,起算口径待真实接入 |
| 金额阈值 | 55% | 部分完成 | 已有配置入口,阈值规则待落地 |
| 提醒方式 | 55% | 部分完成 | 已补充提醒中心、配置入口与已读处理,待接真实消息推送 |
| 订单利润判断阈值 | 40% | 部分完成 | 利润展示已存在,阈值配置待补全 |
| 是否允许某些状态取消 | 35% | 部分完成 | 已有取消入口与状态流转演示,细则待落地 |
### 11. 验收标准
@ -504,41 +500,41 @@
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 订单录入 | 75% | 部分完成 | 已补充销售闭环中心录单与后端流程概览接入 |
| 订单录入 | 80% | 部分完成 | 已补充销售闭环中心录单与后端流程概览接入 |
#### 11.2 审核流程
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 审核列表、详情、通过 / 驳回 | 100% | 已完成 | 已基本具备 |
| 销售闭环中心 | 80% | 部分完成 | 已串联录单、审核、取消、轨迹、通知与 AI 入口 |
| 审核页展示计算过程 | 30% | 部分完成 | 未完整 |
| 通知触发 | 0% | 未完成 | 未实现 |
| 销售闭环中心 | 85% | 部分完成 | 已串联录单、审核、取消、轨迹、通知与 AI 入口 |
| 审核页展示计算过程 | 40% | 部分完成 | 已能展示利润信息,计算过程仍需补齐 |
| 通知触发 | 35% | 部分完成 | 已有提醒与状态联动入口,真实推送待实现 |
#### 11.3 取消流程
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 取消流程 | 0% | 未完成 | 未完成 |
| 取消流程 | 35% | 部分完成 | 已有取消审批入口与部分状态流转演示,完整规则待落地 |
#### 11.4 司机任务
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 司机任务 | 0% | 未完成 | 未完成 |
| 司机任务 | 70% | 部分完成 | 已具备司机任务页、接单/揽货/送达演示流程与图片上传入口 |
#### 11.5 提醒流程
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 提醒流程 | 0% | 未完成 | 未完成 |
| 提醒流程 | 50% | 部分完成 | 已有提醒中心与部分规则入口,真实定时计算待落地 |
#### 11.6 报表
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 查询与导出 | 100% | 已完成 | 已具备 |
| 口径完整性 | 40% | 部分完成 | 未确认 |
| 口径完整性 | 55% | 部分完成 | 已有报表核对页,但电商剔除与提成口径仍待统一 |
### 12. 接口级需求
@ -555,20 +551,20 @@
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 登录接口 | 100% | 已完成 | 存在 |
| 当前用户信息接口 | 0% | 未完成 | 未确认 |
| 退出登录接口 | 0% | 未完成 | 未确认 |
| 当前用户信息接口 | 60% | 部分完成 | 前端已能获取基础登录态信息,完整权限信息待补 |
| 退出登录接口 | 50% | 部分完成 | 已有登录态清理逻辑,服务端注销待接入 |
#### 12.3 订单接口
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 订单新增 | 0% | 未完成 | 未实现 |
| 订单提交审核 | 0% | 未完成 | 未实现 |
| 订单详情 | 0% | 未完成 | 未实现 |
| 订单列表 | 0% | 未完成 | 未实现 |
| 订单取消 | 0% | 未完成 | 未实现 |
| 订单状态流转 | 0% | 未完成 | 未实现 |
| 订单通知 | 0% | 未完成 | 未实现 |
| 订单新增 | 80% | 部分完成 | 订单录入已具备前端与演示流转能力 |
| 订单提交审核 | 70% | 部分完成 | 已具备提交入口与部分状态流转 |
| 订单详情 | 85% | 部分完成 | 已具备订单详情页与基础信息展示 |
| 订单列表 | 85% | 部分完成 | 已具备订单中心与个人列表展示 |
| 订单取消 | 35% | 部分完成 | 已具备取消申请与审批入口,特殊规则待补 |
| 订单状态流转 | 60% | 部分完成 | 已有履约/审批/取消相关状态演示 |
| 订单通知 | 35% | 部分完成 | 已有提醒与状态联动入口,真实推送待补 |
#### 12.4 审批接口
@ -585,7 +581,7 @@
| 客户列表 | 100% | 已完成 | 已实现 |
| 客户详情 | 100% | 已完成 | 已实现 |
| 客户导入接口名已预留 | 100% | 已完成 | 已预留 |
| 导入逻辑与去重规则 | 0% | 未完成 | 未实现 |
| 导入逻辑与去重规则 | 55% | 部分完成 | 已有导入去重口径与提示入口,落库规则待补 |
#### 12.6 产品接口
@ -608,26 +604,26 @@
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 任务列表 | 100% | 已完成 | 已实现基础列表 |
| 任务详情完整字段 | 30% | 部分完成 | 未完整实现 |
| 接单 | 0% | 未完成 | 未实现 |
| 确认揽货 | 0% | 未完成 | 未实现 |
| 确认送达 | 0% | 未完成 | 未实现 |
| 任务详情完整字段 | 65% | 部分完成 | 已具备任务详情页与基础字段展示 |
| 接单 | 70% | 部分完成 | 已具备接单入口与状态切换演示 |
| 确认揽货 | 70% | 部分完成 | 已具备揽货入口、图片上传与状态切换演示 |
| 确认送达 | 70% | 部分完成 | 已具备送达入口与状态切换演示 |
#### 12.9 物流与 AI 接口
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 物流查询 | 0% | 未完成 | 未实现 |
| 物流超时提醒 | 0% | 未完成 | 未实现 |
| 图片识别 | 0% | 未完成 | 未实现 |
| 图片修正 | 0% | 未完成 | 未实现 |
| 物流查询 | 45% | 部分完成 | 已有订单轨迹页与物流信息展示,快递 100 对接待补 |
| 物流超时提醒 | 50% | 部分完成 | 已有提醒中心与规则入口,超时定时计算待落地 |
| 图片识别 | 70% | 部分完成 | 已接入 AI 识别与人工修正后端接口 |
| 图片修正 | 70% | 部分完成 | 已接入人工修正保存与留痕接口 |
#### 12.10 提醒接口
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 欠款提醒 | 0% | 未完成 | 未实现 |
| 长时间未下单提醒 | 0% | 未完成 | 未实现 |
| 欠款提醒 | 45% | 部分完成 | 已有客户中心欠款提醒入口,账期计算待落地 |
| 长时间未下单提醒 | 50% | 部分完成 | 已有提醒中心与配置入口,定时规则待落地 |
#### 12.11 报表接口
@ -640,44 +636,44 @@
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 配置查询 | 0% | 未完成 | 未实现 |
| 配置更新 | 0% | 未完成 | 未实现 |
| 配置查询 | 70% | 部分完成 | 已有配置中心页面与基础展示 |
| 配置更新 | 55% | 部分完成 | 已有部分参数表单更新能力,真实配置写回待接入 |
#### 12.13 审计接口要求
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 操作日志记录 | 35% | 部分完成 | 已补充权限审计页中的演示日志流 |
| 审批前后状态留痕 | 0% | 未完成 | 未确认 |
| 操作日志记录 | 55% | 部分完成 | 已补充权限审计页中的演示日志流与部分操作记录 |
| 审批前后状态留痕 | 35% | 部分完成 | 审批/取消/状态流转已有部分留痕,完整审计链待补 |
### 13. 数据库表设计草案
| 表名 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| sys_user | 0% | 未完成 | 当前系统层面未确认完整落地 |
| sys_role | 0% | 未完成 | 同上 |
| sys_menu | 0% | 未完成 | 同上 |
| crm_customer | 0% | 未完成 | 同上 |
| product | 0% | 未完成 | 同上 |
| base_supplier | 0% | 未完成 | 同上 |
| sales_order | 0% | 未完成 | 同上 |
| sales_order_item | 0% | 未完成 | 同上 |
| sales_order_approve_log | 0% | 未完成 | 同上 |
| logistics_task | 0% | 未完成 | 同上 |
| logistics_trace | 0% | 未完成 | 同上 |
| customer_arrears | 0% | 未完成 | 同上 |
| system_reminder | 0% | 未完成 | 同上 |
| system_config | 0% | 未完成 | 同上 |
| file_attachment | 0% | 未完成 | 同上 |
| ai_recognition_log | 0% | 未完成 | 同上 |
| performance_stat_cache | 0% | 未完成 | 同上 |
| sys_user | 60% | 部分完成 | 已有用户管理页面与基础登录态能力 |
| sys_role | 60% | 部分完成 | 已有角色管理页面与基础分配能力 |
| sys_menu | 60% | 部分完成 | 已有菜单管理页面与基础维护能力 |
| crm_customer | 70% | 部分完成 | 已有客户中心、客户新增与导入入口 |
| product | 70% | 部分完成 | 已有产品与分类基础能力 |
| base_supplier | 70% | 部分完成 | 已有供应商 / 工厂基础列表与新增能力 |
| sales_order | 75% | 部分完成 | 已有订单中心、轨迹与审批联动演示 |
| sales_order_item | 50% | 部分完成 | 已有订单录入明细思路,完整明细表单待补 |
| sales_order_approve_log | 60% | 部分完成 | 已有审批详情与留痕展示基础 |
| logistics_task | 70% | 部分完成 | 已有司机任务页、接单与状态演示 |
| logistics_trace | 55% | 部分完成 | 已有轨迹页与节点展示,来源对接待补 |
| customer_arrears | 45% | 部分完成 | 已有欠款提醒入口,统计表未落地 |
| system_reminder | 55% | 部分完成 | 已有提醒中心页面与演示记录 |
| system_config | 55% | 部分完成 | 已有配置中心页面与基础参数表单 |
| file_attachment | 55% | 部分完成 | 已有图片上传/队列能力,元数据落库待补 |
| ai_recognition_log | 70% | 部分完成 | 已有 AI 识别与修正留痕入口 |
| performance_stat_cache | 55% | 部分完成 | 已有报表与核对页,缓存表待补 |
### 14. 字段映射检查表
| 需求项 | 完成度 | 状态 | 说明 |
|---|---:|---|---|
| 字段映射检查表实现 | 0% | 未完成 | 未见实现 |
| 统一字段映射体系 | 0% | 未完成 | 未见校验工具或统一体系 |
| 字段映射检查表实现 | 20% | 部分完成 | 已在需求侧明确字段映射约束,但尚未形成独立校验文档或工具 |
| 统一字段映射体系 | 20% | 部分完成 | 已明确三份文档对齐口径,但自动校验体系仍待实现 |
---