mirror of
https://github.com/liufree/obsidian-querydash.git
synced 2026-07-22 05:41:49 +00:00
commit
4768d1fc0a
8 changed files with 174 additions and 6 deletions
21
README-zh.md
21
README-zh.md
|
|
@ -6,8 +6,11 @@ QueryDash的目标是开发一款类似Notion Database的Obsidian插件,但不
|
|||
**由于使用了dataview的api,必须启用dataview插件。**
|
||||
未来将逐步扩展更多实用特性,帮助用户更高效地管理知识、任务和生活。
|
||||
|
||||
**如果你对项目感兴趣,请star一下,非常感谢,这对我很重要**
|
||||
|
||||
**现有功能**
|
||||
1. **多视图支持**:提供表格和列表两种视图,满足不同场景需求。
|
||||
- **时间轴视图**: 使用时间轴来展示数据
|
||||
2. **Dataview SQL支持**:兼容Dataview的SQL语法。
|
||||
3. **增强功能**:
|
||||
- **搜索**:快速定位所需内容。
|
||||
|
|
@ -27,6 +30,24 @@ file.mtime as "mtime" ,file.tags as "tags" from #clippings
|
|||
|
||||

|
||||
|
||||
**timeline**
|
||||
|
||||
**simple mode**
|
||||
~~~markdown
|
||||
```querydash
|
||||
timeline from #start
|
||||
```
|
||||
~~~
|
||||

