vue2server7 7.0.51 → 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/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/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
|
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
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");
|
package/test/222.txt
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { defineStore } from "pinia"
|
|
2
|
-
import { ref, computed } from "vue"
|
|
3
|
-
import { CharRefSheet } from "@chic/types/constant"
|
|
4
|
-
import storage from "@chic/utils/storage"
|
|
5
|
-
|
|
6
|
-
export const useCBCManualVerificationStore = defineStore(
|
|
7
|
-
"CBCManualVerification",
|
|
8
|
-
() => {
|
|
9
|
-
// state
|
|
10
|
-
const selectList = ref<any[]>([])
|
|
11
|
-
|
|
12
|
-
// getter(获取)
|
|
13
|
-
const getSelectList = computed(() => selectList.value)
|
|
14
|
-
|
|
15
|
-
// actions
|
|
16
|
-
const setSelectList = (list: any[]) => {
|
|
17
|
-
selectList.value = list
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const addSelectItem = (item: any) => {
|
|
21
|
-
selectList.value.push(item)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const clearSelectList = () => {
|
|
25
|
-
selectList.value = []
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
selectList,
|
|
30
|
-
getSelectList,
|
|
31
|
-
setSelectList,
|
|
32
|
-
addSelectItem,
|
|
33
|
-
clearSelectList
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
persist: {
|
|
38
|
-
enabled: true,
|
|
39
|
-
strategies: [
|
|
40
|
-
{
|
|
41
|
-
key: CharRefSheet.k101y,
|
|
42
|
-
storage
|
|
43
|
-
}
|
|
44
|
-
]
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
)
|
package/test/3.js
DELETED
|
@@ -1,75 +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
|
-
// 默认值
|
|
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
|
-
const regex =
|
|
37
|
-
/@ApiModelProperty\s*\(\s*value\s*=\s*"([^"]+)"[^)]*\)\s*[\r\n]+\s*private\s+(\w+)\s+(\w+)\s*;/g;
|
|
38
|
-
|
|
39
|
-
let match;
|
|
40
|
-
|
|
41
|
-
const jsonResult = [];
|
|
42
|
-
const tsFields = [];
|
|
43
|
-
const formFields = [];
|
|
44
|
-
|
|
45
|
-
while ((match = regex.exec(content)) !== null) {
|
|
46
|
-
const label = match[1];
|
|
47
|
-
const javaType = match[2];
|
|
48
|
-
const field = match[3];
|
|
49
|
-
|
|
50
|
-
const tsType = javaToTsType(javaType);
|
|
51
|
-
|
|
52
|
-
// JSON
|
|
53
|
-
jsonResult.push({ field, label });
|
|
54
|
-
|
|
55
|
-
// TS
|
|
56
|
-
tsFields.push(` /** ${label} */\n ${field}: ${tsType};`);
|
|
57
|
-
|
|
58
|
-
// JS 表单对象(带注释)
|
|
59
|
-
formFields.push(` /** ${label} */\n ${field}: ${defaultValue(tsType)}`);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// 写文件
|
|
63
|
-
fs.writeFileSync("output.json", JSON.stringify(jsonResult, null, 2));
|
|
64
|
-
|
|
65
|
-
fs.writeFileSync(
|
|
66
|
-
"model.ts",
|
|
67
|
-
`export interface Model {\n${tsFields.join("\n\n")}\n}\n`
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
fs.writeFileSync(
|
|
71
|
-
"form.js",
|
|
72
|
-
`export const formModel = {\n${formFields.join(",\n\n")}\n};\n`
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
console.log("✅ 已生成 output.json / model.ts / form.js");
|
package/test/4.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
const express = require('express');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
|
-
const app = express();
|
|
6
|
-
const PORT = 3000;
|
|
7
|
-
|
|
8
|
-
// 设置 public 为静态目录
|
|
9
|
-
const publicDir = path.join(__dirname, 'public');
|
|
10
|
-
app.use('/static', express.static(publicDir));
|
|
11
|
-
|
|
12
|
-
// 首页:列出 public 文件夹里的所有文件
|
|
13
|
-
app.get('/', (req, res) => {
|
|
14
|
-
fs.readdir(publicDir, (err, files) => {
|
|
15
|
-
if (err) {
|
|
16
|
-
return res.status(500).send('读取文件夹失败');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
let fileListHtml = `
|
|
20
|
-
<h1>文件列表</h1>
|
|
21
|
-
<ul>
|
|
22
|
-
`;
|
|
23
|
-
|
|
24
|
-
files.forEach(file => {
|
|
25
|
-
fileListHtml += `
|
|
26
|
-
<li>
|
|
27
|
-
${file} -
|
|
28
|
-
<a href="/download/${file}">下载</a>
|
|
29
|
-
</li>
|
|
30
|
-
`;
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
fileListHtml += `
|
|
34
|
-
</ul>
|
|
35
|
-
`;
|
|
36
|
-
|
|
37
|
-
res.send(fileListHtml);
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
// 下载接口
|
|
42
|
-
app.get('/download/:filename', (req, res) => {
|
|
43
|
-
const filename = req.params.filename;
|
|
44
|
-
const filePath = path.join(publicDir, filename);
|
|
45
|
-
|
|
46
|
-
// 防止路径穿越攻击
|
|
47
|
-
if (!filePath.startsWith(publicDir)) {
|
|
48
|
-
return res.status(400).send('非法访问');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
res.download(filePath, filename, (err) => {
|
|
52
|
-
if (err) {
|
|
53
|
-
res.status(404).send('文件不存在');
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
app.listen(PORT, () => {
|
|
59
|
-
console.log(`服务器已启动: http://localhost:${PORT}`);
|
|
60
|
-
});
|
package/test/6.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
const express = require('express');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
|
-
const app = express();
|
|
6
|
-
const PORT = 3000;
|
|
7
|
-
|
|
8
|
-
// 设置 public 为静态目录
|
|
9
|
-
const publicDir = path.join(__dirname, 'public');
|
|
10
|
-
app.use('/static', express.static(publicDir));
|
|
11
|
-
|
|
12
|
-
// 首页:列出 public 文件夹里的所有文件
|
|
13
|
-
app.get('/', (req, res) => {
|
|
14
|
-
fs.readdir(publicDir, (err, files) => {
|
|
15
|
-
if (err) {
|
|
16
|
-
return res.status(500).send('读取文件夹失败');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
let fileListHtml = `
|
|
20
|
-
<h1>文件列表</h1>
|
|
21
|
-
<ul>
|
|
22
|
-
`;
|
|
23
|
-
|
|
24
|
-
files.forEach(file => {
|
|
25
|
-
fileListHtml += `
|
|
26
|
-
<li>
|
|
27
|
-
${file} -
|
|
28
|
-
<a href="/download/${file}">下载</a>
|
|
29
|
-
</li>
|
|
30
|
-
`;
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
fileListHtml += `
|
|
34
|
-
</ul>
|
|
35
|
-
`;
|
|
36
|
-
|
|
37
|
-
res.send(fileListHtml);
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
// 下载接口
|
|
42
|
-
app.get('/download/:filename', (req, res) => {
|
|
43
|
-
const filename = req.params.filename;
|
|
44
|
-
const filePath = path.join(publicDir, filename);
|
|
45
|
-
|
|
46
|
-
// 防止路径穿越攻击
|
|
47
|
-
if (!filePath.startsWith(publicDir)) {
|
|
48
|
-
return res.status(400).send('非法访问');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
res.download(filePath, filename, (err) => {
|
|
52
|
-
if (err) {
|
|
53
|
-
res.status(404).send('文件不存在');
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
app.listen(PORT, () => {
|
|
59
|
-
console.log(`服务器已启动: http://localhost:${PORT}`);
|
|
60
|
-
});
|
package/test/777
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import axios from 'axios'
|
|
2
|
-
|
|
3
|
-
const downloadFile = async (url: string, filename = 'file.zip') => {
|
|
4
|
-
const res = await axios.get(url, {
|
|
5
|
-
responseType: 'blob',
|
|
6
|
-
onDownloadProgress: (e) => {
|
|
7
|
-
if (e.total) {
|
|
8
|
-
const percent = Math.round((e.loaded / e.total) * 100)
|
|
9
|
-
console.log('下载进度:', percent + '%')
|
|
10
|
-
} else {
|
|
11
|
-
console.log('已下载:', e.loaded)
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
// 创建下载
|
|
17
|
-
const blob = new Blob([res.data])
|
|
18
|
-
const link = document.createElement('a')
|
|
19
|
-
link.href = URL.createObjectURL(blob)
|
|
20
|
-
link.download = filename
|
|
21
|
-
link.click()
|
|
22
|
-
|
|
23
|
-
URL.revokeObjectURL(link.href)
|
|
24
|
-
}
|
package/test/9.text
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
|
|
3
|
-
const content = fs.readFileSync("input.txt", "utf8");
|
|
4
|
-
const lines = content.split(/\r?\n/);
|
|
5
|
-
|
|
6
|
-
// Java → TS 类型
|
|
7
|
-
function javaToTsType(javaType) {
|
|
8
|
-
const map = {
|
|
9
|
-
String: "string",
|
|
10
|
-
Integer: "number",
|
|
11
|
-
Long: "number",
|
|
12
|
-
Double: "number",
|
|
13
|
-
Float: "number",
|
|
14
|
-
BigDecimal: "number",
|
|
15
|
-
Boolean: "boolean",
|
|
16
|
-
Date: "string",
|
|
17
|
-
LocalDate: "string",
|
|
18
|
-
LocalDateTime: "string",
|
|
19
|
-
};
|
|
20
|
-
return map[javaType] || "any";
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// TS 默认值
|
|
24
|
-
function defaultValue(tsType) {
|
|
25
|
-
switch (tsType) {
|
|
26
|
-
case "string":
|
|
27
|
-
return `""`;
|
|
28
|
-
case "number":
|
|
29
|
-
return `0`;
|
|
30
|
-
case "boolean":
|
|
31
|
-
return `false`;
|
|
32
|
-
default:
|
|
33
|
-
return `null`;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// 支持:@ApiModelProperty(value="xx") / @ApiModelProperty(value = "xx", required=true)
|
|
38
|
-
// 也顺手兼容:@ApiModelProperty("xx") 这种写法(有些项目会这样写)
|
|
39
|
-
function parseApiModelProperty(line) {
|
|
40
|
-
let m = /@ApiModelProperty\s*\(\s*value\s*=\s*"([^"]+)"/.exec(line);
|
|
41
|
-
if (m?.[1]) return m[1];
|
|
42
|
-
m = /@ApiModelProperty\s*\(\s*"([^"]+)"\s*\)/.exec(line);
|
|
43
|
-
if (m?.[1]) return m[1];
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// 支持:@CheckV(desc="xx") / @CheckV(desc = "xx", ...)
|
|
48
|
-
function parseCheckV(line) {
|
|
49
|
-
const m = /@CheckV\s*\(\s*desc\s*=\s*"([^"]+)"/.exec(line);
|
|
50
|
-
return m?.[1] || null;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// 字段:private String queryDate;
|
|
54
|
-
function parseField(line) {
|
|
55
|
-
const m = /^\s*private\s+(\w+)\s+(\w+)\s*;\s*$/.exec(line);
|
|
56
|
-
if (!m) return null;
|
|
57
|
-
return { javaType: m[1], field: m[2] };
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
let apiLabel = null;
|
|
61
|
-
let checkLabel = null;
|
|
62
|
-
|
|
63
|
-
const jsonResult = [];
|
|
64
|
-
const tsFields = [];
|
|
65
|
-
const formFields = [];
|
|
66
|
-
|
|
67
|
-
for (const line of lines) {
|
|
68
|
-
const a = parseApiModelProperty(line);
|
|
69
|
-
if (a) {
|
|
70
|
-
apiLabel = a;
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const c = parseCheckV(line);
|
|
75
|
-
if (c) {
|
|
76
|
-
checkLabel = c;
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const f = parseField(line);
|
|
81
|
-
if (f) {
|
|
82
|
-
const label = apiLabel || checkLabel || f.field;
|
|
83
|
-
const tsType = javaToTsType(f.javaType);
|
|
84
|
-
|
|
85
|
-
jsonResult.push({ field: f.field, label });
|
|
86
|
-
tsFields.push(` /** ${label} */\n ${f.field}: ${tsType};`);
|
|
87
|
-
formFields.push(` /** ${label} */\n ${f.field}: ${defaultValue(tsType)}`);
|
|
88
|
-
|
|
89
|
-
// ✅ 关键:输出后立刻清空,避免串到下一个字段
|
|
90
|
-
apiLabel = null;
|
|
91
|
-
checkLabel = null;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// 写文件
|
|
96
|
-
fs.writeFileSync("output.json", JSON.stringify(jsonResult, null, 2), "utf8");
|
|
97
|
-
fs.writeFileSync("model.ts", `export interface Model {\n${tsFields.join("\n\n")}\n}\n`, "utf8");
|
|
98
|
-
fs.writeFileSync("form.js", `export const formModel = {\n${formFields.join(",\n\n")}\n};\n`, "utf8");
|
|
99
|
-
|
|
100
|
-
console.log("✅ 已生成 output.json / model.ts / form.js");
|
|
@@ -1,52 +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
|
-
|
|
20
|
-
return map[javaType] || "any";
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// 正则匹配 注解 + 字段
|
|
24
|
-
const regex =
|
|
25
|
-
/@ApiModelProperty\s*\(\s*value\s*=\s*"([^"]+)"[^)]*\)\s*[\r\n]+\s*private\s+(\w+)\s+(\w+)\s*;/g;
|
|
26
|
-
|
|
27
|
-
let match;
|
|
28
|
-
const jsonResult = [];
|
|
29
|
-
const tsFields = [];
|
|
30
|
-
|
|
31
|
-
while ((match = regex.exec(content)) !== null) {
|
|
32
|
-
const label = match[1]; // 中文
|
|
33
|
-
const javaType = match[2]; // Java类型
|
|
34
|
-
const field = match[3]; // 字段名
|
|
35
|
-
const tsType = javaToTsType(javaType);
|
|
36
|
-
|
|
37
|
-
jsonResult.push({
|
|
38
|
-
field,
|
|
39
|
-
label
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
tsFields.push(` /** ${label} */\n ${field}: ${tsType};`);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// 输出 JSON 文件
|
|
46
|
-
fs.writeFileSync("output.json", JSON.stringify(jsonResult, null, 2));
|
|
47
|
-
|
|
48
|
-
// 输出 TS 文件
|
|
49
|
-
const tsContent = `export interface Model {\n${tsFields.join("\n\n")}\n}\n`;
|
|
50
|
-
fs.writeFileSync("model.ts", tsContent);
|
|
51
|
-
|
|
52
|
-
console.log("✅ 已生成 output.json 和 model.ts");
|
package/test/fun.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
function addIdText(list = []) {
|
|
2
|
-
const stack = [...list]
|
|
3
|
-
|
|
4
|
-
while (stack.length) {
|
|
5
|
-
const node = stack.pop()
|
|
6
|
-
|
|
7
|
-
node.idText = `${node.id}-${node.text}`
|
|
8
|
-
|
|
9
|
-
if (Array.isArray(node.children) && node.children.length) {
|
|
10
|
-
stack.push(...node.children)
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return list
|
|
15
|
-
}
|
package/test/health.test.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
const assert = require("node:assert/strict");
|
|
2
|
-
const test = require("node:test");
|
|
3
|
-
|
|
4
|
-
const request = require("supertest");
|
|
5
|
-
|
|
6
|
-
const app = require("../dist/app").default;
|
|
7
|
-
|
|
8
|
-
test("GET /health returns ok", async () => {
|
|
9
|
-
const res = await request(app).get("/api/health").expect(200);
|
|
10
|
-
assert.equal(res.body.code, 0);
|
|
11
|
-
assert.equal(res.body.data.status, "ok");
|
|
12
|
-
});
|
package/test/i.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { execSync } from 'child_process'
|
|
2
|
-
|
|
3
|
-
export function hasWebStorm() {
|
|
4
|
-
try {
|
|
5
|
-
const result = execSync(
|
|
6
|
-
`mdfind "kMDItemCFBundleIdentifier == 'com.jetbrains.WebStorm'"`,
|
|
7
|
-
{ encoding: 'utf-8' }
|
|
8
|
-
).trim()
|
|
9
|
-
|
|
10
|
-
return result.length > 0
|
|
11
|
-
} catch (e) {
|
|
12
|
-
return false
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// 测试
|
|
17
|
-
console.log('WebStorm 是否安装:', hasWebStorm())
|
package/test/trend.test.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
const assert = require("node:assert/strict");
|
|
2
|
-
const test = require("node:test");
|
|
3
|
-
const request = require("supertest");
|
|
4
|
-
|
|
5
|
-
const app = require("../dist/app").default;
|
|
6
|
-
|
|
7
|
-
function isLeapYear(year) {
|
|
8
|
-
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
test("POST /api/mock/trend returns full year daily data", async () => {
|
|
12
|
-
const year = 2024;
|
|
13
|
-
const res = await request(app).post("/api/mock/trend").send({ year }).expect(200);
|
|
14
|
-
assert.equal(res.body.code, 0);
|
|
15
|
-
const data = res.body.data;
|
|
16
|
-
assert.ok(Array.isArray(data), "data should be an array");
|
|
17
|
-
assert.equal(data[0].label, `${year}-01-01`);
|
|
18
|
-
assert.equal(data[data.length - 1].label, `${year}-12-31`);
|
|
19
|
-
assert.equal(data.length, isLeapYear(year) ? 366 : 365);
|
|
20
|
-
// Check that all values are either number or "_"
|
|
21
|
-
for (const item of data) {
|
|
22
|
-
assert.ok(typeof item.value === "number" || item.value === "_", `Value ${item.value} at ${item.label} is invalid`);
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test("POST /api/mock/trend rejects invalid year", async () => {
|
|
27
|
-
const res = await request(app).post("/api/mock/trend").send({ year: "abc" }).expect(400);
|
|
28
|
-
assert.equal(res.body.code, "INVALID_YEAR");
|
|
29
|
-
});
|
|
30
|
-
|