feat: 添加Neo4j图数据库支持及前端代码重构
- 新增 Neo4j 图数据库 handler、service、model - 后端添加 SaveGraph API 接口 - 前端 Database.vue 重构,拆分为独立组件 - 新增 web/src/views/database/ 组件目录 - 删除临时文件 (temp_*.go) - 添加 Neo4j 相关 API 需求文档 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
95
web/src/utils/format.ts
Normal file
95
web/src/utils/format.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* 格式化工具函数
|
||||
*/
|
||||
|
||||
/**
|
||||
* 格式化日期
|
||||
* @param date - 日期字符串或 Date 对象
|
||||
* @param format - 格式化模板,默认 'YYYY-MM-DD'
|
||||
*/
|
||||
export function formatDate(date: string | Date, format: string = 'YYYY-MM-DD'): string {
|
||||
if (!date) return ''
|
||||
|
||||
const d = typeof date === 'string' ? new Date(date) : date
|
||||
|
||||
if (isNaN(d.getTime())) return ''
|
||||
|
||||
const year = d.getFullYear()
|
||||
const month = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(d.getDate()).padStart(2, '0')
|
||||
const hours = String(d.getHours()).padStart(2, '0')
|
||||
const minutes = String(d.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(d.getSeconds()).padStart(2, '0')
|
||||
|
||||
return format
|
||||
.replace('YYYY', String(year))
|
||||
.replace('MM', month)
|
||||
.replace('DD', day)
|
||||
.replace('HH', hours)
|
||||
.replace('mm', minutes)
|
||||
.replace('ss', seconds)
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化数字(千分位)
|
||||
* @param num - 数字
|
||||
*/
|
||||
export function formatNumber(num: number | string): string {
|
||||
if (num === null || num === undefined) return ''
|
||||
const n = typeof num === 'string' ? parseFloat(num) : num
|
||||
if (isNaN(n)) return ''
|
||||
return n.toLocaleString()
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文件大小
|
||||
* @param bytes - 字节数
|
||||
*/
|
||||
export function formatFileSize(bytes: number): string {
|
||||
if (bytes === 0) return '0 B'
|
||||
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
const k = 1024
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
|
||||
return `${(bytes / Math.pow(k, i)).toFixed(2)} ${units[i]}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 截断文本
|
||||
* @param text - 文本
|
||||
* @param maxLength - 最大长度
|
||||
* @param suffix - 后缀,默认 '...'
|
||||
*/
|
||||
export function truncate(text: string, maxLength: number, suffix: string = '...'): string {
|
||||
if (!text || text.length <= maxLength) return text
|
||||
return text.slice(0, maxLength) + suffix
|
||||
}
|
||||
|
||||
/**
|
||||
* 首字母大写
|
||||
* @param text - 文本
|
||||
*/
|
||||
export function capitalize(text: string): string {
|
||||
if (!text) return ''
|
||||
return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase()
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时长(秒)
|
||||
* @param seconds - 秒数
|
||||
*/
|
||||
export function formatDuration(seconds: number): string {
|
||||
if (!seconds || seconds < 0) return '0s'
|
||||
|
||||
const h = Math.floor(seconds / 3600)
|
||||
const m = Math.floor((seconds % 3600) / 60)
|
||||
const s = Math.floor(seconds % 60)
|
||||
|
||||
const parts: string[] = []
|
||||
if (h > 0) parts.push(`${h}h`)
|
||||
if (m > 0) parts.push(`${m}m`)
|
||||
if (s > 0 || parts.length === 0) parts.push(`${s}s`)
|
||||
|
||||
return parts.join(' ')
|
||||
}
|
||||
128
web/src/utils/validate.ts
Normal file
128
web/src/utils/validate.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* 校验工具函数
|
||||
*/
|
||||
|
||||
/**
|
||||
* 校验邮箱
|
||||
*/
|
||||
export function isEmail(value: string): boolean {
|
||||
const reg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||
return reg.test(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验手机号(中国大陆)
|
||||
*/
|
||||
export function isPhone(value: string): boolean {
|
||||
const reg = /^1[3-9]\d{9}$/
|
||||
return reg.test(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验 URL
|
||||
*/
|
||||
export function isUrl(value: string): boolean {
|
||||
try {
|
||||
new URL(value)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验 IP 地址
|
||||
*/
|
||||
export function isIP(value: string): boolean {
|
||||
const reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/
|
||||
return reg.test(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验端口号
|
||||
*/
|
||||
export function isPort(value: string | number): boolean {
|
||||
const port = typeof value === 'string' ? parseInt(value) : value
|
||||
return !isNaN(port) && port >= 1 && port <= 65535
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验必填
|
||||
*/
|
||||
export function isRequired(value: any): boolean {
|
||||
if (value === null || value === undefined) return false
|
||||
if (typeof value === 'string') return value.trim().length > 0
|
||||
if (Array.isArray(value)) return value.length > 0
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验最小长度
|
||||
*/
|
||||
export function minLength(value: string, min: number): boolean {
|
||||
return value.length >= min
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验最大长度
|
||||
*/
|
||||
export function maxLength(value: string, max: number): boolean {
|
||||
return value.length <= max
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验数值范围
|
||||
*/
|
||||
export function inRange(value: number, min: number, max: number): boolean {
|
||||
return value >= min && value <= max
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验密码强度
|
||||
* @returns 0-4, 0=无, 1=弱, 2=中, 3=强, 4=很强
|
||||
*/
|
||||
export function passwordStrength(password: string): number {
|
||||
if (!password) return 0
|
||||
|
||||
let strength = 0
|
||||
|
||||
// 长度
|
||||
if (password.length >= 8) strength++
|
||||
if (password.length >= 12) strength++
|
||||
|
||||
// 字符类型
|
||||
if (/[a-z]/.test(password)) strength++
|
||||
if (/[A-Z]/.test(password)) strength++
|
||||
if (/[0-9]/.test(password)) strength++
|
||||
if (/[^a-zA-Z0-9]/.test(password)) strength++
|
||||
|
||||
return Math.min(strength, 4)
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验对象
|
||||
*/
|
||||
export interface ValidationRule {
|
||||
validator: (value: any) => boolean
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface ValidationResult {
|
||||
valid: boolean
|
||||
errors: string[]
|
||||
}
|
||||
|
||||
export function validate(value: any, rules: ValidationRule[]): ValidationResult {
|
||||
const errors: string[] = []
|
||||
|
||||
for (const rule of rules) {
|
||||
if (!rule.validator(value)) {
|
||||
errors.push(rule.message)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
valid: errors.length === 0,
|
||||
errors,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user