diff --git a/apps/web-antd/src/api/dashboard/dashboard.ts b/apps/web-antd/src/api/dashboard/dashboard.ts new file mode 100644 index 0000000..5428fc6 --- /dev/null +++ b/apps/web-antd/src/api/dashboard/dashboard.ts @@ -0,0 +1,68 @@ +import { requestClient } from '#/api/request'; + +export namespace DashboardApi { + export interface OrderStatistics { + today_count: number; + month_count: number; + total_count: number; + yesterday_count: number; + change_rate: number; + } + + export interface RevenueStatistics { + today_amount: number; + month_amount: number; + total_amount: number; + yesterday_amount: number; + change_rate: number; + } + + export interface AgentStatistics { + total_count: number; + today_new: number; + month_new: number; + } + + export interface ProfitDetail { + revenue: number; + commission: number; + rebate: number; + company_tax: number; + api_cost: number; + tax_income: number; + profit: number; + profit_rate: number; + } + + export interface ProfitStatistics { + today_profit: number; + month_profit: number; + total_profit: number; + today_profit_rate: number; + month_profit_rate: number; + total_profit_rate: number; + today_detail: ProfitDetail; + month_detail: ProfitDetail; + total_detail: ProfitDetail; + } + + export interface TrendData { + date: string; + value: number; + } + + export interface DashboardStatistics { + order_stats: OrderStatistics; + revenue_stats: RevenueStatistics; + agent_stats: AgentStatistics; + profit_stats: ProfitStatistics; + order_trend: TrendData[]; + revenue_trend: TrendData[]; + } +} + +export async function getDashboardStatistics(): Promise { + return await requestClient.get( + '/dashboard/statistics', + ); +} diff --git a/apps/web-antd/src/api/dashboard/index.ts b/apps/web-antd/src/api/dashboard/index.ts new file mode 100644 index 0000000..b58b6c9 --- /dev/null +++ b/apps/web-antd/src/api/dashboard/index.ts @@ -0,0 +1 @@ +export * from './dashboard'; diff --git a/apps/web-antd/src/api/index.ts b/apps/web-antd/src/api/index.ts index 9c7bf19..65767ff 100644 --- a/apps/web-antd/src/api/index.ts +++ b/apps/web-antd/src/api/index.ts @@ -1,5 +1,6 @@ export * from './agent'; export * from './core'; +export * from './dashboard'; export * from './notification'; export * from './order'; export * from './platform-user'; diff --git a/apps/web-antd/src/api/product-manage/feature.ts b/apps/web-antd/src/api/product-manage/feature.ts index cbf9de3..1cf82f0 100644 --- a/apps/web-antd/src/api/product-manage/feature.ts +++ b/apps/web-antd/src/api/product-manage/feature.ts @@ -4,9 +4,11 @@ import { requestClient } from '#/api/request'; export namespace FeatureApi { export interface FeatureItem { - id: number; + id: string; api_id: string; name: string; + whitelist_price: number; + cost_price: number; create_time: string; update_time: string; } @@ -19,11 +21,15 @@ export namespace FeatureApi { export interface CreateFeatureRequest { api_id: string; name: string; + whitelist_price?: number; + cost_price?: number; } export interface UpdateFeatureRequest { api_id?: string; name?: string; + whitelist_price?: number; + cost_price?: number; } export interface FeatureExampleItem { @@ -36,7 +42,7 @@ export namespace FeatureApi { } export interface ConfigFeatureExampleRequest { - feature_id: number; + feature_id: string; data: string; } @@ -45,12 +51,12 @@ export namespace FeatureApi { } export interface GetFeatureExampleRequest { - feature_id: number; + feature_id: string; } export interface GetFeatureExampleResponse { - id: number; - feature_id: number; + id: string; + feature_id: string; api_id: string; data: string; create_time: string; @@ -71,7 +77,7 @@ async function getFeatureList(params: Recordable) { * 获取模块详情 * @param id 模块ID */ -async function getFeatureDetail(id: number) { +async function getFeatureDetail(id: string) { return requestClient.get(`/feature/detail/${id}`); } @@ -80,7 +86,7 @@ async function getFeatureDetail(id: number) { * @param data 模块数据 */ async function createFeature(data: FeatureApi.CreateFeatureRequest) { - return requestClient.post<{ id: number }>('/feature/create', data); + return requestClient.post<{ id: string }>('/feature/create', data); } /** @@ -89,7 +95,7 @@ async function createFeature(data: FeatureApi.CreateFeatureRequest) { * @param data 模块数据 */ async function updateFeature( - id: number, + id: string, data: FeatureApi.UpdateFeatureRequest, ) { return requestClient.put<{ success: boolean }>(`/feature/update/${id}`, data); @@ -99,7 +105,7 @@ async function updateFeature( * 删除模块 * @param id 模块ID */ -async function deleteFeature(id: number) { +async function deleteFeature(id: string) { return requestClient.delete<{ success: boolean }>(`/feature/delete/${id}`); } @@ -120,7 +126,7 @@ async function configFeatureExample( * 获取功能示例数据 * @param featureId 功能ID */ -async function getFeatureExample(featureId: number) { +async function getFeatureExample(featureId: string) { return requestClient.get( `/feature/example/${featureId}`, ); diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-profit-panel.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-profit-panel.vue new file mode 100644 index 0000000..d1d2af4 --- /dev/null +++ b/apps/web-antd/src/views/dashboard/analytics/analytics-profit-panel.vue @@ -0,0 +1,304 @@ + + + + + diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-revenue-trend.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-revenue-trend.vue new file mode 100644 index 0000000..de868bb --- /dev/null +++ b/apps/web-antd/src/views/dashboard/analytics/analytics-revenue-trend.vue @@ -0,0 +1,112 @@ + + + diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue index c3d3d81..d1f8616 100644 --- a/apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue +++ b/apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue @@ -1,14 +1,28 @@ diff --git a/apps/web-antd/src/views/dashboard/analytics/index.vue b/apps/web-antd/src/views/dashboard/analytics/index.vue index 5155d3e..8dc7742 100644 --- a/apps/web-antd/src/views/dashboard/analytics/index.vue +++ b/apps/web-antd/src/views/dashboard/analytics/index.vue @@ -1,102 +1,175 @@ + + diff --git a/apps/web-antd/src/views/product-manage/feature/data.ts b/apps/web-antd/src/views/product-manage/feature/data.ts index 01af972..1e105cf 100644 --- a/apps/web-antd/src/views/product-manage/feature/data.ts +++ b/apps/web-antd/src/views/product-manage/feature/data.ts @@ -17,6 +17,39 @@ export function useFormSchema(): VbenFormSchema[] { label: '描述', rules: 'required', }, + { + component: 'Switch', + fieldName: 'no_offline', + label: '不支持下架', + defaultValue: false, + help: '勾选后该模块不开放下架功能,提交时白名单价格传 -1', + }, + { + component: 'InputNumber', + fieldName: 'whitelist_price', + label: '白名单屏蔽价格(元)', + componentProps: { + min: 0, + precision: 2, + placeholder: '0=免费下架,>0=付费下架;勾选「不支持下架」时此项忽略', + }, + dependencies: { + triggerFields: ['no_offline'], + if(values) { + return !values?.no_offline; + }, + }, + }, + { + component: 'InputNumber', + componentProps: { + min: 0, + precision: 2, + }, + fieldName: 'cost_price', + label: '成本价(元)', + rules: 'required', + }, ]; } @@ -51,6 +84,28 @@ export function useColumns( title: '描述', minWidth: 200, }, + { + field: 'whitelist_price', + title: '白名单屏蔽价格(元)', + minWidth: 150, + cellRender: { + name: 'VxeCellRender', + props: { + render: ({ row }: { row: FeatureApi.FeatureItem }) => { + const price = (row as FeatureApi.FeatureItem).whitelist_price ?? 0; + if (price < 0) return '不支持下架'; + if (price === 0) return '免费下架'; + return `¥${price.toFixed(2)}`; + }, + }, + }, + }, + { + field: 'cost_price', + formatter: ({ cellValue }) => `¥${(cellValue || 0).toFixed(2)}`, + title: '成本价(元)', + minWidth: 120, + }, { field: 'create_time', title: '创建时间', diff --git a/apps/web-antd/src/views/product-manage/feature/modules/form.vue b/apps/web-antd/src/views/product-manage/feature/modules/form.vue index b0d390e..5b1cf1e 100644 --- a/apps/web-antd/src/views/product-manage/feature/modules/form.vue +++ b/apps/web-antd/src/views/product-manage/feature/modules/form.vue @@ -24,7 +24,12 @@ const [Drawer, drawerApi] = useVbenDrawer({ async onConfirm() { const { valid } = await formApi.validate(); if (!valid) return; - const values = await formApi.getValues(); + const values = (await formApi.getValues()) as Record; + // 「不支持下架」时给后端传 whitelist_price: -1 + if (values.no_offline) { + values.whitelist_price = -1; + } + delete values.no_offline; drawerApi.lock(); try { await (id.value @@ -43,7 +48,13 @@ const [Drawer, drawerApi] = useVbenDrawer({ if (data) { formData.value = data; id.value = data.id; - formApi.setValues(data); + // 回显:whitelist_price < 0 表示不支持下架,勾选「不支持下架」并隐藏价格输入 + const noOffline = (data.whitelist_price ?? 0) < 0; + formApi.setValues({ + ...data, + no_offline: noOffline, + whitelist_price: noOffline ? 0 : (data.whitelist_price ?? 0), + }); } else { id.value = undefined; }