mirror of
https://github.com/liufree/obsidian-querydash.git
synced 2026-07-22 05:41:49 +00:00
feat: support task
This commit is contained in:
parent
2a1324d130
commit
da991daa6a
3 changed files with 281 additions and 32 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "querydash",
|
||||
"name": "QueryDash",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.6",
|
||||
"minAppVersion": "1.8.0",
|
||||
"description": "Refer to Dataview and add search, sorting, and pagination functions, just like Notion.",
|
||||
"author": "lwx",
|
||||
|
|
|
|||
244
src/components/EditableTable.tsx
Normal file
244
src/components/EditableTable.tsx
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
import {PlusOutlined} from '@ant-design/icons';
|
||||
import type {ActionType, ProColumns} from '@ant-design/pro-components';
|
||||
import {
|
||||
EditableProTable,
|
||||
ProCard,
|
||||
ProFormField,
|
||||
} from '@ant-design/pro-components';
|
||||
import type {InputRef} from 'antd';
|
||||
import {Button, Form, Input, Space, Tag} from 'antd';
|
||||
import React, {useRef, useState} from 'react';
|
||||
|
||||
const waitTime = (time: number = 100) => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(true);
|
||||
}, time);
|
||||
});
|
||||
};
|
||||
|
||||
const TagList: React.FC<{
|
||||
value?: {
|
||||
key: string;
|
||||
label: string;
|
||||
}[];
|
||||
onChange?: (
|
||||
value: {
|
||||
key: string;
|
||||
label: string;
|
||||
}[],
|
||||
) => void;
|
||||
}> = ({value, onChange}) => {
|
||||
const ref = useRef<InputRef | null>(null);
|
||||
const [newTags, setNewTags] = useState<
|
||||
{
|
||||
key: string;
|
||||
label: string;
|
||||
}[]
|
||||
>([]);
|
||||
const [inputValue, setInputValue] = useState<string>('');
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setInputValue(e.target.value);
|
||||
};
|
||||
|
||||
const handleInputConfirm = () => {
|
||||
let tempsTags = [...(value || [])];
|
||||
if (
|
||||
inputValue &&
|
||||
tempsTags.filter((tag) => tag.label === inputValue).length === 0
|
||||
) {
|
||||
tempsTags = [
|
||||
...tempsTags,
|
||||
{key: `new-${tempsTags.length}`, label: inputValue},
|
||||
];
|
||||
}
|
||||
onChange?.(tempsTags);
|
||||
setNewTags([]);
|
||||
setInputValue('');
|
||||
};
|
||||
|
||||
return (
|
||||
<Space>
|
||||
{(value || []).concat(newTags).map((item) => (
|
||||
<Tag key={item.key}>{item.label}</Tag>
|
||||
))}
|
||||
<Input
|
||||
ref={ref}
|
||||
type="text"
|
||||
size="small"
|
||||
style={{width: 78}}
|
||||
value={inputValue}
|
||||
onChange={handleInputChange}
|
||||
onBlur={handleInputConfirm}
|
||||
onPressEnter={handleInputConfirm}
|
||||
/>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
type DataSourceType = {
|
||||
id: React.Key;
|
||||
title?: string;
|
||||
labels?: {
|
||||
key: string;
|
||||
label: string;
|
||||
}[];
|
||||
state?: string;
|
||||
created_at?: number;
|
||||
children?: DataSourceType[];
|
||||
};
|
||||
|
||||
const defaultData: DataSourceType[] = [
|
||||
{
|
||||
id: 624748504,
|
||||
title: '活动名称一',
|
||||
labels: [{key: 'woman', label: '川妹子'}],
|
||||
state: 'open',
|
||||
created_at: 1590486176000,
|
||||
},
|
||||
{
|
||||
id: 624691229,
|
||||
title: '活动名称二',
|
||||
labels: [{key: 'man', label: '西北汉子'}],
|
||||
state: 'closed',
|
||||
created_at: 1590481162000,
|
||||
},
|
||||
];
|
||||
|
||||
const columns: ProColumns<DataSourceType>[] = [
|
||||
{
|
||||
title: '活动名称',
|
||||
dataIndex: 'title',
|
||||
formItemProps: {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '此项为必填项',
|
||||
},
|
||||
],
|
||||
},
|
||||
width: '30%',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'state',
|
||||
dataIndex: 'state',
|
||||
valueType: 'select',
|
||||
valueEnum: {
|
||||
all: {text: '全部', status: 'Default'},
|
||||
open: {
|
||||
text: '未解决',
|
||||
status: 'Error',
|
||||
},
|
||||
closed: {
|
||||
text: '已解决',
|
||||
status: 'Success',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '标签',
|
||||
dataIndex: 'labels',
|
||||
width: '20%',
|
||||
formItemProps: {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '此项为必填项',
|
||||
},
|
||||
],
|
||||
},
|
||||
renderFormItem: (_, {isEditable}) => {
|
||||
return isEditable ? <TagList/> : <Input/>;
|
||||
},
|
||||
render: (_, row) =>
|
||||
row?.labels?.map((item) => <Tag key={item.key}>{item.label}</Tag>),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
valueType: 'option',
|
||||
width: 250,
|
||||
render: (text, record, _, action) => [
|
||||
<a
|
||||
key="editable"
|
||||
onClick={() => {
|
||||
action?.startEditable?.(record.id);
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</a>,
|
||||
<EditableProTable.RecordCreator
|
||||
key="copy"
|
||||
record={{
|
||||
...record,
|
||||
id: (Math.random() * 1000000).toFixed(0),
|
||||
}}
|
||||
>
|
||||
<a>复制此项到末尾</a>
|
||||
</EditableProTable.RecordCreator>,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default () => {
|
||||
const actionRef = useRef<ActionType>();
|
||||
const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]);
|
||||
const [dataSource, setDataSource] = useState<readonly DataSourceType[]>([]);
|
||||
const [form] = Form.useForm();
|
||||
return (
|
||||
<>
|
||||
<EditableProTable<DataSourceType>
|
||||
rowKey="id"
|
||||
scroll={{
|
||||
x: 960,
|
||||
}}
|
||||
actionRef={actionRef}
|
||||
headerTitle="可编辑表格"
|
||||
maxLength={5}
|
||||
// 关闭默认的新建按钮
|
||||
recordCreatorProps={{
|
||||
position: 'top',
|
||||
record: () => ({id: (Math.random() * 1000000).toFixed(0)}),
|
||||
}}
|
||||
columns={columns}
|
||||
request={async () => ({
|
||||
data: defaultData,
|
||||
total: 3,
|
||||
success: true,
|
||||
})}
|
||||
value={dataSource}
|
||||
onChange={setDataSource}
|
||||
search={{
|
||||
labelWidth: 'auto',
|
||||
}}
|
||||
pagination={{
|
||||
pageSize: 5,
|
||||
onChange: (page) => console.log(page),
|
||||
}}
|
||||
editable={{
|
||||
form,
|
||||
editableKeys,
|
||||
onSave: async () => {
|
||||
await waitTime(2000);
|
||||
},
|
||||
onChange: setEditableRowKeys,
|
||||
actionRender: (row, config, dom) => [dom.save, dom.cancel],
|
||||
}}
|
||||
/>
|
||||
<ProCard title="表格数据" headerBordered collapsible defaultCollapsed>
|
||||
<ProFormField
|
||||
ignoreFormItem
|
||||
fieldProps={{
|
||||
style: {
|
||||
width: '100%',
|
||||
},
|
||||
}}
|
||||
mode="read"
|
||||
valueType="jsonCode"
|
||||
text={JSON.stringify(dataSource)}
|
||||
/>
|
||||
</ProCard>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
@ -5,6 +5,7 @@ import {getAPI, STask} from "obsidian-dataview";
|
|||
import {ViewProps} from "../../models/ViewProps";
|
||||
import {TFile, Vault} from "obsidian";
|
||||
import EditableText from "./EditableText";
|
||||
import EditableTable from "../../components/EditableTable";
|
||||
|
||||
const {Text, Link} = Typography;
|
||||
|
||||
|
|
@ -22,7 +23,7 @@ const KanbanView: React.FC<ViewProps> = ({app, source}) => {
|
|||
leaf.openFile(file);
|
||||
}
|
||||
}}
|
||||
>
|
||||
>
|
||||
<Text
|
||||
style={display ? {maxWidth: 200, color: '#1890ff', cursor: 'pointer'} : {
|
||||
color: '#1890ff',
|
||||
|
|
@ -172,36 +173,40 @@ const KanbanView: React.FC<ViewProps> = ({app, source}) => {
|
|||
}, [app]);
|
||||
|
||||
return (
|
||||
<ProCard ghost gutter={8}>
|
||||
{columns.map((column) => (
|
||||
<ProCard key={column.title} title={column.title} bordered>
|
||||
{column.items.map((item: any, index: number) => (
|
||||
<>
|
||||
<ProCard key={index} bordered>
|
||||
<Row>
|
||||
<Col span={1}>
|
||||
<Checkbox
|
||||
onChange={(e) => onChange(e.target.checked, item.text, item)}
|
||||
checked={item.checked}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={18}>
|
||||
<EditableText
|
||||
app={app}
|
||||
item={item}
|
||||
onSave={(newText) => {
|
||||
console.log("保存的新文本:", newText);
|
||||
onChange(item.checked, newText, item);
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</ProCard>
|
||||
</>
|
||||
))}
|
||||
</ProCard>
|
||||
))}
|
||||
</ProCard>
|
||||
<>
|
||||
<EditableTable></EditableTable>
|
||||
|
||||
<ProCard ghost gutter={8}>
|
||||
{columns.map((column) => (
|
||||
<ProCard key={column.title} title={column.title} bordered>
|
||||
{column.items.map((item: any, index: number) => (
|
||||
<>
|
||||
<ProCard key={index} bordered>
|
||||
<Row>
|
||||
<Col span={1}>
|
||||
<Checkbox
|
||||
onChange={(e) => onChange(e.target.checked, item.text, item)}
|
||||
checked={item.checked}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={18}>
|
||||
<EditableText
|
||||
app={app}
|
||||
item={item}
|
||||
onSave={(newText) => {
|
||||
console.log("保存的新文本:", newText);
|
||||
onChange(item.checked, newText, item);
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</ProCard>
|
||||
</>
|
||||
))}
|
||||
</ProCard>
|
||||
))}
|
||||
</ProCard>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue