st-comp 0.0.216 → 0.0.218

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.216",
4
+ "version": "0.0.218",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite",
@@ -0,0 +1,189 @@
1
+ <template>
2
+ <div>
3
+ <el-tag
4
+ v-for="(item, index) in showCheckTagList"
5
+ closable
6
+ style="margin-right: 10px;"
7
+ type="info"
8
+ @close="handleAction('delete', index)"
9
+ >
10
+ <span>{{ item.name }}</span>
11
+ </el-tag>
12
+ <el-button
13
+ class="screen-btn"
14
+ type="primary"
15
+ plain
16
+ size="small"
17
+ :icon="Plus"
18
+ @click="handleAction('add')"
19
+ >
20
+ 添加标签
21
+ </el-button>
22
+ </div>
23
+ <!-- 添加标签弹窗 -->
24
+ <el-dialog
25
+ v-model="visible"
26
+ title="Tips"
27
+ width="1000"
28
+ >
29
+ <template #header>
30
+ <div>
31
+ 添加标签
32
+ <el-radio-group v-model="tagType" size="small" style="margin-left: 8px; vertical-align: 5px;" v-if="0">
33
+ <el-radio-button label="个人" value="person" />
34
+ <el-radio-button label="系统" value="system" />
35
+ </el-radio-group>
36
+ <el-input v-model="searchValue" size="small" style="width: 120px; margin-left: 8px;" placeholder="请输入标签名称" />
37
+ </div>
38
+ </template>
39
+ <div class="tag-list">
40
+ <div class="tab-list-item" v-for="item in tagList">
41
+ <el-checkbox :checked="isCheck(item)" @change="changeCheckBox(item)">
42
+ <div style="display: flex; width: 280px;">
43
+ <div style="flex: 1;line-height: 24px;">{{ item.name }}</div>
44
+ <el-button size="small" :icon="Edit" v-if="0"></el-button>
45
+ <el-button size="small" :icon="Delete" @click="deleteTag(item)"></el-button>
46
+ </div>
47
+ </el-checkbox>
48
+ </div>
49
+ </div>
50
+ <template #footer>
51
+ <div class="dialog-footer">
52
+ <el-button @click="visible = false">取消</el-button>
53
+ <el-button type="primary" @click="handleAction('submit')">确定</el-button>
54
+ </div>
55
+ </template>
56
+ </el-dialog>
57
+ <!-- 编辑标签弹窗 -->
58
+ </template>
59
+
60
+ <script setup>
61
+ import { inject, ref, watch, computed, nextTick } from "vue";
62
+ import { Plus, Edit, Delete } from "@element-plus/icons-vue";
63
+
64
+ let searchTimer = null
65
+
66
+ const { request } = inject("stConfig"); // 组件库全局配置
67
+
68
+ const data = defineModel("data", { default: [] });
69
+ const visible = ref(false);
70
+ const tagType = ref('person'); // 标签类型
71
+ const searchValue = ref(''); // 搜索值
72
+ const totalTagList = ref([]); // 全部标签列表
73
+ const tagList = ref([]); // 标签列表
74
+ const checkList = ref([]); // 勾选列表
75
+
76
+ const showCheckTagList = computed(() => {
77
+ return data.value.map(id => {
78
+ return {
79
+ name: totalTagList.value?.find(i => i.id === id)?.tagName,
80
+ id,
81
+ }
82
+ })
83
+ })
84
+
85
+ watch(() => tagType.value, () => {
86
+ searchValue.value = ''
87
+ })
88
+
89
+ watch(() => searchValue.value, () => {
90
+ clearTimeout(searchTimer)
91
+ searchTimer = setTimeout(() => {
92
+ getTagList()
93
+ }, 300)
94
+ })
95
+
96
+ const isCheck = (item) => {
97
+ return Boolean(checkList.value.includes(item.id))
98
+ }
99
+
100
+ const changeCheckBox = (item) => {
101
+ if (isCheck(item)) {
102
+ // 已经勾选
103
+ checkList.value = checkList.value.filter(i => i !== item.id)
104
+ } else {
105
+ checkList.value.push(item.id)
106
+ }
107
+ }
108
+
109
+ const handleAction = (action, index) => {
110
+ switch (action) {
111
+ // 新增
112
+ case "add": {
113
+ tagList.value = []
114
+ visible.value = true;
115
+ searchValue.value = '';
116
+ checkList.value = [...data.value]
117
+ getTagList()
118
+ break;
119
+ }
120
+ // 编辑
121
+ case "edit": {
122
+ const item = data.value[index];
123
+ compositeOrderForm.value = { ...item };
124
+ visible.value = true;
125
+ break;
126
+ }
127
+ // 窗口确认
128
+ case "submit": {
129
+ visible.value = false;
130
+ data.value = [...checkList.value]
131
+ break;
132
+ }
133
+ // 删除
134
+ case "delete": {
135
+ data.value.splice(index, 1);
136
+ break;
137
+ }
138
+ }
139
+ };
140
+
141
+ const deleteTag = (item) => {
142
+ ElMessageBox.alert(`确认删除标签“${item.name}”吗?`, '删除标签', {
143
+ confirmButtonText: 'OK',
144
+ callback: async (action) => {
145
+ if (action === 'confirm') {
146
+ await request.post('/alarm/deliversign/deleteTag', { tagId: item.id })
147
+ getTagList()
148
+ Elmessage.success('删除成功!')
149
+ }
150
+ },
151
+ })
152
+ }
153
+
154
+ const getTagList = async (isTotal = false) => {
155
+ if (tagType.value === 'person') {
156
+ // 个人标签
157
+ const res = await request.post('/alarm/deliversign/findTagsByUserId', { tagName: searchValue.value })
158
+ tagList.value = []
159
+ nextTick(() => {
160
+ tagList.value = res.body?.map(item => ({
161
+ name: item.tagName,
162
+ id: item.id
163
+ }))
164
+ })
165
+ if (isTotal) {
166
+ totalTagList.value = res.body
167
+ }
168
+ } else {
169
+ // 系统标签
170
+ }
171
+ }
172
+
173
+ getTagList(true)
174
+
175
+ defineExpose({
176
+ updateTag: () => {
177
+ getTagList(true)
178
+ }
179
+ })
180
+ </script>
181
+
182
+ <style lang="scss" scoped>
183
+ .tag-list {
184
+ .tab-list-item {
185
+ display: inline-block;
186
+ width: 33%;
187
+ }
188
+ }
189
+ </style>
@@ -38,6 +38,8 @@ const visibleDescriptions = ref(false);
38
38
  const factorType = ref("脚本");
