st-comp 0.0.271 → 0.0.273
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/VarietySearch.cjs +19 -19
- package/es/VarietySearch.js +3429 -3458
- package/es/style.css +1 -1
- package/lib/bundle.js +1 -1
- package/lib/bundle.umd.cjs +183 -183
- package/lib/{index-15c834c8.js → index-ae71be8d.js} +28259 -28288
- package/lib/{python-dcd40798.js → python-74871600.js} +1 -1
- package/lib/style.css +1 -1
- package/package.json +1 -1
- package/packages/VarietySearch/components/CommonIndicator/IndicatorTags.vue +52 -0
- package/packages/VarietySearch/components/CommonIndicator/index.vue +367 -479
- package/packages/VarietySearch/components/CommonIndicator/tools.js +260 -0
- package/packages/VarietySearch/config.js +177 -288
- package/packages/VarietySearch/index.vue +53 -15
- package/src/App.vue +16 -16
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// ==================== 常量 ====================
|
|
2
|
+
|
|
3
|
+
// 成交量 - 时间选项
|
|
4
|
+
export const PER_VOLUMN_RADIO_OPTIONS = [
|
|
5
|
+
{ label: "近2周", value: "1" },
|
|
6
|
+
{ label: "近1个月", value: "2" },
|
|
7
|
+
{ label: "近3个月", value: "3" },
|
|
8
|
+
{ label: "近6个月", value: "4" },
|
|
9
|
+
{ label: "近1年", value: "5" },
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
// 年报审计意见 - 选项
|
|
13
|
+
export const AUDIT_OPINION_TYPE_RADIO_OPTIONS = [
|
|
14
|
+
{ label: "无保留意见", value: 1 },
|
|
15
|
+
{ label: "保留意见", value: 2 },
|
|
16
|
+
{ label: "否定意见", value: 3 },
|
|
17
|
+
{ label: "无法表示意见", value: 4 },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
// ==================== 工具函数 ====================
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 判断两个数组是否有交集
|
|
24
|
+
*/
|
|
25
|
+
export const hasIntersection = (arr1, arr2) => {
|
|
26
|
+
if (!arr1?.length || !arr2?.length) return false;
|
|
27
|
+
return arr1.some((item) => arr2.includes(item));
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 校验是否为有效数字
|
|
32
|
+
*/
|
|
33
|
+
export const isValidNumber = (val) => {
|
|
34
|
+
if (val === "" || val === null || val === undefined) return false;
|
|
35
|
+
return !isNaN(Number(val));
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// ==================== 指标配置(策略模式) ====================
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 获取指标的默认值
|
|
42
|
+
*/
|
|
43
|
+
export const indicatorToGetDefaultValue = (item, rankKey) => {
|
|
44
|
+
const base = {
|
|
45
|
+
key: item.key,
|
|
46
|
+
type: item.type,
|
|
47
|
+
label: item.label,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const defaults = {
|
|
51
|
+
// 是否ST
|
|
52
|
+
st: () => ({ ...base, st: null }),
|
|
53
|
+
// 净利润
|
|
54
|
+
tFeaturelncomes: () => ({
|
|
55
|
+
...base,
|
|
56
|
+
rule: 1, // 1超, 2跌
|
|
57
|
+
withFewYears: null, // 近N年内
|
|
58
|
+
yearsCount: null, // N年以上
|
|
59
|
+
netProfit: null, // 盈利金额(亿)
|
|
60
|
+
}),
|
|
61
|
+
// 股价分位
|
|
62
|
+
priceLevels: () => ({
|
|
63
|
+
...base,
|
|
64
|
+
priceLevels: null, // 选中的分位值(1-5分位)
|
|
65
|
+
}),
|
|
66
|
+
// 市盈率分位
|
|
67
|
+
peTtmLevels: () => ({
|
|
68
|
+
...base,
|
|
69
|
+
peTtmLevels: null, // 选中的分位值(1-10分位)
|
|
70
|
+
}),
|
|
71
|
+
// 市盈率估值走势分位
|
|
72
|
+
peTrendTtmLevels: () => ({
|
|
73
|
+
...base,
|
|
74
|
+
peTrendTtmLevels: null, // 选中的分位值(1-10分位)
|
|
75
|
+
}),
|
|
76
|
+
// 认沽认购
|
|
77
|
+
optionsCpType: () => ({
|
|
78
|
+
...base,
|
|
79
|
+
optionsCpType: null, // 1看涨, 2看跌
|
|
80
|
+
}),
|
|
81
|
+
// 成交量
|
|
82
|
+
perVolumn: () => ({
|
|
83
|
+
...base,
|
|
84
|
+
radioType: item.defaultRadioType,
|
|
85
|
+
range: [null, null],
|
|
86
|
+
unit: item.defaultUnit?.length ? [...item.defaultUnit] : [null, null],
|
|
87
|
+
radio: null, // 时间周期: 1近2周, 2近1个月, 3近3个月, 4近6个月, 5近1年
|
|
88
|
+
levels: [], // 选中的分位值(1-5分位)
|
|
89
|
+
}),
|
|
90
|
+
// 主力净流入资金
|
|
91
|
+
mainFlow: () => ({
|
|
92
|
+
...base,
|
|
93
|
+
radioType: item.defaultRadioType,
|
|
94
|
+
rankRange: [null, null], // 区间范围 [左值, 右值]
|
|
95
|
+
}),
|
|
96
|
+
// 年报审计意见
|
|
97
|
+
auditOpinionType: () => ({
|
|
98
|
+
...base,
|
|
99
|
+
enumType: null, // 1无保留意见, 2保留意见, 3否定意见, 4无法表示意见
|
|
100
|
+
}),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const getDefault = () => ({
|
|
104
|
+
...base,
|
|
105
|
+
range: [null, null],
|
|
106
|
+
unit: item.defaultUnit?.length ? [...item.defaultUnit] : [null, null],
|
|
107
|
+
radioType: rankKey && rankKey !== item.key ? "0" : item.defaultRadioType,
|
|
108
|
+
rankRange: item.defaultRankRange || [null, null],
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return (defaults[item.key] || getDefault)();
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 校验并格式化指标
|
|
116
|
+
* 返回: { valid: boolean, message?: string, tagText?: string }
|
|
117
|
+
*/
|
|
118
|
+
export const indicatorToValidateAndFormatTagText = (value) => {
|
|
119
|
+
const { key, label } = value;
|
|
120
|
+
|
|
121
|
+
// 各指标校验 + 格式化规则
|
|
122
|
+
const rules = {
|
|
123
|
+
// 是否ST
|
|
124
|
+
st: (val) => {
|
|
125
|
+
if (val.st === null) return { valid: false, message: "格式错误: 请至少选择一个值" };
|
|
126
|
+
return { valid: true, tagText: `${label}: ${val.st ? "是" : "否"}` };
|
|
127
|
+
},
|
|
128
|
+
// 净利润
|
|
129
|
+
tFeaturelncomes: (val) => {
|
|
130
|
+
if (["", null].includes(val.withFewYears) || ["", null].includes(val.yearsCount) || ["", null].includes(val.netProfit)) {
|
|
131
|
+
return { valid: false, message: "格式错误: 请填写完整" };
|
|
132
|
+
}
|
|
133
|
+
return { valid: true, tagText: `${label}: ${val.withFewYears}年内${val.yearsCount}年以上盈利${val.rule === 1 ? "超" : "跌"}过${val.netProfit}亿` };
|
|
134
|
+
},
|
|
135
|
+
// 股价分位
|
|
136
|
+
priceLevels: (val) => {
|
|
137
|
+
if (!val.priceLevels?.length) return { valid: false, message: "格式错误: 请至少选择一个值" };
|
|
138
|
+
return { valid: true, tagText: `${label}: 已选${val.priceLevels}分位` };
|
|
139
|
+
},
|
|
140
|
+
// 市盈率分位
|
|
141
|
+
peTtmLevels: (val) => {
|
|
142
|
+
if (!val.peTtmLevels?.length) return { valid: false, message: "格式错误: 请至少选择一个值" };
|
|
143
|
+
return { valid: true, tagText: `${label}: 已选${val.peTtmLevels}分位` };
|
|
144
|
+
},
|
|
145
|
+
// 市盈率估值走势分位
|
|
146
|
+
peTrendTtmLevels: (val) => {
|
|
147
|
+
if (!val.peTrendTtmLevels?.length) return { valid: false, message: "格式错误: 请至少选择一个值" };
|
|
148
|
+
return { valid: true, tagText: `${label}: 已选${val.peTrendTtmLevels}分位` };
|
|
149
|
+
},
|
|
150
|
+
// 认沽认购
|
|
151
|
+
optionsCpType: (val) => {
|
|
152
|
+
if (val.optionsCpType === null) return { valid: false, message: "格式错误: 请至少选择一个值" };
|
|
153
|
+
return { valid: true, tagText: `${label}: ${val.optionsCpType === 1 ? "看涨" : "看跌"}` };
|
|
154
|
+
},
|
|
155
|
+
// 成交量
|
|
156
|
+
perVolumn: (val) => {
|
|
157
|
+
// 数值模式
|
|
158
|
+
if (val.radioType === "0") {
|
|
159
|
+
if (["", null].includes(val.range?.[0]) && ["", null].includes(val.range?.[1])) {
|
|
160
|
+
return { valid: false, message: "格式错误: 请至少填写一个值" };
|
|
161
|
+
}
|
|
162
|
+
const unitLeft = val.unit?.[0] ?? "";
|
|
163
|
+
const unitRight = val.unit?.[1] ?? "";
|
|
164
|
+
let tagText = "";
|
|
165
|
+
if (!["", null].includes(val.range[0]) && !["", null].includes(val.range[1])) {
|
|
166
|
+
tagText = `${label}: ${val.range[0]}${unitLeft} ~ ${val.range[1]}${unitRight}`;
|
|
167
|
+
} else if (!["", null].includes(val.range[0])) {
|
|
168
|
+
tagText = `${label}: ≥${val.range[0]}${unitLeft}`;
|
|
169
|
+
} else {
|
|
170
|
+
tagText = `${label}: ≤${val.range[1]}${unitRight}`;
|
|
171
|
+
}
|
|
172
|
+
return { valid: true, tagText };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 分位模式
|
|
176
|
+
if (!val.radio) return { valid: false, message: "格式错误: 请选择时间周期" };
|
|
177
|
+
if (!val.levels?.length) return { valid: false, message: "格式错误: 请选择分位" };
|
|
178
|
+
const timeLabel = PER_VOLUMN_RADIO_OPTIONS.find((item) => item.value === val.radio)?.label;
|
|
179
|
+
return { valid: true, tagText: `${label}: ${timeLabel}${val.levels.map((i) => `${i}分位`).join("、")}` };
|
|
180
|
+
},
|
|
181
|
+
// 主力净流入资金
|
|
182
|
+
mainFlow: (val) => {
|
|
183
|
+
const { radioType, radio, rankRange } = val;
|
|
184
|
+
// 校验:是否选择时间周期
|
|
185
|
+
if (!radio) return { valid: false, message: "格式错误: 请选择时间周期" };
|
|
186
|
+
// 校验:左右区间至少填一个
|
|
187
|
+
if (!isValidNumber(rankRange[0]) && !isValidNumber(rankRange[1])) {
|
|
188
|
+
return { valid: false, message: "格式错误: 请填写区间范围" };
|
|
189
|
+
}
|
|
190
|
+
// 校验:排名模式下,左值不能大于右值,且不能为负数
|
|
191
|
+
if (radioType !== "0" && (rankRange[0] < 0 || rankRange[1] < 0 || rankRange[0] > rankRange[1])) {
|
|
192
|
+
return { valid: false, message: "格式错误: 无效的排名范围" };
|
|
193
|
+
}
|
|
194
|
+
// 格式化文案
|
|
195
|
+
let tagText = "";
|
|
196
|
+
// 左右都填
|
|
197
|
+
if (!["", null].includes(rankRange[0]) && !["", null].includes(rankRange[1])) {
|
|
198
|
+
tagText = `${label}: ${radio}日${radioType === "0" ? "涨幅范围" : "排名范围"}: ${rankRange[0]}${radioType === "0" ? "%" : ""} ~ ${rankRange[1]}${radioType === "0" ? "%" : ""}`;
|
|
199
|
+
}
|
|
200
|
+
// 只填左 → ≥
|
|
201
|
+
else if (!["", null].includes(rankRange[0])) {
|
|
202
|
+
tagText = `${label}: ${radio}日${radioType === "0" ? "涨幅范围" : "排名范围"}: ≥${rankRange[0]}${radioType === "0" ? "%" : ""}`;
|
|
203
|
+
}
|
|
204
|
+
// 只填右 → ≤
|
|
205
|
+
else {
|
|
206
|
+
tagText = `${label}: ${radio}日${radioType === "0" ? "涨幅范围" : "排名范围"}: ≤${rankRange[1]}${radioType === "0" ? "%" : ""}`;
|
|
207
|
+
}
|
|
208
|
+
return { valid: true, tagText };
|
|
209
|
+
},
|
|
210
|
+
// 年报审计意见
|
|
211
|
+
auditOpinionType: (val) => {
|
|
212
|
+
if (val.enumType === null) return { valid: false, message: "格式错误: 请至少选择一个值" };
|
|
213
|
+
const labelText = AUDIT_OPINION_TYPE_RADIO_OPTIONS.find((item) => item.value === val.enumType)?.label;
|
|
214
|
+
return { valid: true, tagText: `${label}: ${labelText}` };
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// 通用默认规则(适用于没有特殊配置的指标)
|
|
219
|
+
const getDefault = (val) => {
|
|
220
|
+
const { label, unit, radioType } = val;
|
|
221
|
+
const range = radioType === "1" ? val.rankRange : val.range;
|
|
222
|
+
|
|
223
|
+
// 校验:左右至少填一个
|
|
224
|
+
if (["", null].includes(range[0]) && ["", null].includes(range[1])) {
|
|
225
|
+
return { valid: false, message: "格式错误: 请至少填写一个值" };
|
|
226
|
+
}
|
|
227
|
+
// 校验:排名模式特殊规则
|
|
228
|
+
if (radioType === "1") {
|
|
229
|
+
// 必须是有效数字
|
|
230
|
+
if (!isValidNumber(range[0]) && !isValidNumber(range[1])) {
|
|
231
|
+
return { valid: false, message: "格式错误: 请填写有效排名" };
|
|
232
|
+
}
|
|
233
|
+
// 不能为负数,左值不能大于右值
|
|
234
|
+
if (range[0] < 0 || range[1] < 0 || range[0] > range[1]) {
|
|
235
|
+
return { valid: false, message: "格式错误: 无效的排名范围" };
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// 单位处理:排名模式没有单位
|
|
240
|
+
const unitLeft = radioType === "1" ? "" : (unit?.[0] ?? "");
|
|
241
|
+
const unitRight = radioType === "1" ? "" : (unit?.[1] ?? "");
|
|
242
|
+
|
|
243
|
+
// 格式化文案
|
|
244
|
+
let tagText = "";
|
|
245
|
+
// 左右都填
|
|
246
|
+
if (!["", null].includes(range[0]) && !["", null].includes(range[1])) {
|
|
247
|
+
tagText = `${label}${radioType === "1" ? "排名" : ""}: ${range[0]}${unitLeft} ~ ${range[1]}${unitRight}`;
|
|
248
|
+
// 只填左 → ≥
|
|
249
|
+
} else if (!["", null].includes(range[0])) {
|
|
250
|
+
tagText = `${label}${radioType === "1" ? "排名" : ""}: ≥${range[0]}${unitLeft}`;
|
|
251
|
+
// 只填右 → ≤
|
|
252
|
+
} else {
|
|
253
|
+
tagText = `${label}${radioType === "1" ? "排名" : ""}: ≤${range[1]}${unitRight}`;
|
|
254
|
+
}
|
|
255
|
+
return { valid: true, tagText };
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// 如果有对应的规则则使用,否则使用通用默认规则
|
|
259
|
+
return (rules[key] || getDefault)(value);
|
|
260
|
+
};
|