我喜欢极简风格,所以我搭建了这款微信记账本小程序。在开发微信记账本小程序过程中,有一些值得关注的技术要点,我则简而记之。
1、空态界面
在没有数据时,我设计了空状态时的占位提示。
在框架中,我使用了 wx:if="{{ isShow }}" 来判断是否需要渲染该代码块。而isShow的赋值逻辑是通过判断数组的长度,如果为0则为false,否则为true。
2、新增记账
这里我基于表单元素封装了一个业务表单组件,通过遮罩层弹出。使用到的表单项有form、input、radio、textarea、picker、button,通过自定义样式实现定制化界面。
其实,我有考虑过使用Skyline进行渲染的。但是由于还不太掌握,所以还是使用自定义组件的方式,实现弹层效果。
况且,这个模块后面还会在其他地方用到,所以为了提高可复用性才做了封装。
那么,在微信小程序中,如何封装自定义组件和引用呢?
1) 创建自定义组件:
首先,在项目根目录下创建一个文件夹 `components`,用于存放自定义组件。
在 `components` 文件夹下创建一个子文件夹`my-component`,用于存放自定义组件的相关文件。
在 `my-component` 文件夹下创建以下四个文件:
-
`my-component.wxml`:组件的模板结构。
-
`my-component.wxss`:组件的样式表。
-
`my-component.js`:组件的逻辑代码。
-
`my-component.json`:组件的配置信息。
编写组件的代码,然后在需要使用该组件的页面中进行引用。
2)引用自定义组件:
在需要使用自定义组件的页面的 `json` 文件中,添加 `usingComponents` 字段,用于声明要使用的自定义组件。
// json
{
"usingComponents": {
"my-component": "/components/my-component/my-component"
}
}
在页面的 `wxml` 文件中,使用自定义组件的标签来引用它。
// wxml
<my-component></my-component>
注意,自定义组件的名称必须遵循一定的命名规范,即以字母开头,可以包含数字、下划线、横杠等字符。
这样,就大致上在微信小程序中使用自定义组件了。接下来,就是写页面结构布局与样式,还有业务逻辑代码了。
这里的拖拽效果,我是基于框架提供的draggable-sheet 组件实现的,封装了这一能力,包括:
-
隐藏滚动条
-
滚动回弹效果
-
滚动到指定位置(snap 到关键点)
-
滚动帧回调(实现滚动驱动动画)
<draggable-sheet
class="sheet"
initial-child-size="0.5"
min-child-size="0.2"
max-child-size="0.8"
snap="{{true}}"
snap-sizes="{{[0.4, 0.6]}}"
worklet:onsizeupdate="onSizeUpdate"
>
<scroll-view
scroll-y="{{true}}"
type="list"
associative-container="draggable-sheet"
bounces="{{true}}"
/>
</draggable-sheet>
3、选择类型和分类管理
这里的长按拖动分类排序,我认为也是一个技术难点。
实现长按拖拽某个选项进行排序,我采用了以下技术方案来实现。
首先,在页面的 `wxml` 文件中,为每个选项添加一个 `touchstart` 和 `touchmove` 事件监听器。
// wxml
<view class="item" bindtouchstart="startDrag" bindtouchmove="drag" data-index="{{index}}">
{{item}}
</view>
在对应的 `js` 文件中,定义 `startDrag` 和 `drag` 方法。`startDrag` 方法用于记录触摸开始时的位置和索引,`drag` 方法用于处理拖拽过程中的逻辑。
// javascript
Page({
data: {
items: ['餐饮', '交通', '服饰']
},
startDrag: function (e) {
// 记录触摸开始时的位置和索引
this.setData({
startX: e.touches[0].clientX,
startY: e.touches[0].clientY,
startIndex: e.currentTarget.dataset.index
});
},
drag: function (e) {
// 获取触摸移动时的位置
const moveX = e.touches[0].clientX;
const moveY = e.touches[0].clientY;
const startX = this.data.startX;
const startY = this.data.startY;
const startIndex = this.data.startIndex;
// 计算拖拽的距离
const distanceX = moveX - startX;
const distanceY = moveY - startY;
// 如果拖拽距离大于一定阈值,则认为是拖拽操作
if (Math.abs(distanceX) > 10 || Math.abs(distanceY) > 10) {
// 获取当前拖拽的元素
const currentItem = this.data.items[startIndex];
// 从数组中移除当前元素
const newItems = this.data.items.filter((item, index) => index !== startIndex);
// 计算新的插入位置
const newIndex = startIndex + Math.round(distanceX / 100);
// 将当前元素插入到新的位置
newItems.splice(newIndex, 0, currentItem);
// 更新数据
this.setData({
items: newItems
});
}
}
});
这样,当用户长按并拖动某个选项时,选项会根据拖动的距离进行重新排序。
4、智能分类和报表分析
为了实现日、月的收支报表,以及分类统计图表,我采用了echarts可视化图表库。这也是一项大工程,后面再单独展开写一下。
5、账目管理
看,这里修改数据的模块,就是引用了前面封装的组件。这开发效率,杠杠滴。
而删除前询问用户以避免误删数据,我是使用了框架的wx.showModal方法显示模态对话框。
当然,还有更多开发过程中的技术要点和开发技巧。但是如果都要一一细写,恐怕就要等后面出教程了。