diff --git a/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/controller/StockCollectionController.java b/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/controller/StockCollectionController.java index 72c29d3..eef8ae7 100644 --- a/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/controller/StockCollectionController.java +++ b/leopard-server/src/main/java/com/lanyuanxiaoyao/leopard/server/controller/StockCollectionController.java @@ -2,10 +2,10 @@ package com.lanyuanxiaoyao.leopard.server.controller; import com.lanyuanxiaoyao.leopard.core.entity.StockCollection; import com.lanyuanxiaoyao.leopard.core.service.StockCollectionService; -import com.lanyuanxiaoyao.leopard.core.service.StockService; import com.lanyuanxiaoyao.leopard.server.entity.StockScoreVo; import com.lanyuanxiaoyao.service.template.controller.GlobalResponse; import com.lanyuanxiaoyao.service.template.controller.SimpleControllerSupport; +import java.time.LocalDateTime; import java.util.Comparator; import java.util.List; import java.util.function.Function; @@ -15,11 +15,8 @@ import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("stock_collection") public class StockCollectionController extends SimpleControllerSupport { - private final StockService stockService; - - public StockCollectionController(StockCollectionService service, StockService stockService) { + public StockCollectionController(StockCollectionService service) { super(service); - this.stockService = stockService; } @Override @@ -38,7 +35,9 @@ public class StockCollectionController extends SimpleControllerSupport scores + List scores, + LocalDateTime createdTime, + LocalDateTime modifiedTime ) { } } diff --git a/leopard-web/src/pages/stock/StockCollectionList.tsx b/leopard-web/src/pages/stock/StockCollectionList.tsx index e3e5a2a..b06373b 100644 --- a/leopard-web/src/pages/stock/StockCollectionList.tsx +++ b/leopard-web/src/pages/stock/StockCollectionList.tsx @@ -1,5 +1,5 @@ import React from "react" -import {amisRender, commonInfo, crudCommonOptions, paginationTemplate} from '../../util/amis.tsx' +import {amisRender, commonInfo, crudCommonOptions, paginationTemplate, time} from '../../util/amis.tsx' import {useNavigate} from 'react-router' function StockCollectionList() { @@ -14,8 +14,16 @@ function StockCollectionList() { { type: 'crud', api: { - method: 'get', + method: 'post', url: `${commonInfo.baseUrl}/stock_collection/list`, + data: { + sort: [ + { + column: 'createdTime', + direction: 'DESC', + }, + ], + }, }, ...crudCommonOptions(), ...paginationTemplate(15, undefined, ['filter-toggler']), @@ -35,6 +43,20 @@ function StockCollectionList() { align: 'center', width: 100, }, + { + name: 'createdTime', + label: '创建时间', + width: 150, + align: 'center', + ...time('createdTime'), + }, + { + name: 'modifiedTime', + label: '更新时间', + width: 150, + align: 'center', + ...time('modifiedTime'), + }, { type: 'operation', label: '操作', @@ -58,6 +80,16 @@ function StockCollectionList() { }, }, }, + { + className: 'text-danger btn-deleted', + type: 'action', + label: '删除', + level: 'link', + actionType: 'ajax', + api: `get:${commonInfo.baseUrl}/stock_collection/remove/\${id}`, + confirmText: '确认删除股票集${name}?', + confirmTitle: '删除', + }, ], }, ], diff --git a/leopard-web/src/pages/stock/StockDetail.tsx b/leopard-web/src/pages/stock/StockDetail.tsx deleted file mode 100644 index 1237d7c..0000000 --- a/leopard-web/src/pages/stock/StockDetail.tsx +++ /dev/null @@ -1,550 +0,0 @@ -import React from 'react' -import {useParams} from 'react-router' -import {amisRender, commonInfo, readOnlyDialogOptions, remoteMappings} from '../../util/amis.tsx' -import type {Schema} from 'amis' -import {isNil} from 'es-toolkit' -import {toNumber} from 'es-toolkit/compat' - -const formatFinanceNumber = (value: number): string => { - if (isNil(value)) { - return '-' - } - - const isNegative = value < 0 - const absoluteValue = Math.abs(value) - - let formatted: string - if (absoluteValue >= 100000000) { - formatted = (absoluteValue / 100000000).toFixed(2) + '亿' - } else if (absoluteValue >= 10000) { - formatted = (absoluteValue / 10000).toFixed(2) + '万' - } else { - formatted = absoluteValue.toLocaleString() - } - - return isNegative ? `-${formatted}` : formatted -} - -const formatDaysNumber = (value: number): string => { - if (isNil(value)) { - return '-' - } - return `${value.toFixed(0)}天` -} - -const formatPercentageNumber = (value: number): string => { - if (isNil(value)) { - return '-' - } - return `${(value * 100).toFixed(2)}%` -} - -type FinanceType = 'PERCENTAGE' | 'FINANCE' | 'DAYS' - -const financePropertyLabel = (id: string | undefined, label: string, type: FinanceType, field: string): Schema => { - if (!id) { - return { - type: 'tpl', - tpl: label, - } - } - let formatter: (value: number) => string - switch (type) { - case 'PERCENTAGE': - formatter = formatPercentageNumber - break - case 'FINANCE': - formatter = formatFinanceNumber - break - case 'DAYS': - formatter = formatDaysNumber - break - default: - formatter = (v: number) => v.toFixed(2) - } - return { - type: 'wrapper', - size: 'none', - body: [ - { - className: 'text-current font-bold', - type: 'action', - label: label, - level: 'link', - tooltip: '这是什么?', - tooltipPlacement: 'top', - actionType: 'dialog', - dialog: { - title: '', - size: 'lg', - ...readOnlyDialogOptions(), - actions: [ - { - type: 'action', - label: '新页面打开', - icon: 'fa fa-solid fa-arrow-up-right-from-square', - actionType: 'url', - url: `https://zh.wikipedia.org/wiki/${label}`, - blank: true, - }, - ], - body: { - type: 'iframe', - src: `https://zh.wikipedia.org/wiki/${label}`, - height: 800, - }, - }, - }, - { - className: 'text-secondary', - type: 'action', - label: '', - icon: 'fa fa-eye', - level: 'link', - size: 'xs', - tooltip: '查看五年趋势', - tooltipPlacement: 'top', - actionType: 'dialog', - dialog: { - title: `${label}五年趋势`, - size: 'lg', - bodyClassName: 'p-0', - ...readOnlyDialogOptions(), - body: { - type: 'chart', - api: `get:${commonInfo.baseUrl}/stock/finance/${id}/${field}`, - height: 500, - config: { - tooltip: { - trigger: 'axis', - backgroundColor: 'rgba(255, 255, 255, 0.9)', - borderColor: '#ccc', - borderWidth: 1, - textStyle: { - color: '#333', - }, - padding: [10, 15], - formatter: (params: any) => { - const item = params[0] - return `${item.name}
${item.marker}${formatter(item.value)}` - }, - }, - grid: { - left: '5%', - right: '5%', - top: '10%', - bottom: '15%', - containLabel: true, - }, - xAxis: { - type: 'category', - data: '${xList || []}', - axisLine: { - lineStyle: { - color: '#e0e0e0', - }, - }, - axisLabel: { - color: '#666', - fontWeight: 'bold', - }, - axisTick: { - show: false, - }, - }, - yAxis: { - type: 'value', - show: true, - splitLine: { - lineStyle: { - type: 'dashed', - color: '#f0f0f0', - }, - }, - axisLine: { - show: false, - }, - axisLabel: { - color: '#999', - fontSize: 12, - formatter: (value: number) => { - return formatter(value) - }, - }, - axisTick: { - show: false, - }, - }, - series: [ - { - data: '${yList || []}', - type: 'line', - smooth: true, - showSymbol: true, - symbolSize: 6, - lineStyle: { - width: 3, - color: '#4096ff', - shadowColor: 'rgba(64, 150, 255, 0.3)', - shadowBlur: 5, - shadowOffsetY: 2, - }, - itemStyle: { - color: '#4096ff', - borderWidth: 2, - borderColor: '#fff', - }, - areaStyle: { - color: { - type: 'linear', - x: 0, - y: 0, - x2: 0, - y2: 1, - colorStops: [{ - offset: 0, color: 'rgba(64, 150, 255, 0.2)', - }, { - offset: 1, color: 'rgba(64, 150, 255, 0.01)', - }], - }, - }, - label: { - show: true, - position: 'top', - color: '#333', - fontWeight: 'bold', - fontSize: 12, - formatter: (params: any) => { - return formatter(params.value) - }, - }, - }, - ], - }, - }, - }, - }, - ], - } -} - -function StockDetail() { - const {id} = useParams() - return ( -
- {amisRender( - { - type: 'page', - title: '股票详情(${code} ${name})', - initApi: `get:${commonInfo.baseUrl}/stock/detail/${id}`, - body: [ - { - type: 'property', - items: [ - {label: '编码', content: '${code}'}, - {label: '名称', content: '${name}'}, - {label: '全名', content: '${fullname}'}, - { - label: '市场', - content: { - ...remoteMappings('stock_market', 'market'), - value: '${market}', - }, - }, - {label: '行业', content: '${industry}'}, - {label: '上市日期', content: '${listedDate}'}, - ], - }, - {type: 'divider'}, - { - type: 'service', - api: `get:${commonInfo.baseUrl}/stock/finance/${id}`, - body: [ - '资产负债表', - { - className: 'my-2', - type: 'property', - column: 4, - items: [ - { - label: financePropertyLabel(id, '总资产', 'FINANCE', 'totalAssets'), - content: '${balanceSheet.totalAssets}', - span: 2, - }, - { - label: financePropertyLabel(id, '总负债', 'FINANCE', 'totalLiabilities'), - content: '${balanceSheet.totalLiabilities}', - span: 2, - }, - { - label: financePropertyLabel(id, '流动资产', 'FINANCE', 'currentAssets'), - content: '${balanceSheet.currentAssets}', - }, - { - label: financePropertyLabel(id, '流动资产占比', 'PERCENTAGE', 'currentAssetsToTotalAssetsRatio'), - content: '${balanceSheet.currentAssetsRatio}', - }, - { - label: financePropertyLabel(id, '流动负债', 'FINANCE', 'currentLiabilities'), - content: '${balanceSheet.currentLiabilities}', - }, - { - label: financePropertyLabel(id, '流动负债占比', 'PERCENTAGE', 'currentLiabilitiesToTotalAssetsRatio'), - content: '${balanceSheet.currentLiabilitiesRatio}', - }, - { - label: financePropertyLabel(id, '非流动资产', 'FINANCE', 'fixedAssets'), - content: '${balanceSheet.fixedAssets}', - }, - { - label: financePropertyLabel(id, '非流动资产占比', 'PERCENTAGE', 'fixedAssetsToTotalAssetsRatio'), - content: '${balanceSheet.fixedAssetsRatio}', - }, - { - label: financePropertyLabel(id, '非流动负债', 'FINANCE', 'longTermLiabilities'), - content: '${balanceSheet.longTermLiabilities}', - }, - { - label: financePropertyLabel(id, '非流动负债占比', 'PERCENTAGE', 'longTermLiabilitiesToTotalAssetsRatio'), - content: '${balanceSheet.longTermLiabilitiesRatio}', - }, - ], - }, - '利润表', - { - className: 'my-2', - type: 'property', - items: [ - { - label: financePropertyLabel(id, '营业收入', 'FINANCE', 'operatingRevenue'), - content: '${income.operatingRevenue}', - }, - { - label: financePropertyLabel(id, '营业成本', 'FINANCE', 'operatingCost'), - content: '${income.operatingCost}', - }, - { - label: financePropertyLabel(id, '营业利润', 'FINANCE', 'operatingProfit'), - content: '${income.operatingProfit}', - }, - ], - }, - '现金流量表', - { - className: 'my-2', - type: 'property', - items: [ - { - label: financePropertyLabel(id, '净利润', 'FINANCE', 'netProfit'), - content: '${cashFlow.netProfit}', - span: 3, - }, - { - label: financePropertyLabel(id, '营业活动现金流量', 'FINANCE', 'cashFlowFromOperatingActivities'), - content: '${cashFlow.cashFlowFromOperatingActivities}', - }, - { - label: financePropertyLabel(id, '投资活动现金流量', 'FINANCE', 'cashFlowFromInvestingActivities'), - content: '${cashFlow.cashFlowFromInvestingActivities}', - }, - { - label: financePropertyLabel(id, '筹资活动现金流量', 'FINANCE', 'cashFlowFromFinancingActivities'), - content: '${cashFlow.cashFlowFromFinancingActivities}', - }, - ], - }, - '财务指标', - { - className: 'my-2', - type: 'property', - column: 4, - items: [ - { - label: financePropertyLabel(id, '流动比率', 'FINANCE', 'currentRatio'), - content: '${indicate.currentRatio}', - }, - { - label: financePropertyLabel(id, '速动比率', 'FINANCE', 'quickRatio'), - content: '${indicate.quickRatio}', - }, - { - label: financePropertyLabel(id, 'ROE', 'FINANCE', 'returnOnEquity'), - content: '${indicate.roe}', - }, - { - label: financePropertyLabel(id, 'ROA', 'FINANCE', 'returnOnAssets'), - content: '${indicate.roa}', - }, - { - label: financePropertyLabel(id, '应收账款周转率', 'FINANCE', 'accountsReceivableTurnover'), - content: '${indicate.accountsReceivableTurnover}', - }, - { - label: financePropertyLabel(id, '应收账款周转天数', 'DAYS', 'daysAccountsReceivableTurnover'), - content: '${indicate.daysAccountsReceivableTurnover}', - }, - { - label: financePropertyLabel(id, '存货周转率', 'FINANCE', 'inventoryTurnover'), - content: '${indicate.inventoryTurnover}', - }, - { - label: financePropertyLabel(id, '存货周转天数', 'DAYS', 'daysInventoryTurnover'), - content: '${indicate.daysInventoryTurnover}', - }, - { - label: financePropertyLabel(id, '固定资产周转率', 'FINANCE', 'fixedAssetsTurnover'), - content: '${indicate.fixedAssetsTurnover}', - }, - { - label: financePropertyLabel(id, '固定资产周转天数', 'DAYS', 'daysFixedAssetsTurnover'), - content: '${indicate.daysFixedAssetsTurnover}', - }, - { - label: financePropertyLabel(id, '总资产周转率', 'FINANCE', 'totalAssetsTurnover'), - content: '${indicate.totalAssetsTurnover}', - }, - { - label: financePropertyLabel(id, '总资产周转天数', 'DAYS', 'daysTotalAssetsTurnover'), - content: '${indicate.daysTotalAssetsTurnover}', - }, - ], - }, - {type: 'divider'}, - "100日线数据", - { - type: 'chart', - height: 500, - api: `get:${commonInfo.baseUrl}/stock/daily/${id}`, - config: { - backgroundColor: '#fff', - animation: true, - animationDuration: 1000, - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'cross', - }, - backgroundColor: 'rgba(0, 0, 0, 0.7)', - borderColor: '#333', - borderWidth: 1, - textStyle: { - color: '#fff', - fontSize: 12, - }, - padding: 12, - formatter: function (params: any) { - const param = params[0] - const open = toNumber(param.data[1]).toFixed(2) - const close = toNumber(param.data[2]).toFixed(2) - const lowest = toNumber(param.data[3]).toFixed(2) - const highest = toNumber(param.data[4]).toFixed(2) - - return `
${param.name}
-
- 开盘: - ${open} -
-
- 收盘: - ${close} -
-
- 最低: - ${lowest} -
-
- 最高: - ${highest} -
` - }, - }, - grid: { - left: '10%', - right: '10%', - top: '10%', - bottom: '15%', - containLabel: true, - }, - xAxis: { - data: '${xList || []}', - axisLine: { - lineStyle: { - color: '#e0e0e0', - }, - }, - axisLabel: { - color: '#666', - fontWeight: 'bold', - }, - splitLine: { - show: false, - }, - axisTick: { - show: false, - }, - }, - yAxis: { - scale: true, - axisLine: { - lineStyle: { - color: '#e0e0e0', - }, - }, - axisLabel: { - color: '#666', - fontWeight: 'bold', - formatter: function (value: number) { - return value.toFixed(2) - }, - }, - splitLine: { - lineStyle: { - type: 'dashed', - color: '#f0f0f0', - }, - }, - axisTick: { - show: false, - }, - }, - dataZoom: [ - { - type: 'inside', - start: 0, - end: 100, - }, - { - show: true, - type: 'slider', - top: '90%', - start: 0, - end: 100, - }, - ], - series: [ - { - type: 'candlestick', - data: '${yList || []}', - itemStyle: { - color: '#eb5454', - color0: '#4aaa93', - borderColor: '#eb5454', - borderColor0: '#4aaa93', - borderWidth: 1, - }, - - }, - ], - }, - }, - "12月线数据", - ], - }, - ], - }, - )} -
- ) -} - -export default React.memo(StockDetail) \ No newline at end of file