chore: export

This commit is contained in:
lencx
2023-01-14 23:31:12 +08:00
parent a2fcfa3b89
commit ae2c56805c
15 changed files with 264 additions and 69 deletions

View File

@@ -1,4 +1,7 @@
import { useState, useCallback } from 'react';
import { FC, useState, useCallback } from 'react';
import { Input } from 'antd';
import { DISABLE_AUTO_COMPLETE } from '@/utils';
export default function useColumns(columns: any[] = []) {
const [opType, setOpType] = useState('');
@@ -41,4 +44,40 @@ export default function useColumns(columns: any[] = []) {
setExtra,
opExtra,
};
}
}
interface EditRowProps {
rowKey: string;
row: Record<string, any>;
actions: any;
}
export const EditRow: FC<EditRowProps> = ({ rowKey, row, actions }) => {
const [isEdit, setEdit] = useState(false);
const [val, setVal] = useState(row[rowKey]);
const handleEdit = () => {
setEdit(true);
};
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
setVal(e.target.value)
};
const handleSave = () => {
setEdit(false);
row[rowKey] = val;
actions?.setRecord(row, 'rowedit')
};
return isEdit
? (
<Input.TextArea
value={val}
rows={1}
onChange={handleChange}
{...DISABLE_AUTO_COMPLETE}
onPressEnter={handleSave}
/>
)
: (
<div className='rowedit' onClick={handleEdit}>{val}</div>
);
};

View File

@@ -1,6 +1,6 @@
import { useState } from 'react';
import { readJSON } from '@/utils';
import { readJSON, writeJSON } from '@/utils';
import useInit from '@/hooks/useInit';
export default function useJson<T>(file: string) {
@@ -11,7 +11,12 @@ export default function useJson<T>(file: string) {
setData(data);
};
const updateJson = async (data: any) => {
await writeJSON(file, data);
await refreshJson();
};
useInit(refreshJson);
return { json, refreshJson };
return { json, refreshJson, updateJson };
}

View File

