web-component-gallery 2.2.1 → 2.2.2

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.
@@ -15,45 +15,37 @@ function extractColor(content, varName) {
15
15
 
16
16
  /**
17
17
  * 向LESS文件添加新变量并保持原有格式
18
- * @param {string} lessContent - 原始LESS文件内容
19
18
  * @param {Object} newVars - 要添加的新变量对象
19
+ * @param {string} lessContent - 原始LESS文件内容
20
20
  * @param {Object} newVars.export - 需要导出的变量对象
21
21
  * @param {Object} newVars.nonExport - 不需要导出的变量对象
22
22
  * @returns {string} 修改后的LESS文件内容
23
23
  */
24
- function addLessVariables(lessContent, newVars) {
25
- const lines = lessContent.split('\n')
26
- let exportStartIndex = -1
27
-
28
- for (let i = 0; i < lines.length; i++) {
29
- if (lines[i].trim() === ':export {') exportStartIndex = i
30
- }
31
-
32
- // 如果没有找到:export块,则添加到文件末尾
33
- if (exportStartIndex === -1) exportStartIndex = lines.length
34
-
35
- // 添加非导出变量
36
- const addNonExportVars = () => {
37
- const newVarLines = ['\n// 扩展变量']
38
- for (const [lessVar, cssVar] of Object.entries(newVars.nonExport || {})) {
39
- newVarLines.push(`${lessVar}: ${cssVar};`)
24
+ function addLessVariables(newVars, lessContent) {
25
+ const lines = lessContent?.split('\n') || []
26
+ const newLines = []
27
+
28
+ // 智能添加非导出变量(仅在newVars.nonExport存在时添加)
29
+ if (newVars.nonExport && Object.keys(newVars.nonExport).length > 0) {
30
+ newLines.push('\n// 扩展变量')
31
+ for (const [lessVar, cssVar] of Object.entries(newVars.nonExport)) {
32
+ newLines.push(`${lessVar}: ${cssVar};`)
40
33
  }
41
- return newVarLines
42
34
  }
43
35
 
44
- // 添加导出变量
45
- const addExportVars = () => {
46
- const newVarLines = ['\r:root {']
47
- for (const [cssVar, lessVar] of Object.entries(newVars.export || {})) {
48
- newVarLines.push(` ${cssVar}: ${lessVar};`)
36
+ // 智能添加导出变量(仅在newVars.export存在时添加)
37
+ if (newVars.export && Object.keys(newVars.export).length > 0) {
38
+ newLines.push('\n:root {')
39
+ for (const [cssVar, lessVar] of Object.entries(newVars.export)) {
40
+ newLines.push(` ${cssVar}: ${lessVar};`)
49
41
  }
50
- return [...newVarLines, '}']
42
+ newLines.push('}')
51
43
  }
52
44
 
53
- // 在适当位置插入新变量
54
- lines.splice(exportStartIndex - 1, 0, ...addNonExportVars())
55
- // 添加ROOT变量
56
- lines.splice(lines.length, 0, ...addExportVars())
45
+ // 在文件末尾插入新变量
46
+ if (newLines.length > 0) {
47
+ lines.splice(lines.length, 0, ...newLines)
48
+ }
57
49
 
58
50
  return lines.join('\n')
59
51
  }
@@ -86,29 +78,31 @@ async function processLessFiles(theme) {
86
78
  const result = parseExtendVariables(cssVariables, fileAVariables)
87
79
 
88
80
  processedContent = addLessVariables(
89
- processedContent,
90
81
  {
91
- export: {
92
- ...parseCSSVariables(cssVariables),
93
- ...result.export
94
- },
95
- nonExport: result.nonExport
96
- }
82
+ nonExport: result.nonExport
83
+ },
84
+ processedContent
97
85
  )
98
86
 
99
87
  const basePath = path.resolve(hostPath, '../src/styles')
100
88
 
101
- // 写入输出文件
102
- const outputPath = path.join(basePath, `${cssFileName}.less`)
103
- fs.writeFileSync(outputPath, processedContent)
89
+ // 确保目录存在
90
+ fs.mkdirSync(basePath, { recursive: true })
104
91
 
105
- // 转换为 "xxx:111;" 格式
106
- // const formattedString = Object.entries(fileAVariables)
107
- // .map(([key, value]) => `${key}: ${value};`)
108
- // .join('\n') // 每行一个键值对
92
+ // 写入LESS文件
93
+ const lessOutPut = path.join(basePath, `${cssFileName}.less`)
94
+ fs.writeFileSync(lessOutPut, processedContent)
109
95
 
110
- // const themeOutputPath = path.join(basePath, `${theme}.less`)
111
- // fs.writeFileSync(themeOutputPath, formattedString)
96
+ // 生成并写入CSS变量文件
97
+ const cssOutPut = path.join(basePath, 'root.css')
98
+ fs.writeFileSync(cssOutPut, addLessVariables(
99
+ {
100
+ export: {
101
+ ...parseCSSVariables(cssVariables),
102
+ ...result.export
103
+ }
104
+ }
105
+ ))
112
106
 
113
107
  return fileAVariables
114
108
  } catch (error) {
@@ -184,7 +178,7 @@ const customFunctions = {
184
178
  return new less.tree.Quoted('"', `fade(${defaultValue}, ${amount.value}%)`)
185
179
  }
186
180
  // 处理静态颜色值
187
- const alpha = Math.max(0, Math.min(1, color.alpha * (1 - amount.value / 100)))
181
+ const alpha = Math.max(0, Math.min(1, color.alpha * (amount.value / 100)))
188
182
  return new less.tree.Color(color.rgb, alpha)
189
183
  })
190
184
  }
@@ -100,26 +100,46 @@ function parseExtendVariables(extendVar, lessVariables) {
100
100
  async function updateTheme(theme) {
101
101
  if (!less || !less.modifyVars) return
102
102
 
103
- const root = document.documentElement
104
- // 获取主题变量最新数据
105
- const themeVariables = await fetchAndParseLessFile(theme, true)
106
- const cssVariables = await fetchAndParseLessFile(cssFileName, true)
103
+ const root = document.documentElement
104
+ // 并行获取主题变量和CSS变量
105
+ const [themeVariables, cssVariables] = await Promise.all([
106
+ fetchAndParseLessFile(theme, true),
107
+ fetchAndParseLessFile(cssFileName, true)
108
+ ])
109
+ // 解析CSS变量
107
110
  const resetValue = parseCSSVariables(cssVariables, themeVariables)
108
111
 
109
- const updateVariables = {}
112
+ const updateVariables = {
113
+ css: {},
114
+ less: {}
115
+ }
110
116
 
111
- for (const [key, colorA] of Object.entries(resetValue)) {
112
- // 对比变量的旧数据,判断是否改动
117
+ // 对比新旧变量值,收集需要更新的变量
118
+ for (const [key, colorA] of Object.entries(resetValue)) {
113
119
  const colorB = getComputedStyle(root).getPropertyValue(key).trim()
114
- // 存储改动数据
115
- if (colorA !== colorB) updateVariables[`@${key.slice(2)}`] = colorA
120
+ // 只有当颜色值确实发生变化时才更新
121
+ if (colorA !== colorB) {
122
+ updateVariables.css[key] = colorA
123
+ // 将CSS变量名转换为LESS变量名
124
+ updateVariables.less[`@${key.slice(2)}`] = colorA
125
+ }
116
126
  }
117
127
 
118
- less.modifyVars({...themeVariables, ...updateVariables}).then(console.log("换肤成功"))
128
+ await less.modifyVars({
129
+ ...themeVariables,
130
+ ...updateVariables.less
131
+ }).then(console.log("换肤成功"))
119
132
 
120
- if (!Object.keys(updateVariables).length) return
133
+ if (!Object.keys(updateVariables.css).length) return
121
134
 
122
- const updateCSSViables = parseExtendVariables(cssVariables, updateVariables).export
135
+ // 解析并更新CSS变量
136
+ const updateCSSViables = {
137
+ ...updateVariables.css,
138
+ ...parseExtendVariables(
139
+ cssVariables,
140
+ updateVariables.less
141
+ ).export
142
+ }
123
143
 
124
144
  for (const [key, color] of Object.entries(updateCSSViables)) {
125
145
  root.style.setProperty(key, color)
@@ -1,4 +1,3 @@
1
1
  iframe {
2
- width: 100%;
3
- height: 100%;
2
+ .layout();
4
3
  }
@@ -0,0 +1,283 @@
1
+ <template>
2
+ <div
3
+ ref="sliderContainer"
4
+ class="slider-wrapper"
5
+ :style="sliderWrapperStyle"
6
+ @mousemove="handleDragMove"
7
+ @mouseup="handleDragEnd"
8
+ @mouseleave="handleDragEnd"
9
+ @touchmove="handleDragMove"
10
+ @touchend="handleDragEnd"
11
+ >
12
+ <!-- 背景轨道 -->
13
+ <div class="slider-track">
14
+ <!-- 进度条 -->
15
+ <div
16
+ class="slider-progress"
17
+ :class="{ 'progress-resetting': isResetting }"
18
+ ref="progressBar"
19
+ :style="progressBarStyle"
20
+ />
21
+
22
+ <!-- 提示文字 -->
23
+ <div
24
+ class="slider-text"
25
+ ref="sliderText"
26
+ :style="sliderTextStyle"
27
+ >
28
+ <span v-if="!isVerified">{{ dragText }}</span>
29
+ <span v-else class="success-text">{{ successText }}</span>
30
+ </div>
31
+ </div>
32
+
33
+ <!-- 滑块手柄 -->
34
+ <div
35
+ class="slider-handle"
36
+ :class="{
37
+ 'handle-resetting': isResetting,
38
+ 'handle-success': isVerified
39
+ }"
40
+ @mousedown="handleDragStart"
41
+ @touchstart="handleDragStart"
42
+ ref="sliderHandle"
43
+ :style="sliderHandleStyle"
44
+ >
45
+ <IconFont
46
+ :type="currentIconType"
47
+ :style="{ fontSize: '16px', color: iconColor }"
48
+ />
49
+ </div>
50
+ </div>
51
+ </template>
52
+
53
+ <script>
54
+
55
+ import debounce from 'lodash/debounce'
56
+ import IconFont from '../icon-font'
57
+
58
+ export default {
59
+ name: 'SliderVerify',
60
+ components: { IconFont },
61
+ props: {
62
+ successText: {
63
+ type: String,
64
+ default: '验证成功'
65
+ },
66
+ dragText: {
67
+ type: String,
68
+ default: '请按住滑块拖动到最右侧'
69
+ },
70
+ sliderHeight: {
71
+ type: Number,
72
+ default: 40
73
+ },
74
+ handleSize: {
75
+ type: Number,
76
+ default: 40
77
+ },
78
+ verifyThreshold: {
79
+ type: Number,
80
+ default: 85,
81
+ validator: (value) => value >= 0 && value <= 100
82
+ }
83
+ },
84
+
85
+ data() {
86
+ return {
87
+ // 拖拽状态
88
+ isDragging: false,
89
+ // 起始位置
90
+ startX: 0,
91
+ // 当前位置
92
+ currentX: 0,
93
+ // 是否通过
94
+ isVerified: false,
95
+ // 是否重置
96
+ isResetting: false,
97
+ // 容器宽度
98
+ containerWidth: 0,
99
+ // 拖拽开始时间
100
+ dragStartTime: null
101
+ }
102
+ },
103
+
104
+ computed: {
105
+ sliderWrapperStyle() {
106
+ return {
107
+ height: `${this.sliderHeight}px`
108
+ }
109
+ },
110
+
111
+ progressBarStyle() {
112
+ return {
113
+ width: `${this.progressWidth}px`,
114
+ background: this.isVerified ? '#52c41a' : '#1890ff',
115
+ transition: this.isResetting ? 'width 0.5s ease' : 'none'
116
+ }
117
+ },
118
+
119
+ sliderHandleStyle() {
120
+ return {
121
+ left: `${this.handlePosition}px`,
122
+ width: `${this.handleSize}px`,
123
+ height: `${this.handleSize}px`,
124
+ transition: this.isResetting ? 'left 0.5s ease' : 'none'
125
+ }
126
+ },
127
+
128
+ sliderTextStyle() {
129
+ return {
130
+ lineHeight: `${this.sliderHeight}px`,
131
+ color: this.isVerified ? '#fff' : 'transparent'
132
+ }
133
+ },
134
+
135
+ currentIconType() {
136
+ return this.isVerified ? 'passSlider' : 'arrowSlider'
137
+ },
138
+
139
+ iconColor() {
140
+ return this.isVerified ? '#01DC66' : '#666'
141
+ },
142
+
143
+ progressWidth() {
144
+ if (this.isVerified) {
145
+ return this.containerWidth
146
+ }
147
+ return this.currentX + this.handleSize / 2
148
+ },
149
+
150
+ handlePosition() {
151
+ if (this.isVerified) {
152
+ return this.containerWidth - this.handleSize
153
+ }
154
+ return this.currentX
155
+ },
156
+
157
+ maxDragDistance() {
158
+ return this.containerWidth - this.handleSize
159
+ },
160
+
161
+ thresholdDistance() {
162
+ return (this.verifyThreshold / 100) * this.maxDragDistance
163
+ }
164
+ },
165
+
166
+ watch: {
167
+ isVerified(newVal) {
168
+ this.$emit('update:isVerified', newVal)
169
+ }
170
+ },
171
+
172
+ mounted() {
173
+ this.initSlider()
174
+ this.$bus.$onWindow(this, 'resize', this.handleWindowResize)
175
+ },
176
+
177
+ methods: {
178
+ initSlider() {
179
+ this.updateContainerWidth()
180
+ this.setupCSSVariables()
181
+ },
182
+
183
+ updateContainerWidth() {
184
+ if (this.$refs.sliderContainer) {
185
+ this.containerWidth = this.$refs.sliderContainer.offsetWidth
186
+ }
187
+ },
188
+
189
+ setupCSSVariables() {
190
+ const container = this.$refs.sliderContainer
191
+ if (container) {
192
+ const halfWidth = Math.floor(this.containerWidth / 2)
193
+ container.style.setProperty('--slider-width', `${this.containerWidth}px`)
194
+ container.style.setProperty('--half-width', `${halfWidth}px`)
195
+ container.style.setProperty('--negative-half-width', `-${halfWidth}px`)
196
+ }
197
+ },
198
+
199
+ handleDragStart(event) {
200
+ if (this.isVerified) return
201
+
202
+ this.isDragging = true
203
+ this.dragStartTime = Date.now()
204
+ const handle = this.$refs.sliderHandle
205
+ const currentLeft = parseInt(handle.style.left || '0', 10)
206
+
207
+ this.startX = (event.pageX || event.touches[0].pageX) - currentLeft
208
+ },
209
+
210
+ handleDragMove(event) {
211
+ if (!this.isDragging || this.isVerified) return
212
+
213
+ const currentX = (event.pageX || event.touches[0].pageX) - this.startX
214
+
215
+ if (currentX > 0 && currentX <= this.maxDragDistance) {
216
+ this.currentX = currentX
217
+ this.$emit('dragging', {
218
+ currentX,
219
+ progress: (currentX / this.maxDragDistance) * 100
220
+ })
221
+ }
222
+ },
223
+
224
+ handleDragEnd(event) {
225
+ if (!this.isDragging || this.isVerified) return
226
+
227
+ const currentX = (event.pageX || event.changedTouches[0].pageX) - this.startX
228
+
229
+ if (currentX >= this.thresholdDistance) {
230
+ this.handleVerifySuccess()
231
+ } else {
232
+ this.handleVerifyFail()
233
+ }
234
+
235
+ this.isDragging = false
236
+ },
237
+
238
+ handleVerifySuccess() {
239
+ this.isVerified = true
240
+ this.currentX = this.maxDragDistance
241
+ const dragDuration = Date.now() - this.dragStartTime
242
+
243
+ this.$emit('verify-success', {
244
+ duration: dragDuration,
245
+ endTime: Date.now()
246
+ })
247
+ },
248
+
249
+ handleVerifyFail() {
250
+ this.isResetting = true
251
+
252
+ setTimeout(() => {
253
+ this.resetSlider()
254
+ this.isResetting = false
255
+ }, 500)
256
+ },
257
+
258
+ resetSlider() {
259
+ this.currentX = 0
260
+ this.isVerified = false
261
+
262
+ const handle = this.$refs.sliderHandle
263
+ const progressBar = this.$refs.progressBar
264
+ const text = this.$refs.sliderText
265
+
266
+ if (handle) handle.style.left = '0px'
267
+ if (progressBar) progressBar.style.width = '0px'
268
+
269
+ if (text) {
270
+ // 重新设置动画
271
+ // text.style.animation = 'slidetounlock 3s infinite'
272
+ text.style.color = 'transparent'
273
+ }
274
+ },
275
+
276
+ handleWindowResize: debounce(function() {
277
+ this.updateContainerWidth()
278
+ this.setupCSSVariables()
279
+ this.resetSlider()
280
+ }, 250)
281
+ }
282
+ }
283
+ </script>
@@ -13,6 +13,8 @@ import ARangePicker from './ARangePicker.vue'
13
13
  import ADayTimePicker from './ADayTimePicker.vue'
14
14
  /* 文件上传选择器 */
15
15
  import AUpload from './AUpload.vue'
16
+ /* 滑块 */
17
+ import ASliderVerify from './ASliderVerify.vue'
16
18
 
17
19
  const components = {
18
20
  ACascaderMultiple,
@@ -21,7 +23,8 @@ const components = {
21
23
  ATagsInput,
22
24
  ARangePicker,
23
25
  ADayTimePicker,
24
- AUpload
26
+ AUpload,
27
+ ASliderVerify
25
28
  }
26
29
 
27
30
  const install = function (Vue) {
@@ -0,0 +1,94 @@
1
+ @import '~ant-design-vue/lib/style/themes/default.less';
2
+ @import '../../style/mixins.less';
3
+
4
+ .slider-wrapper {
5
+ position: relative;
6
+ overflow: hidden;
7
+
8
+ .slider-track {
9
+ position: relative;
10
+ background: fade(@black, 10%);
11
+ border-radius: @border-radius-base;
12
+ .layout();
13
+ }
14
+
15
+ .slider-progress {
16
+ height: 100%;
17
+ position: absolute;
18
+ background: @primary-color;
19
+ border-radius: @border-radius-base 0 0 @border-radius-base;
20
+ transition: width 0.3s ease;
21
+ }
22
+
23
+ .slider-text {
24
+ top: 0;
25
+ left: 0;
26
+ position: absolute;
27
+ text-align: center;
28
+ font-size: @font-size-base;
29
+ background: -webkit-gradient(
30
+ linear,
31
+ left top,
32
+ right top,
33
+ color-stop(0, @black),
34
+ color-stop(0.4, @black),
35
+ color-stop(0.5, @white),
36
+ color-stop(0.6, @black),
37
+ color-stop(1, @black)
38
+ );
39
+ -webkit-background-clip: text;
40
+ -webkit-text-fill-color: transparent;
41
+ animation: slidetounlock 3s infinite;
42
+ .layout();
43
+ }
44
+
45
+ .success-text {
46
+ color: @white;
47
+ -webkit-text-fill-color: @white;
48
+ animation: none;
49
+ }
50
+
51
+ .slider-handle {
52
+ top: 0;
53
+ left: 0;
54
+ z-index: 2;
55
+ cursor: move;
56
+ position: absolute;
57
+ background: @white;
58
+ border: 1px solid fade(@black, 10%);
59
+ border-radius: @border-radius-base 0 0 @border-radius-base;
60
+ transition: left 0.3s ease, background 0.3s ease;
61
+ .flex-mixins();
62
+ }
63
+
64
+ .handle-success {
65
+ border-color: @success-color;
66
+ border-radius: 0 @border-radius-base @border-radius-base 0;
67
+ }
68
+
69
+ .handle-resetting {
70
+ left: 0px;
71
+ transition: left 0.5s ease;
72
+ }
73
+
74
+ .progress-resetting {
75
+ width: 0px;
76
+ transition: width 0.5s ease;
77
+ }
78
+ }
79
+
80
+ /* 触摸设备优化 */
81
+ @media (hover: none) and (pointer: coarse) {
82
+ .slider-handle {
83
+ cursor: pointer;
84
+ }
85
+ }
86
+
87
+ @keyframes slidetounlock {
88
+ 0% {
89
+ background-position: var(--negative-half-width) 0;
90
+ }
91
+ 100% {
92
+ background-position: var(--half-width) 0;
93
+ }
94
+ }
@@ -4,4 +4,5 @@
4
4
  @import './ASelectCustom.less';
5
5
  @import './ATagsInput.less';
6
6
  @import './ADayTimePicker.less';
7
- @import './AUpload.less';
7
+ @import './AUpload.less';
8
+ @import './ADragVerify.less';
@@ -25,7 +25,7 @@ const ModalComp = {
25
25
  },
26
26
  methods: {
27
27
  handleBlank() {
28
- window.open(this.$attrs.blankUrl, '_blank')
28
+ window.open(this.$attrs.externalLink, '_blank')
29
29
  },
30
30
  handleConvert() {
31
31
  const currentIndex = this.modalModes.indexOf(this.internalMode)
@@ -62,7 +62,7 @@ const ModalComp = {
62
62
  on={{ click: this.handleConvert }}
63
63
  />
64
64
  {
65
- $attrs.blankUrl &&
65
+ $attrs.externalLink &&
66
66
  <IconFont
67
67
  type="modal_blank"
68
68
  on={{ click: this.handleBlank }}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-component-gallery",
3
- "version": "2.2.1",
3
+ "version": "2.2.2",
4
4
  "description": "基础vue、antdvue、less实现的私有组件库",
5
5
  "main": "dist/index.umd.js",
6
6
  "files": [