web-component-gallery 2.0.9 → 2.0.11

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/dist/style.css CHANGED
@@ -2,12 +2,6 @@
2
2
  /* stylelint-disable no-duplicate-selectors */
3
3
  /* stylelint-disable */
4
4
  /* stylelint-disable declaration-bang-space-before,no-duplicate-selectors,string-no-newline */
5
- /* 布局( 纯为了不想多写一行 */
6
- /* 正方体盒子 */
7
- /* 滚动条样式 */
8
- /* 文字换行省略(默认1行)*/
9
- /* flex布局 */
10
- /* flex排版方式 */
11
5
  .weather {
12
6
  font-size: 16px;
13
7
  display: flex;
@@ -1,26 +1,28 @@
1
1
  <template>
2
+
2
3
  <div
3
- @mousedown="
4
- e => {
5
- e.preventDefault()
6
- !attrs.disabled && (selectOpen = true)
7
- }
8
- "
4
+ @mousedown="e => {
5
+ e.preventDefault()
6
+ !attrs.disabled && (isSelectOpen = true)
7
+ }"
9
8
  ref="main"
10
9
  class="ASelectCustom"
11
10
  >
12
11
  <Select
13
- class="t_select"
14
- v-model="childSelectedValue"
12
+ show-search
15
13
  :style="{ width: width || '100%' }"
14
+ :mode="mode"
15
+ :open="isSelectOpen"
16
16
  :placeholder="placeholder"
17
- :open="selectOpen"
18
- :filterOption="filterOption"
17
+ :filterOption="false"
18
+ :getPopupContainer="tirggerNode => tirggerNode.parentNode"
19
19
  @select="handleSelect"
20
- v-bind="attrs"
20
+ @search="handleSearch"
21
+ @popupScroll="handleScroll"
21
22
  v-on="$listeners"
22
- :mode="mode"
23
- >
23
+ v-bind="attrs"
24
+ v-model="childSelectedValue"
25
+ >
24
26
  <template v-for="(index, name) in $slots" v-slot:[name]>
25
27
  <slot :name="name" />
26
28
  </template>
@@ -28,25 +30,11 @@
28
30
  <slot :name="name" v-bind="data"></slot>
29
31
  </template>
30
32
  <div slot="dropdownRender" slot-scope="menu">
31
- <Checkbox v-if="mode && !isShowPagination" :checked="selectChecked" @change="selectAll">
32
- 全选
33
- </Checkbox>
33
+ <Checkbox v-if="mode" :checked="selectChecked" @change="handleSelectAll">全选</Checkbox>
34
34
  <v-nodes :vnodes="menu" />
35
- <div class="t_select__pagination" v-if="isShowPagination">
36
- <Pagination
37
- :page-size.sync="paginationOption.pageSize"
38
- v-model="paginationOption.current"
39
- :total="paginationOption.total"
40
- @change="currentChange"
41
- v-bind="{
42
- size: 'small',
43
- 'hide-on-single-page': true,
44
- showQuickJumper: true,
45
- ...$attrs,
46
- ...paginationOption.bind
47
- }"
48
- v-on="$listeners"
49
- />
35
+ <div v-if="isLoadingMore" class="loading-more">
36
+ <Icon type="loading" />
37
+ 加载中...
50
38
  </div>
51
39
  </div>
52
40
  <SelectOption v-for="(item, index) in options" :key="index" :value="item[valueKey]">
@@ -55,8 +43,10 @@
55
43
  </Select>
56
44
  </div>
57
45
  </template>
46
+
58
47
  <script>
59
- import { Select, Checkbox, Pagination } from 'ant-design-vue'
48
+ import { Icon, Select, Checkbox } from 'ant-design-vue'
49
+ import debounce from 'lodash/debounce'
60
50
 
61
51
  export default {
62
52
  name: 'ASelectCustom',
@@ -65,78 +55,62 @@ export default {
65
55
  functional: true,
66
56
  render: (h, ctx) => ctx.props.vnodes
67
57
  },
58
+ Icon,
68
59
  Select,
69
60
  SelectOption: Select.Option,
70
- Checkbox,
71
- Pagination
61
+ Checkbox
72
62
  },
