feat(stock): ✨️完成公告板查询
This commit is contained in:
parent
ccd19641d8
commit
ab234cd9e4
@ -1,6 +1,6 @@
|
||||
{
|
||||
"useTabs": true,
|
||||
"tabWidth": 4,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100
|
||||
|
2
package
2
package
@ -1 +1 @@
|
||||
Subproject commit f968447dd61331eda162ca8e8fb8d2307f532139
|
||||
Subproject commit 38ffc419475dd2f68b0b82acaef60af4e8b4785f
|
@ -1,22 +1,18 @@
|
||||
import ReactDOM from "react-dom/client";
|
||||
|
||||
import createApp from "../package/package/index.tsx"
|
||||
import createApp from "@package/index.tsx"
|
||||
import StockQuery from '@/stock-query';
|
||||
import PageSetting from '@package/pages/setting';
|
||||
|
||||
const HelloWorld = ()=><h1>Hello world</h1>
|
||||
console.log(createApp)
|
||||
const App = createApp([
|
||||
{
|
||||
path: "hello-world",
|
||||
element: <HelloWorld/>
|
||||
path: "stock-query",
|
||||
element: <StockQuery/>
|
||||
},
|
||||
{
|
||||
path: "hello-2",
|
||||
element: <HelloWorld/>
|
||||
path: "setting",
|
||||
element: <PageSetting settingKeys={['lexicon']}/>
|
||||
},
|
||||
{
|
||||
path: "hello-3",
|
||||
element: <HelloWorld/>
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
|
134
src/stock-query/index.tsx
Normal file
134
src/stock-query/index.tsx
Normal file
@ -0,0 +1,134 @@
|
||||
import { AutoComplete, AutoCompleteProps, Button, Empty, Space } from 'antd';
|
||||
import React from 'react';
|
||||
import { useSetState, useMount } from 'ahooks';
|
||||
|
||||
import { AppTitleBar } from '@package/components/app/title-bar.tsx';
|
||||
|
||||
import { queryStockInfo, searchStockList } from '@/stock-query/service/api.ts';
|
||||
import { SecuritiesInfo } from '@/stock-query/service/types.ts';
|
||||
|
||||
import styles from './style.module.less';
|
||||
import { formatMoney } from '@package/utils/strings.ts';
|
||||
|
||||
|
||||
/**
|
||||
* - 营业收入*10%<1000万,输出1000万
|
||||
* - 净利润*10%<100万,输出100万
|
||||
* - 净资产*10%<1000万,输出1000万
|
||||
* - 如果表格有结果为负数,输出绝对值
|
||||
* @param amount
|
||||
* @param minValue
|
||||
*/
|
||||
function formatStockMoney(amount: number, minValue = 1000) {
|
||||
if (amount < 0) amount = Math.abs(amount);
|
||||
return formatMoney(amount < minValue ? minValue : amount);
|
||||
}
|
||||
export default function StockQuery() {
|
||||
const [options, setOptions] = React.useState<AutoCompleteProps['options']>([]);
|
||||
|
||||
const [state, setState] = useSetState<{
|
||||
companyName: string;
|
||||
result?: SecuritiesInfo;
|
||||
queried?: boolean;
|
||||
}>({
|
||||
companyName: ''
|
||||
});
|
||||
|
||||
// 模糊查询证券列表
|
||||
const handleFilter = async (name: string) => {
|
||||
if (!name) {
|
||||
setOptions(() => []);
|
||||
return;
|
||||
}
|
||||
const list = await searchStockList(name);
|
||||
setOptions(list.security_names.map((name) => ({
|
||||
label: name,
|
||||
value: name
|
||||
})));
|
||||
};
|
||||
// 查询证券营收信息
|
||||
const handleSearch = async () => {
|
||||
if (!state.companyName) return;
|
||||
try {
|
||||
const result = await queryStockInfo(state.companyName);
|
||||
if (!result) return;
|
||||
setState({ result, queried: true });
|
||||
} catch (e) {
|
||||
console.log('query error:', e);
|
||||
setState({ result: undefined, queried: true });
|
||||
}
|
||||
};
|
||||
useMount(() => {
|
||||
chrome.webview?.hostObjects.host.SetTitle('交易权限速查');
|
||||
});
|
||||
|
||||
return <>
|
||||
<AppTitleBar title="交易权限速查" backgroundColor={'#eddcb9'} />
|
||||
<div className={styles.stockQuery}>
|
||||
<Space size={20}>
|
||||
<AutoComplete
|
||||
style={{ width: 200 }}
|
||||
onSearch={handleFilter}
|
||||
placeholder="请输入证券名称"
|
||||
options={options}
|
||||
value={state.companyName}
|
||||
onChange={(companyName: string) => setState({ companyName })}
|
||||
/>
|
||||
<Button type="primary" onClick={handleSearch}>搜索</Button>
|
||||
</Space>
|
||||
<div style={{ minHeight: 200 }}>
|
||||
{state.queried && !state.result && <div className={styles.emptyQuery}>
|
||||
未找到该证券
|
||||
</div>}
|
||||
{state.queried && state.result && (
|
||||
<div className="result">
|
||||
<div className={styles.stockInfo}>
|
||||
<div>
|
||||
<span>证券代码:</span>
|
||||
<span>{state.result.securities_code}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>证券名称:</span>
|
||||
<span>{state.result.security_name}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.stockResult}>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className={styles.slashed}></th>
|
||||
<th>10%(单位:万元)</th>
|
||||
<th>50%(单位:万元)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>营业收入</td>
|
||||
<td>{formatStockMoney(state.result.percent10.operating_income)}</td>
|
||||
<td>{formatStockMoney(state.result.percent50.operating_income)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>净利润</td>
|
||||
<td>{formatStockMoney(state.result.percent10.net_profit,100)}</td>
|
||||
<td>{formatStockMoney(state.result.percent50.net_profit,100)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>资产总额</td>
|
||||
<td>{formatMoney(state.result.percent10.total_assets)}</td>
|
||||
<td>{formatMoney(state.result.percent50.total_assets)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>净资产</td>
|
||||
<td>{formatStockMoney(state.result.percent10.net_assets)}</td>
|
||||
<td>{formatStockMoney(state.result.percent50.net_assets)}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.stockTips}>*科创板公司需额外注意市值指标(指交易前10个交易日收盘市值的算术平均值)</div>
|
||||
</div>
|
||||
</>;
|
||||
}
|
26
src/stock-query/service/api.ts
Normal file
26
src/stock-query/service/api.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { post } from '@package/service/request.ts';
|
||||
import { SecuritiesInfo, SecuritiesInfoDto } from '@/stock-query/service/types.ts';
|
||||
|
||||
/**
|
||||
* 查询上市公司收益信息
|
||||
* @param stockCompanyName
|
||||
*/
|
||||
export async function queryStockInfo(stockCompanyName: string) {
|
||||
const info = await post<{
|
||||
securities_info: SecuritiesInfoDto
|
||||
}>('/api/announce/trade', { security_name: stockCompanyName });
|
||||
return {
|
||||
securities_code: info.securities_info.securities_code,
|
||||
security_name: info.securities_info.security_name,
|
||||
percent10: info.securities_info['10%'],
|
||||
percent50: info.securities_info['50%']
|
||||
} as SecuritiesInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 模糊查询上市公司名称
|
||||
* @param stockCompanyName
|
||||
*/
|
||||
export function searchStockList(stockCompanyName: string) {
|
||||
return post<{security_names:string[]}>('/api/announce/search_name', { security_name: stockCompanyName });
|
||||
}
|
23
src/stock-query/service/types.ts
Normal file
23
src/stock-query/service/types.ts
Normal file
@ -0,0 +1,23 @@
|
||||
interface CompanyRevenueDto {
|
||||
// 营业收入
|
||||
operating_income: number;
|
||||
// 净利润
|
||||
net_profit: number;
|
||||
// 资产总额
|
||||
total_assets: number;
|
||||
// 净资产
|
||||
net_assets: number;
|
||||
}
|
||||
export interface SecuritiesInfoDto {
|
||||
securities_code: string;
|
||||
security_name: string;
|
||||
'10%': CompanyRevenueDto
|
||||
'50%': CompanyRevenueDto
|
||||
}
|
||||
|
||||
export interface SecuritiesInfo {
|
||||
securities_code: string;
|
||||
security_name: string;
|
||||
percent10: CompanyRevenueDto;
|
||||
percent50: CompanyRevenueDto;
|
||||
}
|
2
src/stock-query/stock-company.ts
Normal file
2
src/stock-query/stock-company.ts
Normal file
File diff suppressed because one or more lines are too long
54
src/stock-query/style.module.less
Normal file
54
src/stock-query/style.module.less
Normal file
@ -0,0 +1,54 @@
|
||||
.stockQuery{
|
||||
padding: 30px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.emptyQuery{
|
||||
padding: 15px 0;
|
||||
}
|
||||
.stockInfo{
|
||||
line-height: 1.2em;
|
||||
margin: 15px 0;
|
||||
}
|
||||
.stockResult{
|
||||
table{
|
||||
text-align: center;
|
||||
border-left: 1px #999 solid;
|
||||
border-top: 1px #999 solid;
|
||||
border-collapse: collapse;
|
||||
td,th{
|
||||
border-right: 1px #999 solid;
|
||||
border-bottom: 1px #999 solid;
|
||||
padding: 6px 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.stockTips{
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin: 15px 0;
|
||||
}
|
||||
// 单元格斜线
|
||||
.slashed{
|
||||
position: relative;
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%; /* 或者具体数值 */
|
||||
height: 100%; /* 或者具体数值 */
|
||||
background-image: linear-gradient(20deg, transparent 49%, #666 50%,transparent 51%,);
|
||||
}
|
||||
|
||||
//&::after {
|
||||
// content: "";
|
||||
// position: absolute;
|
||||
// left: 0;
|
||||
// top: 50%;
|
||||
// width: 100%;
|
||||
// height: 1px; /* 或者使用 border-bottom */
|
||||
// background-color: black; /* 斜线颜色 */
|
||||
// transform: rotate(-45deg); /* -45度斜线 */
|
||||
// z-index: -1; /* 确保内容在上面 */
|
||||
//}
|
||||
}
|
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
@ -16,6 +16,9 @@
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
],
|
||||
"@package/*": [
|
||||
"./package/package/*"
|
||||
]
|
||||
},
|
||||
/* Linting */
|
||||
@ -25,6 +28,9 @@
|
||||
"noImplicitAny": false,
|
||||
"noFallthroughCasesInSwitch": false
|
||||
},
|
||||
"include": ["src"],
|
||||
"include": [
|
||||
"src",
|
||||
"package/package"
|
||||
],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ export default defineConfig(({mode}) => {
|
||||
server: {
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: "http://192.168.0.114:9892", // https://gm.gachafun.com http://gm.zverse.group http://192.168.0.231:9892 192.168.0.114:9892 pre-gm-plugin.gachafun.com
|
||||
target: "https://gm-plugin-fn.gachafun.com", // https://gm.gachafun.com http://gm.zverse.group http://192.168.0.231:9892 192.168.0.114:9892 pre-gm-plugin.gachafun.com
|
||||
changeOrigin: true,
|
||||
// rewrite: (path) => path.replace(/^\/api/, ""),
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user