st-comp 0.0.247 → 0.0.249

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.
Files changed (170) hide show
  1. package/components.d.ts +1 -0
  2. package/es/ChartLayout.js +4 -4
  3. package/es/CustomFunction.cjs +2 -2
  4. package/es/CustomFunction.js +72 -70
  5. package/es/FactorWarning.cjs +1 -1
  6. package/es/FactorWarning.js +36 -35
  7. package/es/Kline.cjs +1 -1
  8. package/es/Kline.js +20 -19
  9. package/es/KlineBasic.cjs +1 -1
  10. package/es/KlineBasic.js +30 -28
  11. package/es/KlineConfig.cjs +1 -1
  12. package/es/KlineConfig.js +50 -49
  13. package/es/KlineNew.cjs +1 -1
  14. package/es/KlineNew.js +19 -18
  15. package/es/KlinePlus.cjs +4 -4
  16. package/es/KlinePlus.js +590 -575
  17. package/es/MonacoEditor.cjs +1 -1
  18. package/es/MonacoEditor.js +28 -26
  19. package/es/Pagination.cjs +1 -1
  20. package/es/Pagination.js +77 -76
  21. package/es/PasswordPrompt.cjs +1 -1
  22. package/es/PasswordPrompt.js +3 -3
  23. package/es/Table.cjs +1 -1
  24. package/es/Table.js +38 -37
  25. package/es/User.cjs +1 -1
  26. package/es/User.js +143 -141
  27. package/es/VarSelectDialog.cjs +2 -2
  28. package/es/VarSelectDialog.js +29 -27
  29. package/es/VarietyAiHelper.cjs +4 -0
  30. package/es/VarietyAiHelper.js +270 -0
  31. package/es/VarietyAutoComplete.cjs +1 -1
  32. package/es/VarietyAutoComplete.js +13 -12
  33. package/es/VarietySearch.cjs +10 -10
  34. package/es/VarietySearch.js +70 -68
  35. package/es/{VarietySelect-2fd501da.cjs → VarietySelect-5c845562.cjs} +1 -1
  36. package/es/{VarietySelect-5a9dd50b.js → VarietySelect-9f267958.js} +4 -4
  37. package/es/VarietyTextCopy.cjs +1 -1
  38. package/es/VarietyTextCopy.js +24 -22
  39. package/es/VirtualTable.cjs +1 -1
  40. package/es/VirtualTable.js +70 -70
  41. package/es/{_initCloneObject-3823a101.cjs → _initCloneObject-2b82e9f7.cjs} +1 -1
  42. package/es/{_initCloneObject-c34c65bc.js → _initCloneObject-69c8ae23.js} +3 -3
  43. package/es/aiTools.js +36 -0
  44. package/es/{base-a5af3db3.js → base-29f73b05.js} +2 -2
  45. package/es/{castArray-7741a212.js → castArray-609a313e.js} +1 -1
  46. package/es/{config-provider-2182708a.cjs → config-provider-2ae47cc8.cjs} +1 -1
  47. package/es/{config-provider-06a63185.js → config-provider-7860903c.js} +4 -4
  48. package/es/{debounce-8d53f4dd.js → debounce-a09ce9a3.js} +1 -1
  49. package/es/{dropdown-302f71e7.js → dropdown-497442b7.js} +20 -19
  50. package/es/dropdown-eba9eaf5.cjs +1 -0
  51. package/es/{el-autocomplete-ed75a659.js → el-autocomplete-27c60cc8.js} +18 -17
  52. package/es/el-autocomplete-b59eb529.cjs +1 -0
  53. package/es/{el-button-68baab7b.cjs → el-button-974ff9e9.cjs} +1 -1
  54. package/es/{el-button-d09ff85f.js → el-button-e2c63c08.js} +4 -4
  55. package/es/el-checkbox-08185353.cjs +1 -0
  56. package/es/{el-checkbox-64648e02.js → el-checkbox-71ebd862.js} +4 -4
  57. package/es/el-dialog-1b185570.cjs +1 -0
  58. package/es/{el-dialog-6a80e3d8.js → el-dialog-eedcfd3e.js} +6 -6
  59. package/es/{el-divider-4e059794.js → el-divider-523e5874.js} +1 -1
  60. package/es/{el-empty-33cb66f1.js → el-empty-f5a1607a.js} +3 -3
  61. package/es/{el-form-item-4eca95be.js → el-form-item-bdcfd297.js} +8 -8
  62. package/es/{el-form-item-4076e55f.cjs → el-form-item-c997b4fa.cjs} +1 -1
  63. package/es/{el-input-cae60510.js → el-input-d47281da.js} +68 -68
  64. package/es/el-input-fa18ef84.cjs +1 -0
  65. package/es/el-input-number-3d94fa58.cjs +1 -0
  66. package/es/{el-input-number-c2499410.js → el-input-number-c8018cb1.js} +15 -15
  67. package/es/{el-loading-c738468d.js → el-loading-0cd81d05.js} +2 -2
  68. package/es/{el-loading-05826e64.cjs → el-loading-969a79ca.cjs} +1 -1
  69. package/es/el-menu-item-26071fd6.cjs +1 -0
  70. package/es/{el-menu-item-f904f685.js → el-menu-item-dac65bb3.js} +18 -17
  71. package/es/el-message-0ea8fbf8.cjs +1 -0
  72. package/es/el-message-4ed993c7.js +1 -0
  73. package/es/el-message-box-31e0aa98.cjs +1 -0
  74. package/es/{el-message-box-05d8cf39.js → el-message-box-fea4fca8.js} +11 -11
  75. package/es/{el-overlay-cc9bc792.js → el-overlay-1ee0338d.js} +19 -19
  76. package/es/el-overlay-ea65cb05.cjs +1 -0
  77. package/es/{el-popconfirm-a6f66a0e.js → el-popconfirm-089b8bec.js} +21 -20
  78. package/es/el-popconfirm-9e232436.cjs +1 -0
  79. package/es/el-popper-2d3914e4.cjs +1 -0
  80. package/es/el-popper-c9b3d3cf.js +1662 -0
  81. package/es/el-segmented-140ac042.cjs +1 -0
  82. package/es/{el-segmented-51b1c797.js → el-segmented-9d3a9e11.js} +4 -4
  83. package/es/el-select-a11f33e8.cjs +1 -0
  84. package/es/{el-select-1b149fab.js → el-select-e51e11c1.js} +44 -43
  85. package/es/{el-table-column-3e30ebae.js → el-table-column-05d292a8.js} +25 -24
  86. package/es/el-table-column-98570a4d.cjs +14 -0
  87. package/es/{el-tag-0a25efdf.js → el-tag-17cd04a1.js} +13 -13
  88. package/es/el-tag-6d8e653e.cjs +1 -0
  89. package/es/{el-text-73d899ff.js → el-text-2710fff3.js} +2 -2
  90. package/es/{el-text-1470de46.cjs → el-text-a18106cb.cjs} +1 -1
  91. package/es/{index-42e59bf5.js → index-0ee486ad.js} +3 -3
  92. package/es/index-0f79095c.js +440 -0
  93. package/es/{index-6806997d.js → index-1d9b50de.js} +3 -3
  94. package/es/index-28e03bad.cjs +1 -0
  95. package/es/{index-269b22da.cjs → index-299ee017.cjs} +1 -1
  96. package/es/{index-cebc7160.cjs → index-33f80550.cjs} +1 -1
  97. package/es/{index-4f48940d.cjs → index-37b8d3c6.cjs} +1 -1
  98. package/es/{index-2375023e.cjs → index-5befc414.cjs} +4 -4
  99. package/es/{index-696b6a94.cjs → index-6b99def3.cjs} +1 -1
  100. package/es/index-6ca95c8a.cjs +1 -0
  101. package/es/index-7dce9f59.cjs +1 -0
  102. package/es/{index-960806da.js → index-8391a3df.js} +3 -3
  103. package/es/{index-ac98a4d8.js → index-8f9d0d17.js} +16 -15
  104. package/es/{index-94e43e0d.js → index-95e5d454.js} +3 -3
  105. package/es/{index-4194c942.js → index-b0117ba2.js} +2 -2
  106. package/es/{index-6e967429.js → index-bcd895a0.js} +3 -3
  107. package/es/{index-87b4bf61.js → index-c2b9bbfd.js} +95 -65
  108. package/es/{index-54d289d1.js → index-c71e37dc.js} +11 -11
  109. package/es/{index-d77a7336.js → index-de24705f.js} +1 -1
  110. package/es/index-e8eeea22.cjs +1 -0
  111. package/es/{index-ee977f79.cjs → index-eb99b188.cjs} +1 -1
  112. package/es/{el-message-0df23ae7.js → index-f30561d3.js} +17 -17
  113. package/es/{python-c67c8901.cjs → python-27ece6fa.cjs} +2 -2
  114. package/es/{python-a914569a.js → python-656db04f.js} +30 -28
  115. package/es/{raf-80ef0c0a.js → raf-744cf95a.js} +1 -1
  116. package/es/{scroll-679bd6bf.js → scroll-6799bafc.js} +1 -1
  117. package/es/style.css +1 -1
  118. package/es/{use-form-common-props-47e50c10.js → use-form-common-props-cb0ca65c.js} +41 -41
  119. package/es/{use-form-common-props-344056f9.cjs → use-form-common-props-d3ed62c6.cjs} +1 -1
  120. package/es/{use-global-config-cf78ebac.cjs → use-global-config-c80f33a4.cjs} +1 -1
  121. package/es/{use-global-config-f52caea0.js → use-global-config-cdaeca54.js} +4 -4
  122. package/es/{validator-3cad04b2.cjs → validator-07160325.cjs} +1 -1
  123. package/es/{validator-94c04152.js → validator-4ab9774f.js} +1 -1
  124. package/es/{vnode-5ddb7ed1.js → vnode-a83e6de8.js} +1 -1
  125. package/es/{zh-cn-aabfaa94.cjs → zh-cn-6a0f844c.cjs} +1 -1
  126. package/es/{zh-cn-4921961d.js → zh-cn-8a6390a4.js} +2 -2
  127. package/lib/aiTools.js +36 -0
  128. package/lib/bundle.js +1 -1
  129. package/lib/bundle.umd.cjs +188 -185
  130. package/lib/{index-750ede1b.js → index-6ac0579e.js} +20224 -19939
  131. package/lib/{python-e34a2958.js → python-0432340a.js} +1 -1
  132. package/lib/style.css +1 -1
  133. package/package.json +1 -1
  134. package/packages/KlinePlus/index.vue +88 -62
  135. package/packages/VarietyAiHelper/index.ts +8 -0
  136. package/packages/VarietyAiHelper/index.vue +430 -0
  137. package/packages/index.ts +2 -0
  138. package/public/aiTools.js +36 -0
  139. package/src/App.vue +21 -0
  140. package/src/pages/KlineNew/components/KlineAction/mockApi/index.js +1 -1
  141. package/src/pages/KlineNew/components/KlineBasic/utils.js +1 -1
  142. package/src/pages/KlineNew/components/KlineSlide/utils.js +1 -1
  143. package/src/pages/KlinePlus/KlineDialog/MultiCycle.vue +312 -0
  144. package/src/pages/KlinePlus/KlineDialog/api.js +108 -0
  145. package/src/pages/KlinePlus/KlineDialog/index.vue +582 -0
  146. package/src/pages/KlinePlus/KlineDialog/indicator.js +109 -0
  147. package/src/pages/KlinePlus/KlineDialog/tools.js +86 -0
  148. package/src/pages/KlinePlus/index.vue +23 -209
  149. package/src/pages/VarietyAiHelper/index.vue +20 -0
  150. package/src/pages/VarietySearch/index.vue +75 -51
  151. package/src/router/routes.ts +5 -0
  152. package/es/dropdown-89b74bc9.cjs +0 -1
  153. package/es/el-autocomplete-b9a3054a.cjs +0 -1
  154. package/es/el-checkbox-b982e2ef.cjs +0 -1
  155. package/es/el-dialog-ad7309e9.cjs +0 -1
  156. package/es/el-input-172c49f8.cjs +0 -1
  157. package/es/el-input-number-c2e71528.cjs +0 -1
  158. package/es/el-menu-item-7f986598.cjs +0 -1
  159. package/es/el-message-a86c0efa.cjs +0 -1
  160. package/es/el-message-box-40ff2af5.cjs +0 -1
  161. package/es/el-overlay-d7a6e4a9.cjs +0 -1
  162. package/es/el-popconfirm-737a015b.cjs +0 -1
  163. package/es/el-popper-7ba87e05.cjs +0 -1
  164. package/es/el-popper-a38874f4.js +0 -2094
  165. package/es/el-segmented-3fd66a0e.cjs +0 -1
  166. package/es/el-select-12f6deb7.cjs +0 -1
  167. package/es/el-table-column-516a0ed9.cjs +0 -14
  168. package/es/el-tag-789f05d3.cjs +0 -1
  169. package/es/index-8de94a49.cjs +0 -1
  170. package/es/index-c04f444f.cjs +0 -1
