feat: 默认以当前账户发起考核调研
This commit is contained in:
parent
687ec77967
commit
7af0c1b926
@ -2130,7 +2130,6 @@ function CreateSurveyForm({
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [publishingLabel, setPublishingLabel] = useState('')
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const defaultTargetInitializedRef = useRef(false)
|
||||
|
||||
const selectedTemplate = templateCode || templates[0]?.code || ''
|
||||
const selectedTemplateInfo = templates.find((template) => template.code === selectedTemplate)
|
||||
@ -2140,33 +2139,26 @@ function CreateSurveyForm({
|
||||
1,
|
||||
Number(selectedTemplateInfo?.requiredWorkItemCount || DEFAULT_VISUAL_CASE_COUNT),
|
||||
)
|
||||
const effectiveTargetEmployees = useMemo(
|
||||
() => surveyKind === 'item_course'
|
||||
? []
|
||||
: mergeEmployeesById(currentEmployee ? [currentEmployee] : [], targetEmployees),
|
||||
[currentEmployee, surveyKind, targetEmployees],
|
||||
)
|
||||
const currentTargetSelected = Boolean(
|
||||
currentEmployee?.id && targetEmployees.some((employee) => employee.id === currentEmployee.id),
|
||||
currentEmployee?.id && effectiveTargetEmployees.some((employee) => employee.id === currentEmployee.id),
|
||||
)
|
||||
const createdUploadTarget = visualTemplateSelected
|
||||
? (createdSurvey?.targets || []).find((target: any) => target.canUploadWorkItems)
|
||||
: null
|
||||
const delegatedTargetCount = surveyKind === 'item_course'
|
||||
? 0
|
||||
: targetEmployees.filter((employee) => employee.id !== currentEmployee?.id).length
|
||||
: effectiveTargetEmployees.filter((employee) => employee.id !== currentEmployee?.id).length
|
||||
const evaluatorEmployees = useMemo(
|
||||
() => mergeEmployeesById(manualEvaluatorEmployees, ...Object.values(evaluatorDepartmentEmployees)),
|
||||
[manualEvaluatorEmployees, evaluatorDepartmentEmployees],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
defaultTargetInitializedRef.current
|
||||
|| surveyKind === 'item_course'
|
||||
|| !currentEmployee?.id
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
defaultTargetInitializedRef.current = true
|
||||
setTargetEmployees((current) => current.length > 0 ? current : [currentEmployee])
|
||||
}, [currentEmployee, surveyKind])
|
||||
|
||||
const removeEvaluatorEmployee = (id: string) => {
|
||||
setManualEvaluatorEmployees((current) => current.filter((employee) => employee.id !== id))
|
||||
setEvaluatorDepartmentEmployees((current) => {
|
||||
@ -2193,8 +2185,8 @@ function CreateSurveyForm({
|
||||
if (surveyKind === 'item_course' && targetItemsToSubmit.length === 0) {
|
||||
throw new Error(`请填写${selectedTemplateInfo?.itemName || '课程、事项或活动'}名称`)
|
||||
}
|
||||
if (surveyKind !== 'item_course' && targetEmployees.length === 0) {
|
||||
throw new Error(`请至少选择 1 个${selectedTemplateInfo?.ownerRoleName || '被考核人'}`)
|
||||
if (surveyKind !== 'item_course' && !currentEmployee?.id) {
|
||||
throw new Error('当前账户尚未匹配考核人员档案,暂时无法发起调研')
|
||||
}
|
||||
const visualCasesToSubmit = visualCases.slice(0, visualRequiredCount)
|
||||
if (visualTemplateSelected && currentTargetSelected) {
|
||||
@ -2221,7 +2213,7 @@ function CreateSurveyForm({
|
||||
templateCode: selectedTemplate,
|
||||
dueAt: assessmentMonth ? `${assessmentMonth}-01T00:00:00` : null,
|
||||
openNow,
|
||||
targetEmployeeIds: surveyKind === 'item_course' ? [] : targetEmployees.map((employee) => employee.id),
|
||||
targetEmployeeIds: effectiveTargetEmployees.map((employee) => employee.id),
|
||||
targetItems: targetItemsToSubmit,
|
||||
evaluatorEmployeeIds: evaluatorEmployees.map((employee) => employee.id),
|
||||
}),
|
||||
@ -2252,7 +2244,7 @@ function CreateSurveyForm({
|
||||
setTitle('')
|
||||
setDescription('')
|
||||
setDueAt(getCurrentMonthValue())
|
||||
setTargetEmployees(surveyKind === 'item_course' || !currentEmployee ? [] : [currentEmployee])
|
||||
setTargetEmployees([])
|
||||
setManualEvaluatorEmployees([])
|
||||
setEvaluatorDepartmentValues([])
|
||||
setEvaluatorDepartmentEmployees({})
|
||||
@ -2310,7 +2302,7 @@ function CreateSurveyForm({
|
||||
|
||||
{error && <div className="border border-[#ffdad6] bg-[#fff6f4] px-4 py-3 text-sm font-semibold text-[#93000a]">{error}</div>}
|
||||
<button
|
||||
disabled={saving || Boolean(createdSurvey)}
|
||||
disabled={saving || Boolean(createdSurvey) || (surveyKind !== 'item_course' && !currentEmployee?.id)}
|
||||
onClick={submit}
|
||||
className="h-12 w-full rounded bg-[#0050cb] px-5 text-[15px] font-bold text-white hover:bg-[#003fa4] disabled:opacity-50"
|
||||
>
|
||||
@ -2339,10 +2331,43 @@ function CreateSurveyForm({
|
||||
<div className="grid gap-8 lg:grid-cols-2">
|
||||
<div>
|
||||
{surveyKind !== 'item_course' ? (
|
||||
<div>
|
||||
<EmployeePicker title={selectedTemplateInfo?.ownerRoleName || '被考核人'} purpose="target" selected={targetEmployees} onChange={setTargetEmployees} />
|
||||
<div className="grid gap-4">
|
||||
<div>
|
||||
<div className="text-[12px] font-semibold tracking-[0.05em] text-[#5f5e5e]">
|
||||
{selectedTemplateInfo?.ownerRoleName || '被考核人'}
|
||||
</div>
|
||||
{currentEmployee ? (
|
||||
<div className="mt-2 flex items-center gap-3 border border-[#dce5f3] bg-[#f8faff] px-3 py-3">
|
||||
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-[#0050cb] text-[13px] font-bold text-white">
|
||||
{getSurveyInitials(currentEmployee.displayName)}
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="truncate text-[14px] font-bold text-[#1a1c1c]">{currentEmployee.displayName}</div>
|
||||
<div className="mt-0.5 truncate text-[12px] text-[#5f5e5e]">
|
||||
{[currentEmployee.departmentName, currentEmployee.positionLevel || currentEmployee.levelType]
|
||||
.filter(Boolean)
|
||||
.join(' · ') || '当前登录账户'}
|
||||
</div>
|
||||
</div>
|
||||
<span className="shrink-0 rounded-full bg-[#e8f0ff] px-2 py-1 text-[11px] font-bold text-[#0050cb]">
|
||||
当前账户
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-2 border border-[#ffdad6] bg-[#fff6f4] px-3 py-3 text-[12px] font-semibold text-[#93000a]">
|
||||
当前账户尚未匹配考核人员档案
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<EmployeePicker
|
||||
title="添加其他被考核人(可选)"
|
||||
purpose="target"
|
||||
selected={targetEmployees}
|
||||
excludedIds={currentEmployee?.id ? [currentEmployee.id] : []}
|
||||
onChange={setTargetEmployees}
|
||||
/>
|
||||
{delegatedTargetCount > 0 && (
|
||||
<div className="mt-2 text-[12px] font-semibold text-[#0050cb]">
|
||||
<div className="text-[12px] font-semibold text-[#0050cb]">
|
||||
将向 {delegatedTargetCount} 位被考核人发送钉钉确认
|
||||
</div>
|
||||
)}
|
||||
@ -2645,6 +2670,7 @@ function EmployeePicker({
|
||||
title,
|
||||
purpose,
|
||||
selected,
|
||||
excludedIds = [],
|
||||
onChange,
|
||||
onAddEmployee,
|
||||
onRemoveEmployee,
|
||||
@ -2652,6 +2678,7 @@ function EmployeePicker({
|
||||
title: string
|
||||
purpose?: 'target' | 'evaluator'
|
||||
selected: Employee[]
|
||||
excludedIds?: string[]
|
||||
onChange: (employees: Employee[]) => void
|
||||
onAddEmployee?: (employee: Employee) => void
|
||||
onRemoveEmployee?: (id: string) => void
|
||||
@ -2717,7 +2744,7 @@ function EmployeePicker({
|
||||
return () => document.removeEventListener('mousedown', closeWhenOutside)
|
||||
}, [])
|
||||
|
||||
const selectedIds = new Set(selected.map((employee) => employee.id))
|
||||
const selectedIds = new Set([...selected.map((employee) => employee.id), ...excludedIds])
|
||||
const selectEmployee = (employee: Employee) => {
|
||||
if (selectedIds.has(employee.id)) return
|
||||
if (onAddEmployee) {
|
||||
|
||||
27
lib/__tests__/performance-survey-target-policy.test.ts
Normal file
27
lib/__tests__/performance-survey-target-policy.test.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { getPerformanceSurveyTargetEmployeeIds } from '@/lib/performance-survey-target-policy'
|
||||
|
||||
describe('performance survey target policy', () => {
|
||||
test('人员考核默认使用当前账户', () => {
|
||||
expect(getPerformanceSurveyTargetEmployeeIds({
|
||||
requestedEmployeeIds: [],
|
||||
currentEmployeeId: 'employee-current',
|
||||
surveyKind: 'person_period',
|
||||
})).toEqual(['employee-current'])
|
||||
})
|
||||
|
||||
test('添加其他被考核人时保留当前账户并去重', () => {
|
||||
expect(getPerformanceSurveyTargetEmployeeIds({
|
||||
requestedEmployeeIds: ['employee-other', 'employee-current', 'employee-other'],
|
||||
currentEmployeeId: 'employee-current',
|
||||
surveyKind: 'visual_case',
|
||||
})).toEqual(['employee-current', 'employee-other'])
|
||||
})
|
||||
|
||||
test('事项型调研不自动注入当前账户', () => {
|
||||
expect(getPerformanceSurveyTargetEmployeeIds({
|
||||
requestedEmployeeIds: ['employee-other'],
|
||||
currentEmployeeId: 'employee-current',
|
||||
surveyKind: 'item_course',
|
||||
})).toEqual(['employee-other'])
|
||||
})
|
||||
})
|
||||
@ -38,6 +38,7 @@ import {
|
||||
withPerformanceSurveyImageProcessingSlot,
|
||||
type PerformanceSurveyStoredImageGroup,
|
||||
} from '@/lib/performance-survey-image-processing'
|
||||
import { getPerformanceSurveyTargetEmployeeIds } from '@/lib/performance-survey-target-policy'
|
||||
import { deleteFromS3, uploadToS3 } from '@/lib/s3'
|
||||
|
||||
export class PerformanceSurveyApiError extends Error {
|
||||
@ -1472,7 +1473,11 @@ export async function createPerformanceSurvey(session: Session | null, body: any
|
||||
throw new PerformanceSurveyApiError('只能创建本人部门范围内的考核调研', 403)
|
||||
}
|
||||
|
||||
const targets = await resolveSurveyTargets(body, surveyKind)
|
||||
if (surveyKind !== 'item_course' && !ctx.employee?.id) {
|
||||
throw new PerformanceSurveyApiError('当前账户尚未匹配考核人员档案,暂时无法发起调研', 409)
|
||||
}
|
||||
|
||||
const targets = await resolveSurveyTargets(body, surveyKind, ctx.employee?.id)
|
||||
if (targets.length === 0) {
|
||||
throw new PerformanceSurveyApiError(
|
||||
surveyKind === 'item_course'
|
||||
@ -2442,8 +2447,12 @@ export async function runPerformanceSurveyNotificationMaintenance(session: Sessi
|
||||
return { reminderCount: reminderIds.length, recovered }
|
||||
}
|
||||
|
||||
async function resolveSurveyTargets(body: any, surveyKind: string) {
|
||||
const targetEmployeeIds = normalizeList(body?.targetEmployeeIds)
|
||||
async function resolveSurveyTargets(body: any, surveyKind: string, currentEmployeeId?: string | null) {
|
||||
const targetEmployeeIds = getPerformanceSurveyTargetEmployeeIds({
|
||||
requestedEmployeeIds: body?.targetEmployeeIds,
|
||||
currentEmployeeId,
|
||||
surveyKind,
|
||||
})
|
||||
const itemTargets = Array.isArray(body?.targetItems)
|
||||
? body.targetItems
|
||||
.map((item: any) => ({
|
||||
|
||||
24
lib/performance-survey-target-policy.ts
Normal file
24
lib/performance-survey-target-policy.ts
Normal file
@ -0,0 +1,24 @@
|
||||
type PerformanceSurveyTargetPolicyInput = {
|
||||
requestedEmployeeIds: unknown
|
||||
currentEmployeeId?: string | null
|
||||
surveyKind: string
|
||||
}
|
||||
|
||||
function normalizeEmployeeIds(value: unknown): string[] {
|
||||
if (!Array.isArray(value)) return []
|
||||
return Array.from(new Set(
|
||||
value
|
||||
.map((item) => String(item || '').trim())
|
||||
.filter(Boolean),
|
||||
))
|
||||
}
|
||||
|
||||
export function getPerformanceSurveyTargetEmployeeIds({
|
||||
requestedEmployeeIds,
|
||||
currentEmployeeId,
|
||||
surveyKind,
|
||||
}: PerformanceSurveyTargetPolicyInput): string[] {
|
||||
const requested = normalizeEmployeeIds(requestedEmployeeIds)
|
||||
if (surveyKind === 'item_course') return requested
|
||||
return normalizeEmployeeIds([currentEmployeeId, ...requested])
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user