更新记录
1.1.6(2021-04-15)
解决多个对象merge失败问题
1.1.5(2021-04-07)
修复聚合操作回退,顺序不对问题
1.1.4(2021-04-02)
数据调整, 从mixin文件移入vuex中管理, 添加实战项目gif图展示
查看更多平台兼容性
阿里云 | 腾讯云 | 支付宝云 |
---|---|---|
√ | × | × |
uni-admin插件通用教程:
uni-admin
是一套基于uniCloud的开源应用管理端系统。详见:uni-admin 基础框架
本插件是uni-admin
系统的扩展插件,为开发者的admin系统添加功能。
需先确保本机有uni-admin
项目,然后将此插件导入到uni-admin
项目下。
如本机没有uni-admin
项目,需要先在HBuilderX中新建项目 -> uni-app项目 -> 选择uni-admin
模板。
如本机的uni-admin
项目版本过老,可能无法使用新插件,需及时升级本机的uni-admin
项目。详见uni-admin
项目更新日志
v587-snapshot 快照(撤回/重做/回档)
简介
顾名思义: 记录一段时间的操作步骤, 可以撤回、重做、回档这段时间的所有记录
兼容性
同类 Proxy
配置
1. 安装依赖
无
2.配置page
- pages.json中subPackages节点中添加
"subPackages": [{
"root": "uni_modules/v587-snapshot/pages",
"pages": [{
"path": "/index/index",
"style": {
"navigationBarTitleText": "快照Snapshot"
}
}]
}]
main.js
中引入插件
import V587Snapshot from '@/uni_modules/v587-snapshot'
Vue.use(V587Snapshot, {store, limit: 100})
- 使用的地方引入
import SNAPSHOT from 'uni_modules/v587-snapshot/mixins'
import CRUD from 'uni_modules/v587-snapshot/js_sdk/v587-toolbox/crud.js'
export default {
//...
mixins: [SNAPSHOT],
//...
}
- 全局引入
main.js
文件中插入
import SNAPSHOT from 'uni_modules/v587-snapshot/mixins'
Vue.mixin(SNAPSHOT)
其中 CRUD
为定义的操作类型
const CRUD = {
C: 'v587-create', // 创建操作
R: 'v587-retrieve',// 聚合操作
U: 'v587-update', // 更新操作
D: 'v587-delete' // 删除操作
}
export default CRUD
使用
开启/关闭(默认开启) 操作 this.enabledSnap(true/false)
, 如需屏蔽初始化操作,可以先关闭,再开启
创建监听对象(捕捉操作痕迹基于数据)
- 数组
tableData: this._createObservable([])
- 对象
observeObj: this._createObservable({a: 1 })
深度监听(默认监听当前对象, 及false),开启设定为true, 如 this._createObservable(obj, true)
手动捕捉(单操作)
捕捉快照方法1: _snap(action = CRUD.C, target, diff, key)
捕捉快照方法2:_snapshot(snapshot)
,其中 snapshot
由 _createSnapshot(action = CRUD.C, target, diff, key)
创建而来
以更新表格中某一项为例
// 点击编辑时, 备份旧数据
edit(item, index) {
item._backup = this._clone(item)
this.$set(item, 'editable', true)
},
// 更新数据,
updateItem(item, index) {
item.editable = false
const newVal = this._clone(item)
delete newVal._backup
this._snap(CRUD.U, this.tableData, {
oldVal: item._backup,
newVal
}, index)
}
其中 CRUD.U
操作中 _snap
第三个参数diff
传入结构为 {oldVal, newVal}
,
捕捉的数据需要通过_clone(obj)
方法,克隆一份,不能传入原对象引用
手动捕捉(多操作、组合操作)
提供了两种方式捕捉
-
- 通过
_composeObservables(fn)
方法, 如批量删除
- 通过
//批量删除
delTable() {
this._composeObservables(_ => {
// 逆向删除, 先删除序号大的
this.selectedIndexs.reverse().forEach(i => this.tableData.splice(i, 1))
})
},
-
- 通过
_beginTransaction()
和_endTransaction()
开启一段事务操作, 此过程中的所有单操作都会记录在这个过程中, 如插入操作
- 通过
this._beginTransaction()
this.tableData.unshift(item)
this._endTransaction()
// 或
// this._composeObservables(_=>{
// this.tableData.unshift(item)
// })
【注意】 排序等复杂操作属于聚合操作类型, 需要包裹聚合操作或事务中
单独使用, 不创建监听对象
如交换位置操作(组合操作)
// 交换位置, 两步操作
this._beginTransaction()
const {
newIndex,
oldIndex
} = e
const newVal = this._clone(this.children[newIndex])
const oldVal = this._clone(this.children[oldIndex])
let oldSnap = this._createSnapshot(this.U, this.children, {
oldVal,
newVal
}, newIndex)
const newSnap = this._createSnapshot(this.U, this.children, {
oldVal: newVal,
newVal: oldVal
}, oldIndex)
this.composeSnapshot.target.push(oldSnap, newSnap)
this._endTransaction()
撤销 (actions 返回一个Promise, 携带快照数据)
undo(e) {
this._undo().then(res => {
console.log('undo:', res);
})
}
重做 (actions 返回一个Promise, 携带快照数据)
redo(e) {
this._redo().then(res => {
console.log('redo:', res);
})
}
回档
retreated(index) {
this._retreated(index)
}
【注意】 避免observable对象整体赋值操作,否则监听将会失效, 如数组concat操作(拷贝赋值), 或重新observable包裹
数据结构
- 创建操作
{
"action": "v587-create",
"target": [
{
"date": 1616739373535,
"name": "Dcloud48",
"address": "上海市普陀区金沙江路 1516 弄"
},
{
"date": "2020-09-01",
"name": "Dcloud1",
"address": "上海市普陀区金沙江路 1518 弄"
},
{
"date": "2020-09-02",
"name": "Dcloud2",
"address": "上海市普陀区金沙江路 1517 弄"
}
],
"diff": {
"date": "2020-09-02",
"name": "Dcloud2",
"address": "上海市普陀区金沙江路 1517 弄"
},
"time": 1616739373535,
"key": "2"
}
- 更新操作
{
"action": "v587-update",
"target": [
{
"date": 1616739373535,
"name": "Dcloud48",
"address": "上海市普陀区金沙江路 1516 弄"
},
{
"date": "2020-09-01",
"name": "Dcloud1",
"address": "上海市普陀区金沙江路 1518 弄"
},
{
"date": "2020-09-02",
"name": "Dcloud2",
"address": "上海市普陀区金沙江路 1517 弄"
}
],
"diff": {
"oldVal": {
"date": "2020-09-02",
"name": "Dcloud2",
"address": "上海市普陀区金沙江路 1517 弄"
},
"newVal": {
"date": "2020-09-01",
"name": "Dcloud1",
"address": "上海市普陀区金沙江路 1518 弄"
}
},
"time": 1616739373535,
"key": "1"
}
- 聚合操作
{
"action": "v587-retrieve",
"target": [
{
"action": "v587-create",
"target": [
{
"date": 1616739373535,
"name": "Dcloud48",
"address": "上海市普陀区金沙江路 1516 弄"
},
{
"date": "2020-09-01",
"name": "Dcloud1",
"address": "上海市普陀区金沙江路 1518 弄"
},
{
"date": "2020-09-02",
"name": "Dcloud2",
"address": "上海市普陀区金沙江路 1517 弄"
}
],
"diff": {
"date": "2020-09-02",
"name": "Dcloud2",
"address": "上海市普陀区金沙江路 1517 弄"
},
"time": 1616739373535,
"key": "2"
},
{
"action": "v587-update",
"target": [
{
"date": 1616739373535,
"name": "Dcloud48",
"address": "上海市普陀区金沙江路 1516 弄"
},
{
"date": "2020-09-01",
"name": "Dcloud1",
"address": "上海市普陀区金沙江路 1518 弄"
},
{
"date": "2020-09-02",
"name": "Dcloud2",
"address": "上海市普陀区金沙江路 1517 弄"
}
],
"diff": {
"oldVal": {
"date": "2020-09-02",
"name": "Dcloud2",
"address": "上海市普陀区金沙江路 1517 弄"
},
"newVal": {
"date": "2020-09-01",
"name": "Dcloud1",
"address": "上海市普陀区金沙江路 1518 弄"
}
},
"time": 1616739373535,
"key": "1"
},
{
"action": "v587-update",
"target": [
{
"date": 1616739373535,
"name": "Dcloud48",
"address": "上海市普陀区金沙江路 1516 弄"
},
{
"date": "2020-09-01",
"name": "Dcloud1",
"address": "上海市普陀区金沙江路 1518 弄"
},
{
"date": "2020-09-02",
"name": "Dcloud2",
"address": "上海市普陀区金沙江路 1517 弄"
}
],
"diff": {
"oldVal": {
"date": "2020-09-01",
"name": "Dcloud1",
"address": "上海市普陀区金沙江路 1518 弄"
},
"newVal": {
"date": 1616739373535,
"name": "Dcloud48",
"address": "上海市普陀区金沙江路 1516 弄"
}
},
"time": 1616739373535,
"key": "0"
}
],
"time": 1616739373535
}
原理
目前web上的撤销重做有两种方式:
命令式:每步操作都要写逆操作方法,每次保存正向和逆向两个操作(命令)
快照式:维护一个数组,保存用户每次操作的数据快照,每次恢复都是一次页面数据重置
本插件采用的是命令式方式, 使用vuex管理数据快照, 为了通用性, 只管理数据, 快照的数据结构需要自行定
义, 为没类操作定义个类型, 然后通过类型去做数据回档(比如 diff操作)
功能分析(借用 Web 应用的撤销重做实现)
- 用户的一系列操作会改变页面的状态:
- 在进行了某个操作后,用户有能力回到之前的某个状态,即撤销:
- 在撤销某个操作后,用户有能力再次恢复这个操作,即重做:
- 当页面处于某个历史状态时,这时用户进行了某个操作后,这个状态后面的状态会被抛弃,此时产生一个新的状态分支:
-
通过设置回档索引, 可以直接回档到指定存档位置
图略
-
通过设置limit, 可以限定存档最大size, 保证不会过度占用内存, 默认size: 100,可以通过
store.commit('snapshot/SET_LIMIT', limit || LIMIT)
动态设定, 下次存档snap
时生效
成品展示
功能在完善中,体验功能可关注订阅号
关注订阅号
关注订阅号,回复“v587体验账号” 获取体验账号
附
一、数组
+ 一个插入操作触发proxy set方法数据示例(unshift操作), 具体查看‘步骤*’
// 原始数据
target[key]: [
{
"date": "2020-09-01",
"name": "Dcloud1",
"address": "上海市普陀区金沙江路 1518 弄"
},
{
"date": "2020-09-02",
"name": "Dcloud2",
"address": "上海市普陀区金沙江路 1517 弄"
}
]
observable.js:35 target[key], type, key, indexOf: {"date":"2020-09-02","name":"Dcloud2","address":"上海市普陀区金沙江路 1517 弄"} v587-create 2 1
// 步骤一:拷贝一份数据
observable.js:31 target[key]: [
{
"date": "2020-09-01",
"name": "Dcloud1",
"address": "上海市普陀区金沙江路 1518 弄"
},
{
"date": "2020-09-02",
"name": "Dcloud2",
"address": "上海市普陀区金沙江路 1517 弄"
},
{
"date": "2020-09-02",
"name": "Dcloud2",
"address": "上海市普陀区金沙江路 1517 弄"
}
]
observable.js:35 target[key], type, key, indexOf: {"date":"2020-09-01","name":"Dcloud1","address":"上海市普陀区金沙江路 1518 弄"} v587-update 1 0
// 步骤二:交换位置
observable.js:31 target[key]: [
{
"date": "2020-09-01",
"name": "Dcloud1",
"address": "上海市普陀区金沙江路 1518 弄"
},
{
"date": "2020-09-01",
"name": "Dcloud1",
"address": "上海市普陀区金沙江路 1518 弄"
},
{
"date": "2020-09-02",
"name": "Dcloud2",
"address": "上海市普陀区金沙江路 1517 弄"
}
]
observable.js:35 target[key], type, key, indexOf: {"date":1616726158414,"name":"Dcloud48","address":"上海市普陀区金沙江路 1516 弄"} v587-update 0 0
// 步骤三:将新数据插入到指定位置
observable.js:31 target[key]: [
{
"date": 1616726158414,
"name": "Dcloud48",
"address": "上海市普陀区金沙江路 1516 弄"
},
{
"date": "2020-09-01",
"name": "Dcloud1",
"address": "上海市普陀区金沙江路 1518 弄"
},
{
"date": "2020-09-02",
"name": "Dcloud2",
"address": "上海市普陀区金沙江路 1517 弄"
}
]