st-comp 0.0.103 → 0.0.105

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "st-comp",
3
3
  "public": true,
4
- "version": "0.0.103",
4
+ "version": "0.0.105",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite",
@@ -2,7 +2,7 @@
2
2
  import { ref, watch } from "vue";
3
3
  import zhCn from "element-plus/es/locale/lang/zh-cn";
4
4
 
5
- const visible = defineModel("visible", { default: false });
5
+ const emit = defineEmits(["add", "delete", "enabled", "closed"]);
6
6
  const props = defineProps({
7
7
  // 弹窗标题
8
8
  title: { type: [String, null], default: null },
@@ -16,7 +16,6 @@ const props = defineProps({
16
16
  // 个别字段功能是否展示
17
17
  showConfig: { type: Object, default: () => ({}) },
18
18
  });
19
- const emit = defineEmits(["add", "delete", "enabled", "closed"]);
20
19
  const showConfig = Object.assign(
21
20
  {
22
21
  ruleFormPrice: true, // 表单-价格范围
@@ -29,12 +28,12 @@ const showConfig = Object.assign(
29
28
  props.showConfig
30
29
  );
31
30
 
32
- // 添加-ruleForm, 管理-table
31
+ const visible = defineModel("visible", { default: false });
32
+
33
+ // 内容类型: [添加-ruleForm, 管理-table]
33
34
  const contentType = ref(props.allowOperation ? "ruleForm" : "table");
34
35
 
35
- /**
36
- * @description: 添加-表单相关参数和方法
37
- */
36
+ // 表单相关参数
38
37
  const ruleFormRef = ref(null);
39
38
  const ruleForm = ref({
40
39
  factorType: 1,
@@ -49,49 +48,82 @@ const rules = ref({
49
48
  factorSelectedList: [{ required: true, message: "请选择预警因子", trigger: "blur" }],
50
49
  totalCount: [{ required: true, message: "请填写预警次数", trigger: "blur" }],
51
50
  });
51
+
52
+ // 表单: 三次多/三次空
52
53
  const handleFastControls = async (type) => {
53
54
  switch (type) {
54
55
  case "threeMore":
55
56
  Object.assign(ruleForm.value, {
56
57
  factorType: 1,
57
58
  factorSelectedList: ["dkx金叉", "dkx黄白线上+双箱单次", "dkx金叉后+单箱突破", "30均线上+向上+单箱"],
58
- totalCount: 3,
59
59
  });
60
60
  break;
61
61
  case "threeEmpty":
62
62
  Object.assign(ruleForm.value, {
63
63
  factorType: -1,
64
64
  factorSelectedList: ["dkx死叉", "dkx黄白线下+双箱单次", "dkx死叉后+单箱突破", "30均线下+向下+单箱"],
65
- totalCount: 3,
66
65
  });
67
66
  break;
68
67
  }
69
- handleSubmit(ruleFormRef.value, false);
68
+ // 特殊需求 [2025-03-10]: 葛总要求通过3次多3次空添加的, 具备上限10的校验
69
+ const tsParams = {
70
+ factorSelectedList: ruleForm.value.factorSelectedList.map((factorName) => {
71
+ let totalCount = 3;
72
+ // 判断表格里面有没有同因子的数据, 如果有的话需要确保增加以后的有效次数 <= 10
73
+ const repeatData = props.tableData.find(({ factorExtendName }) => factorExtendName === factorName);
74
+ if (repeatData) {
75
+ const diff = repeatData.totalCount - repeatData.currentCount;
76
+ // 如果: 已触发次数 === 触发次数上限, 直接+3, 此时后端会另起一行数据的
77
+ if (diff === 0) totalCount = 3;
78
+ // 确保添加后的diff要小于等于10, 出于兼容老数据1/20类似的情况考虑, 加个容错
79
+ else totalCount = Math.min(3, 10 - diff >= 0 ? 10 - diff : 0);
80
+ }
81
+ return {
82
+ factorName,
83
+ ...ruleForm.value,
84
+ totalCount,
85
+ };
86
+ }),
87
+ };
88
+ handleSubmit(ruleFormRef.value, false, tsParams);
70
89
  };
71
- const handleSubmit = async (formEl, close = true) => {
90
+ // 表单: 提交
91
+ const handleSubmit = async (formEl, close = true, tsParams) => {
72
92
  if (!formEl) return;
73
93
  await formEl.validate((valid) => {
74
94
  if (!valid) return false;
75
- emit("add", ruleForm.value);
76
- if (close) {
77
- visible.value = false;
78
- } else {
79
- ruleForm.value = {
80
- factorType: 1,
81
- factorSelectedList: [],
82
- totalCount: 1,
83
- minPrice: null,
84
- maxPrice: null,
85
- mark: null,
86
- };
95
+ // 将表单值抛出
96
+ const params = tsParams ?? {
97
+ factorSelectedList: ruleForm.value.factorSelectedList.map((factorName) => {
98
+ return {
99
+ factorName,
100
+ ...ruleForm.value,
101
+ };
102
+ }),
103
+ };
104
+ emit("add", params);
105
+ // 根据是否关闭弹窗, 进行对应的逻辑处理
106
+ switch (close) {
107
+ // 关闭弹窗
108
+ case true: {
109
+ visible.value = false;
110
+ break;
111
+ }
112
+ // 重置表单值
113
+ case false: {
114
+ ruleForm.value = {
115
+ factorType: 1,
116
+ factorSelectedList: [],
117
+ totalCount: 1,
118
+ minPrice: null,
119
+ maxPrice: null,
120
+ mark: null,
121
+ };
122
+ break;
123
+ }
87
124
  }
88
125
  });
89
126
  };
90
- /**
91
- * @description: 管理-表格相关参数和方法
92
- */
93
- const handleDelete = (row) => emit("delete", row);
94
- const handleEnabled = (row) => emit("enabled", row);
95
127
 
96
128
  // 监视组件开关
97
129
  watch(
@@ -122,7 +154,7 @@ watch(
122
154
  destroy-on-close
123
155
  @closed="emit('closed')"
124
156
  >
125
- <!-- 头部 -->
157
+ <!-- 头部: 标题 + 切换 -->
126
158
  <template #header="{ close, titleId, titleClass }">
127
159
  <div class="custom-header">
128
160
  <div :class="titleClass">
@@ -331,13 +363,13 @@ watch(
331
363
  v-if="showConfig.tableEnable"
332
364
  :disabled="scope.row.currentCount !== scope.row.totalCount"
333
365
  size="small"
334
- @click="handleEnabled(scope.row)"
366
+ @click="emit('enabled', scope.row)"
335
367
  >
336
368
  启用
337
369
  </el-button>
338
370
  <el-popconfirm
339
371
  title="确定删除?"
340
- @confirm="handleDelete(scope.row)"
372
+ @confirm="emit('delete', scope.row)"
341
373
  >
342
374
  <template #reference>
343
375
  <el-button size="small">删除</el-button>
@@ -63,12 +63,14 @@ const handleSubmit = () => {
63
63
  dialogFormRef.value.validate((valid) => {
64
64
  const { list, sqlEnable, sqlValue } = dialogForm.value;
65
65
  if (!valid) return;
66
- // 针对于开启了SQL功能后的字段校验
66
+ // 针对开启了SQL功能后的字段校验
67
67
  if (sqlEnable) {
68
68
  if (!sqlValue) return ElMessage.error("SQL语句不能为空");
69
- // 校验语句里是否存在因子中不存在的条件
69
+ // 提取SQL语句中的条件1, 条件2, ...
70
70
  const sqlConditions = extractConditionDetails(sqlValue);
71
+ // 生成因子表格中的条件1, 条件2, ...
71
72
  const factorConditions = list.map((item, index) => `条件${index + 1}`);
73
+ // 校验SQL中是否全部都能在因子表格中找到
72
74
  const noSaveConditions = sqlConditions.filter((item) => !factorConditions.includes(item));
73
75
  if (noSaveConditions.length) return ElMessage.error(`请检查SQL语句, 不存在[${noSaveConditions}]`);
74
76
  }
@@ -86,12 +88,30 @@ const handleDeleteFactor = (index) => {
86
88
  dialogForm.value.list.splice(index, 1);
87
89
  };
88
90
  // 表单: 添加因子
89
- const handleAppendFactor = () => {
90
- dialogForm.value.list.push({
91
- cycle: props.config.cycleDefault ?? null,
92
- factor: null,
93
- score: [null, null],
94
- });
91
+ const handleAppendFactor = (type) => {
92
+ switch (type) {
93
+ // 对比
94
+ case "compare": {
95
+ dialogForm.value.list.push({
96
+ key: "compare",
97
+ cycle: props.config.cycleDefault ?? null,
98
+ factor: null,
99
+ compareType: ">",
100
+ cycle2: props.config.cycleDefault ?? null,
101
+ factor2: null,
102
+ });
103
+ break;
104
+ }
105
+ // 常规
106
+ default: {
107
+ dialogForm.value.list.push({
108
+ cycle: props.config.cycleDefault ?? null,
109
+ factor: null,
110
+ score: [null, null],
111
+ });
112
+ break;
113
+ }
114
+ }
95
115
  };
96
116
  // 表单: 生成SQL
97
117
  const handleGenerateSql = () => {
@@ -125,14 +145,30 @@ const handleGenerateSql = () => {
125
145
 
126
146
  // Tag: 文案
127
147
  const handleFormatTag = (tag) => {
128
- const { cycle, factor, score } = tag;
129
- let str = "";
130
- str += ` ${props.config.cycleOptions.find(({ value }) => value === cycle).label}`;
131
- str += ` ${props.config.factorOptions.find(({ value }) => value === factor).label}`;
132
- str += ` ${score[0] || score[0] === 0 ? `${score[0]}分` : ""}`;
133
- str += " ~";
134
- str += ` ${score[1] || score[1] === 0 ? `${score[1]}分` : "∞"}`;
135
- return str;
148
+ switch (tag.key) {
149
+ // 对比
150
+ case "compare": {
151
+ const { cycle, factor, compareType, cycle2, factor2 } = tag;
152
+ let str = "";
153
+ str += ` ${props.config.cycleOptions.find(({ value }) => value === cycle).label}`;
154
+ str += ` ${props.config.factorOptions.find(({ value }) => value === factor).label}`;
155
+ str += ` ${compareType}`;
156
+ str += ` ${props.config.cycleOptions.find(({ value }) => value === cycle2).label}`;
157
+ str += ` ${props.config.factorOptions.find(({ value }) => value === factor2).label}`;
158
+ return str;
159
+ }
160
+ // 常规
161
+ default: {
162
+ const { cycle, factor, score } = tag;
163
+ let str = "";
164
+ str += ` ${props.config.cycleOptions.find(({ value }) => value === cycle).label}`;
165
+ str += ` ${props.config.factorOptions.find(({ value }) => value === factor).label}`;
166
+ str += ` ${score[0] || score[0] === 0 ? `${score[0]}分` : "∞"}`;
167
+ str += " ~";
168
+ str += ` ${score[1] || score[1] === 0 ? `${score[1]}分` : "∞"}`;
169
+ return str;
170
+ }
171
+ }
136
172
  };
137
173
  // Tag: 删除
138
174
  const handleDeleteTag = (aciton, index) => {
@@ -200,7 +236,7 @@ const handleDeleteTag = (aciton, index) => {
200
236
  <!-- 因子筛选弹窗 -->
201
237
  <el-dialog
202
238
  v-model="visible"
203
- width="530"
239
+ width="602"
204
240
  align-center
205
241
  destroy-on-close
206
242
  >
@@ -230,69 +266,171 @@ const handleDeleteTag = (aciton, index) => {
230
266
  v-for="(item, index) in dialogForm.list"
231
267
  class="form-row"
232
268
  >
233
- <!-- 序列标记 -->
269
+ <!-- 序列号 -->
234
270
  <span class="index">{{ `条件${index + 1}` }}</span>
235
- <!-- 周期 -->
236
- <el-form-item
237
- v-if="config.cycleShow"
238
- :prop="'list.' + index + '.cycle'"
239
- :rules="{ required: true, message: '周期不能为空', trigger: 'blur' }"
240
- style="width: 100px; margin-right: 10px"
241
- >
242
- <el-select
243
- v-model="item.cycle"
244
- placeholder="选择周期"
245
- size="small"
271
+ <!-- 对比因子 -->
272
+ <template v-if="item.key === 'compare'">
273
+ <!-- 周期 -->
274
+ <el-form-item
275
+ v-if="config.cycleShow"
276
+ :prop="'list.' + index + '.cycle'"
277
+ :rules="{ required: true, message: '周期不能为空', trigger: 'blur' }"
278
+ style="width: 100px; margin-right: 10px"
246
279
  >
247
- <el-option
248
- v-for="{ label, value } in config.cycleOptions"
249
- :label="label"
250
- :value="value"
251
- :key="value"
252
- />
253
- </el-select>
254
- </el-form-item>
255
- <!-- 因子 -->
256
- <el-form-item
257
- :prop="'list.' + index + '.factor'"
258
- :rules="{ required: true, message: '因子不能为空', trigger: 'blur' }"
259
- style="width: 100px; margin-right: 10px"
260
- >
261
- <el-select
262
- v-model="item.factor"
263
- placeholder="选择因子"
264
- filterable
265
- size="small"
266
- no-match-text="无匹配数据"
280
+ <el-select
281
+ v-model="item.cycle"
282
+ placeholder="选择周期"
283
+ size="small"
284
+ >
285
+ <el-option
286
+ v-for="{ label, value } in config.cycleOptions"
287
+ :label="label"
288
+ :value="value"
289
+ :key="value"
290
+ />
291
+ </el-select>
292
+ </el-form-item>
293
+ <!-- 因子 -->
294
+ <el-form-item
295
+ :prop="'list.' + index + '.factor'"
296
+ :rules="{ required: true, message: '因子不能为空', trigger: 'blur' }"
297
+ style="width: 100px; margin-right: 10px"
267
298
  >
268
- <el-option
269
- v-for="{ label, value } in config.factorOptions"
270
- :label="label"
271
- :value="value"
272
- :key="value"
273
- />
274
- </el-select>
275
- </el-form-item>
276
- <!-- 分数 -->
277
- <el-form-item
278
- :prop="'list.' + index + '.score'"
279
- :rules="{ validator: handleVerifyScore, trigger: 'blur' }"
280
- style="width: 200px; margin-right: 10px"
281
- >
282
- <div style="display: flex; align-items: center; width: 100%; height: 24px">
283
- <el-input-number
284
- v-model="item.score[0]"
299
+ <el-select
300
+ v-model="item.factor"
301
+ placeholder="选择因子"
302
+ filterable
285
303
  size="small"
286
- controls-position="right"
287
- />
288
- <span>~</span>
289
- <el-input-number
290
- v-model="item.score[1]"
304
+ no-match-text="无匹配数据"
305
+ >
306
+ <el-option
307
+ v-for="{ label, value } in config.factorOptions"
308
+ :label="label"
309
+ :value="value"
310
+ :key="value"
311
+ />
312
+ </el-select>
313
+ </el-form-item>
314
+ <!-- 对比符 -->
315
+ <el-form-item style="width: 52px; margin-right: 10px">
316
+ <el-select
317
+ v-model="item.compareType"
291
318
  size="small"
292
- controls-position="right"
293
- />
294
- </div>
295
- </el-form-item>
319
+ >
320
+ <el-option
321
+ v-for="item in ['>', '>=', '<', '<=']"
322
+ :label="item"
323
+ :value="item"
324
+ :key="item"
325
+ />
326
+ </el-select>
327
+ </el-form-item>
328
+ <!-- 周期2 -->
329
+ <el-form-item
330
+ v-if="config.cycleShow"
331
+ :prop="'list.' + index + '.cycle2'"
332
+ :rules="{ required: true, message: '周期不能为空', trigger: 'blur' }"
333
+ style="width: 100px; margin-right: 10px"
334
+ >
335
+ <el-select
336
+ v-model="item.cycle2"
337
+ placeholder="选择周期"
338
+ size="small"
339
+ >
340
+ <el-option
341
+ v-for="{ label, value } in config.cycleOptions"
342
+ :label="label"
343
+ :value="value"
344
+ :key="value"
345
+ />
346
+ </el-select>
347
+ </el-form-item>
348
+ <!-- 因子2 -->
349
+ <el-form-item
350
+ :prop="'list.' + index + '.factor2'"
351
+ :rules="{ required: true, message: '因子不能为空', trigger: 'blur' }"
352
+ style="width: 100px; margin-right: 10px"
353
+ >
354
+ <el-select
355
+ v-model="item.factor2"
356
+ placeholder="选择因子"
357
+ filterable
358
+ size="small"
359
+ no-match-text="无匹配数据"
360
+ >
361
+ <el-option
362
+ v-for="{ label, value } in config.factorOptions"
363
+ :label="label"
364
+ :value="value"
365
+ :key="value"
366
+ />
367
+ </el-select>
368
+ </el-form-item>
369
+ </template>
370
+ <!-- 常规因子 -->
371
+ <template v-else>
372
+ <!-- 周期 -->
373
+ <el-form-item
374
+ v-if="config.cycleShow"
375
+ :prop="'list.' + index + '.cycle'"
376
+ :rules="{ required: true, message: '周期不能为空', trigger: 'blur' }"
377
+ style="width: 100px; margin-right: 10px"
378
+ >
379
+ <el-select
380
+ v-model="item.cycle"
381
+ placeholder="选择周期"
382
+ size="small"
383
+ >
384
+ <el-option
385
+ v-for="{ label, value } in config.cycleOptions"
386
+ :label="label"
387
+ :value="value"
388
+ :key="value"
389
+ />
390
+ </el-select>
391
+ </el-form-item>
392
+ <!-- 因子 -->
393
+ <el-form-item
394
+ :prop="'list.' + index + '.factor'"
395
+ :rules="{ required: true, message: '因子不能为空', trigger: 'blur' }"
396
+ style="width: 100px; margin-right: 10px"
397
+ >
398
+ <el-select
399
+ v-model="item.factor"
400
+ placeholder="选择因子"
401
+ filterable
402
+ size="small"
403
+ no-match-text="无匹配数据"
404
+ >
405
+ <el-option
406
+ v-for="{ label, value } in config.factorOptions"
407
+ :label="label"
408
+ :value="value"
409
+ :key="value"
410
+ />
411
+ </el-select>
412
+ </el-form-item>
413
+ <!-- 分数 -->
414
+ <el-form-item
415
+ :prop="'list.' + index + '.score'"
416
+ :rules="{ validator: handleVerifyScore, trigger: 'blur' }"
417
+ style="width: 200px; margin-right: 10px"
418
+ >
419
+ <div style="display: flex; align-items: center; width: 100%; height: 24px">
420
+ <el-input-number
421
+ v-model="item.score[0]"
422
+ size="small"
423
+ controls-position="right"
424
+ />
425
+ <span>~</span>
426
+ <el-input-number
427
+ v-model="item.score[1]"
428
+ size="small"
429
+ controls-position="right"
430
+ />
431
+ </div>
432
+ </el-form-item>
433
+ </template>
296
434
  <!-- 删除 -->
297
435
  <el-icon @click="handleDeleteFactor(index)"><CircleCloseFilled /></el-icon>
298
436
  </div>
@@ -305,6 +443,15 @@ const handleDeleteTag = (aciton, index) => {
305
443
  style="margin-bottom: 10px"
306
444
  >添加因子</el-button
307
445
  >
446
+ <el-button
447
+ type="primary"
448
+ plain
449
+ size="small"
450
+ :icon="Plus"
451
+ @click="handleAppendFactor('compare')"
452
+ style="margin-bottom: 10px"
453
+ >添加因子对比</el-button
454
+ >
308
455
  <!-- SQL功能 -->
309
456
  <template v-if="config.sqlShow">
310
457
  <el-form-item label="SQL功能: ">
@@ -128,37 +128,64 @@ defineExpose({
128
128
  // 5.因子筛选
129
129
  if (config.value.factorScreen?.show) {
130
130
  const { factorScreen } = data;
131
- // 因子列表参数
131
+ // tbFeatureFactorScores参数整理
132
132
  if (factorScreen?.list?.length) {
133
133
  params.tbFeatureFactorScores = factorScreen.list.map((item) => {
134
- return {
135
- freqId: item.cycle,
136
- factorId: item.factor,
137
- startScore: item.score[0],
138
- endScore: item.score[1],
139
- };
134
+ switch (item.key) {
135
+ // 对比
136
+ case "compare": {
137
+ return {
138
+ freqId: item.cycle,
139
+ factorId: item.factor,
140
+ compareType: item.compareType,
141
+ freqId2: item.cycle2,
142
+ factorId2: item.factor2,
143
+ };
144
+ }
145
+ // 常规
146
+ default: {
147
+ return {
148
+ freqId: item.cycle,
149
+ factorId: item.factor,
150
+ startScore: item.score[0],
151
+ endScore: item.score[1],
152
+ };
153
+ }
154
+ }
140
155
  });
141
156
  }
142
- // SQL语句参数
143
- params.enableSql = factorScreen.sqlEnable === 1 ? 2 : 1;
157
+ // sql参数整理
144
158
  if (factorScreen.sqlEnable) {
145
159
  const conditions = factorScreen.list.reduce((result, item, index) => {
146
160
  const key = `条件${index + 1}`;
147
- let sql = `factor_id = ${item.factor} and `;
148
- if (config.value.factorScreen?.cycleShow) sql = `freq_id = ${item.cycle} and factor_id = ${item.factor} and `;
149
- // 分数语句的判断
150
- if ((item.score[0] || item.score[0] === 0) && (item.score[1] || item.score[1] === 0)) {
151
- sql += `score >= ${item.score[0]} and score <= ${item.score[1]}`;
152
- } else if (item.score[0] || item.score[0] === 0) {
153
- sql += `score >= ${item.score[0]}`;
154
- } else if (item.score[1] || item.score[1] === 0) {
155
- sql += `score <= ${item.score[1]}`;
161
+ switch (item.key) {
162
+ case "compare": {
163
+ let sql = "";
164
+ if (config.value.factorScreen?.cycleShow) {
165
+ sql = `freq_id = ${item.cycle} and factor_id = ${item.factor} ${item.compareType} freq_id = ${item.cycle2} and factor_id = ${item.factor2}`;
166
+ } else {
167
+ sql = `factor_id = ${item.factor} ${item.compareType} factor_id = ${item.factor2}`;
168
+ }
169
+ result.set(key, `(${sql})`);
170
+ break;
171
+ }
172
+ default: {
173
+ let sql = `factor_id = ${item.factor} and `;
174
+ if (config.value.factorScreen?.cycleShow) sql = `freq_id = ${item.cycle} and factor_id = ${item.factor} and `;
175
+ // 分数语句
176
+ if ((item.score[0] || item.score[0] === 0) && (item.score[1] || item.score[1] === 0)) {
177
+ sql += `score >= ${item.score[0]} and score <= ${item.score[1]}`;
178
+ } else if (item.score[0] || item.score[0] === 0) {
179
+ sql += `score >= ${item.score[0]}`;
180
+ } else if (item.score[1] || item.score[1] === 0) {
181
+ sql += `score <= ${item.score[1]}`;
182
+ }
183
+ result.set(key, `(${sql})`);
184
+ }
156
185
  }
157
- // 格式化因子sql语句
158
- sql = `(${sql})`;
159
- result.set(key, sql);
160
186
  return result;
161
187
  }, new Map([]));
188
+ // 将(SQL语句)与(字符串条件N)进行替换
162
189
  let sqlStr = factorScreen.sqlValue;
163
190
  for (const [key, value] of conditions) {
164
191
  const regex = new RegExp(key, "g");
@@ -166,6 +193,8 @@ defineExpose({
166
193
  }
167
194
  params.sql = sqlStr;
168
195
  }
196
+ // enableSql参数整理
197
+ params.enableSql = factorScreen.sqlEnable === 1 ? 2 : 1;
169
198
  }
170
199
  // 6.常用指标
171
200
  {