新聞中心
在上一篇文章《??低代碼平臺(tái)的屬性面板該如何設(shè)計(jì)???》中聊到了低代碼平臺(tái)的屬性面板的設(shè)計(jì),今天來聊一下畫布區(qū)域的撤銷、重做的設(shè)計(jì)。

10多年的大石橋網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。全網(wǎng)整合營(yíng)銷推廣的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整大石橋建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)從事“大石橋網(wǎng)站設(shè)計(jì)”,“大石橋網(wǎng)站推廣”以來,每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
撤銷、重做其實(shí)是我們平時(shí)一直在用的操作。對(duì)應(yīng)快捷鍵一般就是? Z / Ctrl+Z、?? Z / Ctrl+Shift+Z。這個(gè)功能是很常見的,它可以極大的提升用戶體驗(yàn),提高編輯效率,但是用代碼應(yīng)該如何實(shí)現(xiàn)呢?再具體點(diǎn),在我們的低代碼平臺(tái),針對(duì)畫布區(qū)域元素的一系列操作,又該如何去設(shè)計(jì)呢?
我們先對(duì)其中的一系列狀態(tài)變更做一下分析。
默認(rèn)情況下,用戶在畫布的一系列操作會(huì)改變整個(gè)畫布的呈現(xiàn)狀態(tài):
在進(jìn)行到某個(gè)操作時(shí),用戶是可以回退到之前的狀態(tài)的,也就是撤銷:
當(dāng)然在進(jìn)行撤銷操作后,用戶是可以恢復(fù)這個(gè)操作的,對(duì)應(yīng)的就是重做:
來看下之前畫布的數(shù)據(jù)結(jié)構(gòu):
const editorModule = {
state: {
components: [],
},
mutations: {
addComponent(state, component) {
component.id = uuidv4();
state.components.push(component);
},
updateComponent(state, { id, key, value, isProps }) {
const updatedComponent = state.components.find(
(component) => component.id === (id || state.currentElement)
);
if (updatedComponent) {
if (isProps) {
updatedComponent.props[key] = value;
} else {
updatedComponent[key] = value;
}
}
},
deleteComponent(state, id) {
state.components = state.components.filter(
(component) => component.id !== id
);
},
},
}對(duì)應(yīng)操作:
- 添加組件:addComponent
- 更新組件:updateComponent
- 刪除組件:deleteComponent
結(jié)合上面的三張圖,不難想到我們要單獨(dú)維護(hù)一份數(shù)據(jù)來存儲(chǔ)變更記錄,執(zhí)行撤銷和重做操作時(shí)就是在這份變更記錄取出已有的數(shù)據(jù),然后去更新原來的components。在原有的state中添加:
// 變更記錄
histories: [],
// 游標(biāo),用來標(biāo)記變更的位置
historyIndex: -1,
在畫布區(qū)域操作(添加、刪除、更新)時(shí),更新原有組件數(shù)據(jù)的同時(shí),也要維護(hù)變更記錄。
我們需要封裝一個(gè)更新變更記錄的方法updateHistory。
正常情況下其實(shí)只用往histories添加記錄就可以了:
const updateHistory = (state, historyRecord) => {
state.histories.push(historyRecord);
}在之前的添加組件、更新組件、刪除組件節(jié)點(diǎn)做一下調(diào)整:
添加組件
添加組件的同時(shí)往histories添加一項(xiàng)changeType為add的組件數(shù)據(jù),不過這里的component要做下深拷貝:
addComponent(state, component) {
component.id = uuidv4();
state.components.push(component);
updateHistory(state, {
id: uuidv4(),
componentId: component.id,
changeType: "add",
data: cloneDeep(component),
});
}更新組件
更新組件時(shí)向histories添加一項(xiàng)changeType為modify的組件數(shù)據(jù),同時(shí)要把新/老value和key也添加進(jìn)去:
updateComponent(state, { id, key, value, isProps }) {
const updatedComponent = state.components.find(
(component) => component.id === (id || state.currentElement)
);
if (updatedComponent) {
if (isProps) {
const oldValue = updatedComponent.props[key]
updatedComponent.props[key] = value;
updateHistory(state, {
id: uuidv4(),
componentId: id || state.currentElement,
changeType: "modify",
data: { oldValue, newValue: value, key },
});
} else {
updatedComponent[key] = value;
}
}
},刪除組件
刪除組件時(shí)往histories添加一條changeType為delete的數(shù)據(jù),同時(shí)要把index也做下記錄,因?yàn)楹竺孀龀蜂N操作時(shí)是根據(jù)index重新插入到原來的位置:
deleteComponent(state, id) {
const componentData = state.components.find(
(component) => component.id === id
) as ComponentData;
const componentIndex = state.components.findIndex(
(component) => component.id === id
);
state.components = state.components.filter(
(component) => component.id !== id
);
updateHistory(state, {
id: uuidv4(),
componentId: componentData.id,
changeType: "delete",
data: componentData,
index: componentIndex,
});
},可以看到在添加歷史記錄的過程中,多了一個(gè)changeType字段來區(qū)分是什么類型的變更:
type changeType = 'add' | 'modify' | 'delete'
這個(gè)也是為后面的撤銷/重做做鋪墊,有了歷史記錄,針對(duì)不同的changeType分別執(zhí)行對(duì)應(yīng)的數(shù)據(jù)處理。
首先來看下撤銷,也就是undo。第一步是要找到當(dāng)前的游標(biāo),也就是撤銷操作的位置。如果在此之前,從未有過撤銷操作,也就是 historyIndex 為-1 時(shí),這時(shí)將 historyIndex 置為歷史記錄的最后一項(xiàng)。否則就將 historyIndex--:
if (state.historyIndex === -1) {
state.historyIndex = state.histories.length - 1;
} else {
state.historyIndex--;
}找到撤銷的位置,下一步就是根據(jù)上一步記錄到histories中的不同changeType做對(duì)應(yīng)的數(shù)據(jù)處理:
const history = state.histories[state.historyIndex];
switch (history.changeType) {
case "add":
state.components = state.components.filter(
(component) => component.id !== history.componentId
);
break;
case "delete":
state.components = insert(
state.components,
history.index,
history.data
);
break;
case "modify": {
const { componentId, data } = history;
const { key, oldValue } = data
const updatedComponent = state.component.find(component => component.id === componentId)
if(updatedComponent) {
updatedComponent.props[key] = oldValue
}
break;
}
default:
break;
}
如果之前是做了添加組件操作,那么撤銷時(shí)對(duì)應(yīng)的就是刪除處理。
如果之前是做了刪除處理,那么撤銷時(shí)對(duì)應(yīng)的就是把之前刪除的組件恢復(fù)添加到原來的位置。
如果之前是對(duì)組件屬性做了改動(dòng),那么撤銷時(shí)對(duì)應(yīng)的就是把組件對(duì)應(yīng)的屬性恢復(fù)到原來的值。
那么對(duì)于重做,就是撤銷的逆向操作了,可以理解為就是正常的操作:
const history = state.histories[state.historyIndex];
switch (history.changeType) {
case "add":
state.components.push(history.data);
break;
case "delete":
state.components = state.components.filter(
(component) => component.id !== history.componentId
);
break;
case "modify": {
const { componentId, data } = history;
const { key, newValue } = data
const updatedComponent = state.component.find(component => component.id === componentId)
if(updatedComponent) {
updatedComponent.props[key] = newValue
}
break;
}
default:
break;
}
其實(shí)到這里,一個(gè)基礎(chǔ)的撤銷、重做就已經(jīng)實(shí)現(xiàn)了。
但這是不符合使用習(xí)慣的,我們?cè)谟镁庉嬈鞯臅r(shí)候,不可能讓你無限的撤銷,這個(gè)我們通過設(shè)置maxHistoryNumber來控制,調(diào)整一下之前的updateHistory:
const updateHistory = (state, historyRecord) => {
if (state.histories.length < maxHistoryNumber) {
state.histories.push(historyRecord);
} else {
state.histories.shift();
state.histories.push(historyRecord);
}
}當(dāng)歷史記錄條目小于設(shè)定的最大歷史條目前,正常往histories添加記錄。
如果大于或等于maxHistoryNumber時(shí),就把歷史記錄中最前面的一個(gè)剔除,同時(shí)把最新的這條加到歷史記錄的最后。
還有一個(gè)場(chǎng)景是:在撤銷/重做的過程中,又正常對(duì)畫布區(qū)域執(zhí)行了操作。
這種情況,常用的做法就是把大于historyIndex的歷史記錄直接全部刪除,同時(shí)把historyIndex置為-1,也就是初始狀態(tài)。因?yàn)楝F(xiàn)在已經(jīng)進(jìn)入了一個(gè)新的狀態(tài)分支:
if (state.historyIndex !== -1) {
state.histories = state.histories.slice(0, state.historyIndex);
state.historyIndex = -1;
}至此,低代碼平臺(tái)的撤銷/重做的設(shè)計(jì)思路就分享結(jié)束了。
網(wǎng)站欄目:低代碼平臺(tái)的撤銷與重做該如何設(shè)計(jì)?
瀏覽地址:http://www.fisionsoft.com.cn/article/dhsccph.html


咨詢
建站咨詢
