一、前情提要
我们之前实现了一个白板,并且让它能够显示文档下的子文档了。
思源笔记折腾记录 - 做一个白板 - 显示多张卡片 - 链滴 (ld246.com)
但是好像还是不够给力,因为它的数据没有办法保存啊,每次重新打开,它就被打回原型了。
这样好像不行,所以我们来给它加上数据保存。
二、实现过程
1、数据设计
之前我做过一个简单的白板,cc-image-tag-new
,在那个里面我使用了 indexDB 来存储数据并且把数据结构搞得害挺复杂,把我自己都整得晕晕的。
所以这回我们就不搞那么复杂了,白板的数据不单独存储,而是依赖于思源的数据结构,然后提供几个显示模式,把数据直接存到思源的块里面去~~~
这里就有一个问题,肯定一个块不止显示在一个白板上啊,所以我们需要一个方式来让块知道,在哪个白板上显示的时候它应该在什么位置,显示成什么样子。
所以我们来搞一个块属性 custom-whiteBoardData-< 白板 id>,里面就弄一个数组,记录它被哪个块的白板视图显示的时候,该显示到哪里去。
[
{
mode:<白板的显示模式>
data:{
...transform:<卡片的几何信息>
}
}
]
这样就可以直接查 attribute 表来找到数据了。
但是这样还有一个问题,如果保存的时候直接存储的话,那就会把其他白板的数据也覆盖掉了,这个肯定不是我们想要的。
所以干脆这样好了,每次保存卡片数据的时候先获取一次数据,然后再用最新的数据保存~~~
为甚数据不用 custom-whiteBoardData 来保存然后把白板 id 写到属性里面去?
因为这样的话,一删掉这个属性,那么这个块在所有白板上的属性显示都没有了.......
2、抽离数据获取过程
之前我们都是把数据获取直接写到组件里面的,之后要是数据越来越多可就没法搞了,我们尝试一下把数据获取过程抽离出来:
先不管那么多,抽到 data/index.js 好了:
//没错ref是可以在组件外面用的嗷
import {ref} from 'vue'
function 获取地址参数(){
let 中间变量 = {}
new URL(window.location.href).searchParams.forEach(
(value,key)=>{中间变量[key]=value}
)
return 中间变量
}
let 块列表 = ref([])
let 获取所有子块id= ()=>{
let 块id = 获取地址参数().id
fetch(
'api/query/sql',{
method:'POST',
body:JSON.stringify(
{
stmt:`select * from blocks where path like '%${块id}%' and type='d'`
}
)
}
).then(
data=>{
return data.json()
}
).then(
json=>{
if(json.data){
块列表.value= json.data
}
}
)
}
获取所有子块id()
export {块列表 as 块列表}
这样我们的 app.vue 就只有这么长了:
<template>
<div class="container" @click="(当前激活卡片序号=-1)">
<template v-for = "(块数据,i) in 块列表">
<DragableCard :激活="当前激活卡片序号===i" :思源块id="块数据.id" @click="e=>{e.stopPropagation();当前激活卡片序号=i}"></DragableCard>
</template>
</div>
</template>
<script setup>
import DragableCard from './components/DragableCard.vue';
import {ref} from 'vue'
import {块列表} from './data/index.js'
//这里是在控制卡片状态
let 当前激活卡片序号 = ref(null)
//这里是获取数据,已经挪出去了
</script>
<style scoped>
.container {
width: 100%;
height: 100%
}
</style>
然后不是说了要搞出多种视图吗,所以要弄一个 adapter.js,用来适配视图,现阶段我觉得一个适配器大概要告诉白板:
1、怎么获取所有的卡片
2、怎么获取单个卡片的几何数据
3、怎么获取卡片的内容数据
4、怎么保存单个卡片的几何数据
先来做一个最简单的:
import { 获取地址参数, 核心api } from "./index.js";
let { id, mode } = 获取地址参数()
if (!mode) {
mode = "子文档视图"
}
if (!id) {
id = '20200812220555-lj3enxa'
}
let 白板id = id
let 适配器列表 = {
子文档视图: {
//获取所有在这个文档路径下的文档
获取卡片列表: async () => {
return await 核心api.sql(
{
stmt: `select * from blocks where path like '%${id}%' and type='d'`
}
)
},
//用导出html的预览当成卡片显示内容
获取卡片内容数据: async (卡片id) => {
return await 核心api.exportPreview(
{ id: 卡片id }
)
},
//根据窗口的id和模式来获取对应的数据
获取卡片几何数据: async (卡片id) => {
let 原始数据 = await 核心api.sql(
{
stmt: `select * from attributes where name = 'custom-whiteBoardData-${白板id}' and block_id='${卡片id}'`
}
)
if (原始数据 && 原始数据[0]) {
let json数据 = JSON.parse(原始数据[0].value)
return json数据.find(
item => { return item.mode === mode }
)
}
},
//将卡片的几何数据保存到块属性里
保存卡片几何数据: async (卡片id, 显示数据) => {
let 原始数据 = await 核心api.sql(
{
stmt: `select * from attributes where name = 'custom-whiteBoardData-${白板id}' and block_id='${卡片id}'`
}
)
if (原始数据 && 原始数据[0]) {
let json数据 = JSON.parse(原始数据[0].value)
json数据.find(
item => { return item.mode === mode }
).data = 显示数据
let obj = {}
obj.id =卡片id
obj.attrs ={}
obj.attrs[`custom-whiteBoardData-${白板id}`]=JSON.stringify(json数据)
await 核心api.setBlockAttrs(obj)
} else {
let obj = {}
obj.id =卡片id
obj.attrs ={}
obj.attrs[`custom-whiteBoardData-${白板id}`]=JSON.stringify([{
mode: mode,
data: 显示数据
}])
await 核心api.setBlockAttrs(obj)
}
}
}
}
let 当前数据适配器 = 适配器列表[mode]
export default 当前数据适配器
这个适配器就是获取了文档下的所有子文档来显示卡片视图,现在先把它当成默认适配器。
然后我们引用它获取数据和保存数据就可以了
import {ref} from 'vue'
import 核心api from 'http://127.0.0.1:6806/snippets/noobApi/util/kernelApi.js'
import 当前适配器 from './adapter'
export function 获取地址参数(){
let 中间变量 = {}
new URL(window.location.href).searchParams.forEach(
(value,key)=>{中间变量[key]=value}
)
return 中间变量
}
let 块列表 = await 当前适配器.获取卡片列表()
export {块列表 as 块列表}
export {核心api as 核心api}
export {当前适配器 as 当前适配器}
然后在卡片里利用它来显示和保存数据。
这样就鼓捣完啦。
<template>
<div ref="卡片框架元素" class="card_frame" @click="e => { emits('click', e) }">
<div class="card_body">
<div class="card_content" v-bind:innerHTML="卡片内容.html"></div>
</div>
</div>
<Moveable className="moveable" v-if="激活" :target="卡片框架元素" :draggable="true" :scalable="true" :resizable="true"
:rotatable="true" :keepRatio="false" @drag="onDrag" @scale="onScale" @rotate="onRotate" @resize="onResize">
</Moveable>
</template>
<script setup>
import Moveable from 'vue3-moveable';
import { defineProps } from 'vue';
import { reactive, ref, onMounted, onActivated } from 'vue'
import { 当前适配器 } from '../data/index.js'
let emits = defineEmits(['click'])
//这里来获取数据
let { 思源块id, 激活 } = defineProps(['思源块id', '激活'])
let 卡片内容 = ref({})
const 卡片框架元素 = ref(null)
onMounted(async () => {
卡片内容.value = await 当前适配器.获取卡片内容数据(思源块id)
let 几何数据 = await 当前适配器.获取卡片几何数据(思源块id)
console.log(几何数据)
if (几何数据) {
卡片框架元素.value.style.width = 几何数据.data.width
卡片框架元素.value.style.height = 几何数据.data.height
卡片框架元素.value.style.transform = 几何数据.data.transform
}
})
//这里来保存数据
let 保存数据 = () => {
if (卡片框架元素.value) {
当前适配器.保存卡片几何数据(思源块id,
{
width: 卡片框架元素.value.style.width,
height: 卡片框架元素.value.style.height,
transform: 卡片框架元素.value.style.transform
}
)
}
}
//这里定义了卡片的外观属性
const 卡片尺寸 = reactive({
边框宽度: 1,
内边距: 15,
宽度: 120,
高度: 160,
})
//这里的都是事件回调
function onDrag(e) {
卡片框架元素.value.style.transform = e.transform;
保存数据()
}
function onScale(e) {
卡片框架元素.value.style.transform = e.drag.transform;
保存数据()
}
function onRotate(e) {
卡片框架元素.value.style.transform = e.drag.transform;
保存数据()
}
function onResize(e) {
卡片框架元素.value.style.width = `${e.width}px`;
卡片框架元素.value.style.height = `${e.height}px`;
卡片框架元素.value.style.transform = e.drag.transform;
保存数据()
}
</script>
<style scoped>
.card_frame {
font-size: large;
position: absolute;
box-sizing: border-box;
width: v-bind('(卡片尺寸.高度+"px")');
height: v-bind('(卡片尺寸.高度+"px")');
margin: 0%;
padding: 5px;
}
.card_body {
border:v-bind('`${卡片尺寸.边框宽度}px solid grey`');
border-radius: 15px;
width:v-bind('`calc(100% - ${2*(卡片尺寸.边框宽度+卡片尺寸.内边距)}px)`');
height:v-bind('`calc(100% - ${2*(卡片尺寸.边框宽度+卡片尺寸.内边距)}px)`');
padding:v-bind('`${卡片尺寸.内边距}px`');
background-color: white;
}
.card_content {
max-height: 100%;
max-width: 100%;
overflow-y: scroll;
overflow-x: hidden;
}
</style>
其实这里也可以直接保存卡片的样式的,反正位置和大小那些都是通过样式来保存的。
不过先不管啦。
现在尝试一下打开 127.0.0.1:6809/?id=20210808180320-fqgskfj 试试看吧。
移动卡片之后数据应该已经可以保存了。
之后只需要更多的适配器,就能够显示更多的卡片视图,而且在不同的白板上,同一个文档作为卡片也可以显示到不同的位置了。
代码片段的仓库在这里
leolee9086/snippets (github.com)
viteWdigets 的仓库在这里
leolee9086/viteWidgets (github.com)
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于