|
||||
**full mode**
|
||||
|
||||
If you want to append the time later, alias a specific field as "time".
|
||||
~~~markdown
|
||||
```querydash
|
||||
timeline file.ctime as "time" from #start where file.ctime<=date(today) sort file.mtime desc
|
||||
```
|
||||
~~~
|
||||
|
||||
|
||||
**远景规划**
|
||||
1. **首页**:将常用功能整合到首页,提供一站式便捷操作。
|
||||
|
|
|
|||
22
README.md
22
README.md
|
|
@ -3,12 +3,15 @@ The goal of QueryDash is to develop an Obsidian plugin similar to Notion Databas
|
|||
**Since the API of Dataview is used, the Dataview plugin must be enabled.**
|
||||
In the future, it will gradually expand with more practical features to help users manage knowledge, tasks, and life more efficiently.
|
||||
|
||||
**If you are interested in this project, please consider starring it. Thank you very much, it means a lot to me.**
|
||||
|
||||
**language**
|
||||
- [English](README.md)
|
||||
- [简体中文](README-zh.md)
|
||||
|
||||
**Current Features**
|
||||
1. **Multi-view Support**: Provides table and list views to meet different scenario needs.
|
||||
- **timeline**: use timeline view to display data.
|
||||
2. **Dataview SQL Support**: Compatible with Dataview's SQL syntax .
|
||||
3. **Enhanced Features**:
|
||||
- **Search**: Quickly locate the content you need.
|
||||
|
|
@ -18,6 +21,7 @@ In the future, it will gradually expand with more practical features to help use
|
|||
|
||||
**Tutorial**
|
||||
|
||||
**table**
|
||||
~~~markdown
|
||||
```querydash
|
||||
table file.name , file.outlinks as "links" ,file.ctime as "ctime",
|
||||
|
|
@ -27,6 +31,24 @@ file.mtime as "mtime" ,file.tags as "tags" from #clippings
|
|||
|
||||

|
||||
|
||||
**timeline**
|
||||
|
||||
**simple mode**
|
||||
~~~markdown
|
||||
```querydash
|
||||
timeline from #start
|
||||
```
|
||||
~~~
|
||||

|
||||
**full mode**
|
||||
|
||||
If you want to append the time later, alias a specific field as "time".
|
||||
~~~markdown
|
||||
```querydash
|
||||
timeline file.ctime as "time" from #start where file.ctime<=date(today) sort file.mtime desc
|
||||
```
|
||||
~~~
|
||||
|
||||
**Future Vision**
|
||||
1. **Homepage**: Integrate frequently used functions into the homepage for one-stop convenience.
|
||||
2. **Multi-view Support**: Supports timeline, gallery, table, and other views to meet diverse scenario needs.
|
||||
|
|
|
|||
BIN
docs/timeline.png
Normal file
BIN
docs/timeline.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "querydash",
|
||||
"name": "QueryDash",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.3",
|
||||
"minAppVersion": "1.8.0",
|
||||
"description": "Refer to Dataview and add search, sorting, and pagination functions, just like Notion.",
|
||||
"author": "lwx",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"name": "obsidian-querydash",
|
||||
"version": "1.0.2",
|
||||
"description": "Refer to Dataview and add search, sorting, and pagination functions, just like Notion.",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export function formatValue(
|
|||
function formatObject(value: any) {
|
||||
const type =value.type;
|
||||
if(type==='file') {
|
||||
const fileName = value.path.replace(/\.md$/, '');
|
||||
const fileName = value.path.split('/').pop().replace(/\.md$/, '')
|
||||
return {type: "link", path: value.path, display: fileName};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ import React, {useEffect} from 'react';
|
|||
import {App} from "obsidian";
|
||||
import TableView from "./tableview/TableView";
|
||||
import ListView from "./listview/ListView";
|
||||
import {ConfigProvider, ConfigProviderProps} from "antd";
|
||||
import {ConfigProvider, ConfigProviderProps, Timeline} from "antd";
|
||||
import enUS from 'antd/locale/en_US';
|
||||
import TimeLineView from "./timelineview/TimeLineView";
|
||||
|
||||
|
||||
interface QueryDashViewDashs {
|
||||
|
|
@ -18,15 +19,18 @@ const QueryDashView: React.FC<QueryDashViewDashs> = ({app, source}) => {
|
|||
const [sourceType, setSourceType] = React.useState<string>("table");
|
||||
|
||||
useEffect(() => {
|
||||
const sourceType = source.toLowerCase().startsWith("list") ? "list" : "table";
|
||||
// 获取第一个单词
|
||||
const sourceType = source.split(" ")[0].toLowerCase();
|
||||
setSourceType(sourceType);
|
||||
}, []);
|
||||
|
||||
const getView = (app: App, source: string) => {
|
||||
if (sourceType === "table") {
|
||||
return <TableView app={app} source={source}/>;
|
||||
} else {
|
||||
} else if (sourceType === "list") {
|
||||
return <ListView app={app} source={source}/>;
|
||||
} else if (sourceType === "timeline") {
|
||||
return <TimeLineView app={app} source={source}/>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
122
src/pages/timelineview/TimeLineView.tsx
Normal file
122
src/pages/timelineview/TimeLineView.tsx
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
import {ProList} from '@ant-design/pro-components';
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {formatValue} from "../GenerateColumns";
|
||||
import {getAPI} from 'obsidian-dataview';
|
||||
import {ViewProps} from "../../models/ViewProps";
|
||||
import {Radio, RadioChangeEvent, Timeline} from "antd";
|
||||
import {TFile} from "obsidian";
|
||||
|
||||
|
||||
const TimeLineView: React.FC<ViewProps> = ({app, source}) => {
|
||||
|
||||
const [data, setData] = React.useState<any>([]);
|
||||
|
||||
const dv = getAPI(app);
|
||||
const [mode, setMode] = useState<'left' | 'alternate' | 'right'>('alternate');
|
||||
|
||||
const onChange = (e: RadioChangeEvent) => {
|
||||
setMode(e.target.value);
|
||||
};
|
||||
|
||||
|
||||
const renderItem = (itemFile: any, itemCtime: any) => {
|
||||
const date = itemCtime?.display.split(" ")[0] || "";
|
||||
|
||||
return (
|
||||
<span>
|
||||
<a onClick={() => {
|
||||
const file = app.vault.getAbstractFileByPath(itemFile.path);
|
||||
if (file && file instanceof TFile) {
|
||||
const leaf = app.workspace.getLeaf();
|
||||
leaf.openFile(file);
|
||||
}
|
||||
}}>
|
||||
{itemFile.display}
|
||||
</a>
|
||||
{date ? " " + date : ""}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
let resData: any[] = [];
|
||||
|
||||
const response = executeTableQuery(dv, source, {});
|
||||
|
||||
response.then((res) => {
|
||||
const {tableData} = res;
|
||||
tableData.forEach((item: any) => {
|
||||
const file = item["File"];
|
||||
const ctime = item["time"];
|
||||
const finalDisplay = renderItem(file, ctime);
|
||||
resData.push({"children": finalDisplay});
|
||||
});
|
||||
setData(resData);
|
||||
}).catch((error) => {
|
||||
console.error("Error executing query:", error);
|
||||
});
|
||||
// console.log(response, response);
|
||||
|
||||
}, [app, source]);
|
||||
|
||||
function parseTableResult(value: any, params: any): Array<Record<string, any>> {
|
||||
const headers: string[] = value.headers;
|
||||
const rows: Array<Record<string, any>> = [];
|
||||
value.values.forEach((row: any) => {
|
||||
const values: Record<string, any> = {};
|
||||
headers.forEach((header, index) => {
|
||||
const value = row[index];
|
||||
// console.log("list value", value);
|
||||
const resValue = formatValue(value);
|
||||
// console.log("list resValue", resValue);
|
||||
values[header] = resValue;
|
||||
});
|
||||
rows.push(values);
|
||||
}); // console.log("list rows", rows);
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
function replaceFirstWord(source: string): string {
|
||||
const words = source.split(' ');
|
||||
if (words[0] === 'timeline') {
|
||||
words[0] = 'table';
|
||||
}
|
||||
return words.join(' ');
|
||||
}
|
||||
|
||||
const executeTableQuery = async (dvApi: any, source: any, params: any) => {
|
||||
const sql = replaceFirstWord(source);
|
||||
const queryResult = await dvApi.query(sql);
|
||||
|
||||
console.log("queryResult", queryResult);
|
||||
const tableData: any = parseTableResult(queryResult.value, params);
|
||||
return {tableData: tableData};
|
||||
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<h4>Timeline</h4>
|
||||
<br/>
|
||||
<Radio.Group
|
||||
onChange={onChange}
|
||||
value={mode}
|
||||
style={{
|
||||
marginBottom: 20,
|
||||
}}
|
||||
>
|
||||
<Radio value="left">Left</Radio>
|
||||
<Radio value="right">Right</Radio>
|
||||
<Radio value="alternate">Alternate</Radio>
|
||||
</Radio.Group>
|
||||
<Timeline
|
||||
mode={mode}
|
||||
items={data}
|
||||
/>
|
||||
</>)
|
||||
}
|
||||
|
||||
export default TimeLineView;
|
||||
Loading…
Reference in a new issue