vue2server7 7.0.50 → 7.0.52
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/frontEnd/.workbuddy/memory/MEMORY.md +0 -0
- package/frontEnd/src/components/MoneyInput.vue +262 -0
- package/frontEnd/src/pages/MoneyInputPage.vue +101 -0
- package/frontEnd/src/router/routes.js +10 -0
- package/package.json +1 -1
- package/ms-vscode-remote.remote-ssh-0.119.2025033120.vsix +0 -0
- package/test/111 +0 -52
- package/test/111.txt +0 -44
- package/test/11111111111 +0 -24
- package/test/111111111111111112222 +0 -60
- package/test/12.js +0 -115
- package/test/13420256837985870.gif +0 -0
- package/test/13420256921603132.gif +0 -0
- package/test/13420256985163546.png +0 -0
- package/test/222.txt +0 -47
- package/test/3.js +0 -75
- package/test/4.js +0 -60
- package/test/6.js +0 -60
- package/test/777 +0 -24
- package/test/9.text +0 -100
- package/test/convert-java-to-ts.js +0 -52
- package/test/fun.js +0 -15
- package/test/health.test.js +0 -12
- package/test/i.js +0 -17
- package/test/trend.test.js +0 -30
- package/test/vue3_permission_directive.md +0 -206
- package/test/vue3_permission_directive_advanced.md +0 -248
|
File without changes
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="money-input">
|
|
3
|
+
<el-input
|
|
4
|
+
v-model="inputDisplayValue"
|
|
5
|
+
:class="{ 'is-error': showError }"
|
|
6
|
+
:placeholder="placeholder"
|
|
7
|
+
:disabled="disabled"
|
|
8
|
+
clearable
|
|
9
|
+
@input="onInput"
|
|
10
|
+
@focus="onFocus"
|
|
11
|
+
@blur="onBlur"
|
|
12
|
+
@clear="onClear"
|
|
13
|
+
/>
|
|
14
|
+
<span v-if="showError" class="money-input__error">{{ errorMessage }}</span>
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script setup lang="ts">
|
|
19
|
+
import { computed, ref, watch } from 'vue'
|
|
20
|
+
import { ElMessageBox } from 'element-plus'
|
|
21
|
+
|
|
22
|
+
const FORMAT_REG = /^(?:0|[1-9]\d{0,15})\.\d{2}$/
|
|
23
|
+
|
|
24
|
+
const props = withDefaults(defineProps<{
|
|
25
|
+
modelValue?: string | null
|
|
26
|
+
placeholder?: string
|
|
27
|
+
disabled?: boolean
|
|
28
|
+
errorMessage?: string
|
|
29
|
+
showChineseUppercase?: boolean
|
|
30
|
+
}>(), {
|
|
31
|
+
modelValue: null,
|
|
32
|
+
placeholder: '请输入金额',
|
|
33
|
+
disabled: false,
|
|
34
|
+
errorMessage: '金额格式应为最多 16 位整数 + 2 位小数(如 1.00)',
|
|
35
|
+
showChineseUppercase: false
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const emit = defineEmits<{
|
|
39
|
+
'update:modelValue': [value: string | null]
|
|
40
|
+
'validate': [valid: boolean]
|
|
41
|
+
}>()
|
|
42
|
+
|
|
43
|
+
const displayValue = ref('')
|
|
44
|
+
const focused = ref(false)
|
|
45
|
+
const hasBlurred = ref(false)
|
|
46
|
+
|
|
47
|
+
// 千分位格式化(用于显示)
|
|
48
|
+
function toThousands(numStr: string): string {
|
|
49
|
+
if (!numStr) return ''
|
|
50
|
+
const [intPart, decPart] = numStr.split('.')
|
|
51
|
+
const formatted = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
|
52
|
+
return decPart ? `${formatted}.${decPart}` : formatted
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 移除千分位(用于编辑)
|
|
56
|
+
function fromThousands(str: string): string {
|
|
57
|
+
return str.replace(/,/g, '')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const inputDisplayValue = computed(() => {
|
|
61
|
+
if (!displayValue.value) return ''
|
|
62
|
+
return focused.value ? displayValue.value : toThousands(displayValue.value)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const isValid = computed(() => {
|
|
66
|
+
if (!displayValue.value) return true
|
|
67
|
+
return FORMAT_REG.test(displayValue.value)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
const showError = computed(() => hasBlurred.value && !focused.value && !isValid.value)
|
|
71
|
+
|
|
72
|
+
function normalizeIntegerPart(rawInt: string): string {
|
|
73
|
+
const digits = rawInt.replace(/\D/g, '').slice(0, 16)
|
|
74
|
+
if (!digits) return ''
|
|
75
|
+
const trimmed = digits.replace(/^0+/, '')
|
|
76
|
+
return trimmed || '0'
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function sanitizeInput(raw: string): string {
|
|
80
|
+
if (!raw) return ''
|
|
81
|
+
|
|
82
|
+
const cleaned = raw.replace(/[^\d.]/g, '')
|
|
83
|
+
const dotIndex = cleaned.indexOf('.')
|
|
84
|
+
|
|
85
|
+
if (dotIndex === -1) {
|
|
86
|
+
return normalizeIntegerPart(cleaned)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const intPart = normalizeIntegerPart(cleaned.slice(0, dotIndex))
|
|
90
|
+
const decPart = cleaned.slice(dotIndex + 1).replace(/\./g, '').slice(0, 2)
|
|
91
|
+
return `${intPart || '0'}.${decPart}`
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function formatToMoney(raw: string): string {
|
|
95
|
+
const sanitized = sanitizeInput(raw)
|
|
96
|
+
if (!sanitized) return ''
|
|
97
|
+
|
|
98
|
+
const [intPartRaw, decPartRaw = ''] = sanitized.split('.')
|
|
99
|
+
const intPart = normalizeIntegerPart(intPartRaw || '0') || '0'
|
|
100
|
+
const decPart = decPartRaw.padEnd(2, '0').slice(0, 2)
|
|
101
|
+
return `${intPart}.${decPart}`
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function emitValue() {
|
|
105
|
+
const val = displayValue.value || null
|
|
106
|
+
emit('update:modelValue', val)
|
|
107
|
+
emit('validate', !val || FORMAT_REG.test(val))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function onInput(val: string | number) {
|
|
111
|
+
// 输入时移除千分位逗号
|
|
112
|
+
displayValue.value = sanitizeInput(fromThousands(String(val)))
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function onFocus() {
|
|
116
|
+
focused.value = true
|
|
117
|
+
hasBlurred.value = false
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function numberToChineseUppercase(numStr: string): string {
|
|
121
|
+
if (!numStr) return ''
|
|
122
|
+
|
|
123
|
+
const num = parseFloat(numStr)
|
|
124
|
+
if (isNaN(num) || num < 0) return ''
|
|
125
|
+
|
|
126
|
+
const fraction = ['角', '分']
|
|
127
|
+
const digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
|
|
128
|
+
const unit = [['元', '万', '亿'], ['', '拾', '佰', '仟']]
|
|
129
|
+
|
|
130
|
+
let s = ''
|
|
131
|
+
const parts = numStr.split('.')
|
|
132
|
+
const intPart = parts[0]
|
|
133
|
+
const decPart = parts[1] || ''
|
|
134
|
+
|
|
135
|
+
// 处理整数部分
|
|
136
|
+
let intNum = parseInt(intPart)
|
|
137
|
+
if (intNum === 0) {
|
|
138
|
+
s = '零元'
|
|
139
|
+
} else {
|
|
140
|
+
let tmp = ''
|
|
141
|
+
let zeroCount = 0
|
|
142
|
+
|
|
143
|
+
for (let i = 0; i < intPart.length; i++) {
|
|
144
|
+
const p = intPart.length - 1 - i
|
|
145
|
+
const d = parseInt(intPart[i])
|
|
146
|
+
|
|
147
|
+
if (d === 0) {
|
|
148
|
+
zeroCount++
|
|
149
|
+
} else {
|
|
150
|
+
if (zeroCount > 0) {
|
|
151
|
+
tmp += digit[0]
|
|
152
|
+
}
|
|
153
|
+
zeroCount = 0
|
|
154
|
+
tmp += digit[d] + unit[1][p % 4]
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (p % 4 === 0 && zeroCount < 4) {
|
|
158
|
+
const unitIndex = Math.floor(p / 4)
|
|
159
|
+
if (unitIndex < unit[0].length) {
|
|
160
|
+
tmp += unit[0][unitIndex]
|
|
161
|
+
zeroCount = 0
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
s = tmp.replace(/零+$/, '') + '元'
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// 处理小数部分
|
|
170
|
+
if (decPart) {
|
|
171
|
+
for (let i = 0; i < decPart.length && i < 2; i++) {
|
|
172
|
+
const d = parseInt(decPart[i])
|
|
173
|
+
if (d !== 0) {
|
|
174
|
+
s += digit[d] + fraction[i]
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (s === '零元') s = '零元整'
|
|
180
|
+
else if (!decPart || decPart === '00') s += '整'
|
|
181
|
+
|
|
182
|
+
return s
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function onBlur() {
|
|
186
|
+
focused.value = false
|
|
187
|
+
hasBlurred.value = true
|
|
188
|
+
if (displayValue.value) {
|
|
189
|
+
displayValue.value = formatToMoney(displayValue.value)
|
|
190
|
+
}
|
|
191
|
+
emitValue()
|
|
192
|
+
|
|
193
|
+
// 如果启用了中文大写显示,则弹出消息框
|
|
194
|
+
if (props.showChineseUppercase && displayValue.value) {
|
|
195
|
+
const chineseUppercase = numberToChineseUppercase(displayValue.value)
|
|
196
|
+
if (chineseUppercase) {
|
|
197
|
+
ElMessageBox.alert(
|
|
198
|
+
chineseUppercase,
|
|
199
|
+
'金额大写',
|
|
200
|
+
{
|
|
201
|
+
confirmButtonText: '确定',
|
|
202
|
+
type: 'info'
|
|
203
|
+
}
|
|
204
|
+
)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function onClear() {
|
|
210
|
+
displayValue.value = ''
|
|
211
|
+
hasBlurred.value = false
|
|
212
|
+
emitValue()
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function validate(): boolean {
|
|
216
|
+
hasBlurred.value = true
|
|
217
|
+
if (displayValue.value) {
|
|
218
|
+
displayValue.value = formatToMoney(displayValue.value)
|
|
219
|
+
emitValue()
|
|
220
|
+
}
|
|
221
|
+
return !displayValue.value || FORMAT_REG.test(displayValue.value)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function reset() {
|
|
225
|
+
displayValue.value = ''
|
|
226
|
+
hasBlurred.value = false
|
|
227
|
+
emitValue()
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
defineExpose({ validate, reset })
|
|
231
|
+
|
|
232
|
+
watch(
|
|
233
|
+
() => props.modelValue,
|
|
234
|
+
(val) => {
|
|
235
|
+
const next = val ? formatToMoney(String(val)) : ''
|
|
236
|
+
if (next !== displayValue.value) {
|
|
237
|
+
displayValue.value = next
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
{ immediate: true }
|
|
241
|
+
)
|
|
242
|
+
</script>
|
|
243
|
+
|
|
244
|
+
<style scoped>
|
|
245
|
+
.money-input {
|
|
246
|
+
display: flex;
|
|
247
|
+
flex-direction: column;
|
|
248
|
+
width: 100%;
|
|
249
|
+
min-width: 0;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.is-error :deep(.el-input__wrapper) {
|
|
253
|
+
box-shadow: 0 0 0 1px var(--el-color-danger) inset;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.money-input__error {
|
|
257
|
+
color: var(--el-color-danger);
|
|
258
|
+
font-size: 12px;
|
|
259
|
+
line-height: 1.4;
|
|
260
|
+
padding-top: 4px;
|
|
261
|
+
}
|
|
262
|
+
</style>
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<section class="page money-input-page">
|
|
3
|
+
<h1 class="title">金额输入组件</h1>
|
|
4
|
+
<p class="desc">
|
|
5
|
+
固定金额格式:整数最多 16 位,小数固定 2 位,禁止前导零(如 <code>01.00</code>)。
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<el-card class="demo-card" shadow="never">
|
|
9
|
+
<template #header>
|
|
10
|
+
<span>基础示例</span>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<el-form label-width="100px">
|
|
14
|
+
<el-form-item label="金额">
|
|
15
|
+
<MoneyInput
|
|
16
|
+
:showChineseUppercase="true"
|
|
17
|
+
ref="moneyInputRef"
|
|
18
|
+
v-model="amount"
|
|
19
|
+
placeholder="请输入金额,如 1.00"
|
|
20
|
+
@validate="onValidate"
|
|
21
|
+
/>
|
|
22
|
+
</el-form-item>
|
|
23
|
+
</el-form>
|
|
24
|
+
|
|
25
|
+
<div class="demo-output">
|
|
26
|
+
当前值:<code>{{ amount ?? 'null' }}</code>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="demo-output">
|
|
29
|
+
校验状态:<code>{{ valid ? '通过' : '不通过' }}</code>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div class="demo-actions">
|
|
33
|
+
<el-button type="primary" @click="doValidate">手动校验</el-button>
|
|
34
|
+
<el-button @click="doReset">重置</el-button>
|
|
35
|
+
</div>
|
|
36
|
+
</el-card>
|
|
37
|
+
</section>
|
|
38
|
+
</template>
|
|
39
|
+
|
|
40
|
+
<script setup lang="ts">
|
|
41
|
+
import { ref } from 'vue'
|
|
42
|
+
import MoneyInput from '../components/MoneyInput.vue'
|
|
43
|
+
|
|
44
|
+
const moneyInputRef = ref<InstanceType<typeof MoneyInput>>()
|
|
45
|
+
const amount = ref<string | null>(null)
|
|
46
|
+
const valid = ref(true)
|
|
47
|
+
|
|
48
|
+
function onValidate(v: boolean) {
|
|
49
|
+
valid.value = v
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function doValidate() {
|
|
53
|
+
valid.value = moneyInputRef.value?.validate() ?? true
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function doReset() {
|
|
57
|
+
moneyInputRef.value?.reset()
|
|
58
|
+
amount.value = null
|
|
59
|
+
valid.value = true
|
|
60
|
+
}
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<style scoped>
|
|
64
|
+
.page.money-input-page {
|
|
65
|
+
padding: 20px 24px;
|
|
66
|
+
max-width: 720px;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.title {
|
|
70
|
+
margin: 0 0 8px;
|
|
71
|
+
font-size: 20px;
|
|
72
|
+
font-weight: 600;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.desc {
|
|
76
|
+
margin: 0 0 20px;
|
|
77
|
+
color: var(--el-text-color-secondary);
|
|
78
|
+
font-size: 14px;
|
|
79
|
+
line-height: 1.5;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.demo-card {
|
|
83
|
+
margin-bottom: 16px;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.demo-output {
|
|
87
|
+
margin-top: 12px;
|
|
88
|
+
font-size: 13px;
|
|
89
|
+
color: var(--el-text-color-regular);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.demo-output code {
|
|
93
|
+
font-size: 12px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.demo-actions {
|
|
97
|
+
margin-top: 12px;
|
|
98
|
+
display: flex;
|
|
99
|
+
gap: 8px;
|
|
100
|
+
}
|
|
101
|
+
</style>
|
|
@@ -5,6 +5,7 @@ import ImportTablePage from '../pages/ImportTablePage.vue'
|
|
|
5
5
|
import PositionReportPage from '../pages/PositionReportPage.vue'
|
|
6
6
|
import DateRangePage from '../pages/DateRangePage.vue'
|
|
7
7
|
import OrgTreeSelectPage from '../pages/OrgTreeSelectPage.vue'
|
|
8
|
+
import MoneyInputPage from '../pages/MoneyInputPage.vue'
|
|
8
9
|
|
|
9
10
|
export const routes = [
|
|
10
11
|
{
|
|
@@ -73,5 +74,14 @@ export const routes = [
|
|
|
73
74
|
title: '机构树选择',
|
|
74
75
|
showInMenu: true
|
|
75
76
|
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
path: '/money-input',
|
|
80
|
+
name: 'MoneyInput',
|
|
81
|
+
component: MoneyInputPage,
|
|
82
|
+
meta: {
|
|
83
|
+
title: '金额输入',
|
|
84
|
+
showInMenu: true
|
|
85
|
+
}
|
|
76
86
|
}
|
|
77
87
|
]
|
package/package.json
CHANGED
|
Binary file
|
package/test/111
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
// src/directives/permission.ts
|
|
2
|
-
import type { Directive } from 'vue'
|
|
3
|
-
|
|
4
|
-
function getPermissions(): string[] {
|
|
5
|
-
return JSON.parse(localStorage.getItem('permissions') || '[]')
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export const permissionDirective: Directive = {
|
|
9
|
-
mounted(el, binding) {
|
|
10
|
-
const { value } = binding
|
|
11
|
-
const permissions = getPermissions()
|
|
12
|
-
|
|
13
|
-
if (!value) return
|
|
14
|
-
|
|
15
|
-
const hasPermission = Array.isArray(value)
|
|
16
|
-
? value.some(p => permissions.includes(p))
|
|
17
|
-
: permissions.includes(value)
|
|
18
|
-
|
|
19
|
-
if (!hasPermission) {
|
|
20
|
-
el.parentNode?.removeChild(el)
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
// main.ts
|
|
27
|
-
import { createApp } from 'vue'
|
|
28
|
-
import App from './App.vue'
|
|
29
|
-
import { permissionDirective } from '@/directives/permission'
|
|
30
|
-
|
|
31
|
-
const app = createApp(App)
|
|
32
|
-
|
|
33
|
-
app.directive('permission', permissionDirective)
|
|
34
|
-
|
|
35
|
-
app.mount('#app')
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
<el-button v-permission="'user:add'">新增</el-button>
|
|
39
|
-
|
|
40
|
-
<el-button v-permission="['user:edit', 'user:update']">
|
|
41
|
-
编辑
|
|
42
|
-
</el-button>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
<el-button v-permission="'user:add'">新增</el-button>
|
|
48
|
-
|
|
49
|
-
<el-button v-permission="['user:edit', 'user:update']">
|
|
50
|
-
编辑
|
|
51
|
-
</el-button>
|
|
52
|
-
|
package/test/111.txt
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
|
-
|
|
3
|
-
const service = axios.create({
|
|
4
|
-
baseURL: import.meta.env.VITE_API_BASE,
|
|
5
|
-
timeout: 15000,
|
|
6
|
-
headers: {
|
|
7
|
-
"Content-Type": "application/json",
|
|
8
|
-
},
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
// 分页字段
|
|
12
|
-
const PAGE_KEYS = ["pageIndex", "pageSize"];
|
|
13
|
-
|
|
14
|
-
// 拆分函数
|
|
15
|
-
function splitHeadBody(params = {}, tranCode) {
|
|
16
|
-
const head = {
|
|
17
|
-
tranCode, // 关键:接口地址放进 head.tranCode
|
|
18
|
-
};
|
|
19
|
-
const body = {};
|
|
20
|
-
|
|
21
|
-
Object.keys(params).forEach((key) => {
|
|
22
|
-
if (PAGE_KEYS.includes(key)) {
|
|
23
|
-
head[key] = params[key];
|
|
24
|
-
} else {
|
|
25
|
-
body[key] = params[key];
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
return { head, body };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const request = {
|
|
33
|
-
post(tranCode, params = {}, config = {}) {
|
|
34
|
-
const { head, body } = splitHeadBody(params, tranCode);
|
|
35
|
-
|
|
36
|
-
return service.post(
|
|
37
|
-
"/gateway", // 实际统一请求地址
|
|
38
|
-
{ head, body },
|
|
39
|
-
config
|
|
40
|
-
);
|
|
41
|
-
},
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export default request;
|
package/test/11111111111
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
let el = $0
|
|
2
|
-
while (el) {
|
|
3
|
-
if (el.__vueParentComponent) {
|
|
4
|
-
console.log('找到了 Vue3 实例:', el)
|
|
5
|
-
console.log(el.__vueParentComponent)
|
|
6
|
-
break
|
|
7
|
-
}
|
|
8
|
-
el = el.parentElement
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
el.__vueParentComponent.setupState
|
|
12
|
-
|
|
13
|
-
let el = $0
|
|
14
|
-
while (el) {
|
|
15
|
-
const comp = el.__vueParentComponent
|
|
16
|
-
if (comp) {
|
|
17
|
-
const data = comp.setupState || comp.ctx
|
|
18
|
-
if (data && data.forrcats) {
|
|
19
|
-
console.log('找到了 forrcats:', data.forrcats)
|
|
20
|
-
break
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
el = el.parentElement
|
|
24
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
// scripts/check-permission-duplicate.js
|
|
2
|
-
import fs from 'fs'
|
|
3
|
-
import path from 'path'
|
|
4
|
-
|
|
5
|
-
const rootDir = path.resolve(process.cwd(), 'src')
|
|
6
|
-
|
|
7
|
-
const permissionMap = new Map()
|
|
8
|
-
|
|
9
|
-
function scanFile(filePath) {
|
|
10
|
-
const content = fs.readFileSync(filePath, 'utf-8')
|
|
11
|
-
|
|
12
|
-
// 匹配 v-permission="xxx" 或 v-permission="'xxx'"
|
|
13
|
-
const regex = /v-permission\s*=\s*["']([^"']+)["']/g
|
|
14
|
-
|
|
15
|
-
let match
|
|
16
|
-
while ((match = regex.exec(content))) {
|
|
17
|
-
const permission = match[1].trim()
|
|
18
|
-
|
|
19
|
-
if (!permissionMap.has(permission)) {
|
|
20
|
-
permissionMap.set(permission, [])
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
permissionMap.get(permission).push(filePath)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function scanDir(dir) {
|
|
28
|
-
const files = fs.readdirSync(dir)
|
|
29
|
-
|
|
30
|
-
for (const file of files) {
|
|
31
|
-
const fullPath = path.join(dir, file)
|
|
32
|
-
const stat = fs.statSync(fullPath)
|
|
33
|
-
|
|
34
|
-
if (stat.isDirectory()) {
|
|
35
|
-
scanDir(fullPath)
|
|
36
|
-
} else if (/\.(vue|js|ts|jsx|tsx)$/.test(file)) {
|
|
37
|
-
scanFile(fullPath)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
scanDir(rootDir)
|
|
43
|
-
|
|
44
|
-
// 输出重复
|
|
45
|
-
console.log('\n🔍 重复的权限指令如下:\n')
|
|
46
|
-
|
|
47
|
-
let hasDuplicate = false
|
|
48
|
-
|
|
49
|
-
for (const [key, files] of permissionMap.entries()) {
|
|
50
|
-
if (files.length > 1) {
|
|
51
|
-
hasDuplicate = true
|
|
52
|
-
console.log(`🚨 权限:${key}`)
|
|
53
|
-
files.forEach(f => console.log(` - ${f}`))
|
|
54
|
-
console.log('')
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (!hasDuplicate) {
|
|
59
|
-
console.log('✅ 没有发现重复权限')
|
|
60
|
-
}
|
package/test/12.js
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
|
|
3
|
-
const content = fs.readFileSync("input.txt", "utf8");
|
|
4
|
-
|
|
5
|
-
// Java → TS 类型
|
|
6
|
-
function javaToTsType(javaType) {
|
|
7
|
-
const map = {
|
|
8
|
-
String: "string",
|
|
9
|
-
Integer: "number",
|
|
10
|
-
Long: "number",
|
|
11
|
-
Double: "number",
|
|
12
|
-
Float: "number",
|
|
13
|
-
BigDecimal: "number",
|
|
14
|
-
Boolean: "boolean",
|
|
15
|
-
Date: "string",
|
|
16
|
-
LocalDate: "string",
|
|
17
|
-
LocalDateTime: "string",
|
|
18
|
-
};
|
|
19
|
-
return map[javaType] || "any";
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// TS 类型默认值
|
|
23
|
-
function defaultValue(tsType) {
|
|
24
|
-
switch (tsType) {
|
|
25
|
-
case "string":
|
|
26
|
-
return `""`;
|
|
27
|
-
case "number":
|
|
28
|
-
return `0`;
|
|
29
|
-
case "boolean":
|
|
30
|
-
return `false`;
|
|
31
|
-
default:
|
|
32
|
-
return `null`;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* 解析策略:
|
|
38
|
-
* - 按“字段声明”逐个找:private Type field;
|
|
39
|
-
* - 然后回看它前面一段文本(最多回看 10 行左右)抓 label
|
|
40
|
-
* - label 优先级:ApiModelProperty.value > CheckV.desc > 兜底 field 名
|
|
41
|
-
*/
|
|
42
|
-
const fieldRegex = /private\s+(\w+)\s+(\w+)\s*;/g;
|
|
43
|
-
|
|
44
|
-
const lines = content.split(/\r?\n/);
|
|
45
|
-
const jsonResult = [];
|
|
46
|
-
const tsFields = [];
|
|
47
|
-
const formFields = [];
|
|
48
|
-
|
|
49
|
-
// 为了“回看”,我们把每一行的起始偏移算出来,方便从 match.index 找到行号
|
|
50
|
-
const lineStartOffsets = [];
|
|
51
|
-
let offset = 0;
|
|
52
|
-
for (const line of lines) {
|
|
53
|
-
lineStartOffsets.push(offset);
|
|
54
|
-
offset += line.length + 1; // +1 for \n(\r\n 也足够用来定位)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function findLineIndexByOffset(idx) {
|
|
58
|
-
// 二分找 <= idx 的最大 lineStartOffsets
|
|
59
|
-
let l = 0, r = lineStartOffsets.length - 1, ans = 0;
|
|
60
|
-
while (l <= r) {
|
|
61
|
-
const m = (l + r) >> 1;
|
|
62
|
-
if (lineStartOffsets[m] <= idx) { ans = m; l = m + 1; }
|
|
63
|
-
else r = m - 1;
|
|
64
|
-
}
|
|
65
|
-
return ans;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function extractLabelAround(lineIndex) {
|
|
69
|
-
// 往上最多找 12 行(你可以按需调大)
|
|
70
|
-
const start = Math.max(0, lineIndex - 12);
|
|
71
|
-
const window = lines.slice(start, lineIndex).join("\n");
|
|
72
|
-
|
|
73
|
-
// 先找 ApiModelProperty(value="xxx")
|
|
74
|
-
const api = /@ApiModelProperty\s*\(\s*value\s*=\s*"([^"]+)"/.exec(window);
|
|
75
|
-
if (api?.[1]) return api[1];
|
|
76
|
-
|
|
77
|
-
// 再找 CheckV(desc="xxx")
|
|
78
|
-
const check = /@CheckV\s*\(\s*desc\s*=\s*"([^"]+)"/.exec(window);
|
|
79
|
-
if (check?.[1]) return check[1];
|
|
80
|
-
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
let match;
|
|
85
|
-
while ((match = fieldRegex.exec(content)) !== null) {
|
|
86
|
-
const javaType = match[1];
|
|
87
|
-
const field = match[2];
|
|
88
|
-
|
|
89
|
-
const lineIndex = findLineIndexByOffset(match.index);
|
|
90
|
-
const label = extractLabelAround(lineIndex) || field;
|
|
91
|
-
|
|
92
|
-
const tsType = javaToTsType(javaType);
|
|
93
|
-
|
|
94
|
-
jsonResult.push({ field, label });
|
|
95
|
-
|
|
96
|
-
tsFields.push(` /** ${label} */\n ${field}: ${tsType};`);
|
|
97
|
-
formFields.push(` /** ${label} */\n ${field}: ${defaultValue(tsType)}`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// 写文件
|
|
101
|
-
fs.writeFileSync("output.json", JSON.stringify(jsonResult, null, 2), "utf8");
|
|
102
|
-
|
|
103
|
-
fs.writeFileSync(
|
|
104
|
-
"model.ts",
|
|
105
|
-
`export interface Model {\n${tsFields.join("\n\n")}\n}\n`,
|
|
106
|
-
"utf8"
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
fs.writeFileSync(
|
|
110
|
-
"form.js",
|
|
111
|
-
`export const formModel = {\n${formFields.join(",\n\n")}\n};\n`,
|
|
112
|
-
"utf8"
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
console.log("✅ 已生成 output.json / model.ts / form.js");
|
|
Binary file
|
|
Binary file
|
|
Binary file
|