listpage_cli 0.0.309 → 0.0.310
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/commands/lark/parse-doc.js +16 -21
- package/package.json +1 -1
- package/templates/backend-template/package.json.tmpl +1 -1
- package/templates/frontend-template/package.json.tmpl +2 -2
- package/templates/skills-template/listpage-filter-group/SKILL.md +33 -0
- package/templates/skills-template/listpage-filter-group/api.md +46 -0
- package/templates/skills-template/listpage-filter-group/examples.md +82 -0
|
@@ -133,29 +133,24 @@ async function parseBlocksToMarkdown(blocks, options) {
|
|
|
133
133
|
const token = block.image?.token;
|
|
134
134
|
if (token) {
|
|
135
135
|
const downloadUrl = `https://internal-api-drive-stream.feishu.cn/space/api/box/stream/download/v2/cover/${token}/?fallback_source=1&mount_node_token=${block.block_id}&mount_point=docx_image&policy=equal`;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
options.fs.ensureDir(imageDir);
|
|
144
|
-
}
|
|
145
|
-
// 使用 sdk 提供的下载方法,并直接使用 writeFile 写入
|
|
146
|
-
const fileRes = await options.client.drive.media.download({
|
|
147
|
-
path: { file_token: token },
|
|
148
|
-
});
|
|
149
|
-
// 使用官方推荐的 writeFile 方法直接将文件保存到本地
|
|
150
|
-
await fileRes.writeFile(imagePath);
|
|
151
|
-
content += `${indent}\n\n`;
|
|
152
|
-
}
|
|
153
|
-
catch (e) {
|
|
154
|
-
console.warn(`[Lark Parse] Download image failed for block ${block.block_id}:`, e);
|
|
155
|
-
content += `${indent}\n\n`;
|
|
136
|
+
try {
|
|
137
|
+
const imageFileName = `assets/${token}.png`; // 保存到 assets 目录
|
|
138
|
+
const imagePath = options.fs.resolve(options.outputDir, imageFileName);
|
|
139
|
+
// 确保 assets 目录存在
|
|
140
|
+
const imageDir = options.fs.dirname(imagePath);
|
|
141
|
+
if (!options.fs.exists(imageDir)) {
|
|
142
|
+
options.fs.ensureDir(imageDir);
|
|
156
143
|
}
|
|
144
|
+
// 使用 sdk 提供的下载方法,并直接使用 writeFile 写入
|
|
145
|
+
const fileRes = await options.client.drive.media.download({
|
|
146
|
+
path: { file_token: token },
|
|
147
|
+
});
|
|
148
|
+
// 使用官方推荐的 writeFile 方法直接将文件保存到本地
|
|
149
|
+
await fileRes.writeFile(imagePath);
|
|
150
|
+
content += `${indent}\n\n`;
|
|
157
151
|
}
|
|
158
|
-
|
|
152
|
+
catch (e) {
|
|
153
|
+
console.warn(`[Lark Parse] Download image failed for block ${block.block_id}:`, e);
|
|
159
154
|
content += `${indent}\n\n`;
|
|
160
155
|
}
|
|
161
156
|
}
|
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"react": "^19.2.0",
|
|
14
14
|
"react-dom": "^19.2.0",
|
|
15
|
-
"listpage-next": "~0.0.
|
|
15
|
+
"listpage-next": "~0.0.310",
|
|
16
16
|
"react-router-dom": ">=6.0.0",
|
|
17
17
|
"@ant-design/v5-patch-for-react-19": "~1.0.3",
|
|
18
18
|
"ahooks": "^3.9.5",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"styled-components": "^6.1.19",
|
|
24
24
|
"mobx": "~6.15.0",
|
|
25
25
|
"@ant-design/icons": "~6.0.2",
|
|
26
|
-
"listpage-components": "~0.0.
|
|
26
|
+
"listpage-components": "~0.0.310",
|
|
27
27
|
"lucide-react": "~0.575.0"
|
|
28
28
|
"mobx-react-lite": "~4.1.1"
|
|
29
29
|
},
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: listpage-filter-group
|
|
3
|
+
description: 使用 listpage-components 的 FilterGroup 与 FilterFormOption 搭建筛选区。Use ONLY when the user explicitly requests FilterGroup by name (e.g. 使用 FilterGroup、用 FilterGroup 做筛选). Do NOT apply when the user only mentions listpage, ListPage, or listpage pages—those belong to the separate listpage skill; package name listpage-components is unrelated to this trigger. Do not apply for generic filters unless FilterGroup is explicitly named.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# FilterGroup 使用技能
|
|
7
|
+
|
|
8
|
+
在用户**明确写出 FilterGroup** 时,按本技能与 [api.md](api.md)、[examples.md](examples.md) 实现。
|
|
9
|
+
|
|
10
|
+
## 命名与触发(与 listpage 页面技能区分)
|
|
11
|
+
|
|
12
|
+
组件从包 `listpage-components` 引入,包名含 **listpage**,易与「用 listpage 做列表页」混淆,但这是**两种独立场景**:
|
|
13
|
+
|
|
14
|
+
| 用户说法 | 使用的技能 |
|
|
15
|
+
|----------|------------|
|
|
16
|
+
| 仅 listpage / ListPage / 用 listpage 实现某页 等 | **listpage 页面技能**,**不要**启用本技能 |
|
|
17
|
+
| 明确写出 **FilterGroup**(或「用 FilterGroup 组件」) | **本技能** |
|
|
18
|
+
|
|
19
|
+
若用户从头到尾只提 listpage、未提 FilterGroup,**忽略本技能**,避免与 listpage 技能抢上下文。
|
|
20
|
+
|
|
21
|
+
## 生成流程
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
Task Progress:
|
|
25
|
+
- [ ] 步骤 1:从 listpage-components 导入 FilterGroup、FilterFormOption
|
|
26
|
+
- [ ] 步骤 2:用 FilterFormOption[] 声明字段(name、label、component、colSpan、formItemProps);`component` 可为任意能按注入的 `value`/`onChange`(或 `formItemProps` 中自定义的 valuePropName/trigger)工作的控件,不限组件种类
|
|
27
|
+
- [ ] 步骤 3:按需配置控件细节(placeholder、是否可清空、选项数据等,以用户/产品要求为准)
|
|
28
|
+
- [ ] 步骤 4:实现 onSubmit(参数为筛选条件对象:已去掉值为 `undefined` 的字段,见 api.md);按需实现 onReset
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 在已选用 FilterGroup 时的约定
|
|
32
|
+
|
|
33
|
+
- 避免再手写一套纯 AntD `Form` + 栅格替代本组件;选项形状以 [api.md](api.md) 为准。
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# FilterGroup API
|
|
2
|
+
|
|
3
|
+
对应技能仅在用户**明确写出** `FilterGroup` 时启用;用户若只提 listpage / ListPage,**不要**套用本 API 文档(见 SKILL.md 的触发说明)。
|
|
4
|
+
|
|
5
|
+
包名:`listpage-components`(与「listpage 页面技能」无关,仅为 npm 包名)。例如:`import { FilterGroup, type FilterGroupProps, type FilterFormOption } from 'listpage-components'`。
|
|
6
|
+
|
|
7
|
+
## FilterGroup
|
|
8
|
+
|
|
9
|
+
内部组合表单项与栅格布局。
|
|
10
|
+
|
|
11
|
+
### 提交给 `onSubmit` 的数据
|
|
12
|
+
|
|
13
|
+
点击「搜索」或触发与提交等价的逻辑时,`onSubmit` 收到的是**当前筛选条件对象**:在表单取值之后,会**去掉值为 `undefined` 的字段**再传入,便于直接用于请求参数或状态;值为 `null` 的字段会保留,是否参与请求由业务决定。
|
|
14
|
+
|
|
15
|
+
重置流程中在调用 `onReset` 之前,也会用**同样规则**得到当前表单值并调用一次 `onSubmit`(见下)。
|
|
16
|
+
|
|
17
|
+
### FilterGroupProps
|
|
18
|
+
|
|
19
|
+
| 属性 | 类型 | 说明 |
|
|
20
|
+
|------|------|------|
|
|
21
|
+
| `options` | `FilterFormOption[]` | 筛选项配置(必填) |
|
|
22
|
+
| `initialValues` | `FormValue` | 表单初始值 |
|
|
23
|
+
| `onSubmit` | `(values?: FormValue) => void` | 筛选条件变化需提交时触发,参数为上一节说明的对象 |
|
|
24
|
+
| `onReset` | `() => void` | 重置时额外回调(在内部已 `resetFields`、并会再触发一次 `onSubmit` 之后调用) |
|
|
25
|
+
|
|
26
|
+
**重置行为(需牢记)**:用户点「重置」时,会先清空字段,再按上述规则调用 `onSubmit`,最后调用 `onReset`。若你只关心「条件变化后拉数」,通常只实现 `onSubmit` 即可。
|
|
27
|
+
|
|
28
|
+
## FilterFormOption
|
|
29
|
+
|
|
30
|
+
| 字段 | 类型 | 说明 |
|
|
31
|
+
|------|------|------|
|
|
32
|
+
| `name` | `string` | 表单字段名(必填) |
|
|
33
|
+
| `label` | `ReactNode` | 标签文案 |
|
|
34
|
+
| `component` | `ReactElement` | 筛选项控件,**具体用哪种组件不限制**(见下) |
|
|
35
|
+
| `colSpan` | `number` | 栅格占位,对应 12 列网格,**默认 2** |
|
|
36
|
+
| `formItemProps` | 除 `children` 外的 `FormItemProps` | 传给 `Form.Item` 的额外配置;若控件不用 `value`/`onChange`,可在此设 `valuePropName`、`trigger` |
|
|
37
|
+
|
|
38
|
+
### `component` 与受控
|
|
39
|
+
|
|
40
|
+
`FilterItem` 会对 `component` 做 `cloneElement`,注入**当前值**与**变更回调**(默认对应受控的 `value` 与 `onChange`,与 Ant Design Form 常见写法一致)。只要传入的节点能按注入后的 props 作为受控组件工作即可,**不限定**必须是 `Input`、`Select`、`DatePicker` 等某一种。
|
|
41
|
+
|
|
42
|
+
若控件使用其它字段名(例如 `checked`),通过 `formItemProps.valuePropName`、`formItemProps.trigger` 与 Ant Design `Form.Item` 约定一致即可。
|
|
43
|
+
|
|
44
|
+
未提供 `component` 时,该筛选项使用默认的 `Input`。
|
|
45
|
+
|
|
46
|
+
布局使用内部 `FilterGridLayout`(`grid-cols-12`),操作按钮占一列;筛选项 `colSpan` 为 1–12。
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# FilterGroup 示例
|
|
2
|
+
|
|
3
|
+
本技能仅在用户**明确写出 FilterGroup** 时启用;以下为实现参考。
|
|
4
|
+
|
|
5
|
+
## 仅筛选区 + 本地状态或请求
|
|
6
|
+
|
|
7
|
+
示例里使用 `Input` / `Select` / `DatePicker` 仅为演示;`component` 可换成任意合适的受控控件(见 [api.md](api.md) 中 `component` 说明)。示例中部分使用 `allowClear` 仅为常见写法,以实际需求为准。
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { useState, useCallback } from 'react';
|
|
11
|
+
import { Input, Select, DatePicker } from 'antd';
|
|
12
|
+
import type { Dayjs } from 'dayjs';
|
|
13
|
+
import {
|
|
14
|
+
FilterGroup,
|
|
15
|
+
type FilterFormOption,
|
|
16
|
+
} from 'listpage-components';
|
|
17
|
+
|
|
18
|
+
type FilterValues = {
|
|
19
|
+
keyword?: string;
|
|
20
|
+
status?: string;
|
|
21
|
+
date?: Dayjs;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function ReportFilterPanel() {
|
|
25
|
+
const [filters, setFilters] = useState<FilterValues>({});
|
|
26
|
+
|
|
27
|
+
const options: FilterFormOption[] = [
|
|
28
|
+
{
|
|
29
|
+
name: 'keyword',
|
|
30
|
+
label: '关键词',
|
|
31
|
+
colSpan: 3,
|
|
32
|
+
component: <Input placeholder="请输入" allowClear />,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'status',
|
|
36
|
+
label: '状态',
|
|
37
|
+
colSpan: 2,
|
|
38
|
+
component: (
|
|
39
|
+
<Select
|
|
40
|
+
allowClear
|
|
41
|
+
placeholder="全部"
|
|
42
|
+
options={[
|
|
43
|
+
{ label: '启用', value: '1' },
|
|
44
|
+
{ label: '停用', value: '0' },
|
|
45
|
+
]}
|
|
46
|
+
/>
|
|
47
|
+
),
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'date',
|
|
51
|
+
label: '日期',
|
|
52
|
+
colSpan: 3,
|
|
53
|
+
component: <DatePicker className="w-full" allowClear />,
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
const handleSubmit = useCallback((values?: FilterValues) => {
|
|
58
|
+
setFilters(values ?? {});
|
|
59
|
+
// 此处可改为调用 API:fetchReport({ ...values })
|
|
60
|
+
}, []);
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<div className="bg-white px-6 py-4 rounded-lg border border-[#f0f0f0]">
|
|
64
|
+
<FilterGroup<FilterValues>
|
|
65
|
+
options={options}
|
|
66
|
+
initialValues={filters}
|
|
67
|
+
onSubmit={handleSubmit}
|
|
68
|
+
onReset={() => {
|
|
69
|
+
/* 可选:仅重置以外的副作用 */
|
|
70
|
+
}}
|
|
71
|
+
/>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## 自检
|
|
78
|
+
|
|
79
|
+
- 各控件的交互(是否可清空、选项来源等)与用户/产品要求一致
|
|
80
|
+
- `name` 与类型/接口字段一致
|
|
81
|
+
- 理解「重置」会再次触发 `onSubmit`,避免重复请求未做防抖时可在业务侧处理
|
|
82
|
+
|