思源笔记折腾记录 - 做一个白板 - 显示多张卡片

一 、前情提要

我们之前弄了一个最最最简单的卡片,就像/下面这样:

思源笔记折腾记录 - 做一个白板 - 最最最基础的卡片显示 - 链滴 (ld246.com)

现在它还只能显示一张卡片,现在来让它能够显示更多卡片。

二、实现过程

1、拆分组件

我们之前实现的卡片组件是长这个样子:

<template>
    <div class="container" @click="卡片被激活 = false">
        <div ref="卡片框架元素" class="card_frame" @click="e => { e.stopPropagation(); 卡片被激活 = true }">
            <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>
    </div>
</template>
<script setup>
import Moveable from "vue3-moveable";
import { reactive, ref, onMounted } from 'vue'
const 卡片框架元素 = ref(null)
const 卡片被激活 = ref(false)

const 卡片尺寸 = reactive({
    边框宽度: 1,
    内边距: 15,
    宽度: 300,
    高度: 400,
})
let 卡片内容 = ref({})
fetch('/api/export/preview', {
    method: 'POST',
    body: JSON.stringify(
        {
            id: '20221204091100-tf8z0um'
        }
    )
}).then(
    data => {
        return data.json()
    }
).then(
    json => {
        if (json.data) {
            卡片内容.value = json.data
            console.log(卡片内容)
        }
    }
)

//这里的都是事件回调
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>
.container {
    width: 100%;
    height: 100%
}

.card_frame {
    font-size: large;
    box-sizing: border-box;
    width: v-bind('卡片尺寸.高度+"px"');
    height: v-bind('卡片尺寸.高度+"px"');
    margin: 0%;
    padding: 5px;
    transform: translate(603px, 270px);

}

.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;

}

::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

::-webkit-scrollbar-corner {
    background-color: rgba(0, 0, 0, 0);
}

::-webkit-scrollbar-thumb {
    box-sizing: content-box;
    border: 4px solid rgba(0, 0, 0, 0);
    border-radius: 5px;
    box-shadow: inset 0 0 5px 5px gray;
}
</style>

这里我们需要让它能够显示更多的内容,因此需要拆出一个组件来,这里要拆的就是卡片的部分。

whiteBoard\src\DragableCard.vue

<template>
        <div ref="卡片框架元素" class="card_frame" @click="e => { e.stopPropagation(); 卡片被激活 = true }">
            <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 } from 'vue'
//这里来获取数据
let {思源块id} = defineProps(['思源块id'])
console.log(思源块id)
let 卡片内容 = ref({})
let 获取卡片内容= ()=>fetch('/api/export/preview', {
    method: 'POST',
    body: JSON.stringify(
        {
            id:思源块id
        }
    )
}).then(
    data => {
        return data.json()
    }
).then(
    json => {
        if (json.data) {
            卡片内容.value = json.data
        }
    }
)
onMounted(()=>{获取卡片内容()})
//这里定义了卡片的外观属性
const 卡片框架元素 = ref(null)
const 卡片被激活 = ref(false)
const 卡片尺寸 = reactive({
    边框宽度: 1,
    内边距: 15,
    宽度: 300,
    高度: 400,
})

//这里的都是事件回调
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;
    box-sizing: border-box;
    width: v-bind('卡片尺寸.高度+"px"');
    height: v-bind('卡片尺寸.高度+"px"');
    margin: 0%;
    padding: 5px;
    transform: translate(603px, 270px);
}
.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>

然后在 app.vue 中引入它

<template>
    <div class="container" >
        <DragableCard :思源块id="'20221204180515-sadlztc'"></DragableCard>
    </div>
</template>
<script setup>
import DragableCard from './DragableCard.vue';

</script>
<style scoped>
.container {
    width: 100%;
    height: 100%
}
::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}
::-webkit-scrollbar-corner {
    background-color: rgba(0, 0, 0, 0);
}

::-webkit-scrollbar-thumb {
    box-sizing: content-box;
    border: 4px solid rgba(0, 0, 0, 0);
    border-radius: 5px;
    box-shadow: inset 0 0 5px 5px gray;
}
</style>

这里说一个地方,如果你引入组件的时候,本来的组件文件名是 DragableCard.vue,但是打成了 dragableCard.vue

