@veltra/desktop 1.2.27 → 1.2.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/action/action-group.vue.d.ts +2 -2
- package/dist/components/action/action.vue.d.ts +2 -2
- package/dist/components/auto-complete/auto-complete.vue.d.ts +2 -2
- package/dist/components/cascade/cascade.vue.d.ts +3 -3
- package/dist/components/checkbox/checkbox-button.vue.d.ts +1 -1
- package/dist/components/checkbox-group/checkbox-group.vue.d.ts +2 -2
- package/dist/components/code-editor/code-editor.js +7 -2
- package/dist/components/code-editor/code-editor.js.map +1 -1
- package/dist/components/code-editor/lang-loaders.js +2 -1
- package/dist/components/code-editor/lang-loaders.js.map +1 -1
- package/dist/components/code-editor/langs/spel/completion.js +88 -0
- package/dist/components/code-editor/langs/spel/completion.js.map +1 -0
- package/dist/components/code-editor/langs/spel/index.js +12 -0
- package/dist/components/code-editor/langs/spel/index.js.map +1 -0
- package/dist/components/code-editor/langs/spel/language.js +177 -0
- package/dist/components/code-editor/langs/spel/language.js.map +1 -0
- package/dist/components/code-editor/style2.css +11 -0
- package/dist/components/date-panel/date-panel.vue.d.ts +1 -1
- package/dist/components/date-picker/date-picker.vue.d.ts +3 -3
- package/dist/components/date-range-picker/date-range-picker.vue.d.ts +3 -3
- package/dist/components/empty/empty.vue.d.ts +1 -1
- package/dist/components/file-picker/file-picker.vue.d.ts +1 -1
- package/dist/components/grid-input/grid-input.js +18 -12
- package/dist/components/grid-input/grid-input.js.map +1 -1
- package/dist/components/grid-input/grid-input.vue.d.ts +1 -1
- package/dist/components/input/input.vue.d.ts +2 -2
- package/dist/components/multi-select/multi-select.vue.d.ts +3 -3
- package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +3 -3
- package/dist/components/number/number.vue.d.ts +1 -1
- package/dist/components/number-input/number-input.vue.d.ts +2 -2
- package/dist/components/number-range-input/number-range-input.vue.d.ts +1 -1
- package/dist/components/password-input/password-input.vue.d.ts +1 -1
- package/dist/components/pop-confirm/pop-confirm.vue.d.ts +1 -1
- package/dist/components/radio-group/radio-group.vue.d.ts +2 -2
- package/dist/components/rich-text-editor/rich-text-editor.vue.d.ts +1 -1
- package/dist/components/select/select.vue.d.ts +2 -2
- package/dist/components/steps/steps.vue.d.ts +3 -3
- package/dist/components/textarea/textarea.vue.d.ts +2 -2
- package/dist/components/tip/tip.vue.d.ts +1 -1
- package/dist/components/tree-select/tree-select.vue.d.ts +3 -3
- package/dist/components/watermark/watermark.vue.d.ts +1 -1
- package/dist/node_modules/.bun/@codemirror_language@6.12.4/node_modules/@codemirror/language/dist/index.js +452 -2
- package/dist/node_modules/.bun/@codemirror_language@6.12.4/node_modules/@codemirror/language/dist/index.js.map +1 -1
- package/dist/types/code-editor.d.ts +3 -2
- package/dist/types/grid-input.d.ts +12 -5
- package/package.json +6 -6
- package/src/components/code-editor/code-editor.vue +6 -0
- package/src/components/code-editor/lang-loaders.ts +2 -1
- package/src/components/code-editor/langs/spel/completion.ts +30 -0
- package/src/components/code-editor/langs/spel/index.ts +12 -0
- package/src/components/code-editor/langs/spel/language.ts +198 -0
- package/src/components/code-editor/style.scss +12 -0
- package/src/components/grid-input/grid-input.vue +21 -17
- package/src/types/code-editor.ts +3 -2
- package/src/types/grid-input.ts +12 -5
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { DeconstructValue, FormComponentProps } from "@veltra/utils";
|
|
2
2
|
|
|
3
3
|
//#region src/types/code-editor.d.ts
|
|
4
|
-
type CodeEditorLang = 'js' | 'sql' | 'java' | 'json';
|
|
4
|
+
type CodeEditorLang = 'js' | 'sql' | 'java' | 'json' | 'spel';
|
|
5
5
|
/** 代码编辑器组件属性(不支持 `size`) */
|
|
6
6
|
interface CodeEditorProps extends Omit<FormComponentProps, 'size'> {
|
|
7
7
|
modelValue?: string;
|
|
8
8
|
/**
|
|
9
|
-
* 可选语言列表。长度大于 1
|
|
9
|
+
* 可选语言列表。长度大于 1 时在编辑器右上角显示内置语言选择器;
|
|
10
|
+
* 仅一种语言时显示语言名称标签
|
|
10
11
|
*/
|
|
11
12
|
langs?: CodeEditorLang[];
|
|
12
13
|
/** 当前语言,配合 `v-model:lang` 使用 */
|
|
@@ -1,23 +1,30 @@
|
|
|
1
1
|
import { DeconstructValue } from "@veltra/utils";
|
|
2
2
|
|
|
3
3
|
//#region src/types/grid-input.d.ts
|
|
4
|
-
/**
|
|
4
|
+
/** 网格输入框组件属性 */
|
|
5
5
|
interface GridInputProps {
|
|
6
6
|
modelValue?: string;
|
|
7
|
+
/** 格子数量 */
|
|
7
8
|
length?: number;
|
|
8
|
-
|
|
9
|
+
/**
|
|
10
|
+
* 是否允许输入 0
|
|
11
|
+
* @description 验证码场景通常开启;组织编码结构等场景关闭(如 3-3-2)
|
|
12
|
+
* @default false
|
|
13
|
+
*/
|
|
14
|
+
zero?: boolean;
|
|
15
|
+
/** 格子之间的分隔符 */
|
|
9
16
|
separator?: string;
|
|
10
17
|
}
|
|
11
|
-
/**
|
|
18
|
+
/** 网格输入框组件定义的事件 */
|
|
12
19
|
interface GridInputEmits {
|
|
13
20
|
(e: 'update:modelValue', value: string): void;
|
|
14
21
|
(e: 'input', value: string): void;
|
|
15
22
|
}
|
|
16
|
-
/**
|
|
23
|
+
/** 网格输入框组件暴露的属性和方法(组件内部使用) */
|
|
17
24
|
interface _GridInputExposed {
|
|
18
25
|
clear: () => void;
|
|
19
26
|
}
|
|
20
|
-
/**
|
|
27
|
+
/** 网格输入框组件暴露的属性和方法(组件外部使用, 引用的值会被自动解构) */
|
|
21
28
|
type GridInputExposed = DeconstructValue<_GridInputExposed>;
|
|
22
29
|
//#endregion
|
|
23
30
|
export { GridInputEmits, GridInputExposed, GridInputProps, _GridInputExposed };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veltra/desktop",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.29",
|
|
4
4
|
"files": [
|
|
5
5
|
"dist",
|
|
6
6
|
"src"
|
|
@@ -57,11 +57,11 @@
|
|
|
57
57
|
"@cat-kit/core": ">=1.1.8",
|
|
58
58
|
"@cat-kit/excel": ">=1.0.0",
|
|
59
59
|
"@cat-kit/fe": "^1.1.8",
|
|
60
|
-
"@veltra/compositions": "1.2.
|
|
61
|
-
"@veltra/directives": "1.2.
|
|
62
|
-
"@veltra/icons": "1.2.
|
|
63
|
-
"@veltra/styles": "1.2.
|
|
64
|
-
"@veltra/utils": "1.2.
|
|
60
|
+
"@veltra/compositions": "1.2.29",
|
|
61
|
+
"@veltra/directives": "1.2.29",
|
|
62
|
+
"@veltra/icons": "1.2.29",
|
|
63
|
+
"@veltra/styles": "1.2.29",
|
|
64
|
+
"@veltra/utils": "1.2.29",
|
|
65
65
|
"vue": ">=3.5.39"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
:clearable="false"
|
|
12
12
|
:disabled="disabled"
|
|
13
13
|
/>
|
|
14
|
+
<span v-else-if="langLabel" :class="cls.e('lang-label')">{{ langLabel }}</span>
|
|
14
15
|
</u-scroll>
|
|
15
16
|
</template>
|
|
16
17
|
|
|
@@ -89,6 +90,11 @@ const langOptions = computed(() =>
|
|
|
89
90
|
/** 实际生效的语言:优先 lang model,否则回落 langs[0] */
|
|
90
91
|
const activeLang = computed<CodeEditorLang | undefined>(() => lang.value ?? langs.value[0])
|
|
91
92
|
|
|
93
|
+
/** 仅一种语言时,在选择器位置展示名称 */
|
|
94
|
+
const langLabel = computed(() =>
|
|
95
|
+
langs.value.length === 1 && activeLang.value ? activeLang.value.toUpperCase() : undefined
|
|
96
|
+
)
|
|
97
|
+
|
|
92
98
|
const shellConfig = computed<ShellConfig>(() => ({
|
|
93
99
|
prefix: props.prefix ?? '',
|
|
94
100
|
suffix: props.suffix ?? ''
|
|
@@ -7,7 +7,8 @@ const LANG_LOADERS: Record<CodeEditorLang, () => Promise<Extension>> = {
|
|
|
7
7
|
js: () => import('@codemirror/lang-javascript').then((m) => m.javascript({ typescript: true })),
|
|
8
8
|
sql: () => import('@codemirror/lang-sql').then((m) => m.sql()),
|
|
9
9
|
java: () => import('@codemirror/lang-java').then((m) => m.java()),
|
|
10
|
-
json: () => import('@codemirror/lang-json').then((m) => m.json())
|
|
10
|
+
json: () => import('@codemirror/lang-json').then((m) => m.json()),
|
|
11
|
+
spel: () => import('./langs/spel').then((m) => m.spel())
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
/** 按需加载语言扩展;模块缓存由运行时保证,重复切换同一语言不会重复下载 */
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { completeFromList, type Completion } from '@codemirror/autocomplete'
|
|
2
|
+
|
|
3
|
+
/** SpEL 关键字与常用内置标识补全 */
|
|
4
|
+
const SPEL_COMPLETIONS: Completion[] = [
|
|
5
|
+
// 字面量
|
|
6
|
+
{ label: 'true', type: 'constant' },
|
|
7
|
+
{ label: 'false', type: 'constant' },
|
|
8
|
+
{ label: 'null', type: 'constant' },
|
|
9
|
+
// 关键字 / 运算符词
|
|
10
|
+
{ label: 'and', type: 'keyword' },
|
|
11
|
+
{ label: 'or', type: 'keyword' },
|
|
12
|
+
{ label: 'not', type: 'keyword' },
|
|
13
|
+
{ label: 'eq', type: 'keyword' },
|
|
14
|
+
{ label: 'ne', type: 'keyword' },
|
|
15
|
+
{ label: 'lt', type: 'keyword' },
|
|
16
|
+
{ label: 'le', type: 'keyword' },
|
|
17
|
+
{ label: 'gt', type: 'keyword' },
|
|
18
|
+
{ label: 'ge', type: 'keyword' },
|
|
19
|
+
{ label: 'matches', type: 'keyword' },
|
|
20
|
+
{ label: 'between', type: 'keyword' },
|
|
21
|
+
{ label: 'instanceof', type: 'keyword' },
|
|
22
|
+
{ label: 'new', type: 'keyword' },
|
|
23
|
+
// 类型引用与上下文变量
|
|
24
|
+
{ label: 'T', type: 'type', detail: 'T(Type)', info: '类型引用' },
|
|
25
|
+
{ label: '#this', type: 'variable', detail: '当前上下文' },
|
|
26
|
+
{ label: '#root', type: 'variable', detail: '根对象' }
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
/** 基于关键字列表的补全源 */
|
|
30
|
+
export const spelCompletion = completeFromList(SPEL_COMPLETIONS)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { LanguageSupport } from '@codemirror/language'
|
|
2
|
+
|
|
3
|
+
import { spelCompletion } from './completion'
|
|
4
|
+
import { spelLanguage } from './language'
|
|
5
|
+
|
|
6
|
+
/** SpEL(Spring Expression Language)语言支持:语法高亮 + 基础补全 */
|
|
7
|
+
export function spel(): LanguageSupport {
|
|
8
|
+
return new LanguageSupport(spelLanguage, [spelLanguage.data.of({ autocomplete: spelCompletion })])
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export { spelLanguage } from './language'
|
|
12
|
+
export { spelCompletion } from './completion'
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { StreamLanguage, type StringStream } from '@codemirror/language'
|
|
2
|
+
|
|
3
|
+
/** 词法状态:跟踪 T(...) 类型引用与属性访问 */
|
|
4
|
+
interface SpelState {
|
|
5
|
+
/** T(...) 内括号深度,>0 表示处于类型名上下文 */
|
|
6
|
+
typeDepth: number
|
|
7
|
+
/** 刚消费完 `.`,下一标识符按属性名着色 */
|
|
8
|
+
afterDot: boolean
|
|
9
|
+
/** 刚消费完类型引用关键字 `T`,下一 `(` 进入类型上下文 */
|
|
10
|
+
afterT: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const KEYWORDS = new Set([
|
|
14
|
+
'new',
|
|
15
|
+
'instanceof',
|
|
16
|
+
'matches',
|
|
17
|
+
'between',
|
|
18
|
+
'and',
|
|
19
|
+
'or',
|
|
20
|
+
'not',
|
|
21
|
+
'eq',
|
|
22
|
+
'ne',
|
|
23
|
+
'lt',
|
|
24
|
+
'le',
|
|
25
|
+
'gt',
|
|
26
|
+
'ge'
|
|
27
|
+
])
|
|
28
|
+
|
|
29
|
+
const BOOL_LITERALS = new Set(['true', 'false'])
|
|
30
|
+
|
|
31
|
+
/** 多字符运算符,按长度优先匹配 */
|
|
32
|
+
const OPERATORS = ['?.', '?:', '?[', '![', '==', '!=', '<=', '>=', '&&', '||', '++', '--']
|
|
33
|
+
|
|
34
|
+
function isIdentStart(ch: string): boolean {
|
|
35
|
+
return /[A-Za-z_]/.test(ch)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isIdentChar(ch: string): boolean {
|
|
39
|
+
return /[A-Za-z0-9_]/.test(ch)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function tokenString(stream: StringStream, quote: string): string {
|
|
43
|
+
let escaped = false
|
|
44
|
+
while (!stream.eol()) {
|
|
45
|
+
const next = stream.next()
|
|
46
|
+
if (next === quote && !escaped) break
|
|
47
|
+
escaped = !escaped && next === '\\'
|
|
48
|
+
}
|
|
49
|
+
return 'string'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function tokenNumber(stream: StringStream): string {
|
|
53
|
+
stream.match(/^\d+(\.\d+)?([eE][+-]?\d+)?/)
|
|
54
|
+
return 'number'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function tokenIdentifier(stream: StringStream, state: SpelState): string {
|
|
58
|
+
stream.eatWhile(isIdentChar)
|
|
59
|
+
const word = stream.current()
|
|
60
|
+
state.afterT = false
|
|
61
|
+
|
|
62
|
+
if (state.typeDepth > 0) {
|
|
63
|
+
state.afterDot = false
|
|
64
|
+
return 'typeName'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (state.afterDot) {
|
|
68
|
+
state.afterDot = false
|
|
69
|
+
return 'propertyName'
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (BOOL_LITERALS.has(word)) return 'bool'
|
|
73
|
+
if (word === 'null') return 'null'
|
|
74
|
+
if (KEYWORDS.has(word)) return 'keyword'
|
|
75
|
+
|
|
76
|
+
// T(...) 类型引用
|
|
77
|
+
if (word === 'T' && stream.peek() === '(') {
|
|
78
|
+
state.afterT = true
|
|
79
|
+
return 'typeName'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return 'name'
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function tokenSpel(stream: StringStream, state: SpelState): string | null {
|
|
86
|
+
if (stream.eatSpace()) return null
|
|
87
|
+
|
|
88
|
+
const ch = stream.peek()
|
|
89
|
+
if (!ch) return null
|
|
90
|
+
|
|
91
|
+
// 字符串
|
|
92
|
+
if (ch === "'" || ch === '"') {
|
|
93
|
+
stream.next()
|
|
94
|
+
state.afterDot = false
|
|
95
|
+
state.afterT = false
|
|
96
|
+
return tokenString(stream, ch)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 数字
|
|
100
|
+
if (/\d/.test(ch)) {
|
|
101
|
+
state.afterDot = false
|
|
102
|
+
state.afterT = false
|
|
103
|
+
return tokenNumber(stream)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// #变量 / #this / #root
|
|
107
|
+
if (ch === '#') {
|
|
108
|
+
stream.next()
|
|
109
|
+
state.afterDot = false
|
|
110
|
+
state.afterT = false
|
|
111
|
+
const peek = stream.peek()
|
|
112
|
+
if (peek && isIdentStart(peek)) {
|
|
113
|
+
stream.eatWhile(isIdentChar)
|
|
114
|
+
return 'variableName'
|
|
115
|
+
}
|
|
116
|
+
return 'operator'
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 多字符运算符
|
|
120
|
+
for (const op of OPERATORS) {
|
|
121
|
+
if (stream.match(op)) {
|
|
122
|
+
state.afterDot = false
|
|
123
|
+
state.afterT = false
|
|
124
|
+
return 'operator'
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 单字符运算符
|
|
129
|
+
if ('+-*/%^!=<>&|?:'.includes(ch)) {
|
|
130
|
+
stream.next()
|
|
131
|
+
state.afterDot = false
|
|
132
|
+
state.afterT = false
|
|
133
|
+
return 'operator'
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (ch === '(') {
|
|
137
|
+
stream.next()
|
|
138
|
+
state.afterDot = false
|
|
139
|
+
if (state.afterT || state.typeDepth > 0) {
|
|
140
|
+
state.typeDepth++
|
|
141
|
+
state.afterT = false
|
|
142
|
+
}
|
|
143
|
+
return 'paren'
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (ch === ')') {
|
|
147
|
+
stream.next()
|
|
148
|
+
state.afterDot = false
|
|
149
|
+
state.afterT = false
|
|
150
|
+
if (state.typeDepth > 0) state.typeDepth--
|
|
151
|
+
return 'paren'
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (ch === '[' || ch === ']') {
|
|
155
|
+
stream.next()
|
|
156
|
+
state.afterDot = false
|
|
157
|
+
state.afterT = false
|
|
158
|
+
return 'paren'
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (ch === '{' || ch === '}') {
|
|
162
|
+
stream.next()
|
|
163
|
+
state.afterDot = false
|
|
164
|
+
state.afterT = false
|
|
165
|
+
return 'brace'
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (ch === ',') {
|
|
169
|
+
stream.next()
|
|
170
|
+
state.afterDot = false
|
|
171
|
+
state.afterT = false
|
|
172
|
+
return 'punctuation'
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (ch === '.') {
|
|
176
|
+
stream.next()
|
|
177
|
+
state.afterDot = true
|
|
178
|
+
state.afterT = false
|
|
179
|
+
return 'punctuation'
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (isIdentStart(ch)) {
|
|
183
|
+
return tokenIdentifier(stream, state)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
stream.next()
|
|
187
|
+
state.afterDot = false
|
|
188
|
+
state.afterT = false
|
|
189
|
+
return 'punctuation'
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** SpEL StreamLanguage */
|
|
193
|
+
export const spelLanguage = StreamLanguage.define<SpelState>({
|
|
194
|
+
name: 'spel',
|
|
195
|
+
startState: () => ({ typeDepth: 0, afterDot: false, afterT: false }),
|
|
196
|
+
token: tokenSpel,
|
|
197
|
+
languageData: { closeBrackets: { brackets: ['(', '[', '{', "'", '"'] } }
|
|
198
|
+
})
|
|
@@ -45,6 +45,18 @@ $root-name: code-editor;
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
@include m.e(lang-label) {
|
|
49
|
+
position: absolute;
|
|
50
|
+
top: 2px;
|
|
51
|
+
right: 8px;
|
|
52
|
+
z-index: 2;
|
|
53
|
+
font-size: 12px;
|
|
54
|
+
line-height: 24px;
|
|
55
|
+
color: var(--u-code-editor-gutter-fg);
|
|
56
|
+
pointer-events: none;
|
|
57
|
+
user-select: none;
|
|
58
|
+
}
|
|
59
|
+
|
|
48
60
|
.cm-editor {
|
|
49
61
|
background: transparent;
|
|
50
62
|
color: var(--u-code-editor-fg);
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
ref="items"
|
|
11
11
|
:class="[cls.e('item'), bem.is('focus', index === position && !blur)]"
|
|
12
12
|
>
|
|
13
|
-
<span :class="cls.em('item', 'text')" v-if="valueArray
|
|
13
|
+
<span :class="cls.em('item', 'text')" v-if="index < valueArray.length">
|
|
14
14
|
{{ valueArray[index] }}
|
|
15
15
|
</span>
|
|
16
16
|
|
|
@@ -40,7 +40,7 @@ const { size } = useFallbackProps([props], { size: 'default' })
|
|
|
40
40
|
|
|
41
41
|
const cls = bem('grid-input')
|
|
42
42
|
|
|
43
|
-
const
|
|
43
|
+
const numberReg = computed(() => (props.zero ? /^[0-9]$/ : /^[1-9]$/))
|
|
44
44
|
|
|
45
45
|
const valueArray = ref<string[]>([])
|
|
46
46
|
|
|
@@ -50,6 +50,12 @@ const blur = ref<boolean>(false)
|
|
|
50
50
|
|
|
51
51
|
const instance = getCurrentInstance()
|
|
52
52
|
|
|
53
|
+
const emitValue = () => {
|
|
54
|
+
const value = valueArray.value.join(props.separator)
|
|
55
|
+
emit('input', value)
|
|
56
|
+
emit('update:modelValue', value)
|
|
57
|
+
}
|
|
58
|
+
|
|
53
59
|
const focus = async (index?: number) => {
|
|
54
60
|
if (index === undefined) {
|
|
55
61
|
focus(position.value)
|
|
@@ -67,9 +73,8 @@ const focus = async (index?: number) => {
|
|
|
67
73
|
|
|
68
74
|
const changeValue = (value: string, index: number) => {
|
|
69
75
|
if (position.value === props.length) return
|
|
70
|
-
const currentValue = valueArray.value[index]
|
|
71
76
|
|
|
72
|
-
if (
|
|
77
|
+
if (index < valueArray.value.length) {
|
|
73
78
|
valueArray.value.splice(index, 1, value)
|
|
74
79
|
} else if (index === valueArray.value.length) {
|
|
75
80
|
valueArray.value.push(value)
|
|
@@ -79,12 +84,12 @@ const changeValue = (value: string, index: number) => {
|
|
|
79
84
|
position.value++
|
|
80
85
|
focus()
|
|
81
86
|
|
|
82
|
-
|
|
87
|
+
emitValue()
|
|
83
88
|
}
|
|
84
89
|
|
|
85
|
-
const baseOperation = {
|
|
86
|
-
Backspace: (
|
|
87
|
-
if (value) {
|
|
90
|
+
const baseOperation: Record<string, (value: string, index: number) => void> = {
|
|
91
|
+
Backspace: (_value: string, index: number) => {
|
|
92
|
+
if (index < valueArray.value.length) {
|
|
88
93
|
valueArray.value.splice(index, 1)
|
|
89
94
|
} else if (index - 1 >= 0) {
|
|
90
95
|
valueArray.value.splice(index - 1, 1)
|
|
@@ -92,30 +97,29 @@ const baseOperation = {
|
|
|
92
97
|
if (index !== 0) {
|
|
93
98
|
focus(index - 1)
|
|
94
99
|
}
|
|
95
|
-
|
|
100
|
+
emitValue()
|
|
96
101
|
},
|
|
97
|
-
ArrowLeft: (
|
|
102
|
+
ArrowLeft: (_value: string, index: number) => {
|
|
98
103
|
if (index === 0) return
|
|
99
104
|
focus(index - 1)
|
|
100
105
|
},
|
|
101
|
-
ArrowRight: (
|
|
106
|
+
ArrowRight: (_value: string, index: number) => {
|
|
102
107
|
if (index === valueArray.value.length) return
|
|
103
108
|
focus(index + 1)
|
|
104
109
|
}
|
|
105
110
|
}
|
|
106
111
|
|
|
107
|
-
const onKeydown = (e:
|
|
108
|
-
if (
|
|
112
|
+
const onKeydown = (e: KeyboardEvent, value: string, index: number) => {
|
|
113
|
+
if (numberReg.value.test(e.key)) {
|
|
109
114
|
changeValue(e.key, index)
|
|
110
115
|
return
|
|
111
116
|
}
|
|
112
|
-
baseOperation[e.key](value, index)
|
|
117
|
+
baseOperation[e.key]?.(value, index)
|
|
113
118
|
}
|
|
114
119
|
|
|
115
120
|
const clear = () => {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
121
|
+
valueArray.value = []
|
|
122
|
+
position.value = -1
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
defineExpose<GridInputExposed>({ clear })
|
package/src/types/code-editor.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { DeconstructValue, FormComponentProps } from '@veltra/utils'
|
|
2
2
|
|
|
3
|
-
export type CodeEditorLang = 'js' | 'sql' | 'java' | 'json'
|
|
3
|
+
export type CodeEditorLang = 'js' | 'sql' | 'java' | 'json' | 'spel'
|
|
4
4
|
|
|
5
5
|
/** 代码编辑器组件属性(不支持 `size`) */
|
|
6
6
|
export interface CodeEditorProps extends Omit<FormComponentProps, 'size'> {
|
|
7
7
|
modelValue?: string
|
|
8
8
|
/**
|
|
9
|
-
* 可选语言列表。长度大于 1
|
|
9
|
+
* 可选语言列表。长度大于 1 时在编辑器右上角显示内置语言选择器;
|
|
10
|
+
* 仅一种语言时显示语言名称标签
|
|
10
11
|
*/
|
|
11
12
|
langs?: CodeEditorLang[]
|
|
12
13
|
/** 当前语言,配合 `v-model:lang` 使用 */
|
package/src/types/grid-input.ts
CHANGED
|
@@ -1,23 +1,30 @@
|
|
|
1
1
|
import type { DeconstructValue } from '@veltra/utils'
|
|
2
2
|
|
|
3
|
-
/**
|
|
3
|
+
/** 网格输入框组件属性 */
|
|
4
4
|
export interface GridInputProps {
|
|
5
5
|
modelValue?: string
|
|
6
|
+
/** 格子数量 */
|
|
6
7
|
length?: number
|
|
7
|
-
|
|
8
|
+
/**
|
|
9
|
+
* 是否允许输入 0
|
|
10
|
+
* @description 验证码场景通常开启;组织编码结构等场景关闭(如 3-3-2)
|
|
11
|
+
* @default false
|
|
12
|
+
*/
|
|
13
|
+
zero?: boolean
|
|
14
|
+
/** 格子之间的分隔符 */
|
|
8
15
|
separator?: string
|
|
9
16
|
}
|
|
10
17
|
|
|
11
|
-
/**
|
|
18
|
+
/** 网格输入框组件定义的事件 */
|
|
12
19
|
export interface GridInputEmits {
|
|
13
20
|
(e: 'update:modelValue', value: string): void
|
|
14
21
|
(e: 'input', value: string): void
|
|
15
22
|
}
|
|
16
23
|
|
|
17
|
-
/**
|
|
24
|
+
/** 网格输入框组件暴露的属性和方法(组件内部使用) */
|
|
18
25
|
export interface _GridInputExposed {
|
|
19
26
|
clear: () => void
|
|
20
27
|
}
|
|
21
28
|
|
|
22
|
-
/**
|
|
29
|
+
/** 网格输入框组件暴露的属性和方法(组件外部使用, 引用的值会被自动解构) */
|
|
23
30
|
export type GridInputExposed = DeconstructValue<_GridInputExposed>
|