Skip to content

Commit f533b2e

Browse files
authored
Merge pull request #439 from tapdata/feat/TAP-7791-api
feat: add MQL help dialog and enhance MongoDB operator localization
2 parents 3dce376 + 960c7f1 commit f533b2e

10 files changed

Lines changed: 376 additions & 10 deletions

File tree

apps/daas/src/components.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ declare module 'vue' {
108108
'IMingcute:brushLine': typeof import('~icons/mingcute/brush-line')['default']
109109
'IMingcute:checkLine': typeof import('~icons/mingcute/check-line')['default']
110110
'IMingcute:closeLine': typeof import('~icons/mingcute/close-line')['default']
111+
'IMingcute:copy2Line': typeof import('~icons/mingcute/copy2-line')['default']
111112
'IMingcute:download2Line': typeof import('~icons/mingcute/download2-line')['default']
112113
'IMingcute:externalLinkLine': typeof import('~icons/mingcute/external-link-line')['default']
113114
'IMingcute:loadingLine': typeof import('~icons/mingcute/loading-line')['default']

packages/assets/styles/utilities.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2440,6 +2440,10 @@
24402440
padding-left: 0 !important;
24412441
}
24422442

2443+
.pl-0\.5 {
2444+
padding-left: 0.125rem !important;
2445+
}
2446+
24432447
.pl-1 {
24442448
padding-left: 0.25rem !important;
24452449
}
Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
<script setup lang="ts">
2+
import { useI18n } from '@tap/i18n'
3+
import { copyToClipboard } from '@tap/shared'
4+
import { computed, ref } from 'vue'
5+
6+
defineOptions({ name: 'MqlHelpDialog' })
7+
8+
const { t } = useI18n()
9+
10+
const keyword = ref('')
11+
12+
type Operator = {
13+
name: string
14+
descKey: string
15+
example: string
16+
related?: string[]
17+
}
18+
19+
type Group = {
20+
key: string
21+
title: string
22+
items: Operator[]
23+
}
24+
25+
const groups = ref<Group[]>([
26+
{
27+
key: 'comparison',
28+
title: t('packages_business_comparison'),
29+
items: [
30+
{
31+
name: '$eq',
32+
descKey: 'packages_business_mongo_operator_eq',
33+
example: `{"price": {"$eq": 10}} // price 等于 10`,
34+
related: ['$ne', '$in'],
35+
},
36+
{
37+
name: '$ne',
38+
descKey: 'packages_business_mongo_operator_ne',
39+
example: `{"status": {"$ne": "archived"}} // status 不等于 archived`,
40+
related: ['$eq'],
41+
},
42+
{
43+
name: '$gt',
44+
descKey: 'packages_business_mongo_operator_gt',
45+
example: `{"age": {"$gt": 18}} // age 大于 18`,
46+
related: ['$gte', '$lt'],
47+
},
48+
{
49+
name: '$gte',
50+
descKey: 'packages_business_mongo_operator_gte',
51+
example: `{"score": {"$gte": 90}} // score 大于等于 90`,
52+
related: ['$gt', '$lte'],
53+
},
54+
{
55+
name: '$lt',
56+
descKey: 'packages_business_mongo_operator_lt',
57+
example: `{"age": {"$lt": 65}} // age 小于 65`,
58+
related: ['$lte', '$gt'],
59+
},
60+
{
61+
name: '$lte',
62+
descKey: 'packages_business_mongo_operator_lte',
63+
example: `{"price": {"$lte": 100}} // price 小于等于 100`,
64+
related: ['$lt', '$gte'],
65+
},
66+
{
67+
name: '$in',
68+
descKey: 'packages_business_mongo_operator_in',
69+
example: `{"category": {"$in": ["A", "B", "C"]}} // category 在 A, B, C 中`,
70+
related: ['$nin', '$eq'],
71+
},
72+
{
73+
name: '$nin',
74+
descKey: 'packages_business_mongo_operator_nin',
75+
example: `{"category": {"$nin": ["A", "B"]}} // category 不在 A, B 中`,
76+
related: ['$in'],
77+
},
78+
],
79+
},
80+
{
81+
key: 'logical',
82+
title: t('packages_business_logical'),
83+
items: [
84+
{
85+
name: '$and',
86+
descKey: 'packages_business_mongo_operator_and',
87+
example: `{"$and": [{"age": {"$gt": 18}}, {"status": "A"}]}`,
88+
related: ['$or', '$nor'],
89+
},
90+
{
91+
name: '$or',
92+
descKey: 'packages_business_mongo_operator_or',
93+
example: '{"$or": [{"age": {"$lt": 18}}, {"status": "B"}]}',
94+
related: ['$and', '$nor'],
95+
},
96+
{
97+
name: '$nor',
98+
descKey: 'packages_business_mongo_operator_nor',
99+
example: '{"$nor": [{"price": {"$gt": 100}}, {"qty": {"$lt": 10}}]}',
100+
related: ['$and', '$or'],
101+
},
102+
],
103+
},
104+
{
105+
key: 'element',
106+
title: t('packages_business_element'),
107+
items: [
108+
{
109+
name: '$exists',
110+
descKey: 'packages_business_mongo_operator_exists',
111+
example: `{"deletedAt": {"$exists": false}} // 字段不存在`,
112+
},
113+
{
114+
name: '$type',
115+
descKey: 'packages_business_mongo_operator_type',
116+
example: `{"age": {"$type": "int"}} // 也可用数字代号,如 16 表示 int`,
117+
},
118+
],
119+
},
120+
{
121+
key: 'evaluation',
122+
title: t('packages_business_evaluation'),
123+
items: [
124+
{
125+
name: '$regex',
126+
descKey: 'packages_business_mongo_operator_regex',
127+
example: `{"name": {"$regex": "^A", "$options": "i"}} // 以 A 开头,忽略大小写`,
128+
},
129+
],
130+
},
131+
{
132+
key: 'array',
133+
title: t('public_array'),
134+
items: [
135+
{
136+
name: '$all',
137+
descKey: 'packages_business_mongo_operator_all',
138+
example: '{"tags": {"$all": ["red", "blue"]}}',
139+
},
140+
{
141+
name: '$elemMatch',
142+
descKey: 'packages_business_mongo_operator_elemMatch',
143+
example:
144+
'{"grades": {"$elemMatch": {"score": {"$gte": 80}, "mean": {"$gt": 75}}}}',
145+
},
146+
{
147+
name: '$size',
148+
descKey: 'packages_business_mongo_operator_size',
149+
example: `{"tags": {"$size": 3}} // tags 数组长度为 3`,
150+
},
151+
],
152+
},
153+
{
154+
key: 'other',
155+
title: t('public_others'),
156+
items: [
157+
{
158+
name: '$mod',
159+
descKey: 'packages_business_mongo_operator_mod',
160+
example: `{"qty": {"$mod": [5, 0]}} // qty 能被 5 整除`,
161+
},
162+
],
163+
},
164+
])
165+
166+
const filteredGroups = computed(() => {
167+
const k = keyword.value.trim().toLowerCase()
168+
if (!k) return groups.value
169+
return groups.value
170+
.map((g) => ({
171+
...g,
172+
items: g.items.filter((op) =>
173+
[op.name, ...(op.related || []), t(op.descKey)]
174+
.join(' ')
175+
.toLowerCase()
176+
.includes(k),
177+
),
178+
}))
179+
.filter((g) => g.items.length)
180+
})
181+
182+
function copy(text: string) {
183+
copyToClipboard(
184+
text.replace(/\/\/.*$/, '').trim(),
185+
document.querySelector('#mql-help-dialog'),
186+
)
187+
ElMessage.success(t('public_message_copy_success'))
188+
}
189+
190+
function open() {
191+
visible.value = true
192+
}
193+
function close() {
194+
visible.value = false
195+
}
196+
197+
defineExpose({ open, close })
198+
</script>
199+
200+
<template>
201+
<ElDialog
202+
append-to-body
203+
:close-on-click-modal="false"
204+
width="720px"
205+
top="6vh"
206+
:title="$t('package_business_mongodb_query_operators')"
207+
class="px-0"
208+
header-class="px-6"
209+
>
210+
<div id="mql-help-dialog" class="mql-help-dialog">
211+
<div class="px-6">
212+
<el-input
213+
v-model="keyword"
214+
clearable
215+
class="mb-3"
216+
:placeholder="$t('package_business_operators_search')"
217+
>
218+
<template #prefix>
219+
<el-icon><i-mingcute:search-line /></el-icon>
220+
</template>
221+
</el-input>
222+
</div>
223+
224+
<div class="px-6 overflow-y-auto" style="max-height: 72vh">
225+
<div v-for="group in filteredGroups" :key="group.key" class="group">
226+
<div
227+
class="fw-sub py-2 mb-1 position-sticky top-0 bg-white z-10 pl-0.5"
228+
style="margin-inline-start: -2px"
229+
>
230+
{{ group.title }}
231+
<span class="text-caption">({{ group.items.length }})</span>
232+
</div>
233+
<div class="grid">
234+
<div
235+
v-for="op in group.items"
236+
:key="op.name"
237+
class="card p-3 rounded-xl shadow-sm"
238+
>
239+
<div class="card-head">
240+
<code
241+
class="card-head-code fs-8 rounded-lg fw-sub border px-2 py-0.5"
242+
>
243+
{{ op.name }}
244+
</code>
245+
</div>
246+
<div class="desc mt-2">{{ t(op.descKey) }}</div>
247+
<div class="example mt-2">
248+
<div
249+
class="flex align-center justify-content-between mb-1 lh-6"
250+
>
251+
<span class="fs-8 text-caption">{{
252+
$t('public_example')
253+
}}</span>
254+
<el-button
255+
class="copy-btn"
256+
text
257+
size="small"
258+
@click="copy(op.example)"
259+
>
260+
<el-icon class="mr-1"><i-mingcute:copy-2-line /></el-icon>
261+
{{ t('public_button_copy') }}
262+
</el-button>
263+
</div>
264+
<pre class="example-code p-2 rounded-lg">{{ op.example }}</pre>
265+
</div>
266+
</div>
267+
</div>
268+
</div>
269+
</div>
270+
</div>
271+
</ElDialog>
272+
</template>
273+
274+
<style lang="scss" scoped>
275+
.mql-help-dialog {
276+
.group + .group {
277+
margin-top: 12px;
278+
}
279+
.group-title {
280+
color: var(--el-text-color-secondary);
281+
margin: 8px 0 6px;
282+
}
283+
.grid {
284+
display: grid;
285+
grid-template-columns: repeat(2, minmax(0, 1fr));
286+
gap: 12px;
287+
}
288+
.card {
289+
border: 1px solid var(--el-border-color-lighter);
290+
background: #fff;
291+
}
292+
.card-head {
293+
display: flex;
294+
align-items: center;
295+
justify-content: space-between;
296+
}
297+
.card-head-code {
298+
line-height: 1rem;
299+
border: 1px solid oklch(0.922 0 0);
300+
background-color: oklch(98.5% 0 0);
301+
font-family: var(--code-font-family);
302+
}
303+
.copy-btn {
304+
display: none;
305+
}
306+
.card:hover .copy-btn {
307+
display: inline-flex;
308+
}
309+
.desc {
310+
color: var(--el-text-color-regular);
311+
}
312+
.example-code {
313+
position: relative;
314+
background: oklch(98.5% 0 0);
315+
border: 1px solid oklch(0.922 0 0);
316+
}
317+
pre {
318+
margin: 0;
319+
font-size: 12px;
320+
white-space: pre-wrap;
321+
word-break: break-word;
322+
}
323+
}
324+
</style>

