st-comp 0.0.47 → 0.0.49

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.
@@ -0,0 +1,142 @@
1
+ <template>
2
+ <div class="st-VarietySearch-modelSearch">
3
+ <div class="st-VarietySearch-modelSearch-label">
4
+ {{ config.label }}:
5
+ <el-button
6
+ text
7
+ size="small"
8
+ type="primary"
9
+ >
10
+ 不限
11
+ </el-button>
12
+ </div>
13
+ <div class="st-VarietySearch-modelSearch-content">
14
+ <el-button
15
+ v-for="item in orderByInfoList"
16
+ :key="item.ksy"
17
+ size="small"
18
+ text
19
+ @click="open(item)"
20
+ >
21
+ {{ item.label }}
22
+ </el-button>
23
+ </div>
24
+ <div class="st-VarietySearch-modelSearch-tag">
25
+ <el-tag
26
+ v-for="item in list"
27
+ :key="item.key"
28
+ closable
29
+ type="info"
30
+ :disable-transitions="false"
31
+ @close="deleteList(item)"
32
+ style="margin-right: 5px; margin-bottom: 5px;"
33
+ >
34
+ <template v-if="item.type === 'default'">
35
+ <template v-if="typeof item.start === 'number' && typeof item.end === 'number'">
36
+ {{ item.label }}:{{ item.start }}{{ item.startUnit }} ~ {{ item.end }}{{ item.endUnit }}
37
+ </template>
38
+ <template v-else-if="typeof item.start === 'number'">
39
+ {{ item.label }}:≥{{ item.start }}{{ item.endUnit }}
40
+ </template>
41
+ <template v-else-if="typeof item.end === 'number'">
42
+ {{ item.label }}:≤{{ item.end }}{{ item.endUnit }}
43
+ </template>
44
+ </template>
45
+ <template v-else-if="item.type ==='radio'">
46
+ {{ item.label }}:{{ item.radioLabel }}
47
+ </template>
48
+ <template v-else-if="item.type ==='tFeaturelncomes'">
49
+ {{ item.label }}:{{ item.withFewYears }}年内{{ item.yearsCount ? `${item.yearsCount}年以上` : '' }}盈利{{ item.rule === 2 ? "跌" : "超" }}过{{ item.netProfit }}亿
50
+ </template>
51
+ <el-text
52
+ style="font-size: 12px; cursor: pointer"
53
+ type="primary"
54
+ @click="open(item)"
55
+ >
56
+ 修改
57
+ </el-text>
58
+ </el-tag>
59
+ </div>
60
+ <Dialog
61
+ v-model="visible"
62
+ :data="modelItem"
63
+ @submit="onSubmit"
64
+ />
65
+ </div>
66
+ </template>
67
+
68
+ <script setup>
69
+ import { ref, computed } from 'vue'
70
+ import { getConfig } from './config'
71
+ import Dialog from './Dialog.vue'
72
+
73
+ const orderByInfoList = getConfig()
74
+
75
+ const emit = defineEmits(['change']);
76
+ const props = defineProps({
77
+ config: {
78
+ type: Object,
79
+ require: true,
80
+ default: () => ({}),
81
+ },
82
+ formData: {
83
+ type: Object,
84
+ require: true,
85
+ },
86
+ })
87
+
88
+ const visible = ref(false)
89
+ const modelItem = ref({})
90
+ const list = computed(() => {
91
+ return props.formData[props.config.key]
92
+ })
93
+
94
+ const open = (item) => {
95
+ modelItem.value = item
96
+ visible.value = true
97
+ }
98
+
99
+ const deleteList = (data) => {
100
+ emit('change', props.config.key, list.value.filter((item) => item.key !== data.key))
101
+ }
102
+
103
+ const onSubmit = (data) => {
104
+ if (list.value.find((item) => item.key === data.key)) {
105
+ // 已经存在当前指标
106
+ emit('change', props.config.key, list.value.map(item => {
107
+ if (item.key === data.key) {
108
+ return data
109
+ }
110
+ return item
111
+ }))
112
+ } else {
113
+ // 不存在当前指标
114
+ emit('change', props.config.key, [...list.value, data])
115
+ }
116
+ }
117
+ </script>
118
+
119
+ <style lang="scss" scoped>
120
+ .st-VarietySearch-modelSearch {
121
+ &-label {
122
+ display: inline-block;
123
+ vertical-align: top;
124
+ width: 120px;
125
+ height: 25px;
126
+ line-height: 24px;
127
+ font-size: 12px;
128
+ &-clear {
129
+ color: #409eff;
130
+ }
131
+ }
132
+ &-content {
133
+ vertical-align: top;
134
+ width: calc(100% - 120px);
135
+ display: inline-block;
136
+ }
137
+ &-tag {
138
+ min-height: 29px;
139
+ margin: 5px 0 10px 0;
140
+ }
141
+ }
142
+ </style>
@@ -0,0 +1,8 @@
1
+ import { App } from "vue";
2
+ import StVarietySearch from "./index.vue";
3
+
4
+ export default {
5
+ install(app: App) {
6
+ app.component("st-varietySearch", StVarietySearch);
7
+ },
8
+ }
@@ -0,0 +1,85 @@
1
+ <template>
2
+ <div class="st-VarietySearch">
3
+ <div
4
+ v-for="item in formConfig"
5
+ :key="item.key"
6
+ class="st-VarietySearch-item"
7
+ >
8
+ <CheckBox
9
+ v-if="item.type === 'checkbox' && (item.show === true || item.show === undefined)"
10
+ :formData="formData"
11
+ :config="item"
12
+ @change="change"
13
+ />
14
+ <FactorFilter
15
+ v-else-if="item.type === 'factorFilter' && (item.show === true || item.show === undefined)"
16
+ :formData="formData"
17
+ :config="item"
18
+ @change="change"
19
+ />
20
+ <ModelSearch
21
+ v-else-if="item.type === 'queryList' && (item.show === true || item.show === undefined)"
22
+ :formData="formData"
23
+ :config="item"
24
+ @change="change"
25
+ />
26
+ </div>
27
+ </div>
28
+ </template>
29
+
30
+ <script setup>
31
+ import { computed } from "vue"
32
+ import CheckBox from './components/CheckBox/index.vue'
33
+ import FactorFilter from './components/FactorFilter/index.vue'
34
+ import ModelSearch from './components/ModelSearch/index.vue'
35
+
36
+ const emit = defineEmits(['update:modelValue', 'change']);
37
+
38
+ const props = defineProps({
39
+ modelValue: {
40
+ type: Object,
41
+ require: true,
42
+ },
43
+ config: {
44
+ type: Object,
45
+ require: true,
46
+ },
47
+ });
48
+
49
+ const formData = computed({
50
+ get() {
51
+ return props.modelValue;
52
+ },
53
+ set(val) {
54
+ emit("update:modelValue", val);
55
+ },
56
+ });
57
+
58
+ // 另设表单数据源,避免直接使用传入数据源导致非主动的子影响父
59
+ const formConfig = computed(() => {
60
+ return Object.keys(props.config).map((key) => {
61
+ return {
62
+ key,
63
+ ...props.config[key],
64
+ show:
65
+ props.config[key].show === undefined ? true : props.config[key].show, // 默认展示
66
+ };
67
+ });
68
+ });
69
+
70
+ const change = (key, value) => {
71
+ formData.value = {
72
+ ...formData.value,
73
+ [key]: value,
74
+ }
75
+ emit('change', key, value)
76
+ }
77
+ </script>
78
+
79
+ <style lang="scss" scoped>
80
+ .st-VarietySearch {
81
+ &-item {
82
+ margin-bottom: 5px;
83
+ }
84
+ }
85
+ </style>
@@ -0,0 +1,8 @@
1
+ import { App } from "vue";
2
+ import StVirtualTable from "./index.vue";
3
+
4
+ export default {
5
+ install(app: App) {
6
+ app.component("st-virtualTable", StVirtualTable);
7
+ },
8
+ }
@@ -0,0 +1,237 @@
1
+ <!-- 虚拟表格 -->
2
+ <script lang="jsx" setup name="VirtualTable">
3
+ import { ref, computed, watch, onUnmounted } from "vue";
4
+
5
+ const emit = defineEmits(["scrollCallBack"]);
6
+
7
+ const props = defineProps({
8
+ // 表格数据
9
+ data: {
10
+ type: Array,
11
+ default: () => [
12
+ {
13
+ featureName: "第一个",
14
+ },
15
+ {
16
+ featureName: "第二个",
17
+ },
18
+ {
19
+ featureName: "第三个",
20
+ },
21
+ ],
22
+ },
23
+ // 表格渲染DOM结构
24
+ columns: {
25
+ type: Array,
26
+ default: () => [
27
+ {
28
+ key: "code",
29
+ title: "品种",
30
+ dataKey: "code",
31
+ width: 120,
32
+ fixed: true,
33
+ cellRenderer: ({ rowData }) => {
34
+ const { featureName } = rowData;
35
+ return (
36
+ <div class="overflowText" title={`${featureName}`}>
37
+ {featureName}
38
+ </div>
39
+ );
40
+ },
41
+ },
42
+ ],
43
+ },
44
+ // 功能配置项
45
+ config: {
46
+ type: Object,
47
+ default: () => {},
48
+ },
49
+ });
50
+
51
+ // 本组件的功能配置
52
+ const config = computed(() => {
53
+ return {
54
+ activeRowNum: 1, // 激活行数量, 默认1
55
+ activeRowKey: "id", // 激活行key, 默认id
56
+ isOpenPageUpDown: true, // 是否开启键盘pageUp,pageDown移动选择行功能
57
+ ...props.config,
58
+ };
59
+ });
60
+
61
+ const tableRef = ref(null); // 表格实例
62
+
63
+ const activeData = defineModel("activeData", { default: () => [] });
64
+ const activeDataKeys = computed(() => {
65
+ const { activeRowKey } = config.value;
66
+ return activeData.value.map((item) => item[activeRowKey]);
67
+ }); // 利用计算属性,方便表格行样式运算,放在函数中判断该数据会被多次运算,造成额外开销
68
+
69
+ /**
70
+ * @description: 表格行样式
71
+ */
72
+ const tableRowClass = ({ rowData }) => {
73
+ const { activeRowNum, activeRowKey } = config.value;
74
+ if (!activeRowNum || !activeData.value.length) return;
75
+ // 激活行样式处理
76
+ let className = "";
77
+ // 1.激活区域第一条,增加上边框高亮样式
78
+ if (rowData[activeRowKey] === activeDataKeys.value[0]) {
79
+ className += " virtualTable-active-Area-start";
80
+ }
81
+ // 2.激活区域最后一条,增加下边框高亮样式
82
+ if (rowData[activeRowKey] === activeDataKeys.value[activeDataKeys.value.length - 1]) {
83
+ className += " virtualTable-active-Area-end";
84
+ }
85
+ // 3.激活区域内所有,左右两边均增加高亮样式
86
+ if (activeDataKeys.value.includes(rowData[activeRowKey])) {
87
+ className += " virtualTable-active-Area-row";
88
+ }
89
+ return className;
90
+ };
91
+
92
+ /**
93
+ * @description: 表格行点击
94
+ */
95
+ const tableRowClick = ({ rowData, rowIndex }) => {
96
+ const { activeRowNum, activeRowKey } = config.value;
97
+ if (!activeRowNum || activeDataKeys.value.includes(rowData[activeRowKey])) return;
98
+ const startIndex = Math.floor(rowIndex / activeRowNum) * activeRowNum;
99
+ activeData.value = props.data.slice(startIndex, startIndex + activeRowNum);
100
+ };
101
+
102
+ /**
103
+ * @description: 表格滚动
104
+ * @todo: 返回表格滚动相关参数,并处理是否触底标记,暴露给传入的滚动事件
105
+ */
106
+ let keyDownFlag = false; // 解决手动调用table-v2实例滚动方法的BUG: scrollToRow无法保持横向滚动条位置,需手动记忆再调用scrollToLeft
107
+ let scrollLeft = 0;
108
+ const tableScroll = (params) => {
109
+ let isTouchBottom = false; // Y轴是否触底
110
+ // 1.计算虚拟容器高度
111
+ const virtualDom = document
112
+ .querySelector("#VirtualTable")
113
+ .querySelector(".el-table-v2__body")
114
+ .querySelector("div")
115
+ .querySelector("div");
116
+ const virtualDomHeight = Number(virtualDom.style.height.split("px")[0]);
117
+ // 2.计算表格内容展示高度
118
+ const tableContentDom = document
119
+ .querySelector("#VirtualTable")
120
+ .querySelector(".el-table-v2__body")
121
+ .querySelector("div");
122
+ const tableContentHeight = Number(tableContentDom.style.height.split("px")[0]);
123
+ // 3.计算Y轴是否触底
124
+ if (virtualDomHeight - params.scrollTop === tableContentHeight) isTouchBottom = true;
125
+ // 4.记录非键盘操作进行的表格滚动操作时, 横向滚动条位置, 用于解决scrollToRow会调整横向滚动条位置的问题
126
+ if (keyDownFlag === false) {
127
+ scrollLeft = params.scrollLeft;
128
+ }
129
+ // 5.执行传入的滚动回调函数
130
+ emit("scrollCallBack", { ...params, isTouchBottom });
131
+ };
132
+
133
+ // pageUp,pageDown切换选择行事件
134
+ const bindPageUpDownEvent = ({ code }) => {
135
+ const { data } = props;
136
+ const { activeRowNum, activeRowKey } = config.value;
137
+ if (!data.length) return;
138
+ const startIndex = data.findIndex((rowData) => rowData[activeRowKey] === activeDataKeys.value[0]);
139
+ if (code === "PageUp") {
140
+ const newStartIndex = startIndex - activeRowNum;
141
+ // 上移选中行
142
+ if (newStartIndex >= 0) {
143
+ activeData.value = data.slice(newStartIndex, startIndex);
144
+ keyDownFlag = true;
145
+ tableRef.value?.scrollToRow(newStartIndex);
146
+ tableRef.value?.scrollToLeft(scrollLeft);
147
+ keyDownFlag = false;
148
+ }
149
+ return;
150
+ } else if (code === "PageDown") {
151
+ // 如果激活行数据的最后一条, 是表数据的最后一行,触发触底事件
152
+ if (activeData.value[activeData.value.length - 1][activeRowKey] === data[data.length - 1][activeRowKey]) {
153
+ // 5.执行传入的滚动回调函数
154
+ emit("scrollCallBack", {
155
+ yAxisScrollDir: "forward",
156
+ isTouchBottom: true,
157
+ });
158
+ } else {
159
+ const newStartIndex = startIndex + activeRowNum;
160
+ activeData.value = data.slice(newStartIndex, newStartIndex + activeRowNum);
161
+ keyDownFlag = true;
162
+ tableRef.value?.scrollToRow(newStartIndex + activeData.value.length - 1);
163
+ tableRef.value?.scrollToLeft(scrollLeft);
164
+ keyDownFlag = false;
165
+ }
166
+ }
167
+ };
168
+
169
+ watch(
170
+ () => config.value.isOpenPageUpDown,
171
+ (newValue) => {
172
+ // 绑定pageUp,pageDown事件
173
+ newValue && config.value.activeRowNum && window.addEventListener("keydown", bindPageUpDownEvent);
174
+ },
175
+ {
176
+ immediate: true,
177
+ }
178
+ );
179
+
180
+ onUnmounted(() => {
181
+ // 解绑pageUp,pageDown事件
182
+ window.removeEventListener("keydown", bindPageUpDownEvent);
183
+ });
184
+ </script>
185
+
186
+ <template>
187
+ <el-auto-resizer>
188
+ <template #default="{ height, width }">
189
+ <el-table-v2
190
+ id="VirtualTable"
191
+ ref="tableRef"
192
+ :width="width"
193
+ :height="height"
194
+ :data="data"
195
+ :columns="columns"
196
+ :row-class="tableRowClass"
197
+ :row-event-handlers="{
198
+ onClick: tableRowClick,
199
+ }"
200
+ fixed
201
+ border
202
+ @scroll="tableScroll"
203
+ >
204
+ <!-- 空数据状态 -->
205
+ <template #empty>
206
+ <el-empty description="暂无数据" style="height: 100%" />
207
+ </template>
208
+ </el-table-v2>
209
+ </template>
210
+ </el-auto-resizer>
211
+ </template>
212
+
213
+ <style lang="scss" scoped>
214
+ #VirtualTable {
215
+ // 激活行样式
216
+ :deep(.el-table-v2__row.virtualTable-active-Area-start) {
217
+ border-top: 1px solid var(--el-color-primary);
218
+ }
219
+ :deep(.el-table-v2__row.virtualTable-active-Area-row) {
220
+ border-left: 1px solid var(--el-color-primary);
221
+ border-right: 1px solid var(--el-color-primary);
222
+ }
223
+ :deep(.el-table-v2__row.virtualTable-active-Area-end) {
224
+ border-bottom: 1px solid var(--el-color-primary);
225
+ }
226
+ // 固定左侧列的右边框样式消除
227
+ :deep(.el-table-v2__left) {
228
+ .virtualTable-active-Area-row {
229
+ border-right: 0;
230
+ }
231
+ }
232
+ // 表格空状态
233
+ :deep(.el-table-v2__empty) {
234
+ height: calc(100% - 50px);
235
+ }
236
+ }
237
+ </style>
package/packages/index.ts CHANGED
@@ -10,7 +10,8 @@ import StPagination from "./Pagination/index.ts"
10
10
  import StPie from "./Pie/index.ts"
