diff --git a/frontend/src/api/calc.ts b/frontend/src/api/calc.ts new file mode 100644 index 0000000..6bdb5ad --- /dev/null +++ b/frontend/src/api/calc.ts @@ -0,0 +1,70 @@ +import request from './request' +import type { PageData } from './types' + +/** 绩效台账行(金额/比例一律 str) */ +export interface LedgerRow { + id: number + receipt_id: number | null + calc_version: number + is_active: number + calc_type: number + business_id: number + business_no: string + personnel_id: number + personnel_name: string + role_code: string + customer_type: string + product_category: string + ship_date: string | null + receipt_date: string + payment_days: number | null + rate_text: string + converted_profit: string | null + performance_amount: string | null + barter_applied: number + weight_r: number | null + payment_weight: string | null + payable_amount: string | null + settlement_month: string + is_probation: number + missing_cost: number + snapshot_json: Record | null + calc_reason: string | null +} + +export interface LedgerQueryParams { + pageNum?: number + pageSize?: number + settlement_month?: string + personnel_id?: number + business_id?: number + is_active?: number + calc_type?: number +} + +export function listLedger(params: LedgerQueryParams): Promise> { + return request({ url: '/v1/calc/ledger', method: 'get', params }) +} + +export function getLedger(id: number): Promise { + return request({ url: `/v1/calc/ledger/${id}`, method: 'get' }) +} + +/** 核算回款:返回台账行 + repeated(业务级一次性计提标记) */ +export function calcReceipt(receiptId: number): Promise<{ ledger: LedgerRow; repeated: boolean }> { + return request({ url: `/v1/calc/receipts/${receiptId}/calc`, method: 'post' }) +} + +export function recalcReceipt(receiptId: number, reason: string): Promise { + return request({ + url: `/v1/calc/receipts/${receiptId}/recalc`, + method: 'post', + data: { reason }, + }) +} + +export function batchCalc(receiptIds: number[]): Promise< + { receipt_id: number; ok: boolean; repeated?: boolean; error?: string }[] +> { + return request({ url: '/v1/calc/batch', method: 'post', data: { receipt_ids: receiptIds } }) +} diff --git a/frontend/src/api/settlements.ts b/frontend/src/api/settlements.ts new file mode 100644 index 0000000..6848a87 --- /dev/null +++ b/frontend/src/api/settlements.ts @@ -0,0 +1,105 @@ +import request from './request' +import type { PageData } from './types' + +/** 月结状态:1草稿 2待审批 3已审定(=已锁定)4已驳回 */ +export const SETTLEMENT_STATUS = { + DRAFT: 1, + PENDING: 2, + APPROVED: 3, + REJECTED: 4, +} as const + +export function settlementStatusLabel(status: number): string { + return ( + { 1: '草稿', 2: '待审批', 3: '已审定(锁定)', 4: '已驳回' }[status] ?? String(status) + ) +} + +export function settlementStatusTagType( + status: number, +): 'primary' | 'success' | 'danger' | 'warning' | 'info' { + return ( + ({ 1: 'info', 2: 'primary', 3: 'success', 4: 'danger', 5: 'info' } as const)[status] ?? 'info' + ) +} + +export interface SettlementItem { + id: number + personnel_id: number + personnel_name: string + role_code: string + calc_type: number + performance_sum: string + payable_sum: string + basic_amount: string | null + salary_mode: string | null + net_amount: string | null + is_probation: number + probation_note: string | null +} + +export interface Settlement { + id: number + settlement_month: string + status: number + total_performance: string | null + total_payable: string | null + person_count: number + approval_order_no: string | null + submitter_name: string + locked_at: string | null + locked_by: string | null + create_time: string +} + +export interface SettlementDetail extends Settlement { + items: SettlementItem[] +} + +export interface SettlementQueryParams { + pageNum?: number + pageSize?: number + status?: number +} + +export function listSettlements(params: SettlementQueryParams): Promise> { + return request({ url: '/v1/settlements', method: 'get', params }) +} + +export function getSettlement(id: number): Promise { + return request({ url: `/v1/settlements/${id}`, method: 'get' }) +} + +export function generateSettlement(month: string): Promise { + return request({ url: '/v1/settlements/generate', method: 'post', data: { month } }) +} + +export function submitSettlement(id: number): Promise { + return request({ url: `/v1/settlements/${id}/submit`, method: 'post' }) +} + +/** 调整单(已锁定月份的补发/扣减) */ +export interface AdjustmentRow { + id: number + order_no: string + settlement_month: string + personnel_name: string + direction: number + amount: string + reason: string + status: number +} + +export function createAdjustment(data: { + settlement_month: string + personnel_id: number + direction: number + amount: string + reason: string +}): Promise> { + return request({ url: '/v1/settlements/adjustments', method: 'post', data }) +} + +export function listAdjustments(): Promise { + return request({ url: '/v1/settlements/adjustments', method: 'get' }) +} diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 225ee4b..ecbdebf 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -137,6 +137,20 @@ const routes: RouteRecordRaw[] = [ component: () => import('@/views/business/rating/index.vue'), meta: { title: '客户评级计算器', group: '业务数据', order: 16 }, }, + { + // 绩效工资组(M3 ④):核算台账读登录即可,月结写限管理员与营销后勤 + // (api 层 require_roles 兜底,页面层不再叠加 requiresAdmin) + path: '/calc/ledger', + name: 'calc-ledger', + component: () => import('@/views/calc/ledger/index.vue'), + meta: { title: '绩效台账', group: '绩效工资', order: 17 }, + }, + { + path: '/settlements', + name: 'settlements', + component: () => import('@/views/settlements/index.vue'), + meta: { title: '月度结算', group: '绩效工资', order: 18 }, + }, ], }, // 未匹配路由回首页;若未登录会被守卫拦到登录页 diff --git a/frontend/src/views/calc/ledger/index.vue b/frontend/src/views/calc/ledger/index.vue new file mode 100644 index 0000000..00b635a --- /dev/null +++ b/frontend/src/views/calc/ledger/index.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/frontend/src/views/settlements/index.vue b/frontend/src/views/settlements/index.vue new file mode 100644 index 0000000..66a4807 --- /dev/null +++ b/frontend/src/views/settlements/index.vue @@ -0,0 +1,245 @@ + + + + +