你会很神奇地发现就算名字打错了它还是可以工作,但是当你修改 DragableCard.vue 的时候,并不会触发 hmr(热更新),而且还不会报错~~~~~

所以如果出现了这种情况可以去检查一下是不是哪里大小写写错了。

别问我是怎么知道,又是怎么解决的。。。。。。。

你问我为什么不干脆把蛋疼进行到底,组件文件也用中文命名算了?

抱歉 template 里的标签不能用中文的,到时候组件和文件很难对上号,要不然你以为我不会这么干么。。。。。

这么改完之后,组件还是正常工作的,代表我们的拆分已经成功了。

现在来获取更多的数据,因为页面开始加载的时候很难获知我们到底是要加载哪一个画板,所以干脆从浏览器的地址里面获取 id 算了。

现在把浏览器里的地址改成 127.0.0.1:6809?id=aaaa

然后在控制台输入:

new URL(window.location.href).searchParams.forEach(
	(value,key)=>{console.log(value,key)}
)

这样就能看到:

image

所以我们可以这样来获取所有的参数

function 获取地址参数(){
	let 中间变量 = {}
	new URL(window.location.href).searchParams.forEach(
		(value,key)=>{中间变量[key]=value}
	)
	return 中间变量
}

URL.searchParamsURLSearchParams 可以参考这里:

URL.searchParams - Web API 接口参考 | MDN (mozilla.org)

反正这样就获取到地址栏的参数了,然后我们随便试一下,比如按照 id 把所有子块渲染到画板上。

whiteBoard\src\components\DragableCard.vue

<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 } from 'vue'
let emits =defineEmits(['click'])
//这里来获取数据
let {思源块id,激活} = defineProps(['思源块id','激活'])
console.log(思源块id)
let 卡片内容 = ref({})
let 获取卡片内容= ()=>fetch('/api/export/preview', {
    method: 'POST',
    body: JSON.stringify(
        {
            id:思源块id
        }
    )
}).then(
    data => {
        return data.json()
    }
).then(
    json => {
        if (json.data) {
            卡片内容.value = json.data
        }
    }
)
onMounted(()=>{获取卡片内容()})
//这里定义了卡片的外观属性
const 卡片框架元素 = ref(null)
const 卡片被激活 = ref(false)
const 卡片尺寸 = reactive({
    边框宽度: 1,
    内边距: 15,
    宽度: 300,
    高度: 400,
})

//这里的都是事件回调
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;
    box-sizing: border-box;
    width: v-bind('卡片尺寸.高度+"px"');
    height: v-bind('卡片尺寸.高度+"px"');
    margin: 0%;
    padding: 5px;
    transform: translate(603px, 270px);
}
.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>

whiteBoard\src\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,onMounted} from 'vue'
//这里是在控制卡片状态
let 当前激活卡片序号 = ref(null)
//这里是获取数据
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
            }
        }
    )
}
onMounted(()=>{获取所有子块id()})
</script>
<style scoped>
.container {
    width: 100%;
    height: 100%
}
</style>

这里使用了一个 defineEmits,因为我们的子组件里面顶层有两个标签,所以需要使用它覆盖一下原本的 click 事件。

用法是这样的(这个不用专门 import)

//定义一堆自定义事件
const emit = defineEmits(["自定义事件"])
//触发事件并把数据传出去
emit("自定义事件",数据)

然后就可以 @自定义事件 来使用了。

这么鼓捣完了之后可以试着访问一下 127.0.0.1:6809/?id=20200812220555-lj3enxa 没出问题的话你的浏览器应该会显示这样:

image

然后拖一下它们试试看吧:

很多张卡片


代码片段的仓库在这里

leolee9086/snippets (github.com)

viteWdigets 的仓库在这里

leolee9086/viteWidgets (github.com)

相关帖子

欢迎来到这里!

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

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

    真会玩 👍

  • aa999
    订阅者

    强!

  • yumenawei
    支持者 订阅者

    快进到,白板上架集市。

    1 回复
  • zxhd86
    订阅者

    厉害!

  • leolee
    订阅者 作者

    白板早就上架集市了,只不过去年底子没打好,它长歪了,所以这回是重写一个新的白板而已