2026-04-25 18:10:45 +08:00
|
|
|
export type Option<T = string> = {
|
|
|
|
|
label: string
|
|
|
|
|
value: T
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const OWNERSHIP_TYPE = {
|
|
|
|
|
major: '专业所',
|
|
|
|
|
comprehensive: '综合所',
|
|
|
|
|
subcontract: '专业分包'
|
|
|
|
|
} as const
|
|
|
|
|
|
|
|
|
|
export const CALCULATION_METHOD = {
|
|
|
|
|
guidancePrice: '指导价法',
|
|
|
|
|
contractPrice: '合同价法',
|
|
|
|
|
virtualOutput: '虚拟产值法'
|
|
|
|
|
} as const
|
|
|
|
|
|
|
|
|
|
export const VIRTUAL_CALCULATION_METHOD = {
|
|
|
|
|
guidancePrice: '指导单价法',
|
|
|
|
|
guidanceTotalPrice: '指导总价法',
|
|
|
|
|
workingDay: '工日法'
|
|
|
|
|
} as const
|
|
|
|
|
|
|
|
|
|
export const DESIGN_STAGE = {
|
|
|
|
|
scheme: '方案',
|
|
|
|
|
constructionDrawing: '施工图',
|
|
|
|
|
schemeAndConstructionDrawing: '方案+施工图'
|
|
|
|
|
} as const
|
|
|
|
|
|
|
|
|
|
export const DESIGN_PART = {
|
|
|
|
|
realEstate: '地上部分',
|
|
|
|
|
underground: '地下部分'
|
|
|
|
|
} as const
|
|
|
|
|
|
|
|
|
|
export const PROJECT_TYPE = {
|
|
|
|
|
building: '建筑工程',
|
|
|
|
|
decoration: '精装工程',
|
|
|
|
|
comprehensive: '综合工程',
|
|
|
|
|
special: '专项设计',
|
|
|
|
|
bim: 'BIM设计',
|
|
|
|
|
other: '其他'
|
|
|
|
|
} as const
|
|
|
|
|
|
|
|
|
|
export const PROJECT_CATEGORY = {
|
|
|
|
|
residential: '住宅',
|
|
|
|
|
publicBuilding: '公建',
|
|
|
|
|
industry: '工业',
|
|
|
|
|
landscape: '园林景观',
|
|
|
|
|
other: '其他'
|
|
|
|
|
} as const
|
|
|
|
|
|
|
|
|
|
export const PROJECT_STATUS = {
|
|
|
|
|
inProgress: '进行中',
|
|
|
|
|
completed: '完成',
|
|
|
|
|
paused: '暂停',
|
|
|
|
|
terminated: '中止'
|
|
|
|
|
} as const
|
|
|
|
|
|
|
|
|
|
export const EMPLOYEE_GENDER = {
|
|
|
|
|
male: '男',
|
|
|
|
|
female: '女'
|
|
|
|
|
} as const
|
|
|
|
|
|
|
|
|
|
export const EMPLOYEE_STATUS = {
|
|
|
|
|
active: '在职',
|
|
|
|
|
resigned: '离职',
|
|
|
|
|
suspended: '停职'
|
|
|
|
|
} as const
|
|
|
|
|
|
|
|
|
|
const normalizeValue = (value: string | undefined, validValues: string[]) =>
|
|
|
|
|
value && validValues.includes(value) ? value : undefined
|
|
|
|
|
|
|
|
|
|
const findLabel = <T>(options: Array<Option<T>>, value?: T | string) =>
|
|
|
|
|
options.find((item) => String(item.value) === String(value))?.label
|
|
|
|
|
|
|
|
|
|
export const normalizeOwnershipType = (value?: string) =>
|
|
|
|
|
normalizeValue(value, Object.values(OWNERSHIP_TYPE))
|
|
|
|
|
|
|
|
|
|
export const normalizeCalculationMethod = (value?: string) =>
|
|
|
|
|
normalizeValue(value, Object.values(CALCULATION_METHOD))
|
|
|
|
|
|
|
|
|
|
export const normalizeVirtualCalculationMethod = (value?: string) =>
|
|
|
|
|
normalizeValue(value, Object.values(VIRTUAL_CALCULATION_METHOD))
|
|
|
|
|
|
|
|
|
|
export const normalizeDesignStage = (value?: string) =>
|
|
|
|
|
normalizeValue(value, Object.values(DESIGN_STAGE))
|
|
|
|
|
|
|
|
|
|
export const normalizeDesignPart = (value?: string) =>
|
|
|
|
|
normalizeValue(value, Object.values(DESIGN_PART))
|
|
|
|
|
|
|
|
|
|
export const normalizeProjectType = (value?: string) =>
|
|
|
|
|
normalizeValue(value, Object.values(PROJECT_TYPE))
|
|
|
|
|
|
|
|
|
|
export const normalizeProjectCategory = (value?: string) =>
|
|
|
|
|
normalizeValue(value, Object.values(PROJECT_CATEGORY))
|
|
|
|
|
|
|
|
|
|
export const normalizeProjectStatus = (value?: string) =>
|
|
|
|
|
normalizeValue(value, Object.values(PROJECT_STATUS))
|
|
|
|
|
|
|
|
|
|
export const OWNERSHIP_TYPE_OPTIONS: Option[] = [
|
|
|
|
|
{ label: OWNERSHIP_TYPE.major, value: OWNERSHIP_TYPE.major },
|
|
|
|
|
{ label: OWNERSHIP_TYPE.comprehensive, value: OWNERSHIP_TYPE.comprehensive },
|
|
|
|
|
{ label: OWNERSHIP_TYPE.subcontract, value: OWNERSHIP_TYPE.subcontract }
|
2026-04-17 18:17:42 +08:00
|
|
|
]
|
|
|
|
|
|
2026-04-25 18:10:45 +08:00
|
|
|
export const CALCULATION_METHOD_OPTIONS: Option[] = [
|
|
|
|
|
{ label: CALCULATION_METHOD.guidancePrice, value: CALCULATION_METHOD.guidancePrice },
|
|
|
|
|
{ label: CALCULATION_METHOD.contractPrice, value: CALCULATION_METHOD.contractPrice },
|
|
|
|
|
{ label: CALCULATION_METHOD.virtualOutput, value: CALCULATION_METHOD.virtualOutput }
|
2026-04-17 18:17:42 +08:00
|
|
|
]
|
|
|
|
|
|
2026-04-25 18:10:45 +08:00
|
|
|
export const VIRTUAL_CALCULATION_METHOD_OPTIONS: Option[] = [
|
|
|
|
|
{
|
|
|
|
|
label: VIRTUAL_CALCULATION_METHOD.guidancePrice,
|
|
|
|
|
value: VIRTUAL_CALCULATION_METHOD.guidancePrice
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: VIRTUAL_CALCULATION_METHOD.guidanceTotalPrice,
|
|
|
|
|
value: VIRTUAL_CALCULATION_METHOD.guidanceTotalPrice
|
|
|
|
|
},
|
|
|
|
|
{ label: VIRTUAL_CALCULATION_METHOD.workingDay, value: VIRTUAL_CALCULATION_METHOD.workingDay }
|
2026-04-17 18:17:42 +08:00
|
|
|
]
|
|
|
|
|
|
2026-04-25 18:10:45 +08:00
|
|
|
export const DESIGN_STAGE_OPTIONS: Option[] = [
|
|
|
|
|
{ label: DESIGN_STAGE.scheme, value: DESIGN_STAGE.scheme },
|
|
|
|
|
{ label: DESIGN_STAGE.constructionDrawing, value: DESIGN_STAGE.constructionDrawing },
|
|
|
|
|
{
|
|
|
|
|
label: DESIGN_STAGE.schemeAndConstructionDrawing,
|
|
|
|
|
value: DESIGN_STAGE.schemeAndConstructionDrawing
|
|
|
|
|
}
|
2026-04-17 18:17:42 +08:00
|
|
|
]
|
|
|
|
|
|
2026-04-25 18:10:45 +08:00
|
|
|
export const DESIGN_PART_OPTIONS: Option[] = [
|
|
|
|
|
{ label: DESIGN_PART.realEstate, value: DESIGN_PART.realEstate },
|
|
|
|
|
{ label: DESIGN_PART.underground, value: DESIGN_PART.underground }
|
2026-04-17 18:17:42 +08:00
|
|
|
]
|
|
|
|
|
|
2026-04-25 18:10:45 +08:00
|
|
|
export const CONTRACT_SIGN_OPTIONS: Option<boolean>[] = [
|
|
|
|
|
{ label: '已签约', value: true },
|
|
|
|
|
{ label: '未签约', value: false }
|
2026-04-17 18:17:42 +08:00
|
|
|
]
|
|
|
|
|
|
2026-04-25 18:10:45 +08:00
|
|
|
export const PROJECT_TYPE_OPTIONS: Option[] = [
|
|
|
|
|
{ label: PROJECT_TYPE.building, value: PROJECT_TYPE.building },
|
|
|
|
|
{ label: PROJECT_TYPE.decoration, value: PROJECT_TYPE.decoration },
|
|
|
|
|
{ label: PROJECT_TYPE.comprehensive, value: PROJECT_TYPE.comprehensive },
|
|
|
|
|
{ label: PROJECT_TYPE.special, value: PROJECT_TYPE.special },
|
|
|
|
|
{ label: PROJECT_TYPE.bim, value: PROJECT_TYPE.bim },
|
|
|
|
|
{ label: PROJECT_TYPE.other, value: PROJECT_TYPE.other }
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
export const PROJECT_CATEGORY_OPTIONS: Option[] = [
|
|
|
|
|
{ label: PROJECT_CATEGORY.residential, value: PROJECT_CATEGORY.residential },
|
|
|
|
|
{ label: PROJECT_CATEGORY.publicBuilding, value: PROJECT_CATEGORY.publicBuilding },
|
|
|
|
|
{ label: PROJECT_CATEGORY.industry, value: PROJECT_CATEGORY.industry },
|
|
|
|
|
{ label: PROJECT_CATEGORY.landscape, value: PROJECT_CATEGORY.landscape },
|
|
|
|
|
{ label: PROJECT_CATEGORY.other, value: PROJECT_CATEGORY.other }
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
export const PROJECT_STATUS_OPTIONS: Option[] = [
|
|
|
|
|
{ label: PROJECT_STATUS.inProgress, value: PROJECT_STATUS.inProgress },
|
|
|
|
|
{ label: PROJECT_STATUS.completed, value: PROJECT_STATUS.completed },
|
|
|
|
|
{ label: PROJECT_STATUS.paused, value: PROJECT_STATUS.paused },
|
|
|
|
|
{ label: PROJECT_STATUS.terminated, value: PROJECT_STATUS.terminated }
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
export const EMPLOYEE_GENDER_OPTIONS: Option[] = [
|
|
|
|
|
{ label: EMPLOYEE_GENDER.male, value: EMPLOYEE_GENDER.male },
|
|
|
|
|
{ label: EMPLOYEE_GENDER.female, value: EMPLOYEE_GENDER.female }
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
export const EMPLOYEE_STATUS_OPTIONS: Option[] = [
|
|
|
|
|
{ label: EMPLOYEE_STATUS.active, value: EMPLOYEE_STATUS.active },
|
|
|
|
|
{ label: EMPLOYEE_STATUS.resigned, value: EMPLOYEE_STATUS.resigned },
|
|
|
|
|
{ label: EMPLOYEE_STATUS.suspended, value: EMPLOYEE_STATUS.suspended }
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
export const EMPLOYEE_JOB_TITLE = {
|
|
|
|
|
junior: '初级',
|
|
|
|
|
intermediate: '中级',
|
|
|
|
|
viceSenior: '副高级',
|
|
|
|
|
senior: '正高级'
|
|
|
|
|
} as const
|
|
|
|
|
|
|
|
|
|
export const EMPLOYEE_JOB_TITLE_OPTIONS: Option[] = [
|
|
|
|
|
{ label: EMPLOYEE_JOB_TITLE.junior, value: EMPLOYEE_JOB_TITLE.junior },
|
|
|
|
|
{ label: EMPLOYEE_JOB_TITLE.intermediate, value: EMPLOYEE_JOB_TITLE.intermediate },
|
|
|
|
|
{ label: EMPLOYEE_JOB_TITLE.viceSenior, value: EMPLOYEE_JOB_TITLE.viceSenior },
|
|
|
|
|
{ label: EMPLOYEE_JOB_TITLE.senior, value: EMPLOYEE_JOB_TITLE.senior }
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
export const DEFAULT_INNOVATION_OUTPUT_RATE = 0.01
|
|
|
|
|
export const DEFAULT_WORKING_DAY_UNIT_PRICE = 1000
|
|
|
|
|
|
|
|
|
|
export const QUARTER_OPTIONS: Option<number>[] = [
|
2026-04-17 18:17:42 +08:00
|
|
|
{ label: '一季度', value: 1 },
|
|
|
|
|
{ label: '二季度', value: 2 },
|
|
|
|
|
{ label: '三季度', value: 3 },
|
|
|
|
|
{ label: '四季度', value: 4 }
|
|
|
|
|
]
|
|
|
|
|
|
2026-04-25 18:10:45 +08:00
|
|
|
export const OUTPUT_SPLIT_SPECIALTY_OPTIONS: Option[] = [
|
2026-04-17 18:17:42 +08:00
|
|
|
{ label: '建筑', value: 'arch' },
|
2026-04-25 18:10:45 +08:00
|
|
|
{ label: '装饰', value: 'decor' },
|
2026-04-17 18:17:42 +08:00
|
|
|
{ label: '结构', value: 'struct' },
|
2026-04-25 18:10:45 +08:00
|
|
|
{ label: '给排水', value: 'water' },
|
2026-04-17 18:17:42 +08:00
|
|
|
{ label: '电气', value: 'elec' },
|
|
|
|
|
{ label: '暖通', value: 'hvac' },
|
|
|
|
|
{ label: '数字化设计', value: 'digital' }
|
|
|
|
|
]
|
|
|
|
|
|
2026-04-25 18:10:45 +08:00
|
|
|
export const SPECIALTY_ROLE_OPTIONS: Option[] = [
|
2026-04-17 18:17:42 +08:00
|
|
|
{ label: '专业负责人', value: 'director' },
|
|
|
|
|
{ label: '校对', value: 'check' },
|
|
|
|
|
{ label: '审核', value: 'review' },
|
|
|
|
|
{ label: '审定', value: 'approve' },
|
|
|
|
|
{ label: '设计', value: 'design' }
|
|
|
|
|
]
|
|
|
|
|
|
2026-04-25 18:10:45 +08:00
|
|
|
export const isMajorOwnership = (value?: string) =>
|
|
|
|
|
normalizeOwnershipType(value) === OWNERSHIP_TYPE.major
|
|
|
|
|
export const isComprehensiveOwnership = (value?: string) =>
|
|
|
|
|
normalizeOwnershipType(value) === OWNERSHIP_TYPE.comprehensive
|
|
|
|
|
export const isSubcontractOwnership = (value?: string) =>
|
|
|
|
|
normalizeOwnershipType(value) === OWNERSHIP_TYPE.subcontract
|
|
|
|
|
|
|
|
|
|
export const isGuidancePriceMethod = (value?: string) =>
|
|
|
|
|
normalizeCalculationMethod(value) === CALCULATION_METHOD.guidancePrice
|
|
|
|
|
export const isContractPriceMethod = (value?: string) =>
|
|
|
|
|
normalizeCalculationMethod(value) === CALCULATION_METHOD.contractPrice
|
|
|
|
|
export const isVirtualOutputMethod = (value?: string) =>
|
|
|
|
|
normalizeCalculationMethod(value) === CALCULATION_METHOD.virtualOutput
|
|
|
|
|
|
|
|
|
|
export const isVirtualGuidanceMethod = (value?: string) =>
|
|
|
|
|
normalizeVirtualCalculationMethod(value) === VIRTUAL_CALCULATION_METHOD.guidancePrice
|
|
|
|
|
export const isVirtualGuidanceTotalPriceMethod = (value?: string) =>
|
|
|
|
|
normalizeVirtualCalculationMethod(value) === VIRTUAL_CALCULATION_METHOD.guidanceTotalPrice
|
|
|
|
|
export const isWorkingDayMethod = (value?: string) =>
|
|
|
|
|
normalizeVirtualCalculationMethod(value) === VIRTUAL_CALCULATION_METHOD.workingDay
|
|
|
|
|
|
|
|
|
|
export const getOwnershipTypeLabel = (value?: string) =>
|
|
|
|
|
findLabel(OWNERSHIP_TYPE_OPTIONS, normalizeOwnershipType(value)) || '-'
|
|
|
|
|
|
|
|
|
|
export const getCalculationMethodLabel = (value?: string) =>
|
|
|
|
|
findLabel(CALCULATION_METHOD_OPTIONS, normalizeCalculationMethod(value)) || '-'
|
|
|
|
|
|
|
|
|
|
export const getProjectTypeLabel = (value?: string) =>
|
|
|
|
|
findLabel(PROJECT_TYPE_OPTIONS, normalizeProjectType(value)) || '-'
|
|
|
|
|
|
|
|
|
|
export const getProjectCategoryLabel = (value?: string) =>
|
|
|
|
|
findLabel(PROJECT_CATEGORY_OPTIONS, normalizeProjectCategory(value)) || '-'
|
|
|
|
|
|
|
|
|
|
export const getProjectStatusLabel = (value?: string) =>
|
|
|
|
|
findLabel(PROJECT_STATUS_OPTIONS, normalizeProjectStatus(value)) || '-'
|
|
|
|
|
|
|
|
|
|
export const getDesignStageLabel = (value?: string) =>
|
|
|
|
|
findLabel(DESIGN_STAGE_OPTIONS, normalizeDesignStage(value)) || '-'
|
|
|
|
|
|
|
|
|
|
export const getDesignPartLabel = (value?: string) =>
|
|
|
|
|
findLabel(DESIGN_PART_OPTIONS, normalizeDesignPart(value)) || '-'
|
|
|
|
|
|
|
|
|
|
export const getVirtualCalculationMethodLabel = (value?: string) =>
|
|
|
|
|
findLabel(VIRTUAL_CALCULATION_METHOD_OPTIONS, normalizeVirtualCalculationMethod(value)) || '-'
|
2026-04-17 18:17:42 +08:00
|
|
|
|
|
|
|
|
export const getCalculationRatioLabel = (ownershipType?: string) => {
|
|
|
|
|
if (isComprehensiveOwnership(ownershipType)) {
|
|
|
|
|
return '协作比例'
|
|
|
|
|
}
|
|
|
|
|
if (isSubcontractOwnership(ownershipType)) {
|
|
|
|
|
return '分包比例'
|
|
|
|
|
}
|
|
|
|
|
return '比例'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const getReviewOutsourceDefaultPercent = (
|
|
|
|
|
ownershipType?: string,
|
|
|
|
|
reviewOutsourceFlag?: boolean
|
|
|
|
|
) => {
|
|
|
|
|
if (!reviewOutsourceFlag) {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
if (isMajorOwnership(ownershipType)) {
|
|
|
|
|
return 6
|
|
|
|
|
}
|
|
|
|
|
return 25
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const getCalculationRatioDefaultPercent = (ownershipType?: string) => {
|
|
|
|
|
if (isComprehensiveOwnership(ownershipType)) {
|
|
|
|
|
return 8
|
|
|
|
|
}
|
|
|
|
|
if (isSubcontractOwnership(ownershipType)) {
|
|
|
|
|
return 4
|
|
|
|
|
}
|
|
|
|
|
return undefined
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const toPercentValue = (value?: number | string | null, digits = 2) => {
|
|
|
|
|
if (value === undefined || value === null || value === '') {
|
|
|
|
|
return undefined
|
|
|
|
|
}
|
|
|
|
|
const numericValue = Number(value)
|
|
|
|
|
if (Number.isNaN(numericValue)) {
|
|
|
|
|
return undefined
|
|
|
|
|
}
|
|
|
|
|
return Number((numericValue * 100).toFixed(digits))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const fromPercentValue = (value?: number | string | null, digits = 4) => {
|
|
|
|
|
if (value === undefined || value === null || value === '') {
|
|
|
|
|
return undefined
|
|
|
|
|
}
|
|
|
|
|
const numericValue = Number(value)
|
|
|
|
|
if (Number.isNaN(numericValue)) {
|
|
|
|
|
return undefined
|
|
|
|
|
}
|
|
|
|
|
return Number((numericValue / 100).toFixed(digits))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const formatAmountText = (value?: number | string | null) => {
|
|
|
|
|
if (value === undefined || value === null || value === '') {
|
|
|
|
|
return '0.00'
|
|
|
|
|
}
|
|
|
|
|
const numericValue = Number(value)
|
|
|
|
|
if (Number.isNaN(numericValue)) {
|
|
|
|
|
return '0.00'
|
|
|
|
|
}
|
|
|
|
|
return numericValue.toFixed(2)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const formatAreaText = (value?: number | string | null) => `${formatAmountText(value)} ㎡`
|
|
|
|
|
|
|
|
|
|
export const formatPercentText = (value?: number | string | null, digits = 2) => {
|
|
|
|
|
const percentValue = toPercentValue(value, digits)
|
|
|
|
|
return `${(percentValue ?? 0).toFixed(digits)}%`
|
|
|
|
|
}
|