diff --git a/backend/app/api/daily_notes.py b/backend/app/api/daily_notes.py index bf5f227..4f836d1 100644 --- a/backend/app/api/daily_notes.py +++ b/backend/app/api/daily_notes.py @@ -80,7 +80,7 @@ async def create_note( db: AsyncSession = Depends(get_db), ): note = DailyNote( - manager_id=uuid.UUID(current_user["user_id"]), + manager_id=data.manager_id if (current_user["role"] in ("director", "leader") and data.manager_id) else uuid.UUID(current_user["user_id"]), note_date=parse_date(data.note_date), category=data.category, content=data.content, @@ -108,6 +108,8 @@ async def update_note( 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 current_user["role"] not in ("director", "leader"): + update_data.pop("manager_id", None) 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(): diff --git a/backend/app/api/key_visits.py b/backend/app/api/key_visits.py index 2544343..8a98a76 100644 --- a/backend/app/api/key_visits.py +++ b/backend/app/api/key_visits.py @@ -63,7 +63,7 @@ async def create_key_visit( planned_date=data.planned_date, planned_visitor=data.planned_visitor, visit_target=data.visit_target, - manager_id=uuid.UUID(current_user["user_id"]), + manager_id=data.manager_id if (current_user["role"] in ("director", "leader") and data.manager_id) else uuid.UUID(current_user["user_id"]), ) init_entry(k, current_user["name"]) db.add(k) @@ -87,6 +87,8 @@ async def update_key_visit( 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) + if current_user["role"] not in ("director", "leader"): + update_data.pop("manager_id", None) 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} diff --git a/backend/app/api/mini_business.py b/backend/app/api/mini_business.py index 7cb9fc5..390ac04 100644 --- a/backend/app/api/mini_business.py +++ b/backend/app/api/mini_business.py @@ -60,7 +60,7 @@ async def create_mini_business( amount=data.amount, follow_up_detail=data.follow_up_detail, status=data.status, - manager_id=uuid.UUID(current_user["user_id"]), + manager_id=data.manager_id if (current_user["role"] in ("director", "leader") and data.manager_id) else uuid.UUID(current_user["user_id"]), expected_revenue_date=data.expected_revenue_date, ) init_entry(m, current_user["name"]) @@ -85,6 +85,8 @@ async def update_mini_business( 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) + if current_user["role"] not in ("director", "leader"): + update_data.pop("manager_id", None) 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} diff --git a/backend/app/api/visits.py b/backend/app/api/visits.py index 001388c..f02ab82 100644 --- a/backend/app/api/visits.py +++ b/backend/app/api/visits.py @@ -147,7 +147,7 @@ async def create_visit( companions=data.companions, companion_names=data.companion_names, photos=data.photos, - manager_id=uuid.UUID(current_user["user_id"]), + manager_id=data.manager_id if (current_user["role"] in ("director", "leader") and data.manager_id) else uuid.UUID(current_user["user_id"]), ) init_entry(visit, current_user["name"]) db.add(visit) @@ -212,6 +212,8 @@ async def update_visit( } update_data = data.model_dump(exclude_unset=True) + if current_user["role"] not in ("director", "leader"): + update_data.pop("manager_id", None) if "visit_date" in update_data and update_data["visit_date"]: update_data["visit_date"] = parse_date(update_data["visit_date"]) diff --git a/backend/app/api/work_plans.py b/backend/app/api/work_plans.py index 269e42a..b8075e3 100644 --- a/backend/app/api/work_plans.py +++ b/backend/app/api/work_plans.py @@ -58,7 +58,7 @@ async def create_work_plan( customer_id=data.customer_id, plan_content=data.plan_content, plan_date=parse_date(data.plan_date), - manager_id=uuid.UUID(current_user["user_id"]), + manager_id=data.manager_id if (current_user["role"] in ("director", "leader") and data.manager_id) else uuid.UUID(current_user["user_id"]), status=data.status, ) init_entry(wp, current_user["name"]) @@ -83,6 +83,8 @@ async def update_work_plan( 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 current_user["role"] not in ("director", "leader"): + update_data.pop("manager_id", None) 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(): diff --git a/backend/app/schemas/daily_note.py b/backend/app/schemas/daily_note.py index 1664ada..3ce960b 100644 --- a/backend/app/schemas/daily_note.py +++ b/backend/app/schemas/daily_note.py @@ -9,6 +9,7 @@ class DailyNoteCreate(BaseModel): category: str = "其他" content: str = "" time_range: str = "" + manager_id: Optional[uuid.UUID] = None class DailyNoteUpdate(BaseModel): @@ -16,6 +17,7 @@ class DailyNoteUpdate(BaseModel): category: Optional[str] = None content: Optional[str] = None time_range: Optional[str] = None + manager_id: Optional[uuid.UUID] = None class DailyNoteOut(BaseModel): diff --git a/backend/app/schemas/key_visit.py b/backend/app/schemas/key_visit.py index c7d7454..91063cc 100644 --- a/backend/app/schemas/key_visit.py +++ b/backend/app/schemas/key_visit.py @@ -11,6 +11,7 @@ class KeyVisitCreate(BaseModel): planned_date: str = "" planned_visitor: str = "" visit_target: str = "" + manager_id: Optional[uuid.UUID] = None class KeyVisitUpdate(BaseModel): @@ -21,6 +22,7 @@ class KeyVisitUpdate(BaseModel): planned_date: Optional[str] = None planned_visitor: Optional[str] = None visit_target: Optional[str] = None + manager_id: Optional[uuid.UUID] = None class KeyVisitOut(BaseModel): diff --git a/backend/app/schemas/mini_business.py b/backend/app/schemas/mini_business.py index a0719aa..0da30e7 100644 --- a/backend/app/schemas/mini_business.py +++ b/backend/app/schemas/mini_business.py @@ -10,6 +10,7 @@ class MiniBusinessCreate(BaseModel): follow_up_detail: str = "" status: str = "跟进中" expected_revenue_date: str = "" + manager_id: Optional[uuid.UUID] = None class MiniBusinessUpdate(BaseModel): @@ -19,6 +20,7 @@ class MiniBusinessUpdate(BaseModel): follow_up_detail: Optional[str] = None status: Optional[str] = None expected_revenue_date: Optional[str] = None + manager_id: Optional[uuid.UUID] = None class MiniBusinessOut(BaseModel): diff --git a/backend/app/schemas/visit.py b/backend/app/schemas/visit.py index 7984e37..3db7d1b 100644 --- a/backend/app/schemas/visit.py +++ b/backend/app/schemas/visit.py @@ -16,6 +16,7 @@ class VisitCreate(BaseModel): companions: list[uuid.UUID] = [] companion_names: list[str] = [] photos: list[str] = [] + manager_id: Optional[uuid.UUID] = None # director/leader can specify class VisitUpdate(BaseModel): @@ -28,6 +29,7 @@ class VisitUpdate(BaseModel): communication_content: Optional[str] = None customer_demand: Optional[str] = None companions: Optional[list[uuid.UUID]] = None + manager_id: Optional[uuid.UUID] = None companion_names: Optional[list[str]] = None photos: Optional[list[str]] = None diff --git a/backend/app/schemas/work_plan.py b/backend/app/schemas/work_plan.py index 1816798..5b10799 100644 --- a/backend/app/schemas/work_plan.py +++ b/backend/app/schemas/work_plan.py @@ -9,6 +9,7 @@ class WorkPlanCreate(BaseModel): plan_content: str = "" plan_date: str # "YYYY-MM-DD" status: str = "计划中" + manager_id: Optional[uuid.UUID] = None class WorkPlanUpdate(BaseModel): @@ -16,6 +17,7 @@ class WorkPlanUpdate(BaseModel): plan_content: Optional[str] = None plan_date: Optional[str] = None status: Optional[str] = None + manager_id: Optional[uuid.UUID] = None class WorkPlanOut(BaseModel): diff --git a/frontend/src/views/desktop/KeyVisits.vue b/frontend/src/views/desktop/KeyVisits.vue index 98ade7f..914a179 100644 --- a/frontend/src/views/desktop/KeyVisits.vue +++ b/frontend/src/views/desktop/KeyVisits.vue @@ -15,6 +15,7 @@ const loading = ref(false) const isLight = computed(() => themeStore.currentTheme === 'light') const keyVisits = ref([]) const customers = ref([]) +const allManagers = ref([]) const shotRef = ref(null) const { capturing, captureEl } = useScreenshot() const allUsers = ref([]) @@ -79,6 +80,9 @@ const managerSummary = computed(() => { onMounted(async () => { await Promise.all([loadItems(), loadCustomers(), loadUsers(), loadManagerColors()]) + if (auth.isDirector || auth.isLeader) { + api.get('/users/', { params: { role: 'manager' } }).then(r => { allManagers.value = r.data }).catch(() => {}) + } }) async function loadUsers() { @@ -132,7 +136,7 @@ async function loadItems() { function openCreate() { dialogMode.value = 'create' - form.value = { customer_id: '', urgency_level: '一般', description: '', progress_status: '未开始', planned_date: '', planned_visitor: '', visit_target: '' } + form.value = { customer_id: '', urgency_level: '一般', description: '', progress_status: '未开始', planned_date: '', planned_visitor: '', visit_target: '', manager_id: auth.userId || '' } plannedVisitors.value = [] dialogVisible.value = true } @@ -304,6 +308,11 @@ async function quickStatusChange(row: any, newStatus: string) { + + + + +
([]) const visits = ref([]) const workPlans = ref([]) @@ -39,6 +42,9 @@ const keyStatuses = ['未开始', '进行中', '已完成'] onMounted(async () => { await Promise.all([loadAll(), loadCustomers(), loadUsers()]) + if (auth.isDirector || auth.isLeader) { + api.get('/users/', { params: { role: 'manager' } }).then(r => { allManagers.value = r.data }).catch(() => {}) + } }) async function loadUsers() { @@ -117,11 +123,11 @@ async function loadAll() { function openCreate(type: string) { dialogMode.value = 'create'; dialogType.value = type dialogTimeRange.value = null - if (type === 'visit') form.value = { customer_id: '', visit_date: todayStr(), visit_method: '上门', time_range: '', visitor_name: '', visitor_phone: '', communication_content: '', customer_demand: '', companions: [], companion_names: [] } - else if (type === 'note') form.value = { note_date: todayStr(), category: '其他', content: '', time_range: '' } - else if (type === 'plan') form.value = { customer_id: '', plan_content: '', plan_date: todayStr(), status: '计划中' } - else if (type === 'mini') form.value = { customer_id: '', product_type: '', amount: '', follow_up_detail: '', status: '跟进中', expected_revenue_date: '' } - else if (type === 'key') { form.value = { customer_id: '', urgency_level: '一般', description: '', progress_status: '未开始', planned_date: '', planned_visitor: '', visit_target: '' }; plannedVisitors.value = [] } + if (type === 'visit') form.value = { customer_id: '', visit_date: todayStr(), visit_method: '上门', time_range: '', visitor_name: '', visitor_phone: '', communication_content: '', customer_demand: '', companions: [], companion_names: [], manager_id: auth.userId || '' } + else if (type === 'note') form.value = { note_date: todayStr(), category: '其他', content: '', time_range: '', manager_id: auth.userId || '' } + else if (type === 'plan') form.value = { customer_id: '', plan_content: '', plan_date: todayStr(), status: '计划中', manager_id: auth.userId || '' } + else if (type === 'mini') form.value = { customer_id: '', product_type: '', amount: '', follow_up_detail: '', status: '跟进中', expected_revenue_date: '', manager_id: auth.userId || '' } + else if (type === 'key') { form.value = { customer_id: '', urgency_level: '一般', description: '', progress_status: '未开始', planned_date: '', planned_visitor: '', visit_target: '', manager_id: auth.userId || '' }; plannedVisitors.value = [] } dialogVisible.value = true } @@ -480,6 +486,11 @@ const notesByDate = computed(() => { + + + + + diff --git a/frontend/src/views/desktop/MiniBusiness.vue b/frontend/src/views/desktop/MiniBusiness.vue index 1dd849c..fbd7364 100644 --- a/frontend/src/views/desktop/MiniBusiness.vue +++ b/frontend/src/views/desktop/MiniBusiness.vue @@ -15,6 +15,7 @@ const loading = ref(false) const isLight = computed(() => themeStore.currentTheme === 'light') const miniBusiness = ref([]) const customers = ref([]) +const allManagers = ref([]) const shotRef = ref(null) const { capturing, captureEl } = useScreenshot() @@ -64,6 +65,9 @@ const managerSummary = computed(() => { onMounted(async () => { await Promise.all([loadItems(), loadCustomers(), loadManagerColors()]) + if (auth.isDirector || auth.isLeader) { + api.get('/users/', { params: { role: 'manager' } }).then(r => { allManagers.value = r.data }).catch(() => {}) + } }) const customerSearch = ref('') @@ -110,7 +114,7 @@ async function loadItems() { function openCreate() { dialogMode.value = 'create' - form.value = { customer_id: '', product_type: '', amount: '', follow_up_detail: '', status: '跟进中', expected_revenue_date: '' } + form.value = { customer_id: '', product_type: '', amount: '', follow_up_detail: '', status: '跟进中', expected_revenue_date: '', manager_id: auth.userId || '' } dialogVisible.value = true } @@ -271,6 +275,11 @@ async function quickStatusChange(row: any, newStatus: string) { + + + + + diff --git a/frontend/src/views/desktop/WorkPlans.vue b/frontend/src/views/desktop/WorkPlans.vue index 0e326ae..cae02ff 100644 --- a/frontend/src/views/desktop/WorkPlans.vue +++ b/frontend/src/views/desktop/WorkPlans.vue @@ -14,6 +14,7 @@ const themeStore = useThemeStore() const loading = ref(false) const workPlans = ref([]) const customers = ref([]) +const allManagers = ref([]) const shotRef = ref(null) const { capturing, captureEl } = useScreenshot() @@ -65,6 +66,9 @@ const managerSummary = computed(() => { onMounted(async () => { await Promise.all([loadPlans(), loadCustomers(), loadManagerColors()]) + if (auth.isDirector || auth.isLeader) { + api.get('/users/', { params: { role: 'manager' } }).then(r => { allManagers.value = r.data }).catch(() => {}) + } }) const customerSearch = ref('') @@ -111,7 +115,7 @@ async function loadPlans() { function openCreate() { dialogMode.value = 'create' - form.value = { customer_id: '', plan_content: '', plan_date: todayStr(), status: '计划中' } + form.value = { customer_id: '', plan_content: '', plan_date: todayStr(), status: '计划中', manager_id: auth.userId || '' } dialogVisible.value = true } @@ -270,6 +274,11 @@ async function quickStatusChange(row: any, newStatus: string) { + + + + + diff --git a/frontend/src/views/mobile/DailyNoteForm.vue b/frontend/src/views/mobile/DailyNoteForm.vue index 6346515..e4c9b9f 100644 --- a/frontend/src/views/mobile/DailyNoteForm.vue +++ b/frontend/src/views/mobile/DailyNoteForm.vue @@ -3,13 +3,16 @@ import { ref, onMounted, computed } from 'vue' import { useRouter, useRoute } from 'vue-router' import { ElMessage, ElMessageBox } from 'element-plus' import { todayStr } from '@/utils' +import { useAuthStore } from '@/stores/auth' import api from '@/api/index' import EditLogPanel from '@/components/EditLogPanel.vue' const router = useRouter() const route = useRoute() +const auth = useAuthStore() const isEdit = computed(() => !!route.params.id) const submitLoading = ref(false) +const allManagers = ref([]) const categories = ['行政事务', '合同整理', '发票处理', '内部会议', '培训学习', '其他'] @@ -24,6 +27,7 @@ const form = ref({ category: '其他', content: '', time_range: '', + manager_id: auth.userId || '', }) function onTimeRangeChange(val: [string, string] | null) { @@ -36,6 +40,9 @@ function parseTimeRange(str: string): [string, string] | null { } onMounted(async () => { + if (auth.isDirector || auth.isLeader) { + try { const res = await api.get('/users/', { params: { role: 'manager' } }); allManagers.value = res.data } catch (_) {} + } if (isEdit.value) { try { const res = await api.get(`/daily-notes/${route.params.id}`) @@ -45,6 +52,7 @@ onMounted(async () => { category: n.category, content: n.content || '', time_range: n.time_range || '', + manager_id: n.manager_id || auth.userId || '', } timeRangeValue.value = parseTimeRange(n.time_range) } catch (_) {} @@ -105,6 +113,13 @@ async function handleDelete() { + + + + + + +