思源笔记折腾记录 - 做一个白板 - 保存数据和显示模式

本贴最后更新于 841 天前,其中的信息可能已经时过境迁

一、前情提要

我们之前实现了一个白板,并且让它能够显示文档下的子文档了。

思源笔记折腾记录 - 做一个白板 - 显示多张卡片 - 链滴 (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)

  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    24751 引用 • 101728 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...