diff --git a/.umirc.ts b/.umirc.ts deleted file mode 100644 index 77d18f6..0000000 --- a/.umirc.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {defineConfig} from '@umijs/max'; - -export default defineConfig({ - - - antd: {}, - access: {}, - model: {}, - initialState: {}, - request: {}, - layout: { - title: '@umijs/max', - }, - routes: [ - {path: '/', component: '@/pages/index'}, - ], - npmClient: 'npm', -}); - diff --git a/README-zh.md b/README-zh.md new file mode 100644 index 0000000..4515d70 --- /dev/null +++ b/README-zh.md @@ -0,0 +1,42 @@ + + + +**项目目标** +QueryDash的目标是开发一款类似Notion Database的Obsidian插件,但不仅限于数据库功能。未来将逐步扩展更多实用特性,帮助用户更高效地管理知识、任务和生活。 + +**现有功能** +1. **多视图支持**:提供表格和列表两种视图,满足不同场景需求。 +2. **Dataview SQL支持**:兼容Dataview的SQL语法(暂不支持`GROUP BY`)。 +3. **增强功能**: + - **搜索**:快速定位所需内容。 + - **过滤**:根据条件筛选数据。 + - **合计**:支持数据汇总统计。 + - **分页**:优化大数据量下的浏览体验。 + +**使用教程** +![demo.gif](docs/demo.gif) + + +**远景规划** +1. **首页**:将常用功能整合到首页,提供一站式便捷操作。 +2. **多视图支持**:支持时间线、画廊、表格等多种视图,满足不同场景需求。 +3. **复习卡片**:内置复习功能,帮助用户高效巩固知识。 +4. **Database功能**:支持创建、编辑、删除等操作,打造灵活的数据管理体验。 +5. **更多功能**:持续开发,不断丰富插件功能,提升用户体验。 + +**参考项目** +- [Dataview](https://github.com/blacksmithgu/obsidian-dataview):强大的数据查询和管理工具。 +- [Project](https://github.com/marcusolsson/obsidian-projects):任务和项目管理插件。 + +**项目简介** +作为一名Obsidian的深度用户,我发现现有的插件在搜索、复习以及Dataview表格的编辑和删除等功能上无法完全满足我的需求。因此,我决定基于Ant Design开发一套全新的视图系统,旨在提升Obsidian的使用体验,更好地辅助日常生活和工作。 + +目前,项目的功能还处于初期阶段,但我会持续投入开发,逐步完善和扩展功能。我相信通过不断迭代,这个项目会变得越来越实用和强大。 + +**关于赞助** +本项目完全基于个人兴趣开发,如果您觉得这个项目对您有帮助,并愿意支持我的工作。您的每一份赞助都将成为我持续开发的动力,非常感谢! + +**未来计划** +- 完善现有功能,提升用户体验 +- 增加更多实用特性,如高级搜索、智能复习等 +- 优化性能,确保系统稳定运行 diff --git a/README.md b/README.md index 578b2e2..2bb2f9b 100644 --- a/README.md +++ b/README.md @@ -1,96 +1,38 @@ -# Obsidian Sample Plugin +**Project Objective** +The goal of QueryDash is to develop an Obsidian plugin similar to Notion Database, but not limited to database functionality. In the future, it will gradually expand with more practical features to help users manage knowledge, tasks, and life more efficiently. -This is a sample plugin for Obsidian (https://obsidian.md). +**Current Features** +1. **Multi-view Support**: Provides table and list views to meet different scenario needs. +2. **Dataview SQL Support**: Compatible with Dataview's SQL syntax (currently does not support `GROUP BY`). +3. **Enhanced Features**: + - **Search**: Quickly locate the content you need. + - **Filter**: Screen data based on conditions. + - **Aggregation**: Supports data summary statistics. + - **Pagination**: Optimizes browsing experience for large datasets. -This project uses TypeScript to provide type checking and documentation. -The repo depends on the latest plugin API (obsidian.d.ts) in TypeScript Definition format, which contains TSDoc comments describing what it does. +**Tutorial** +![demo.gif](docs/demo.gif) -**Note:** The Obsidian API is still in early alpha and is subject to change at any time! +**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. +3. **Review Cards**: Built-in review function to help users efficiently consolidate knowledge. +4. **Database Functionality**: Supports create, edit, delete, and other operations for flexible data management. +5. **More Features**: Continuous development to enrich plugin functionality and enhance user experience. -This sample plugin demonstrates some of the basic functionality the plugin API can do. -- Adds a ribbon icon, which shows a Notice when clicked. -- Adds a command "Open Sample Modal" which opens a Modal. -- Adds a plugin setting tab to the settings page. -- Registers a global click event and output 'click' to the console. -- Registers a global interval which logs 'setInterval' to the console. +**Reference Projects** +- [Dataview](https://github.com/blacksmithgu/obsidian-dataview): A powerful data query and management tool. +- [Project](https://github.com/marcusolsson/obsidian-projects): A task and project management plugin. -## First time developing plugins? +**Project Introduction** +As a deep user of Obsidian, I found that existing plugins could not fully meet my needs in terms of search, review, and editing/deleting Dataview tables. Therefore, I decided to develop a brand-new view system based on Ant Design, aiming to enhance the Obsidian experience and better assist daily life and work. -Quick starting guide for new plugin devs: +Currently, the project is in its early stages, but I will continue to invest in development to gradually improve and expand its features. I believe that through continuous iteration, this project will become increasingly practical and powerful. -- Check if [someone already developed a plugin for what you want](https://obsidian.md/plugins)! There might be an existing plugin similar enough that you can partner up with. -- Make a copy of this repo as a template with the "Use this template" button (login to GitHub if you don't see it). -- Clone your repo to a local development folder. For convenience, you can place this folder in your `.obsidian/plugins/your-plugin-name` folder. -- Install NodeJS, then run `npm i` in the command line under your repo folder. -- Run `npm run dev` to compile your plugin from `main.ts` to `main.js`. -- Make changes to `main.ts` (or create new `.ts` files). Those changes should be automatically compiled into `main.js`. -- Reload Obsidian to load the new version of your plugin. -- Enable plugin in settings window. -- For updates to the Obsidian API run `npm update` in the command line under your repo folder. +**About Sponsorship** +This project is entirely developed out of personal interest. If you find it helpful and are willing to support my work, every sponsorship will be a motivation for me to continue developing. Thank you very much! -## Releasing new releases - -- Update your `manifest.json` with your new version number, such as `1.0.1`, and the minimum Obsidian version required for your latest release. -- Update your `versions.json` file with `"new-plugin-version": "minimum-obsidian-version"` so older versions of Obsidian can download an older version of your plugin that's compatible. -- Create new GitHub release using your new version number as the "Tag version". Use the exact version number, don't include a prefix `v`. See here for an example: https://github.com/obsidianmd/obsidian-sample-plugin/releases -- Upload the files `manifest.json`, `main.js`, `styles.css` as binary attachments. Note: The manifest.json file must be in two places, first the root path of your repository and also in the release. -- Publish the release. - -> You can simplify the version bump process by running `npm version patch`, `npm version minor` or `npm version major` after updating `minAppVersion` manually in `manifest.json`. -> The command will bump version in `manifest.json` and `package.json`, and add the entry for the new version to `versions.json` - -## Adding your plugin to the community plugin list - -- Check the [plugin guidelines](https://docs.obsidian.md/Plugins/Releasing/Plugin+guidelines). -- Publish an initial version. -- Make sure you have a `README.md` file in the root of your repo. -- Make a pull request at https://github.com/obsidianmd/obsidian-releases to add your plugin. - -## How to use - -- Clone this repo. -- Make sure your NodeJS is at least v16 (`node --version`). -- `npm i` or `yarn` to install dependencies. -- `npm run dev` to start compilation in watch mode. - -## Manually installing the plugin - -- Copy over `main.js`, `styles.css`, `manifest.json` to your vault `VaultFolder/.obsidian/plugins/your-plugin-id/`. - -## Improve code quality with eslint (optional) -- [ESLint](https://eslint.org/) is a tool that analyzes your code to quickly find problems. You can run ESLint against your plugin to find common bugs and ways to improve your code. -- To use eslint with this project, make sure to install eslint from terminal: - - `npm install -g eslint` -- To use eslint to analyze this project use this command: - - `eslint main.ts` - - eslint will then create a report with suggestions for code improvement by file and line number. -- If your source code is in a folder, such as `src`, you can use eslint with this command to analyze all files in that folder: - - `eslint .\src\` - -## Funding URL - -You can include funding URLs where people who use your plugin can financially support it. - -The simple way is to set the `fundingUrl` field to your link in your `manifest.json` file: - -```json -{ - "fundingUrl": "https://buymeacoffee.com" -} -``` - -If you have multiple URLs, you can also do: - -```json -{ - "fundingUrl": { - "Buy Me a Coffee": "https://buymeacoffee.com", - "GitHub Sponsor": "https://github.com/sponsors", - "Patreon": "https://www.patreon.com/" - } -} -``` - -## API Documentation - -See https://github.com/obsidianmd/obsidian-api +**Future Plans** +- Improve existing features to enhance user experience. +- Add more practical features, such as advanced search and intelligent review. +- Optimize performance to ensure system stability. diff --git a/docs/demo.gif b/docs/demo.gif new file mode 100644 index 0000000..123597f Binary files /dev/null and b/docs/demo.gif differ diff --git a/manifest.json b/manifest.json index dfa940e..a32212a 100644 --- a/manifest.json +++ b/manifest.json @@ -1,11 +1,11 @@ { - "id": "sample-plugin", - "name": "Sample Plugin", + "id": "obsidian-querydash-plugin", + "name": "QueryDash", "version": "1.0.0", - "minAppVersion": "0.15.0", - "description": "Demonstrates some of the capabilities of the Obsidian API.", - "author": "Obsidian", - "authorUrl": "https://obsidian.md", + "minAppVersion": "1.8.0", + "description": "This is a database search plugin for Obsidian", + "author": "lwx", + "authorUrl": "https://github.com/liufree/obsidian-querydash", "fundingUrl": "https://obsidian.md/pricing", "isDesktopOnly": false } diff --git a/package.json b/package.json index 2c33cd0..2c53b25 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "obsidian-sample-plugin", + "name": "obsidian-querydash", "version": "1.0.0", - "description": "This is a sample plugin for Obsidian (https://obsidian.md)", + "description": "This is a database search plugin for Obsidian", "main": "main.js", "scripts": { "dev": "vite build --watch --mode development", diff --git a/src/components/SampleSettingTab.ts b/src/components/SampleSettingTab.ts index b7fd9ec..c3f2ca4 100644 --- a/src/components/SampleSettingTab.ts +++ b/src/components/SampleSettingTab.ts @@ -1,10 +1,10 @@ import {App, PluginSettingTab, Setting} from "obsidian"; -import MyPlugin from "../main"; +import QueryDashPlugin from "../main"; class SampleSettingTab extends PluginSettingTab { - plugin: MyPlugin; + plugin: QueryDashPlugin; - constructor(app: App, plugin: MyPlugin) { + constructor(app: App, plugin: QueryDashPlugin) { super(app, plugin); this.plugin = plugin; } diff --git a/src/main.tsx b/src/main.tsx index 4e9aeae..f993b65 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,108 +1,18 @@ -import {Editor, MarkdownView, Notice, Plugin} from 'obsidian'; -import SampleSettingTab from './components/SampleSettingTab'; -import SampleModal from "./components/SampleModal"; +import {Plugin} from 'obsidian'; +import ReactComponent from "./pages/ReactComponent"; +import {Root} from 'react-dom/client'; -import {SampleView} from "./sampleView"; -import {SAMPLE_VIEW_TYPE} from './constants'; -import React from "react"; -import AntdTableDemo from "./pages/AntdTableDemo"; -import TableDemo from "./pages/TableDemo"; -import {StrictMode} from 'react'; -import ReactDemo from "./pages/ReactDemo"; -import { Root, createRoot } from 'react-dom/client'; -interface MyPluginSettings { - mySetting: string; -} +export default class QueryDashPlugin extends Plugin { -const DEFAULT_SETTINGS: MyPluginSettings = { - mySetting: 'default' -} - -export default class MyPlugin extends Plugin { - settings: MyPluginSettings; root: Root | null = null; async onload() { - await this.loadSettings(); - this.registerView(SAMPLE_VIEW_TYPE, (leaf) => new SampleView(leaf, this)); - - // This creates an icon in the left ribbon. - const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => { - // Called when the user clicks the icon. - new Notice('This is a notice!'); - this.openMemos(); - }); - // Perform additional things with the ribbon - ribbonIconEl.addClass('my-plugin-ribbon-class'); - - // This adds a status bar item to the bottom of the app. Does not work on mobile apps. - const statusBarItemEl = this.addStatusBarItem(); - statusBarItemEl.setText('Status Bar Text'); - - // This adds a simple command that can be triggered anywhere - this.addCommand({ - id: 'open-sample-modal-simple', - name: 'Open sample modal (simple)', - callback: () => { - new SampleModal(this.app).open(); - } - }); - // This adds an editor command that can perform some operation on the current editor instance - this.addCommand({ - id: 'sample-editor-command', - name: 'Sample editor command', - editorCallback: (editor: Editor, view: MarkdownView) => { - console.log(editor.getSelection()); - editor.replaceSelection('Sample Editor Command'); - } - }); - // This adds a complex command that can check whether the current state of the app allows execution of the command - this.addCommand({ - id: 'open-sample-modal-complex', - name: 'Open sample modal (complex)', - checkCallback: (checking: boolean) => { - // Conditions to check - const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView); - if (markdownView) { - // If checking is true, we're simply "checking" if the command can be run. - // If checking is false, then we want to actually perform the operation. - if (!checking) { - new SampleModal(this.app).open(); - } - - // This command will only show up in Command Palette when the check function returns true - return true; - } - } - }); - - // This adds a settings tab so the user can configure various aspects of the plugin - this.addSettingTab(new SampleSettingTab(this.app, this)); - - // If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin) - // Using this function will automatically remove the event listener when this plugin is disabled. - this.registerDomEvent(document, 'click', (evt: MouseEvent) => { - console.log('click', evt); - }); - - // When registering intervals, this function will automatically clear the interval when the plugin is disabled. - this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000)); - - // 代码块中标注table的绘制成sampleView - this.registerMarkdownCodeBlockProcessor('sample', (source, el, ctx) => { - - console.log("start") - console.log('source',source) - console.log('ctx',ctx) - // 插入一个div + this.registerMarkdownCodeBlockProcessor('querydash', (source, el, ctx) => { const container = el.createDiv(); - - // 创建一个 MarkdownRenderChild 实例 - const reactComponent = new ReactDemo(container, source,this.app); + const reactComponent = new ReactComponent(container, source, this.app); ctx.addChild(reactComponent); - }); } @@ -110,24 +20,5 @@ export default class MyPlugin extends Plugin { } - async loadSettings() { - this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); - } - - async saveSettings() { - await this.saveData(this.settings); - } - - async openMemos() { - const workspace = this.app.workspace; - workspace.detachLeavesOfType(SAMPLE_VIEW_TYPE); - // const leaf = workspace.getLeaf( - // !Platform.isMobile && workspace.activeLeaf && workspace.activeLeaf.view instanceof FileView, - // ); - const leaf = workspace.getLeaf(false); - await leaf.setViewState({type: SAMPLE_VIEW_TYPE}); - workspace.revealLeaf(leaf); - - } } diff --git a/src/models/ViewProps.ts b/src/models/ViewProps.ts new file mode 100644 index 0000000..ac210ce --- /dev/null +++ b/src/models/ViewProps.ts @@ -0,0 +1,6 @@ +import {App} from "obsidian"; + +export interface ViewProps { + app: App; + source: string; +} diff --git a/src/pages/AntdTableDemo.tsx b/src/pages/AntdTableDemo.tsx deleted file mode 100644 index 57926b7..0000000 --- a/src/pages/AntdTableDemo.tsx +++ /dev/null @@ -1,207 +0,0 @@ -import React from 'react'; -import { InboxOutlined, UploadOutlined } from '@ant-design/icons'; -import { - Button, - Checkbox, - Col, - ColorPicker, - Form, - InputNumber, - Radio, - Rate, - Row, - Select, - Slider, - Space, - Switch, - Upload, -} from 'antd'; - -const { Option } = Select; - -const formItemLayout = { - labelCol: { span: 6 }, - wrapperCol: { span: 14 }, -}; - -const normFile = (e: any) => { - console.log('Upload event:', e); - if (Array.isArray(e)) { - return e; - } - return e?.fileList; -}; - -const onFinish = (values: any) => { - console.log('Received values of form: ', values); -}; - -const App: React.FC = () => ( -
- - China - - - - - - - - - - - - - - - machines - - - - - - - - - - - - - - item 1 - item 2 - item 3 - - - - - - item 1 - item 2 - item 3 - - - - - - - - - A - - - - - B - - - - - C - - - - - D - - - - - E - - - - - F - - - - - - - - - - - - - - - - - - -

- -

-

Click or drag file to this area to upload

-

Support for a single or bulk upload.

-
-
-
- - - - - - - - - - -
-); - -export default App; diff --git a/src/pages/FormDemo.tsx b/src/pages/FormDemo.tsx deleted file mode 100644 index 1c70089..0000000 --- a/src/pages/FormDemo.tsx +++ /dev/null @@ -1,168 +0,0 @@ -import { SmileOutlined } from '@ant-design/icons'; -import { - FooterToolbar, - PageContainer, - ProForm, - ProFormDateRangePicker, - ProFormDigit, - ProFormRadio, - ProFormSelect, - ProFormText, - ProFormTextArea, - ProFormUploadButton, - ProLayout, -} from '@ant-design/pro-components'; -import { Card } from 'antd'; -import React from 'react'; - -export default () => { - return ( - [ - { - path: '/one', - icon: , - name: '一级名称', - children: [ - { - path: 'two', - name: '二级名称', - }, - ], - }, - ]} - layout="mix" - location={{ - pathname: '/one/two', - }} - > - - - {dom}, - }} - onFinish={async (values) => console.log(values)} - > - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; diff --git a/src/pages/GenerateColumns.tsx b/src/pages/GenerateColumns.tsx new file mode 100644 index 0000000..735a9c1 --- /dev/null +++ b/src/pages/GenerateColumns.tsx @@ -0,0 +1,37 @@ +import dayjs from "dayjs"; + +export function formatValue( + value: Record +) { + let res; + if (!value) { + return; + } + console.log("format value", value); + if (Array.isArray(value)) { + value.map((v) => (typeof v === "object" ? formatObject(v) : v)); + res = {type: "array", display: value}; + } else if (typeof value === "object") { + res = formatObject(value); + } else { + res = {type: "text", display: value}; + } + return res; +} + +function formatObject(value: any) { + const type =value.type; + if(type==='file') { + const fileName = value.path.split('/').pop()?.split('.')[0]; + return {type: "link", path: value.path, display: fileName}; + } + + if ("path" in value && "display" in value) { + console.log("link value", value); + return {type: "link", path: value.path, display: value.display}; + } + if ("ts" in value) { + const display= dayjs(value.ts).format("YYYY-MM-DD HH:mm:ss"); + return {type: "datetime", display: display}; + } +} diff --git a/src/pages/QueryDashView.tsx b/src/pages/QueryDashView.tsx new file mode 100644 index 0000000..d153921 --- /dev/null +++ b/src/pages/QueryDashView.tsx @@ -0,0 +1,40 @@ +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 enUS from 'antd/locale/en_US'; + + +interface QueryDashViewDashs { + app: App; // Obsidian app + source: string; +} + + +const QueryDashView: React.FC = ({app, source}) => { + + + const [sourceType, setSourceType] = React.useState("table"); + + useEffect(() => { + const sourceType = source.toLowerCase().startsWith("list") ? "list" : "table"; + setSourceType(sourceType); + }, []); + + const getView = (app: App, source: string) => { + if (sourceType === "table") { + return ; + } else { + return ; + } + } + + return ( + + {getView(app, source)} + + ) + +}; +export default QueryDashView; diff --git a/src/pages/ReactDemo.tsx b/src/pages/ReactComponent.tsx similarity index 52% rename from src/pages/ReactDemo.tsx rename to src/pages/ReactComponent.tsx index 8da9b7f..871e832 100644 --- a/src/pages/ReactDemo.tsx +++ b/src/pages/ReactComponent.tsx @@ -1,12 +1,11 @@ -import {App, Plugin, MarkdownRenderChild} from 'obsidian'; +import {App, MarkdownRenderChild} from 'obsidian'; import * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import TableDemo from "./TableDemo"; -import AntdTableDemo from "./AntdTableDemo"; import {createRoot, Root} from "react-dom/client"; +import QueryDashView from "./QueryDashView"; +import { Platform } from 'obsidian'; + export default class ReactComponent extends MarkdownRenderChild { - private sampleComponent: React.ReactElement; root: Root; container: HTMLElement; @@ -16,15 +15,12 @@ export default class ReactComponent extends MarkdownRenderChild { } onload() { - // 渲染 React 组件 this.renderReactComponent(); } renderReactComponent() { - // 使用 ReactDOM.render 渲染 React 组件 - // 使用 createRoot 渲染 React 组件 const root = createRoot(this.container); - root.render(); + root.render(); } } diff --git a/src/pages/TableDemo.module.css b/src/pages/TableDemo.module.css deleted file mode 100644 index bdcf80e..0000000 --- a/src/pages/TableDemo.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.container { - all: initial; /* 重置容器内的样式 */ -} diff --git a/src/pages/TableDemo.tsx b/src/pages/TableDemo.tsx deleted file mode 100644 index 4d390a9..0000000 --- a/src/pages/TableDemo.tsx +++ /dev/null @@ -1,306 +0,0 @@ -import {EllipsisOutlined, PlusOutlined} from '@ant-design/icons'; -import type {ActionType, ProColumns} from '@ant-design/pro-components'; -import {ProTable, TableDropdown} from '@ant-design/pro-components'; -import {Button, Dropdown, Space, Tag} from 'antd'; -import {useEffect, useRef} from 'react'; -import React from 'react'; -import request from 'umi-request'; -import { getAPI} from 'obsidian-dataview'; -import {App, Workspace} from "obsidian"; -import {continuedIndent} from "@codemirror/language"; - -export const waitTimePromise = async (time: number = 100) => { - return new Promise((resolve) => { - setTimeout(() => { - resolve(true); - }, time); - }); -}; - -export const waitTime = async (time: number = 100) => { - await waitTimePromise(time); -}; - -type GithubIssueItem = { - url: string; - id: number; - number: number; - title: string; - labels: { - name: string; - color: string; - }[]; - state: string; - comments: number; - created_at: string; - updated_at: string; - closed_at?: string; - header: { - path: string; - }, - text: string; -}; - - -interface MyReactComponentProps { - app: any; // Obsidian的App对象 - source: string; // 当前文件的路径 -} - -const MyReactComponent: React.FC = ({app, source}) => { - const actionRef = useRef(); - - const dv = getAPI(app); - - const [data, setData] = React.useState([]); - - function parseTableResult(value: any, params:any): Array> { - const headers: string[] = value.headers; - - const rows: Array> = []; - console.log("params1", params); - - - - value.values.forEach((row) => { - const values: Record = {}; - - let flag = [] - headers.forEach((header, index) => { - - const searchParam = params[header]; - console.log("searchParam",searchParam); - - - const value = row[index]; - // 如果字符串value中不包含searchParam,则排除 - if(searchParam){ - flag[index] = value.indexOf(searchParam) > -1; - }else{ - flag[index] = true; - } - - console.log("header",header) - console.log("row.value",value) - values[header] = value; - }); - rows.push(values); - }); - - - // params参数去除current和pageSize - delete params.current; - delete params.pageSize; - - const filteredData = rows.filter(item => { - return Object.keys(params).every(key => { - console.log("key", key); - console.log("params[key]", params[key]); - console.log("item[key]", item[key]); - if (params[key]) { - // If item[key] is empty, return false directly. - if (item[key]) { - return item[key].toString().toLowerCase().includes(params[key].toString().toLowerCase()); - }else{ - return true; - } - } - return true; - }); - }); - - - console.log("rows2", rows); - console.log("keyParams", Object.keys(params)); - console.log("params2", params); - - return filteredData; - } - - const executeTableQuery=async (dvApi: any,source:any,params:any) => { - // 定义查询语句 - const sql = source; - - // 使用 Dataview API 执行查询 - const queryResult = await dvApi.query(sql); - console.log("Query Result:", queryResult); - console.log("Query Result1:", queryResult.successful); - console.log("Query Result2:", queryResult.value); - - const tableData:any =parseTableResult(queryResult.value,params); - - - console.log("tableData:", tableData); - - // 处理查询结果 - if (queryResult.successful) { - const tableData = queryResult.value; - console.log("Query Result:", tableData); - // 在这里可以对查询结果进行进一步处理 - tableData.values.forEach(row => { - console.log("Row:", row); - }); - } else { - console.error("Query failed:", queryResult.error); - } - return tableData; - - } - - useEffect(() => { - - //executeTableQuery(dv,source,{}); - - // const data = dv.pages().file.lists.values; - //console.log("data", data); - // setData(data); - }, []); - - const columns: ProColumns[] = [ - { - dataIndex: 'index', - valueType: 'indexBorder', - width: 48, - }, - { - title: '标题', - dataIndex: 'file.name', - render: (_, record) => ( - console.log("record,{},text", _,record), - { - // 调用 Obsidian API - const file = app.vault.getAbstractFileByPath(record.File.path); - if (file) { - app.workspace.activeLeaf?.openFile(file); - } - }}> - {record.File.path} - - - ), - }, - { - title: '文本', - dataIndex: 'text', - filters: true, - onFilter: true, - ellipsis: true, - render: (_, record) => ( - console.log("record", record), - - {record.text} - - - ), - }, - { - title: '文本1', - dataIndex: 'file.ctime', - filters: true, - onFilter: true, - ellipsis: true, - render: (_, record) => ( - console.log("record", record), - - {record.text} - - - ), - }, - ]; - - return ( - - columns={columns} - actionRef={actionRef} - cardBordered - editable={{ - type: 'multiple', - }} - // dataSource={data} - columnsState={{ - persistenceKey: 'pro-table-singe-demos', - persistenceType: 'localStorage', - defaultValue: { - option: {fixed: 'right', disable: true}, - }, - onChange(value) { - console.log('value: ', value); - }, - }} - rowKey="title" - search={{ - labelWidth: 'auto', - }} - options={{ - setting: { - listsHeight: 400, - }, - }} - form={{ - // 由于配置了 transform,提交的参数与定义的不同这里需要转化一下 - syncToUrl: (values, type) => { - if (type === 'get') { - return { - ...values, - created_at: [values.startTime, values.endTime], - }; - } - return values; - }, - }} - pagination={{ - pageSize: 5, - onChange: (page) => console.log(page), - }} - dateFormatter="string" - headerTitle="高级表格" - request={async (params, sort, filter) => { - console.log('params:', params); - const response = await executeTableQuery(dv,source,params); - console.log("response1",response); - return { - data: response, - success: true, - total: response.length, - }; - }} - toolBarRender={() => [ - , - - - , - ]} - /> - ); -}; -export default MyReactComponent; diff --git a/src/pages/app.tsx b/src/pages/app.tsx deleted file mode 100644 index bb2b25f..0000000 --- a/src/pages/app.tsx +++ /dev/null @@ -1,16 +0,0 @@ -// 运行时配置 - -// 全局初始化数据配置,用于 Layout 用户信息和权限初始化 -// 更多信息见文档:https://umijs.org/docs/api/runtime-config#getinitialstate -export async function getInitialState(): Promise<{ name: string }> { - return { name: '@umijs/max' }; -} - -export const layout = () => { - return { - logo: 'https://img.alicdn.com/tfs/TB1YHEpwUT1gK0jSZFhXXaAtVXa-28-27.svg', - menu: { - locale: false, - }, - }; -}; diff --git a/src/pages/calendarview/todo b/src/pages/calendarview/todo new file mode 100644 index 0000000..e69de29 diff --git a/src/pages/index.tsx b/src/pages/index.tsx deleted file mode 100644 index 5ce38e8..0000000 --- a/src/pages/index.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; - -const IndexPage: React.FC = () => { - - return ( -
-

Hello Umi

-
- ); -}; - -export default IndexPage; diff --git a/src/pages/kanbanview/todo b/src/pages/kanbanview/todo new file mode 100644 index 0000000..e69de29 diff --git a/src/pages/listview/ListView.tsx b/src/pages/listview/ListView.tsx new file mode 100644 index 0000000..3c77f1b --- /dev/null +++ b/src/pages/listview/ListView.tsx @@ -0,0 +1,111 @@ +import {ProList} from '@ant-design/pro-components'; +import React from "react"; +import {formatValue} from "../GenerateColumns"; +import {getAPI} from 'obsidian-dataview'; +import {ViewProps} from "../../models/ViewProps"; + + +const ListView: React.FC = ({app, source}) => { + + const [metas, setMetas] = React.useState({}); + + const dv = getAPI(app); + + function parseTableResult(value: any, params: any): Array> { + const headers: string[] = value.headers; + const rows: Array> = []; + value.values.forEach((row: any) => { + const values: Record = {}; + 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); + }); + // params参数去除current和pageSize + delete params.current; + delete params.pageSize; + + console.log("list rows", rows); + + const filteredData = rows.filter(item => { + return Object.keys(params).every(key => { + + if (params[key]) { + // If item[key] is empty, return false directly. + if (item[key]) { + return item[key].toString().toLowerCase().includes(params[key].toString().toLowerCase()); + } else { + return true; + } + } + return true; + }); + }); + + + return filteredData; + } + + function replaceFirstWord(source: string): string { + // 将句子按空格分割成单词数组 + const words = source.split(' '); + + // 如果第一个单词是"List",则替换为"Table" + if (words[0] === 'list') { + words[0] = 'table'; + } + + // 将单词数组重新组合成句子 + return words.join(' '); + } + + const executeTableQuery = async (dvApi: any, source: any, params: any) => { + // 定义查询语句 + // source是一个句子,将source的第一个单词List转成Table + + const sql = replaceFirstWord(source); + // 使用 Dataview API 执行查询 + const queryResult = await dvApi.query(sql); + const tableData: any = parseTableResult(queryResult.value, params); + return {tableData: tableData}; + + } + + + return { + // console.log('params:', params); + const response = await executeTableQuery(dv, source, params); + const {tableData} = response; + // console.log("listData", tableData); + return Promise.resolve({ + data: tableData, + success: true, + total: tableData.length, + // columns: columns, + }); + }} + pagination={{ + pageSize: 5, + }} + showActions="hover" + metas={{ + title: { + title: 'title', + render: (_, record: any) => { + console.log("list record", record); + return {record.File.path}; + } + }, + }} + /> +} + +export default ListView; diff --git a/src/pages/tableview/TableView.tsx b/src/pages/tableview/TableView.tsx new file mode 100644 index 0000000..7dd4057 --- /dev/null +++ b/src/pages/tableview/TableView.tsx @@ -0,0 +1,182 @@ +import type {ActionType, ProColumns} from '@ant-design/pro-components'; +import {ProTable} from '@ant-design/pro-components'; +import {List, Tag} from 'antd'; +import React, {useEffect, useRef} from 'react'; +import {getAPI} from 'obsidian-dataview'; +import {formatValue} from "../GenerateColumns"; +import {App, TFile} from "obsidian"; +import {ViewProps} from "../../models/ViewProps"; + + +const TableView: React.FC = ({app, source}) => { + + const actionRef = useRef(); + const [columns, setColumns] = React.useState[]>([]); + + const dv = getAPI(app); + + const createColumns = (headers: string[]) => { + let columns = []; + const firstColumn: ProColumns = { + dataIndex: 'index', + valueType: 'indexBorder', + title: 'index', + sorter: true, + }; + columns.push(firstColumn); + const dynamicColumns: ProColumns[] = headers.map((header) => { + return { + title: header, + dataIndex: header, + sorter: (a, b) => a[header].display.toString().localeCompare(b[header].display.toString()), + render: (_, record) => { + const {type, path, display} = record[header]; + if (type === "datetime") { + return display; + } + if (type === "link") { + return { + const file = app.vault.getAbstractFileByPath(path); + if (file && file instanceof TFile) { + const leaf = app.workspace.getLeaf(); + leaf.openFile(file); + } + }}> + {display} + + } + if (type === "array") { + return display.map((v: any) => { + if (typeof v === "object") { + return + { + const file = app.vault.getAbstractFileByPath(v.path); + if (file && file instanceof TFile) { + const leaf = app.workspace.getLeaf(); + leaf.openFile(file); + } + }}> + {v.display} + + + } else { + // tags + return + {v} + + } + }) + } + return display.toString(); + } + }; + }); + columns.push(...dynamicColumns); + return columns; + } + + function parseTableResult(value: any, params: any): Array> { + const headers: string[] = value.headers; + console.log("headers", headers); + // console.log("valueData", value); + + const rows: Array> = []; + + value.values.forEach((row: any) => { + const values: Record = {}; + headers.forEach((header, index) => { + const value = row[index]; + const resValue = formatValue(value); + values[header] = resValue; + }); + rows.push(values); + }); + + delete params.current; + delete params.pageSize; + + // console.log("rowsData", rows); + // console.log("paramsData", params); + + const filteredData = rows.filter(item => { + return Object.keys(params).every(key => { + if (params[key]) { + // console.log("param1 key", key); + // If item[key] is empty, return false directly. + if (item[key]) { + // console.log("item[key]", item[key]); + return item[key].display.toString().toLowerCase().includes(params[key].toString().toLowerCase()); + } else { + return true; + } + } + return true; + }); + }); + + return filteredData; + } + + const executeTableQuery = async (dvApi: any, source: any, params: any) => { + const sql = source; + + const queryResult = await dvApi.query(sql); + + const tableData: any = parseTableResult(queryResult.value, params); + + const headers: string[] = queryResult.value.headers; + const columns = createColumns(headers); + + return {tableData: tableData, columns: columns}; + } + + useEffect(() => { + console.log("source", source); + }, []); + + + return ( + { + console.log('params:', params); + const response = await executeTableQuery(dv, source, params); + const {tableData, columns} = response; + + setColumns(columns); + return Promise.resolve({ + data: tableData, + success: true, + total: tableData.length, + // columns: columns, + }); + }} + /> + ); +}; +export default TableView; diff --git a/src/sampleView.ts b/src/sampleView.ts deleted file mode 100644 index 4db310f..0000000 --- a/src/sampleView.ts +++ /dev/null @@ -1,49 +0,0 @@ -import {HoverPopover, ItemView, WorkspaceLeaf} from 'obsidian'; -import React from 'react'; -import ReactDOM from 'react-dom'; -import type MyPlugin from './main'; -import { Root,createRoot } from 'react-dom/client'; - -import FormDemo from "./pages/FormDemo"; -import TableDemo from "./pages/TableDemo"; -import AntdTableDemo from "./pages/AntdTableDemo"; - - -export class SampleView extends ItemView { - plugin: MyPlugin; - hoverPopover: HoverPopover | null; - private sampleComponent: React.ReactElement; - root: Root; - - constructor(leaf: WorkspaceLeaf, plugin: MyPlugin) { - super(leaf); - this.plugin = plugin; - } - - getDisplayText(): string { - // TODO: Make this interactive: Either the active workspace or the local graph - return 'Memos'; - } - - getIcon(): string { - return 'Memos'; - } - - getViewType(): string { - return "umi-view"; - } - - - async onOpen(): Promise { - - this.sampleComponent = React.createElement(AntdTableDemo); - this.root = createRoot(this.contentEl as HTMLElement); - this.root.render(this.sampleComponent); - - } - - async onClose() { - // Nothing to clean up. - this.root.unmount(); - } -}