packages/business/src/locale/lang/en.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,6 +1622,12 @@ export default {
16221622
packages_business_data_source_monitor: 'Connection Monitor',
16231623

16241624
// MongoDB operators
1625+
package_business_mongodb_query_operators: 'MongoDB Query Operators',
1626+
package_business_operators_search: 'Search operators',
1627+
packages_business_comparison: 'Comparison',
1628+
packages_business_logical: 'Logical',
1629+
packages_business_element: 'Element',
1630+
packages_business_evaluation: 'Evaluation',
16251631
packages_business_mongo_operator_eq:
16261632
'Matches values that are equal to a specified value.',
16271633
packages_business_mongo_operator_gt:
@@ -1652,9 +1658,9 @@ export default {
16521658
packages_business_mongo_operator_all:
16531659
'Matches arrays that contain all elements specified in the query.',
16541660
packages_business_mongo_operator_elemMatch:
1655-
'Selects documents if element in the array field matches all the specified conditions.',
1661+
'Matches arrays that contain at least one element satisfying all specified conditions.',
16561662
packages_business_mongo_operator_size:
16571663
'Selects documents if the array field is a specified size.',
16581664
packages_business_mongo_operator_mod:
1659-
'Performs a modulo operation on the value of a field.',
1665+
'Performs a modulo match on a field value [divisor, remainder].',
16601666
}

packages/business/src/locale/lang/zh-CN.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,6 +1446,12 @@ export default {
14461446
packages_business_data_source_monitor: '数据源监控',
14471447

14481448
// MongoDB 操作符
1449+
package_business_mongodb_query_operators: 'MongoDB 查询操作符',
1450+
package_business_operators_search: '搜索操作符',
1451+
packages_business_comparison: '比较',
1452+
packages_business_logical: '逻辑',
1453+
packages_business_element: '元素',
1454+
packages_business_evaluation: '表达式',
14491455
packages_business_mongo_operator_eq: '匹配等于指定值的值',
14501456
packages_business_mongo_operator_gt: '匹配大于指定值的值',
14511457
packages_business_mongo_operator_gte: '匹配大于或等于指定值的值',
@@ -1461,7 +1467,8 @@ export default {
14611467
packages_business_mongo_operator_type: '如果字段是指定类型,则选择文档',
14621468
packages_business_mongo_operator_regex: '选择值匹配指定正则表达式的文档',
14631469
packages_business_mongo_operator_all: '匹配包含查询中指定的所有元素的数组',
1464-
packages_business_mongo_operator_elemMatch: '如果数组字段中的元素匹配所有指定条件,则选择文档',
1470+
packages_business_mongo_operator_elemMatch:
1471+
'匹配数组中至少有一个元素满足所有给定条件',
14651472
packages_business_mongo_operator_size: '如果数组字段是指定大小,则选择文档',
1466-
packages_business_mongo_operator_mod: '对字段值执行模运算',
1473+
packages_business_mongo_operator_mod: '对字段值做取模匹配 [除数, 余数]',
14671474
}

0 commit comments

Comments
 (0)