st-comp 0.0.270 → 0.0.272

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.
@@ -1,308 +1,91 @@
1
1
  <!-- 常用指标组件 -->
2
2
  <script setup name="CommonIndicator">
3
3
  import { ref, watch, computed, inject } from "vue";
4
-
5
- const perVolumnRadioOptions = [
6
- { label: "近2周", value: "1" },
7
- { label: "近1个月", value: "2" },
8
- { label: "近3个月", value: "3" },
9
- { label: "近6个月", value: "4" },
10
- { label: "近1年", value: "5" },
11
- ];
12
- const auditOpinionTypeRadioOptions = [
13
- { label: "无保留意见", value: 1 },
14
- { label: "保留意见", value: 2 },
15
- { label: "否定意见", value: 3 },
16
- { label: "无法表示意见", value: 4 },
17
- ];
4
+ import { PER_VOLUMN_RADIO_OPTIONS, AUDIT_OPINION_TYPE_RADIO_OPTIONS, hasIntersection, indicatorToGetDefaultValue, indicatorToValidateAndFormatTagText } from "./tools";
5
+ import IndicatorTags from "./IndicatorTags.vue";
18
6
 
19
7
  const clearRow = inject("clearRow");
20
8
  const data = defineModel("data", { default: [] });
21
9
  const props = defineProps({
22
- config: { type: Object, default: () => {} },
23
- varietyMarket: { type: null || Number, default: () => null }, // 已选品种市场
24
- commonOption: { type: Array, default: () => [] }, // 已选常用选项
10
+ config: { type: Object, default: () => {} }, // 常用指标配置
11
+ varietyMarket: { type: null || Number, default: () => null },
12
+ commonOption: { type: Array, default: () => [] },
25
13
  });
14
+
15
+ // 指标窗口开关
26
16
  const visible = ref(false);
17
+
18
+ // 当前已选指标中,第一个开启排名模式的 key(用于互斥:同一时间只能有一个指标使用排名)
27
19
  const rankKey = computed(() => {
28
- return props.data.find((i) => i.radioType === "1")?.key;
20
+ return data.value.find((i) => i.radioType === "1")?.key;
29
21
  });
30
22
 
