From 738358ab766daa3fa1e165f940c0ebf54308c191 Mon Sep 17 00:00:00 2001 From: joey Date: Thu, 7 Aug 2025 17:36:04 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=BD=A2=E7=8A=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shape-card-v1/shape-card copy.less | 57 -------- .../components/shape-card-v1/shape-card.json | 7 - .../components/shape-card-v1/shape-card.less | 58 --------- .../components/shape-card-v1/shape-card.ts | 36 ------ .../components/shape-card-v1/shape-card.wxml | 19 --- .../shape-card/shape-card copy.less | 57 -------- .../components/shape-card/shape-card.less | 4 +- .../components/shape-card/shape-card.ts | 33 +++-- .../components/shape-card/shape-card.wxml | 8 +- .../components/shape-picker/shape-picker.json | 8 ++ .../components/shape-picker/shape-picker.less | 101 +++++++++++++++ .../components/shape-picker/shape-picker.ts | 122 ++++++++++++++++++ .../components/shape-picker/shape-picker.wxml | 46 +++++++ miniprogram/constants/shapes.ts | 64 ++++++--- miniprogram/pages/shape/index.json | 3 +- miniprogram/pages/shape/index.ts | 92 ++++++++++++- miniprogram/pages/shape/index.wxml | 8 +- 17 files changed, 455 insertions(+), 268 deletions(-) delete mode 100644 miniprogram/components/shape-card-v1/shape-card copy.less delete mode 100644 miniprogram/components/shape-card-v1/shape-card.json delete mode 100644 miniprogram/components/shape-card-v1/shape-card.less delete mode 100644 miniprogram/components/shape-card-v1/shape-card.ts delete mode 100644 miniprogram/components/shape-card-v1/shape-card.wxml delete mode 100644 miniprogram/components/shape-card/shape-card copy.less create mode 100644 miniprogram/components/shape-picker/shape-picker.json create mode 100644 miniprogram/components/shape-picker/shape-picker.less create mode 100644 miniprogram/components/shape-picker/shape-picker.ts create mode 100644 miniprogram/components/shape-picker/shape-picker.wxml diff --git a/miniprogram/components/shape-card-v1/shape-card copy.less b/miniprogram/components/shape-card-v1/shape-card copy.less deleted file mode 100644 index 9f139cd..0000000 --- a/miniprogram/components/shape-card-v1/shape-card copy.less +++ /dev/null @@ -1,57 +0,0 @@ -.shape-card { - position: relative; - width: 300rpx; - height: 300rpx; - border-radius: 16rpx; - // background: #f5f5f5; - background: #ffffff; - border: 1rpx solid rgba(242, 242, 242, 1); - box-shadow: 0rpx 16rpx 24rpx rgba(184, 210, 188, 0.2); - - .shape-card-img-wrapper { - padding: 16rpx; - width: 100%; - height: 200rpx; - display: flex; - justify-content: center; - align-items: center; - box-sizing: border-box; - - .shape-card-img { - width: 180rpx; - height: 180rpx; - } - } - - .shape-card-footer { - margin-inline: 32rpx; - padding-block: 16rpx; - display: flex; - justify-content: start; - align-items: center; - border-top: 1rpx solid #CCCCCC; - - .shape-card-footer-color { - width: 60rpx; - height: 60rpx; - border-radius: 50%; - border: 2rpx solid #141414; - margin-right: 34rpx; - } - - .shape-card-footer-name { - font-size: 28rpx; - color: #141414; - } - } - - .delete-icon { - position: absolute; - top: -18rpx; - right: -18rpx; - - .delete-icon-inner { - color: rgba(255, 0, 0, 0.7); - } - } -} \ No newline at end of file diff --git a/miniprogram/components/shape-card-v1/shape-card.json b/miniprogram/components/shape-card-v1/shape-card.json deleted file mode 100644 index 44daf20..0000000 --- a/miniprogram/components/shape-card-v1/shape-card.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "component": true, - "styleIsolation": "apply-shared", - "usingComponents": { - "van-icon": "@vant/weapp/icon/index" - } -} diff --git a/miniprogram/components/shape-card-v1/shape-card.less b/miniprogram/components/shape-card-v1/shape-card.less deleted file mode 100644 index 70fd139..0000000 --- a/miniprogram/components/shape-card-v1/shape-card.less +++ /dev/null @@ -1,58 +0,0 @@ -.shape-card { - position: relative; - width: 240rpx; - height: 240rpx; - border-radius: 16rpx; - // background: #f5f5f5; - background: #ffffff; - border: 1rpx solid rgba(242, 242, 242, 1); - box-shadow: 0rpx 16rpx 24rpx rgba(184, 210, 188, 0.2); - - .shape-card-img-wrapper { - padding: 16rpx; - width: 100%; - height: 120rpx; - display: flex; - justify-content: center; - align-items: center; - box-sizing: border-box; - - .shape-card-img { - width: 120rpx; - height: 120rpx; - color: skyblue; - } - } - - .shape-card-footer { - margin-inline: 32rpx; - padding-block: 16rpx; - display: flex; - justify-content: start; - align-items: center; - border-top: 1rpx solid #CCCCCC; - - .shape-card-footer-color { - width: 60rpx; - height: 60rpx; - border-radius: 50%; - border: 2rpx solid #141414; - margin-right: 34rpx; - } - - .shape-card-footer-name { - font-size: 28rpx; - color: #141414; - } - } - - .delete-icon { - position: absolute; - top: -18rpx; - right: -18rpx; - - .delete-icon-inner { - color: rgba(255, 0, 0, 0.7); - } - } -} \ No newline at end of file diff --git a/miniprogram/components/shape-card-v1/shape-card.ts b/miniprogram/components/shape-card-v1/shape-card.ts deleted file mode 100644 index 1450f19..0000000 --- a/miniprogram/components/shape-card-v1/shape-card.ts +++ /dev/null @@ -1,36 +0,0 @@ -Component({ - /** - * 组件的属性列表 - */ - properties: { - key: { - type: Number, - value: 0, - }, - img: { - type: String, - value: '', - }, - name: { - type: String, - value: '', - }, - color: { - type: String, - value: '', - }, - }, - /** - * 组件的方法列表 - */ - methods: { - onDelete() { - const { key } = this.data; - this.triggerEvent('onDelete', { key }); - }, - onColorTap() { - const { key, color } = this.data; - this.triggerEvent('onColorTap', { key, color }); - }, - }, -}); diff --git a/miniprogram/components/shape-card-v1/shape-card.wxml b/miniprogram/components/shape-card-v1/shape-card.wxml deleted file mode 100644 index f3e8cd7..0000000 --- a/miniprogram/components/shape-card-v1/shape-card.wxml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - {{ name }} - - - - - diff --git a/miniprogram/components/shape-card/shape-card copy.less b/miniprogram/components/shape-card/shape-card copy.less deleted file mode 100644 index 9f139cd..0000000 --- a/miniprogram/components/shape-card/shape-card copy.less +++ /dev/null @@ -1,57 +0,0 @@ -.shape-card { - position: relative; - width: 300rpx; - height: 300rpx; - border-radius: 16rpx; - // background: #f5f5f5; - background: #ffffff; - border: 1rpx solid rgba(242, 242, 242, 1); - box-shadow: 0rpx 16rpx 24rpx rgba(184, 210, 188, 0.2); - - .shape-card-img-wrapper { - padding: 16rpx; - width: 100%; - height: 200rpx; - display: flex; - justify-content: center; - align-items: center; - box-sizing: border-box; - - .shape-card-img { - width: 180rpx; - height: 180rpx; - } - } - - .shape-card-footer { - margin-inline: 32rpx; - padding-block: 16rpx; - display: flex; - justify-content: start; - align-items: center; - border-top: 1rpx solid #CCCCCC; - - .shape-card-footer-color { - width: 60rpx; - height: 60rpx; - border-radius: 50%; - border: 2rpx solid #141414; - margin-right: 34rpx; - } - - .shape-card-footer-name { - font-size: 28rpx; - color: #141414; - } - } - - .delete-icon { - position: absolute; - top: -18rpx; - right: -18rpx; - - .delete-icon-inner { - color: rgba(255, 0, 0, 0.7); - } - } -} \ No newline at end of file diff --git a/miniprogram/components/shape-card/shape-card.less b/miniprogram/components/shape-card/shape-card.less index bb7b411..7d26112 100644 --- a/miniprogram/components/shape-card/shape-card.less +++ b/miniprogram/components/shape-card/shape-card.less @@ -27,8 +27,8 @@ } .shape-color { - width: 72rpx; - height: 72rpx; + width: 60rpx; + height: 60rpx; border-radius: 50%; border: 1rpx solid #141414; background-color: skyblue; diff --git a/miniprogram/components/shape-card/shape-card.ts b/miniprogram/components/shape-card/shape-card.ts index 68050c6..ea46f99 100644 --- a/miniprogram/components/shape-card/shape-card.ts +++ b/miniprogram/components/shape-card/shape-card.ts @@ -11,7 +11,7 @@ Component({ type: String, value: '', }, - color: { + fillColor: { type: String, value: '', }, @@ -34,7 +34,7 @@ Component({ * 监听属性变化 */ observers: { - 'svg, color': function () { + 'svg, fillColor': function () { this.generateSvgDataUri(); } }, @@ -46,12 +46,24 @@ Component({ * 生成完整的 SVG Data URI */ generateSvgDataUri() { - const { svg, color } = this.data; + const { svg, fillColor } = this.data; + if (fillColor === 'transparent') return; if (!svg) return; - // 创建完整的 SVG 字符串,替换填充颜色 - const coloredSvg = svg.replace(/fill="#ff0000"/g, `fill="${color || '#ff0000'}"`); - const completeSvg = `${coloredSvg}`; + // 处理 fill 属性:无论原来有没有 fill,都要赋值为 fillColor + let processedSvg = svg; + + // 1. 如果有 fill 属性,全部替换为 fillColor + if (/fill="[^"]*"/.test(processedSvg)) { + processedSvg = processedSvg.replace(/fill="[^"]*"/g, `fill="${fillColor || '#ff0000'}"`); + } else { + // 2. 没有 fill 属性,则在第一个标签内加上 fill + processedSvg = processedSvg.replace( + /(<(?:circle|rect|polygon|ellipse|path|line)[^>]*?)(\/?>)/, + `$1 fill="${fillColor || '#ff0000'}"$2` + ); + } + const completeSvg = `${processedSvg}`; // 转换为 Data URI const svgDataUri = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(completeSvg)}`; @@ -65,8 +77,13 @@ Component({ this.triggerEvent('onDelete', { key }); }, onColorTap() { - const { key, color } = this.data; - this.triggerEvent('onColorTap', { key, color }); + const { key, fillColor } = this.data; + this.triggerEvent('onColorTap', { key, fillColor }); + }, + + onShapeTap() { + const { key } = this.data; + this.triggerEvent('onShapeTap', { key }); }, }, }); diff --git a/miniprogram/components/shape-card/shape-card.wxml b/miniprogram/components/shape-card/shape-card.wxml index a1aee73..dd9873c 100644 --- a/miniprogram/components/shape-card/shape-card.wxml +++ b/miniprogram/components/shape-card/shape-card.wxml @@ -1,9 +1,13 @@ - + { + const completeSvg = `${shape.svg}`; + const svgDataUri = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(completeSvg)}`; + return { + ...shape, + svgDataUrl: svgDataUri + }; + }); + console.log('shapesWithDataUrl---:', shapesWithDataUrl); + this.setData({ + shapes: shapesWithDataUrl + }); + }, + }, + /** + * 组件的方法列表 + */ + methods: { + onClose() { + this.triggerEvent('onClose'); + }, + + onSelectShape(e: WechatMiniprogram.TouchEvent) { + const { index } = e.currentTarget.dataset; + const { currentShapeKey, shapes } = this.data; + + if (currentShapeKey) { + + // 单选模式:currentShapeKey 不为空时,只能选中一个形状 + this.setData({ + shapes: shapes.map((item, i) => ({ + ...item, + checked: i === index + })) + }); + } else { + // 多选模式:currentShapeKey 为空时,支持多选,最多可选6个 + let shapesCopy = this.data.shapes.map(item => ({ ...item })); + const targetIndex = index; + + // 统计当前已选中的数量 + const checkedCount = shapesCopy.filter(item => item.checked).length; + + if (shapesCopy[targetIndex].checked) { + // 如果已选中,则取消选中 + shapesCopy[targetIndex].checked = false; + } else { + if (checkedCount >= 6) { + wx.showToast({ + title: '最多可选择6个形状', + icon: 'none', + duration: 1500 + }); + return; + } + // 如果未选中,则添加到选中列表 + shapesCopy[targetIndex].checked = true; + } + + this.setData({ + shapes: shapesCopy, + }); + } + }, + + onChange() { + const { selectedShape, selectedShapes, currentShapeKey } = this.data; + + if (currentShapeKey) { + // 单选模式:返回单个选中的形状 + this.triggerEvent('onChange', { shape: selectedShape, currentShapeKey }); + } else { + // 多选模式:返回选中的形状数组 + this.triggerEvent('onChange', { shapes: selectedShapes }); + } + }, + + // 获取选中状态的 CSS 类名 + getShapeClass(shapeId: string): string { + const { currentShapeKey, selectedShapes } = this.data; + + if (currentShapeKey) { + // 单选模式:检查是否等于当前选中的形状 + return shapeId === currentShapeKey ? 'selected single-mode' : ''; + } else { + // 多选模式:检查是否在选中列表中 + return selectedShapes.includes(shapeId) ? 'selected multi-mode' : ''; + } + }, + }, +}); diff --git a/miniprogram/components/shape-picker/shape-picker.wxml b/miniprogram/components/shape-picker/shape-picker.wxml new file mode 100644 index 0000000..a84ea49 --- /dev/null +++ b/miniprogram/components/shape-picker/shape-picker.wxml @@ -0,0 +1,46 @@ + + + 选择形状{{ currentShapeKey ? '' : '(多选)' }} + + + + + + + + + + + {{item.name}} + + + + + + 确认选择{{ !currentShapeKey && selectedShapes.length > 0 ? '(' + + selectedShapes.length + '个)' : '' }} + + + + diff --git a/miniprogram/constants/shapes.ts b/miniprogram/constants/shapes.ts index 554958f..a8d1c4a 100644 --- a/miniprogram/constants/shapes.ts +++ b/miniprogram/constants/shapes.ts @@ -6,7 +6,18 @@ * */ -const SHAPES = [ +interface ShapeCard { + id: string; + name: string; + type: string; + svg: string; + svgDataUrl?: string; + fillColor: string; + drawFunction?: string; + checked?: boolean; +} + +const SHAPES: ShapeCard[] = [ // 三角形 { id: 'obtuse-triangle', @@ -14,13 +25,15 @@ const SHAPES = [ type: 'triangle', // svg: '', svg: '', + fillColor: '#ff0000', drawFunction: 'drawTriangle', }, { id: 'right-triangle', name: '直角三角形', type: 'triangle', - svg: '', + svg: '', + fillColor: '#ff0000', drawFunction: 'drawTriangle', }, { @@ -28,7 +41,8 @@ const SHAPES = [ name: '等腰三角形', type: 'triangle', // svg: '', - svg: '', + svg: '', + fillColor: '#ff0000', drawFunction: 'drawTriangle', }, // 四边形 @@ -37,7 +51,8 @@ const SHAPES = [ name: '正方形', type: 'rect', // svg: '', - svg: '', + svg: '', + fillColor: '#ff0000', drawFunction: 'drawSquare', }, { @@ -45,7 +60,8 @@ const SHAPES = [ name: '长方形', type: 'rect', // svg: '', - svg: '', + svg: '', + fillColor: '#ff0000', drawFunction: 'drawRectangle', }, { @@ -53,7 +69,8 @@ const SHAPES = [ name: '平行四边形', type: 'rect', // svg: '', - svg: '', + svg: '', + fillColor: '#ff0000', drawFunction: 'drawParallelogram', }, { @@ -61,7 +78,8 @@ const SHAPES = [ name: '菱形', type: 'rect', // svg: '', - svg: '', + svg: '', + fillColor: '#ff0000', drawFunction: 'drawParallelogram', }, { @@ -69,7 +87,8 @@ const SHAPES = [ name: "梯形", type: 'rect', // svg: '', - svg: '', + svg: '', + fillColor: '#ff0000', drawFunction: 'drawTrapezoid', }, // 五边形 @@ -78,7 +97,8 @@ const SHAPES = [ name: '五边形', type: 'rect', // svg: '', - svg: '', + svg: '', + fillColor: '#ff0000', drawFunction: 'drawHexagon', }, @@ -87,7 +107,8 @@ const SHAPES = [ id: 'hexagon', name: '六边形', type: 'rect', - svg: '', + svg: '', + fillColor: '#ff0000', drawFunction: 'drawHexagon', }, // 五角星 @@ -95,7 +116,8 @@ const SHAPES = [ id: 'pentagram', name: '五角星', type: 'rect', - svg: '', + svg: '', + fillColor: '#ff0000', drawFunction: 'drawPentagram', }, // 心形 @@ -103,7 +125,8 @@ const SHAPES = [ id: 'heart', name: '心形', type: 'rect', - svg: '', + svg: '', + fillColor: '#ff0000', drawFunction: 'drawHeart', }, // 圆形 @@ -111,7 +134,8 @@ const SHAPES = [ id: 'circle', name: '圆形', type: 'circle', - svg: '', + svg: '', + fillColor: '#ff0000', drawFunction: 'drawCircle', }, // 椭圆 @@ -119,7 +143,8 @@ const SHAPES = [ id: 'ellipse', name: '椭圆', type: 'circle', - svg: '', + svg: '', + fillColor: '#ff0000', drawFunction: 'drawEllipse', }, // 半圆 @@ -128,7 +153,8 @@ const SHAPES = [ name: '半圆', type: 'circle', // svg: '', - svg: '', + svg: '', + fillColor: '#ff0000', drawFunction: 'drawSemicircle', }, // 扇形 @@ -137,7 +163,8 @@ const SHAPES = [ name: '扇形', type: 'circle', // svg: '', - svg: '', + svg: '', + fillColor: '#ff0000', drawFunction: 'drawSector', }, // 圆环 @@ -146,9 +173,10 @@ const SHAPES = [ name: '圆环', type: 'circle', // svg: '', - svg: '', + svg: '', + fillColor: '#ff0000', drawFunction: 'drawRing', }, ]; -export { SHAPES }; \ No newline at end of file +export { SHAPES, ShapeCard }; \ No newline at end of file diff --git a/miniprogram/pages/shape/index.json b/miniprogram/pages/shape/index.json index b19c868..685d1ea 100644 --- a/miniprogram/pages/shape/index.json +++ b/miniprogram/pages/shape/index.json @@ -9,6 +9,7 @@ "van-icon": "@vant/weapp/icon/index", "van-popup": "@vant/weapp/popup/index", "toy-button": "../../ui/button/button", - "shape-card": "../../components/shape-card/shape-card" + "shape-card": "../../components/shape-card/shape-card", + "shape-picker": "../../components/shape-picker/shape-picker" } } diff --git a/miniprogram/pages/shape/index.ts b/miniprogram/pages/shape/index.ts index 19eea84..487e040 100644 --- a/miniprogram/pages/shape/index.ts +++ b/miniprogram/pages/shape/index.ts @@ -1,11 +1,99 @@ -import { SHAPES } from '../../constants/shapes'; +import { SHAPES, ShapeCard } from '../../constants/shapes'; +import { WATER_COLORS, PAPER_SIZE } from '../../constants/colors'; + + Page({ data: { - shapeList: SHAPES, + shapeList: [] as ShapeCard[], + showSelectShapePopup: false, + currentShapeKey: '', }, onLoad() { }, + + openSelectShapePopup(e: any) { + const { key } = e.detail; + this.setData({ + showSelectShapePopup: true, + currentShapeKey: key + }); + }, + + closeSelectShapePopup() { + this.setData({ + showSelectShapePopup: false, + currentShapeKey: '' + }); + }, + + onChangeShape(e: any) { + const { shape, currentShapeKey, shapes } = e.detail; + console.log('shape', shape); + console.log('shapes', shapes); + if (currentShapeKey) { + this.setData({ + showSelectShapePopup: false, + currentShapeKey: '', + shapeList: this.data.shapeList.map(item => { + if (item.id === currentShapeKey) { + return { ...item, fillColor: item.fillColor }; + } + return item; + }) + }); + } else { + // 随机从 WATER_COLORS.basic12 中获取不重复的颜色 + const colorList = WATER_COLORS.basic12.map(item => item.hex); + const colorsCopy = [...colorList]; + for (let i = colorsCopy.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [colorsCopy[i], colorsCopy[j]] = [colorsCopy[j], colorsCopy[i]]; + } + const selectedColors = colorsCopy.slice(0, shapes.length); + + this.setData({ + showSelectShapePopup: false, + currentShapeKey: '', + shapeList: shapes.map((item: ShapeCard, idx: number) => ({ + ...item, + fillColor: selectedColors[idx] || item.fillColor + })) + }); + } + }, + /** + * 随机生成6个图形 + */ + refreshShapeCard() { + // 随机取6个图形 + const shapesCopy = [...SHAPES]; + for (let i = shapesCopy.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shapesCopy[i], shapesCopy[j]] = [shapesCopy[j], shapesCopy[i]]; + } + const selectedShapes = shapesCopy.slice(0, 6); + + // 随机取6个颜色 + const colorList = WATER_COLORS.basic12.map(item => item.hex); + const colorsCopy = [...colorList]; + for (let i = colorsCopy.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [colorsCopy[i], colorsCopy[j]] = [colorsCopy[j], colorsCopy[i]]; + } + const selectedColors = colorsCopy.slice(0, 6); + + // 分配颜色到图形 + const shapeList = selectedShapes.map((shape, idx) => ({ + ...shape, + fillColor: selectedColors[idx] + })); + + // 更新数据 + this.setData({ + shapeList + }); + } }); \ No newline at end of file diff --git a/miniprogram/pages/shape/index.wxml b/miniprogram/pages/shape/index.wxml index 6cd0447..711414d 100644 --- a/miniprogram/pages/shape/index.wxml +++ b/miniprogram/pages/shape/index.wxml @@ -26,9 +26,15 @@ wx:key="{{index}}" key="{{index}}" svg="{{ item.svg }}" - color="{{ item.color }}" + fillColor="{{ item.fillColor }}" + bind:onShapeTap="openSelectShapePopup" bind:onDelete="onDelete" bind:onColorTap="onColorTap" /> +