-
求助将数据库中的内容渲染为表格
2025-03-12 19:58是的,就是渲染数据和存储数据不一致导致的。这个要改成读渲染数据。这两种数据格式完全不一样,由于是底层,上层依赖较多,修改后影响较大,要做中间兼容层,改动较麻烦。
-
求助 SQL 使用两个 LEFT JOIN 时报错
2025-03-12 17:49思源中不可以,但使用其他客户端工具查询是可以的。
可能是低版本的 sqlite 不支持链式 join,改进方式:用括号明确优先级
这样可以
SELECT Block1.content AS content1, Block2.content AS content2, Block3.content AS content3 FROM (( SELECT memo, content FROM blocks WHERE id = '20250312162657-u184k20' ) AS Block1 LEFT JOIN ( SELECT id, content, memo FROM blocks ) AS Block2 ON Block2.id = Block1.memo) LEFT JOIN ( SELECT id, content FROM blocks ) AS Block3 ON Block3.id = Block2.memo;
但建议简化成这样,性能更优
SELECT b1.content AS content1, b2.content AS content2, b3.content AS content3 FROM (blocks b1 LEFT JOIN blocks b2 ON b2.id = b1.memo) LEFT JOIN blocks b3 ON b3.id = b2.memo WHERE b1.id = '20250312162657-u184k20';
-
[js] 文档树文档置顶和设置颜色 [0.0.8 完美版]
2025-03-12 00:34没有问题,就这样设计的。
优先级别是 用户配置 > 代码默认配置
在用户配置和代码默认配置内 主题配置 > default 配置
这里之所以叫优先级别,是不会消失,是同名会覆盖,不同名被合并。
这种设计,主要为了以默认为蓝本,去扩展想要修改的配置,而不需要完全去重新设计。
如果你的配置中的编码完全不同,又不希望默认配置被生效,就要删除默认配置了。
我想了下,你说的有道理,既然第一次运行时已经把默认文件存储到配置文件了,而用户又去掉了默认配置,大概是不希望再用了。
下个版本改进下,已有用户配置文件的情况下,代码里的默认配置就不再效了。
(最新版已支持!) -
求助将数据库中的内容渲染为表格
2025-03-11 19:23这个问题,是思源笔记渲染结果和 api 解析不一致导致的。
已向官方提交了 issue Issue #14345 · siyuan-note/siyuan
暂时解决不了,你可以关注下这个 issue,等待官方回复后再看怎么解决。
-
[js] 简单查询,让数据从此不再难查,支持显示多字段多视图
2025-03-11 17:00这种情况建议先在‘({ row, index, data, options, toLink, toRef, formatDate, formatDateTime, renderMarkdown, getTypeText, renderListView, renderChartView, renderViewByMarkdown, ...args }) => {} 前面加上 async 关键词,方便后面的 await 调用。
然后,有 3 种方法:
- 使用 query 函数
const result = await query(`SELECT content FROM blocks WHERE type = 'd' AND id = (SELECT root_id FROM blocks WHERE id = 'blockBId');`);
- 使用 fromSql
const result = await args.fromSql(`SELECT content FROM blocks WHERE type = 'd' AND id = (SELECT root_id FROM blocks WHERE id = 'blockBId');`, true);
- 使用思源自带 api
const result = await fetchSyncPost('/api/query/sql', { "stmt": sql });
这里的 sql 就是上面的查询语句,这里省略了
- 使用 query 函数
-
求助将数据库中的内容渲染为表格
2025-03-11 09:38那个帖子是根据 [js] 图表如何和表格联动 - wilsons 的回帖 这个改造的,这种方式需要自己手动解析数据库 json 数据,较为麻烦。
建议使用简单查询,实现起来更方便,可参考 震惊,思源表格和数据库竟然可以用 SQL 查询
然后,分别新建一个数据和表格,并复制数据库块 id 和表格块 id,然后在当前文档输入 {{}} 在弹出的对话框中,输入以下代码,并把刚才复制的 id 填入到下面的相应参数中即可
//!js // 数据库块id const avBlockId = '20250311063435-9066xpv'; // 表格块id const tableBlockId = '20250311063452-p03kxva'; // 数据库变更后自动更新延迟,单位是毫秒,默认是1秒,0则不自动更新 // 注意:更新该参数后需要刷新页面才能生效 const autoFreshDelay = 1000; // 监听数据库变化 observeAvChange(); return query( "select * from ?", [fromAv(avBlockId), item], '', ({ rawData, updateTable, renderSuccess }) => { let tableData = transformData(rawData); tableData = toMarkdownTable(tableData); updateTable(tableBlockId, tableData); return renderSuccess('更新完成', item); } ); function toMarkdownTable(data) { // 提取所有列名(包括空列 "") const columns = Array.from( data.reduce((set, row) => { Object.keys(row).forEach(key => set.add(key)); return set; }, new Set()) ).sort((a, b) => (a === "" ? -1 : a.localeCompare(b))); // 确保空列 "" 在最前面 // 构造表头行 const headerRow = `| ${columns.map(col => (col === "" ? "" : col)).join(" | ")} |`; // 构造分隔符行 const separatorRow = `| ${columns.map(() => "---").join(" | ")} |`; // 构造数据行 const dataRows = data.map(row => { return `| ${columns.map(col => row[col] || "").join(" | ")} |`; }); // 拼接所有部分 return [headerRow, separatorRow, ...dataRows].join("\n"); } function transformData(input) { // 创建一个 Map 来按横坐标分组 const groupedData = new Map(); // 遍历输入数组,按横坐标分组 input.forEach(item => { const { 主键, 横坐标, 纵坐标 } = item; // 如果横坐标不存在于 Map 中,则初始化 if (!groupedData.has(横坐标)) { groupedData.set(横坐标, { "": 横坐标 }); } // 将纵坐标和主键添加到对应的横坐标分组中 groupedData.get(横坐标)[纵坐标] = 主键; }); // 将 Map 转换为数组并返回 return Array.from(groupedData.values()); } function observeAvChange() { // 监听av变化,当数据库块被修改时,重新获取数据 if(autoFreshDelay > 0 && !window['__table_observe__' + avBlockId]) { window['__table_observe__' + avBlockId] = observeDOMChanges(document.querySelector('div[data-node-id="'+avBlockId+'"]'), ()=>{ setTimeout(() => { item.querySelector('.protyle-action__reload').click(); }, 100); }, autoFreshDelay, {attributes: false}); } } // 监听dom变化 function observeDOMChanges(targetNode, callback, debounceTime = 1000, options = {}) { // 默认配置 const defaultOptions = { attributes: true, childList: true, subtree: true, }; // 合并默认配置与传入的配置 const config = Object.assign({}, defaultOptions, options); // 创建一个观察器实例 const observer = new MutationObserver((mutationsList) => { // 使用防抖函数确保单位时间内最多只调用一次回调 if(window['__table_observeTimer__' + avBlockId]) { clearTimeout(window['__table_observeTimer__' + avBlockId]); } window['__table_observeTimer__' + avBlockId] = setTimeout(() => { // 处理变化 callback(mutationsList); }, debounceTime); }); // 开始观察目标节点 observer.observe(targetNode, config); // 返回一个函数,以便在不需要时能够停止观察 return () => { observer.disconnect(); }; }
-
[js] 搜索时自动填充选取内容
2025-03-11 07:38虽然能填充,但并不能搜索耶。
我改了一版,可以正常搜索了,并适配了 Mac 用户的按键习惯。
(()=>{ // 等待元素渲染完成后执行 function whenElementExist(selector) { return new Promise(resolve => { const checkForElement = () => { let isExist = false; if (typeof selector === 'function') { isExist = selector(); } else { isExist = document.querySelector(selector); } if (isExist) { resolve(true); } else { requestAnimationFrame(checkForElement); } }; checkForElement(); }); } async function fill_search_text(_t) { if (_t.length == 0) { return } whenElementExist('.b3-text-field.fn__size200').then(() => { const input = document.querySelector('.b3-text-field.fn__size200'); input.value = _t input.dispatchEvent(new Event('input')); setTimeout(()=>{ document.querySelectorAll('.icon--14_14')[1]?.parentElement?.click(); }, 400); }) } function isMac() { return navigator.platform.indexOf("Mac") > -1; } // 事件监听 document.addEventListener('keydown', async (event) => { const ctrlKey = isMac() ? event.metaKey : event.ctrlKey; const controlKey = isMac() ? event.ctrlKey : event.metaKey; if (ctrlKey && !controlKey && !event.shiftKey && !event.altKey && event.key === 'f') { //event.preventDefault(); // 防止快捷键默认行为 fill_search_text(window.getSelection().toString().trim()) } }); })()
-
谈中国互联网的畸形发展与未来出路
2025-03-10 23:21是的,我觉得要看软件的功能和是否刚需了,如果收费合理是可以接受的,但每个人对这个合理的标准可能不同。
我觉得用户之所以有这种抵触心里,
一是因为免费互联网的惯性,培养了用户对免费的依赖心里,一旦开始收费,就觉得不爽。
二是缺乏对开发成本的认知,普通用户不知道软件开发背后的复杂性和高昂成本,误以为只是敲敲键盘的事,分分钟钟搞定似的。忽略了背后的团队研发投入,测试,维护等工作。
三是不同产品对比的错觉,用户不理解产品的细节,觉得界面复杂的就应当贵,简单的就应当便宜,比如,可能认为大语言模型,比如聊天软件更简单。
四是不理解通用软件和定制的区别。比如误以定制和通用软件价格差不多。还是缺乏对开发成本的认知导致的,通用是把成本分摊了,而定制无法分摊,因太个性而没有广泛市场。
-
谈中国互联网的畸形发展与未来出路
2025-03-10 22:40有道理,但房产经济释放出来,各行业都蓬勃发展,toC 能分到多少羹未可知,如果大家对收费有抵触心里,加上盗版不有效管控的话,也不容乐观。
大家有没有感受到,即使今天付费环境已经很好的今天。大家看到付费软件的第一眼是什么感觉?是不是有些抵触心里,想找找有没有破解版等。但如果你逛街时遇到一个卖画的,你不会有这种抵触,你会觉得如果想要这幅画的话就应该给钱。而软件不是,你不会觉得想用的话就应当付费,甚至不想付费。这种情景如果套到卖画的身上是不是感觉很荒唐,比如当你想要画时,你想的不是买下,而是想让他免费送,有时还想办法偷过来,是不是很荒唐。为什么拿画来对比,因为这两个都是人力劳动产品,看似成本都较低。
-
谈中国互联网的畸形发展与未来出路
2025-03-10 20:44是的,或许能打破目前的格局。
另外,这也意味着普通人的春天来了。
你只要有足够的眼光,发现市场,有足够的创意实现想法。
在 ai 的帮助下,你也可以创造小而美的软件,从而实现财务自由,迎娶白富美。
-
谈中国互联网的畸形发展与未来出路
2025-03-10 20:37是的,我认同这一点。
我想可能是因为,这些公司需要让金主爸爸(投资者)看到收益,才会继续追加投资,但由于大部分软件是不盈利的。他们只能透支那些有意愿付费的 vip 用户的信任,说白了就是用透支少数 VIP 用户的利益养活自己罢了。
试想,哪个创业者不是聪明者,他们看不到这些问题吗?不知道良好的用户体验吗?并不是,他们为什么还继续作?我想可能和目前中国付费环境有关吧,不得为盈利而抉择。仅个人观点,未必正确。
所以,我认为小而美才是中国软件健康发展的正确方式。
-
谈中国互联网的畸形发展与未来出路
2025-03-10 20:07有人可能会觉得,日常用品是必需品,而软件不是,其实并不是。
如果说必须品的话,只有食物和衣服才是必需品,而且只需要最廉价和最丑的就足够了。生活中 80% 的东西都不是必需品。
你为什么需要更好的,这就是那 80% 中的一部分了。而软件作为普通生活中的一个产品,也是这 80% 中的一部分。
-
谈中国互联网的畸形发展与未来出路
2025-03-10 19:56并不认同,人均 3000 的人不生活吗?除了生活必需品外没有任性消费过吗?并不是,他也会为自己的喜爱的产品任性过。
只不过,软件更容易找到破解版或免费版,而让人们忘记了这一点。
这就像,你本来想买某件特别喜欢的衣服,但发现有免费送衣服的,或一些人将偷来的衣服(相当于破解软件)免费送,你还会买吗?
如果,人人都这样,你觉得未来还有人生产衣服吗?你还有衣服穿吗?
同样,软件领域,如果人人都这样,未来也没人愿意投入软件领域,中国的软件将难以健康发展,更难以百花齐放。
还是那句话,当想不通时,把它当做普通的日常用品想想看,或许就想通了。
-
期望思源笔记可支持全局行号,而不是代码块中显示行号
2025-03-10 11:27这部分内容往那些放可以实现您说的效果
行号,块号,都是一种说法罢了
其实也不完全一样,行号,通常每行高度一致。块号每个块高度不一致。
-
期望思源笔记可支持全局行号,而不是代码块中显示行号
2025-03-10 11:06因为思源的文档是以块为单位的,不是以行为单位,且每个块的高度没有规律,因此无法实现真正的行号。
只能实现块号,或者叫块数,类似
不过,如果有演示等特殊需求,可以用标尺应付一下,类似
-
JS 求助 - 按更新时间排序,查询当前文档内的块
2025-03-09 23:56因为有引用样式导致的,去掉样式即可
查标题
//!js // 这里可以修改文档id,为空则自动获取当前文档的id let docId = '' // 是否显示更新时间,true显示, false不显示 const isShowUpdated = true; // 更新时间排序,desc由新到旧,asc由旧到新 const updatedOrder = 'desc'; // 是否包含子文档,true包含, false不包含 const isWithSubDoc = false; docId = docId || protyle.options.rootId; const updatedSql = isShowUpdated ? `, updated as 更新时间__date_w150_2` : ''; const subDocSql = isWithSubDoc ? `and path like '%/${docId}%'` : `and root_id = '${docId}'`; return query( `SELECT id as id__hide, markdown as 标题__md_ref_left_1 ${updatedSql} FROM blocks where 1=1 ${subDocSql} and type='h' and subtype='h4' order by updated ${updatedOrder};`, item, '', ({ row, index, data, ...args }) => { row.标题_style = (row.标题_style || '') + ';max-height:none;'; row.标题 = row.标题.replace(/<span data-type="block-ref"/ig, '<span data-type="block-ref" style="background-color: transparent;border-bottom: none;"'); row.更新时间_style = (row.更新时间_style || '') + ';max-height:none;'; } );
查段落
//!js // 这里可以修改文档id,为空则自动获取当前文档的id let docId = '' // 是否显示更新时间,true显示, false不显示 const isShowUpdated = true; // 更新时间排序,desc由新到旧,asc由旧到新 const updatedOrder = 'desc'; // 是否包含子文档,true包含, false不包含 const isWithSubDoc = false; docId = docId || protyle.options.rootId; const updatedSql = isShowUpdated ? `, updated as 更新时间__date_w150_2` : ''; const subDocSql = isWithSubDoc ? `and path like '%/${docId}%'` : `and root_id = '${docId}'`; return query( `SELECT id as id__hide, markdown as 内容__md_ref_left_1 ${updatedSql} FROM blocks where 1=1 ${subDocSql} and (type='p' or type='t') and content != '' order by updated ${updatedOrder} limit 10;`, item, '', ({ row, index, data, ...args }) => { row.内容_style = (row.内容_style || '') + ';max-height:none;'; row.更新时间_style = (row.更新时间_style || '') + ';max-height:none;'; row.内容 = row.内容.replace(/<img/ig, '<img width="25%" style="float: left;"'); row.内容 = row.内容.replace(/<span class="img__net">.*?<\/span>/ig, ''); row.内容 = row.内容.replace(/<span data-type="block-ref"/ig, '<span data-type="block-ref" style="background-color: transparent;border-bottom: none;"'); } );