yanleafadmin 2.0.3__tar.gz → 2.0.5__tar.gz
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.
- {yanleafadmin-2.0.3/yanleafadmin.egg-info → yanleafadmin-2.0.5}/PKG-INFO +29 -13
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/README.md +28 -12
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/apps.py +10 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templates/admin/base.html +0 -8
- yanleafadmin-2.0.5/apps/theme/templates/admin/base_site.html +15 -0
- yanleafadmin-2.0.5/apps/theme/templates/admin/dashboard_index.html +394 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/setup.py +1 -1
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5/yanleafadmin.egg-info}/PKG-INFO +29 -13
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/yanleafadmin.egg-info/SOURCES.txt +2 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/LICENSE +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/MANIFEST.in +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/__init__.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/ai_assistant/__init__.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/ai_assistant/apps.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/ai_assistant/services.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/ai_assistant/static/ai/ai-search.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/ai_assistant/templates/ai/fullpage.html +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/ai_assistant/urls.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/ai_assistant/views.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/dashboard_engine/__init__.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/dashboard_engine/admin.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/dashboard_engine/apps.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/dashboard_engine/migrations/__init__.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/dashboard_engine/models.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/dashboard_engine/templates/admin/base_site.html +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/dashboard_engine/templates/admin/dashboard_index.html +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/dashboard_engine/tests.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/dashboard_engine/views.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/erd_engine/__init__.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/erd_engine/apps.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/erd_engine/sql_parser.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/erd_engine/static/erd/er-diagram.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/erd_engine/templates/erd/er_diagram.html +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/erd_engine/urls.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/erd_engine/views.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/__init__.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/admin.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/components/__init__.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/components/actions.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/components/charts.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/context_processors.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/forms.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/migrations/__init__.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/models.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/settings.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/css/admin.css +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/css/change-form.css +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/css/change-list.css +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/css/dashboard.css +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/css/login.css +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/css/password-form.css +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/css/user-change-form.css +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/js/components.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/js/core.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/js/filter-widget.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/js/i18n/datatables.en.json +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/js/i18n/datatables.zh-hans.json +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/js/login.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/js/password-check.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/js/smart-chart.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/js/user-password-field.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/bulma/bulma.min.css +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/bulma-calendar/bulma-calendar.min.css +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/bulma-calendar/bulma-calendar.min.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/datatables/css/buttons.bulma.min.css +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/datatables/css/dataTables.bulma.min.css +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/datatables/js/buttons.html5.min.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/datatables/js/buttons.print.min.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/datatables/js/dataTables.bulma.min.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/datatables/js/dataTables.buttons.min.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/datatables/js/jquery.dataTables.min.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/dropzone/dropzone-min.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/dropzone/dropzone.css +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/echarts/echarts.min.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/fontawesome/css/all.min.css +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/fontawesome/webfonts/fa-brands-400.woff2 +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/fontawesome/webfonts/fa-regular-400.woff2 +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/fontawesome/webfonts/fa-solid-900.woff2 +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/gojs/go.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/jquery/jquery.min.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/jszip/jszip.min.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/select2/css/select2.min.css +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/select2/js/select2.min.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/sweetalert2/sweetalert2.all.min.js +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templates/admin/app_index.html +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templates/admin/auth/user/add_form.html +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templates/admin/auth/user/change_form.html +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templates/admin/auth/user/change_password.html +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templates/admin/change_form.html +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templates/admin/change_list.html +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templates/admin/change_list_results.html +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templates/admin/edit_inline/tabular.html +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templates/admin/login.html +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templates/admin/pagination.html +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templates/registration/password_change_done.html +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templates/registration/password_change_form.html +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templatetags/__init__.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templatetags/yla_charts.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templatetags/yla_components.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/tests.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/urls.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/views.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/users/__init__.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/users/admin.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/users/apps.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/users/management/__init__.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/users/management/commands/__init__.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/users/management/commands/seed_demo_data.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/users/migrations/0001_initial.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/users/migrations/__init__.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/users/models.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/users/tests.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/users/views.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/requirements.txt +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/setup.cfg +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/yanleafadmin/__init__.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/yanleafadmin/apps.py +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/yanleafadmin.egg-info/dependency_links.txt +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/yanleafadmin.egg-info/requires.txt +0 -0
- {yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/yanleafadmin.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: yanleafadmin
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.5
|
|
4
4
|
Summary: 极简白色现代 Django Admin 主题体系 — 基于 Django + Bulma CSS
|
|
5
5
|
Home-page: https://github.com/zhouyanye/yanleafadmin
|
|
6
6
|
Author: zhouyanye
|
|
@@ -53,7 +53,7 @@ License-File: LICENSE
|
|
|
53
53
|
### AI 数据助手
|
|
54
54
|
- **右下角悬浮聊天机器人**,点击即可对话
|
|
55
55
|
- **自然语言查询**:基于 DeepSeek 驱动,自动转 Django ORM
|
|
56
|
-
- **
|
|
56
|
+
- **ECharts 图表**:查询结果原地渲染折线图/饼图/柱状图
|
|
57
57
|
- **多模型支持**:DeepSeek Chat / Reasoner / GPT-4o / 通义千问
|
|
58
58
|
- **配置保存在浏览器**:API Key 不上传服务器
|
|
59
59
|
- **聊天历史**:localStorage 持久化最近 50 条对话
|
|
@@ -97,33 +97,49 @@ import os, sys
|
|
|
97
97
|
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
####
|
|
100
|
+
#### 一行启用(主题 + 仪表盘,开箱即用)
|
|
101
101
|
|
|
102
102
|
```python
|
|
103
103
|
INSTALLED_APPS = [
|
|
104
|
-
'yanleafadmin', #
|
|
104
|
+
'yanleafadmin', # 一行搞定主题和仪表盘
|
|
105
105
|
'django.contrib.admin',
|
|
106
106
|
'django.contrib.auth',
|
|
107
107
|
'django.contrib.contenttypes',
|
|
108
108
|
'django.contrib.sessions',
|
|
109
109
|
'django.contrib.messages',
|
|
110
110
|
'django.contrib.staticfiles',
|
|
111
|
-
'captcha', # 登录验证码
|
|
112
|
-
'apps.users.apps.UsersConfig',
|
|
113
|
-
'apps.dashboard_engine.apps.DashboardEngineConfig',
|
|
114
111
|
]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
> 仪表盘首页自动启用(统计卡片 + 热力图 + 玫瑰图 + 系统动态时间线)。
|
|
115
|
+
|
|
116
|
+
#### 可选:启用语言切换
|
|
115
117
|
|
|
116
|
-
|
|
118
|
+
```python
|
|
119
|
+
# urls.py
|
|
120
|
+
urlpatterns = [
|
|
121
|
+
path('i18n/', include('django.conf.urls.i18n')),
|
|
122
|
+
...
|
|
123
|
+
]
|
|
117
124
|
```
|
|
118
125
|
|
|
119
|
-
####
|
|
126
|
+
#### 可选:启用登录验证码
|
|
120
127
|
|
|
121
|
-
|
|
128
|
+
```python
|
|
129
|
+
INSTALLED_APPS += ['captcha']
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
#### 可选:启用 ER 图 / AI 助手 / SmartChart
|
|
133
|
+
|
|
134
|
+
这些功能需要额外配置 URL 路由:
|
|
122
135
|
|
|
123
136
|
```python
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
137
|
+
# urls.py
|
|
138
|
+
urlpatterns = [
|
|
139
|
+
...
|
|
140
|
+
path('admin/erd/', include('apps.erd_engine.urls')), # ER 图
|
|
141
|
+
path('api/ai/', include('apps.ai_assistant.urls')), # AI 助手
|
|
142
|
+
path('yla-api/', include('apps.theme.urls')), # SmartChart
|
|
127
143
|
]
|
|
128
144
|
```
|
|
129
145
|
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
### AI 数据助手
|
|
35
35
|
- **右下角悬浮聊天机器人**,点击即可对话
|
|
36
36
|
- **自然语言查询**:基于 DeepSeek 驱动,自动转 Django ORM
|
|
37
|
-
- **
|
|
37
|
+
- **ECharts 图表**:查询结果原地渲染折线图/饼图/柱状图
|
|
38
38
|
- **多模型支持**:DeepSeek Chat / Reasoner / GPT-4o / 通义千问
|
|
39
39
|
- **配置保存在浏览器**:API Key 不上传服务器
|
|
40
40
|
- **聊天历史**:localStorage 持久化最近 50 条对话
|
|
@@ -78,33 +78,49 @@ import os, sys
|
|
|
78
78
|
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
####
|
|
81
|
+
#### 一行启用(主题 + 仪表盘,开箱即用)
|
|
82
82
|
|
|
83
83
|
```python
|
|
84
84
|
INSTALLED_APPS = [
|
|
85
|
-
'yanleafadmin', #
|
|
85
|
+
'yanleafadmin', # 一行搞定主题和仪表盘
|
|
86
86
|
'django.contrib.admin',
|
|
87
87
|
'django.contrib.auth',
|
|
88
88
|
'django.contrib.contenttypes',
|
|
89
89
|
'django.contrib.sessions',
|
|
90
90
|
'django.contrib.messages',
|
|
91
91
|
'django.contrib.staticfiles',
|
|
92
|
-
'captcha', # 登录验证码
|
|
93
|
-
'apps.users.apps.UsersConfig',
|
|
94
|
-
'apps.dashboard_engine.apps.DashboardEngineConfig',
|
|
95
92
|
]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
> 仪表盘首页自动启用(统计卡片 + 热力图 + 玫瑰图 + 系统动态时间线)。
|
|
96
|
+
|
|
97
|
+
#### 可选:启用语言切换
|
|
96
98
|
|
|
97
|
-
|
|
99
|
+
```python
|
|
100
|
+
# urls.py
|
|
101
|
+
urlpatterns = [
|
|
102
|
+
path('i18n/', include('django.conf.urls.i18n')),
|
|
103
|
+
...
|
|
104
|
+
]
|
|
98
105
|
```
|
|
99
106
|
|
|
100
|
-
####
|
|
107
|
+
#### 可选:启用登录验证码
|
|
101
108
|
|
|
102
|
-
|
|
109
|
+
```python
|
|
110
|
+
INSTALLED_APPS += ['captcha']
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### 可选:启用 ER 图 / AI 助手 / SmartChart
|
|
114
|
+
|
|
115
|
+
这些功能需要额外配置 URL 路由:
|
|
103
116
|
|
|
104
117
|
```python
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
118
|
+
# urls.py
|
|
119
|
+
urlpatterns = [
|
|
120
|
+
...
|
|
121
|
+
path('admin/erd/', include('apps.erd_engine.urls')), # ER 图
|
|
122
|
+
path('api/ai/', include('apps.ai_assistant.urls')), # AI 助手
|
|
123
|
+
path('yla-api/', include('apps.theme.urls')), # SmartChart
|
|
108
124
|
]
|
|
109
125
|
```
|
|
110
126
|
|
|
@@ -23,6 +23,16 @@ class ThemeConfig(AppConfig):
|
|
|
23
23
|
# 保留 per_page 参数补丁
|
|
24
24
|
self._patch_changelist_per_page()
|
|
25
25
|
|
|
26
|
+
# 仪表盘首页(pip install 时自动启用)
|
|
27
|
+
try:
|
|
28
|
+
from apps.dashboard_engine.apps import DashboardEngineConfig
|
|
29
|
+
import apps.dashboard_engine
|
|
30
|
+
dashboard_cfg = DashboardEngineConfig(apps.dashboard_engine.__name__, apps.dashboard_engine)
|
|
31
|
+
dashboard_cfg.path = __import__('os').path.dirname(apps.dashboard_engine.__file__)
|
|
32
|
+
dashboard_cfg.ready()
|
|
33
|
+
except Exception:
|
|
34
|
+
pass # 仪表盘可选
|
|
35
|
+
|
|
26
36
|
def _patch_changelist_per_page(self):
|
|
27
37
|
from django.contrib.admin.views.main import ChangeList
|
|
28
38
|
from django.core.paginator import Paginator
|
|
@@ -82,14 +82,6 @@
|
|
|
82
82
|
{% endfor %}
|
|
83
83
|
{% endif %}
|
|
84
84
|
|
|
85
|
-
<!-- 工具区 -->
|
|
86
|
-
<div class="sidebar-section">
|
|
87
|
-
<p class="sidebar-section-title">{% translate '工具' %}</p>
|
|
88
|
-
<ul class="sidebar-menu">
|
|
89
|
-
<li><a href="/admin/erd/"><span class="menu-icon"><i class="fas fa-project-diagram"></i></span>{% translate 'ER 图' %}</a></li>
|
|
90
|
-
</ul>
|
|
91
|
-
</div>
|
|
92
|
-
|
|
93
85
|
{% if not available_apps %}
|
|
94
86
|
<div class="sidebar-section">
|
|
95
87
|
<p class="sidebar-section-title">{% translate '导航' %}</p>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{% extends "admin/base.html" %}
|
|
2
|
+
{% load i18n static %}
|
|
3
|
+
|
|
4
|
+
{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | YanleafAdmin{% endblock %}
|
|
5
|
+
|
|
6
|
+
{% block sidebar_brand %}
|
|
7
|
+
<div class="sidebar-brand">
|
|
8
|
+
<a href="{% url 'admin:index' %}" style="display:flex;align-items:center;gap:0.5rem;text-decoration:none;">
|
|
9
|
+
<span class="brand-icon"><i class="fas fa-leaf"></i></span>
|
|
10
|
+
<span class="brand-text">
|
|
11
|
+
Yanleaf<span class="brand-sub">Admin</span>
|
|
12
|
+
</span>
|
|
13
|
+
</a>
|
|
14
|
+
</div>
|
|
15
|
+
{% endblock %}
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
{% extends "admin/base_site.html" %}
|
|
2
|
+
{% load i18n static %}
|
|
3
|
+
|
|
4
|
+
{% block extrastyle %}
|
|
5
|
+
{{ block.super }}
|
|
6
|
+
<link rel="stylesheet" href="{% static 'yanleafadmin/css/dashboard.css' %}">
|
|
7
|
+
{% endblock %}
|
|
8
|
+
|
|
9
|
+
{% block content %}
|
|
10
|
+
<div>
|
|
11
|
+
|
|
12
|
+
<div class="mb-6">
|
|
13
|
+
<h1 class="title is-3 has-text-weight-bold mb-2">{% translate '控制台' %}</h1>
|
|
14
|
+
<p class="subtitle is-6 has-text-grey-light">{% translate '欢迎回来' %},{{ user.username }}。{% translate '以下是系统核心组件的实时运行指标。' %}</p>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<div class="columns is-desktop is-multiline mb-6">
|
|
18
|
+
|
|
19
|
+
<div class="column is-3-desktop is-6-tablet">
|
|
20
|
+
<div class="box dashboard-stat-card p-5" style="height:110px;display:flex;flex-direction:column;justify-content:space-between;">
|
|
21
|
+
<p class="heading mb-1"><i class="fas fa-users"></i> {% translate '总注册用户' %}</p>
|
|
22
|
+
<div>
|
|
23
|
+
<span class="title is-3 has-text-weight-bold" style="margin-bottom:0;white-space:nowrap;">{{ total_users_count }}</span>
|
|
24
|
+
<div class="trend-tag" style="font-size:0.7rem;color:#22c55e;margin-top:0.15rem;">
|
|
25
|
+
<i class="fas fa-arrow-up"></i> 本周 +{{ week_new }}
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div class="column is-3-desktop is-6-tablet">
|
|
32
|
+
<div class="box dashboard-stat-card p-5" style="height:110px;display:flex;flex-direction:column;justify-content:space-between;">
|
|
33
|
+
<p class="heading mb-1"><i class="fas fa-user-plus"></i> {% translate '今日新增' %}</p>
|
|
34
|
+
<div>
|
|
35
|
+
<span class="title is-3 has-text-weight-bold" style="margin-bottom:0;white-space:nowrap;">{{ today_users_count }}</span>
|
|
36
|
+
{% if today_vs_yesterday != 0 %}
|
|
37
|
+
<div class="trend-tag" style="font-size:0.7rem;{% if today_vs_yesterday > 0 %}color:#22c55e;{% else %}color:#ef4444;{% endif %}margin-top:0.15rem;">
|
|
38
|
+
<i class="fas fa-arrow-{% if today_vs_yesterday > 0 %}up{% else %}down{% endif %}"></i>
|
|
39
|
+
{% if today_vs_yesterday > 0 %}+{% endif %}{{ today_vs_yesterday }} ({% if today_pct > 0 %}+{% endif %}{{ today_pct }}%)
|
|
40
|
+
</div>
|
|
41
|
+
{% endif %}
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div class="column is-3-desktop is-6-tablet">
|
|
47
|
+
<div class="box dashboard-stat-card p-5" style="height:110px;display:flex;flex-direction:column;justify-content:space-between;">
|
|
48
|
+
<p class="heading mb-1"><i class="fas fa-user-check"></i> {% translate '活跃用户' %}</p>
|
|
49
|
+
<div>
|
|
50
|
+
<span class="title is-3 has-text-weight-bold" style="margin-bottom:0;white-space:nowrap;">{{ active_users_count }}</span>
|
|
51
|
+
<div class="trend-tag" style="font-size:0.7rem;color:var(--yla-text-muted);margin-top:0.15rem;">
|
|
52
|
+
占比 {{ active_pct }}%
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<div class="column is-3-desktop is-6-tablet">
|
|
59
|
+
<div class="box dashboard-stat-card p-5" style="height:110px;display:flex;flex-direction:column;justify-content:space-between;">
|
|
60
|
+
<p class="heading mb-1"><i class="fas fa-microchip"></i> {% translate '服务器内存负载' %}</p>
|
|
61
|
+
<div>
|
|
62
|
+
<span class="title is-3 has-text-weight-bold" style="margin-bottom:0;white-space:nowrap;letter-spacing:-0.03em;">{{ memory_usage }}</span>
|
|
63
|
+
<div class="trend-tag" style="font-size:0.7rem;color:var(--yla-text-muted);margin-top:0.15rem;">
|
|
64
|
+
系统运行中
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<div class="columns">
|
|
73
|
+
<div class="column is-4">
|
|
74
|
+
<div class="box p-5 dashboard-stat-card" style="max-height: 420px; overflow-y: auto;">
|
|
75
|
+
<h3 class="subtitle is-6 has-text-weight-bold mb-3">{% translate '业务模块导航' %}</h3>
|
|
76
|
+
|
|
77
|
+
{% if app_list %}
|
|
78
|
+
<div class="is-flex is-flex-direction-column gap-3">
|
|
79
|
+
{% for app in app_list %}
|
|
80
|
+
<div class="mb-3">
|
|
81
|
+
<p class="is-size-7 has-text-weight-semibold has-text-grey-light uppercase tracking-wider mb-2"
|
|
82
|
+
style="letter-spacing: 0.05em;">
|
|
83
|
+
{{ app.name }}
|
|
84
|
+
</p>
|
|
85
|
+
|
|
86
|
+
<div class="is-flex is-flex-direction-column gap-2" style="padding-left: 4px;">
|
|
87
|
+
{% for model in app.models %}
|
|
88
|
+
<div class="quick-link-item p-2 is-flex is-justify-content-space-between is-align-items-center">
|
|
89
|
+
<div>
|
|
90
|
+
{% if model.admin_url %}
|
|
91
|
+
<a href="{{ model.admin_url }}" class="has-text-weight-medium is-size-6">
|
|
92
|
+
<i class="fas {% if 'user' in model.object_name|lower %}fa-user{% elif 'group' in model.object_name|lower %}fa-users{% elif 'permission' in model.object_name|lower or 'perm' in model.object_name|lower %}fa-shield-alt{% elif 'log' in model.object_name|lower %}fa-history{% else %}fa-table{% endif %} mr-1" style="opacity:0.5;font-size:0.8rem;"></i>
|
|
93
|
+
{{ model.name }}
|
|
94
|
+
</a>
|
|
95
|
+
{% else %}
|
|
96
|
+
<span class="has-text-grey-light is-size-6">{{ model.name }}</span>
|
|
97
|
+
{% endif %}
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<div class="is-flex gap-2">
|
|
101
|
+
{% if model.add_url %}
|
|
102
|
+
<a href="{{ model.add_url }}" class="button is-small is-light is-rounded py-0 px-2" title="{% translate '新增' %}">
|
|
103
|
+
<span class="has-text-grey">+</span>
|
|
104
|
+
</a>
|
|
105
|
+
{% endif %}
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
{% endfor %}
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
{% endfor %}
|
|
112
|
+
</div>
|
|
113
|
+
{% else %}
|
|
114
|
+
<div class="has-text-centered py-5">
|
|
115
|
+
<p class="has-text-grey-light is-size-6">{% translate '暂无可用业务模块' %}</p>
|
|
116
|
+
</div>
|
|
117
|
+
{% endif %}
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<div class="column is-8">
|
|
122
|
+
<div class="box p-5 dashboard-stat-card" style="min-height:380px;overflow:hidden;">
|
|
123
|
+
<h3 class="subtitle is-6 has-text-weight-bold mb-2">
|
|
124
|
+
<i class="fas fa-chart-line"></i> {% translate '最近 7 天注册趋势' %}
|
|
125
|
+
</h3>
|
|
126
|
+
<div id="main-chart" style="width:100%;height:330px;"></div>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
{% if recent_logs %}
|
|
132
|
+
<div class="box dashboard-stat-card p-5" style="margin-top:1.5rem;">
|
|
133
|
+
<div class="is-flex is-justify-content-space-between is-align-items-center mb-3">
|
|
134
|
+
<h3 class="subtitle is-6 has-text-weight-bold" style="margin-bottom:0;">
|
|
135
|
+
<i class="fas fa-history"></i> {% translate '系统动态' %}
|
|
136
|
+
</h3>
|
|
137
|
+
<div class="is-flex is-align-items-center" style="gap:0.5rem;font-size:0.72rem;color:var(--yla-text-muted);">
|
|
138
|
+
<i class="fab {{ os_icon }}"></i>
|
|
139
|
+
<span>{{ client_ip|default:'—' }}</span>
|
|
140
|
+
<i class="fab {{ browser_icon }}" style="margin-left:0.5rem;"></i>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
<div class="timeline">
|
|
144
|
+
{% for log in recent_logs %}
|
|
145
|
+
<div class="timeline-item yla-log-row" style="display:flex;align-items:flex-start;gap:0.75rem;padding:0.6rem 0;{% if not forloop.last %}border-bottom:1px solid var(--yla-border-light);{% endif %}" data-log-id="{{ log.id }}">
|
|
146
|
+
<div class="timeline-dot" style="flex-shrink:0;width:8px;height:8px;border-radius:50%;background:{% if log.badge == 'success' %}#22c55e{% elif log.badge == 'danger' %}#ef4444{% elif log.badge == 'warning' %}#f59e0b{% else %}#3b82f6{% endif %};margin-top:0.45rem;"></div>
|
|
147
|
+
<div style="flex:1;min-width:0;">
|
|
148
|
+
<div style="display:flex;align-items:center;gap:0.5rem;flex-wrap:wrap;">
|
|
149
|
+
<strong style="font-size:0.82rem;">{{ log.user }}</strong>
|
|
150
|
+
<span class="yla-badge yla-badge-{{ log.badge }}" style="font-size:0.7rem;">{{ log.action }}</span>
|
|
151
|
+
{% if log.module %}
|
|
152
|
+
<span style="font-size:0.7rem;color:var(--yla-text-muted);">{{ log.module }}</span>
|
|
153
|
+
{% endif %}
|
|
154
|
+
</div>
|
|
155
|
+
<div style="font-size:0.82rem;color:var(--yla-text-primary);margin-top:2px;">
|
|
156
|
+
{{ log.object_name }}
|
|
157
|
+
</div>
|
|
158
|
+
{% if log.has_diff %}
|
|
159
|
+
<div class="yla-log-diff" style="display:none;margin-top:6px;padding:8px 10px;background:var(--yla-bg-hover);border-radius:6px;font-size:0.75rem;">
|
|
160
|
+
<div class="yla-diff-loading has-text-grey">{% translate '加载变更详情...' %}</div>
|
|
161
|
+
</div>
|
|
162
|
+
<a href="#" class="yla-log-expand is-size-7" style="display:inline-block;margin-top:4px;" data-log-id="{{ log.id }}">
|
|
163
|
+
<i class="fas fa-chevron-down"></i> {% translate '展开变更' %}
|
|
164
|
+
</a>
|
|
165
|
+
{% endif %}
|
|
166
|
+
<span style="font-size:0.7rem;color:var(--yla-text-muted);margin-left:0.5rem;white-space:nowrap;">{{ log.time|date:"m-d H:i" }}</span>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
{% endfor %}
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
{% endif %}
|
|
173
|
+
|
|
174
|
+
<div class="columns" style="margin-top:1.5rem;">
|
|
175
|
+
<div class="column is-8">
|
|
176
|
+
<div class="box dashboard-stat-card p-5" style="min-height:200px;overflow:hidden;">
|
|
177
|
+
<h3 class="subtitle is-6 has-text-weight-bold mb-2">
|
|
178
|
+
<i class="fas fa-calendar-alt"></i> {% translate '近半年操作热力图' %}
|
|
179
|
+
</h3>
|
|
180
|
+
<div id="heatmap-chart" style="width:100%;height:220px;"></div>
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
<div class="column is-4">
|
|
184
|
+
<div class="box dashboard-stat-card p-5" style="min-height:200px;overflow:hidden;">
|
|
185
|
+
<h3 class="subtitle is-6 has-text-weight-bold mb-2">
|
|
186
|
+
<i class="fas fa-chart-pie"></i> {% translate '近7天模块活跃度' %}
|
|
187
|
+
</h3>
|
|
188
|
+
<div id="module-pie-chart" style="width:100%;height:220px;"></div>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
</div>
|
|
194
|
+
{% endblock %}
|
|
195
|
+
|
|
196
|
+
{% block extra_js %}
|
|
197
|
+
{{ block.super }}
|
|
198
|
+
<script>
|
|
199
|
+
(function() {
|
|
200
|
+
var chartDom = document.getElementById('main-chart');
|
|
201
|
+
if (!chartDom) return;
|
|
202
|
+
var chart = null;
|
|
203
|
+
|
|
204
|
+
function getTheme() {
|
|
205
|
+
var d = document.documentElement.classList.contains('theme-dark');
|
|
206
|
+
return {
|
|
207
|
+
isDark: d,
|
|
208
|
+
text: d ? '#94a3b8' : '#64748b',
|
|
209
|
+
line: d ? '#60a5fa' : '#3b82f6',
|
|
210
|
+
grid: d ? '#1e293b' : '#f1f5f9',
|
|
211
|
+
axis: d ? '#334155' : '#e2e8f0',
|
|
212
|
+
bg: d ? '#1e293b' : '#fff',
|
|
213
|
+
bgBorder: d ? '#334155' : '#e2e8f0',
|
|
214
|
+
tipText: d ? '#e2e8f0' : '#0f172a',
|
|
215
|
+
area: d
|
|
216
|
+
? new echarts.graphic.LinearGradient(0,0,0,1,[{offset:0,color:'rgba(96,165,250,.25)'},{offset:1,color:'rgba(96,165,250,0)'}])
|
|
217
|
+
: new echarts.graphic.LinearGradient(0,0,0,1,[{offset:0,color:'rgba(59,130,246,.15)'},{offset:1,color:'rgba(59,130,246,0)'}])
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function buildOption(t) {
|
|
222
|
+
return {
|
|
223
|
+
tooltip: {
|
|
224
|
+
trigger: 'axis',
|
|
225
|
+
backgroundColor: t.bg, borderColor: t.bgBorder,
|
|
226
|
+
textStyle: { color: t.tipText, fontSize: 13 },
|
|
227
|
+
formatter: function(p) { return p[0].axisValue + ' 新增 <b>' + p[0].value + '</b> 人'; }
|
|
228
|
+
},
|
|
229
|
+
grid: { top: 15, right: 20, bottom: 25, left: 45 },
|
|
230
|
+
xAxis: {
|
|
231
|
+
type: 'category', boundaryGap: false,
|
|
232
|
+
data: {{ chart_labels|safe }},
|
|
233
|
+
axisLine: { lineStyle: { color: t.axis } },
|
|
234
|
+
axisTick: { show: false },
|
|
235
|
+
axisLabel: { color: t.text, fontSize: 11 }
|
|
236
|
+
},
|
|
237
|
+
yAxis: {
|
|
238
|
+
type: 'value', minInterval: 1,
|
|
239
|
+
splitLine: { lineStyle: { color: t.grid } },
|
|
240
|
+
axisLabel: { color: t.text, fontSize: 11 }
|
|
241
|
+
},
|
|
242
|
+
series: [{
|
|
243
|
+
data: {{ chart_data|safe }},
|
|
244
|
+
type: 'line', smooth: true,
|
|
245
|
+
symbol: 'circle', symbolSize: 6,
|
|
246
|
+
lineStyle: { color: t.line, width: 2.5 },
|
|
247
|
+
itemStyle: { color: t.line },
|
|
248
|
+
areaStyle: { color: t.area },
|
|
249
|
+
}]
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function render() {
|
|
254
|
+
if (chart) chart.dispose();
|
|
255
|
+
chart = echarts.init(chartDom);
|
|
256
|
+
chart.setOption(buildOption(getTheme()));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
render();
|
|
260
|
+
window.addEventListener('resize', function() { if (chart) chart.resize(); });
|
|
261
|
+
var observer = new MutationObserver(function() { render(); });
|
|
262
|
+
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
|
|
263
|
+
|
|
264
|
+
})();
|
|
265
|
+
|
|
266
|
+
// ── Activity heatmap ──
|
|
267
|
+
(function() {
|
|
268
|
+
var dom = document.getElementById('heatmap-chart');
|
|
269
|
+
if (!dom) return;
|
|
270
|
+
var chart = echarts.init(dom);
|
|
271
|
+
var data = {{ heatmap_data_json|default:'[]'|safe }};
|
|
272
|
+
|
|
273
|
+
function buildHeatmap() {
|
|
274
|
+
var d = document.documentElement.classList.contains('theme-dark');
|
|
275
|
+
var today = new Date();
|
|
276
|
+
var startDate = new Date(today.getTime() - 180 * 24 * 3600 * 1000);
|
|
277
|
+
|
|
278
|
+
// Build full date range
|
|
279
|
+
var dateMap = {};
|
|
280
|
+
data.forEach(function(item) { dateMap[item[0]] = item[1]; });
|
|
281
|
+
|
|
282
|
+
var seriesData = [];
|
|
283
|
+
for (var dt = new Date(startDate); dt <= today; dt.setDate(dt.getDate() + 1)) {
|
|
284
|
+
var ds = dt.toISOString().slice(0, 10);
|
|
285
|
+
seriesData.push([ds, dateMap[ds] || 0]);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
var option = {
|
|
289
|
+
tooltip: {
|
|
290
|
+
formatter: function(p) { return p.value[0] + '<br/>{% translate "操作" %}: <b>' + p.value[1] + '</b> {% translate "次" %}'; }
|
|
291
|
+
},
|
|
292
|
+
visualMap: {
|
|
293
|
+
min: 0, max: Math.max.apply(null, data.map(function(i){return i[1];}).concat([1])),
|
|
294
|
+
type: 'piecewise', orient: 'horizontal', left: 'center', bottom: 0,
|
|
295
|
+
textStyle: { color: d ? '#94a3b8' : '#555' },
|
|
296
|
+
pieces: [
|
|
297
|
+
{ value: 0, color: d ? '#1a1a2e' : '#e8e8e8' },
|
|
298
|
+
{ gt: 0, lte: 2, color: d ? '#1a4a1a' : '#6bc470' },
|
|
299
|
+
{ gt: 2, lte: 5, color: d ? '#2d6a1e' : '#2ea84a' },
|
|
300
|
+
{ gt: 5, lte: 10, color: d ? '#3d7b24' : '#1d8c36' },
|
|
301
|
+
{ gt: 10, color: d ? '#4d8c2a' : '#156b2a' }
|
|
302
|
+
]
|
|
303
|
+
},
|
|
304
|
+
calendar: {
|
|
305
|
+
top: 30, left: 40, right: 20, bottom: 30,
|
|
306
|
+
range: [(new Date(Date.now() - 180*86400000)).toISOString().slice(0,10), today.toISOString().slice(0,10)],
|
|
307
|
+
cellSize: [13, 13],
|
|
308
|
+
yearLabel: { show: false },
|
|
309
|
+
dayLabel: { fontSize: 9, color: d ? '#667' : '#999', firstDay: 1 },
|
|
310
|
+
monthLabel: { fontSize: 10, color: d ? '#667' : '#888', position: 'start', margin: 8 },
|
|
311
|
+
itemStyle: { borderColor: d ? '#2a2a3a' : '#ddd', borderWidth: 2 }
|
|
312
|
+
},
|
|
313
|
+
series: [{
|
|
314
|
+
type: 'heatmap', coordinateSystem: 'calendar',
|
|
315
|
+
data: seriesData,
|
|
316
|
+
label: { show: false }
|
|
317
|
+
}]
|
|
318
|
+
};
|
|
319
|
+
chart.setOption(option);
|
|
320
|
+
}
|
|
321
|
+
buildHeatmap();
|
|
322
|
+
window.addEventListener('resize', function() { chart.resize(); });
|
|
323
|
+
new MutationObserver(function() { buildHeatmap(); }).observe(document.documentElement, {attributes:true,attributeFilter:['class']});
|
|
324
|
+
})();
|
|
325
|
+
|
|
326
|
+
// ── Module rose chart ──
|
|
327
|
+
(function() {
|
|
328
|
+
var dom2 = document.getElementById('module-pie-chart');
|
|
329
|
+
if (!dom2) return;
|
|
330
|
+
var chart2 = echarts.init(dom2);
|
|
331
|
+
var moduleData = {{ module_stats_json|default:'[]'|safe }};
|
|
332
|
+
|
|
333
|
+
function buildRose() {
|
|
334
|
+
var d = document.documentElement.classList.contains('theme-dark');
|
|
335
|
+
chart2.setOption({
|
|
336
|
+
tooltip: { trigger: 'item', formatter: '{b}: {c} ' + '次' + ' ({d}%)' },
|
|
337
|
+
legend: { bottom: 0, textStyle: { fontSize: 9, color: d ? '#94a3b8' : '#64748b' } },
|
|
338
|
+
series: [{
|
|
339
|
+
type: 'pie',
|
|
340
|
+
roseType: 'area',
|
|
341
|
+
radius: ['15%', '70%'],
|
|
342
|
+
center: ['50%', '45%'],
|
|
343
|
+
data: moduleData.length ? moduleData : [{name: '无数据', value: 0}],
|
|
344
|
+
label: { fontSize: 9, color: d ? '#94a3b8' : '#555' },
|
|
345
|
+
emphasis: { label: { fontSize: 13, fontWeight: 'bold' } },
|
|
346
|
+
itemStyle: { borderRadius: 4, borderColor: d ? '#1a1a2e' : '#fff', borderWidth: 2 }
|
|
347
|
+
}]
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
buildRose();
|
|
351
|
+
window.addEventListener('resize', function() { chart2.resize(); });
|
|
352
|
+
new MutationObserver(function() { buildRose(); }).observe(document.documentElement, {attributes:true,attributeFilter:['class']});
|
|
353
|
+
})();
|
|
354
|
+
|
|
355
|
+
// ── Diff expand/collapse ──
|
|
356
|
+
(function($) {
|
|
357
|
+
$(document).on('click', '.yla-log-expand', function(e) {
|
|
358
|
+
e.preventDefault();
|
|
359
|
+
var $link = $(this);
|
|
360
|
+
var logId = $link.data('log-id');
|
|
361
|
+
var $row = $link.closest('.yla-log-row');
|
|
362
|
+
var $diff = $row.find('.yla-log-diff');
|
|
363
|
+
|
|
364
|
+
if ($diff.is(':visible')) {
|
|
365
|
+
$diff.slideUp(150);
|
|
366
|
+
$link.html('<i class="fas fa-chevron-down"></i> ' + '{% translate "展开变更" %}');
|
|
367
|
+
} else {
|
|
368
|
+
$diff.slideDown(150);
|
|
369
|
+
$link.html('<i class="fas fa-chevron-up"></i> ' + '{% translate "收起" %}');
|
|
370
|
+
|
|
371
|
+
// Load diff data if not yet loaded
|
|
372
|
+
if ($diff.find('.yla-diff-loading').length) {
|
|
373
|
+
$.getJSON('/admin/log-diff/' + logId + '/', function(data) {
|
|
374
|
+
if (data.error) {
|
|
375
|
+
$diff.html('<span class="has-text-grey">' + data.error + '</span>');
|
|
376
|
+
} else {
|
|
377
|
+
var html = data.changes.map(function(c) {
|
|
378
|
+
return '<div style="margin-bottom:3px;"><span style="color:var(--yla-text-muted);">' + c.field + '</span>: ' +
|
|
379
|
+
'<span style="text-decoration:line-through;color:#ef4444;">' + (c.old || '—') + '</span> ' +
|
|
380
|
+
'<i class="fas fa-arrow-right has-text-grey" style="font-size:0.6rem;"></i> ' +
|
|
381
|
+
'<span style="color:#22c55e;font-weight:500;">' + (c.new || '—') + '</span></div>';
|
|
382
|
+
}).join('');
|
|
383
|
+
if (!html) html = '<span class="has-text-grey">{% translate "无字段级变更记录" %}</span>';
|
|
384
|
+
$diff.html(html);
|
|
385
|
+
}
|
|
386
|
+
}).fail(function() {
|
|
387
|
+
$diff.html('<span class="has-text-grey">{% translate "加载失败" %}</span>');
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
})(jQuery);
|
|
393
|
+
</script>
|
|
394
|
+
{% endblock %}
|
|
@@ -3,7 +3,7 @@ from setuptools import setup, find_packages
|
|
|
3
3
|
|
|
4
4
|
setup(
|
|
5
5
|
name='yanleafadmin',
|
|
6
|
-
version='2.0.
|
|
6
|
+
version='2.0.5',
|
|
7
7
|
description='极简白色现代 Django Admin 主题体系 — 基于 Django + Bulma CSS',
|
|
8
8
|
long_description=open('README.md', encoding='utf-8').read() if __import__('os').path.exists('README.md') else '',
|
|
9
9
|
long_description_content_type='text/markdown',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: yanleafadmin
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.5
|
|
4
4
|
Summary: 极简白色现代 Django Admin 主题体系 — 基于 Django + Bulma CSS
|
|
5
5
|
Home-page: https://github.com/zhouyanye/yanleafadmin
|
|
6
6
|
Author: zhouyanye
|
|
@@ -53,7 +53,7 @@ License-File: LICENSE
|
|
|
53
53
|
### AI 数据助手
|
|
54
54
|
- **右下角悬浮聊天机器人**,点击即可对话
|
|
55
55
|
- **自然语言查询**:基于 DeepSeek 驱动,自动转 Django ORM
|
|
56
|
-
- **
|
|
56
|
+
- **ECharts 图表**:查询结果原地渲染折线图/饼图/柱状图
|
|
57
57
|
- **多模型支持**:DeepSeek Chat / Reasoner / GPT-4o / 通义千问
|
|
58
58
|
- **配置保存在浏览器**:API Key 不上传服务器
|
|
59
59
|
- **聊天历史**:localStorage 持久化最近 50 条对话
|
|
@@ -97,33 +97,49 @@ import os, sys
|
|
|
97
97
|
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
####
|
|
100
|
+
#### 一行启用(主题 + 仪表盘,开箱即用)
|
|
101
101
|
|
|
102
102
|
```python
|
|
103
103
|
INSTALLED_APPS = [
|
|
104
|
-
'yanleafadmin', #
|
|
104
|
+
'yanleafadmin', # 一行搞定主题和仪表盘
|
|
105
105
|
'django.contrib.admin',
|
|
106
106
|
'django.contrib.auth',
|
|
107
107
|
'django.contrib.contenttypes',
|
|
108
108
|
'django.contrib.sessions',
|
|
109
109
|
'django.contrib.messages',
|
|
110
110
|
'django.contrib.staticfiles',
|
|
111
|
-
'captcha', # 登录验证码
|
|
112
|
-
'apps.users.apps.UsersConfig',
|
|
113
|
-
'apps.dashboard_engine.apps.DashboardEngineConfig',
|
|
114
111
|
]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
> 仪表盘首页自动启用(统计卡片 + 热力图 + 玫瑰图 + 系统动态时间线)。
|
|
115
|
+
|
|
116
|
+
#### 可选:启用语言切换
|
|
115
117
|
|
|
116
|
-
|
|
118
|
+
```python
|
|
119
|
+
# urls.py
|
|
120
|
+
urlpatterns = [
|
|
121
|
+
path('i18n/', include('django.conf.urls.i18n')),
|
|
122
|
+
...
|
|
123
|
+
]
|
|
117
124
|
```
|
|
118
125
|
|
|
119
|
-
####
|
|
126
|
+
#### 可选:启用登录验证码
|
|
120
127
|
|
|
121
|
-
|
|
128
|
+
```python
|
|
129
|
+
INSTALLED_APPS += ['captcha']
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
#### 可选:启用 ER 图 / AI 助手 / SmartChart
|
|
133
|
+
|
|
134
|
+
这些功能需要额外配置 URL 路由:
|
|
122
135
|
|
|
123
136
|
```python
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
137
|
+
# urls.py
|
|
138
|
+
urlpatterns = [
|
|
139
|
+
...
|
|
140
|
+
path('admin/erd/', include('apps.erd_engine.urls')), # ER 图
|
|
141
|
+
path('api/ai/', include('apps.ai_assistant.urls')), # AI 助手
|
|
142
|
+
path('yla-api/', include('apps.theme.urls')), # SmartChart
|
|
127
143
|
]
|
|
128
144
|
```
|
|
129
145
|
|
|
@@ -82,9 +82,11 @@ apps/theme/static/yanleafadmin/vendor/select2/js/select2.min.js
|
|
|
82
82
|
apps/theme/static/yanleafadmin/vendor/sweetalert2/sweetalert2.all.min.js
|
|
83
83
|
apps/theme/templates/admin/app_index.html
|
|
84
84
|
apps/theme/templates/admin/base.html
|
|
85
|
+
apps/theme/templates/admin/base_site.html
|
|
85
86
|
apps/theme/templates/admin/change_form.html
|
|
86
87
|
apps/theme/templates/admin/change_list.html
|
|
87
88
|
apps/theme/templates/admin/change_list_results.html
|
|
89
|
+
apps/theme/templates/admin/dashboard_index.html
|
|
88
90
|
apps/theme/templates/admin/login.html
|
|
89
91
|
apps/theme/templates/admin/pagination.html
|
|
90
92
|
apps/theme/templates/admin/auth/user/add_form.html
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/dashboard_engine/templates/admin/base_site.html
RENAMED
|
File without changes
|
{yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/dashboard_engine/templates/admin/dashboard_index.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/css/change-form.css
RENAMED
|
File without changes
|
{yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/css/change-list.css
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/css/password-form.css
RENAMED
|
File without changes
|
{yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/css/user-change-form.css
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/js/filter-widget.js
RENAMED
|
File without changes
|
{yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/js/i18n/datatables.en.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/js/password-check.js
RENAMED
|
File without changes
|
|
File without changes
|
{yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/js/user-password-field.js
RENAMED
|
File without changes
|
{yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/bulma/bulma.min.css
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/jquery/jquery.min.js
RENAMED
|
File without changes
|
{yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/static/yanleafadmin/vendor/jszip/jszip.min.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templates/admin/auth/user/add_form.html
RENAMED
|
File without changes
|
{yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templates/admin/auth/user/change_form.html
RENAMED
|
File without changes
|
{yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templates/admin/auth/user/change_password.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templates/admin/change_list_results.html
RENAMED
|
File without changes
|
{yanleafadmin-2.0.3 → yanleafadmin-2.0.5}/apps/theme/templates/admin/edit_inline/tabular.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|