feat(business): 前端业务/发货/回款管理三页

- 业务页:明细行编辑器、详情抽屉五分区(含状态时间线与毛利润卡片)
- 回款页:业务→发货批次级联指定(可清空)、已核算行禁改禁删
- 金额/比率字符串直出;毛利润卡片注明不含运费口径

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
liuwenxinandClaude Code committed 2026-10-13 12:50:10 +08:00
1 parent 5207d14daa
commit 06cdf4141f
7 files changed
+2848

No files matched your search

+294
View File
@@ -0,0 +1,294 @@
import { request } from './request'
import type { PageData } from './types'
/**
* 业务产品明细行(对应后端 `BusinessItemOut`)。
*
* 数量与金额一律字符串直出 —— 金额红线:不经 float、不 toFixed,
* Decimal 运算收敛在后端 service 层。
*/
export interface BusinessItemRow {
id: number
product_id: number | null
product_name: string
spec_model: string | null
unit: string | null
/** municipal_water 市政水利 / rail_transit 轨道交通 —— 行级折算锚点 */
product_category: string
/** 数字字符串,>0 */
quantity: string
sale_price: string | null
/** 行销售额 = 数量 × 销售单价,后端计算固化 */
sale_amount: string
cost_amount: string | null
guided_price: string | null
remark: string | null
}
/** 业务列表项(对应后端 `BusinessOut`;四个汇总金额均为字符串直出) */
export interface BusinessItem {
id: number
business_no: string
customer_id: number
customer_name: string | null
/** ISO 串(YYYY-MM-DD) */
business_date: string | null
/** 1执行中 2已完成 3已终止 */
status: number
owner_personnel_id: number | null
owner_personnel_name: string | null
assistant_personnel_id: number | null
assistant_personnel_name: string | null
/** 特殊业务标记:1是 0否 */
is_special: number
special_note: string | null
total_sale_amount: string | null
total_cost_amount: string | null
total_ship_amount: string | null
total_received_amount: string | null
total_freight_amount: string | null
remark: string | null
}
/** 业务详情:附产品明细行(编辑弹窗一次回显) */
export interface BusinessDetail extends BusinessItem {
items: BusinessItemRow[]
}
export interface BusinessQueryParams {
pageNum?: number
pageSize?: number
/** 业务编号/公司名称,模糊匹配 */
keyword?: string
status?: number
customer_id?: number
owner_personnel_id?: number
/** 业务日期起(含),ISO 串 */
date_from?: string
/** 业务日期止(含),ISO 串 */
date_to?: string
}
/** 明细行入参(product_id 空 = 自由录入;金额/数量为数字字符串) */
export interface BusinessItemParams {
product_id?: number | null
product_name: string
spec_model?: string | null
unit?: string | null
product_category: string
quantity: string
sale_price?: string | null
cost_amount?: string | null
guided_price?: string | null
remark?: string | null
}
/**
* 创建业务:主表信息 + 明细行(≥1)。
*
* is_special=1 时 special_note 必填(后端校验,前端同步拦截)。
*/
export interface BusinessCreateParams {
customer_id: number
business_date?: string | null
owner_personnel_id?: number | null
assistant_personnel_id?: number | null
is_special?: number
special_note?: string | null
items: BusinessItemParams[]
remark?: string | null
}
/**
* 编辑业务:明细全量替换并重算汇总。
*
* 不含归属字段 —— 归属调整统一走 `PUT /{id}/ownership`(唯一写归属
* 留痕的入口),编辑表单即使提交归属也会被后端忽略。
*/
export interface BusinessUpdateParams {
customer_id: number
business_date?: string | null
is_special?: number
special_note?: string | null
items: BusinessItemParams[]
remark?: string | null
}
/** 状态流转入参:执行中 → 2已完成 / 3已终止;终止时 reason 必填 */
export interface BusinessStatusParams {
status: number
reason?: string | null
}
/** 归属变更入参(新负责/新协助;前后值写入留痕,不回溯历史) */
export interface BusinessOwnershipParams {
new_owner_personnel_id?: number | null
new_assistant_personnel_id?: number | null
reason?: string | null
}
/** 业务归属变更留痕条目(对应后端 `OwnershipChangeLogOut`;姓名可随人员消失,ID 固化) */
export interface OwnershipLogItem {
id: number
business_id: number
old_owner_personnel_id: number | null
new_owner_personnel_id: number | null
old_assistant_personnel_id: number | null
new_assistant_personnel_id: number | null
old_owner_name: string | null
new_owner_name: string | null
change_reason: string | null
operator_id: number | null
operator_name: string
create_time: string
}
/** 业务状态流转记录(对应后端 `BusinessStatusLogOut`;创建首条 old=new=1) */
export interface StatusLogItem {
id: number
business_id: number
old_status: number
new_status: number
change_reason: string | null
operator_id: number | null
operator_name: string
create_time: string
}
/** 毛利润视图行(对应后端 `GrossProfitRowOut`);成本缺失行派生列为 null */
export interface GrossProfitRowItem {
item_id: number
product_category: string
product_name: string
spec_model: string | null
quantity: string
sale_amount: string
cost_amount: string | null
profit_amount: string | null
profit_rate: string | null
threshold: string
converted_profit: string | null
missing_reason: string | null
}
/** 按产品类别的毛利润汇总(成本缺失行不计入聚合) */
export interface GrossProfitCategory {
product_category: string
sale_amount: string
cost_amount: string
profit_amount: string
/** 分数字符串原样(如 "997/1000"),不做数值化展示 */
profit_rate: string
threshold: string
converted_profit: string
}
/** 毛利润数据视图(对应后端 `GrossProfitOut`) */
export interface GrossProfitView {
business_id: number
/** 参数取值时点(默认今日),ISO 串 */
as_of: string
rows: GrossProfitRowItem[]
categories: GrossProfitCategory[]
missing_count: number
note: string
}
/** 业务状态下拉项(与后端值域一致;执行中=1 已完成=2 已终止=3) */
export const BUSINESS_STATUS_OPTIONS = [
{ label: '执行中', value: 1 },
{ label: '已完成', value: 2 },
{ label: '已终止', value: 3 },
] as const
/** 状态编码 → 展示名 */
export function businessStatusLabel(status: number): string {
return BUSINESS_STATUS_OPTIONS.find((o) => o.value === status)?.label ?? `状态${status}`
}
/** 状态编码 → 标签类型(三色:执行中 primary / 已完成 success / 已终止 danger) */
export function businessStatusTagType(status: number): 'primary' | 'success' | 'danger' | 'info' {
if (status === 2) return 'success'
if (status === 3) return 'danger'
return 'primary'
}
/** 分页查询业务(编号/公司名称模糊,可按状态/客户/归属/日期范围筛选) */
export function listBusinesses(params: BusinessQueryParams): Promise<PageData<BusinessItem>> {
return request({ url: '/v1/business/businesses', method: 'get', params })
}
/** 创建业务(编号自动生成,写首条状态留痕) */
export function createBusiness(data: BusinessCreateParams): Promise<BusinessItem> {
return request({ url: '/v1/business/businesses', method: 'post', data })
}
/** 业务详情(附产品明细行) */
export function getBusinessDetail(businessId: number): Promise<BusinessDetail> {
return request({ url: `/v1/business/businesses/${businessId}`, method: 'get' })
}
/** 编辑业务(明细全量替换;归属不在此端点调整) */
export function updateBusiness(
businessId: number,
data: BusinessUpdateParams,
): Promise<BusinessItem> {
return request({ url: `/v1/business/businesses/${businessId}`, method: 'put', data })
}
/** 删除业务(有发货或回款记录时后端拒绝,应改为已终止) */
export function deleteBusiness(businessId: number): Promise<null> {
return request({ url: `/v1/business/businesses/${businessId}`, method: 'delete' })
}
/** 状态流转(终态不可再变更;终止必填原因) */
export function changeBusinessStatus(
businessId: number,
data: BusinessStatusParams,
): Promise<BusinessItem> {
return request({ url: `/v1/business/businesses/${businessId}/status`, method: 'put', data })
}
/** 调整负责/协助营销人员(同事务写归属变更留痕) */
export function changeBusinessOwnership(
businessId: number,
data: BusinessOwnershipParams,
): Promise<BusinessItem> {
return request({ url: `/v1/business/businesses/${businessId}/ownership`, method: 'put', data })
}
/** 某业务的归属变更留痕(分页,按变更时间倒序) */
export function listOwnershipLogs(
businessId: number,
params: { pageNum?: number; pageSize?: number },
): Promise<PageData<OwnershipLogItem>> {
return request({
url: `/v1/business/businesses/${businessId}/ownership-logs`,
method: 'get',
params,
})
}
/** 某业务的状态流转时间线(分页,按时间正序,从创建起展示) */
export function listStatusLogs(
businessId: number,
params: { pageNum?: number; pageSize?: number },
): Promise<PageData<StatusLogItem>> {
return request({
url: `/v1/business/businesses/${businessId}/status-logs`,
method: 'get',
params,
})
}
/** 毛利润数据视图(as_of 缺省=今日;阈值按该时点参数快照) */
export function getGrossProfit(
businessId: number,
asOf?: string,
): Promise<GrossProfitView> {
return request({
url: `/v1/business/businesses/${businessId}/gross-profit`,
method: 'get',
params: asOf ? { as_of: asOf } : undefined,
})
}