73
63
  props: {
74
64
  value: {
75
65
  type: [String, Number, Array, Boolean, Object],
76
66
  default: undefined
77
67
  },
78
- // 多选 'multiple'
79
- mode: {
80
- type: String
81
- },
82
- // 是否支持输入
68
+ // 是否多选
69
+ mode: String,
70
+ // 是否支持输入保留字符
83
71
  isInput: {
84
72
  type: Boolean,
85
73
  default: false
86
74
  },
75
+ // 提示文字
87
76
  placeholder: {
88
77
  type: String,
89
78
  default: '请选择'
90
79
  },
91
- // 选择框宽度
92
- width: {
93
- type: String
94
- },
95
- // 是否自定义设置下拉label
96
- customLabel: {
97
- type: String
98
- },
99
- // 传入的option数组中,要作为最终选择项的键值key
80
+ width: String,
81
+ // 根据valueKey来进行自定义label
82
+ customLabel: String,
100
83
  valueKey: {
101
84
  type: String,
102
85
  default: 'value'
103
86
  },
104
- // 传入的option数组中,要作为显示项的键值名称
105
87
  labelKey: {
106
88
  type: String,
107
89
  default: 'label'
108
90
  },
109
- // 下拉框组件数据源
91
+ // 数据源
110
92
  options: {
111
- type: Array
93
+ type: Array,
94
+ default: () => ([])
112
95
  },
113
- // 是否显示分页
114
- isShowPagination: {
115
- type: Boolean,
116
- default: false
117
- },
118
- // 分页配置项
119
- paginationOption: {
120
- type: Object,
121
- default: () => {
122
- return {
123
- pageSize: 6, // 每页显示条数
124
- current: 1, // 当前页
125
- total: 0 // 总条数
126
- }
127
- }
128
- }
96
+ // 分页页数
97
+ pSize: Number,
98
+ // 分页总数,如要实现完整分页功能要传递接口总数
99
+ pTotal: Number
129
100
  },
130
101
  data() {
131
102
  return {
132
- selectOpen: false
103
+ isSelectOpen: false,
104
+ currentPage: 1,
105
+ isLoadingMore: false,
106
+ searchKeyword: ''
133
107
  }
134
108
  },
135
109
  computed: {
136
110
  childSelectedValue: {
137
111
  get() {
138
112
  return this.value
139
- },
113
+ },
140
114
  set(val) {
141
115
  this.$emit('input', val)
142
116
  }
@@ -148,6 +122,9 @@ export default {
148
122
  ...this.$attrs
149
123
  }
150
124
  },
125
+ hasMore() {
126
+ return this.options.length < this.pTotal
127
+ },
151
128
  selectChecked: {
152
129
  get() {
153
130
  return this.childSelectedValue?.length === this.options?.length
@@ -157,6 +134,10 @@ export default {
157
134
  }
158
135
  }
159
136
  },
137
+ created() {
138
+ this.debouncedSearch = debounce(this.handleSearch, 800)
139
+ this.debouncedLoadMore = debounce(this.loadMoreData, 300)
140
+ },
160
141
  mounted() {
161
142
  document.addEventListener('click', this.bodyCloseMenus)
162
143
  },
@@ -164,56 +145,74 @@ export default {
164
145
  document.removeEventListener('click', this.bodyCloseMenus)
165
146
  },
