vue2server7 7.0.51 → 7.0.53

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.
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>
@@ -52,6 +52,13 @@
52
52
  >
53
53
  <template #default="{ node, data }">
54
54
  <span class="org-tree-select__node">
55
+ <el-radio
56
+ v-if="!multiple"
57
+ :label="data.id"
58
+ v-model="selectedValue"
59
+ class="org-tree-select__radio"
60
+ @click.stop
61
+ />
55
62
  <el-icon v-if="data.icon" class="org-tree-select__node-icon">
56
63
  <component :is="data.icon" />
57
64
  </el-icon>
@@ -279,19 +286,23 @@ function onDialogOpened(): void {
279
286
  if (props.multiple) {
280
287
  const keys = Array.isArray(selectedValue.value) ? selectedValue.value : []
281
288
  treeRef.value.setCheckedKeys(keys as (string | number)[])
282
- } else if (selectedValue.value !== null) {
283
- treeRef.value.setCurrentKey(selectedValue.value as string | number)
289
+ } else {
290
+ // 单选时,同步 selectedValue 为当前选中的 ID
291
+ const currentNode = treeRef.value?.getCurrentNode() as OrgTreeNode | null
292
+ if (currentNode) {
293
+ selectedValue.value = currentNode.id
294
+ }
284
295
  }
285
296
  })
286
297
  }
287
298
 
288
299
  /**
289
- * 节点点击事件(单选模式)
300
+ * 节点点击事件(多选模式,单选由 el-radio v-model 处理)
290
301
  * @param data - 点击的节点数据
291
302
  */
292
303
  function onNodeClick(data: OrgTreeNode): void {
293
304
  if (props.multiple) return
294
- selectedValue.value = data.id
305
+ // 单选时选中节点由 el-radio v-model 处理,这里同步 selectedNodes 用于确认后返回
295
306
  selectedNodes.value = [data]
296
307
  }
297
308
 
@@ -319,12 +330,10 @@ function onConfirm(): void {
319
330
  emit('update:modelValue', keys)
320
331
  emit('change', keys, checkedNodes)
321
332
  } else {
322
- const currentNode = treeRef.value?.getCurrentNode() as OrgTreeNode | null
323
- if (currentNode) {
324
- selectedValue.value = currentNode.id
325
- selectedNodes.value = [currentNode]
326
- emit('update:modelValue', currentNode.id)
327
- emit('change', currentNode.id, currentNode)
333
+ // selectedValue 现在直接是选中的 ID
334
+ if (selectedValue.value !== null && selectedValue.value !== undefined) {
335
+ emit('update:modelValue', selectedValue.value)
336
+ emit('change', selectedValue.value, selectedNodes.value[0])
328
337
  }
329
338
  }
330
339
  dialogVisible.value = false
@@ -420,6 +429,15 @@ defineExpose<OrgTreeSelectExpose>({
420
429
  gap: 4px;
421
430
  }
422
431
 
432
+ .org-tree-select__radio {
433
+ margin-right: 4px;
434
+ }
435
+
436
+ .org-tree-select__radio :deep(.el-radio__inner) {
437
+ width: 14px;
438
+ height: 14px;
439
+ }
440
+
423
441
  .org-tree-select__node-icon {
424
442
  color: var(--el-color-primary);
425
443
  font-size: 16px;
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue2server7",
3
- "version": "7.0.51",
3
+ "version": "7.0.53",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "dev": "nodemon --watch src --ext ts --exec \"ts-node src/app.ts\"",
package/test/11.js ADDED
@@ -0,0 +1,19 @@
1
+ // Please install OpenAI SDK first: `npm install openai`
2
+
3
+ import OpenAI from "openai";
4
+
5
+ const openai = new OpenAI({
6
+ baseURL: 'https://api.deepseek.com',
7
+ apiKey: process.env.DEEPSEEK_API_KEY,
8
+ });
9
+
10
+ async function main() {
11
+ const completion = await openai.chat.completions.create({
12
+ messages: [{ role: "system", content: "You are a helpful assistant." }],
13
+ model: "deepseek-chat",
14
+ });
15
+
16
+ console.log(completion.choices[0].message.content);
17
+ }
18
+
19
+ main();
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
- }
@@ -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())
@@ -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
-