@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.
@@ -1,8 +1,8 @@
1
1
  import os
2
2
  import numpy as np
3
3
  import pandas as pd
4
- import matplotlib.pyplot as plt
5
4
  import seaborn as sns
5
+ import matplotlib.pyplot as plt
6
6
  from sklearn.cluster import DBSCAN
7
7
  from sklearn.preprocessing import StandardScaler
8
8
 
@@ -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=(10, 20), textcoords='offset points',
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%W')).groupby('W')[['clones', 'uniques']].sum().tail(12)
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'].tail(45), df['purity'].tail(
245
- 45), color='#ff00ff', alpha=0.1)
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/screenShot` 目录丢入任意图片(系统自动空投标准 `example.json` 构筑包)。
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/screenShot` (system ignores image content and drops standard `example.json` construction package).
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,6 @@
1
1
  {
2
2
  "name": "@yorha2b-lab/autodev",
3
- "version": "7.5.5",
3
+ "version": "7.5.7",
4
4
  "description": "基于 AI 视觉与语义协议的前端 (React+Antd) 代码构筑与旧系统重构引擎",
5
5
  "bin": {
6
6
  "bunker": "bin/bunker.js"
@@ -1,7 +1,9 @@
1
1
 
2
2
  {{#each operations}}
3
3
  const {{this.action}} = async params => {
4
+ setLoading(true)
4
5
  const response = await {{{this.request}}}
6
+ setLoading(false)
5
7
  if({{{../responseSuccess}}}){
6
8
  refresh()
7
9
  }
@@ -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, search, setSearch, setDataSource, refresh } = useTableQuery({
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', 'handleBlock', 'hookBlock', hasOperate ? 'operateBlock' : '', 'renderBlock'].filter(Boolean)
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, extraOperate, customFinish, onValuesChange, syncUrlParams = true, defaultPageSize = 10 }) => {
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
- }, [initialValues, form, formItems])
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
- {formItems.slice(0, limit)?.map((item, ind) => (
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
- {extraOperate}
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
- return { ...col, render: renderAction[col.dataIndex] }
106
+ currentCol.render = renderAction[col.dataIndex]
106
107
  }
107
- return col
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, isLocalPaging])
176
+ }, [total, search, pagination])
149
177
 
150
178
  /**
151
179
  * @description [视觉追踪协议] 当检测到 query.rowIndex 信号时,执行“物理重定向”。