166
147
  methods: {
167
- filterOption(input, option) {
168
- this.isInput && (this.childSelectedValue = input)
169
- return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
170
- },
171
- // 点击空白区域
148
+ // 下拉滚动加载更多
149
+ handleScroll(e) {
150
+ const { scrollTop, scrollHeight, clientHeight } = e.target
151
+ const reachBottom = scrollTop + clientHeight >= scrollHeight - 10
152
+ if (reachBottom && !this.isLoadingMore && this.hasMore) this.debouncedLoadMore()
153
+ },
154
+ // 搜索事件
155
+ handleSearch(value) {
156
+ this.isInput && (this.childSelectedValue = value)
157
+ this.searchKeyword = value
158
+ this.currentPage = 1
159
+
160
+ // 根据是否有分页决定emit参数格式
161
+ this.$emit('search', this.pTotal ? {
162
+ keyword: value,
163
+ current: this.currentPage,
164
+ size: this.pSize
165
+ } : value)
166
+ },
167
+ // 点击选择器外内容下拉回收
172
168
  bodyCloseMenus(e) {
173
- if (this.$refs.main && !this.$refs.main.contains(e.target)) {
174
- if (this.selectOpen == true) {
175
- this.selectOpen = false
176
- }
177
- }
169
+ if (this.$refs.main && !this.$refs.main.contains(e.target)) this.isSelectOpen = false
178
170
  },
179
- // 点击全选
180
- selectAll(val) {
181
- const options = JSON.parse(JSON.stringify(this.options))
182
- if (val.target.checked) {
183
- const childSelectedValue = options?.map(item => {
184
- return item[this.valueKey]
185
- })
186
- setTimeout(() => {
187
- this.$emit('input', childSelectedValue)
188
- }, 0)
189
- } else {
190
- this.$emit('input', null)
171
+ // 自定义label显示
172
+ customLabelHandler(item) {
173
+ try {
174
+ // 使用函数式替代eval
175
+ const func = new Function('item', `return ${this.customLabel}`)
176
+ return func(item)
177
+ } catch (e) {
178
+ console.error('自定义label解析错误:', e)
179
+ return item[this.labelKey]
191
180
  }
192
- this.selectOpen = false
193
181
  },
182
+ // 选中全部
183
+ handleSelectAll(event) {
184
+ const isChecked = event.target.checked
185
+ const selectOptions = isChecked ? this.options.map(item => item[this.valueKey]) : []
186
+ this.$emit('input', selectOptions)
187
+ this.isSelectOpen = false
188
+ },
189
+ // 选中事件
194
190
  handleSelect(value, option) {
195
- if (value) {
196
- this.selectOpen = false
197
- }
198
- const options = this.options.find(e => e[this.valueKey] == value)
199
- options[this.valueKey] && this.$emit('select', value, options)
200
- },
201
- // 切换分页
202
- currentChange(val) {
203
- // console.log('切换分页', val)
204
- if (!this.mode) {
205
- this.childSelectedValue = null
191
+ if (!value) return
192
+ this.isSelectOpen = false
193
+ const selectedOption = this.options.find(item => item[this.valueKey] === value)
194
+ selectedOption && this.$emit('select', value, selectedOption)
195
+ },
196
+ // 加载更多
197
+ loadMoreData() {
198
+ if (this.isLoadingMore || !this.hasMore) return
199
+
200
+ this.isLoadingMore = true
201
+ this.currentPage++
202
+
203
+ try {
204
+ this.$emit('load-more', {
205
+ keyword: this.searchKeyword,
206
+ current: this.currentPage,
207
+ size: this.pSize
208
+ })
209
+ } catch (error) {
210
+ this.currentPage--
211
+ console.error('加载更多失败:', error)
212
+ } finally {
213
+ this.isLoadingMore = false
206
214
  }
207
- setTimeout(() => {
208
- this.selectOpen = true
209
- }, 0)
210
- this.$emit('current-change', val)
211
- },
212
- // 自定义label显示
213
- customLabelHandler(item) {
214
- // eslint-disable-next-line no-eval
215
- return eval(this.customLabel)
216
215
  }
217
216
  }
218
217
  }
219
- </script>
218
+ </script>
@@ -1,4 +1,20 @@
1
+ @import '~ant-design-vue/lib/style/themes/default.less';
2
+ @import '../../style/mixins.less';
3
+
1
4
  // 添加flex属性解决css中div莫名高度
2
5
  .ASelectCustom {
3
6
  width: 100%;
4
- }
7
+
8
+ .loading-more {
9
+ font-size: @font-size-base;
10
+ text-align: center;
11
+ padding: @padding-xs 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ .ant-select-dropdown-menu {
16
+ max-height: 300px;
17
+ overflow-y: auto;
18
+ .scrollbarStyle();
19
+ }
20
+ }
@@ -6,8 +6,7 @@ const ModalProps = {
6
6
  mode: PropTypes.string.def('small'),
7
7
  title: PropTypes.string.def('标题'),
8
8
  visible: PropTypes.bool.def(false),
9
- cancelHandle: PropTypes.func,
10
- attrs: PropTypes.object.def({})
9
+ cancelHandle: PropTypes.func
11
10
  }
12
11
 
13
12
  const ModalComp = {
@@ -20,6 +20,7 @@
20
20
  &__max {
21
21
  .ant-modal {
22
22
  top: 0;
23
+ padding-bottom: 0;
23
24
  .layout();
24
25
  }
25
26
  }
@@ -1,14 +1,20 @@
1
- /* 布局( 纯为了不想多写一行 */
2
- .layout( @w: 100%, @h: 100% ) {
1
+ // 布局
2
+ .layout(@w: 100%, @h: 100%) {
3
3
  width: @w;
4
4
  height: @h;
5
5
  }
6
6
 
7
- /* 正方体盒子 */
7
+ // 正方体盒子
8
8
  .square(@size) {
9
9
  .layout(@size; @size);
10
10
  }
11
11
 
12
+ // 页面间距混入
13
+ .specing(@padding: @padding-lg) {
14
+ padding: @padding;
15
+ box-sizing: border-box;
16
+ }
17
+
12
18
  // 提示文字样式
13
19
  .placeholder(@color: @input-placeholder-color) {
14
20
  // Firefox
@@ -20,7 +26,7 @@
20
26
  &:-ms-input-placeholder {
21
27
  color: @color;
22
28
  }
23
- // Safari and Chrome
29
+ // Safari and Chrome
24
30
  &::-webkit-input-placeholder {
25
31
  color: @color;
26
32
  }
@@ -30,8 +36,8 @@
30
36
  }
31
37
  }
32
38
 
33
- /* 滚动条样式 */
34
- .scrollbarStyle( @w: 6px, @h: 6px, @color1: @primary-color, @color2: transparent ) {
39
+ // 滚动条样式
40
+ .scrollbarStyle(@w: 6px, @h: 6px, @color1: @primary-color, @color2: transparent) {
35
41
  &::-webkit-scrollbar-track {
36
42
  border-radius: 0;
37
43
  background-color: transparent;
@@ -52,8 +58,8 @@
52
58
  }
53
59
  }
54
60
 
55
- /* 文字换行省略(默认1行)*/
56
- .ellipsis( @row: 1 ) {
61
+ // 文字换行省略(默认1行)
62
+ .ellipsis(@row: 1) {
57
63
  text-overflow: -o-ellipsis-lastline;
58
64
  overflow: hidden;
59
65
  text-overflow: ellipsis;
@@ -63,14 +69,14 @@
63
69
  -webkit-box-orient: vertical;
64
70
  }
65
71
 
66
- /* flex布局 */
67
- .flex-layout( @flexDirection: row, @flexGap: 0 ) {
72
+ // flex布局
73
+ .flex-layout(@flexDirection: row, @flexGap: 0) {
68
74
  display: flex;
69
75
  gap: @flexGap;
70
76
  flex-direction: @flexDirection;
71
77
  }
72
78
 
73
- /* flex排版方式 */
79
+ // flex排版
74
80
  .flex-mixins(@direction: row, @align: center, @justify: center, @gap: 0 ) {
75
81
  .flex-layout(@direction, @gap);
76
82
  flex-wrap: wrap;
@@ -0,0 +1,144 @@
1
+ @import '~ant-design-vue/lib/style/themes/default.less';
2
+ @import './mixins.less';
3
+
4
+ html,
5
+ body,
6
+ #app, #root {
7
+ height: 100%;
8
+ }
9
+
10
+ svg,
11
+ canvas {
12
+ display: block;
13
+ }
14
+
15
+ body {
16
+ text-rendering: optimizeLegibility;
17
+ -webkit-font-smoothing: antialiased;
18
+ -moz-osx-font-smoothing: grayscale;
19
+ }
20
+
21
+ ul,
22
+ ol {
23
+ list-style: none;
24
+ }
25
+
26
+ // 页面内边距
27
+ .PageSpecing {
28
+ .specing()
29
+ }
30
+
31
+ // 表格——Title
32
+ .Table__Name {
33
+ flex: 1;
34
+ color: @white;
35
+ font-size: @font-size-lg;
36
+ padding-left: @padding-sm;
37
+ box-sizing: border-box;
38
+ position: relative;
39
+
40
+ &::after {
41
+ left: 0;
42
+ top: calc((100% - 12px) / 2);
43
+ content: '';
44
+ position: absolute;
45
+ border-radius: @border-radius-sm;
46
+ background: @primary-color;
47
+ .layout(4px, 12px);
48
+ }
49
+ }
50
+
51
+ // 页面——分割式
52
+ .Page {
53
+ .layout();
54
+ .flex-layout(column, @padding-md 0);
55
+
56
+ // 页面——搜索
57
+ &__Search {
58
+ background: @component-background;
59
+ .specing();
60
+ }
61
+
62
+ // 页面——列表
63
+ &__Table {
64
+ flex: 1;
65
+ min-height: 0;
66
+ background: @component-background;
67
+ .specing(0 @padding-lg @padding-md);
68
+ }
69
+ }
70
+
71
+ // 页面——合并式
72
+ .PageOverall {
73
+ background: @component-background;
74
+ .layout();
75
+ .flex-layout(column);
76
+
77
+ // 页面——搜索
78
+ &__Search {
79
+ .specing();
80
+ }
81
+
82
+ // 页面——列表
83
+ &__Table {
84
+ flex: 1;
85
+ min-height: 0;
86
+ .specing(0 @padding-lg @padding-md);
87
+ }
88
+ }
89
+
90
+ // 页面——详情式
91
+ .PageDeatils {
92
+ overflow-y: auto;
93
+ background: @component-background;
94
+ .layout();
95
+ .scrollbarStyle();
96
+ }
97
+
98
+ // 表单——Model
99
+ .Model {
100
+ background: @component-background;
101
+ .layout();
102
+ .flex-layout(column);
103
+
104
+ // 禁用
105
+ &__Disabled {
106
+ cursor: not-allowed;
107
+ pointer-events: none;
108
+
109
+ .ant-form-item {
110
+ opacity: 0.6;
111
+ }
112
+ }
113
+
114
+ // 弹窗主体
115
+ &__BodyModal,
116
+ // 页面主体
117
+ &__BodyPage {
118
+ flex: 1;
119
+ min-height: 0;
120
+ overflow-y: auto;
121
+ .scrollbarStyle();
122
+ }
123
+
124
+ &__BodyPage {
125
+ padding: @padding-lg;
126
+ padding-right: 0;
127
+ box-sizing: border-box;
128
+ }
129
+
130
+ // 弹窗操作
131
+ &__ActionsModal,
132
+ // 页面操作
133
+ &__ActionsPage {
134
+ justify-content: flex-end;
135
+ padding-top: @padding-md;
136
+ box-sizing: border-box;
137
+ border-top: 1px solid @border-color-split;
138
+ .flex-layout(@flexGap: 0 @padding-xs);
139
+ }
140
+
141
+ &__ActionsPage {
142
+ .specing(@padding-md);
143
+ }
144
+ }
@@ -67,6 +67,8 @@ export default {
67
67
  size: 10,
68
68
  current: 1
69
69
  },
70
+ // 尺寸观察器
71
+ resizeObserver: null,
70
72
  tableScrollBody: 600,
71
73
  /** 跨分页记录选中数据 */
72
74
  selectedRecords: []
@@ -156,6 +158,8 @@ export default {
156
158
  },
157
159
  mounted() {
158
160
  this.pagination = { ...this.paginationParams }
161
+ // 监听父级高度
162
+ this.initResizeObserver()
159
163
  const scrollTimer = setTimeout(() => {
160
164
  this.getScrollBodyH()
161
165
  clearTimeout(scrollTimer)
@@ -164,21 +168,36 @@ export default {
164
168
  this.removeDomElement()
165
169
  },
166
170
  destroyed() {
167
- // window.removeEventListener( 'resize' )
171
+ if (this.resizeObserver) this.resizeObserver.disconnect()
172
+ window.removeEventListener('resize', this.getScrollBodyH)
168
173
  },
169
174
  methods: {
175
+ // 初始化尺寸观察器
176
+ initResizeObserver() {
177
+ if (typeof ResizeObserver !== 'undefined') {
178
+ this.resizeObserver = new ResizeObserver(() => {
179
+ this.getScrollBodyH()
180
+ })
181
+ this.resizeObserver.observe(this.$refs.Table)
182
+ }
183
+ },
170
184
  /** 根据内容高度计算滚动长度 */
171
185
  getScrollBodyH() {
186
+ if (!this.$refs.Table || !this.$refs.TableHead || !this.$refs.TablePagination) return
187
+
188
+ const tableTitleHeight = this.$refs.TableListTitle ? this.$refs.TableListTitle.offsetHeight : 0
189
+ const tableHeader = document.querySelector('.ant-table-thead')
190
+ const headerHeight = tableHeader ? tableHeader.offsetHeight : 0
191
+
172
192
  this.tableScrollBody =
173
193
  this.$refs.Table.clientHeight -
174
194
  this.$refs.TableHead.offsetHeight -
175
195
  this.$refs.TablePagination.offsetHeight -
176
- 16 -
177
- this.$refs.TableListTitle.offsetHeight -
178
- document.querySelector('.ant-table-thead').offsetHeight -
179
- 20
196
+ 16 - // 基础间距
197
+ tableTitleHeight -
198
+ headerHeight -
199
+ 20 // 浮动空间缓冲
180
200
  /** 20为获取高度时抹掉的小数点后两位的浮动空间(存在叠加多个获取错误的情况) */
181
- console.log(this.$refs.Table.clientHeight, this.tableScrollBody, 'tableScrollBody滚动高度')
182
201
  },
183
202
  /** 根据高度判断dom元素是否加载 进行删除 */
184
203
  removeDomElement() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-component-gallery",
3
- "version": "2.0.9",
3
+ "version": "2.0.11",
4
4
  "description": "基础vue、antdvue、less实现的私有组件库",
5
5
  "main": "dist/index.umd.js",
6
6
  "files": [