944 lines
29 KiB
TypeScript
944 lines
29 KiB
TypeScript
import {AlertComponent, attachmentAdpator, makeTranslator, render, type Schema, ToastComponent} from 'amis'
|
|
import 'amis/lib/themes/antd.css'
|
|
import 'amis/lib/helper.css'
|
|
import 'amis/sdk/iconfont.css'
|
|
import '@fortawesome/fontawesome-free/css/all.min.css'
|
|
import axios from 'axios'
|
|
import {isEqual, isNil} from 'es-toolkit'
|
|
// @ts-ignore
|
|
import type {ColumnSchema} from 'amis/lib/renderers/Table2'
|
|
import {toNumber} from 'es-toolkit/compat'
|
|
|
|
export const commonInfo = {
|
|
debug: isEqual(import.meta.env.MODE, 'development'),
|
|
baseUrl: isEqual(import.meta.env.MODE, 'development') ? 'http://localhost:9786' : '',
|
|
}
|
|
|
|
const __ = makeTranslator('zh')
|
|
|
|
const responseAdaptor = () => (response: any) => {
|
|
let payload = response.data || {} // blob 下可能会返回内容为空?
|
|
if (payload.hasOwnProperty('errno')) {
|
|
payload.status = payload.errno
|
|
payload.msg = payload.errmsg
|
|
} else if (payload.hasOwnProperty('no')) {
|
|
payload.status = payload.no
|
|
payload.msg = payload.error
|
|
}
|
|
return {
|
|
...response,
|
|
data: payload,
|
|
}
|
|
}
|
|
|
|
export const amisRender = (schema: Schema, data: Record<any, any> = {}) => {
|
|
const theme = 'antd'
|
|
const locale = 'zh-CN'
|
|
return (
|
|
<>
|
|
<ToastComponent
|
|
theme={theme}
|
|
key="toast"
|
|
position={'top-right'}
|
|
locale={locale}
|
|
/>
|
|
<AlertComponent theme={theme} key="alert" locale={locale}/>
|
|
{render(
|
|
schema,
|
|
{
|
|
data: {
|
|
...commonInfo,
|
|
...data,
|
|
},
|
|
theme: theme,
|
|
},
|
|
{
|
|
enableAMISDebug: commonInfo.debug,
|
|
fetcher: async (api: any) => {
|
|
let {url, method, data, responseType, config, headers} = api
|
|
config = config || {}
|
|
config.url = url
|
|
config.withCredentials = true
|
|
responseType && (config.responseType = responseType)
|
|
|
|
if (config.cancelExecutor) {
|
|
config.cancelToken = new (axios as any).CancelToken(
|
|
config.cancelExecutor,
|
|
)
|
|
}
|
|
|
|
config.headers = headers || {}
|
|
config.method = method
|
|
config.data = data
|
|
|
|
if (method === 'get' && data) {
|
|
config.params = data
|
|
} else if (data && data instanceof FormData) {
|
|
// config.headers['Content-Type'] = 'multipart/form-data';
|
|
} else if (
|
|
data &&
|
|
typeof data !== 'string' &&
|
|
!(data instanceof Blob) &&
|
|
!(data instanceof ArrayBuffer)
|
|
) {
|
|
data = JSON.stringify(data)
|
|
config.headers['Content-Type'] = 'application/json'
|
|
}
|
|
|
|
// 支持返回各种报错信息
|
|
config.validateStatus = function () {
|
|
return true
|
|
}
|
|
|
|
let response = await axios(config)
|
|
response = await attachmentAdpator(response, __, api)
|
|
response = responseAdaptor()(response)
|
|
|
|
if (response.status >= 400) {
|
|
if (response.data) {
|
|
// 主要用于 raw: 模式下,后端自己校验登录,
|
|
if (
|
|
response.status === 401 &&
|
|
response.data.location &&
|
|
response.data.location.startsWith('http')
|
|
) {
|
|
location.href = response.data.location.replace(
|
|
'{{redirect}}',
|
|
encodeURIComponent(location.href),
|
|
)
|
|
return new Promise(() => {
|
|
})
|
|
} else if (response.data.msg) {
|
|
throw new Error(response.data.msg)
|
|
} else {
|
|
throw new Error(
|
|
'System.requestError' + JSON.stringify(response.data, null, 2),
|
|
)
|
|
}
|
|
} else {
|
|
throw new Error(
|
|
`${'System.requestErrorStatus'} ${response.status}`,
|
|
)
|
|
}
|
|
}
|
|
|
|
return response
|
|
},
|
|
isCancel: (value: any) => (axios as any).isCancel(value),
|
|
},
|
|
)}
|
|
</>
|
|
)
|
|
}
|
|
|
|
export function horizontalFormOptions() {
|
|
return {
|
|
mode: 'horizontal',
|
|
horizontal: {
|
|
leftFixed: 'sm',
|
|
},
|
|
}
|
|
}
|
|
|
|
export function crudCommonOptions() {
|
|
return {
|
|
affixHeader: false,
|
|
stopAutoRefreshWhenModalIsOpen: true,
|
|
resizable: false,
|
|
syncLocation: false,
|
|
silentPolling: true,
|
|
columnsTogglable: false,
|
|
}
|
|
}
|
|
|
|
export function readOnlyDialogOptions() {
|
|
return {
|
|
actions: [],
|
|
showCloseButton: false,
|
|
closeOnEsc: true,
|
|
closeOnOutside: true,
|
|
disabled: true,
|
|
}
|
|
}
|
|
|
|
export function paginationCommonOptions(perPage = true, maxButtons = 5) {
|
|
let option = {
|
|
type: 'pagination',
|
|
layout: [
|
|
'pager',
|
|
],
|
|
maxButtons: maxButtons,
|
|
showPageInput: false,
|
|
perPageAvailable: [10, 15, 20, 50, 100, 200],
|
|
}
|
|
if (perPage) {
|
|
option.layout.push('perPage')
|
|
}
|
|
return option
|
|
}
|
|
|
|
export function paginationTemplate(perPage = 20, maxButtons = 5, extraHeaders: Array<Schema | string> = [], extraFooters: Array<Schema | string> = []) {
|
|
return {
|
|
perPage: perPage,
|
|
headerToolbar: [
|
|
'reload',
|
|
paginationCommonOptions(true, maxButtons),
|
|
...extraHeaders,
|
|
],
|
|
footerToolbar: [
|
|
'statistics',
|
|
paginationCommonOptions(true, maxButtons),
|
|
...extraFooters,
|
|
],
|
|
}
|
|
}
|
|
|
|
export function copyField(field: string, tips = '复制', ignoreLength = 0) {
|
|
let tpl = ignoreLength === 0 ? `\${${field}}` : `\${TRUNCATE(${field}, ${ignoreLength})}`
|
|
return {
|
|
type: 'wrapper',
|
|
size: 'none',
|
|
body: [
|
|
{
|
|
type: 'tpl',
|
|
className: 'mr-1',
|
|
tpl: tpl,
|
|
},
|
|
{
|
|
type: 'action',
|
|
level: 'link',
|
|
label: '',
|
|
icon: 'fa fa-copy',
|
|
size: 'xs',
|
|
actionType: 'copy',
|
|
content: `\$${field}`,
|
|
tooltip: `${tips}`,
|
|
},
|
|
],
|
|
}
|
|
}
|
|
|
|
export function mappingItem(label: string, value: string, color = 'bg-info') {
|
|
return {
|
|
label: label,
|
|
value: value,
|
|
color: color,
|
|
}
|
|
}
|
|
|
|
export function mappingField(field: string, mapping: Array<Record<string, string>>) {
|
|
let mapData: Record<string, string> = {
|
|
'*': `<span class='label bg-gray-300'>\${${field}}</span>`,
|
|
}
|
|
mapping.forEach(item => {
|
|
mapData[item['value']] = `<span class='label ${item['color']}'>${item['label']}</span>`
|
|
})
|
|
return {
|
|
type: 'mapping',
|
|
value: `\${${field}}`,
|
|
map: mapData,
|
|
}
|
|
}
|
|
|
|
export function filterableField(mapping: Array<Record<string, any>>, multiple = false) {
|
|
return {
|
|
multiple: multiple,
|
|
options: [
|
|
...mapping,
|
|
],
|
|
}
|
|
}
|
|
|
|
export function time(field: string) {
|
|
return {
|
|
type: 'tpl',
|
|
tpl: `\${IF(${field}, DATETOSTR(${field}, 'YYYY-MM-DD HH:mm:ss'), '/')}`,
|
|
}
|
|
}
|
|
|
|
export function date(field: string) {
|
|
return {
|
|
type: 'tpl',
|
|
tpl: `\${IF(${field}, DATETOSTR(${field}, 'YYYY-MM-DD'), '/')}`,
|
|
}
|
|
}
|
|
|
|
export function pictureFromIds(field: string) {
|
|
return `\${ARRAYMAP(${field},id => '${commonInfo.baseUrl}/upload/download/' + id)}`
|
|
}
|
|
|
|
export const formInputFileStaticColumns = [
|
|
{
|
|
name: 'filename',
|
|
label: '文件名',
|
|
},
|
|
{
|
|
type: 'operation',
|
|
label: '操作',
|
|
width: 140,
|
|
buttons: [
|
|
{
|
|
type: 'action',
|
|
label: '预览',
|
|
level: 'link',
|
|
icon: 'fas fa-eye',
|
|
},
|
|
{
|
|
type: 'action',
|
|
label: '下载',
|
|
level: 'link',
|
|
icon: 'fa fa-download',
|
|
actionType: 'ajax',
|
|
// api: {
|
|
// ...apiGet('${base}/upload/download/${id}'),
|
|
// responseType: 'blob',
|
|
// }
|
|
},
|
|
],
|
|
},
|
|
]
|
|
|
|
export function formInputSingleFileStatic(field: string, label: string) {
|
|
return {
|
|
visibleOn: '${static}',
|
|
type: 'control',
|
|
label: label,
|
|
required: true,
|
|
body: {
|
|
type: 'table',
|
|
source: `\${${field}|asArray}`,
|
|
columns: formInputFileStaticColumns,
|
|
},
|
|
}
|
|
}
|
|
|
|
export function formInputMultiFileStatic(field: string, label: string) {
|
|
return {
|
|
visibleOn: '${static}',
|
|
type: 'input-table',
|
|
label: label,
|
|
name: field,
|
|
required: true,
|
|
resizable: false,
|
|
columns: formInputFileStaticColumns,
|
|
}
|
|
}
|
|
|
|
export function remoteOptions(type: string = 'select', name: string) {
|
|
return {
|
|
type: type,
|
|
source: `get:${commonInfo.baseUrl}/constants/options/${name}`,
|
|
}
|
|
}
|
|
|
|
export function remoteMappings(name: string, field: string) {
|
|
return {
|
|
type: 'mapping',
|
|
source: `get:${commonInfo.baseUrl}/constants/mappings/${name}/${field}`,
|
|
}
|
|
}
|
|
|
|
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 = (idField: string, label: string, type: FinanceType, field: string): Schema => {
|
|
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: [
|
|
{
|
|
visibleOn: `\${!${idField}}`,
|
|
type: 'tpl',
|
|
tpl: label,
|
|
},
|
|
{
|
|
visibleOn: `\${${idField}}`,
|
|
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/\${${idField}}/${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}<br/>${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)
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
],
|
|
}
|
|
}
|
|
|
|
export function stockListColumns(idField: string = 'id', extraColumns: Array<ColumnSchema> = []) {
|
|
return [
|
|
{
|
|
name: 'code',
|
|
label: '编号',
|
|
width: 150,
|
|
},
|
|
{
|
|
name: 'name',
|
|
label: '简称',
|
|
width: 100,
|
|
},
|
|
{
|
|
name: 'fullname',
|
|
label: '全名',
|
|
},
|
|
{
|
|
name: 'market',
|
|
label: '市场',
|
|
width: 100,
|
|
align: 'center',
|
|
...remoteMappings('stock_market', 'market'),
|
|
},
|
|
{
|
|
name: 'industry',
|
|
label: '行业',
|
|
width: 80,
|
|
},
|
|
{
|
|
label: '上市日期',
|
|
width: 100,
|
|
align: 'center',
|
|
...date('listedDate'),
|
|
},
|
|
...extraColumns,
|
|
{
|
|
type: 'operation',
|
|
label: '操作',
|
|
width: 100,
|
|
buttons: [
|
|
{
|
|
type: 'action',
|
|
label: '详情',
|
|
level: 'link',
|
|
actionType: 'dialog',
|
|
dialog: {
|
|
title: '股票详情',
|
|
size: 'full',
|
|
...readOnlyDialogOptions(),
|
|
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/\${${idField}}`,
|
|
body: [
|
|
'资产负债表',
|
|
{
|
|
className: 'my-2',
|
|
type: 'property',
|
|
column: 4,
|
|
items: [
|
|
{
|
|
label: financePropertyLabel(idField, '总资产', 'FINANCE', 'totalAssets'),
|
|
content: '${balanceSheet.totalAssets}',
|
|
span: 2,
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '总负债', 'FINANCE', 'totalLiabilities'),
|
|
content: '${balanceSheet.totalLiabilities}',
|
|
span: 2,
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '流动资产', 'FINANCE', 'currentAssets'),
|
|
content: '${balanceSheet.currentAssets}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '流动资产占比', 'PERCENTAGE', 'currentAssetsToTotalAssetsRatio'),
|
|
content: '${balanceSheet.currentAssetsRatio}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '流动负债', 'FINANCE', 'currentLiabilities'),
|
|
content: '${balanceSheet.currentLiabilities}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '流动负债占比', 'PERCENTAGE', 'currentLiabilitiesToTotalAssetsRatio'),
|
|
content: '${balanceSheet.currentLiabilitiesRatio}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '非流动资产', 'FINANCE', 'fixedAssets'),
|
|
content: '${balanceSheet.fixedAssets}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '非流动资产占比', 'PERCENTAGE', 'fixedAssetsToTotalAssetsRatio'),
|
|
content: '${balanceSheet.fixedAssetsRatio}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '非流动负债', 'FINANCE', 'longTermLiabilities'),
|
|
content: '${balanceSheet.longTermLiabilities}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '非流动负债占比', 'PERCENTAGE', 'longTermLiabilitiesToTotalAssetsRatio'),
|
|
content: '${balanceSheet.longTermLiabilitiesRatio}',
|
|
},
|
|
],
|
|
},
|
|
'利润表',
|
|
{
|
|
className: 'my-2',
|
|
type: 'property',
|
|
items: [
|
|
{
|
|
label: financePropertyLabel(idField, '营业收入', 'FINANCE', 'operatingRevenue'),
|
|
content: '${income.operatingRevenue}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '营业成本', 'FINANCE', 'operatingCost'),
|
|
content: '${income.operatingCost}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '营业利润', 'FINANCE', 'operatingProfit'),
|
|
content: '${income.operatingProfit}',
|
|
},
|
|
],
|
|
},
|
|
'现金流量表',
|
|
{
|
|
className: 'my-2',
|
|
type: 'property',
|
|
items: [
|
|
{
|
|
label: financePropertyLabel(idField, '净利润', 'FINANCE', 'netProfit'),
|
|
content: '${cashFlow.netProfit}',
|
|
span: 3,
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '营业活动现金流量', 'FINANCE', 'cashFlowFromOperatingActivities'),
|
|
content: '${cashFlow.cashFlowFromOperatingActivities}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '投资活动现金流量', 'FINANCE', 'cashFlowFromInvestingActivities'),
|
|
content: '${cashFlow.cashFlowFromInvestingActivities}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '筹资活动现金流量', 'FINANCE', 'cashFlowFromFinancingActivities'),
|
|
content: '${cashFlow.cashFlowFromFinancingActivities}',
|
|
},
|
|
],
|
|
},
|
|
'财务指标',
|
|
{
|
|
className: 'my-2',
|
|
type: 'property',
|
|
column: 4,
|
|
items: [
|
|
{
|
|
label: financePropertyLabel(idField, '流动比率', 'FINANCE', 'currentRatio'),
|
|
content: '${indicate.currentRatio}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '速动比率', 'FINANCE', 'quickRatio'),
|
|
content: '${indicate.quickRatio}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, 'ROE', 'FINANCE', 'returnOnEquity'),
|
|
content: '${indicate.roe}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, 'ROA', 'FINANCE', 'returnOnAssets'),
|
|
content: '${indicate.roa}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '应收账款周转率', 'FINANCE', 'accountsReceivableTurnover'),
|
|
content: '${indicate.accountsReceivableTurnover}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '应收账款周转天数', 'DAYS', 'daysAccountsReceivableTurnover'),
|
|
content: '${indicate.daysAccountsReceivableTurnover}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '存货周转率', 'FINANCE', 'inventoryTurnover'),
|
|
content: '${indicate.inventoryTurnover}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '存货周转天数', 'DAYS', 'daysInventoryTurnover'),
|
|
content: '${indicate.daysInventoryTurnover}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '固定资产周转率', 'FINANCE', 'fixedAssetsTurnover'),
|
|
content: '${indicate.fixedAssetsTurnover}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '固定资产周转天数', 'DAYS', 'daysFixedAssetsTurnover'),
|
|
content: '${indicate.daysFixedAssetsTurnover}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '总资产周转率', 'FINANCE', 'totalAssetsTurnover'),
|
|
content: '${indicate.totalAssetsTurnover}',
|
|
},
|
|
{
|
|
label: financePropertyLabel(idField, '总资产周转天数', 'DAYS', 'daysTotalAssetsTurnover'),
|
|
content: '${indicate.daysTotalAssetsTurnover}',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{type: 'divider'},
|
|
{
|
|
type: 'service',
|
|
api: `get:${commonInfo.baseUrl}/stock/daily/current/\${${idField}}`,
|
|
body: [
|
|
"现价 (${date})",
|
|
{
|
|
className: 'my-2',
|
|
type: 'property',
|
|
column: 4,
|
|
items: [
|
|
{label: '开盘价', content: '${open}'},
|
|
{label: '收盘价', content: '${close}'},
|
|
{label: '最高价', content: '${high}'},
|
|
{label: '最低价', content: '${low}'},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
type: 'chart',
|
|
title: '100日线数据',
|
|
height: 500,
|
|
api: `get:${commonInfo.baseUrl}/stock/daily/\${${idField}}`,
|
|
config: {
|
|
title: {
|
|
text: '100日线数据',
|
|
subtext: '后复权数据',
|
|
},
|
|
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 `<div class="text-center font-bold mb-2">${param.name}</div>
|
|
<div class="text-center">
|
|
<span>开盘:</span>
|
|
<span class="font-bold ml-4">${open}</span>
|
|
</div>
|
|
<div class="text-center">
|
|
<span>收盘:</span>
|
|
<span class="font-bold ml-4">${close}</span>
|
|
</div>
|
|
<div class="text-center">
|
|
<span>最低:</span>
|
|
<span class="font-bold ml-4">${lowest}</span>
|
|
</div>
|
|
<div class="text-center">
|
|
<span>最高:</span>
|
|
<span class="font-bold ml-4">${highest}</span>
|
|
</div>`
|
|
},
|
|
},
|
|
grid: {
|
|
left: '2%',
|
|
right: '2%',
|
|
top: '15%',
|
|
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,
|
|
},
|
|
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
]
|
|
}
|