@yorha2b-lab/autodev 7.5.5 → 7.5.7
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/.github/scripts/analyze/deep_intelligence.py +1 -1
- package/.github/scripts/render/dashboard.py +20 -14
- package/README.md +1 -1
- package/README_EN.md +1 -1
- package/package.json +1 -1
- package/src/workshop/react/blueprint/molds/handleBlock.hbs +2 -0
- package/src/workshop/react/blueprint/molds/hookBlock.hbs +1 -2
- package/src/workshop/react/builder/index.js +1 -1
- package/src/workshop/react/standardParts/MySearchForm.js +16 -9
- package/src/workshop/react/standardParts/MyTable.js +32 -4
|
@@ -10,34 +10,46 @@ def peak_recon():
|
|
|
10
10
|
df = pd.read_csv('ghrs-data/clones_ledger.csv')
|
|
11
11
|
df['date'] = pd.to_datetime(df['date'])
|
|
12
12
|
df = df.sort_values('date').reset_index(drop=True)
|
|
13
|
+
one_year_ago = df['date'].max() - pd.Timedelta(days=365)
|
|
14
|
+
df = df[df['date'] >= one_year_ago].reset_index(drop=True)
|
|
13
15
|
fig3, axes3 = plt.subplots(3, 1, figsize=(16, 14))
|
|
14
16
|
# 3.1 时间序列图
|
|
15
17
|
axA = axes3[0]
|
|
16
18
|
axA.fill_between(df['date'], df['clones'],
|
|
17
19
|
alpha=0.3, color='#3498db', label='Clones')
|
|
18
20
|
axA.plot(df['date'], df['clones'], color='#3498db', linewidth=1.2)
|
|
21
|
+
|
|
22
|
+
# 💡 1. 核心修复:给左侧 Y 轴增加 25% 的顶部呼吸空间(从 859 扩充到 ~1050)
|
|
23
|
+
axA.set_ylim(bottom=0, top=df['clones'].max() * 1.25)
|
|
24
|
+
|
|
19
25
|
axA_twin = axA.twinx()
|
|
20
26
|
axA_twin.plot(df['date'], df['uniques'],
|
|
21
27
|
color='#e74c3c', linewidth=1.2, label='Visitors')
|
|
22
28
|
axA_twin.fill_between(
|
|
23
29
|
df['date'], df['uniques'], alpha=0.15, color='#e74c3c')
|
|
30
|
+
# 💡 2. 右侧 Y 轴同步给点余量
|
|
31
|
+
axA_twin.set_ylim(bottom=0, top=df['uniques'].max() * 1.2)
|
|
32
|
+
|
|
24
33
|
axA.set_ylabel('Clones', color='#3498db', fontsize=12)
|
|
25
34
|
axA_twin.set_ylabel('Visitors', color='#e74c3c', fontsize=12)
|
|
35
|
+
|
|
36
|
+
# 💡 3. 给标题加上 pad=15 间距,防止任何碰撞
|
|
26
37
|
axA.set_title('Bunker Signal Intensity (Peak Detection Mode)',
|
|
27
|
-
fontsize=14, fontweight='bold')
|
|
38
|
+
fontsize=14, fontweight='bold', pad=15)
|
|
28
39
|
|
|
40
|
+
# 💡 4. 战术箭头微调:指向清晰,文字居中悬浮在留白区
|
|
29
41
|
peak_clone_idx = df['clones'].idxmax()
|
|
30
42
|
axA.annotate(f'LEGEND PEAK: {df["clones"].max()}',
|
|
31
43
|
xy=(df.loc[peak_clone_idx, 'date'],
|
|
32
44
|
df.loc[peak_clone_idx, 'clones']),
|
|
33
|
-
xytext=(
|
|
34
|
-
arrowprops=dict(arrowstyle='->', color='yellow'),
|
|
45
|
+
xytext=(25, 20), textcoords='offset points',
|
|
46
|
+
arrowprops=dict(arrowstyle='->', color='yellow', lw=1.5),
|
|
35
47
|
fontsize=10, color='yellow', fontweight='bold')
|
|
36
48
|
|
|
37
49
|
# 3.2 每周汇总
|
|
38
50
|
axB = axes3[1]
|
|
39
51
|
weekly_agg = df.assign(W=df['date'].dt.strftime(
|
|
40
|
-
'%Y-W%
|
|
52
|
+
'%Y-W%V')).groupby('W')[['clones', 'uniques']].sum().tail(12)
|
|
41
53
|
weekly_agg.plot(kind='bar', ax=axB, alpha=0.8)
|
|
42
54
|
axB.set_title('Weekly Operational Summary',
|
|
43
55
|
fontsize=14, fontweight='bold')
|
|
@@ -62,6 +74,8 @@ def intelligence_grid():
|
|
|
62
74
|
df = pd.read_csv('ghrs-data/clones_ledger.csv')
|
|
63
75
|
df['date'] = pd.to_datetime(df['date'])
|
|
64
76
|
df = df.sort_values('date').reset_index(drop=True)
|
|
77
|
+
one_year_ago = df['date'].max() - pd.Timedelta(days=365)
|
|
78
|
+
df = df[df['date'] >= one_year_ago].reset_index(drop=True)
|
|
65
79
|
df['week'] = df['date'].dt.isocalendar().week
|
|
66
80
|
df['year'] = df['date'].dt.isocalendar().year
|
|
67
81
|
df['week_key'] = df['year'].astype(
|
|
@@ -204,13 +218,6 @@ def main_dashboard(df_c, df_v):
|
|
|
204
218
|
df = pd.merge(df_c, df_v, on='date', how='outer').fillna(
|
|
205
219
|
0).sort_values('date')
|
|
206
220
|
df['cumulative'] = df['Clones'].cumsum()
|
|
207
|
-
df['ratio'] = df['Clones'] / \
|
|
208
|
-
df['Views_Uniques'].replace(0, 1)
|
|
209
|
-
|
|
210
|
-
# 💡 2. 核心算法注入:计算“信号纯度” (Purity)
|
|
211
|
-
df['cumulative'] = df['Clones'].cumsum()
|
|
212
|
-
|
|
213
|
-
# 计算“信号纯度”:克隆数 / 访客数
|
|
214
221
|
df['ratio'] = df['Clones'] / df['Views_Uniques'].replace(0, 1)
|
|
215
222
|
|
|
216
223
|
# 算法:(比例/10 * 0.6) + (总量/500 * 0.4)
|
|
@@ -241,9 +248,8 @@ def main_dashboard(df_c, df_v):
|
|
|
241
248
|
# --- 💡 子图 3 [核心新增]: 信号纯度审计 (Signal Purity) ---
|
|
242
249
|
ax_p = fig1.add_subplot(gs1[2])
|
|
243
250
|
# 使用渐变紫色,代表地堡的“骇入感知”
|
|
244
|
-
ax_p.fill_between(df['date']
|
|
245
|
-
|
|
246
|
-
ax_p.plot(df['date'].tail(45), df['purity'].tail(45),
|
|
251
|
+
ax_p.fill_between(df['date'], df['purity'], color='#ff00ff', alpha=0.1)
|
|
252
|
+
ax_p.plot(df['date'], df['purity'],
|
|
247
253
|
color='#ff00ff', lw=1.5, label='Signal Purity %')
|
|
248
254
|
ax_p.set_ylim(0, 105) # 留点空白放标题
|
|
249
255
|
ax_p.axhline(y=50, color='#444', linestyle=':', alpha=0.5) # 50% 警戒线
|
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@ bunker init
|
|
|
56
56
|
> **💡 零配置演示模式 (Demo Mode)**
|
|
57
57
|
> 项目默认开启 Demo 模式:
|
|
58
58
|
> 1. 无需配置 API_KEY,直接运行 `bunker boot`。
|
|
59
|
-
> 2. 向 `./bunker/
|
|
59
|
+
> 2. 向 `./bunker/mission` 目录丢入任意图片(系统自动空投标准 `example.json` 构筑包)。
|
|
60
60
|
> 3. 若想识别真实代码/截图,请在 `bunker/config.js` 中将 `useDemo` 设为 `false` 并配置 `.env`。
|
|
61
61
|
|
|
62
62
|
创建 `.env` 文件并配置以下环境变量:
|
package/README_EN.md
CHANGED
|
@@ -56,7 +56,7 @@ bunker init
|
|
|
56
56
|
> **💡 Zero-Config Demo Mode**
|
|
57
57
|
> Demo mode is enabled by default:
|
|
58
58
|
> 1. No `API_KEY` required, run `bunker boot` directly.
|
|
59
|
-
> 2. Drop any image into `./bunker/
|
|
59
|
+
> 2. Drop any image into `./bunker/mission` (system ignores image content and drops standard `example.json` construction package).
|
|
60
60
|
> 3. To recognize real screenshots, set `useDemo: false` in `bunker/config.js` and configure `.env`.
|
|
61
61
|
|
|
62
62
|
Create a `.env` file and configure environment variables:
|
package/package.json
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
|
|
2
2
|
const initialParams = {{initParams}}
|
|
3
|
-
|
|
4
3
|
const query = formatQuery(Object.fromEntries(new URLSearchParams(location.search).entries()), {{formItems}})
|
|
5
4
|
|
|
6
5
|
/**
|
|
@@ -10,7 +9,7 @@ const query = formatQuery(Object.fromEntries(new URLSearchParams(location.search
|
|
|
10
9
|
* 2. 手动模式:搜索 BUNKER_API_ANCHOR 手动替换为真实接口。
|
|
11
10
|
*/
|
|
12
11
|
|
|
13
|
-
const { total, columns, loading, dataSource,
|
|
12
|
+
const { total, search, columns, loading, dataSource, setLoading, setSearch, setDataSource, refresh } = useTableQuery({
|
|
14
13
|
cols:tableColumns,
|
|
15
14
|
initialParams: { ...initialParams, ...query },
|
|
16
15
|
api: async params => await {{{this.uri}}},
|
|
@@ -118,7 +118,7 @@ module.exports = ({ template, logistics, headquarters }) => {
|
|
|
118
118
|
const functionButtons = pageConfig.functionButton?.filter(item => !['查询', '重置', 'query', 'search', 'reset'].includes(item.btn.toLowerCase().replaceAll(' ', ''))) || []
|
|
119
119
|
const needRenderAction = hasImageColumn
|
|
120
120
|
|
|
121
|
-
const pageStruct = ['stateBlock', '
|
|
121
|
+
const pageStruct = ['stateBlock', 'hookBlock', 'handleBlock', hasOperate ? 'operateBlock' : '', 'renderBlock'].filter(Boolean)
|
|
122
122
|
|
|
123
123
|
let columnsValue = hasTabs ? 'columns[activeKey]' : 'columns'
|
|
124
124
|
if (hasOperate) {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import dayjs from 'dayjs'
|
|
2
2
|
import { MyBaseForm } from './MyBaseForm'
|
|
3
|
-
import { useState, useEffect } from 'react'
|
|
3
|
+
import { useMemo, useState, useEffect } from 'react'
|
|
4
4
|
import { Row, Col, Form, Button, Space } from 'antd'
|
|
5
|
+
import { SearchOutlined, RedoOutlined } from '@ant-design/icons'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @component MySearchForm
|
|
@@ -17,9 +18,9 @@ import { Row, Col, Form, Button, Space } from 'antd'
|
|
|
17
18
|
* @param {number} [props.defaultPageSize=10] - 默认吞吐量:重置时的初始每页条数
|
|
18
19
|
* @param {Function} [props.onValuesChange] - 联动传感:当表单值变动时的即时反馈
|
|
19
20
|
* @param {Object} props.initialValues - 初始信号:从 URL 或缓存中解析出的初始参数
|
|
21
|
+
* @param {React.ReactNode} [props.extra] - 额外挂件:在按钮区域注入自定义战术单元
|
|
20
22
|
* @param {Function} props.setSearch - 状态分发器:用于更新全局搜索参数并触发重新构筑
|
|
21
23
|
* @param {Object} props.search - 当前的全局搜索状态(包含 pageNo, pageSize 及各过滤项)
|
|
22
|
-
* @param {React.ReactNode} [props.extraOperate] - 额外挂件:在按钮区域注入自定义战术单元
|
|
23
24
|
* @param {boolean} props.loading - 战场负载状态:请求中时锁定按钮,防止由于重复指令导致的逻辑冲突
|
|
24
25
|
* @param {boolean} [props.syncUrlParams=true] - 物理同步开关:是否将搜索状态实时镜像至 URL 链接中
|
|
25
26
|
*
|
|
@@ -31,10 +32,12 @@ import { Row, Col, Form, Button, Space } from 'antd'
|
|
|
31
32
|
* formItems={[{ label: '状态', name: 'status', type: 'select', options: [...] }]}
|
|
32
33
|
* />
|
|
33
34
|
*/
|
|
34
|
-
export const MySearchForm = ({ form, search, loading, labelCol, setSearch, formItems, showLimit = 7, initialValues, initialParams, customReset,
|
|
35
|
+
export const MySearchForm = ({ form, search, options, loading, labelCol, setSearch, formItems, showLimit = 7, initialValues, initialParams, customReset, extra, customFinish, onValuesChange, syncUrlParams = true, defaultPageSize = 10 }) => {
|
|
35
36
|
|
|
36
37
|
const [limit, setLimit] = useState(showLimit)
|
|
37
38
|
|
|
39
|
+
const searchItems = useMemo(() => formItems.map(item => ({ ...item, ...(['select'].includes(item.type) ? { options: item.options ?? options?.[item.name] ?? [] } : {}) })), [formItems, options])
|
|
40
|
+
|
|
38
41
|
/**
|
|
39
42
|
* @function handleReset
|
|
40
43
|
* @description 执行“归零协议”:清除当前所有搜索信号,并可选择性擦除 URL 中的物理记录。
|
|
@@ -115,15 +118,19 @@ export const MySearchForm = ({ form, search, loading, labelCol, setSearch, formI
|
|
|
115
118
|
newValues[item.name] = [dayjs(Number(initialValues[startKey])), dayjs(Number(initialValues[endKey]))]
|
|
116
119
|
}
|
|
117
120
|
}
|
|
121
|
+
// 处理多选框回显
|
|
122
|
+
if (item.hasOwnProperty('mode') && !!initialValues[item.name]) {
|
|
123
|
+
newValues[item.name] = initialValues[item.name]?.split(',')
|
|
124
|
+
}
|
|
118
125
|
})
|
|
119
126
|
form.setFieldsValue(newValues)
|
|
120
127
|
}
|
|
121
|
-
}, [
|
|
128
|
+
}, [form, formItems, initialValues])
|
|
122
129
|
|
|
123
130
|
return (
|
|
124
|
-
<Form form={form} onFinish={handleFinish} labelCol={labelCol} onValuesChange={onValuesChange}>
|
|
131
|
+
<Form form={form} onFinish={handleFinish} labelCol={labelCol} onValuesChange={onValuesChange} className='searchForm'>
|
|
125
132
|
<Row gutter={24}>
|
|
126
|
-
{
|
|
133
|
+
{searchItems.slice(0, limit)?.map((item, ind) => (
|
|
127
134
|
<Col key={ind} span={item.span ?? 6}>
|
|
128
135
|
{/* 物理装配:利用底层 MyBaseForm 单元进行渲染 */}
|
|
129
136
|
<MyBaseForm item={item} form={form} />
|
|
@@ -137,9 +144,9 @@ export const MySearchForm = ({ form, search, loading, labelCol, setSearch, formI
|
|
|
137
144
|
<Col flex='auto' style={{ textAlign: 'right' }}>
|
|
138
145
|
<Form.Item>
|
|
139
146
|
<Space>
|
|
140
|
-
<Button type='primary' loading={loading} htmlType='submit'>查询</Button>
|
|
141
|
-
<Button onClick={handleReset} loading={loading} htmlType='reset'>重置</Button>
|
|
142
|
-
{
|
|
147
|
+
<Button type='primary' icon={<SearchOutlined />} loading={loading} htmlType='submit'>查询</Button>
|
|
148
|
+
<Button icon={<RedoOutlined />} onClick={handleReset} loading={loading} htmlType='reset'>重置</Button>
|
|
149
|
+
{extra}
|
|
143
150
|
{/* 动态伸缩协议:根据当前零部件数量决定是否显示展开/收起按钮 */}
|
|
144
151
|
{formItems.length > showLimit && (<a onClick={() => setLimit(formItems.length > limit ? formItems.length : showLimit)}>{formItems.length > limit ? '展开' : '收起'}</a>)}
|
|
145
152
|
</Space>
|
|
@@ -54,7 +54,7 @@ import { useRef, useMemo, useEffect, useCallback } from 'react'
|
|
|
54
54
|
* }}
|
|
55
55
|
* />
|
|
56
56
|
*/
|
|
57
|
-
export const MyTable = ({ size, query, total, search, autoScroll, onChange, pagination, renderAction, rowClassName, customSave, setDataSource, lineFormChange, columns = [], rowSelection, rowKey = 'id', loading = false, dataSource = [], scroll = { x: 'max-content' }, isLocalPaging = false, ...restProps }) => {
|
|
57
|
+
export const MyTable = ({ size, query, total, search, autoScroll, onChange, setSearch, pagination, renderAction, rowClassName, customSave, setDataSource, lineFormChange, columns = [], rowSelection, rowKey = 'id', loading = false, dataSource = [], scroll = { x: 'max-content' }, isLocalPaging = false, ...restProps }) => {
|
|
58
58
|
|
|
59
59
|
const tableRef = useRef(null)
|
|
60
60
|
const hasScrolledRef = useRef(false)
|
|
@@ -101,10 +101,37 @@ export const MyTable = ({ size, query, total, search, autoScroll, onChange, pagi
|
|
|
101
101
|
const mergedColumns = useMemo(() => {
|
|
102
102
|
return columns.map(col => {
|
|
103
103
|
if (!col.editType) {
|
|
104
|
+
let currentCol = { ...col }
|
|
104
105
|
if (col.renderAction && renderAction?.[col.dataIndex]) {
|
|
105
|
-
|
|
106
|
+
currentCol.render = renderAction[col.dataIndex]
|
|
106
107
|
}
|
|
107
|
-
|
|
108
|
+
if (col.search) {
|
|
109
|
+
currentCol = {
|
|
110
|
+
...currentCol,
|
|
111
|
+
filterSearch: true,
|
|
112
|
+
onFilter: (value, record) => col.remote ? undefined : col.customFilter({ value, record, dataIndex: col.dataIndex }),
|
|
113
|
+
filterDropdown: ({ close, confirm, selectedKeys, clearFilters, setSelectedKeys }) => (
|
|
114
|
+
<Space direction='vertical' style={{ padding: 12 }}>
|
|
115
|
+
{col.customFilterDropdown({ dataIndex: col.dataIndex, selectedKeys, setSelectedKeys })}
|
|
116
|
+
<div style={{ display: 'flex' }}>
|
|
117
|
+
<a style={{ marginLeft: 'auto', marginRight: 8 }} onClick={() => {
|
|
118
|
+
if (col.remote) {
|
|
119
|
+
setSearch(prev => ({ ...prev, pageNo: 1, [col.dataIndex]: col.formatFilter(selectedKeys) }))
|
|
120
|
+
}
|
|
121
|
+
confirm()
|
|
122
|
+
}}>确定</a>
|
|
123
|
+
<a onClick={() => {
|
|
124
|
+
if (col.remote) {
|
|
125
|
+
setSearch(prev => ({ ...prev, pageNo: 1, [col.dataIndex]: undefined }))
|
|
126
|
+
}
|
|
127
|
+
clearFilters()
|
|
128
|
+
}}>重置</a>
|
|
129
|
+
</div>
|
|
130
|
+
</Space>
|
|
131
|
+
),
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return currentCol
|
|
108
135
|
}
|
|
109
136
|
return {
|
|
110
137
|
...col,
|
|
@@ -113,6 +140,7 @@ export const MyTable = ({ size, query, total, search, autoScroll, onChange, pagi
|
|
|
113
140
|
handleSave,
|
|
114
141
|
title: col.title,
|
|
115
142
|
rules: col.rules,
|
|
143
|
+
extra: col.extra,
|
|
116
144
|
options: col.options,
|
|
117
145
|
editType: col.editType,
|
|
118
146
|
dataIndex: col.dataIndex,
|
|
@@ -145,7 +173,7 @@ export const MyTable = ({ size, query, total, search, autoScroll, onChange, pagi
|
|
|
145
173
|
}),
|
|
146
174
|
...(typeof pagination === 'object' ? pagination : {})
|
|
147
175
|
}
|
|
148
|
-
}, [total, search, pagination
|
|
176
|
+
}, [total, search, pagination])
|
|
149
177
|
|
|
150
178
|
/**
|
|
151
179
|
* @description [视觉追踪协议] 当检测到 query.rowIndex 信号时,执行“物理重定向”。
|