@@ -0,0 +1,582 @@
1
+ <script setup>
2
+ import { actionStat } from "st-func";
3
+ import { ref, reactive, onMounted, provide, nextTick, watch } from "vue";
4
+ import { ArrowLeftBold, ArrowRightBold, RefreshRight, CloseBold, Setting } from "@element-plus/icons-vue";
5
+ import { sellBuyOptions, rightTypeOptions, klineTypeOptions, formatLabel, getTimeRange } from "./tools.js";
6
+ import useIndicatorStore from "./indicator.js";
7
+ import { queryContractType, queryVarietyNetPositionValue, queryPairedRecordByVariety, queryJCVariety, getUserConfig, getCycleList } from "./api.js";
8
+ import MultiCycle from "./MultiCycle.vue";
9
+
10
+ const actionParams = ref({ pageName: null, other: null }); // 数据埋点
11
+
12
+ const stKlineConfigRef = ref(null);
13
+ const userKlineConfig = reactive({});
14
+ provide("userKlineConfig", userKlineConfig);
15
+
16
+ const indicatorStore = useIndicatorStore();
17
+ const cycleOptions = ref([]);
18
+
19
+ const visible = ref(false);
20
+ const loading = ref(false); // 初始数据载入, 参数变动等
21
+ const MultiCycleRef = ref(null);
22
+
23
+ const pageType = ref(1);
24
+ const analyseId = ref(null);
25
+ const varietyList = ref([]);
26
+ const varietyIndex = ref(0);
27
+
28
+ const varietyName = ref(null);
29
+ const varietyCode = ref(null);
30
+ const jcVarietyOptions = ref([]); // 价差品种数据源
31
+ const ifBasket = ref(0); // 1: 是篮子, 0: 不是篮子
32
+
33
+ // 品种参数
34
+ const varietyStock = ref(1); // 0: 期货, 1: 股票, 2: 期权
35
+ const tradeLog = ref([]);
36
+ const netPositionData = ref([]);
37
+
38
+ // 交互参数
39
+ const cycle = ref("6");
40
+ const sellBuy = ref(1);
41
+ const rightType = ref(1);
42
+ const klineType = ref(0);
43
+ const mainIndicator = ref("DKX_EMA");
44
+
45
+ /**
46
+ * @description: 首屏时间
47
+ * 1. 默认按照交易第一笔开仓 ~ 最后一笔平仓时间 (前后拓展200根)
48
+ * 2. 如果参数positionTime, 则以此时间为准 (前后拓展200根)
49
+ */
50
+ const initTimeRange = ref([]);
51
+ const positionTime = ref(null);
52
+
53
+ // 获取主品种类型
54
+ const getVarietyStock = async () => {
55
+ const params = { condition: varietyCode.value, conditionName: varietyName.value };
56
+ const { body } = await queryContractType(params);
57
+ varietyStock.value = body;
58
+ };
59
+ // 获取价差品种数据
60
+ const getJcVarietyOptions = async () => {
61
+ if (varietyStock.value === 1) {
62
+ const params = { condition: varietyCode.value, conditionName: varietyName.value };
63
+ const { body } = await queryJCVariety(params);
64
+ jcVarietyOptions.value = body ?? [];
65
+ } else {
66
+ jcVarietyOptions.value = [{ varietyName: varietyName.value, varietyCode: varietyCode.value }];
67
+ }
68
+ };
69
+
70
+ // 获取品种交易数据
71
+ const getTradeLog = async () => {
72
+ // 如果已经自带了交易数据, 说明是要聚焦这一笔明细的, 就不用拉取全部数据
73
+ if (tradeLog.value.length !== 0) return;
74
+ const params = {
75
+ analyseId: analyseId.value,
76
+ varietyName: varietyName.value,
77
+ varietyCode: varietyCode.value,
78
+ ifBasket: ifBasket.value, // 是否篮子
79
+ cycle: cycle.value,
80
+ };
81
+ const { body } = await queryPairedRecordByVariety(params);
82
+ tradeLog.value = body ?? [];
83
+ };
84
+ // 获取品种净值数据
85
+ const getNetPositionData = async () => {
86
+ // 如果没有交易数据, 就不用请求净值数据了
87
+ if (tradeLog.value.length === 0) return;
88
+ if (ifBasket.value === 1) return;
89
+ const params = {
90
+ analyseId: analyseId.value,
91
+ varietyCode: varietyCode.value,
92
+ startTime: tradeLog.value[0].openTime,
93
+ endTime: tradeLog.value.at(-1).closeTime,
94
+ };
95
+ const { body } = await queryVarietyNetPositionValue(params);
96
+ netPositionData.value = body;
97
+ };
98
+
99
+ // 顶栏: 时间控制
100
+ const handleTimeRange = async (type) => {
101
+ // 单周期
102
+ if (pageType.value === 1) {
103
+ const params = {
104
+ type,
105
+ cycle: cycle.value,
106
+ startTime: tradeLog.value[0]?.openTime,
107
+ endTime: positionTime.value ? positionTime.value : tradeLog.value.at(-1)?.closeTime,
108
+ };
109
+ initTimeRange.value = getTimeRange(params);
110
+ }
111
+ // 多周期
112
+ else if (pageType.value === 2) MultiCycleRef.value?.changeTimeRange(type);
113
+ };
114
+ // 顶栏: 主品种切换
115
+ const handleChangeVariety = async (type) => {
116
+ const oldIndex = varietyIndex.value;
117
+ const maxIndex = varietyList.value.length - 1;
118
+ switch (type) {
119
+ // 上一个品种
120
+ case "pre": {
121
+ if (oldIndex === 0) return ElMessage.warning("已是本页第一个品种");
122
+ varietyIndex.value -= 1;
123
+ break;
124
+ }
125
+ // 下一个品种
126
+ case "next": {
127
+ if (oldIndex === maxIndex) return ElMessage.warning("已是本页最后一个品种");
128
+ varietyIndex.value += 1;
129
+ break;
130
+ }
131
+ }
132
+ if (oldIndex !== varietyIndex.value) {
133
+ loading.value = true;
134
+ const varietyInfo = varietyList.value[varietyIndex.value];
135
+ varietyName.value = varietyInfo.varietyName;
136
+ varietyCode.value = varietyInfo.varietyCode;
137
+ ifBasket.value = varietyInfo.ifBasket;
138
+ tradeLog.value = varietyInfo.tradeLog ?? [];
139
+ netPositionData.value = [];
140
+ await getVarietyStock();
141
+ await getJcVarietyOptions();
142
+ await getTradeLog();
143
+ await getNetPositionData();
144
+ handleTimeRange("reset");
145
+ loading.value = false;
146
+ }
147
+ };
148
+ // 顶栏: 价差品种切换
149
+ const handleChangeJcVariety = (varietyCode) => {
150
+ const item = jcVarietyOptions.value.find((i) => i.varietyCode === varietyCode);
151
+ // 由于这里要修改其它响应式数据, select组件内部原因, 自动关闭下拉框这一行为会受到影响, 所以使用nextTick
152
+ nextTick(() => {
153
+ varietyName.value = item.varietyName;
154
+ handleTimeRange("reset");
155
+ });
156
+ };
157
+
158
+ // 多周期双击切到单周期
159
+ const goToSingleCycle = (item) => {
160
+ if (!userKlineConfig.enable_dbClickOpenSingel) return;
161
+ cycle.value = item.cycle;
162
+ pageType.value = 1;
163
+ };
164
+ // 用户自定义K线配置(获取, 更新)
165
+ const handleUserKlineConfig = async (action, formJson) => {
166
+ switch (action) {
167
+ case "get": {
168
+ const { body } = await getUserConfig({ appId: 1 });
169
+ Object.assign(userKlineConfig, stKlineConfigRef.value?.getDefaultUserKlineConfig(), body ? JSON.parse(body) : {});
170
+ console.log(userKlineConfig)
171
+ break;
172
+ }
173
+ case "update": {
174
+ loading.value = true;
175
+ // 更新配置数据
176
+ Object.assign(userKlineConfig, JSON.parse(formJson));
177
+ // 默认值 [自定义配置]
178
+ pageType.value = userKlineConfig.pageType;
179
+ cycle.value = userKlineConfig.singelCycle;
180
+ sellBuy.value = userKlineConfig.sellBuy;
181
+ mainIndicator.value = userKlineConfig.mainIndicator;
182
+ nextTick(() => (loading.value = false));
183
+ }
184
+ }
185
+ };
186
+
187
+ onMounted(async () => {
188
+ await indicatorStore.init();
189
+ handleUserKlineConfig("get");
190
+ cycleOptions.value = (await getCycleList()).body[1002].map((item) => ({ label: item.dictName, value: item.dictCode }));
191
+ });
192
+ watch(
193
+ () => pageType.value,
194
+ (newValue) => {
195
+ // 当切换到单周期的时候, 需要重置初始时间, 多周期业务组件内部自带了重置触发逻辑
196
+ if (newValue === 1) handleTimeRange("reset");
197
+ actionParams.value?.pageName && actionStat(actionParams.value?.pageName, "查看K线", pageType.value === 1 ? "单周期" : "多周期");
198
+ }
199
+ );
200
+ defineExpose({
201
+ /**
202
+ * @description: 打开K线弹窗组件
203
+ * @param {Object} params - 入参
204
+ * params.analyseId - 绩效记录id
205
+ * params.varietyInfo - 品种信息 { index, varietyName, varietyCode, ifBasket, positionTime, tradeLog }
206
+ * params.varietyList - 当页品种列表 <varietyInfo>
207
+ * params.actionParams - 行为埋点参数
208
+ */
209
+ open: async (params) => {
210
+ try {
211
+ loading.value = true;
212
+ visible.value = true;
213
+
214
+ // 外部传入
215
+ analyseId.value = params.analyseId;
216
+ varietyList.value = params.varietyList ?? [];
217
+ varietyIndex.value = params.varietyInfo.index ?? 0;
218
+ varietyName.value = params.varietyInfo.varietyName;
219
+ varietyCode.value = params.varietyInfo.varietyCode;
220
+ ifBasket.value = params.varietyInfo.ifBasket;
221
+ positionTime.value = params.varietyInfo.positionTime;
222
+ tradeLog.value = params.varietyInfo.tradeLog ?? [];
223
+
224
+ // 默认值 [内部固定]
225
+ varietyStock.value = 1;
226
+ rightType.value = 1;
227
+ klineType.value = 0;
228
+ initTimeRange.value = [];
229
+ netPositionData.value = [];
230
+
231
+ // 默认值 [自定义配置]
232
+ pageType.value = userKlineConfig.pageType;
233
+ cycle.value = userKlineConfig.singelCycle;
234
+ sellBuy.value = userKlineConfig.sellBuy;
235
+ mainIndicator.value = userKlineConfig.mainIndicator;
236
+
237
+ await getVarietyStock();
238
+ await getJcVarietyOptions();
239
+ await getTradeLog();
240
+ await getNetPositionData();
241
+ handleTimeRange("reset");
242
+ } catch (error) {
243
+ throw new Error(`打开K线弹窗异常: ${error}`);
244
+ } finally {
245
+ loading.value = false;
246
+ // 埋点
247
+ actionParams.value = params.actionParams;
248
+ actionParams.value?.pageName && actionStat(actionParams.value?.pageName, "查看K线", pageType.value === 1 ? "单周期" : "多周期");
249
+ }
250
+ },
251
+ });
252
+ </script>
253
+
254
+ <template>
255
+ <el-dialog
256
+ class="kline-dialog"
257
+ v-model="visible"
258
+ :fullscreen="true"
259
+ :show-close="false"
260
+ :destroy-on-close="true"
261
+ :z-index="2000"
262
+ >
263
+ <div class="main element-dark">
264
+ <template v-if="!loading">
265
+ <!-- 功能栏 -->
266
+ <div class="kline-header">
267
+ <!-- 标题 -->
268
+ <div class="kline-header-item kline-header-item-select">
269
+ <span> {{ varietyName }} {{ varietyCode }} </span>
270
+ <el-select
271
+ v-model="varietyCode"
272
+ popper-class="element-dark"
273
+ @change="handleChangeJcVariety"
274
+ >
275
+ <el-option
276
+ v-for="(item, index) in jcVarietyOptions"
277
+ :key="item.varietyCode"
278
+ :label="`${item.varietyName} ${item.varietyCode}`"
279
+ :value="item.varietyCode"
280
+ />
281
+ </el-select>
282
+ </div>
283
+ <!-- 周期 -->
284
+ <template v-if="pageType === 1">
285
+ <div class="kline-header-item kline-header-item-select">
286
+ <span>{{ formatLabel(cycle, cycleOptions) }}</span>
287
+ <el-select
288
+ v-model="cycle"
289
+ popper-class="element-dark"
290
+ >
291
+ <el-option
292
+ v-for="(item, index) in cycleOptions"
293
+ :key="index"
294
+ :label="item.label"
295
+ :value="item.value"
296
+ />
297
+ </el-select>
298
+ </div>
299
+ </template>
300
+ <!-- 指标 -->
301
+ <div class="kline-header-item kline-header-item-select">
302
+ <span>{{ mainIndicator }}</span>
303
+ <el-select
304
+ v-model="mainIndicator"
305
+ popper-class="element-dark"
306
+ >
307
+ <el-option
308
+ v-for="(item, index) in indicatorStore.mainIndicatorList"
309
+ :key="index"
310
+ :label="item.label"
311
+ :value="item.value"
312
+ />
313
+ <template #footer>
314
+ <el-button
315
+ text
316
+ :icon="Setting"
317
+ @click="indicatorStore.config"
318
+ >
319
+ 指标配置
320
+ </el-button>
321
+ </template>
322
+ </el-select>
323
+ </div>
324
+ <!-- 买卖 -->
325
+ <div class="kline-header-item kline-header-item-select">
326
+ <span>{{ formatLabel(sellBuy, sellBuyOptions) }}</span>
327
+ <el-select
328
+ v-model="sellBuy"
329
+ popper-class="element-dark"
330
+ >
331
+ <el-option
332
+ v-for="(item, index) in sellBuyOptions"
333
+ :key="index"
334
+ :label="item.label"
335
+ :value="item.value"
336
+ />
337
+ </el-select>
338
+ </div>
339
+ <!-- 复权选项 [股票] -->
340
+ <template v-if="varietyStock === 1">
341
+ <div class="kline-header-item kline-header-item-select">
342
+ <span>{{ formatLabel(rightType, rightTypeOptions) }}</span>
343
+ <el-select
344
+ v-model="rightType"
345
+ popper-class="element-dark"
346
+ >
347
+ <el-option
348
+ v-for="(item, index) in rightTypeOptions"
349
+ :key="index"
350
+ :label="item.label"
351
+ :value="item.value"
352
+ />
353
+ </el-select>
354
+ </div>
355
+ </template>
356
+ <!-- 常用选项 [期货] -->
357
+ <template v-if="varietyStock === 0">
358
+ <div class="kline-header-item kline-header-item-select">
359
+ <span>{{ formatLabel(klineType, klineTypeOptions) }}</span>
360
+ <el-select
361
+ v-model="klineType"
362
+ popper-class="element-dark"
363
+ >
364
+ <el-option
365
+ v-for="(item, index) in klineTypeOptions"
366
+ :key="index"
367
+ :label="item.label"
368
+ :value="item.value"
369
+ />
370
+ </el-select>
371
+ </div>
372
+ </template>
373
+ <!-- 品种切换 -->
374
+ <template v-if="varietyList.length">
375
+ <div class="kline-header-item">
376
+ <el-button
377
+ size="small"
378
+ :icon="ArrowLeftBold"
379
+ @click="handleChangeVariety('pre')"
380
+ >
381
+ 上一品种
382
+ </el-button>
383
+ <el-button
384
+ size="small"
385
+ @click="handleChangeVariety('next')"
386
+ >
387
+ 下一品种
388
+ <el-icon class="el-icon--right"><ArrowRightBold /></el-icon>
389
+ </el-button>
390
+ </div>
391
+ </template>
392
+ <!-- 时间控制 -->
393
+ <div class="kline-header-item">
394
+ <el-button
395
+ link
396
+ size="small"
397
+ @click="handleTimeRange('week')"
398
+ >本周</el-button
399
+ >
400
+ <el-button
401
+ link
402
+ size="small"
403
+ @click="handleTimeRange('month')"
404
+ >本月</el-button
405
+ >
406
+ <el-button
407
+ size="small"
408
+ :icon="RefreshRight"
409
+ @click="handleTimeRange('reset')"
410
+ >重置</el-button
411
+ >
412
+ </div>
413
+ <!-- 关闭 -->
414
+ <div class="kline-header-item-right">
415
+ <el-radio-group
416
+ size="small"
417
+ v-model="pageType"
418
+ >
419
+ <el-radio-button :value="1">单周期</el-radio-button>
420
+ <el-radio-button :value="2">多周期</el-radio-button>
421
+ </el-radio-group>
422
+ <el-icon
423
+ class="setting-icon"
424
+ title="K线自定义配置"
425
+ :size="20"
426
+ @click="stKlineConfigRef?.open"
427
+ >
428
+ <Setting />
429
+ </el-icon>
430
+ <el-button
431
+ size="small"
432
+ @click="() => (visible = false)"
433
+ >
434
+ 关闭窗口
435
+ <el-icon class="close-icon">
436
+ <CloseBold />
437
+ </el-icon>
438
+ </el-button>
439
+ </div>
440
+ </div>
441
+ <!-- 内容 -->
442
+ <div class="kline-body">
443
+ <!-- K线组件 [单周期] -->
444
+ <template v-if="pageType === 1">
445
+ <st-klinePlus
446
+ :varietyCode="varietyCode"
447
+ :varietyStock="varietyStock"
448
+ :indicatorStore="indicatorStore"
449
+ :userKlineConfig="userKlineConfig"
450
+ :cycle="cycle"
451
+ :sellBuy="sellBuy"
452
+ :rightType="rightType"
453
+ :klineType="klineType"
454
+ :initTimeRange="initTimeRange"
455
+ :mainIndicator="mainIndicator"
456
+ :tradeLog="tradeLog"
457
+ :netPositionData="netPositionData"
458
+ :positionTime="positionTime"
459
+ />
460
+ </template>
461
+ <!-- K线组件 [多周期] -->
462
+ <template v-else>
463
+ <MultiCycle
464
+ ref="MultiCycleRef"
465
+ :varietyCode="varietyCode"
466
+ :varietyStock="varietyStock"
467
+ :indicatorStore="indicatorStore"
468
+ :userKlineConfig="userKlineConfig"
469
+ :sellBuy="sellBuy"
470
+ :rightType="rightType"
471
+ :klineType="klineType"
472
+ :initTimeRange="initTimeRange"
473
+ :mainIndicator="mainIndicator"
474
+ :tradeLog="tradeLog"
475
+ :netPositionData="netPositionData"
476
+ :positionTime="positionTime"
477
+ @goToSingleCycle="goToSingleCycle"
478
+ />
479
+ </template>
480
+ </div>
481
+ </template>
482
+ <template v-else>
483
+ <div
484
+ class="user-kline-config-loading"
485
+ v-loading="loading"
486
+ />
487
+ </template>
488
+ </div>
489
+ </el-dialog>
490
+ <st-klineConfig
491
+ ref="stKlineConfigRef"
492
+ :indicatorStore="indicatorStore"
493
+ @callBack="(formJson) => handleUserKlineConfig('update', formJson)"
494
+ />
495
+ </template>
496
+
497
+ <style lang="scss">
498
+ .kline-dialog {
499
+ padding: 0;
500
+ background-color: black;
501
+ .el-dialog__header {
502
+ display: none !important;
503
+ }
504
+ .el-dialog__body {
505
+ padding: 0;
506
+ border: none;
507
+ }
508
+ }
509
+ </style>
510
+ <style lang="scss" scoped>
511
+ .kline-dialog {
512
+ .main {
513
+ width: 100vw;
514
+ height: 100vh;
515
+ color-scheme: dark;
516
+ background-color: black;
517
+ overflow: hidden;
518
+ position: relative;
519
+ .kline-header {
520
+ color: white;
521
+ font-size: 14px;
522
+ display: flex;
523
+ align-items: center;
524
+ border-bottom: 1px solid rgb(100, 100, 100);
525
+ position: relative;
526
+ .kline-header-item {
527
+ height: 32px;
528
+ line-height: 32px;
529
+ display: flex;
530
+ align-items: center;
531
+ border-right: 1px solid rgb(100, 100, 100);
532
+ padding: 0 6px;
533
+ box-sizing: border-box;
534
+ }
535
+ .kline-header-item-select {
536
+ :deep(.el-select) {
537
+ margin-left: 4px;
538
+ width: 16px;
539
+ .el-select__icon {
540
+ margin-left: 0;
541
+ color: black;
542
+ width: 16px;
543
+ height: 16px;
544
+ }
545
+ .el-select__wrapper {
546
+ border-radius: 50%;
547
+ background: #ccc;
548
+ padding: 0;
549
+ gap: 0;
550
+ min-height: 12px;
551
+ line-height: 12px;
552
+ }
553
+ }
554
+ }
555
+ .kline-header-item-right {
556
+ height: 100%;
557
+ position: absolute;
558
+ top: 0px;
559
+ right: 6px;
560
+ display: flex;
561
+ align-items: center;
562
+ gap: 10px;
563
+ .el-icon {
564
+ font-size: 18px;
565
+ cursor: pointer;
566
+ &:hover {
567
+ color: var(--el-color-primary);
568
+ }
569
+ }
570
+ }
571
+ }
572
+ .kline-body {
573
+ width: 100vw;
574
+ height: calc(100vh - 39px);
575
+ }
576
+ .user-kline-config-loading {
577
+ width: 100%;
578
+ height: 100%;
579
+ }
580
+ }
581
+ }
582
+ </style>
@@ -0,0 +1,109 @@
1
+ import { defineStore } from "pinia";
2
+ import { getIndicator, getConfig, setConfig } from "./api";
3
+
4
+ const useIndicatorStore = defineStore("indicator", {
5
+ state: () => ({
6
+ visible: false, // 是否显示配置弹窗
7
+ originConfig: {}, // 原始配置
8
+ filterIndicator: {}, // 内置指标过滤配置
9
+ customIndicator: [], // 自定义指标配置
10
+ mainSystemIndicatorList: [], // 内置主图指标配置
11
+ subIndicatorList: [], // 副图指标配置
12
+ indicatorItemList: [], // 指标项配置
13
+ }),
14
+ getters: {
15
+ mainIndicatorList: (state) => {
16
+ // 主图指标列表(过滤隐藏的指标)
17
+ return [...state.mainSystemIndicatorList, ...state.customIndicator];
18
+ },
19
+ // 获取指标项配置
20
+ getIndicatorParams: (state) => {
21
+ return (mainIndicator) => {
22
+ let indicatorParams = [];
23
+ state.mainSystemIndicatorList.forEach((item) => {
24
+ if (item.label === mainIndicator) {
25
+ const filterConfig = state.filterIndicator[mainIndicator] || [];
26
+ indicatorParams = item.config.filter((i) => !filterConfig.includes(i.key)).map((i) => i.key);
27
+ }
28
+ });
29
+ state.customIndicator.forEach((item) => {
30
+ if (item.label === mainIndicator) {
31
+ indicatorParams = item.config.map((i) => i.key);
32
+ }
33
+ });
34
+ return indicatorParams;
35
+ };
36
+ },
37
+ },
38
+ actions: {
39
+ // 初始化配置
40
+ async init() {
41
+ const indicatorRes = await getIndicator();
42
+ this.mainSystemIndicatorList = indicatorRes?.body?.mainIndicatorList || [];
43
+ this.subIndicatorList = indicatorRes?.body?.subIndicatorList || [];
44
+ this.indicatorItemList = indicatorRes?.body?.indicatorItemList || [];
45
+ const res = await getConfig();
46
+ let originConfig = {};
47
+ let filterIndicator = {};
48
+ let customIndicator = [];
49
+ if (res.data) {
50
+ originConfig = JSON.parse(res.data);
51
+ filterIndicator = originConfig.filterIndicator || {};
52
+ customIndicator = originConfig.customIndicator || [];
53
+ }
54
+ this.originConfig = originConfig;
55
+ this.filterIndicator = filterIndicator;
56
+ this.customIndicator = customIndicator;
57
+ },
58
+ // 代开配置弹窗
59
+ config() {
60
+ this.visible = true;
61
+ },
62
+ // 更新内置指标过滤配置
63
+ async setFilterIndicator(key, filterIndicatorList) {
64
+ await setConfig({
65
+ data: JSON.stringify({
66
+ ...this.originConfig,
67
+ filterIndicator: {
68
+ ...this.filterIndicator,
69
+ [key]: filterIndicatorList,
70
+ },
71
+ }),
72
+ });
73
+ this.filterIndicator[key] = filterIndicatorList;
74
+ },
75
+ // 更新自定义指标配置配置
76
+ async setCustomIndicator({ label, config, originLabel, type }) {
77
+ let newCustomIndicator = [...this.customIndicator];
78
+ if (type === "add") {
79
+ // 新增指标
80
+ newCustomIndicator = [...newCustomIndicator, { label, value: label, config: [], type: "custom" }];
81
+ } else if (type === "editLabel") {
82
+ // 编辑指标名称
83
+ newCustomIndicator = newCustomIndicator.map((item) => {
84
+ if (item.label === originLabel) {
85
+ return { ...item, label, value: label };
86
+ }
87
+ return item;
88
+ });
89
+ } else if (type === "editConfig") {
90
+ // 编辑指标配置
91
+ newCustomIndicator = newCustomIndicator.map((item) => {
92
+ if (item.label === label) {
93
+ return { ...item, config };
94
+ }
95
+ return item;
96
+ });
97
+ } else if (type === "delete") {
98
+ // 删除指标
99
+ newCustomIndicator = newCustomIndicator.filter((item) => item.label !== label);
100
+ }
101
+ await setConfig({
102
+ data: JSON.stringify({ ...this.originConfig, customIndicator: newCustomIndicator }),
103
+ });
104
+ this.customIndicator = newCustomIndicator;
105
+ },
106
+ },
107
+ });
108
+
109
+ export default useIndicatorStore;