11
11
  import StTable from "./Table/index.ts"
12
12
  import StTreeMap from "./TreeMap/index.ts"
13
- import StVarSeach from "./VarSeach/index.ts"
13
+ import StVarietySearch from "./VarietySearch/index.ts"
14
+ import StVirtualTable from "./VirtualTable/index.ts"
14
15
 
15
16
  export default {
16
17
  install(app: App) {
@@ -25,7 +26,8 @@ export default {
25
26
  StPie.install(app)
26
27
  StTable.install(app)
27
28
  StTreeMap.install(app)
28
- StVarSeach.install(app)
29
+ StVarietySearch.install(app)
30
+ StVirtualTable.install(app)
29
31
  },
30
32
  }
31
33
 
@@ -0,0 +1,69 @@
1
+ <template>
2
+ <div style="width: 100%; height: 100%;">
3
+ <st-varietySearch
4
+ v-model="searchParams"
5
+ :config="searchConfig"
6
+ @change="changeVarietySearch"
7
+ />
8
+ </div>
9
+ </template>
10
+
11
+ <script setup>
12
+ import { ref, watch, reactive } from "vue";
13
+
14
+ const searchParams = ref({
15
+ exchangeId: [],
16
+ optionId: [],
17
+ factorscores: [],
18
+ queryList: [],
19
+ });
20
+ const searchConfig = reactive({
21
+ exchangeId: {
22
+ label: '品种市场',
23
+ type: 'checkbox',
24
+ options: [
25
+ { label: 'A股', value: '0' },
26
+ { label: 'ETF', value: '1' },
27
+ { label: '期货', value: '2' },
28
+ ]
29
+ },
30
+ optionId: {
31
+ label: '常用选项',
32
+ type: 'checkbox',
33
+ options: [
34
+ { label: '常用选项1', value: '0' },
35
+ { label: '常用选项2', value: '1' },
36
+ { label: '常用选项3', value: '2' },
37
+ ]
38
+ },
39
+ factorscores: {
40
+ label: '因子筛选',
41
+ type: 'factorFilter',
42
+ freqList: [
43
+ { label: '周期1', value: '0' },
44
+ { label: '周期2', value: '1' },
45
+ { label: '周期3', value: '2' },
46
+ ],
47
+ factorList: [
48
+ { label: '因子1', value: '0' },
49
+ { label: '因子2', value: '1' },
50
+ { label: '因子3', value: '2' },
51
+ ]
52
+ },
53
+ queryList: {
54
+ label: '常用指标',
55
+ type: 'queryList',
56
+ }
57
+ })
58
+
59
+ // 用于品种联动
60
+ const changeVarietySearch = () => {
61
+ if (searchParams.value.exchangeId.includes('2')) {
62
+ searchConfig.queryList.show = false
63
+ } else {
64
+ searchConfig.queryList.show = true
65
+ }
66
+ }
67
+ </script>
68
+
69
+ <style lang="scss" scoped></style>