39
39
  const scriptTestLoading = ref(false);
40
40
  const scriptCopyLoading = ref(false);
41
+ const testVariety = ref("");
42
+ const testVarietyVisible = ref(false);
41
43
  const scriptTestLogVisible = ref(false);
42
44
  const scriptTestResult = reactive({
43
45
  result: null,
@@ -320,33 +322,55 @@ const handleScriptCopy = async () => {
320
322
  scriptCopyLoading.value = false;
321
323
  }
322
324
  };
323
- // 脚本: 测试
324
- const handleScriptTest = async () => {
325
- try {
326
- scriptTestLoading.value = true;
327
- const script = monacoEditorRef.value.getValue();
328
- if (!script) return ElMessage.error("请输入脚本语句");
329
- const { body } = await request.post("/common/qt/getFuncExpr", { factorSelectExpr: script });
330
- if (!body) return ElMessage.error("脚本解析失败, 请检查脚本内容是否填写完整或者联系管理员");
331
- const testRes = await request.post("/common/qt/testFactorSelect", { factorSelectExpr: body });
332
- const { result, detail } = testRes.body;
333
- Object.assign(scriptTestResult, { result, detail, code: body });
334
- if (result === 1) {
335
- ElMessage.success("测试通过");
336
- } else {
337
- ElMessage.error("测试未能通过");
325
+ // 测试: 打开/确认/日志
326
+ const extractCodesFromString = (str) => {
327
+ if (!str) return [];
328
+ return str.split("\n").reduce((result, next) => {
329
+ const code = next.trim().replace(/\s+/gi, " ").split(" ")[0];
330
+ code && result.push(code);
331
+ return result;
332
+ }, []);
333
+ };
334
+ const handleScriptTest = async (action) => {
335
+ switch (action) {
336
+ case "open": {
337
+ // 基础校验
338
+ if (!monacoEditorRef.value.getValue()) return ElMessage.error("脚本语句不可为空");
339
+ testVarietyVisible.value = true;
340
+ break;
341
+ }
342
+ case "submit": {
343
+ testVarietyVisible.value = false;
344
+ try {
345
+ scriptTestLoading.value = true;
346
+ const { body } = await request.post("/common/qt/getFuncExpr", { factorSelectExpr: monacoEditorRef.value.getValue() });
347
+ if (!body) return ElMessage.error("脚本解析失败, 请检查脚本内容是否填写完整或者联系管理员");
348
+ const params = {
349
+ codes: extractCodesFromString(testVariety.value),
350
+ factorSelectExpr: body,
351
+ };
352
+ const testRes = await request.post("/common/qt/testFactorSelect", params);
353
+ const { result, detail } = testRes.body;
354
+ Object.assign(scriptTestResult, { result, detail, code: body });
355
+ if (result === 1) {
356
+ ElMessage.success("测试通过");
357
+ } else {
358
+ ElMessage.error("测试未能通过");
359
+ scriptTestLogVisible.value = true;
360
+ }
361
+ } finally {
362
+ scriptTestLoading.value = false;
363
+ }
364
+ break;
365
+ }
366
+ case "log": {
367
+ if (scriptTestResult.result === null) {
368
+ return ElMessage.warning("请先进行测试, 等待测试完成后可查看日志");
369
+ }
338
370
  scriptTestLogVisible.value = true;
371
+ break;
339
372
  }
340
- } finally {
341
- scriptTestLoading.value = false;
342
- }
343
- };
344
- // 脚本: 日志明细
345
- const handleScriptLog = () => {
346
- if (scriptTestResult.result === null) {
347
- return ElMessage.warning("请先进行测试, 等待测试完成后可查看日志");
348
373
  }
349
- scriptTestLogVisible.value = true;
350
374
  };
351
375
 
352
376
  // 监控: 窗口开关
@@ -378,6 +402,7 @@ watch(
378
402
  case false: {
379
403
  stVarSelectDialogRef.value.close();
380
404
  scriptTestLogVisible.value = false;
405
+ testVariety.value = "";
381
406
  break;
382
407
  }
383
408
  }
@@ -526,13 +551,13 @@ watch(
526
551
  type="primary"
527
552
  size="small"
528
553
  :loading="scriptTestLoading"
529
- @click="handleScriptTest"
554
+ @click="handleScriptTest('open')"
530
555
  >测试</el-button
531
556
  >
532
557
  <el-button
533
558
  size="small"
534
559
  :icon="Document"
535
- @click="handleScriptLog"
560
+ @click="handleScriptTest('log')"
536
561
  >日志明细</el-button
537
562
  >
538
563
  </div>
@@ -812,6 +837,55 @@ watch(
812
837
  :factorType="factorType"
813
838
  :data="config.factorDescriptions?.filter((item) => [1, 3].includes(item.type))"
814
839
  />
840
+ <!-- 窗口: 选择测试所用的品种 -->
841
+ <el-dialog
842
+ modal-class="test-variety-dialog"
843
+ v-model="testVarietyVisible"
844
+ width="500"
845
+ align-center
846
+ append-to-body
847
+ overflow
848
+ :modal="false"
849
+ :modal-penetrable="true"
850
+ :show-close="false"
851
+ >
852
+ <template #header="{ titleId, titleClass }">
853
+ <div class="custom-header">
854
+ <div class="left">
855
+ <span
856
+ :id="titleId"
857
+ :class="titleClass"
858
+ >
859
+ 测试品种
860
+ </span>
861
+ </div>
862
+ <div class="right">
863
+ <st-varietyAutoComplete
864
+ label=""
865
+ @select="({ name, code }) => (testVariety += `${code} ${name} \n`)"
866
+ />
867
+ <el-icon @click="testVarietyVisible = false"><Close /></el-icon>
868
+ </div>
869
+ </div>
870
+ </template>
871
+ <el-input
872
+ class="full-height-textarea"
873
+ v-model="testVariety"
874
+ resize="none"
875
+ type="textarea"
876
+ :placeholder="`不填写时, 默认为000001 平安银行\n格式示例:\nhc8888\xa0\xa0\xa0\xa0热轧卷板期货指数\nsp8888\xa0\xa0\xa0\xa0纸浆期货指数\nbu8888\xa0\xa0\xa0\xa0石油沥青期货指数`"
877
+ />
878
+ <template #footer>
879
+ <div class="dialog-footer">
880
+ <el-button
881
+ type="primary"
882
+ @click="handleScriptTest('submit')"
883
+ >
884
+ 确认
885
+ </el-button>
886
+ </div>
887
+ </template>
888
+ </el-dialog>
815
889
  <!-- 窗口: 日志明细 -->
816
890
  <el-dialog
817
891
  modal-class="log-dialog"
@@ -853,7 +927,7 @@ watch(
853
927
  <el-divider direction="vertical" />
854
928
  <!-- 日志 -->
855
929
  <el-scrollbar height="600px">
856
- <pre :class="scriptTestResult.result === 1 ? 'success-log' : 'error-log'">{{ scriptTestResult.result === 1 ? "测试通过 √" : scriptTestResult.detail }}</pre>
930
+ <pre :class="scriptTestResult.result === 1 ? 'success-log' : 'error-log'">{{ scriptTestResult.result === 1 ? `✅️ 测试通过\n${scriptTestResult.detail}` : scriptTestResult.detail }}</pre>
857
931
  </el-scrollbar>
858
932
  </div>
859
933
  <!-- 底部 -->
@@ -912,6 +986,31 @@ watch(
912
986
  }
913
987
  }
914
988
  }
989
+ .test-variety-dialog {
990
+ .custom-header {
991
+ display: flex;
992
+ align-items: center;
993
+ justify-content: space-between;
994
+ .left,
995
+ .right {
996
+ display: flex;
997
+ align-items: center;
998
+ gap: 10px;
999
+ }
1000
+ .el-icon {
1001
+ cursor: pointer;
1002
+ }
1003
+ }
1004
+ .full-height-textarea {
1005
+ width: 100%;
1006
+ height: 200px;
1007
+ margin-top: 10px;
1008
+ :deep(.el-textarea__inner) {
1009
+ box-sizing: border-box;
1010
+ height: 100%;
1011
+ }
1012
+ }
1013
+ }
915
1014
  .log-dialog {
916
1015
  .custom-header {
917
1016
  display: flex;
@@ -1,9 +1,10 @@
1
1
  <script setup name="VarietySearch">
2
- import { watch, computed, provide } from "vue";
2
+ import { watch, computed, provide, ref } from "vue";
3
3
  import defaultConfig from "./config.js";
4
4
  import FactorScreen from "./components/FactorScreen/index.vue";
5
5
  import CommonIndicator from "./components/CommonIndicator/index.vue";
6
6
  import CompositeOrder from "./components/CompositeOrder/index.vue";
7
+ import AddTag from "./components/AddTag/index.vue";
7
8
 
8
9
  const props = defineProps({ config: { type: Object, default: {} } });
9
10
  const searchData = defineModel("searchData");
@@ -34,6 +35,9 @@ searchData.value = Object.assign(searchData.value, {
34
35
  compositeOrder: [],
35
36
  });
36
37
 
38
+ // 添加标签
39
+ const addTagRef = ref();
40
+
37
41
  // 合并配置项
38
42
  const config = computed(() => {
39
43
  const result = {};
@@ -482,6 +486,10 @@ defineExpose({
482
486
  compositeOrder: [],
483
487
  };
484
488
  },
489
+ // 更新标签
490
+ updateTag: () => {
491
+ addTagRef.value.updateTag();
492
+ },
485
493
  });
486
494
  </script>
487
495
 
@@ -548,23 +556,16 @@ defineExpose({
548
556
  </div>
549
557
  </template>
550
558
  <!-- 自定标签 -->
551
- <template v-if="config.customTag?.show && customTagDict.length">
559
+ <template v-if="config.customTag?.show">
552
560
  <div class="variety-search-row">
553
561
  <div class="title">
554
562
  <span>自定标签: </span>
555
563
  <span @click="clearRow('customTag')">不限</span>
556
564
  </div>
557
- <el-checkbox-group
558
- v-model="searchData.customTag"
559
- size="small"
560
- >
561
- <el-checkbox
562
- v-for="{ label, value } in customTagDict"
563
- :label="label"
564
- :value="value"
565
- :key="value"
566
- />
567
- </el-checkbox-group>
565
+ <AddTag
566
+ ref="addTagRef"
567
+ v-model:data="searchData.customTag"
568
+ />
568
569
  </div>
569
570
  </template>
570
571
  <!-- 价差转换 -->