diff --git a/src/components/common/BaseButton.vue b/src/components/common/BaseButton.vue new file mode 100644 index 0000000..89ed44a --- /dev/null +++ b/src/components/common/BaseButton.vue @@ -0,0 +1,83 @@ + + + \ No newline at end of file diff --git a/src/components/common/BaseModal.vue b/src/components/common/BaseModal.vue new file mode 100644 index 0000000..31520ca --- /dev/null +++ b/src/components/common/BaseModal.vue @@ -0,0 +1,130 @@ + + + + + \ No newline at end of file diff --git a/src/components/common/DataPagination.vue b/src/components/common/DataPagination.vue new file mode 100644 index 0000000..cacd577 --- /dev/null +++ b/src/components/common/DataPagination.vue @@ -0,0 +1,115 @@ + + + \ No newline at end of file diff --git a/src/components/common/DataTable.vue b/src/components/common/DataTable.vue new file mode 100644 index 0000000..cdf2107 --- /dev/null +++ b/src/components/common/DataTable.vue @@ -0,0 +1,44 @@ + + + \ No newline at end of file diff --git a/src/components/common/FormInput.vue b/src/components/common/FormInput.vue new file mode 100644 index 0000000..c9f9868 --- /dev/null +++ b/src/components/common/FormInput.vue @@ -0,0 +1,60 @@ + + + \ No newline at end of file diff --git a/src/components/common/PageFooter.vue b/src/components/common/PageFooter.vue new file mode 100644 index 0000000..a0e6802 --- /dev/null +++ b/src/components/common/PageFooter.vue @@ -0,0 +1,53 @@ + + + \ No newline at end of file diff --git a/src/components/common/PageHeader.vue b/src/components/common/PageHeader.vue new file mode 100644 index 0000000..35a8f77 --- /dev/null +++ b/src/components/common/PageHeader.vue @@ -0,0 +1,57 @@ + + + + + \ No newline at end of file diff --git a/src/components/common/RetrieveForm.vue b/src/components/common/RetrieveForm.vue new file mode 100644 index 0000000..5e0efcc --- /dev/null +++ b/src/components/common/RetrieveForm.vue @@ -0,0 +1,101 @@ + + + + + \ No newline at end of file diff --git a/src/components/common/StatCard.vue b/src/components/common/StatCard.vue new file mode 100644 index 0000000..e86cbab --- /dev/null +++ b/src/components/common/StatCard.vue @@ -0,0 +1,69 @@ + + + \ No newline at end of file diff --git a/src/utils/common.ts b/src/utils/common.ts new file mode 100644 index 0000000..a7149cb --- /dev/null +++ b/src/utils/common.ts @@ -0,0 +1,241 @@ +/** + * 通用工具函数 + */ + +/** + * 格式化时间戳为可读格式 + * @param timestamp 时间戳字符串 + * @param format 格式类型 + * @returns 格式化后的时间字符串 + */ +export function formatTimestamp(timestamp: string, format: 'datetime' | 'date' | 'time' = 'datetime'): string { + const date = new Date(timestamp) + const year = date.getFullYear() + const month = (date.getMonth() + 1).toString().padStart(2, '0') + const day = date.getDate().toString().padStart(2, '0') + const hours = date.getHours().toString().padStart(2, '0') + const minutes = date.getMinutes().toString().padStart(2, '0') + const seconds = date.getSeconds().toString().padStart(2, '0') + + switch (format) { + case 'date': + return `${year}-${month}-${day}` + case 'time': + return `${hours}:${minutes}:${seconds}` + case 'datetime': + default: + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` + } +} + +/** + * 格式化文件大小 + * @param bytes 字节数 + * @param decimals 小数位数 + * @returns 格式化后的文件大小字符串 + */ +export function formatFileSize(bytes: number, decimals: number = 2): string { + if (bytes === 0) return '0 Bytes' + + const k = 1024 + const dm = decimals < 0 ? 0 : decimals + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] + + const i = Math.floor(Math.log(bytes) / Math.log(k)) + + return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i] +} + +/** + * 格式化持续时间 + * @param seconds 秒数 + * @returns 格式化后的持续时间字符串 + */ +export function formatDuration(seconds: number): string { + if (seconds === 0) return '永久' + + const units = [ + { name: '天', value: 86400 }, + { name: '小时', value: 3600 }, + { name: '分钟', value: 60 }, + { name: '秒', value: 1 } + ] + + for (const unit of units) { + if (seconds >= unit.value) { + const value = Math.floor(seconds / unit.value) + return `${value}${unit.name}` + } + } + + return `${seconds}秒` +} + +/** + * 复制文本到剪贴板 + * @param text 要复制的文本 + * @returns Promise 是否复制成功 + */ +export async function copyToClipboard(text: string): Promise { + try { + if (navigator.clipboard && window.isSecureContext) { + await navigator.clipboard.writeText(text) + return true + } else { + // 降级方案 + const textArea = document.createElement('textarea') + textArea.value = text + textArea.style.position = 'fixed' + textArea.style.left = '-999999px' + textArea.style.top = '-999999px' + document.body.appendChild(textArea) + textArea.focus() + textArea.select() + const result = document.execCommand('copy') + textArea.remove() + return result + } + } catch (error) { + console.error('复制失败:', error) + return false + } +} + +/** + * 防抖函数 + * @param func 要防抖的函数 + * @param wait 等待时间(毫秒) + * @returns 防抖后的函数 + */ +export function debounce unknown>( + func: T, + wait: number +): (...args: Parameters) => void { + let timeout: number | null = null + + return (...args: Parameters) => { + if (timeout) { + clearTimeout(timeout) + } + timeout = setTimeout(() => func(...args), wait) + } +} + +/** + * 节流函数 + * @param func 要节流的函数 + * @param limit 限制时间(毫秒) + * @returns 节流后的函数 + */ +export function throttle unknown>( + func: T, + limit: number +): (...args: Parameters) => void { + let inThrottle: boolean = false + + return (...args: Parameters) => { + if (!inThrottle) { + func(...args) + inThrottle = true + setTimeout(() => inThrottle = false, limit) + } + } +} + +/** + * 验证邮箱格式 + * @param email 邮箱地址 + * @returns 是否为有效邮箱 + */ +export function isValidEmail(email: string): boolean { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ + return emailRegex.test(email) +} + +/** + * 验证URL格式 + * @param url URL地址 + * @returns 是否为有效URL + */ +export function isValidUrl(url: string): boolean { + try { + new URL(url) + return true + } catch { + return false + } +} + +/** + * 生成随机字符串 + * @param length 字符串长度 + * @param chars 可选字符集 + * @returns 随机字符串 + */ +export function generateRandomString( + length: number = 8, + chars: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' +): string { + let result = '' + for (let i = 0; i < length; i++) { + result += chars.charAt(Math.floor(Math.random() * chars.length)) + } + return result +} + +/** + * 深度克隆对象 + * @param obj 要克隆的对象 + * @returns 克隆后的对象 + */ +export function deepClone(obj: T): T { + if (obj === null || typeof obj !== 'object') { + return obj + } + + if (obj instanceof Date) { + return new Date(obj.getTime()) as T + } + + if (obj instanceof Array) { + return obj.map(item => deepClone(item)) as T + } + + if (typeof obj === 'object') { + const clonedObj = {} as T + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + clonedObj[key] = deepClone(obj[key]) + } + } + return clonedObj + } + + return obj +} + +/** + * 获取文件扩展名 + * @param filename 文件名 + * @returns 文件扩展名(不包含点) + */ +export function getFileExtension(filename: string): string { + return filename.slice((filename.lastIndexOf('.') - 1 >>> 0) + 2) +} + +/** + * 检查是否为移动设备 + * @returns 是否为移动设备 + */ +export function isMobile(): boolean { + return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) +} + +/** + * 格式化数字,添加千分位分隔符 + * @param num 数字 + * @returns 格式化后的数字字符串 + */ +export function formatNumber(num: number): string { + return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') +} \ No newline at end of file diff --git a/src/views/RetrievewFileView.vue b/src/views/RetrievewFileView.vue index 9033c82..6aab376 100644 --- a/src/views/RetrievewFileView.vue +++ b/src/views/RetrievewFileView.vue @@ -12,363 +12,60 @@ ]" >
-
-
-
- -
-
-
-

- {{ config.name }} -

-
-
- -
- -
- -
-
-
-
- -
- -
- - 需要发送文件?点击这里 - -
-
-
- - - 安全加密 - - + +
+ - -
-
-

- 取件记录 -

- -
-
- -
-
- -
-
-

- {{ record.filename }} -

-

- {{ record.date }} · {{ record.size }} -

-
-
- - - -
-
-
-
-
-
+ + + - -
-
-

- 文件详情 -

-
-
- -

- 文件名:{{ selectedRecord.filename }} -

-
-
- -

- 取件日期:{{ selectedRecord.date }} -

-
-
- -

- 文件大小:{{ selectedRecord.size }} -

-
-
- -

- 文件内容: -

-
- -
- -
-
- -
-

- 取件二维码 -

-
- -
-

- 扫描二维码快速取件 -

-
- - -
-
-
- -
-
-
-

- 内容预览 -

-
- - -
-
-
-
-
-
-
-
+ + +