maque 0.2.1__py3-none-any.whl
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.
- maque/__init__.py +30 -0
- maque/__main__.py +926 -0
- maque/ai_platform/__init__.py +0 -0
- maque/ai_platform/crawl.py +45 -0
- maque/ai_platform/metrics.py +258 -0
- maque/ai_platform/nlp_preprocess.py +67 -0
- maque/ai_platform/webpage_screen_shot.py +195 -0
- maque/algorithms/__init__.py +78 -0
- maque/algorithms/bezier.py +15 -0
- maque/algorithms/bktree.py +117 -0
- maque/algorithms/core.py +104 -0
- maque/algorithms/hilbert.py +16 -0
- maque/algorithms/rate_function.py +92 -0
- maque/algorithms/transform.py +27 -0
- maque/algorithms/trie.py +272 -0
- maque/algorithms/utils.py +63 -0
- maque/algorithms/video.py +587 -0
- maque/api/__init__.py +1 -0
- maque/api/common.py +110 -0
- maque/api/fetch.py +26 -0
- maque/api/static/icon.png +0 -0
- maque/api/static/redoc.standalone.js +1782 -0
- maque/api/static/swagger-ui-bundle.js +3 -0
- maque/api/static/swagger-ui.css +3 -0
- maque/cli/__init__.py +1 -0
- maque/cli/clean_invisible_chars.py +324 -0
- maque/cli/core.py +34 -0
- maque/cli/groups/__init__.py +26 -0
- maque/cli/groups/config.py +205 -0
- maque/cli/groups/data.py +615 -0
- maque/cli/groups/doctor.py +259 -0
- maque/cli/groups/embedding.py +222 -0
- maque/cli/groups/git.py +29 -0
- maque/cli/groups/help.py +410 -0
- maque/cli/groups/llm.py +223 -0
- maque/cli/groups/mcp.py +241 -0
- maque/cli/groups/mllm.py +1795 -0
- maque/cli/groups/mllm_simple.py +60 -0
- maque/cli/groups/quant.py +210 -0
- maque/cli/groups/service.py +490 -0
- maque/cli/groups/system.py +570 -0
- maque/cli/mllm_run.py +1451 -0
- maque/cli/script.py +52 -0
- maque/cli/tree.py +49 -0
- maque/clustering/__init__.py +52 -0
- maque/clustering/analyzer.py +347 -0
- maque/clustering/clusterers.py +464 -0
- maque/clustering/sampler.py +134 -0
- maque/clustering/visualizer.py +205 -0
- maque/constant.py +13 -0
- maque/core.py +133 -0
- maque/cv/__init__.py +1 -0
- maque/cv/image.py +219 -0
- maque/cv/utils.py +68 -0
- maque/cv/video/__init__.py +3 -0
- maque/cv/video/keyframe_extractor.py +368 -0
- maque/embedding/__init__.py +43 -0
- maque/embedding/base.py +56 -0
- maque/embedding/multimodal.py +308 -0
- maque/embedding/server.py +523 -0
- maque/embedding/text.py +311 -0
- maque/git/__init__.py +24 -0
- maque/git/pure_git.py +912 -0
- maque/io/__init__.py +29 -0
- maque/io/core.py +38 -0
- maque/io/ops.py +194 -0
- maque/llm/__init__.py +111 -0
- maque/llm/backend.py +416 -0
- maque/llm/base.py +411 -0
- maque/llm/server.py +366 -0
- maque/mcp_server.py +1096 -0
- maque/mllm_data_processor_pipeline/__init__.py +17 -0
- maque/mllm_data_processor_pipeline/core.py +341 -0
- maque/mllm_data_processor_pipeline/example.py +291 -0
- maque/mllm_data_processor_pipeline/steps/__init__.py +56 -0
- maque/mllm_data_processor_pipeline/steps/data_alignment.py +267 -0
- maque/mllm_data_processor_pipeline/steps/data_loader.py +172 -0
- maque/mllm_data_processor_pipeline/steps/data_validation.py +304 -0
- maque/mllm_data_processor_pipeline/steps/format_conversion.py +411 -0
- maque/mllm_data_processor_pipeline/steps/mllm_annotation.py +331 -0
- maque/mllm_data_processor_pipeline/steps/mllm_refinement.py +446 -0
- maque/mllm_data_processor_pipeline/steps/result_validation.py +501 -0
- maque/mllm_data_processor_pipeline/web_app.py +317 -0
- maque/nlp/__init__.py +14 -0
- maque/nlp/ngram.py +9 -0
- maque/nlp/parser.py +63 -0
- maque/nlp/risk_matcher.py +543 -0
- maque/nlp/sentence_splitter.py +202 -0
- maque/nlp/simple_tradition_cvt.py +31 -0
- maque/performance/__init__.py +21 -0
- maque/performance/_measure_time.py +70 -0
- maque/performance/_profiler.py +367 -0
- maque/performance/_stat_memory.py +51 -0
- maque/pipelines/__init__.py +15 -0
- maque/pipelines/clustering.py +252 -0
- maque/quantization/__init__.py +42 -0
- maque/quantization/auto_round.py +120 -0
- maque/quantization/base.py +145 -0
- maque/quantization/bitsandbytes.py +127 -0
- maque/quantization/llm_compressor.py +102 -0
- maque/retriever/__init__.py +35 -0
- maque/retriever/chroma.py +654 -0
- maque/retriever/document.py +140 -0
- maque/retriever/milvus.py +1140 -0
- maque/table_ops/__init__.py +1 -0
- maque/table_ops/core.py +133 -0
- maque/table_viewer/__init__.py +4 -0
- maque/table_viewer/download_assets.py +57 -0
- maque/table_viewer/server.py +698 -0
- maque/table_viewer/static/element-plus-icons.js +5791 -0
- maque/table_viewer/static/element-plus.css +1 -0
- maque/table_viewer/static/element-plus.js +65236 -0
- maque/table_viewer/static/main.css +268 -0
- maque/table_viewer/static/main.js +669 -0
- maque/table_viewer/static/vue.global.js +18227 -0
- maque/table_viewer/templates/index.html +401 -0
- maque/utils/__init__.py +56 -0
- maque/utils/color.py +68 -0
- maque/utils/color_string.py +45 -0
- maque/utils/compress.py +66 -0
- maque/utils/constant.py +183 -0
- maque/utils/core.py +261 -0
- maque/utils/cursor.py +143 -0
- maque/utils/distance.py +58 -0
- maque/utils/docker.py +96 -0
- maque/utils/downloads.py +51 -0
- maque/utils/excel_helper.py +542 -0
- maque/utils/helper_metrics.py +121 -0
- maque/utils/helper_parser.py +168 -0
- maque/utils/net.py +64 -0
- maque/utils/nvidia_stat.py +140 -0
- maque/utils/ops.py +53 -0
- maque/utils/packages.py +31 -0
- maque/utils/path.py +57 -0
- maque/utils/tar.py +260 -0
- maque/utils/untar.py +129 -0
- maque/web/__init__.py +0 -0
- maque/web/image_downloader.py +1410 -0
- maque-0.2.1.dist-info/METADATA +450 -0
- maque-0.2.1.dist-info/RECORD +143 -0
- maque-0.2.1.dist-info/WHEEL +4 -0
- maque-0.2.1.dist-info/entry_points.txt +3 -0
- maque-0.2.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Sparrow Table Viewer</title>
|
|
7
|
+
<script src="/static/vue.global.js"></script>
|
|
8
|
+
<script src="/static/element-plus.js"></script>
|
|
9
|
+
<script src="/static/element-plus-icons.js"></script>
|
|
10
|
+
<link rel="stylesheet" href="/static/element-plus.css" />
|
|
11
|
+
<link rel="stylesheet" href="/static/main.css" />
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
{% raw %}
|
|
15
|
+
<div id="app">
|
|
16
|
+
<div class="main-container">
|
|
17
|
+
<!-- 头部 -->
|
|
18
|
+
<div class="header">
|
|
19
|
+
<div style="display: flex; align-items: center; gap: 16px;">
|
|
20
|
+
<h1 style="margin: 0; font-size: 20px; color: #303133;">
|
|
21
|
+
📊 Sparrow Table Viewer
|
|
22
|
+
</h1>
|
|
23
|
+
<el-tag v-if="tableInfo" type="info">{{ tableInfo.file_path }}</el-tag>
|
|
24
|
+
</div>
|
|
25
|
+
<div style="display: flex; gap: 12px;">
|
|
26
|
+
<el-button @click="showUploadDialog = true" type="success" size="small">
|
|
27
|
+
上传文件
|
|
28
|
+
</el-button>
|
|
29
|
+
<el-button @click="resetTable" type="warning" size="small">
|
|
30
|
+
重置
|
|
31
|
+
</el-button>
|
|
32
|
+
<el-button @click="saveTable" type="primary" size="small">
|
|
33
|
+
保存
|
|
34
|
+
</el-button>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<!-- 内容区域 -->
|
|
39
|
+
<div class="content">
|
|
40
|
+
<!-- 工具栏 -->
|
|
41
|
+
<div class="toolbar">
|
|
42
|
+
<el-button @click="refreshData" size="small" :loading="loading">
|
|
43
|
+
刷新数据
|
|
44
|
+
</el-button>
|
|
45
|
+
|
|
46
|
+
<el-divider direction="vertical"></el-divider>
|
|
47
|
+
|
|
48
|
+
<span style="color: #606266; font-size: 14px;">每页显示:</span>
|
|
49
|
+
<el-select v-model="pagination.pageSize" @change="loadTableData" size="small" style="width: 100px;">
|
|
50
|
+
<el-option :value="50" label="50"></el-option>
|
|
51
|
+
<el-option :value="100" label="100"></el-option>
|
|
52
|
+
<el-option :value="200" label="200"></el-option>
|
|
53
|
+
<el-option :value="500" label="500"></el-option>
|
|
54
|
+
</el-select>
|
|
55
|
+
|
|
56
|
+
<el-divider direction="vertical"></el-divider>
|
|
57
|
+
|
|
58
|
+
<el-button @click="showFilterDialog = true" size="small" type="info">
|
|
59
|
+
行筛选 ({{ activeFilters.length }})
|
|
60
|
+
</el-button>
|
|
61
|
+
|
|
62
|
+
<el-button @click="clearFilters" size="small" v-if="activeFilters.length > 0">
|
|
63
|
+
清除行筛选
|
|
64
|
+
</el-button>
|
|
65
|
+
|
|
66
|
+
<el-divider direction="vertical"></el-divider>
|
|
67
|
+
|
|
68
|
+
<el-button @click="showColumnDialog = true" size="small" type="success">
|
|
69
|
+
列显示 ({{ visibleColumns.length }}/{{ allColumns.length }})
|
|
70
|
+
</el-button>
|
|
71
|
+
|
|
72
|
+
<el-button @click="resetColumns" size="small" v-if="visibleColumns.length < allColumns.length">
|
|
73
|
+
显示全部列
|
|
74
|
+
</el-button>
|
|
75
|
+
|
|
76
|
+
<el-divider direction="vertical" v-if="tableInfo && tableInfo.image_columns && tableInfo.image_columns.length"></el-divider>
|
|
77
|
+
|
|
78
|
+
<!-- 图像设置 -->
|
|
79
|
+
<div v-if="tableInfo && tableInfo.image_columns && tableInfo.image_columns.length" style="display: flex; align-items: center; gap: 16px;">
|
|
80
|
+
<!-- 图像尺寸设置 -->
|
|
81
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
82
|
+
<span style="color: #606266; font-size: 14px;">图像尺寸:</span>
|
|
83
|
+
<el-select v-model="imageSize" @change="onImageSizeChange" size="small" style="width: 140px;">
|
|
84
|
+
<el-option value="small" label="小 (80x60)"></el-option>
|
|
85
|
+
<el-option value="medium" label="中 (120x90)"></el-option>
|
|
86
|
+
<el-option value="large" label="大 (160x120)"></el-option>
|
|
87
|
+
<el-option value="xlarge" label="超大 (200x150)"></el-option>
|
|
88
|
+
<el-option value="xxlarge" label="最大 (400x300)"></el-option>
|
|
89
|
+
</el-select>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<!-- 分隔符设置 -->
|
|
93
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
94
|
+
<span style="color: #606266; font-size: 14px;">分隔符:</span>
|
|
95
|
+
<el-select v-model="imageSeparator" @change="onSeparatorChange" @update:model-value="onSeparatorChange" size="small" style="width: 120px;">
|
|
96
|
+
<el-option value="auto" label="自动检测"></el-option>
|
|
97
|
+
<el-option value="semicolon" label="分号 ;"></el-option>
|
|
98
|
+
<el-option value="newline" label="换行符"></el-option>
|
|
99
|
+
<el-option value="custom" label="自定义"></el-option>
|
|
100
|
+
</el-select>
|
|
101
|
+
|
|
102
|
+
<!-- 自定义分隔符输入 -->
|
|
103
|
+
<el-input
|
|
104
|
+
v-if="imageSeparator === 'custom'"
|
|
105
|
+
v-model="customSeparator"
|
|
106
|
+
@change="onSeparatorChange"
|
|
107
|
+
placeholder="输入分隔符"
|
|
108
|
+
size="small"
|
|
109
|
+
style="width: 100px;"
|
|
110
|
+
></el-input>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<!-- 统计信息 -->
|
|
116
|
+
<div class="stats-info" v-if="tableInfo">
|
|
117
|
+
<span style="margin-right: 24px;">
|
|
118
|
+
<strong>总行数:</strong> {{ tableData.total || 0 }} 行
|
|
119
|
+
</span>
|
|
120
|
+
<span style="margin-right: 24px;">
|
|
121
|
+
<strong>显示列数:</strong> {{ visibleColumns.length }}/{{ allColumns.length }} 列
|
|
122
|
+
</span>
|
|
123
|
+
<span v-if="tableInfo.image_columns && tableInfo.image_columns.length">
|
|
124
|
+
<strong>图片列:</strong> {{ tableInfo.image_columns.join(', ') }}
|
|
125
|
+
</span>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<!-- 表格容器 -->
|
|
129
|
+
<div class="table-container">
|
|
130
|
+
<el-table
|
|
131
|
+
:data="tableData.data"
|
|
132
|
+
stripe
|
|
133
|
+
border
|
|
134
|
+
:loading="loading"
|
|
135
|
+
style="width: 100%;"
|
|
136
|
+
max-height="calc(100vh - 320px)"
|
|
137
|
+
size="small"
|
|
138
|
+
>
|
|
139
|
+
<el-table-column
|
|
140
|
+
v-for="column in tableColumns"
|
|
141
|
+
:key="column"
|
|
142
|
+
:prop="column"
|
|
143
|
+
:label="column"
|
|
144
|
+
:width="getColumnWidth(column)"
|
|
145
|
+
:sortable="false"
|
|
146
|
+
show-overflow-tooltip
|
|
147
|
+
>
|
|
148
|
+
<template #header="{ column: headerColumn }">
|
|
149
|
+
<div style="display: flex; align-items: center; gap: 8px; justify-content: space-between; width: 100%;">
|
|
150
|
+
<span style="flex: 1;">{{ headerColumn.label }}</span>
|
|
151
|
+
<div style="display: flex; align-items: center; gap: 4px;">
|
|
152
|
+
<!-- 排序按钮 -->
|
|
153
|
+
<el-button-group size="small">
|
|
154
|
+
<el-button
|
|
155
|
+
@click.stop="handleSort(headerColumn.label, 'asc')"
|
|
156
|
+
:type="sortConfig.prop === headerColumn.label && sortConfig.order === 'ascending' ? 'primary' : ''"
|
|
157
|
+
size="small"
|
|
158
|
+
style="padding: 2px 4px; font-size: 10px;"
|
|
159
|
+
title="升序"
|
|
160
|
+
>
|
|
161
|
+
↑
|
|
162
|
+
</el-button>
|
|
163
|
+
<el-button
|
|
164
|
+
@click.stop="handleSort(headerColumn.label, 'desc')"
|
|
165
|
+
:type="sortConfig.prop === headerColumn.label && sortConfig.order === 'descending' ? 'primary' : ''"
|
|
166
|
+
size="small"
|
|
167
|
+
style="padding: 2px 4px; font-size: 10px;"
|
|
168
|
+
title="降序"
|
|
169
|
+
>
|
|
170
|
+
↓
|
|
171
|
+
</el-button>
|
|
172
|
+
</el-button-group>
|
|
173
|
+
|
|
174
|
+
<!-- 筛选按钮 -->
|
|
175
|
+
<el-button
|
|
176
|
+
@click.stop="addColumnFilter(headerColumn.label)"
|
|
177
|
+
size="small"
|
|
178
|
+
type="text"
|
|
179
|
+
style="padding: 2px 4px; font-size: 10px;"
|
|
180
|
+
title="筛选"
|
|
181
|
+
>
|
|
182
|
+
🔍
|
|
183
|
+
</el-button>
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
</template>
|
|
187
|
+
|
|
188
|
+
<template #default="scope">
|
|
189
|
+
<div v-if="isImageColumn(column)">
|
|
190
|
+
<div v-if="scope.row[column] && scope.row[column].paths && scope.row[column].paths.length > 0" class="multi-images-container">
|
|
191
|
+
<template v-for="(imagePath, index) in scope.row[column].paths" :key="index">
|
|
192
|
+
<!-- 图像加载成功时显示图片 -->
|
|
193
|
+
<img
|
|
194
|
+
v-if="isImageLoaded(imagePath)"
|
|
195
|
+
:src="`/api/image/proxy?url=${encodeURIComponent(imagePath)}`"
|
|
196
|
+
:alt="imagePath"
|
|
197
|
+
class="multi-image-item"
|
|
198
|
+
@click="showImageDialog(imagePath, scope.row[column].paths, index)"
|
|
199
|
+
/>
|
|
200
|
+
<!-- 图像未加载成功时显示原始URL字符串 -->
|
|
201
|
+
<div
|
|
202
|
+
v-else
|
|
203
|
+
style="color: #666; font-size: 12px; word-break: break-all; line-height: 1.4; cursor: pointer; margin-right: 8px; padding: 4px;"
|
|
204
|
+
@click="showImageDialog(imagePath, scope.row[column].paths, index)"
|
|
205
|
+
>
|
|
206
|
+
{{ imagePath }}
|
|
207
|
+
</div>
|
|
208
|
+
<!-- 隐藏的图片元素用于检测加载状态 -->
|
|
209
|
+
<img
|
|
210
|
+
v-if="!isImageLoaded(imagePath)"
|
|
211
|
+
:src="`/api/image/proxy?url=${encodeURIComponent(imagePath)}`"
|
|
212
|
+
style="display: none;"
|
|
213
|
+
@load="onImageLoadSuccess(imagePath)"
|
|
214
|
+
@error="onImageLoadError(imagePath)"
|
|
215
|
+
/>
|
|
216
|
+
</template>
|
|
217
|
+
</div>
|
|
218
|
+
<div v-else style="color: #666; font-size: 12px; word-break: break-all; line-height: 1.4; padding: 4px;">
|
|
219
|
+
{{ scope.row[column] && scope.row[column].original ? scope.row[column].original : (scope.row[column] || '') }}
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
<div v-else>
|
|
223
|
+
<input
|
|
224
|
+
v-if="editingCell && editingCell.row === scope.$index && editingCell.column === column"
|
|
225
|
+
v-model="editingValue"
|
|
226
|
+
@blur="saveCell(scope.row._index, column)"
|
|
227
|
+
@keyup.enter="saveCell(scope.row._index, column)"
|
|
228
|
+
@keyup.escape="cancelEdit"
|
|
229
|
+
class="cell-editor"
|
|
230
|
+
ref="cellInput"
|
|
231
|
+
/>
|
|
232
|
+
<span
|
|
233
|
+
v-else
|
|
234
|
+
@dblclick="startEdit(scope.$index, column, scope.row[column])"
|
|
235
|
+
style="cursor: pointer; display: block; min-height: 20px;"
|
|
236
|
+
>
|
|
237
|
+
{{ scope.row[column] || '' }}
|
|
238
|
+
</span>
|
|
239
|
+
</div>
|
|
240
|
+
</template>
|
|
241
|
+
</el-table-column>
|
|
242
|
+
</el-table>
|
|
243
|
+
|
|
244
|
+
<!-- 分页 -->
|
|
245
|
+
<div style="margin-top: 20px; display: flex; justify-content: center;">
|
|
246
|
+
<el-pagination
|
|
247
|
+
v-model:current-page="pagination.currentPage"
|
|
248
|
+
v-model:page-size="pagination.pageSize"
|
|
249
|
+
:page-sizes="[50, 100, 200, 500]"
|
|
250
|
+
layout="total, sizes, prev, pager, next, jumper"
|
|
251
|
+
:total="tableData.total"
|
|
252
|
+
@size-change="handleSizeChange"
|
|
253
|
+
@current-change="handleCurrentChange"
|
|
254
|
+
/>
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
<!-- 筛选对话框 -->
|
|
261
|
+
<el-dialog v-model="showFilterDialog" title="高级筛选" width="600px">
|
|
262
|
+
<div v-for="(filter, index) in filterConfigs" :key="index" style="margin-bottom: 16px;">
|
|
263
|
+
<el-row :gutter="12">
|
|
264
|
+
<el-col :span="6">
|
|
265
|
+
<el-select v-model="filter.column" placeholder="选择列">
|
|
266
|
+
<el-option v-for="col in tableColumns" :key="col" :value="col" :label="col"></el-option>
|
|
267
|
+
</el-select>
|
|
268
|
+
</el-col>
|
|
269
|
+
<el-col :span="6">
|
|
270
|
+
<el-select v-model="filter.operator" placeholder="操作符">
|
|
271
|
+
<el-option value="contains" label="包含"></el-option>
|
|
272
|
+
<el-option value="eq" label="等于"></el-option>
|
|
273
|
+
<el-option value="ne" label="不等于"></el-option>
|
|
274
|
+
<el-option value="startswith" label="开头是"></el-option>
|
|
275
|
+
<el-option value="endswith" label="结尾是"></el-option>
|
|
276
|
+
<el-option value="gt" label="大于"></el-option>
|
|
277
|
+
<el-option value="lt" label="小于"></el-option>
|
|
278
|
+
<el-option value="ge" label="大于等于"></el-option>
|
|
279
|
+
<el-option value="le" label="小于等于"></el-option>
|
|
280
|
+
</el-select>
|
|
281
|
+
</el-col>
|
|
282
|
+
<el-col :span="8">
|
|
283
|
+
<el-input v-model="filter.value" placeholder="筛选值"></el-input>
|
|
284
|
+
</el-col>
|
|
285
|
+
<el-col :span="4">
|
|
286
|
+
<el-button @click="removeFilter(index)" type="danger" size="small">删除</el-button>
|
|
287
|
+
</el-col>
|
|
288
|
+
</el-row>
|
|
289
|
+
</div>
|
|
290
|
+
|
|
291
|
+
<el-button @click="addFilter" type="primary" size="small">添加筛选条件</el-button>
|
|
292
|
+
|
|
293
|
+
<template #footer>
|
|
294
|
+
<el-button @click="showFilterDialog = false">取消</el-button>
|
|
295
|
+
<el-button @click="applyFilters" type="primary">应用筛选</el-button>
|
|
296
|
+
</template>
|
|
297
|
+
</el-dialog>
|
|
298
|
+
|
|
299
|
+
<!-- 列显示管理对话框 -->
|
|
300
|
+
<el-dialog v-model="showColumnDialog" title="列显示管理" width="500px">
|
|
301
|
+
<div style="margin-bottom: 16px;">
|
|
302
|
+
<el-button @click="selectAllColumns" size="small" type="primary">全选</el-button>
|
|
303
|
+
<el-button @click="selectNoColumns" size="small">全不选</el-button>
|
|
304
|
+
<span style="margin-left: 16px; color: #666;">
|
|
305
|
+
已选择 {{ visibleColumns.length }} / {{ allColumns.length }} 列
|
|
306
|
+
</span>
|
|
307
|
+
</div>
|
|
308
|
+
|
|
309
|
+
<div style="max-height: 400px; overflow-y: auto; border: 1px solid #ddd; padding: 12px; border-radius: 4px;">
|
|
310
|
+
<el-row :gutter="12">
|
|
311
|
+
<el-col :span="12" v-for="column in allColumns" :key="column" style="margin-bottom: 8px;">
|
|
312
|
+
<el-checkbox
|
|
313
|
+
:model-value="visibleColumns.includes(column)"
|
|
314
|
+
@change="toggleColumn(column)"
|
|
315
|
+
style="width: 100%;"
|
|
316
|
+
>
|
|
317
|
+
<span :style="{ fontWeight: tableInfo && tableInfo.image_columns && tableInfo.image_columns.includes(column) ? 'bold' : 'normal', color: tableInfo && tableInfo.image_columns && tableInfo.image_columns.includes(column) ? '#409eff' : '' }">
|
|
318
|
+
{{ column }}
|
|
319
|
+
<el-tag v-if="tableInfo && tableInfo.image_columns && tableInfo.image_columns.includes(column)" size="small" type="primary">图片</el-tag>
|
|
320
|
+
</span>
|
|
321
|
+
</el-checkbox>
|
|
322
|
+
</el-col>
|
|
323
|
+
</el-row>
|
|
324
|
+
</div>
|
|
325
|
+
|
|
326
|
+
<template #footer>
|
|
327
|
+
<el-button @click="showColumnDialog = false">取消</el-button>
|
|
328
|
+
<el-button @click="resetColumns" type="warning">重置</el-button>
|
|
329
|
+
<el-button @click="applyColumnFilter" type="primary" :disabled="visibleColumns.length === 0">应用</el-button>
|
|
330
|
+
</template>
|
|
331
|
+
</el-dialog>
|
|
332
|
+
|
|
333
|
+
<!-- 图片查看对话框 -->
|
|
334
|
+
<el-dialog v-model="showImagePreview" :title="`${currentImageIndex + 1}/${currentImageList.length} - ${currentImageUrl}`" width="90%">
|
|
335
|
+
<div style="position: relative; text-align: center;">
|
|
336
|
+
<!-- 左箭头按钮 -->
|
|
337
|
+
<el-button
|
|
338
|
+
v-if="currentImageList.length > 1 && currentImageIndex > 0"
|
|
339
|
+
@click="showPreviousImage"
|
|
340
|
+
type="primary"
|
|
341
|
+
size="large"
|
|
342
|
+
circle
|
|
343
|
+
style="position: absolute; left: 20px; top: 50%; transform: translateY(-50%); z-index: 10;"
|
|
344
|
+
>
|
|
345
|
+
←
|
|
346
|
+
</el-button>
|
|
347
|
+
|
|
348
|
+
<!-- 图片 -->
|
|
349
|
+
<img
|
|
350
|
+
:src="`/api/image/proxy?url=${encodeURIComponent(currentImageUrl)}`"
|
|
351
|
+
style="max-width: 100%; max-height: 100vh; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15);"
|
|
352
|
+
@error="handleImageError"
|
|
353
|
+
/>
|
|
354
|
+
|
|
355
|
+
<!-- 右箭头按钮 -->
|
|
356
|
+
<el-button
|
|
357
|
+
v-if="currentImageList.length > 1 && currentImageIndex < currentImageList.length - 1"
|
|
358
|
+
@click="showNextImage"
|
|
359
|
+
type="primary"
|
|
360
|
+
size="large"
|
|
361
|
+
circle
|
|
362
|
+
style="position: absolute; right: 20px; top: 50%; transform: translateY(-50%); z-index: 10;"
|
|
363
|
+
>
|
|
364
|
+
→
|
|
365
|
+
</el-button>
|
|
366
|
+
</div>
|
|
367
|
+
</el-dialog>
|
|
368
|
+
|
|
369
|
+
<!-- 文件上传对话框 -->
|
|
370
|
+
<el-dialog v-model="showUploadDialog" title="上传表格文件" width="600px">
|
|
371
|
+
<div class="upload-area"
|
|
372
|
+
@click="triggerFileInput"
|
|
373
|
+
@drop="handleFileDrop"
|
|
374
|
+
@dragover="handleDragOver"
|
|
375
|
+
@dragleave="handleDragLeave"
|
|
376
|
+
:class="{ 'dragover': isDragOver }">
|
|
377
|
+
<div class="upload-icon">📁</div>
|
|
378
|
+
<div class="upload-text">
|
|
379
|
+
<div>点击选择文件或拖拽文件到此处</div>
|
|
380
|
+
<div class="upload-hint">支持 .xlsx, .xls, .csv 格式</div>
|
|
381
|
+
</div>
|
|
382
|
+
</div>
|
|
383
|
+
|
|
384
|
+
<input type="file"
|
|
385
|
+
ref="fileInput"
|
|
386
|
+
@change="handleFileSelect"
|
|
387
|
+
accept=".xlsx,.xls,.csv"
|
|
388
|
+
style="display: none;">
|
|
389
|
+
|
|
390
|
+
<template #footer>
|
|
391
|
+
<span class="dialog-footer">
|
|
392
|
+
<el-button @click="showUploadDialog = false">取消</el-button>
|
|
393
|
+
</span>
|
|
394
|
+
</template>
|
|
395
|
+
</el-dialog>
|
|
396
|
+
</div>
|
|
397
|
+
{% endraw %}
|
|
398
|
+
|
|
399
|
+
<script src="/static/main.js"></script>
|
|
400
|
+
</body>
|
|
401
|
+
</html>
|
maque/utils/__init__.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""
|
|
2
|
+
工具模块 - 通用工具和辅助功能
|
|
3
|
+
|
|
4
|
+
整合了各种实用工具:颜色处理、字符串操作、距离计算、文件操作等
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# 基础工具(仅依赖标准库或核心依赖)
|
|
8
|
+
try:
|
|
9
|
+
from .compress import *
|
|
10
|
+
from .cursor import *
|
|
11
|
+
from .net import *
|
|
12
|
+
from .time import *
|
|
13
|
+
from .distance import *
|
|
14
|
+
from .ops import * # string ops
|
|
15
|
+
from .tar import *
|
|
16
|
+
from .untar import *
|
|
17
|
+
except ImportError:
|
|
18
|
+
pass # 静默处理,避免启动时警告
|
|
19
|
+
|
|
20
|
+
# 需要 pandas 的模块(可选)
|
|
21
|
+
try:
|
|
22
|
+
from .excel_helper import *
|
|
23
|
+
from .helper_metrics import *
|
|
24
|
+
from .helper_parser import *
|
|
25
|
+
except ImportError:
|
|
26
|
+
pass # pandas 未安装时静默跳过
|
|
27
|
+
|
|
28
|
+
# 核心工具(可能有循环依赖)
|
|
29
|
+
try:
|
|
30
|
+
from .core import async_retry # 只导入关键函数
|
|
31
|
+
except ImportError:
|
|
32
|
+
# 简单的备用实现
|
|
33
|
+
def async_retry(retry_times=3, retry_delay=1.0):
|
|
34
|
+
def decorator(func):
|
|
35
|
+
return func
|
|
36
|
+
return decorator
|
|
37
|
+
|
|
38
|
+
# 颜色工具(可能有复杂依赖)
|
|
39
|
+
try:
|
|
40
|
+
from .color import *
|
|
41
|
+
from .constant import * # color constants
|
|
42
|
+
from .color_string import *
|
|
43
|
+
except ImportError:
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
# 路径工具
|
|
47
|
+
try:
|
|
48
|
+
from .path import rel_to_abs, rel_path_join, ls, add_env_path
|
|
49
|
+
relp = rel_to_abs # alias
|
|
50
|
+
except ImportError:
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
'async_retry',
|
|
55
|
+
# 其他具体导出项将根据实际模块内容确定
|
|
56
|
+
]
|
maque/utils/color.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from colour import Color
|
|
2
|
+
import numpy as np
|
|
3
|
+
from . import constant as _color_const
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def color_to_rgb(color):
|
|
7
|
+
if isinstance(color, str):
|
|
8
|
+
return hex_to_rgb(color)
|
|
9
|
+
elif isinstance(color, Color):
|
|
10
|
+
return np.array(color.get_rgb())
|
|
11
|
+
elif isinstance(color, (tuple, list, np.ndarray)):
|
|
12
|
+
|
|
13
|
+
if isinstance(color[0], (int, np.int32, np.int64)):
|
|
14
|
+
return np.array(color) / 255
|
|
15
|
+
elif isinstance(color[0], (float, np.float32, np.float64)):
|
|
16
|
+
return np.array(color)
|
|
17
|
+
else:
|
|
18
|
+
raise Exception(f"Invalid color type: {type(color[0])}")
|
|
19
|
+
|
|
20
|
+
else:
|
|
21
|
+
raise Exception(f"Invalid color type: {color}")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def color_to_rgba(color, alpha=1):
|
|
25
|
+
return np.array([*color_to_rgb(color), alpha])
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def rgb_to_color(rgb):
|
|
29
|
+
try:
|
|
30
|
+
return Color(rgb=rgb)
|
|
31
|
+
except ValueError:
|
|
32
|
+
return Color(_color_const.WHITE)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def rgba_to_color(rgba):
|
|
36
|
+
return rgb_to_color(rgba[:3])
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def rgb_to_hex(rgb):
|
|
40
|
+
return "#" + "".join(
|
|
41
|
+
hex(int_x // 16)[2] + hex(int_x % 16)[2]
|
|
42
|
+
for x in rgb
|
|
43
|
+
for int_x in [int(255 * x)]
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def hex_to_rgb(hex_code):
|
|
48
|
+
hex_part = hex_code[1:]
|
|
49
|
+
if len(hex_part) == 3:
|
|
50
|
+
hex_part = "".join([2 * c for c in hex_part])
|
|
51
|
+
return np.array([int(hex_part[i : i + 2], 16) / 255 for i in range(0, 6, 2)])
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def invert_color(color):
|
|
55
|
+
return rgb_to_color(1.0 - color_to_rgb(color))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def color_to_int_rgb(color):
|
|
59
|
+
return (255 * color_to_rgb(color)).astype("uint8")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def color_to_int_rgba(color, opacity=1.0):
|
|
63
|
+
alpha = int(255 * opacity)
|
|
64
|
+
return np.array([*color_to_int_rgb(color), alpha])
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def random_color():
|
|
68
|
+
return Color(rgb=[np.random.random() for _ in range(3)])
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from .color import color_to_int_rgb
|
|
2
|
+
from . import constant as color_const
|
|
3
|
+
|
|
4
|
+
CSI = "\033["
|
|
5
|
+
OSC = "\033]"
|
|
6
|
+
OFF = CSI + "0m"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def __set_rgb(RGB_fore=(240, 85, 85), SRG=0, RGB_back=None):
|
|
10
|
+
"""Get foreground or background color chars
|
|
11
|
+
see https://my.oschina.net/dingdayu/blog/1537064
|
|
12
|
+
inputs:
|
|
13
|
+
RGB_fore: rgb list or tupe of foreground, e.g. [255, 0, 0]
|
|
14
|
+
SRG: the style of font
|
|
15
|
+
SRG options: see https://en.wikipedia.org/wiki/ANSI_escape_code#SGR
|
|
16
|
+
| 0 | Close all formats and revert to the original state
|
|
17
|
+
| 1 | Bold (increased intensity)
|
|
18
|
+
| 2 | Faint (decreased intensity)
|
|
19
|
+
| 3 | Italics
|
|
20
|
+
| 4 | Underline (single line)
|
|
21
|
+
| 5 | Slow Blink
|
|
22
|
+
| 6 | Rapid Blink
|
|
23
|
+
| 7 | Swap the background color with the foreground color
|
|
24
|
+
"""
|
|
25
|
+
fore_color = f"{CSI}{SRG};38;2;{RGB_fore[0]};{RGB_fore[1]};{RGB_fore[2]}m"
|
|
26
|
+
if RGB_back is None:
|
|
27
|
+
back_color = ""
|
|
28
|
+
else:
|
|
29
|
+
back_color = f"{CSI}{SRG};48;2;{RGB_back[0]};{RGB_back[1]};{RGB_back[2]}m"
|
|
30
|
+
return fore_color + back_color
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _rgb_str(string, RGB_fore=(240, 85, 85), SRG=0, RGB_back=None):
|
|
34
|
+
return __set_rgb(RGB_fore, SRG, RGB_back) + string + OFF
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def rgb_string(string, color=color_const.RED, **kwargs):
|
|
38
|
+
"""Return the string with color.
|
|
39
|
+
:param string: The string will be colored.
|
|
40
|
+
:param color: can be rgb list [255, 255, 255] or hex string "#ffffff".
|
|
41
|
+
:param `SRG`, `RGB_back` see function `__set_grb()`
|
|
42
|
+
:return Colored string.
|
|
43
|
+
"""
|
|
44
|
+
rgb = color_to_int_rgb(color)
|
|
45
|
+
return _rgb_str(string, rgb, **kwargs)
|
maque/utils/compress.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
from shutil import _find_unpack_format, _UNPACK_FORMATS
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from maque.utils.path import rel_to_abs, rel_path_join
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def pack(source_path: str, target_path=None, format='gztar'):
|
|
9
|
+
"""Pack or compress files.
|
|
10
|
+
|
|
11
|
+
Parameters
|
|
12
|
+
----------
|
|
13
|
+
source_path: str:
|
|
14
|
+
source path
|
|
15
|
+
target_path: str:
|
|
16
|
+
target path
|
|
17
|
+
format : str
|
|
18
|
+
`format` is the archive format: one of "zip", "tar", "gztar"(default), "bztar", or "xztar".
|
|
19
|
+
Or any other registered format.
|
|
20
|
+
"""
|
|
21
|
+
if target_path is None:
|
|
22
|
+
target_path = Path(source_path).name
|
|
23
|
+
new_path = shutil.make_archive(target_path, format, root_dir=source_path)
|
|
24
|
+
print(f"target path:\n{new_path}")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def unpack(filename: str, extract_dir=None, format=None):
|
|
28
|
+
"""Unpack or decompress files.
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
filename : str
|
|
33
|
+
input source file
|
|
34
|
+
extract_dir : str | None
|
|
35
|
+
output dir
|
|
36
|
+
format : str | None
|
|
37
|
+
`format`is the archive format: one of "zip", "tar", "gztar", "bztar", or "xztar".
|
|
38
|
+
If not provided, unpack_archive will use the filename extension.
|
|
39
|
+
"""
|
|
40
|
+
name_path = Path(filename)
|
|
41
|
+
if not name_path.exists():
|
|
42
|
+
raise FileExistsError(f"{name_path} not exist.")
|
|
43
|
+
name = name_path.name
|
|
44
|
+
file_format = _find_unpack_format(filename)
|
|
45
|
+
file_postfix_list = _UNPACK_FORMATS[file_format][0]
|
|
46
|
+
for postfix in file_postfix_list:
|
|
47
|
+
if name.endswith(postfix):
|
|
48
|
+
target_name = name[:-len(postfix)]
|
|
49
|
+
break
|
|
50
|
+
else:
|
|
51
|
+
target_name = name.replace('.', '_')
|
|
52
|
+
|
|
53
|
+
if extract_dir is None:
|
|
54
|
+
extract_dir = f"./{target_name}/"
|
|
55
|
+
if not Path(extract_dir).exists():
|
|
56
|
+
os.mkdir(extract_dir)
|
|
57
|
+
shutil.unpack_archive(filename, extract_dir, format=format)
|
|
58
|
+
print(f"extract dir:\nfile://{Path(extract_dir).absolute()}")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
# source_path = rel_to_abs('../web/', return_str=False, strict=True).name
|
|
63
|
+
# pack('../web/', format='bztar')
|
|
64
|
+
unpack('./web.tar.bz2')
|
|
65
|
+
|
|
66
|
+
|