31
- // 常用指标项数据源 [受到: 品种市场, 常用选项影响]
32
- const commonIndicatorOptions = computed(() => {
33
- let result = [];
34
- // 如果品种市场和常用选项一个都没有选, 那么就展示全部的常用指标
35
- if (props.varietyMarket === null && !props.commonOption.length) {
36
- result = props.config.options;
37
- } else {
38
- result = props.config.options;
39
- // 1. 如果选择了品种市场, 进行一波筛选
40
- if (props.varietyMarket) {
41
- result = result.filter(({ parent }) => parent.varietyMarketIds.includes(props.varietyMarket));
42
- }
43
- // 2. 如果选择了常用选项, 进行一波筛选
44
- if (props.commonOption.length) {
45
- result = result.filter(({ parent }) => {
46
- const { commonOptionIds } = parent;
47
- // 只要选择了的常用选项里面, 有commonOptionIds中的值, 那么就展示, 即判断两个数组是否有重复元素
48
- return [...new Set([...commonOptionIds, ...props.commonOption])].length !== [...commonOptionIds, ...props.commonOption].length;
49
- });
50
- }
23
+ // 指标项数据源
24
+ const indicatorOptions = computed(() => {
25
+ let result = props.config.options;
26
+ // 如果外部有选品种市场条件, 进行过滤
27
+ if (props.varietyMarket !== null && props.varietyMarket !== undefined) {
28
+ result = result.filter(({ parent }) => parent.varietyMarketIds.includes(props.varietyMarket));
29
+ }
30
+ // 如果外部有选常用选项条件, 进行过滤
31
+ if (props.commonOption.length > 0) {
32
+ result = result.filter(({ parent }) => hasIntersection(parent.commonOptionIds, props.commonOption));
51
33
  }
52
34
  return result;
53
35
  });
54
- // 当前操作的指标项类型 [用于弹窗判断是哪个指标]
55
- const nowIndicator = ref({});
56
- // 当前指标项内容中的值 [具体填写的数据]
57
- const indicatorValue = ref({});
58
36
 
59
- // 点击指标选项 (**新增custom配置项时, 需要改动**)
37
+ // 当前选中的指标
38
+ const currentIndicatorConfig = ref({});
39
+ const currentIndicatorForm = ref({});
40
+
41
+ // 指标: 点击
60
42
  const clickIndicator = (item) => {
61
- nowIndicator.value = item;
62
- // 初始化指标值
63
- const baseParams = {
64
- key: item.key,
65
- type: item.type,
66
- label: item.label,
67
- };
68
- switch (item.key) {
69
- // 是否ST
70
- case "st": {
71
- indicatorValue.value = { ...baseParams, st: null };
72
- break;
73
- }
74
- // 净利润
75
- case "tFeaturelncomes": {
76
- indicatorValue.value = {
77
- ...baseParams,
78
- rule: 1, // 1超 2跌
79
- withFewYears: null,
80
- yearsCount: null,
81
- netProfit: null,
82
- };
83
- break;
84
- }
85
- // 股价分位
86
- case "priceLevels": {
87
- indicatorValue.value = { ...baseParams, priceLevels: null };
88
- break;
89
- }
90
- // 市盈率分位
91
- case "peTtmLevels": {
92
- indicatorValue.value = { ...baseParams, peTtmLevels: null };
93
- break;
94
- }
95
- // 市盈率估值走势分位
96
- case "peTrendTtmLevels": {
97
- indicatorValue.value = { ...baseParams, peTrendTtmLevels: null };
98
- break;
99
- }
100
- // 认沽认购
101
- case "optionsCpType": {
102
- indicatorValue.value = {
103
- ...baseParams,
104
- optionsCpType: null, // 1看涨 2看跌
105
- };
106
- break;
107
- }
108
- // 成交量
109
- case "perVolumn": {
110
- indicatorValue.value = {
111
- ...baseParams,
112
- radio: null,
113
- levels: [],
114
- };
115
- break;
116
- }
117
- // 主力净流入资金
118
- case "mainFlow": {
119
- indicatorValue.value = {
120
- ...baseParams,
121
- radioType: "0",
122
- rankRange: [null, null],
123
- };
124
- break;
125
- }
126
- // 年报审计意见
127
- case "auditOpinionType": {
128
- indicatorValue.value = { ...baseParams, enumType: null };
129
- break;
130
- }
131
- // 其它的通用处理
132
- default: {
133
- indicatorValue.value = {
134
- ...baseParams,
135
- range: [null, null],
136
- unit: item.defaultUnit.length ? [...item.defaultUnit] : [null, null],
137
- radioType: rankKey.value && rankKey.value !== item.key ? "0" : item.defaultRadioType, // 0数值 1排名
138
- rankRange: item.defaultRankRange || [null, null], // 默认排名
139
- };
140
- }
141
- }
43
+ currentIndicatorConfig.value = item;
44
+ // 赋予指标对应的默认值
45
+ currentIndicatorForm.value = indicatorToGetDefaultValue(item, rankKey.value);
142
46
  visible.value = true;
143
47
  };
144
- // 确定指标选项 (**新增custom配置项时, 需要改动**)
48
+
49
+ // 指标: 窗口确认
145
50
  const submitDialog = () => {
146
- // 校验 + 格式化文案
147
- switch (indicatorValue.value.key) {
148
- // 是否ST
149
- case "st": {
150
- const { label, st } = indicatorValue.value;
151
- // 校验
152
- if (st === null) return ElMessage.warning("格式错误: 请至少选择一个值");
153
- // 格式化文案
154
- indicatorValue.value.tagText = `${label}: ${st ? "是" : "否"}`;
155
- break;
156
- }
157
- // 净利润
158
- case "tFeaturelncomes": {
159
- const { label, rule, withFewYears, yearsCount, netProfit } = indicatorValue.value;
160
- // 校验
161
- if (["", null].includes(withFewYears) || ["", null].includes(yearsCount) || ["", null].includes(netProfit)) {
162
- return ElMessage.warning("格式错误: 请填写完整");
163
- }
164
- // 格式化文案
165
- indicatorValue.value.tagText = `${label}: ${withFewYears}年内${yearsCount}年以上盈利${rule === 1 ? "超" : "跌"}过${netProfit}亿`;
166
- break;
167
- }
168
- // 股价分位
169
- case "priceLevels": {
170
- const { label, priceLevels } = indicatorValue.value;
171
- // 校验
172
- if (!priceLevels || priceLevels.length === 0) return ElMessage.warning("格式错误: 请至少选择一个值");
173
- // 格式化文案
174
- indicatorValue.value.tagText = `${label}: 已选${priceLevels}分位`;
175
- break;
176
- }
177
- // 市盈率分位
178
- case "peTtmLevels": {
179
- const { label, peTtmLevels } = indicatorValue.value;
180
- // 校验
181
- if (!peTtmLevels || peTtmLevels.length === 0) return ElMessage.warning("格式错误: 请至少选择一个值");
182
- // 格式化文案
183
- indicatorValue.value.tagText = `${label}: 已选${peTtmLevels}分位`;
184
- break;
185
- }
186
- // 市盈率估值走势分位
187
- case "peTrendTtmLevels": {
188
- const { label, peTrendTtmLevels } = indicatorValue.value;
189
- // 校验
190
- if (!peTrendTtmLevels || peTrendTtmLevels.length === 0) return ElMessage.warning("格式错误: 请至少选择一个值");
191
- // 格式化文案
192
- indicatorValue.value.tagText = `${label}: 已选${peTrendTtmLevels}分位`;
193
- break;
194
- }
195
- // 认沽认购
196
- case "optionsCpType": {
197
- const { label, optionsCpType } = indicatorValue.value;
198
- // 校验
199
- if (optionsCpType === null) return ElMessage.warning("格式错误: 请至少选择一个值");
200
- // 格式化文案
201
- indicatorValue.value.tagText = `${label}: ${optionsCpType === 1 ? "看涨" : "看跌"}`;
202
- break;
203
- }
204
- // 成交量
205
- case "perVolumn": {
206
- const { label, radio, levels } = indicatorValue.value;
207
- // 校验
208
- if (!radio) return ElMessage.warning("格式错误: 请选择时间");
209
- if (!levels?.length) return ElMessage.warning("格式错误: 请选择分位");
210
- // 格式化文案
211
- indicatorValue.value.tagText = `${label}: ${perVolumnRadioOptions.find((item) => item.value === radio)?.label}${levels.map((i) => `${i}分位`).join("、")}`;
212
- break;
213
- }
214
- // 主力净流入资金
215
- case "mainFlow": {
216
- const { label, radioType, radio, rankRange } = indicatorValue.value;
217
- const checkNumber = (val) => {
218
- if (val === "" || val === null || val === undefined) return false;
219
- const num = Number(val);
220
- if (isNaN(num)) return false;
221
- return true;
222
- };
223
- // 校验
224
- if (!radio) return ElMessage.warning("格式错误: 请选择时间");
225
- if (!checkNumber(rankRange[0]) && !checkNumber(rankRange[1])) {
226
- return ElMessage.warning(`格式错误: 请填写${radioType === "0" ? "涨幅范围" : "排名范围"}`);
227
- }
228
- // 格式化文案
229
- if (!["", null].includes(rankRange[0]) && !["", null].includes(rankRange[1])) {
230
- indicatorValue.value.tagText = `${label}: ${radio}日${radioType === "0" ? "涨幅范围" : "排名范围"}: ${rankRange[0]}${radioType === "0" ? "%" : ""} ~ ${rankRange[1]}${
231
- radioType === "0" ? "%" : ""
232
- }`;
233
- } else if (!["", null].includes(rankRange[0])) {
234
- indicatorValue.value.tagText = `${label}: ${radio}日${radioType === "0" ? "涨幅范围" : "排名范围"}: ≥${rankRange[0]}${radioType === "0" ? "%" : ""}`;
235
- } else {
236
- indicatorValue.value.tagText = `${label}: ${radio}日${radioType === "0" ? "涨幅范围" : "排名范围"}: ≤${rankRange[1]}${radioType === "0" ? "%" : ""}`;
237
- }
238
- break;
239
- }
240
- // 年报审计意见
241
- case "auditOpinionType": {
242
- const { label, enumType } = indicatorValue.value;
243
- // 校验
244
- if (enumType === null) return ElMessage.warning("格式错误: 请至少选择一个值");
245
- // 格式化文案
246
- indicatorValue.value.tagText = `${label}: ${auditOpinionTypeRadioOptions.find((item) => item.value === enumType)?.label}`;
247
- break;
248
- }
249
- // 其它的通用处理
250
- default: {
251
- const { label, unit, radioType } = indicatorValue.value;
252
- const range = radioType === "1" ? indicatorValue.value.rankRange : indicatorValue.value.range;
253
- // 校验
254
- if (["", null].includes(range[0]) && ["", null].includes(range[1])) {
255
- return ElMessage.warning("格式错误: 请至少填写一个值");
256
- }
257
- // 排名校验
258
- if (radioType === "1") {
259
- const rangeLeft = Number(range[0]);
260
- const rangeRight = Number(range[1]);
261
- if (isNaN(rangeLeft) || isNaN(rangeRight)) return ElMessage.warning("格式错误: 请填写数字");
262
- if (rangeLeft < 0 || rangeRight < 0) return ElMessage.warning("格式错误: 请填写大于0的数字");
263
- if (rangeLeft > rangeRight) return ElMessage.warning("格式错误: 请填写正确的排名范围");
264
- }
265
- const unitLeft = radioType === "1" ? "" : unit[0] ?? "";
266
- const unitRight = radioType === "1" ? "" : unit[1] ?? "";
267
- // 格式化文案
268
- if (!["", null].includes(range[0]) && !["", null].includes(range[1])) {
269
- indicatorValue.value.tagText = `${label}${radioType === "1" ? "排名" : ""}: ${range[0]}${unitLeft} ~ ${range[1]}${unitRight}`;
270
- } else if (!["", null].includes(range[0])) {
271
- indicatorValue.value.tagText = `${label}${radioType === "1" ? "排名" : ""}: ≥${range[0]}${unitLeft}`;
272
- } else {
273
- indicatorValue.value.tagText = `${label}${radioType === "1" ? "排名" : ""}: ≤${range[1]}${unitRight}`;
274
- }
275
- }
276
- }
277
- // 判断data中是否已存在同样key的数据, 如果存在则替换, 如果不存在就push
278
- const keyIndex = data.value.findIndex(({ key }) => key === indicatorValue.value.key);
51
+ // 校验指标内是否填写完整, 并且生成tagText
52
+ const { valid, message, tagText } = indicatorToValidateAndFormatTagText(currentIndicatorForm.value);
53
+ if (!valid) return ElMessage.warning(message);
54
+ currentIndicatorForm.value.tagText = tagText;
55
+
56
+ // 按 key 去重:不存在则新增,存在则替换
57
+ const keyIndex = data.value.findIndex(({ key }) => key === currentIndicatorForm.value.key);
279
58
  if (keyIndex === -1) {
280
- data.value.push(indicatorValue.value);
59
+ data.value.push(currentIndicatorForm.value);
281
60
  } else {
282
- data.value.splice(keyIndex, 1, indicatorValue.value);
61
+ data.value.splice(keyIndex, 1, currentIndicatorForm.value);
283
62
  }
284
63
  visible.value = false;
285
64
  };
286
- // 编辑指标Tag
65
+
66
+ // 指标Tag: 编辑
287
67
  const editTag = (item) => {
288
- nowIndicator.value = commonIndicatorOptions.value.find(({ key }) => key === item.key);
289
- indicatorValue.value = JSON.parse(JSON.stringify(item));
68
+ currentIndicatorConfig.value = indicatorOptions.value.find(({ key }) => key === item.key);
69
+ currentIndicatorForm.value = JSON.parse(JSON.stringify(item));
290
70
  visible.value = true;
291
71
  };
292
- // 删除指标Tag
72
+
73
+ // 指标Tag: 删除
293
74
  const deleteTag = (index) => {
294
75
  data.value.splice(index, 1);
295
76
  };
296
- // 快捷项[通用: type-undefined]
77
+
78
+ // 快捷项: 通用
297
79
  const clickConvenientOption = (item) => {
298
- indicatorValue.value.range = [...item.range];
299
- indicatorValue.value.unit = [...item.unit];
80
+ currentIndicatorForm.value.range = [...item.range];
81
+ currentIndicatorForm.value.unit = [...item.unit];
300
82
  };
301
- // 快捷项[自定义: type-custom-净利润]
83
+
84
+ // 快捷项: 净利润
302
85
  const clicktFeaturelncomesOption = (value) => {
303
86
  switch (value) {
304
87
  case "≤0":
305
- Object.assign(indicatorValue.value, {
88
+ Object.assign(currentIndicatorForm.value, {
306
89
  rule: 2, // 1超 2跌
307
90
  withFewYears: 1,
308
91
  yearsCount: 1,
@@ -310,7 +93,7 @@ const clicktFeaturelncomesOption = (value) => {
310
93
  });
311
94
  break;
312
95
  case "≥0":
313
- Object.assign(indicatorValue.value, {
96
+ Object.assign(currentIndicatorForm.value, {
314
97
  rule: 1,
315
98
  withFewYears: 1,
316
99
  yearsCount: 1,
@@ -318,7 +101,7 @@ const clicktFeaturelncomesOption = (value) => {
318
101
  });
319
102
  break;
320
103
  case "五年内有四年以上盈利超过5千万":
321
- Object.assign(indicatorValue.value, {
104
+ Object.assign(currentIndicatorForm.value, {
322
105
  rule: 1,
323
106
  withFewYears: 5,
324
107
  yearsCount: 4,
@@ -326,7 +109,7 @@ const clicktFeaturelncomesOption = (value) => {
326
109
  });
327
110
  break;
328
111
  case "≥10亿":
329
- Object.assign(indicatorValue.value, {
112
+ Object.assign(currentIndicatorForm.value, {
330
113
  rule: 1,
331
114
  withFewYears: 1,
332
115
  yearsCount: 1,
@@ -334,7 +117,7 @@ const clicktFeaturelncomesOption = (value) => {
334
117
  });
335
118
  break;
336
119
  case "≥20亿":
337
- Object.assign(indicatorValue.value, {
120
+ Object.assign(currentIndicatorForm.value, {
338
121
  rule: 1,
339
122
  withFewYears: 1,
340
123
  yearsCount: 1,
@@ -343,30 +126,51 @@ const clicktFeaturelncomesOption = (value) => {
343
126
  break;
344
127
  }
345
128
  };
129
+
130
+ // 模式切换时清空已填数据
131
+ const handleRadioTypeChange = () => {
132
+ const { key } = currentIndicatorConfig.value;
133
+ const form = currentIndicatorForm.value;
134
+
135
+ // 根据指标 key 清空对应的字段
136
+ if (key === "mainFlow") {
137
+ form.radio = null;
138
+ form.rankRange = [null, null];
139
+ } else if (key === "perVolumn") {
140
+ const newRadioType = form.radioType;
141
+ // 切换到数值模式:清空分位相关字段
142
+ if (newRadioType === "0") {
143
+ form.radio = null;
144
+ form.levels = [];
145
+ }
146
+ // 切换到分位模式:清空数值相关字段
147
+ if (newRadioType === "2") {
148
+ form.range = [null, null];
149
+ }
150
+ }
151
+ // 其他支持排名的指标(成交额、成交量(期权)、持仓量等)不需要清空额外字段
152
+ // 因为它们的非排名模式和排名模式共用 range/rankRange,切换时数据保留
153
+ };
154
+
346
155
  // 监视: 常用指标数据源 => 已勾选且新的数据源中也有它,则要进行勾选保留
347
156
  watch(
348
- () => commonIndicatorOptions.value,
157
+ () => indicatorOptions.value,
349
158
  () => {
350
159
  if (data.value.length) {
351
160
  data.value = data.value.filter(({ key }) => {
352
- return commonIndicatorOptions.value.find((item) => item.key === key);
161
+ return indicatorOptions.value.find((item) => item.key === key);
353
162
  });
354
163
  }
355
- }
164
+ },
356
165
  );
357
-
358
- const changeMainFlowRadioType = () => {
359
- indicatorValue.value.radio = null;
360
- indicatorValue.value.rankRange = [null, null];
361
- };
362
166
  </script>
363
167
 
364
168
  <template>
365
169
  <div
366
170
  class="common-indicator"
367
- v-if="config.show && commonIndicatorOptions.length"
171
+ v-if="config.show && indicatorOptions.length"
368
172
  >
369
- <!-- 指标选项 -->
173
+ <!-- 指标: 选项 -->
370
174
  <div class="indicator">
371
175
  <div class="title">
372
176
  <span>常用指标: </span>
@@ -374,7 +178,7 @@ const changeMainFlowRadioType = () => {
374
178
  </div>
375
179
  <div class="options">
376
180
  <span
377
- v-for="item in commonIndicatorOptions"
181
+ v-for="item in indicatorOptions"
378
182
  :key="item.key"
379
183
  @click="clickIndicator(item)"
380
184
  >
@@ -382,158 +186,126 @@ const changeMainFlowRadioType = () => {
382
186
  </span>
383
187
  </div>
384
188
  </div>
385
- <!-- TagList: 已选指标 -->
386
- <div class="tags">
387
- <el-tag
388
- v-for="(item, index) in data"
389
- closable
390
- type="info"
391
- @close="deleteTag(index)"
392
- >
393
- <span>{{ item.tagText }}</span>
394
- <span
395
- class="edit"
396
- @click="editTag(item)"
397
- >编辑</span
398
- >
399
- </el-tag>
400
- </div>
401
- <!-- TagInfo: 指标具体弹窗 -->
189
+
190
+ <!-- 指标: 弹窗 -->
402
191
  <el-dialog
403
192
  v-model="visible"
404
- :title="nowIndicator.label"
193
+ :title="currentIndicatorConfig.label"
405
194
  width="600"
406
195
  align-center
407
196
  destroy-on-close
408
197
  >
409
- <template
410
- #header
411
- v-if="nowIndicator.showRankType"
412
- >
413
- <span style="font-size: 18px">{{ nowIndicator.label }}</span>
198
+ <template #header>
199
+ <span style="font-size: 18px">{{ currentIndicatorConfig.label }}</span>
414
200
  <el-radio-group
415
- v-model="indicatorValue.radioType"
201
+ v-if="currentIndicatorConfig.radioTypeOptions?.length"
202
+ v-model="currentIndicatorForm.radioType"
416
203
  style="vertical-align: 4px; margin-left: 12px"
204
+ @change="handleRadioTypeChange"
417
205
  >
418
206
  <el-radio-button
419
- label="数值"
420
- value="0"
421
- />
422
- <el-radio-button
423
- :disabled="rankKey && rankKey !== nowIndicator.key"
424
- label="排名"
425
- value="1"
426
- />
427
- </el-radio-group>
428
- </template>
429
- <template
430
- #header
431
- v-else-if="nowIndicator.key === 'mainFlow'"
432
- >
433
- <span style="font-size: 18px">{{ nowIndicator.label }}</span>
434
- <el-radio-group
435
- v-model="indicatorValue.radioType"
436
- style="vertical-align: 4px; margin-left: 12px"
437
- @change="changeMainFlowRadioType"
438
- >
439
- <el-radio-button
440
- label="涨幅范围"
441
- value="0"
442
- />
443
- <el-radio-button
444
- :disabled="rankKey && rankKey !== nowIndicator.key"
445
- label="排名范围"
446
- value="1"
447
- />
207
+ v-for="option in currentIndicatorConfig.radioTypeOptions"
208
+ :key="option.value"
209
+ :value="option.value"
210
+ :disabled="option.value === '1' && rankKey && rankKey !== currentIndicatorConfig.key"
211
+ >
212
+ {{ option.label }}
213
+ </el-radio-button>
448
214
  </el-radio-group>
449
215
  </template>
450
216
 
451
- <template v-if="nowIndicator.type === undefined && indicatorValue.radioType === '1'">
452
- <!-- 输入框区域 -->
453
- <div class="out-box">
217
+ <!-- 通用的指标表单 -->
218
+ <template v-if="!currentIndicatorConfig.type">
219
+ <!-- 排名模式 -->
220
+ <div
221
+ v-if="currentIndicatorForm.radioType === '1'"
222
+ class="out-box"
223
+ >
454
224
  <span>自定义: </span>
455
225
  <el-input
456
- v-model="indicatorValue.rankRange[0]"
226
+ v-model="currentIndicatorForm.rankRange[0]"
457
227
  style="flex: 1"
458
228
  />
459
229
  ~
460
230
  <el-input
461
- v-model="indicatorValue.rankRange[1]"
231
+ v-model="currentIndicatorForm.rankRange[1]"
462
232
  style="flex: 1"
463
233
  />
464
234
  </div>
465
- </template>
466
- <!-- type: undefined 为默认便捷配置项 + 输入框形式 -->
467
- <template v-else-if="nowIndicator.type === undefined">
468
- <!-- 便捷配置项区域 -->
469
- <div
470
- class="convenient-option-box"
471
- v-if="nowIndicator.convenientOptions?.length"
472
- >
473
- <el-button
474
- v-for="item in nowIndicator.convenientOptions"
475
- :key="item.text"
476
- @click="clickConvenientOption(item)"
477
- >{{ item.text }}</el-button
478
- >
479
- </div>
480
- <!-- 输入框区域 -->
481
- <div class="out-box">
482
- <span>自定义: </span>
483
- <el-input
484
- v-model="indicatorValue.range[0]"
485
- style="flex: 1"
235
+
236
+ <!-- 数值模式 -->
237
+ <div v-else>
238
+ <!-- 便捷配置项区域 -->
239
+ <div
240
+ class="convenient-option-box"
241
+ v-if="currentIndicatorConfig.convenientOptions?.length"
486
242
  >
487
- <!-- 单位 -->
488
- <template
489
- #append
490
- v-if="nowIndicator.unitOptions.length"
243
+ <el-button
244
+ v-for="item in currentIndicatorConfig.convenientOptions"
245
+ :key="item.text"
246
+ @click="clickConvenientOption(item)"
247
+ >{{ item.text }}</el-button
491
248
  >
492
- <span v-if="nowIndicator.unitOptions.length === 1">{{ indicatorValue.unit[0] }}</span>
493
- <el-select
494
- v-else
495
- v-model="indicatorValue.unit[0]"
496
- style="width: 72px"
249
+ </div>
250
+ <!-- 输入框区域 -->
251
+ <div class="out-box">
252
+ <span>自定义: </span>
253
+ <el-input
254
+ v-model="currentIndicatorForm.range[0]"
255
+ style="flex: 1"
256
+ >
257
+ <!-- 单位 -->
258
+ <template
259
+ #append
260
+ v-if="currentIndicatorConfig.unitOptions.length"
497
261
  >
498
- <el-option
499
- v-for="unit in nowIndicator.unitOptions"
500
- :label="unit"
501
- :value="unit"
502
- />
503
- </el-select>
504
- </template>
505
- </el-input>
506
- ~
507
- <el-input
508
- v-model="indicatorValue.range[1]"
509
- style="flex: 1"
510
- >
511
- <!-- 单位 -->
512
- <template
513
- #append
514
- v-if="nowIndicator.unitOptions.length"
262
+ <span v-if="currentIndicatorConfig.unitOptions.length === 1">{{ currentIndicatorForm.unit[0] }}</span>
263
+ <el-select
264
+ v-else
265
+ v-model="currentIndicatorForm.unit[0]"
266
+ style="width: 72px"
267
+ >
268
+ <el-option
269
+ v-for="unit in currentIndicatorConfig.unitOptions"
270
+ :label="unit"
271
+ :value="unit"
272
+ />
273
+ </el-select>
274
+ </template>
275
+ </el-input>
276
+ ~
277
+ <el-input
278
+ v-model="currentIndicatorForm.range[1]"
279
+ style="flex: 1"
515
280
  >
516
- <span v-if="nowIndicator.unitOptions.length === 1">{{ indicatorValue.unit[1] }}</span>
517
- <el-select
518
- v-else
519
- v-model="indicatorValue.unit[1]"
520
- style="width: 72px"
281
+ <!-- 单位 -->
282
+ <template
283
+ #append
284
+ v-if="currentIndicatorConfig.unitOptions.length"
521
285
  >
522
- <el-option
523
- v-for="unit in nowIndicator.unitOptions"
524
- :label="unit"
525
- :value="unit"
526
- />
527
- </el-select>
528
- </template>
529
- </el-input>
286
+ <span v-if="currentIndicatorConfig.unitOptions.length === 1">{{ currentIndicatorForm.unit[1] }}</span>
287
+ <el-select
288
+ v-else
289
+ v-model="currentIndicatorForm.unit[1]"
290
+ style="width: 72px"
291
+ >
292
+ <el-option
293
+ v-for="unit in currentIndicatorConfig.unitOptions"
294
+ :label="unit"
295
+ :value="unit"
296
+ />
297
+ </el-select>
298
+ </template>
299
+ </el-input>
300
+ </div>
530
301
  </div>
531
302
  </template>
303
+
532
304
  <!-- type: custom 根据不同key进行自定义 -->
533
305
  <template v-else>
534
306
  <!-- 是否ST -->
535
- <div v-if="nowIndicator.key === 'st'">
536
- <el-radio-group v-model="indicatorValue.st">
307
+ <div v-if="currentIndicatorConfig.key === 'st'">
308
+ <el-radio-group v-model="currentIndicatorForm.st">
537
309
  <el-radio
538
310
  label="是"
539
311
  :value="1"
@@ -544,8 +316,9 @@ const changeMainFlowRadioType = () => {
544
316
  />
545
317
  </el-radio-group>
546
318
  </div>
319
+
547
320
  <!-- 净利润 -->
548
- <div v-if="nowIndicator.key === 'tFeaturelncomes'">
321
+ <div v-if="currentIndicatorConfig.key === 'tFeaturelncomes'">
549
322
  <!-- 便捷配置项区域 -->
550
323
  <div class="tFeaturelncomes-option-box">
551
324
  <el-button @click="clicktFeaturelncomesOption('≤0')">≤0</el-button>
@@ -559,31 +332,32 @@ const changeMainFlowRadioType = () => {
559
332
  <span>自定义: </span>
560
333
  <div>
561
334
  <el-input-number
562
- v-model="indicatorValue.withFewYears"
335
+ v-model="currentIndicatorForm.withFewYears"
563
336
  controls-position="right"
564
337
  />
565
338
  年内
566
339
  <el-input-number
567
- v-model="indicatorValue.yearsCount"
340
+ v-model="currentIndicatorForm.yearsCount"
568
341
  controls-position="right"
569
342
  />
570
- 年以上盈利{{ indicatorValue.rule === 1 ? "超" : "跌" }}过
343
+ 年以上盈利{{ currentIndicatorForm.rule === 1 ? "超" : "跌" }}过
571
344
  <el-input-number
572
- v-model="indicatorValue.netProfit"
345
+ v-model="currentIndicatorForm.netProfit"
573
346
  controls-position="right"
574
347
  />
575
348
  亿
576
349
  </div>
577
350
  </div>
578
351
  </div>
352
+
579
353
  <!-- 股价分位 -->
580
- <div v-if="nowIndicator.key === 'priceLevels'">
354
+ <div v-if="currentIndicatorConfig.key === 'priceLevels'">
581
355
  <el-select
582
- v-model="indicatorValue.priceLevels"
356
+ v-model="currentIndicatorForm.priceLevels"
583
357
  multiple
584
358
  clearable
585
359
  placeholder="请选择"
586
- style="width: 420px"
360
+ style="width: 100%"
587
361
  >
588
362
  <el-option
589
363
  v-for="item in [1, 2, 3, 4, 5]"
@@ -593,14 +367,15 @@ const changeMainFlowRadioType = () => {
593
367
  />
594
368
  </el-select>
595
369
  </div>
370
+
596
371
  <!-- 市盈率分位 -->
597
- <div v-if="nowIndicator.key === 'peTtmLevels'">
372
+ <div v-if="currentIndicatorConfig.key === 'peTtmLevels'">
598
373
  <el-select
599
- v-model="indicatorValue.peTtmLevels"
374
+ v-model="currentIndicatorForm.peTtmLevels"
600
375
  multiple
601
376
  clearable
602
377
  placeholder="请选择"
603
- style="width: 420px"
378
+ style="width: 100%"
604
379
  >
605
380
  <el-option
606
381
  v-for="item in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
@@ -610,14 +385,15 @@ const changeMainFlowRadioType = () => {
610
385
  />
611
386
  </el-select>
612
387
  </div>
388
+
613
389
  <!-- 市盈率估值走势分位 -->
614
- <div v-if="nowIndicator.key === 'peTrendTtmLevels'">
390
+ <div v-if="currentIndicatorConfig.key === 'peTrendTtmLevels'">
615
391
  <el-select
616
- v-model="indicatorValue.peTrendTtmLevels"
392
+ v-model="currentIndicatorForm.peTrendTtmLevels"
617
393
  multiple
618
394
  clearable
619
395
  placeholder="请选择"
620
- style="width: 420px"
396
+ style="width: 100%"
621
397
  >
622
398
  <el-option
623
399
  v-for="item in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
@@ -627,9 +403,10 @@ const changeMainFlowRadioType = () => {
627
403
  />
628
404
  </el-select>
629
405
  </div>
406
+
630
407
  <!-- 认沽认购 -->
631
- <div v-if="nowIndicator.key === 'optionsCpType'">
632
- <el-radio-group v-model="indicatorValue.optionsCpType">
408
+ <div v-if="currentIndicatorConfig.key === 'optionsCpType'">
409
+ <el-radio-group v-model="currentIndicatorForm.optionsCpType">
633
410
  <el-radio
634
411
  label="看涨"
635
412
  :value="1"
@@ -640,57 +417,124 @@ const changeMainFlowRadioType = () => {
640
417
  />
641
418
  </el-radio-group>
642
419
  </div>
420
+
643
421
  <!-- 成交量 -->
644
- <div v-if="nowIndicator.key === 'perVolumn'">
645
- <el-radio-group v-model="indicatorValue.radio">
646
- <el-radio
647
- v-for="item in perVolumnRadioOptions"
648
- :key="item.value"
649
- :value="item.value"
650
- >{{ item.label }}</el-radio
422
+ <div v-if="currentIndicatorConfig.key === 'perVolumn'">
423
+ <!-- 数值模式 -->
424
+ <div
425
+ v-if="currentIndicatorForm.radioType === '0'"
426
+ class="out-box"
427
+ >
428
+ <span>自定义: </span>
429
+ <el-input
430
+ v-model="currentIndicatorForm.range[0]"
431
+ style="flex: 1"
651
432
  >
652
- </el-radio-group>
653
- <el-select
654
- v-model="indicatorValue.levels"
655
- multiple
656
- clearable
657
- placeholder="请选择"
658
- style="width: 420px"
433
+ <!-- 单位 -->
434
+ <template
435
+ #append
436
+ v-if="currentIndicatorConfig.unitOptions.length"
437
+ >
438
+ <span v-if="currentIndicatorConfig.unitOptions.length === 1">{{ currentIndicatorForm.unit[0] }}</span>
439
+ <el-select
440
+ v-else
441
+ v-model="currentIndicatorForm.unit[0]"
442
+ style="width: 72px"
443
+ >
444
+ <el-option
445
+ v-for="unit in currentIndicatorConfig.unitOptions"
446
+ :label="unit"
447
+ :value="unit"
448
+ />
449
+ </el-select>
450
+ </template>
451
+ </el-input>
452
+ ~
453
+ <el-input
454
+ v-model="currentIndicatorForm.range[1]"
455
+ style="flex: 1"
456
+ >
457
+ <!-- 单位 -->
458
+ <template
459
+ #append
460
+ v-if="currentIndicatorConfig.unitOptions.length"
461
+ >
462
+ <span v-if="currentIndicatorConfig.unitOptions.length === 1">{{ currentIndicatorForm.unit[1] }}</span>
463
+ <el-select
464
+ v-else
465
+ v-model="currentIndicatorForm.unit[1]"
466
+ style="width: 72px"
467
+ >
468
+ <el-option
469
+ v-for="unit in currentIndicatorConfig.unitOptions"
470
+ :label="unit"
471
+ :value="unit"
472
+ />
473
+ </el-select>
474
+ </template>
475
+ </el-input>
476
+ </div>
477
+
478
+ <!-- 分位模式 -->
479
+ <div
480
+ v-if="currentIndicatorForm.radioType === '2'"
481
+ class="perVolumn-group"
659
482
  >
660
- <el-option
661
- v-for="item in [1, 2, 3, 4, 5]"
662
- :key="item"
663
- :label="`${item}分位`"
664
- :value="item"
665
- />
666
- </el-select>
483
+ <el-radio-group v-model="currentIndicatorForm.radio">
484
+ <el-radio
485
+ v-for="item in PER_VOLUMN_RADIO_OPTIONS"
486
+ :key="item.value"
487
+ :value="item.value"
488
+ >
489
+ {{ item.label }}
490
+ </el-radio>
491
+ </el-radio-group>
492
+ <el-select
493
+ v-model="currentIndicatorForm.levels"
494
+ multiple
495
+ clearable
496
+ placeholder="请选择分位"
497
+ >
498
+ <el-option
499
+ v-for="item in [1, 2, 3, 4, 5]"
500
+ :key="item"
501
+ :label="`${item}分位`"
502
+ :value="item"
503
+ />
504
+ </el-select>
505
+ </div>
667
506
  </div>
507
+
668
508
  <!-- 主力净流入资金 -->
669
- <div v-if="nowIndicator.key === 'mainFlow'">
670
- <el-radio-group v-model="indicatorValue.radio">
509
+ <div v-if="currentIndicatorConfig.key === 'mainFlow'">
510
+ <el-radio-group v-model="currentIndicatorForm.radio">
671
511
  <el-radio value="3">3日</el-radio>
672
512
  <el-radio value="5">5日</el-radio>
673
513
  <el-radio value="10">10日</el-radio>
674
514
  <el-radio value="20">20日</el-radio>
675
515
  </el-radio-group>
676
- <div style="display: flex; align-items: center">
677
- <span>{{ indicatorValue.radioType === "0" ? "涨幅范围" : "排名范围" }}:&nbsp;</span>
516
+ <div
517
+ class="out-box"
518
+ style="margin-top: 12px"
519
+ >
520
+ <span>{{ currentIndicatorForm.radioType === "0" ? "涨幅范围" : "排名范围" }}:&nbsp;</span>
678
521
  <el-input
679
- v-model="indicatorValue.rankRange[0]"
522
+ v-model="currentIndicatorForm.rankRange[0]"
680
523
  style="flex: 1"
681
524
  />
682
525
  ~
683
526
  <el-input
684
- v-model="indicatorValue.rankRange[1]"
527
+ v-model="currentIndicatorForm.rankRange[1]"
685
528
  style="flex: 1"
686
529
  />
687
530
  </div>
688
531
  </div>
532
+
689
533
  <!-- 年报审计意见 -->
690
- <div v-if="nowIndicator.key === 'auditOpinionType'">
691
- <el-radio-group v-model="indicatorValue.enumType">
534
+ <div v-if="currentIndicatorConfig.key === 'auditOpinionType'">
535
+ <el-radio-group v-model="currentIndicatorForm.enumType">
692
536
  <el-radio
693
- v-for="(item, index) in auditOpinionTypeRadioOptions"
537
+ v-for="(item, index) in AUDIT_OPINION_TYPE_RADIO_OPTIONS"
694
538
  :key="index"
695
539
  :label="item.label"
696
540
  :value="item.value"
@@ -698,6 +542,7 @@ const changeMainFlowRadioType = () => {
698
542
  </el-radio-group>
699
543
  </div>
700
544
  </template>
545
+
701
546
  <!-- 确定 -->
702
547
  <template #footer>
703
548
  <div class="dialog-footer">
@@ -709,6 +554,13 @@ const changeMainFlowRadioType = () => {
709
554
  </div>
710
555
  </template>
711
556
  </el-dialog>
557
+
558
+ <!-- 已选指标 -->
559
+ <IndicatorTags
560
+ :tags="data"
561
+ @edit="editTag"
562
+ @delete="deleteTag"
563
+ />
712
564
  </div>
713
565
  </template>
714
566
 
@@ -734,76 +586,112 @@ const changeMainFlowRadioType = () => {
734
586
  .options {
735
587
  display: flex;
736
588
  flex-wrap: wrap;
589
+ gap: 2px 6px;
737
590
  span {
738
591
  font-size: 12px;
739
- height: 24;
740
592
  line-height: 24px;
593
+ padding: 0 6px;
594
+ border-radius: 2px;
741
595
  color: var(--el-text-color-regular);
742
596
  cursor: pointer;
743
- margin-right: 16px;
597
+ transition: background 0.15s;
744
598
  &:hover {
745
599
  color: var(--el-color-primary);
600
+ background: var(--el-color-primary-light-9);
746
601
  }
747
602
  }
748
603
  }
749
604
  }
605
+
750
606
  :deep(.el-button.is-text) {
751
607
  span {
752
608
  line-height: 24px;
753
609
  color: var(--el-text-color-regular);
754
610
  }
755
611
  }
756
- .tags {
757
- .el-tag {
758
- margin: 6px 10px 6px 0;
759
- .edit {
760
- cursor: pointer;
761
- color: var(--el-color-primary);
762
- margin-left: 6px;
763
- }
764
- }
765
- }
612
+
613
+ // 便捷选项
766
614
  .convenient-option-box {
767
615
  display: grid;
768
- grid-template-columns: 33.3% 33.3% 33.3%;
769
- border-bottom: 1px solid var(--el-border-color);
770
- margin-bottom: 10px;
616
+ grid-template-columns: repeat(3, 1fr);
617
+ gap: 8px;
618
+ padding-bottom: 12px;
619
+ margin-bottom: 12px;
620
+ border-bottom: 1px solid var(--el-border-color-lighter);
771
621
  .el-button {
772
- margin-left: 10px;
773
- margin-right: 10px;
774
- margin-bottom: 10px;
622
+ margin: 0;
775
623
  }
776
624
  }
625
+
626
+ // 输入框组
777
627
  .out-box {
778
628
  display: flex;
779
629
  align-items: center;
780
- padding: 0 10px;
630
+ gap: 10px;
781
631
  span {
782
- margin-right: 10px;
632
+ flex-shrink: 0;
633
+ white-space: nowrap;
634
+ }
635
+ .el-input {
636
+ flex: 1;
637
+ min-width: 0;
783
638
  }
784
639
  }
785
- // 净利润弹窗样式
640
+
641
+ // 净利润快捷选项
786
642
  .tFeaturelncomes-option-box {
787
643
  display: grid;
788
- grid-template-columns: 20% 20% 60%;
789
- border-bottom: 1px solid var(--el-border-color);
790
- margin-bottom: 10px;
644
+ grid-template-columns: 1fr 1fr 2fr;
645
+ gap: 8px;
646
+ padding-bottom: 12px;
647
+ margin-bottom: 12px;
648
+ border-bottom: 1px solid var(--el-border-color-lighter);
791
649
  .el-button {
792
- margin-left: 10px;
793
- margin-right: 10px;
794
- margin-bottom: 10px;
650
+ margin: 0;
795
651
  }
796
652
  }
653
+
654
+ // 净利润输入框组
797
655
  .tFeaturelncomes-out-box {
798
656
  display: flex;
799
657
  align-items: center;
800
- padding: 0 10px;
658
+ gap: 10px;
801
659
  span {
802
- margin-right: 10px;
660
+ flex-shrink: 0;
803
661
  }
804
662
  .el-input-number {
805
663
  width: 100px;
806
664
  }
665
+ > div {
666
+ display: flex;
667
+ align-items: center;
668
+ gap: 6px;
669
+ flex-wrap: wrap;
670
+ }
671
+ }
672
+
673
+ // 成交量 - 分位模式
674
+ .perVolumn-group {
675
+ display: grid;
676
+ grid-template-columns: 1fr;
677
+ gap: 12px;
678
+
679
+ .el-radio-group {
680
+ display: flex;
681
+ flex-wrap: wrap;
682
+ gap: 6px 14px;
683
+ }
684
+
685
+ .el-select {
686
+ width: 100%;
687
+ }
688
+ }
689
+
690
+ // 弹窗内下拉框占满宽度
691
+ :deep(.el-dialog) {
692
+ .el-select {
693
+ width: 100%;
694
+ }
807
695
  }
808
696
  }
809
697
  </style>