feat(stock): ✨️完成公告板查询
This commit is contained in:
parent
ccd19641d8
commit
ab234cd9e4
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"useTabs": true,
|
"useTabs": true,
|
||||||
"tabWidth": 4,
|
"tabWidth": 2,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"trailingComma": "none",
|
"trailingComma": "none",
|
||||||
"printWidth": 100
|
"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 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([
|
const App = createApp([
|
||||||
{
|
{
|
||||||
path: "hello-world",
|
path: "stock-query",
|
||||||
element: <HelloWorld/>
|
element: <StockQuery/>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "hello-2",
|
path: "setting",
|
||||||
element: <HelloWorld/>
|
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": {
|
"paths": {
|
||||||
"@/*": [
|
"@/*": [
|
||||||
"./src/*"
|
"./src/*"
|
||||||
|
],
|
||||||
|
"@package/*": [
|
||||||
|
"./package/package/*"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
/* Linting */
|
/* Linting */
|
||||||
@ -25,6 +28,9 @@
|
|||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"noFallthroughCasesInSwitch": false
|
"noFallthroughCasesInSwitch": false
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": [
|
||||||
|
"src",
|
||||||
|
"package/package"
|
||||||
|
],
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ export default defineConfig(({mode}) => {
|
|||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/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,
|
changeOrigin: true,
|
||||||
// rewrite: (path) => path.replace(/^\/api/, ""),
|
// rewrite: (path) => path.replace(/^\/api/, ""),
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user