feat(cost-center): 成本中心与商品映射开发草案 (含文档与订单页调整 WIP)
This commit is contained in:
@@ -21,6 +21,7 @@ import WorkerLayout from '@/layouts/WorkerLayout'
|
||||
|
||||
const AdminDashboardPage = lazy(() => import('@/pages/admin/AdminDashboardPage'))
|
||||
const AdminFinancePage = lazy(() => import('@/pages/admin/AdminFinancePage'))
|
||||
const AdminCostCenterPage = lazy(() => import('@/pages/admin/AdminCostCenterPage'))
|
||||
const AdminFundPage = lazy(() => import('@/pages/admin/AdminFundPage'))
|
||||
const AdminAuditLogsPage = lazy(() => import('@/pages/admin/AdminAuditLogsPage'))
|
||||
const AdminKuaishouIndustryPage = lazy(() => import('@/pages/admin/AdminKuaishouIndustryPage'))
|
||||
@@ -145,6 +146,7 @@ function AdminApplication() {
|
||||
</Route>
|
||||
<Route element={<RequireRole roles={['admin', 'operator', 'finance']} />}>
|
||||
<Route path="finance" element={<AdminFinancePage />} />
|
||||
<Route path="cost-center" element={<AdminCostCenterPage />} />
|
||||
</Route>
|
||||
<Route element={<RequireRole roles={['admin', 'operator', 'support', 'finance']} />}>
|
||||
<Route path="fund" element={<AdminFundPage />} />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
AuditOutlined,
|
||||
AccountBookOutlined,
|
||||
ApartmentOutlined,
|
||||
BellOutlined,
|
||||
BugOutlined,
|
||||
DashboardOutlined,
|
||||
@@ -131,6 +132,7 @@ export default function AdminLayout() {
|
||||
role === 'finance'
|
||||
? [
|
||||
{ key: '/admin/finance', icon: <AccountBookOutlined />, label: '财务' },
|
||||
{ key: '/admin/cost-center', icon: <ApartmentOutlined />, label: '商品与成本' },
|
||||
{ key: '/admin/fund', icon: <WalletOutlined />, label: '备用金' },
|
||||
{ key: '/admin/worker-platform', icon: <TrophyOutlined />, label: '资金审核' },
|
||||
]
|
||||
@@ -144,7 +146,14 @@ export default function AdminLayout() {
|
||||
},
|
||||
...(role === 'support'
|
||||
? []
|
||||
: [{ key: '/admin/finance', icon: <AccountBookOutlined />, label: '财务' }]),
|
||||
: [
|
||||
{ key: '/admin/finance', icon: <AccountBookOutlined />, label: '财务' },
|
||||
{
|
||||
key: '/admin/cost-center',
|
||||
icon: <ApartmentOutlined />,
|
||||
label: '商品与成本',
|
||||
},
|
||||
]),
|
||||
{ key: '/admin/tasks', icon: <UnorderedListOutlined />, label: '任务' },
|
||||
{ key: '/admin/worker-platform', icon: <TrophyOutlined />, label: '接单平台' },
|
||||
{
|
||||
@@ -747,6 +756,7 @@ function playNotificationSound(kind: NotificationAlertKind) {
|
||||
function resolveSelectedKey(pathname: string) {
|
||||
if (pathname.startsWith('/admin/orders')) return '/admin/orders'
|
||||
if (pathname.startsWith('/admin/finance')) return '/admin/finance'
|
||||
if (pathname.startsWith('/admin/cost-center')) return '/admin/cost-center'
|
||||
if (pathname.startsWith('/admin/tasks')) return '/admin/tasks'
|
||||
if (pathname.startsWith('/admin/worker-platform')) return '/admin/worker-platform'
|
||||
if (pathname.startsWith('/admin/kuaishou-industry')) return '/admin/kuaishou-industry'
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -55,15 +55,6 @@ type OrderFilterForm = {
|
||||
dateRange?: unknown
|
||||
}
|
||||
|
||||
/** 默认展示近 7 天(含今天)。 */
|
||||
function resolveDefaultOrderDateRange() {
|
||||
const today = dayjs()
|
||||
return {
|
||||
dateFrom: today.subtract(6, 'day').format('YYYY-MM-DD'),
|
||||
dateTo: today.format('YYYY-MM-DD'),
|
||||
}
|
||||
}
|
||||
|
||||
export default function AdminOrdersPage() {
|
||||
const navigate = useNavigate()
|
||||
const { message } = App.useApp()
|
||||
@@ -72,20 +63,6 @@ export default function AdminOrdersPage() {
|
||||
const page = Number(searchParams.get('page') || 1) || 1
|
||||
const pageSize = resolveAdminPageSize(searchParams.get('pageSize'), ADMIN_DEFAULT_PAGE_SIZE)
|
||||
|
||||
// 首次进入(仅有分页参数)时写入默认近 7 天,避免一把拉出全库。
|
||||
useEffect(() => {
|
||||
const meaningfulKeys = [...searchParams.keys()].filter(
|
||||
(key) => key !== 'page' && key !== 'pageSize',
|
||||
)
|
||||
if (meaningfulKeys.length > 0) {
|
||||
return
|
||||
}
|
||||
const { dateFrom, dateTo } = resolveDefaultOrderDateRange()
|
||||
setSearchParams(cleanParams({ page: 1, pageSize, dateFrom, dateTo }), { replace: true })
|
||||
// 仅首屏引导默认日期,避免与后续筛选互相打架。
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
const filters = {
|
||||
keyword: searchParams.get('keyword') || '',
|
||||
provider: searchParams.get('provider') || '',
|
||||
@@ -111,17 +88,9 @@ export default function AdminOrdersPage() {
|
||||
],
|
||||
)
|
||||
|
||||
// 等待默认日期写入 URL 后再请求,避免首屏无日期打出全库。
|
||||
const hasBootstrappedFilters =
|
||||
Boolean(filters.dateFrom && filters.dateTo) ||
|
||||
Boolean(filters.keyword) ||
|
||||
searchParams.get('allTime') === '1' ||
|
||||
Boolean(filters.provider || filters.executorKey || filters.payStatus || filters.taskStatus)
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: ['admin-orders', queryParams],
|
||||
queryFn: () => fetchAdminOrders(queryParams),
|
||||
enabled: hasBootstrappedFilters,
|
||||
})
|
||||
const data = query.data?.data
|
||||
|
||||
@@ -410,7 +379,6 @@ export default function AdminOrdersPage() {
|
||||
// 有关键词时不强制日期(后端也会忽略日期);清空日期且无关键词则查全部时间。
|
||||
const dateFrom = rawFrom || ''
|
||||
const dateTo = rawTo || ''
|
||||
const allTime = !dateFrom && !dateTo && !keyword ? '1' : ''
|
||||
|
||||
setSearchParams(
|
||||
cleanParams({
|
||||
@@ -418,7 +386,6 @@ export default function AdminOrdersPage() {
|
||||
keyword,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
allTime,
|
||||
page: 1,
|
||||
pageSize,
|
||||
}),
|
||||
@@ -426,16 +393,15 @@ export default function AdminOrdersPage() {
|
||||
}
|
||||
|
||||
function resetFilters() {
|
||||
const { dateFrom, dateTo } = resolveDefaultOrderDateRange()
|
||||
form.setFieldsValue({
|
||||
keyword: undefined,
|
||||
provider: undefined,
|
||||
executorKey: undefined,
|
||||
payStatus: undefined,
|
||||
taskStatus: undefined,
|
||||
dateRange: [dayjs(dateFrom), dayjs(dateTo)],
|
||||
dateRange: undefined,
|
||||
})
|
||||
setSearchParams(cleanParams({ page: 1, pageSize, dateFrom, dateTo }))
|
||||
setSearchParams(cleanParams({ page: 1, pageSize }))
|
||||
}
|
||||
|
||||
const currentSegmentedProvider = filters.provider || ''
|
||||
@@ -537,7 +503,7 @@ export default function AdminOrdersPage() {
|
||||
<Card>
|
||||
<Table<AdminOrderListItem>
|
||||
rowKey="orderId"
|
||||
loading={query.isLoading || query.isFetching || !hasBootstrappedFilters}
|
||||
loading={query.isLoading || query.isFetching}
|
||||
columns={columns}
|
||||
dataSource={data?.items || []}
|
||||
scroll={{ x: 960 }}
|
||||
@@ -549,7 +515,6 @@ export default function AdminOrdersPage() {
|
||||
setSearchParams(
|
||||
cleanParams({
|
||||
...filters,
|
||||
allTime: searchParams.get('allTime') || '',
|
||||
page: nextPage,
|
||||
pageSize: nextPageSize,
|
||||
}),
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
import { apiGet, apiPatch, apiPost } from '@/lib/http'
|
||||
import type {
|
||||
ChannelProductMapping,
|
||||
ChannelProductMappingListResult,
|
||||
CostCenterOptions,
|
||||
CostCenterOverview,
|
||||
CostException,
|
||||
CostExceptionListResult,
|
||||
CostItem,
|
||||
CostItemListResult,
|
||||
CostPreview,
|
||||
CostVersion,
|
||||
CostVersionListResult,
|
||||
ProductCatalogItem,
|
||||
ProductCatalogListResult,
|
||||
TemplateProductBootstrapPreview,
|
||||
TemplateProductBootstrapResult,
|
||||
} from '@/types/admin'
|
||||
|
||||
export function fetchAdminCostCenterOverview() {
|
||||
return apiGet<CostCenterOverview>('/api/v1/admin/cost-center/overview')
|
||||
}
|
||||
|
||||
export function fetchAdminCostCenterOptions() {
|
||||
return apiGet<CostCenterOptions>('/api/v1/admin/cost-center/options')
|
||||
}
|
||||
|
||||
export function previewAdminTemplateProductBootstrap() {
|
||||
return apiGet<TemplateProductBootstrapPreview>(
|
||||
'/api/v1/admin/cost-center/template-bootstrap/preview',
|
||||
)
|
||||
}
|
||||
|
||||
export function applyAdminTemplateProductBootstrap() {
|
||||
return apiPost<TemplateProductBootstrapResult>(
|
||||
'/api/v1/admin/cost-center/template-bootstrap/apply',
|
||||
{ repair: true },
|
||||
)
|
||||
}
|
||||
|
||||
export function fetchAdminProductCatalog(params?: Record<string, unknown>) {
|
||||
return apiGet<ProductCatalogListResult>('/api/v1/admin/cost-center/products', params)
|
||||
}
|
||||
|
||||
export function createAdminProductCatalog(payload: Record<string, unknown>) {
|
||||
return apiPost<{ product: ProductCatalogItem }>('/api/v1/admin/cost-center/products', payload)
|
||||
}
|
||||
|
||||
export function updateAdminProductCatalog(productId: number, payload: Record<string, unknown>) {
|
||||
return apiPatch<{ product: ProductCatalogItem }>(
|
||||
`/api/v1/admin/cost-center/products/${productId}`,
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
export function fetchAdminChannelProductMappings(params?: Record<string, unknown>) {
|
||||
return apiGet<ChannelProductMappingListResult>('/api/v1/admin/cost-center/mappings', params)
|
||||
}
|
||||
|
||||
export function createAdminChannelProductMapping(payload: Record<string, unknown>) {
|
||||
return apiPost<{ mapping: ChannelProductMapping }>('/api/v1/admin/cost-center/mappings', payload)
|
||||
}
|
||||
|
||||
export function updateAdminChannelProductMapping(
|
||||
mappingId: number,
|
||||
payload: Record<string, unknown>,
|
||||
) {
|
||||
return apiPatch<{ mapping: ChannelProductMapping }>(
|
||||
`/api/v1/admin/cost-center/mappings/${mappingId}`,
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
export function backfillAdminProductMappings(payload: Record<string, unknown>) {
|
||||
return apiPost<{
|
||||
updatedCount: number
|
||||
limit: number
|
||||
batches: number
|
||||
remainingCount: number
|
||||
}>('/api/v1/admin/cost-center/mappings/backfill', payload)
|
||||
}
|
||||
|
||||
export function backfillAdminNormalizedQuantities(payload: Record<string, unknown>) {
|
||||
return apiPost<{
|
||||
updatedCount: number
|
||||
limit: number
|
||||
batches: number
|
||||
remainingCount: number
|
||||
}>('/api/v1/admin/cost-center/normalized-quantities/backfill', payload)
|
||||
}
|
||||
|
||||
export function fetchAdminCostItems(params?: Record<string, unknown>) {
|
||||
return apiGet<CostItemListResult>('/api/v1/admin/cost-center/cost-items', params)
|
||||
}
|
||||
|
||||
export function createAdminCostItem(payload: Record<string, unknown>) {
|
||||
return apiPost<{ costItem: CostItem }>('/api/v1/admin/cost-center/cost-items', payload)
|
||||
}
|
||||
|
||||
export function updateAdminCostItem(costItemId: number, payload: Record<string, unknown>) {
|
||||
return apiPatch<{ costItem: CostItem }>(
|
||||
`/api/v1/admin/cost-center/cost-items/${costItemId}`,
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
export function fetchAdminCostVersions(params?: Record<string, unknown>) {
|
||||
return apiGet<CostVersionListResult>('/api/v1/admin/cost-center/cost-versions', params)
|
||||
}
|
||||
|
||||
export function createAdminCostVersion(payload: Record<string, unknown>) {
|
||||
return apiPost<{ version: CostVersion }>('/api/v1/admin/cost-center/cost-versions', payload)
|
||||
}
|
||||
|
||||
export function retireAdminCostVersion(versionId: number, payload: Record<string, unknown>) {
|
||||
return apiPatch<{ version: CostVersion }>(
|
||||
`/api/v1/admin/cost-center/cost-versions/${versionId}/retire`,
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
export function fetchAdminCostExceptions(params?: Record<string, unknown>) {
|
||||
return apiGet<CostExceptionListResult>('/api/v1/admin/cost-center/exceptions', params)
|
||||
}
|
||||
|
||||
export function resolveAdminCostException(exceptionId: number, payload: Record<string, unknown>) {
|
||||
return apiPatch<{ exception: CostException }>(
|
||||
`/api/v1/admin/cost-center/exceptions/${exceptionId}`,
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
export function previewAdminCost(payload: Record<string, unknown>) {
|
||||
return apiPost<CostPreview>('/api/v1/admin/cost-center/preview', payload)
|
||||
}
|
||||
|
||||
export function createAdminCostSnapshot(payload: Record<string, unknown>) {
|
||||
return apiPost<{
|
||||
preview: CostPreview
|
||||
snapshots: Array<Record<string, unknown>>
|
||||
createdCount: number
|
||||
exceptionCreatedCount: number
|
||||
}>('/api/v1/admin/cost-center/snapshots', payload)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './auth'
|
||||
export * from './dashboard'
|
||||
export * from './finance'
|
||||
export * from './cost-center'
|
||||
export * from './fund'
|
||||
export * from './users'
|
||||
export * from './audit-logs'
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
export interface CostCenterOverview {
|
||||
productCount: number
|
||||
mappingCount: number
|
||||
costItemCount: number
|
||||
activeVersionCount: number
|
||||
openExceptionCount: number
|
||||
unmappedOrderItemCount: number
|
||||
}
|
||||
|
||||
export interface ProductCatalogItem {
|
||||
id: number
|
||||
canonicalSku: string
|
||||
canonicalName: string
|
||||
category: string
|
||||
rootCategoryId: number | null
|
||||
rootCategoryName: string
|
||||
subCategoryId: number | null
|
||||
subCategoryName: string
|
||||
unitName: string
|
||||
status: 'active' | 'archived'
|
||||
attributes: Record<string, unknown>
|
||||
mappingCount: number
|
||||
costVersionCount: number
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface ChannelProductMapping {
|
||||
id: number
|
||||
provider: string
|
||||
shopId: string
|
||||
platformSkuCode: string
|
||||
platformSkuName: string
|
||||
platformSkuNameKey: string
|
||||
matchType: 'sku_code' | 'sku_code_name' | 'sku_code_series' | 'sku_name_series'
|
||||
canonicalProductId: number
|
||||
canonicalSku: string
|
||||
canonicalName: string
|
||||
quantityMultiplier: number
|
||||
confidence: 'exact' | 'rule' | 'manual'
|
||||
source: 'manual' | 'import' | 'rule'
|
||||
enabled: boolean
|
||||
note: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface CostItem {
|
||||
id: number
|
||||
code: string
|
||||
name: string
|
||||
costType:
|
||||
| 'auto_purchase'
|
||||
| 'work_order_reward'
|
||||
| 'manual_service'
|
||||
| 'platform_fee'
|
||||
| 'payment_fee'
|
||||
| 'after_sales_loss'
|
||||
| 'shipping_other'
|
||||
| 'other'
|
||||
carrierType: 'affiliate_dash' | 'work_order' | 'manual' | 'shared'
|
||||
pricingMode: 'per_item' | 'per_order' | 'per_hour' | 'percent_of_revenue' | 'fixed_daily'
|
||||
unitName: string
|
||||
currency: string
|
||||
status: 'active' | 'archived'
|
||||
note: string
|
||||
versionCount: number
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface CostVersion {
|
||||
id: number
|
||||
costItemId: number
|
||||
costItemCode: string
|
||||
costItemName: string
|
||||
pricingMode: CostItem['pricingMode']
|
||||
scopeType: 'global' | 'provider' | 'shop' | 'product' | 'rule'
|
||||
provider: string
|
||||
shopId: string
|
||||
canonicalProductId: number | null
|
||||
canonicalSku: string
|
||||
canonicalName: string
|
||||
workProductRuleId: number | null
|
||||
amountFen: number
|
||||
rateBps: number
|
||||
effectiveFrom: string
|
||||
effectiveTo: string | null
|
||||
source: 'manual' | 'upstream_sync' | 'import'
|
||||
status: 'draft' | 'active' | 'retired'
|
||||
changeReason: string
|
||||
createdByAdminUserId: number | null
|
||||
approvedByAdminUserId: number | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface CostException {
|
||||
id: number
|
||||
subjectType: 'order_item' | 'work_order' | 'manual_case'
|
||||
subjectId: number
|
||||
orderItemId: number | null
|
||||
workOrderId: number | null
|
||||
carrierType: string
|
||||
reason: string
|
||||
status: 'open' | 'resolved' | 'ignored'
|
||||
detail: Record<string, unknown>
|
||||
createdAt: string
|
||||
resolvedAt: string | null
|
||||
resolvedByAdminUserId: number | null
|
||||
}
|
||||
|
||||
export interface CostCenterOptions {
|
||||
providers: string[]
|
||||
products: Array<{
|
||||
id: number
|
||||
canonicalSku: string
|
||||
canonicalName: string
|
||||
unitName: string
|
||||
}>
|
||||
costItems: Array<{
|
||||
id: number
|
||||
code: string
|
||||
name: string
|
||||
pricingMode: CostItem['pricingMode']
|
||||
carrierType: CostItem['carrierType']
|
||||
}>
|
||||
rules: Array<{
|
||||
id: number
|
||||
ruleKey: string
|
||||
productName: string
|
||||
skuCode: string
|
||||
provider: string
|
||||
}>
|
||||
}
|
||||
|
||||
export interface TemplateProductBootstrapPreview {
|
||||
templateCount: number
|
||||
productExistingCount: number
|
||||
productCreateCount: number
|
||||
mappingCandidateCount: number
|
||||
mappingExistingCount: number
|
||||
mappingCreateCount: number
|
||||
mappingCreateByMatchType: Record<
|
||||
'sku_code' | 'sku_code_name' | 'sku_code_series' | 'sku_name_series',
|
||||
number
|
||||
>
|
||||
evidenceItemCount: number
|
||||
orderItemCoverCount: number
|
||||
mappingConflictCount: number
|
||||
mappingConflictItemCount: number
|
||||
multiRuleKeyCount: number
|
||||
seriesFallbackConflictCount: number
|
||||
templatesWithoutWorkOrders: number
|
||||
samples: Array<{
|
||||
ruleKey: string
|
||||
productName: string
|
||||
provider: string
|
||||
shopId: string
|
||||
skuCode: string
|
||||
skuName: string
|
||||
matchType: ChannelProductMapping['matchType']
|
||||
itemCount: number
|
||||
}>
|
||||
conflicts: Array<{
|
||||
provider: string
|
||||
shopId: string
|
||||
skuCode: string
|
||||
skuName: string
|
||||
matchType: 'sku_code_name'
|
||||
itemCount: number
|
||||
ruleKeys: string[]
|
||||
}>
|
||||
}
|
||||
|
||||
export interface TemplateProductBootstrapResult {
|
||||
templateCount: number
|
||||
createdProductCount: number
|
||||
reusedProductCount: number
|
||||
createdMappingCount: number
|
||||
skippedMappingCount: number
|
||||
mappedOrderItemCount: number
|
||||
deletedImportedMappingCount: number
|
||||
resetOrderItemCount: number
|
||||
mappingConflictCount: number
|
||||
mappingConflictItemCount: number
|
||||
}
|
||||
|
||||
export interface CostLine {
|
||||
costItemId: number
|
||||
costItemCode: string
|
||||
costItemName: string
|
||||
costType: string
|
||||
carrierType: string
|
||||
pricingMode: string
|
||||
costVersionId: number
|
||||
scopeType: string
|
||||
amountFen: number
|
||||
rateBps: number
|
||||
quantity: number
|
||||
unitAmountFen: number
|
||||
totalAmountFen: number
|
||||
}
|
||||
|
||||
export interface CostPreview {
|
||||
context: {
|
||||
orderItemId: number | null
|
||||
orderId: number | null
|
||||
provider: string
|
||||
shopId: string
|
||||
skuCode: string
|
||||
skuName: string
|
||||
carrierType: string
|
||||
quantity: number
|
||||
normalizedQuantity: number
|
||||
revenueFen: number
|
||||
workOrderId: number | null
|
||||
workProductRuleId: number | null
|
||||
}
|
||||
mapping: ChannelProductMapping | null
|
||||
product: ProductCatalogItem | null
|
||||
lines: CostLine[]
|
||||
missingCostItems: Array<{
|
||||
costItemId: number
|
||||
costItemCode: string
|
||||
costItemName: string
|
||||
carrierType: string
|
||||
}>
|
||||
totalCostFen: number
|
||||
exceptions: Array<{ reason: string; message: string; detail: Record<string, unknown> }>
|
||||
}
|
||||
|
||||
export interface ProductCategoryNode {
|
||||
categoryId: number
|
||||
name: string
|
||||
total: number
|
||||
directTotal: number
|
||||
children: Array<{
|
||||
categoryId: number
|
||||
name: string
|
||||
total: number
|
||||
}>
|
||||
}
|
||||
|
||||
export interface ProductCatalogListResult {
|
||||
items: ProductCatalogItem[]
|
||||
categoryTree: ProductCategoryNode[]
|
||||
uncategorizedTotal: number
|
||||
pagination: { page: number; pageSize: number; total: number }
|
||||
}
|
||||
|
||||
export interface ChannelProductMappingGroup {
|
||||
canonicalProductId: number
|
||||
canonicalSku: string
|
||||
canonicalName: string
|
||||
category: string
|
||||
unitName: string
|
||||
status: 'active' | 'archived'
|
||||
mappings: ChannelProductMapping[]
|
||||
}
|
||||
|
||||
export interface ChannelProductMappingListResult {
|
||||
groups: ChannelProductMappingGroup[]
|
||||
pagination: { page: number; pageSize: number; total: number; mappingTotal: number }
|
||||
}
|
||||
|
||||
export interface CostItemListResult {
|
||||
items: CostItem[]
|
||||
pagination: { page: number; pageSize: number; total: number }
|
||||
}
|
||||
|
||||
export interface CostVersionListResult {
|
||||
items: CostVersion[]
|
||||
pagination: { page: number; pageSize: number; total: number }
|
||||
}
|
||||
|
||||
export interface CostExceptionListResult {
|
||||
items: CostException[]
|
||||
pagination: { page: number; pageSize: number; total: number }
|
||||
}
|
||||
@@ -122,3 +122,5 @@ export type {
|
||||
AdminAffiliateDashProductListResult,
|
||||
AdminAffiliateDashWallet,
|
||||
} from './platform-config'
|
||||
|
||||
export type * from './cost-center'
|
||||
|
||||
Reference in New Issue
Block a user