diff --git a/CLAUDE.md b/CLAUDE.md
index 6161d33..95de0af 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -56,11 +56,11 @@ qiji/
| `customers` | id, name, industry, address, in_use_services, monthly_fee, remarks, created_by |
| `customer_contacts` | id, customer_id, name, phone, role_desc |
| `customer_assignments` | id, customer_id, manager_id, role(primary/assistant), assigned_by |
-| `visits` | id, customer_id, visit_date, visit_method, time_range, visitor_name, visitor_phone, communication_content, customer_demand, companions(UUID[]), photos(TEXT[]), manager_id |
-| `daily_notes` | id, manager_id, note_date, category, content, time_range |
-| `work_plans` | id, customer_id, plan_content, plan_date, manager_id, status |
-| `mini_business` | id, customer_id, product_type, amount, follow_up_detail, status, manager_id |
-| `key_visits` | id, customer_id, urgency_level, description, progress_status, planned_date, planned_visitor, visit_target, manager_id |
+| `visits` | id, customer_id, visit_date, visit_method, time_range, visitor_name, visitor_phone, communication_content, customer_demand, companions(UUID[]), photos(TEXT[]), manager_id, edit_log(JSONB) |
+| `daily_notes` | id, manager_id, note_date, category, content, time_range, edit_log(JSONB) |
+| `work_plans` | id, customer_id, plan_content, plan_date, manager_id, status, edit_log(JSONB) |
+| `mini_business` | id, customer_id, product_type, amount, follow_up_detail, status, manager_id, expected_revenue_date, edit_log(JSONB) |
+| `key_visits` | id, customer_id, urgency_level, description, progress_status, planned_date, planned_visitor, visit_target, manager_id, edit_log(JSONB) |
## 当前进度
@@ -105,6 +105,13 @@ qiji/
- [x] **填报进度权限** — 经理只看到自己,支局长/领导看全员
- [x] **客户经理列** — 暖灰色hash标签,每人唯一颜色
- [x] **列表序号** — 客户管理+用户管理添加序号列
+- [x] **客户导入修复** — 修复 import_customers 500 错误(errors 变量未初始化)
+- [x] **图片预览增强** — 适应页面/缩放/拖拽平移 + 底部工具栏(PC端+移动端统一 ImagePreview 组件)
+- [x] **信息架构重组** — 周报精简为拜访+纪要两个Tab;工作计划/商机/要客独立为侧边栏「工作」分组下的独立页面;仪表盘四卡可点击跳转
+- [x] **侧边栏分组** — 侧边栏分为「汇总」「工作」「管理」三个分组,JetBrains Mono 标签
+- [x] **变更追踪 (edit_log)** — 5 张表新增 JSONB edit_log 列;POST 初始化创建记录;PUT 自动对比新旧值追加 diff;编辑弹窗底部展示变更时间轴(EditLogPanel.vue);表格被修改过的记录显示 🕐 时钟图标
+- [x] **导入模板增强** — 客户导入失败原因明细显示
+- [x] **操作精简** — 工作计划/商机/要客页面移除冗余"编辑"按钮(点击客户名已可编辑)
### 待完善
@@ -209,3 +216,9 @@ docker-compose up -d
11. **列表操作**: 点击客户名→详情弹窗含编辑/删除按钮,移除表格操作栏
12. **侧边栏**: ink 深色渐变 + SVG 内联图标 + 可折叠(64px) + 金色装饰线
13. **客户经理列**: hash 配色标签,每人唯一颜色(8 色暖灰调)
+14. **侧边栏分组**: 汇总(仪表盘/周报)、工作(工作计划/商机跟单/要客拜访)、管理(客户/用户/系统设置)三层分组,JetBrains Mono 标签
+15. **周报精简**: 只保留每日拜访记录+今日纪要两个 Tab,工作计划/商机/要客独立为侧边栏项目
+16. **独立工作页**: 工作计划/商机/要客各自拥有独立页面 + CRUD 弹窗 + 状态快速切换
+17. **变更追踪 (edit_log)**: 5 张业务表均含 JSONB edit_log 列;POST 自动插入创建记录;PUT 自动 diff 新旧值追加变更条目(编辑人+时间+字段级 diff+原因);编辑弹窗底部折叠时间轴面板;表格 🕐 图标标记被修改过的记录
+18. **图片预览**: ImagePreview.vue 统一组件,支持适应页面/缩放(0.25x~5x)/拖拽平移/滚轮缩放/键盘快捷键,PC+移动端一致体验
+19. **仪表盘跳转**: 四张统计卡片可点击跳转到对应的周报/工作计划/商机/要客页面
diff --git a/backend/app/api/customers.py b/backend/app/api/customers.py
index cd49212..32ccb6c 100644
--- a/backend/app/api/customers.py
+++ b/backend/app/api/customers.py
@@ -196,6 +196,7 @@ async def import_customers(
ws = wb.active
created, updated, skipped = 0, 0, 0
reasons = []
+ errors = []
# Build user name → id lookup (all users, not just managers)
user_rows = await db.execute(select(User.name, User.id))
diff --git a/backend/app/api/daily_notes.py b/backend/app/api/daily_notes.py
index 2f5df98..bf5f227 100644
--- a/backend/app/api/daily_notes.py
+++ b/backend/app/api/daily_notes.py
@@ -10,6 +10,7 @@ from app.models.daily_note import DailyNote
from app.models.user import User
from app.schemas.daily_note import DailyNoteCreate, DailyNoteUpdate, DailyNoteOut
from app.utils.timezone import today_cst, parse_date
+from app.utils.edit_log import compute_diff, append_entry, init_entry
router = APIRouter(prefix="/daily-notes", tags=["DailyNotes"])
@@ -20,8 +21,8 @@ async def _enrich(note: DailyNote, db: AsyncSession) -> dict:
"id": note.id, "manager_id": note.manager_id,
"note_date": note.note_date, "category": note.category,
"content": note.content, "time_range": note.time_range,
- "created_at": note.created_at, "updated_at": note.updated_at,
- "manager_name": mgr.scalar_one_or_none(),
+ "edit_log": note.edit_log or [], "created_at": note.created_at,
+ "updated_at": note.updated_at, "manager_name": mgr.scalar_one_or_none(),
}
@@ -85,6 +86,7 @@ async def create_note(
content=data.content,
time_range=data.time_range,
)
+ init_entry(note, current_user["name"])
db.add(note)
await db.commit()
await db.refresh(note)
@@ -104,11 +106,16 @@ async def update_note(
if current_user["role"] == "manager" and str(note.manager_id) != current_user["user_id"]:
raise HTTPException(status_code=403, detail="Access denied")
+ old_snapshot = {"note_date": str(note.note_date), "category": note.category, "content": note.content, "time_range": note.time_range}
update_data = data.model_dump(exclude_unset=True)
if "note_date" in update_data and update_data["note_date"]:
update_data["note_date"] = parse_date(update_data["note_date"])
for k, v in update_data.items():
setattr(note, k, v)
+ new_snapshot = {"note_date": str(note.note_date), "category": note.category, "content": note.content, "time_range": note.time_range}
+ changes = compute_diff(old_snapshot, new_snapshot)
+ if changes:
+ append_entry(note, current_user["name"], changes, getattr(data, "edit_reason", None))
await db.commit()
await db.refresh(note)
return await _enrich(note, db)
diff --git a/backend/app/api/key_visits.py b/backend/app/api/key_visits.py
index 2fed5d3..2544343 100644
--- a/backend/app/api/key_visits.py
+++ b/backend/app/api/key_visits.py
@@ -9,6 +9,7 @@ from app.models.key_visit import KeyVisit
from app.models.customer import Customer
from app.models.user import User
from app.schemas.key_visit import KeyVisitCreate, KeyVisitUpdate, KeyVisitOut
+from app.utils.edit_log import compute_diff, append_entry, init_entry
router = APIRouter(prefix="/key-visits", tags=["KeyVisits"])
@@ -27,6 +28,7 @@ async def _enrich(k: KeyVisit, db: AsyncSession) -> dict:
"planned_visitor": k.planned_visitor,
"visit_target": k.visit_target,
"manager_id": str(k.manager_id),
+ "edit_log": k.edit_log or [],
"manager_name": mgr.scalar_one_or_none(),
}
@@ -63,6 +65,7 @@ async def create_key_visit(
visit_target=data.visit_target,
manager_id=uuid.UUID(current_user["user_id"]),
)
+ init_entry(k, current_user["name"])
db.add(k)
await db.commit()
await db.refresh(k)
@@ -82,9 +85,14 @@ async def update_key_visit(
if current_user["role"] == "manager" and str(k.manager_id) != current_user["user_id"]:
raise HTTPException(status_code=403, detail="Access denied")
+ old_snapshot = {"customer_id": str(k.customer_id), "urgency_level": k.urgency_level, "description": k.description, "progress_status": k.progress_status, "planned_date": k.planned_date, "planned_visitor": k.planned_visitor, "visit_target": k.visit_target}
update_data = data.model_dump(exclude_unset=True)
for key, v in update_data.items():
setattr(k, key, v)
+ new_snapshot = {"customer_id": str(k.customer_id), "urgency_level": k.urgency_level, "description": k.description, "progress_status": k.progress_status, "planned_date": k.planned_date, "planned_visitor": k.planned_visitor, "visit_target": k.visit_target}
+ changes = compute_diff(old_snapshot, new_snapshot)
+ if changes:
+ append_entry(k, current_user["name"], changes, getattr(data, "edit_reason", None))
await db.commit()
await db.refresh(k)
return await _enrich(k, db)
diff --git a/backend/app/api/mini_business.py b/backend/app/api/mini_business.py
index 95c9c37..7cb9fc5 100644
--- a/backend/app/api/mini_business.py
+++ b/backend/app/api/mini_business.py
@@ -9,6 +9,7 @@ from app.models.mini_business import MiniBusiness
from app.models.customer import Customer
from app.models.user import User
from app.schemas.mini_business import MiniBusinessCreate, MiniBusinessUpdate, MiniBusinessOut
+from app.utils.edit_log import compute_diff, append_entry, init_entry
router = APIRouter(prefix="/mini-business", tags=["MiniBusiness"])
@@ -26,6 +27,7 @@ async def _enrich(m: MiniBusiness, db: AsyncSession) -> dict:
"status": m.status,
"manager_id": str(m.manager_id),
"manager_name": mgr.scalar_one_or_none(),
+ "edit_log": m.edit_log or [],
"expected_revenue_date": m.expected_revenue_date,
}
@@ -61,6 +63,7 @@ async def create_mini_business(
manager_id=uuid.UUID(current_user["user_id"]),
expected_revenue_date=data.expected_revenue_date,
)
+ init_entry(m, current_user["name"])
db.add(m)
await db.commit()
await db.refresh(m)
@@ -80,9 +83,14 @@ async def update_mini_business(
if current_user["role"] == "manager" and str(m.manager_id) != current_user["user_id"]:
raise HTTPException(status_code=403, detail="Access denied")
+ old_snapshot = {"customer_id": str(m.customer_id), "product_type": m.product_type, "amount": m.amount, "follow_up_detail": m.follow_up_detail, "status": m.status, "expected_revenue_date": m.expected_revenue_date}
update_data = data.model_dump(exclude_unset=True)
for k, v in update_data.items():
setattr(m, k, v)
+ new_snapshot = {"customer_id": str(m.customer_id), "product_type": m.product_type, "amount": m.amount, "follow_up_detail": m.follow_up_detail, "status": m.status, "expected_revenue_date": m.expected_revenue_date}
+ changes = compute_diff(old_snapshot, new_snapshot)
+ if changes:
+ append_entry(m, current_user["name"], changes, getattr(data, "edit_reason", None))
await db.commit()
await db.refresh(m)
return await _enrich(m, db)
diff --git a/backend/app/api/visits.py b/backend/app/api/visits.py
index 6bc5bb6..a95162e 100644
--- a/backend/app/api/visits.py
+++ b/backend/app/api/visits.py
@@ -12,6 +12,7 @@ from app.models.user import User
from app.schemas.visit import VisitCreate, VisitUpdate, VisitOut, VisitListOut
from app.utils.timezone import today_cst, parse_date
from app.services.minio_client import delete_objects
+from app.utils.edit_log import compute_diff, append_entry, init_entry
router = APIRouter(prefix="/visits", tags=["Visits"])
@@ -42,6 +43,7 @@ async def _enrich_visit(visit: Visit, db: AsyncSession) -> dict:
"photos": visit.photos,
"manager_id": str(visit.manager_id),
"manager_name": manager_name,
+ "edit_log": visit.edit_log or [],
"created_at": str(visit.created_at),
"updated_at": str(visit.updated_at),
}
@@ -138,6 +140,7 @@ async def create_visit(
photos=data.photos,
manager_id=uuid.UUID(current_user["user_id"]),
)
+ init_entry(visit, current_user["name"])
db.add(visit)
# Create draft copies for companions
@@ -176,6 +179,14 @@ async def update_visit(
if current_user["role"] == "manager" and str(visit.manager_id) != current_user["user_id"]:
raise HTTPException(status_code=403, detail="Access denied")
+ # Snapshot old values for diff
+ old_snapshot = {
+ "customer_id": str(visit.customer_id), "visit_date": str(visit.visit_date),
+ "visit_method": visit.visit_method, "time_range": visit.time_range,
+ "visitor_name": visit.visitor_name or "", "visitor_phone": visit.visitor_phone or "",
+ "communication_content": visit.communication_content, "customer_demand": visit.customer_demand,
+ }
+
update_data = data.model_dump(exclude_unset=True)
if "visit_date" in update_data and update_data["visit_date"]:
update_data["visit_date"] = parse_date(update_data["visit_date"])
@@ -183,6 +194,17 @@ async def update_visit(
for key, value in update_data.items():
setattr(visit, key, value)
+ # Compute diff and append to edit_log
+ new_snapshot = {
+ "customer_id": str(visit.customer_id), "visit_date": str(visit.visit_date),
+ "visit_method": visit.visit_method, "time_range": visit.time_range,
+ "visitor_name": visit.visitor_name or "", "visitor_phone": visit.visitor_phone or "",
+ "communication_content": visit.communication_content, "customer_demand": visit.customer_demand,
+ }
+ changes = compute_diff(old_snapshot, new_snapshot)
+ if changes:
+ append_entry(visit, current_user["name"], changes, getattr(data, "edit_reason", None))
+
await db.commit()
await db.refresh(visit)
return await _enrich_visit(visit, db)
diff --git a/backend/app/api/work_plans.py b/backend/app/api/work_plans.py
index 386b0ea..269e42a 100644
--- a/backend/app/api/work_plans.py
+++ b/backend/app/api/work_plans.py
@@ -7,6 +7,7 @@ from sqlalchemy import select
from app.database import get_db
from app.middleware.auth import get_current_user, require_any_role
from app.utils.timezone import parse_date
+from app.utils.edit_log import compute_diff, append_entry, init_entry
from app.models.work_plan import WorkPlan
from app.models.customer import Customer
from app.models.user import User
@@ -27,6 +28,7 @@ async def _enrich(wp: WorkPlan, db: AsyncSession) -> dict:
"manager_id": str(wp.manager_id),
"manager_name": mgr.scalar_one_or_none(),
"status": wp.status,
+ "edit_log": wp.edit_log or [],
}
@@ -59,6 +61,7 @@ async def create_work_plan(
manager_id=uuid.UUID(current_user["user_id"]),
status=data.status,
)
+ init_entry(wp, current_user["name"])
db.add(wp)
await db.commit()
await db.refresh(wp)
@@ -78,11 +81,16 @@ async def update_work_plan(
if current_user["role"] == "manager" and str(wp.manager_id) != current_user["user_id"]:
raise HTTPException(status_code=403, detail="Access denied")
+ old_snapshot = {"customer_id": str(wp.customer_id), "plan_content": wp.plan_content, "plan_date": str(wp.plan_date), "status": wp.status}
update_data = data.model_dump(exclude_unset=True)
if "plan_date" in update_data and update_data["plan_date"]:
update_data["plan_date"] = parse_date(update_data["plan_date"])
for k, v in update_data.items():
setattr(wp, k, v)
+ new_snapshot = {"customer_id": str(wp.customer_id), "plan_content": wp.plan_content, "plan_date": str(wp.plan_date), "status": wp.status}
+ changes = compute_diff(old_snapshot, new_snapshot)
+ if changes:
+ append_entry(wp, current_user["name"], changes, getattr(data, "edit_reason", None))
await db.commit()
await db.refresh(wp)
return await _enrich(wp, db)
diff --git a/backend/app/main.py b/backend/app/main.py
index 337be54..baa3458 100644
--- a/backend/app/main.py
+++ b/backend/app/main.py
@@ -23,6 +23,11 @@ async def lifespan(app: FastAPI):
await conn.run_sync(lambda c: c.exec_driver_sql(
"ALTER TABLE visits ADD COLUMN IF NOT EXISTS visitor_phone VARCHAR(20) DEFAULT ''"
))
+ # edit_log columns for change tracking
+ for tbl in ["visits", "daily_notes", "work_plans", "mini_business", "key_visits"]:
+ await conn.run_sync(lambda c, t=tbl: c.exec_driver_sql(
+ f"ALTER TABLE {t} ADD COLUMN IF NOT EXISTS edit_log JSONB DEFAULT '[]'"
+ ))
yield
# Shutdown
await engine.dispose()
diff --git a/backend/app/models/daily_note.py b/backend/app/models/daily_note.py
index 96a1ca7..9a1aa7c 100644
--- a/backend/app/models/daily_note.py
+++ b/backend/app/models/daily_note.py
@@ -2,7 +2,7 @@ import uuid
from datetime import date, datetime
from sqlalchemy import String, Text, Date, DateTime, ForeignKey, func
from sqlalchemy.orm import Mapped, mapped_column
-from sqlalchemy.dialects.postgresql import UUID
+from sqlalchemy.dialects.postgresql import UUID, JSONB
from app.database import Base
@@ -15,5 +15,6 @@ class DailyNote(Base):
category: Mapped[str] = mapped_column(String(20), default="其他") # 行政事务/合同整理/发票处理/内部会议/培训学习/其他
content: Mapped[str] = mapped_column(Text, default="")
time_range: Mapped[str] = mapped_column(String(30), default="")
+ edit_log: Mapped[list] = mapped_column(JSONB, default=list)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
diff --git a/backend/app/models/key_visit.py b/backend/app/models/key_visit.py
index b864aef..bf02267 100644
--- a/backend/app/models/key_visit.py
+++ b/backend/app/models/key_visit.py
@@ -1,7 +1,7 @@
import uuid
from sqlalchemy import String, Text, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column
-from sqlalchemy.dialects.postgresql import UUID
+from sqlalchemy.dialects.postgresql import UUID, JSONB
from app.database import Base
@@ -17,3 +17,4 @@ class KeyVisit(Base):
planned_visitor: Mapped[str] = mapped_column(String(100), default="")
visit_target: Mapped[str] = mapped_column(String(100), default="")
manager_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("users.id"), index=True)
+ edit_log: Mapped[list] = mapped_column(JSONB, default=list)
diff --git a/backend/app/models/mini_business.py b/backend/app/models/mini_business.py
index d9e6b8a..fb242dd 100644
--- a/backend/app/models/mini_business.py
+++ b/backend/app/models/mini_business.py
@@ -1,7 +1,7 @@
import uuid
from sqlalchemy import String, Text, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column
-from sqlalchemy.dialects.postgresql import UUID
+from sqlalchemy.dialects.postgresql import UUID, JSONB
from app.database import Base
@@ -16,3 +16,4 @@ class MiniBusiness(Base):
status: Mapped[str] = mapped_column(String(50), default="跟进中")
manager_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("users.id"), index=True)
expected_revenue_date: Mapped[str] = mapped_column(String(50), default="")
+ edit_log: Mapped[list] = mapped_column(JSONB, default=list)
diff --git a/backend/app/models/visit.py b/backend/app/models/visit.py
index 48a1798..af1b59f 100644
--- a/backend/app/models/visit.py
+++ b/backend/app/models/visit.py
@@ -2,7 +2,7 @@ import uuid
from datetime import datetime, date
from sqlalchemy import String, Text, Date, DateTime, ForeignKey, func
from sqlalchemy.orm import Mapped, mapped_column
-from sqlalchemy.dialects.postgresql import UUID, ARRAY
+from sqlalchemy.dialects.postgresql import UUID, ARRAY, JSONB
from app.database import Base
@@ -21,5 +21,6 @@ class Visit(Base):
companions: Mapped[list | None] = mapped_column(ARRAY(UUID(as_uuid=True)), nullable=True)
photos: Mapped[list | None] = mapped_column(ARRAY(Text), nullable=True)
manager_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("users.id"), index=True)
+ edit_log: Mapped[list] = mapped_column(JSONB, default=list)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
diff --git a/backend/app/models/work_plan.py b/backend/app/models/work_plan.py
index 883f2ba..d21f192 100644
--- a/backend/app/models/work_plan.py
+++ b/backend/app/models/work_plan.py
@@ -2,7 +2,7 @@ import uuid
from datetime import date
from sqlalchemy import String, Text, Date, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column
-from sqlalchemy.dialects.postgresql import UUID
+from sqlalchemy.dialects.postgresql import UUID, JSONB
from app.database import Base
@@ -15,3 +15,4 @@ class WorkPlan(Base):
plan_date: Mapped[date] = mapped_column(Date, index=True)
manager_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("users.id"), index=True)
status: Mapped[str] = mapped_column(String(20), default="计划中") # 计划中/已完成/已取消
+ edit_log: Mapped[list] = mapped_column(JSONB, default=list)
diff --git a/backend/app/utils/edit_log.py b/backend/app/utils/edit_log.py
new file mode 100644
index 0000000..c75d5cf
--- /dev/null
+++ b/backend/app/utils/edit_log.py
@@ -0,0 +1,43 @@
+"""Edit log helpers for change tracking."""
+import json
+from datetime import datetime, timezone
+
+
+def compute_diff(old: dict, new: dict, exclude: set = None) -> dict:
+ """Compare old and new values, return {field: [old, new]} for changed fields."""
+ if exclude is None:
+ exclude = {"edit_log", "updated_at", "created_at", "id"}
+ changes = {}
+ for key, new_val in new.items():
+ if key in exclude:
+ continue
+ old_val = old.get(key)
+ # Normalize for comparison
+ old_str = str(old_val) if old_val is not None else ""
+ new_str = str(new_val) if new_val is not None else ""
+ if old_str != new_str:
+ changes[key] = [old_str, new_str]
+ return changes
+
+
+def append_entry(record, editor_name: str, changes: dict, reason: str = None):
+ """Append an edit log entry to a record's edit_log JSON list."""
+ log = list(record.edit_log) if record.edit_log else []
+ entry = {
+ "editor": editor_name,
+ "time": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
+ "changes": changes,
+ }
+ if reason:
+ entry["reason"] = reason
+ log.append(entry)
+ record.edit_log = log
+
+
+def init_entry(record, creator_name: str):
+ """Initialize edit_log with a creation entry."""
+ record.edit_log = [{
+ "editor": creator_name,
+ "time": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
+ "changes": {},
+ }]
diff --git a/frontend/src/components/DesktopLayout.vue b/frontend/src/components/DesktopLayout.vue
index d42b706..b17bae1 100644
--- a/frontend/src/components/DesktopLayout.vue
+++ b/frontend/src/components/DesktopLayout.vue
@@ -8,20 +8,47 @@ const router = useRouter()
const auth = useAuthStore()
const collapsed = ref(false)
-const menuItems = computed(() => {
- const items: { path: string; label: string; icon: string }[] = [
- { path: '/', label: '仪表盘', icon: '' },
- { path: '/weekly-report', label: '周报详情', icon: '' },
+interface MenuGroup { label?: string; items: { path: string; label: string; icon: string }[] }
+
+const menuGroups = computed(() => {
+ const groups: MenuGroup[] = [
+ {
+ label: '汇总',
+ items: [
+ { path: '/', label: '仪表盘', icon: '' },
+ { path: '/weekly-report', label: '周报', icon: '' },
+ ],
+ },
+ {
+ label: '工作',
+ items: [
+ { path: '/work-plans', label: '工作计划', icon: '' },
+ { path: '/mini-business', label: '商机跟单', icon: '' },
+ { path: '/key-visits', label: '要客拜访', icon: '' },
+ ],
+ },
]
if (auth.isManager || auth.isDirector) {
- items.push({ path: '/workspace', label: '我的数据', icon: '' })
+ groups.push({
+ label: undefined,
+ items: [
+ { path: '/workspace', label: '我的数据', icon: '' },
+ ],
+ })
}
- items.push({ path: '/customers', label: '客户管理', icon: '' })
+ groups.push({
+ label: '管理',
+ items: [
+ { path: '/customers', label: '客户管理', icon: '' },
+ ],
+ })
if (auth.isDirector) {
- items.push({ path: '/users', label: '用户管理', icon: '' })
- items.push({ path: '/settings', label: '系统设置', icon: '' })
+ groups[groups.length - 1].items.push(
+ { path: '/users', label: '用户管理', icon: '' },
+ { path: '/settings', label: '系统设置', icon: '' },
+ )
}
- return items
+ return groups
})
const roleLabel = computed(() => {
@@ -42,16 +69,19 @@ const roleLabel = computed(() => {