@@ -4,14 +4,35 @@ import type { TableRowSelection } from 'antd/es/table/interface';
import { safeKey } from '@/hooks/useData';
export default function useTableRowSelection() {
type rowSelectionOptions = {
key: 'id' | string;
rowType: 'id' | 'row' | 'all';
}
export function useTableRowSelection(options: Partial<rowSelectionOptions> = {}) {
const { key = 'id', rowType = 'id' } = options;
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const [selectedRowIDs, setSelectedRowIDs] = useState<string[]>([]);
const [selectedRows, setSelectedRows] = useState<Record<string|symbol, any>[]>([]);
const onSelectChange = (newSelectedRowKeys: React.Key[], selectedRows: Record<string|symbol, any>) => {
const keys = selectedRows.map((i: any) => i[safeKey]);
setSelectedRowIDs(keys);
const onSelectChange = (newSelectedRowKeys: React.Key[], newSelectedRows: Record<string|symbol, any>[]) => {
const keys = newSelectedRows.map((i: any) => i[safeKey] || i[key]);
setSelectedRowKeys(newSelectedRowKeys);
if (rowType === 'id') {
setSelectedRowIDs(keys);
}
if (rowType === 'row') {
setSelectedRows(newSelectedRows);
}
if (rowType === 'all') {
setSelectedRowIDs(keys);
setSelectedRows(newSelectedRows);
}
};
const rowReset = () => {
setSelectedRowKeys([]);
setSelectedRowIDs([]);
setSelectedRows([]);
};
const rowSelection: TableRowSelection<Record<string, any>> = {
@@ -24,14 +45,14 @@ export default function useTableRowSelection() {
],
};
return { rowSelection, selectedRowIDs };
return { rowSelection, selectedRowIDs, selectedRows, rowReset };
}
export const TABLE_PAGINATION = {
hideOnSinglePage: true,
showSizeChanger: true,
showQuickJumper: true,
defaultPageSize: 5,
defaultPageSize: 10,
pageSizeOptions: [5, 10, 15, 20],
showTotal: (total: number) => <span>Total {total} items</span>,
};

19
src/main.scss vendored
View File

@@ -31,10 +31,27 @@ html, body {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.ellipsis-line {
display: inline-block;
width: 180px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.rowedit {
padding: 2px 5px;
&:hover {
box-shadow: 0 0 2px rgba(237, 122, 60, 0.8);
border-radius: 4px;
}
}
.chat-add-btn {
margin-bottom: 5px;
}

View File

@@ -1,37 +1,57 @@
import { useState } from 'react';
import { Tag, Space, Popconfirm } from 'antd';
import { path, shell } from '@tauri-apps/api';
import { fmtDate } from '@/utils';
import { EditRow } from '@/hooks/useColumns';
import useInit from '@/hooks/useInit';
import { fmtDate, chatRoot } from '@/utils';
const colorMap: any = {
pdf: 'blue',
png: 'orange',
}
export const syncColumns = () => [
export const downloadColumns = () => [
{
title: 'Name',
dataIndex: 'name',
fixed: 'left',
key: 'name',
width: 240,
render: (_: string, row: any, actions: any) => (
<EditRow rowKey="name" row={row} actions={actions} />
),
},
{
title: 'Extension',
dataIndex: 'ext',
key: 'ext',
width: 120,
render: (v: string) => <Tag color={colorMap[v]}>{v}</Tag>,
},
{
title: 'Path',
dataIndex: 'path',
key: 'path',
width: 200,
render: (_: string, row: any) => <RenderPath row={row} />,
},
{
title: 'Created',
dataIndex: 'created',
key: 'created',
width: 150,
render: fmtDate,
},
{
title: 'Action',
fixed: 'right',
width: 150,
render: (_: any, row: any, actions: any) => {
return (
<Space>
<a onClick={() => actions.setRecord(row, 'view')}>View</a>
<a onClick={() => actions.setRecord(row, 'preview')}>Preview</a>
<Popconfirm
title="Are you sure to delete this file?"
onConfirm={() => actions.setRecord(row, 'delete')}
@@ -46,9 +66,15 @@ export const syncColumns = () => [
}
];
// {
// id: '',
// name: '',
// type: '.png',
// created: '2022.01.01',
// }
const RenderPath = ({ row }: any) => {
const [filePath, setFilePath] = useState('');
useInit(async () => {
setFilePath(await getPath(row));
})
return <a onClick={() => shell.open(filePath)}>{filePath}</a>;
};
export const getPath = async (row: any) => {
const isImg = ['png'].includes(row?.ext);
return await path.join(await chatRoot(), 'download', isImg ? 'img' : row.ext, row.id) + `.${row.ext}`;
}

View File

@@ -1,12 +0,0 @@
.chat-table-tip, .chat-table-btns {
display: flex;
justify-content: space-between;
}
.chat-table-btns {
margin-bottom: 5px;
.num {
margin-left: 10px;
}
}

View File

@@ -1,14 +1,14 @@
import { useEffect, useState } from 'react';
import { Table, Modal } from 'antd';
import { path, shell, fs } from '@tauri-apps/api';
import { Table, Modal, Popconfirm, Button, message } from 'antd';
import { invoke, path, shell, fs } from '@tauri-apps/api';
import useInit from '@/hooks/useInit';
import useJson from '@/hooks/useJson';
import useData from '@/hooks/useData';
import useColumns from '@/hooks/useColumns';
import useTable, { TABLE_PAGINATION } from '@/hooks/useTable';
import { useTableRowSelection, TABLE_PAGINATION } from '@/hooks/useTable';
import { chatRoot, CHAT_DOWNLOAD_JSON } from '@/utils';
import { syncColumns } from './config';
import './index.scss';
import { downloadColumns } from './config';
function renderFile(buff: Uint8Array, type: string) {
const renderType = {
@@ -19,52 +19,124 @@ function renderFile(buff: Uint8Array, type: string) {
}
export default function SyncPrompts() {
const { rowSelection, selectedRowIDs } = useTable();
const { columns, ...opInfo } = useColumns(syncColumns());
const [downloadPath, setDownloadPath] = useState('');
const { json } = useJson<any[]>(CHAT_DOWNLOAD_JSON);
const [source, setSource] = useState('');
const [isVisible, setVisible] = useState(false);
const { opData, opInit, opReplace, opSafeKey } = useData([]);
const { columns, ...opInfo } = useColumns(downloadColumns());
const { rowSelection, selectedRows, rowReset } = useTableRowSelection({ rowType: 'row' });
const { json, refreshJson, updateJson } = useJson<any[]>(CHAT_DOWNLOAD_JSON);
const selectedItems = rowSelection.selectedRowKeys || [];
useInit(async () => {
const file = await path.join(await chatRoot(), 'chat.download.json');
const file = await path.join(await chatRoot(), CHAT_DOWNLOAD_JSON);
setDownloadPath(file);
});
useEffect(() => {
if (!json || json.length <= 0) return;
opInit(json);
}, [json?.length]);
useEffect(() => {
if (!opInfo.opType) return;
(async () => {
const record = opInfo?.opRecord;
const isImg = ['png'].includes(record?.ext);
const file = await path.join(await chatRoot(), 'download', isImg ? 'img' : record?.ext, `${record?.id}.${record?.ext}`);
if (opInfo.opType === 'view') {
if (opInfo.opType === 'preview') {
const data = await fs.readBinaryFile(file);
const sourceData = renderFile(data, record?.ext);
setSource(sourceData);
setVisible(true);
return;
}
if (opInfo.opType === 'file') {
await shell.open(file);
}
if (opInfo.opType === 'delete') {
await fs.removeFile(file);
await handleRefresh();
}
if (opInfo.opType === 'rowedit') {
const data = opReplace(opInfo?.opRecord?.[opSafeKey], opInfo?.opRecord);
await updateJson(data);
message.success('Name has been changed!');
}
opInfo.resetRecord();
})()
}, [opInfo.opType])
const handleDelete = async () => {
if (opData?.length === selectedRows.length) {
const downloadDir = await path.join(await chatRoot(), 'download');
await fs.removeDir(downloadDir, { recursive: true });
await handleRefresh();
rowReset();
message.success('All files have been cleared!');
return;
}
const rows = selectedRows.map(async (i) => {
const isImg = ['png'].includes(i?.ext);
const file = await path.join(await chatRoot(), 'download', isImg ? 'img' : i?.ext, `${i?.id}.${i?.ext}`);
await fs.removeFile(file);
return file;
})
Promise.all(rows).then(async () => {
await handleRefresh();
message.success('All files selected are cleared!');
});
};
const handleRefresh = async () => {
await invoke('download_list', { pathname: CHAT_DOWNLOAD_JSON });
refreshJson();
};
const handleCancel = () => {
setVisible(false);
opInfo.resetRecord();
};
return (
<div>
<div className="chat-table-btns">
<div>
{selectedItems.length > 0 && (
<>
<Popconfirm
overlayStyle={{ width: 250 }}
title="Sync will overwrite the previous data, confirm to sync?"
placement="topLeft"
onConfirm={handleDelete}
okText="Yes"
cancelText="No"
>
<Button>Batch delete</Button>
</Popconfirm>
<span className="num">Selected {selectedItems.length} items</span>
</>
)}
</div>
</div>
<div className="chat-table-tip">
<div className="chat-file-path">
<div>PATH: <a onClick={() => shell.open(downloadPath)} title={downloadPath}>{downloadPath}</a></div>
</div>
</div>
<Table
rowKey="name"
rowKey="id"
columns={columns}
scroll={{ x: 'auto' }}
dataSource={json}
scroll={{ x: 800 }}
dataSource={opData}
rowSelection={rowSelection}
pagination={TABLE_PAGINATION}
/>
<Modal
open={isVisible}
onCancel={() => setVisible(false)}
title={<div>{opInfo?.opRecord?.name || ''}</div>}
onCancel={handleCancel}
footer={false}
destroyOnClose
>

View File

@@ -1,4 +1,4 @@
import { Switch, Tag, Tooltip } from 'antd';
import { Table, Switch, Tag } from 'antd';
import { genCmd } from '@/utils';
@@ -35,13 +35,14 @@ export const syncColumns = () => [
<Switch checked={v} onChange={(v) => action.setRecord({ ...row, enable: v }, 'enable')} />
),
},
Table.EXPAND_COLUMN,
{
title: 'Prompt',
dataIndex: 'prompt',
key: 'prompt',
// width: 300,
render: (v: string) => (
<Tooltip overlayInnerStyle={{ width: 350 }} title={v}><span className="chat-prompts-val">{v}</span></Tooltip>
<span className="chat-prompts-val">{v}</span>
),
},
];

View File

@@ -6,7 +6,7 @@ import useInit from '@/hooks/useInit';
import useData from '@/hooks/useData';
import useColumns from '@/hooks/useColumns';
import useChatModel, { useCacheModel } from '@/hooks/useChatModel';
import useTable, { TABLE_PAGINATION } from '@/hooks/useTable';
import { useTableRowSelection, TABLE_PAGINATION } from '@/hooks/useTable';
import { fmtDate, chatRoot } from '@/utils';
import { syncColumns } from './config';
import './index.scss';
@@ -14,7 +14,7 @@ import './index.scss';
const promptsURL = 'https://github.com/f/awesome-chatgpt-prompts/blob/main/prompts.csv';
export default function SyncPrompts() {
const { rowSelection, selectedRowIDs } = useTable();
const { rowSelection, selectedRowIDs } = useTableRowSelection();
const [jsonPath, setJsonPath] = useState('');
const { modelJson, modelSet } = useChatModel('sync_prompts');
const { modelCacheJson, modelCacheSet } = useCacheModel(jsonPath);
@@ -93,6 +93,7 @@ export default function SyncPrompts() {
dataSource={opData}
rowSelection={rowSelection}
pagination={TABLE_PAGINATION}
expandable={{expandedRowRender: (record) => <div style={{ padding: 10 }}>{record.prompt}</div>}}
/>
</div>
)

View File

@@ -1,4 +1,4 @@
import { Switch, Tag, Tooltip } from 'antd';
import { Switch, Tag, Table } from 'antd';
import { genCmd } from '@/utils';
@@ -37,13 +37,14 @@ export const syncColumns = () => [
<Switch checked={v} onChange={(v) => action.setRecord({ ...row, enable: v }, 'enable')} />
),
},
Table.EXPAND_COLUMN,
{
title: 'Prompt',
dataIndex: 'prompt',
key: 'prompt',
// width: 300,
render: (v: string) => (
<Tooltip overlayInnerStyle={{ width: 350 }} title={v}><span className="chat-prompts-val">{v}</span></Tooltip>
<span className="chat-prompts-val">{v}</span>
),
},
];

View File

@@ -7,7 +7,7 @@ import { shell, path } from '@tauri-apps/api';
import useColumns from '@/hooks/useColumns';
import useData from '@/hooks/useData';
import { useCacheModel } from '@/hooks/useChatModel';
import useTable, { TABLE_PAGINATION } from '@/hooks/useTable';
import { useTableRowSelection, TABLE_PAGINATION } from '@/hooks/useTable';
import { fmtDate, chatRoot } from '@/utils';
import { getPath } from '@/view/model/SyncCustom/config';
import { syncColumns } from './config';
@@ -19,7 +19,7 @@ export default function SyncRecord() {
const [jsonPath, setJsonPath] = useState('');
const state = location?.state;
const { rowSelection, selectedRowIDs } = useTable();
const { rowSelection, selectedRowIDs } = useTableRowSelection();
const { modelCacheJson, modelCacheSet } = useCacheModel(jsonPath);
const { opData, opInit, opReplace, opReplaceItems, opSafeKey } = useData([]);
const { columns, ...opInfo } = useColumns(syncColumns());
@@ -79,6 +79,7 @@ export default function SyncRecord() {
dataSource={opData}
rowSelection={rowSelection}
pagination={TABLE_PAGINATION}
expandable={{expandedRowRender: (record) => <div style={{ padding: 10 }}>{record.prompt}</div>}}
/>
</div>
)

View File

@@ -1,4 +1,4 @@
import { Tag, Switch, Tooltip, Space, Popconfirm } from 'antd';
import { Tag, Switch, Space, Popconfirm, Table } from 'antd';
export const modelColumns = () => [
{
@@ -33,13 +33,14 @@ export const modelColumns = () => [
<Switch checked={v} onChange={(v) => action.setRecord({ ...row, enable: v }, 'enable')} />
),
},
Table.EXPAND_COLUMN,
{
title: 'Prompt',
dataIndex: 'prompt',
key: 'prompt',
width: 300,
render: (v: string) => (
<Tooltip overlayInnerStyle={{ width: 350 }} title={v}><span className="chat-prompts-val">{v}</span></Tooltip>
<span className="chat-prompts-val">{v}</span>
),
},
{

View File

@@ -6,13 +6,13 @@ import useInit from '@/hooks/useInit';
import useData from '@/hooks/useData';
import useChatModel, { useCacheModel } from '@/hooks/useChatModel';
import useColumns from '@/hooks/useColumns';
import useTable, { TABLE_PAGINATION } from '@/hooks/useTable';
import { useTableRowSelection, TABLE_PAGINATION } from '@/hooks/useTable';
import { chatRoot, fmtDate } from '@/utils';
import { modelColumns } from './config';
import UserCustomForm from './Form';
export default function LanguageModel() {
const { rowSelection, selectedRowIDs } = useTable();
const { rowSelection, selectedRowIDs } = useTableRowSelection();
const [isVisible, setVisible] = useState(false);
const [jsonPath, setJsonPath] = useState('');
const { modelJson, modelSet } = useChatModel('user_custom');
@@ -123,6 +123,7 @@ export default function LanguageModel() {
dataSource={opData}
rowSelection={rowSelection}
pagination={TABLE_PAGINATION}
expandable={{expandedRowRender: (record) => <div style={{ padding: 10 }}>{record.prompt}</div>}}
/>
<Modal
open={isVisible}