企迹(qiji) 政企周报管理系统 — v0.1
后端: FastAPI + SQLAlchemy 2.0 (async) + Alembic + MinIO + Casdoor + 企微 前端: Vue 3 + Vite + TypeScript + Element Plus + Pinia 功能清单: - 8 张数据表自动建表 / Casdoor OIDC 登录 / 企微静默登录 - 双布局: 移动端(填报) + PC端(汇总管理) - 拜访记录 CRUD + MinIO 照片直传 + 缩略图预览 + 同访人草稿 - 今日纪要 (6 分类) / 工作计划 / 小微商机 / 要客拜访 CRUD - 客户档案: 备注/收支费用/联系人/归属分配/批量转移 - 客户导入导出 + 模板下载 + 搜索/分页/筛选 - 仪表盘: 四卡统计 + 填报进度 (拜访+纪要双维度) - 周报详情: 五 Tab + 按人/客户筛选 + 时间轴 - 用户管理 / 客户经理 PC 端工作台 - 企微: 催办/公告/定时提醒 / 时区修正 - Docker 部署配置 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
from app.schemas.customer import (
|
||||
CustomerCreate, CustomerUpdate, CustomerOut, CustomerListOut,
|
||||
ContactCreate, ContactOut, AssignmentCreate, AssignmentOut, BatchAssignRequest,
|
||||
)
|
||||
from app.schemas.visit import VisitCreate, VisitUpdate, VisitOut, VisitListOut
|
||||
from app.schemas.work_plan import WorkPlanCreate, WorkPlanUpdate, WorkPlanOut
|
||||
from app.schemas.mini_business import MiniBusinessCreate, MiniBusinessUpdate, MiniBusinessOut
|
||||
from app.schemas.key_visit import KeyVisitCreate, KeyVisitUpdate, KeyVisitOut
|
||||
from app.schemas.user import TokenResponse, WecomLoginRequest, CasdoorLoginRequest, WecomBindRequest
|
||||
from app.schemas.customer import UserCreate, UserOut
|
||||
@@ -0,0 +1,118 @@
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional
|
||||
import uuid
|
||||
from datetime import date, datetime
|
||||
|
||||
|
||||
# ── User ──
|
||||
class UserCreate(BaseModel):
|
||||
name: str
|
||||
role: str = "manager"
|
||||
department: str = ""
|
||||
wecom_userid: Optional[str] = None
|
||||
|
||||
|
||||
class UserOut(BaseModel):
|
||||
id: uuid.UUID
|
||||
casdoor_id: str
|
||||
name: str
|
||||
role: str
|
||||
department: str
|
||||
wecom_userid: Optional[str] = None
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
# ── CustomerContact ──
|
||||
class ContactCreate(BaseModel):
|
||||
name: str
|
||||
phone: str = ""
|
||||
role_desc: str = ""
|
||||
|
||||
|
||||
class ContactOut(BaseModel):
|
||||
id: uuid.UUID
|
||||
customer_id: uuid.UUID
|
||||
name: str
|
||||
phone: str
|
||||
role_desc: str
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
# ── Customer ──
|
||||
class CustomerCreate(BaseModel):
|
||||
name: str
|
||||
industry: str = ""
|
||||
address: str = ""
|
||||
in_use_services: str = ""
|
||||
monthly_fee: str = ""
|
||||
remarks: str = ""
|
||||
contacts: list[ContactCreate] = []
|
||||
assignee_id: Optional[uuid.UUID] = None
|
||||
|
||||
|
||||
class CustomerUpdate(BaseModel):
|
||||
name: Optional[str] = None
|
||||
industry: Optional[str] = None
|
||||
address: Optional[str] = None
|
||||
in_use_services: Optional[str] = None
|
||||
monthly_fee: Optional[str] = None
|
||||
remarks: Optional[str] = None
|
||||
assignee_id: Optional[uuid.UUID] = None
|
||||
|
||||
|
||||
class CustomerOut(BaseModel):
|
||||
id: uuid.UUID
|
||||
name: str
|
||||
industry: str
|
||||
address: str
|
||||
in_use_services: str
|
||||
monthly_fee: str
|
||||
remarks: str = ""
|
||||
created_by: uuid.UUID
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
contacts: list[ContactOut] = []
|
||||
primary_manager_name: Optional[str] = None
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class CustomerListOut(BaseModel):
|
||||
id: uuid.UUID
|
||||
name: str
|
||||
industry: str
|
||||
in_use_services: str
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class CustomerListResponse(BaseModel):
|
||||
items: list[CustomerListOut]
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
|
||||
|
||||
# ── CustomerAssignment ──
|
||||
class AssignmentCreate(BaseModel):
|
||||
customer_id: uuid.UUID
|
||||
manager_id: uuid.UUID
|
||||
role: str = "primary"
|
||||
|
||||
|
||||
class AssignmentOut(BaseModel):
|
||||
id: uuid.UUID
|
||||
customer_id: uuid.UUID
|
||||
manager_id: uuid.UUID
|
||||
role: str
|
||||
assigned_at: datetime
|
||||
assigned_by: uuid.UUID
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class BatchAssignRequest(BaseModel):
|
||||
customer_ids: list[uuid.UUID]
|
||||
manager_id: uuid.UUID
|
||||
@@ -0,0 +1,32 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
import uuid
|
||||
from datetime import date, datetime
|
||||
|
||||
|
||||
class DailyNoteCreate(BaseModel):
|
||||
note_date: str # YYYY-MM-DD
|
||||
category: str = "其他"
|
||||
content: str = ""
|
||||
time_range: str = ""
|
||||
|
||||
|
||||
class DailyNoteUpdate(BaseModel):
|
||||
note_date: Optional[str] = None
|
||||
category: Optional[str] = None
|
||||
content: Optional[str] = None
|
||||
time_range: Optional[str] = None
|
||||
|
||||
|
||||
class DailyNoteOut(BaseModel):
|
||||
id: uuid.UUID
|
||||
manager_id: uuid.UUID
|
||||
note_date: date
|
||||
category: str
|
||||
content: str
|
||||
time_range: str
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
manager_name: Optional[str] = None
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
@@ -0,0 +1,39 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
import uuid
|
||||
|
||||
|
||||
class KeyVisitCreate(BaseModel):
|
||||
customer_id: uuid.UUID
|
||||
urgency_level: str = "一般" # 重要/一般/紧急
|
||||
description: str = ""
|
||||
progress_status: str = "未开始"
|
||||
planned_date: str = ""
|
||||
planned_visitor: str = ""
|
||||
visit_target: str = ""
|
||||
|
||||
|
||||
class KeyVisitUpdate(BaseModel):
|
||||
customer_id: Optional[uuid.UUID] = None
|
||||
urgency_level: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
progress_status: Optional[str] = None
|
||||
planned_date: Optional[str] = None
|
||||
planned_visitor: Optional[str] = None
|
||||
visit_target: Optional[str] = None
|
||||
|
||||
|
||||
class KeyVisitOut(BaseModel):
|
||||
id: uuid.UUID
|
||||
customer_id: uuid.UUID
|
||||
urgency_level: str
|
||||
description: str
|
||||
progress_status: str
|
||||
planned_date: str
|
||||
planned_visitor: str
|
||||
visit_target: str
|
||||
manager_id: uuid.UUID
|
||||
customer_name: Optional[str] = None
|
||||
manager_name: Optional[str] = None
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
@@ -0,0 +1,36 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
import uuid
|
||||
|
||||
|
||||
class MiniBusinessCreate(BaseModel):
|
||||
customer_id: uuid.UUID
|
||||
product_type: str = ""
|
||||
amount: str = ""
|
||||
follow_up_detail: str = ""
|
||||
status: str = "跟进中"
|
||||
expected_revenue_date: str = ""
|
||||
|
||||
|
||||
class MiniBusinessUpdate(BaseModel):
|
||||
customer_id: Optional[uuid.UUID] = None
|
||||
product_type: Optional[str] = None
|
||||
amount: Optional[str] = None
|
||||
follow_up_detail: Optional[str] = None
|
||||
status: Optional[str] = None
|
||||
expected_revenue_date: Optional[str] = None
|
||||
|
||||
|
||||
class MiniBusinessOut(BaseModel):
|
||||
id: uuid.UUID
|
||||
customer_id: uuid.UUID
|
||||
product_type: str
|
||||
amount: str
|
||||
follow_up_detail: str
|
||||
status: str
|
||||
manager_id: uuid.UUID
|
||||
expected_revenue_date: str
|
||||
customer_name: Optional[str] = None
|
||||
manager_name: Optional[str] = None
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
@@ -0,0 +1,25 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
import uuid
|
||||
|
||||
|
||||
class TokenResponse(BaseModel):
|
||||
access_token: str
|
||||
token_type: str = "bearer"
|
||||
user_id: str
|
||||
name: str
|
||||
role: str
|
||||
|
||||
|
||||
class WecomLoginRequest(BaseModel):
|
||||
code: str
|
||||
|
||||
|
||||
class CasdoorLoginRequest(BaseModel):
|
||||
code: str
|
||||
state: str
|
||||
|
||||
|
||||
class WecomBindRequest(BaseModel):
|
||||
casdoor_code: str
|
||||
wecom_userid: Optional[str] = None
|
||||
@@ -0,0 +1,60 @@
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional
|
||||
import uuid
|
||||
from datetime import date, datetime
|
||||
|
||||
|
||||
class VisitCreate(BaseModel):
|
||||
customer_id: uuid.UUID
|
||||
visit_date: str # "YYYY-MM-DD"
|
||||
visit_method: str = "上门"
|
||||
time_range: str = ""
|
||||
communication_content: str = ""
|
||||
customer_demand: str = ""
|
||||
companions: list[uuid.UUID] = []
|
||||
photos: list[str] = []
|
||||
|
||||
|
||||
class VisitUpdate(BaseModel):
|
||||
customer_id: Optional[uuid.UUID] = None
|
||||
visit_date: Optional[str] = None
|
||||
visit_method: Optional[str] = None
|
||||
time_range: Optional[str] = None
|
||||
communication_content: Optional[str] = None
|
||||
customer_demand: Optional[str] = None
|
||||
companions: Optional[list[uuid.UUID]] = None
|
||||
photos: Optional[list[str]] = None
|
||||
|
||||
|
||||
class VisitOut(BaseModel):
|
||||
id: uuid.UUID
|
||||
customer_id: uuid.UUID
|
||||
visit_date: date
|
||||
visit_method: str
|
||||
time_range: str
|
||||
communication_content: str
|
||||
customer_demand: str
|
||||
companions: Optional[list[uuid.UUID]] = None
|
||||
photos: Optional[list[str]] = None
|
||||
manager_id: uuid.UUID
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
# Joined fields
|
||||
customer_name: Optional[str] = None
|
||||
manager_name: Optional[str] = None
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class VisitListOut(BaseModel):
|
||||
id: uuid.UUID
|
||||
customer_id: uuid.UUID
|
||||
visit_date: date
|
||||
visit_method: str
|
||||
time_range: str
|
||||
manager_id: uuid.UUID
|
||||
customer_name: Optional[str] = None
|
||||
manager_name: Optional[str] = None
|
||||
has_photos: bool = False
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
@@ -0,0 +1,31 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
import uuid
|
||||
from datetime import date
|
||||
|
||||
|
||||
class WorkPlanCreate(BaseModel):
|
||||
customer_id: uuid.UUID
|
||||
plan_content: str = ""
|
||||
plan_date: str # "YYYY-MM-DD"
|
||||
status: str = "计划中"
|
||||
|
||||
|
||||
class WorkPlanUpdate(BaseModel):
|
||||
customer_id: Optional[uuid.UUID] = None
|
||||
plan_content: Optional[str] = None
|
||||
plan_date: Optional[str] = None
|
||||
status: Optional[str] = None
|
||||
|
||||
|
||||
class WorkPlanOut(BaseModel):
|
||||
id: uuid.UUID
|
||||
customer_id: uuid.UUID
|
||||
plan_content: str
|
||||
plan_date: date
|
||||
manager_id: uuid.UUID
|
||||
status: str
|
||||
customer_name: Optional[str] = None
|
||||
manager_name: Optional[str] = None
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
Reference in New Issue
Block a user