JS 和 java 格式化文件大小 单位:Bytes、KB、MB、GB...

该博客介绍了如何使用JS和Java将表示文件大小的数字自适应地转换为KB、MB和GB。提供了三种不同的方法,包括直接从Bytes转换以及指定原始单位的转换。

目录

输入一个表示文件大小的数字,自适应转换到KB,MB,GB

1、JS格式化文件大小

   方法一:bytes自适应转换到KB,MB,GB

   方法二:bytes自适应转换到KB,MB,GB

   方法三:可以设定输入的文件长度的参数的原始单位,自适应转换到KB,MB,GB

        2、 java格式化文件大小


输入一个表示文件大小的数字,自适应转换到KB,MB,GB

  • JS格式化文件大小

   方法一:bytes自适应转换到KB,MB,GB

/// <summary>
/// 格式化文件大小的JS方法
/// </summary>
/// <param name="filesize">文件的大小,传入的是一个bytes为单位的参数</param>
/// <returns>格式化后的值</returns>
function renderSize(filesize){
    if(null==value||value==''){
        return "0 Bytes";
    }
    var unitArr = new Array("Bytes","KB","MB","GB","TB","PB","EB","ZB","YB");
    var index=0;
    var srcsize = parseFloat(value);
    index=Math.floor(Math.log(srcsize)/Math.log(1024));
    var size =srcsize/Math.pow(1024,index);
       if(size%1 === 0){
            size=size.toFixed(0); //如果是整数不保留小数
        }else{
            size=size.toFixed(2);//保留的小数位数
        }
    return size+unitArr[index];
}

方法二:bytes自适应转换到KB,MB,GB

function formatFileSize(fileSize) {
    if (fileSize < 1024) {
        return fileSize + 'B';
    } else if (fileSize < (1024*1024)) {
        var temp = fileSize / 1024;
        temp = temp.toFixed(2);
        return temp + 'KB';
    } else if (fileSize < (1024*1024*1024)) {
        var temp = fileSize / (1024*1024);
        temp = temp.toFixed(2);
        return temp + 'MB';
    } else {
        var temp = fileSize / (1024*1024*1024);
        temp = temp.toFixed(2);
        return temp + 'GB';
    }
}

方法三:可以设定输入的文件长度的参数的原始单位,自适应转换到KB,MB,GB

/**
 * [fileLengthFormat 格式化文件大小]
 * @param  {[int]} total [文件大小]
 * @param  {[int]} n     [total参数的原始单位如果为Byte,则n设为1,如果为kb,则n设为2,如果为mb,则n设为3,以此类推]
 * @return {[string]}       [带单位的文件大小的字符串]
 */
function fileLengthFormat(total, n) {
    var format;
    var len = total / (1024.0);
    if (len > 1000) {
        return arguments.callee(len, ++n);
    } else {
        switch (n) {
            case 1:
                format = len.toFixed(2) + "KB";
                break;
            case 2:
                format = len.toFixed(2) + "MB";
                break;
            case 3:
                format = len.toFixed(2) + "GB";
                break;
            case 4:
                format = len.toFixed(2) + "TB";
                break;
        }
        return format;
    }
}
1 //假如文件大小为1024byte,想自适应到kb,则如下传参
2 fileLengthFormat(1024,1);//"1.00KB"
3 //假如文件大小为1024kb,想自适应到mb,则如下传参
4 fileLengthFormat(1024,2);//"1.00MB"
5 //测试
6 fileLengthFormat(112233445566,1);//"104.53GB"
  •  java格式化文件大小

public static String getFormatSize(double size) {  
        double kiloByte = size/1024;  
        if(kiloByte < 1) {  
            return size + "Byte(s)";  
        }  
          
        double megaByte = kiloByte/1024;  
        if(megaByte < 1) {  
            BigDecimal result1 = new BigDecimal(Double.toString(kiloByte));  
            return result1.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "KB";  
        }  
          
        double gigaByte = megaByte/1024;  
        if(gigaByte < 1) {  
            BigDecimal result2  = new BigDecimal(Double.toString(megaByte));  
            return result2.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "MB";  
        }  
          
        double teraBytes = gigaByte/1024;  
        if(teraBytes < 1) {  
            BigDecimal result3 = new BigDecimal(Double.toString(gigaByte));  
            return result3.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "GB";  
        }  
        BigDecimal result4 = new BigDecimal(teraBytes);  
        return result4.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "TB";  
    }  

 

<script setup> import { ref, onMounted } from 'vue'; import { onShow } from '@dcloudio/uni-app'; // 存储openID的变量 const openId = ref(''); // 表单数据 const formData = ref({ title: '', category: '', // 存储显示的类别名称 categoryValue: '', // 存储实际提交的类别值 complaineeName: '', company: '', department: '', content: '', name: '', // 可选 phone: '', // 可选 email: '' // 可选 }); // 错误信息 const errors = ref({}); // 文件列表 - 存储上传成功后的文件信息 const files = ref([]); // 上传状态管理 const uploading = ref(false); const uploadProgress = ref(0); const uploadDisabled = ref(false); // 处理状态(用于获取OpenID提交过程中) const processing = ref(false); // 类别选择(包含显示文本对应的值) const categories = ref([ { label: '财务违规', value: '1' }, { label: '道德问题', value: '2' }, { label: '职场骚扰', value: '3' }, { label: '安全隐患', value: '4' } ]); const categoryIndex = ref(-1); // 欢迎页状态 const showWelcome = ref(true); const countdown = ref(5); // 处理接受按钮点击 const handleAccept = () => { if (countdown.value > 0) return; showWelcome.value = false; }; // 选择类别 - 同时记录显示文本实际值 const bindPickerChange = (e) => { categoryIndex.value = e.detail.value; const selected = categories.value[e.detail.value]; formData.value.category = selected.label; // 显示用 formData.value.categoryValue = selected.value; // 实际提交用 validateField('category'); // 选择后立即验证 }; // 选择并上传文件 const chooseAndUploadFile = async () => { if (uploading.value) return; try { // 选择文件 const res = await uni.chooseFile({ count: 5 - files.value.length, // 限制总文件数不超过5个 type: 'all', extension: ['png', 'jpg', 'jpeg', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf', 'mp3', 'mp4'], }); if (res.tempFiles && res.tempFiles.length > 0) { // 开始上传 uploadFiles(res.tempFiles); } } catch (err) { console.log('选择文件失败', err); uni.showToast({ title: '选择文件失败', icon: 'error', duration: 2000 }); } }; // 上传文件到服务器 const uploadFiles = async (tempFiles) => { uploading.value = true; uploadDisabled.value = true; uploadProgress.value = 0; // 显示加载弹窗(小圆圈) uni.showLoading({ title: '正在上传...', mask: true // 防止背景点击 }); try { // 计算总文件大小 const totalSize = tempFiles.reduce((sum, file) => sum + file.size, 0); let uploadedSize = 0; // 逐个上传文件 for (let i = 0; i < tempFiles.length; i++) { const file = tempFiles[i]; await new Promise((resolve, reject) => { uni.uploadFile({ url: '/api/file/upload', filePath: file.path, name: 'file', header: {}, timeout: 360000, // 1分钟超时 // 移除进度更新逻辑(不再需要) success: (res) => { try { const response = JSON.parse(res.data); if (res.statusCode === 200 && response.code === 200 && response.data) { uploadedSize += file.size; files.value.push({ name: response.data.name, size: file.size, url: response.data.url, id: Date.now() + i }); resolve(); } else { throw new Error(response.msg || `文件上传失败,错误代码: ${response.code}`); } } catch (err) { reject(err); } }, fail: (err) => { reject(new Error(`上传失败: ${err.errMsg}`)); } }); }); } // 文件上传成功后提示 uni.showToast({ title: '文件上传成功', icon: 'success' }); } catch (err) { console.error('文件上传失败', err); uni.showToast({ title: err.message || '文件上传失败', icon: 'error' }); } finally { // 关闭加载弹窗 uni.hideLoading(); uploading.value = false; uploadDisabled.value = false; uploadProgress.value = 0; } }; // 移除文件 const removeFile = (index) => { files.value.splice(index, 1); }; // 格式化文件大小 const formatFileSize = (bytes) => { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }; // 单个字段验证 const validateField = (field) => { const value = formData.value[field]; let error = ''; switch(field) { case 'title': if (!value) error = '请输入投诉标题'; else if (value.length > 100) error = '标题长度不能超过100个字符'; break; case 'category': if (!value) error = '请选择投诉类别'; break; case 'complaineeName': if (!value) error = '请输入被投诉人姓名'; else if (value.length > 50) error = '姓名长度不能超过50个字符'; break; case 'company': if (!value) error = '请输入被投诉人公司'; else if (value.length > 100) error = '公司名称长度不能超过100个字符'; break; case 'department': if (!value) error = '请输入被投诉人部门'; else if (value.length > 100) error = '部门名称长度不能超过100个字符'; break; case 'content': if (!value) error = '请输入投诉内容'; else if (value.length < 1) error = '投诉内容不能为空'; break; case 'name': if (value && value.length > 50) error = '姓名长度不能超过50个字符'; break; case 'phone': const phoneRegex = /^1[3-9]\d{9}$/; if (value && value.trim() !== '' && !phoneRegex.test(value)) { error = '请输入有效的手机号码'; } break; case 'email': const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (value && value.trim() !== '' && !emailRegex.test(value)) { error = '请输入有效的邮箱地址'; } break; } errors.value[field] = error; return !error; }; // 表单验证 - 验证所有必填项格式 const validateForm = () => { errors.value = {}; let isValid = true; // 验证所有字段 const allFields = ['title', 'category', 'complaineeName', 'company', 'department', 'content', 'name', 'phone', 'email']; allFields.forEach(field => { if (!validateField(field)) { isValid = false; } }); return isValid; }; // 提交表单 const submitForm = async () => { if (uploading.value || processing.value) { uni.showToast({ title: '操作处理中,请稍后', icon: 'none', duration: 2000 }); return; } // 先验证表单 const isValid = validateForm(); if (!isValid) { // 滚动到第一个错误字段 const firstErrorField = Object.keys(errors.value).find(key => errors.value[key]); if (firstErrorField) { const element = document.getElementById(firstErrorField); if (element) { element.scrollIntoView({ behavior: 'smooth', block: 'center' }); // 给错误字段添加闪烁效果 element.classList.add('error-highlight'); setTimeout(() => element.classList.remove('error-highlight'), 1000); } } const firstError = Object.values(errors.value).find(msg => msg); if (firstError) { uni.showToast({ title: firstError, icon: 'none', duration: 2000 }); } return; } processing.value = true; try { // 从本地缓存获取OpenID const openId = uni.getStorageSync('wechat_openid') || ''; // 准备提交的数据 const submitData = { title: formData.value.title, complaintType: formData.value.categoryValue, bcomplaintName: formData.value.complaineeName, bcomplaintCompany: formData.value.company, bcomplaintDept: formData.value.department, bcomplaintContent: formData.value.content, processingStatus: "1", fileUrl: files.value.map(file => file.url).join(','), complaintPerson: formData.value.name || '', phoneNumber: formData.value.phone || '', email: formData.value.email || '', wxUserId: openId // 将openID传入wxUserId字段 }; uni.showLoading({ title: '提交中...', mask: true }); // 提交表单数据到接口 await new Promise((resolve, reject) => { uni.request({ url: '/api/incorruptFront/addComplainTs', method: 'POST', data: submitData, header: { 'Content-Type': 'application/json' }, success: (res) => { if (res.statusCode === 200 && res.data.code === 200) { resolve(res.data); } else { reject(new Error(res.data.msg || '提交失败')); } }, fail: (err) => { reject(new Error(`网络错误: ${err.errMsg}`)); } }); }); uni.hideLoading(); uni.showToast({ title: '投诉信息已提交', icon: 'success', duration: 2000 }); // 2秒后返回首页 setTimeout(() => { uni.reLaunch({ url: '/pages/index/index' }); }, 2000); } catch (error) { console.error('提交表单失败', error); uni.hideLoading(); uni.showToast({ title: error.message || '提交失败,请重试', icon: 'error', duration: 2000 }); } finally { processing.value = false; } }; // 页面加载 onMounted(() => { // 启动倒计时 const timer = setInterval(() => { if (countdown.value > 0) { countdown.value--; } else { clearInterval(timer); } }, 1000); // 从缓存获取openID openId.value = uni.getStorageSync('wechat_openid') || ''; }); </script> 帮我修改代码,或者检查一下是不是nginx配置错了:#user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; # 核心:设置最大请求体大小(至少300M,建议设为1000M留有余地) client_max_body_size 1000m; # 超时设置(大文件需要更长时间) client_body_timeout 3600s; # 客户端发送请求体超时(10分钟) client_header_timeout 3600s; # 读取请求头超时 send_timeout 3600s; # 缓冲区设置 client_body_buffer_size 5M; # 超过此大小会写入临时文件 # 临时文件存储配置(确保有足够空间权限) client_body_temp_path C:/nginx-1.26.3/client_body 1 2; proxy_temp_path C:/nginx-1.26.3/proxy_temp 1 2; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; # HTTPS server server { listen 80; server_name www.wyqg.top; # 关键:设置允许的最大请求体大小(1000M,可根据需要调整) client_max_body_size 1000m; # 代理超时设置(针对后端服务) proxy_connect_timeout 120s; proxy_read_timeout 3600s; # 1800秒 = 30分钟(关键设置) proxy_send_timeout 3600s; # 关键规则:优先匹配/incorruptFront/wechat路径 # 放在最前面以确保最高优先级 location ^~ /incorruptFront/wechat { # 核心调试标识:只要匹配到该规则,就会返回这个响应头 add_header X-Rule-Matched "incorruptFront-wechat" always; proxy_pass http://218.31.203.46:8081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; add_header Access-Control-Allow-Headers 'Content-Type, Authorization'; add_header Cache-Control "no-store, no-cache, must-revalidate"; } # /api路径转发规则 location /api { rewrite ^/api/(.*)$ /$1 break; proxy_pass http://218.31.203.46:8081; #关键:禁用缓冲,启用流式传输(大文件必加) proxy_buffering off; # 不缓冲后端响应 proxy_request_buffering off; # 不缓冲客户端请求(直接转发) proxy_http_version 1.1; # 启用HTTP/1.1长连接 proxy_set_header Connection ""; # 禁用连接复用,避免超时断开 proxy_set_header Transfer-Encoding chunked; # 支持分块传输 # 延长上传超时时间(核心修改) proxy_connect_timeout 120s; proxy_read_timeout 3600s; # 延长至1小时 proxy_send_timeout 3600s; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; add_header Access-Control-Allow-Headers 'Content-Type, Authorization, Content-Length, X-Requested-With'; } # 静态资源缓存配置 location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg)$ { root dist_FWH/build/web; expires 1d; # 静态资源缓存1天 add_header Cache-Control "public, max-age=86400"; } # 前端静态资源(默认规则,放在最后) location / { root dist_FWH/build/web; index index.html; try_files $uri $uri/ /index.html; } } #后台管理项目 server { listen 8080 ; server_name www.wyqg.top; # 替换为您的域名或服务器IP root dist_HTGL; # 应该是目录,不是文件 # 关键:设置允许的最大请求体大小(1000M,可根据需要调整) client_max_body_size 500m; # 启用 Gzip 静态文件支持 gzip_static on; gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; # 路由 history 模式支持 location / { index index.html; try_files $uri $uri/ /index.html; # 缓存配置 if ($request_filename ~* .*\.(?:js|css|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm)$) { expires 365d; add_header Cache-Control "public, immutable"; } } # 8080端口server块中必须包含这段配置 location /statics/ { proxy_pass http://218.31.203.46:9300; # 关键:指向图片服务器IP+端口 proxy_set_header Host $proxy_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 静态资源目录 location /static/ { alias dist_HTGL/static/; expires max; add_header Cache-Control "public, immutable"; access_log off; } # API 代理配置 location /prod-api/ { proxy_pass http://218.31.203.46:8081/; # 请求头处理 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # WebSocket 支持 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # 超时设置 proxy_connect_timeout 60s; proxy_read_timeout 600s; proxy_send_timeout 600s; } # 禁止访问敏感文件 location ~ /\.(env|git) { deny all; return 403; } } }
最新发布
09-02
<template> <view class="container"> <view class="header"> <text class="header-title">在线投诉举报平台</text> <text class="header-subtitle">诚信是企业生存发展的根本</text> </view> <!-- 欢迎页面 --> <view v-if="showWelcome" class="welcome-section"> <view class="welcome-image"> <image src="/static/OnlinComplaint.jpg" mode=""></image> </view> <view class="welcome-content"> <text class="welcome-text">欢迎举报</text> <text class="welcome-text indent">诚信是做人做事的根本,同样诚信的企业才能生存发展。其亚的任何人,无论职务的高低,都应谨守廉洁并自觉接受各方监督。</text> <text class="welcome-text indent">任何单位个人都有有权举报其亚员工的违法违纪行为。同时,我们将对您提供的信息严格保密,一经查实,将根据公司制度给予相应奖励。</text> <text class="welcome-text indent">您可以选择匿名举报,无需填写个人联系方式。</text> </view> <view class="accept-box" :class="{disabled: countdown > 0}" @tap="handleAccept" > <text>我已阅读并理解上述内容</text> <view class="timer"> {{ countdown }}s </view> </view> </view> <!-- 表单页面 --> <view v-else class="form-section"> <view class="section-title"> <uni-icons type="compose" size="24" color="#3498db"></uni-icons> <text>投诉信息</text> </view> <view class="form-row"> <view class="form-col"> <view class="form-group"> <label for="title" class="required">投诉标题</label> <input type="text" id="title" placeholder="请输入投诉标题" v-model="formData.title" :class="{error: errors.title}" @blur="validateField('title')" > <text v-if="errors.title" class="error-text">{{ errors.title }}</text> </view> </view> <view class="form-col"> <view class="form-group"> <label for="category" class="required">类别</label> <picker @change="bindPickerChange" :value="categoryIndex" :range="categories.map(item => item.label)" class="picker" @blur="validateField('category')" > <view class="picker-content" :class="{placeholder: !formData.category, error: errors.category}"> {{ formData.category || '请选择投诉类别' }} </view> </picker> <text v-if="errors.category" class="error-text">{{ errors.category }}</text> </view> </view> </view> <view class="form-row"> <view class="form-col"> <view class="form-group"> <label for="complaineeName" class="required">被投诉人姓名</label> <input type="text" id="complaineeName" placeholder="请输入被投诉人姓名" v-model="formData.complaineeName" :class="{error: errors.complaineeName}" @blur="validateField('complaineeName')" > <text v-if="errors.complaineeName" class="error-text">{{ errors.complaineeName }}</text> </view> </view> <view class="form-col"> <view class="form-group"> <label for="company" class="required">被投诉人公司</label> <input type="text" id="company" placeholder="请输入公司名称" v-model="formData.company" :class="{error: errors.company}" @blur="validateField('company')" > <text v-if="errors.company" class="error-text">{{ errors.company }}</text> </view> </view> </view> <view class="form-row"> <view class="form-col"> <view class="form-group"> <label for="department" class="required">被投诉人部门</label> <input type="text" id="department" placeholder="请输入部门名称" v-model="formData.department" :class="{error: errors.department}" @blur="validateField('department')" > <text v-if="errors.department" class="error-text">{{ errors.department }}</text> </view> </view> </view> <view class="form-group"> <label for="content" class="required">投诉内容</label> <textarea id="content" placeholder="请您如实详细填写投诉内容(时间、地点、事件、经过)" v-model="formData.content" :class="{error: errors.content}" @blur="validateField('content')" ></textarea> <view class="content-counter"> <text>{{ formData.content.length }}/2000</text> </view> <text v-if="errors.content" class="error-text">{{ errors.content }}</text> </view> <view class="section-title"> <uni-icons type="paperclip" size="24" color="#3498db"></uni-icons> <text>上传附件</text> </view> <view class="upload-area" @tap="chooseAndUploadFile" :class="{disabled: uploadDisabled}"> <view class="upload-icon"> <uni-icons type="cloud-upload" size="48" color="#3498db"></uni-icons> </view> <view class="upload-text">点击上传到此处</view> <view class="file-types">支持word文档、照片、MP3、MP4等格式,每次上传文件大小不超过10M,附件过大请发投诉渠道邮箱</view> <view v-if="uploading" class="upload-progress"> <uni-progress :percent="uploadProgress" stroke-width="48" activeColor="#3498db"></uni-progress> </view> </view> <view v-if="files.length > 0" class="file-list"> <view v-for="(file, index) in files" :key="index" class="file-item"> <uni-icons type="paperclip" size="18" color="#3498db"></uni-icons> <view class="file-info"> <text class="file-name">{{ file.name }}</text> <text class="file-size">({{ formatFileSize(file.size) }})</text> </view> <view class="delete-btn" @tap="removeFile(index)" >X <uni-icons type="close" size="16" color="#ffffff" ></uni-icons> </view> </view> </view> <view class="section-title"> <uni-icons type="person" size="24" color="#3498db"></uni-icons> <text>联系方式(选填,支持匿名)</text> </view> <view class="form-row"> <view class="form-col"> <view class="form-group"> <label for="name">姓名(选填)</label> <input type="text" id="name" placeholder="请输入您的姓名(选填)" v-model="formData.name" :class="{error: errors.name}" @blur="validateField('name')" > <text v-if="errors.name" class="error-text">{{ errors.name }}</text> </view> </view> <view class="form-col"> <view class="form-group"> <label for="phone">手机号(选填)</label> <input type="number" id="phone" placeholder="请输入您的手机号(选填)" v-model="formData.phone" :class="{error: errors.phone}" @blur="validateField('phone')" > <text v-if="errors.phone" class="error-text">{{ errors.phone }}</text> </view> </view> </view> <view class="form-row"> <view class="form-col"> <view class="form-group"> <label for="email">邮箱(选填)</label> <input type="email" id="email" placeholder="请输入您的邮箱(选填)" v-model="formData.email" :class="{error: errors.email}" @blur="validateField('email')" > <text v-if="errors.email" class="error-text">{{ errors.email }}</text> </view> </view> </view> <view class="privacy-note"> 您可以选择匿名举报,无需填写个人联系方式。如填写联系方式,我们会对您的个人信息及投诉内容进行严格保密,便于后续跟进调查。 </view> <button class="submit-btn" @tap="submitForm" :disabled="uploading || processing"> <template v-if="processing">处理中...</template> <template v-else>提交投诉</template> </button> </view> </view> </template> <script setup> import { ref, onMounted } from 'vue'; import { onShow } from '@dcloudio/uni-app'; // 存储openID的变量 const openId = ref(''); // 表单数据 const formData = ref({ title: '', category: '', // 存储显示的类别名称 categoryValue: '', // 存储实际提交的类别值 complaineeName: '', company: '', department: '', content: '', name: '', // 可选 phone: '', // 可选 email: '' // 可选 }); // 错误信息 const errors = ref({}); // 文件列表 - 存储上传成功后的文件信息 const files = ref([]); // 上传状态管理 const uploading = ref(false); const uploadProgress = ref(0); const uploadDisabled = ref(false); // 处理状态(用于获取OpenID提交过程中) const processing = ref(false); // 类别选择(包含显示文本对应的值) const categories = ref([ { label: '财务违规', value: '1' }, { label: '道德问题', value: '2' }, { label: '职场骚扰', value: '3' }, { label: '安全隐患', value: '4' } ]); const categoryIndex = ref(-1); // 欢迎页状态 const showWelcome = ref(true); const countdown = ref(5); // 处理接受按钮点击 const handleAccept = () => { if (countdown.value > 0) return; showWelcome.value = false; }; // 选择类别 - 同时记录显示文本实际值 const bindPickerChange = (e) => { categoryIndex.value = e.detail.value; const selected = categories.value[e.detail.value]; formData.value.category = selected.label; // 显示用 formData.value.categoryValue = selected.value; // 实际提交用 validateField('category'); // 选择后立即验证 }; // 选择并上传文件 const chooseAndUploadFile = async () => { if (uploading.value) return; try { // 选择文件 const res = await uni.chooseFile({ count: 5 - files.value.length, // 限制总文件数不超过5个 type: 'all', extension: ['png', 'jpg', 'jpeg', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf', 'mp3', 'mp4'], }); if (res.tempFiles && res.tempFiles.length > 0) { // 开始上传 uploadFiles(res.tempFiles); } } catch (err) { console.log('选择文件失败', err); uni.showToast({ title: '选择文件失败', icon: 'error', duration: 2000 }); } }; // 上传文件到服务器 const uploadFiles = async (tempFiles) => { uploading.value = true; uploadDisabled.value = true; uploadProgress.value = 0; // 显示加载弹窗(小圆圈) uni.showLoading({ title: '正在上传...', mask: true // 防止背景点击 }); try { // 计算总文件大小 const totalSize = tempFiles.reduce((sum, file) => sum + file.size, 0); let uploadedSize = 0; // 逐个上传文件 for (let i = 0; i < tempFiles.length; i++) { const file = tempFiles[i]; await new Promise((resolve, reject) => { uni.uploadFile({ url: '/api/file/upload', filePath: file.path, name: 'file', header: {}, timeout: 360000, // 1分钟超时 // 移除进度更新逻辑(不再需要) success: (res) => { try { const response = JSON.parse(res.data); if (res.statusCode === 200 && response.code === 200 && response.data) { uploadedSize += file.size; files.value.push({ name: response.data.name, size: file.size, url: response.data.url, id: Date.now() + i }); resolve(); } else { throw new Error(response.msg || `文件上传失败,错误代码: ${response.code}`); } } catch (err) { reject(err); } }, fail: (err) => { reject(new Error(`上传失败: ${err.errMsg}`)); } }); }); } // 文件上传成功后提示 uni.showToast({ title: '文件上传成功', icon: 'success' }); } catch (err) { console.error('文件上传失败', err); uni.showToast({ title: err.message || '文件上传失败', icon: 'error' }); } finally { // 关闭加载弹窗 uni.hideLoading(); uploading.value = false; uploadDisabled.value = false; uploadProgress.value = 0; } }; // 移除文件 const removeFile = (index) => { files.value.splice(index, 1); }; // 格式化文件大小 const formatFileSize = (bytes) => { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }; // 单个字段验证 const validateField = (field) => { const value = formData.value[field]; let error = ''; switch(field) { case 'title': if (!value) error = '请输入投诉标题'; else if (value.length > 100) error = '标题长度不能超过100个字符'; break; case 'category': if (!value) error = '请选择投诉类别'; break; case 'complaineeName': if (!value) error = '请输入被投诉人姓名'; else if (value.length > 50) error = '姓名长度不能超过50个字符'; break; case 'company': if (!value) error = '请输入被投诉人公司'; else if (value.length > 100) error = '公司名称长度不能超过100个字符'; break; case 'department': if (!value) error = '请输入被投诉人部门'; else if (value.length > 100) error = '部门名称长度不能超过100个字符'; break; case 'content': if (!value) error = '请输入投诉内容'; else if (value.length < 1) error = '投诉内容不能为空'; break; case 'name': if (value && value.length > 50) error = '姓名长度不能超过50个字符'; break; case 'phone': const phoneRegex = /^1[3-9]\d{9}$/; if (value && value.trim() !== '' && !phoneRegex.test(value)) { error = '请输入有效的手机号码'; } break; case 'email': const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (value && value.trim() !== '' && !emailRegex.test(value)) { error = '请输入有效的邮箱地址'; } break; } errors.value[field] = error; return !error; }; // 表单验证 - 验证所有必填项格式 const validateForm = () => { errors.value = {}; let isValid = true; // 验证所有字段 const allFields = ['title', 'category', 'complaineeName', 'company', 'department', 'content', 'name', 'phone', 'email']; allFields.forEach(field => { if (!validateField(field)) { isValid = false; } }); return isValid; }; // 提交表单 const submitForm = async () => { if (uploading.value || processing.value) { uni.showToast({ title: '操作处理中,请稍后', icon: 'none', duration: 2000 }); return; } // 先验证表单 const isValid = validateForm(); if (!isValid) { // 滚动到第一个错误字段 const firstErrorField = Object.keys(errors.value).find(key => errors.value[key]); if (firstErrorField) { const element = document.getElementById(firstErrorField); if (element) { element.scrollIntoView({ behavior: 'smooth', block: 'center' }); // 给错误字段添加闪烁效果 element.classList.add('error-highlight'); setTimeout(() => element.classList.remove('error-highlight'), 1000); } } const firstError = Object.values(errors.value).find(msg => msg); if (firstError) { uni.showToast({ title: firstError, icon: 'none', duration: 2000 }); } return; } processing.value = true; try { // 从本地缓存获取OpenID const openId = uni.getStorageSync('wechat_openid') || ''; // 准备提交的数据 const submitData = { title: formData.value.title, complaintType: formData.value.categoryValue, bcomplaintName: formData.value.complaineeName, bcomplaintCompany: formData.value.company, bcomplaintDept: formData.value.department, bcomplaintContent: formData.value.content, processingStatus: "1", fileUrl: files.value.map(file => file.url).join(','), complaintPerson: formData.value.name || '', phoneNumber: formData.value.phone || '', email: formData.value.email || '', wxUserId: openId // 将openID传入wxUserId字段 }; uni.showLoading({ title: '提交中...', mask: true }); // 提交表单数据到接口 await new Promise((resolve, reject) => { uni.request({ url: '/api/incorruptFront/addComplainTs', method: 'POST', data: submitData, header: { 'Content-Type': 'application/json' }, success: (res) => { if (res.statusCode === 200 && res.data.code === 200) { resolve(res.data); } else { reject(new Error(res.data.msg || '提交失败')); } }, fail: (err) => { reject(new Error(`网络错误: ${err.errMsg}`)); } }); }); uni.hideLoading(); uni.showToast({ title: '投诉信息已提交', icon: 'success', duration: 2000 }); // 2秒后返回首页 setTimeout(() => { uni.reLaunch({ url: '/pages/index/index' }); }, 2000); } catch (error) { console.error('提交表单失败', error); uni.hideLoading(); uni.showToast({ title: error.message || '提交失败,请重试', icon: 'error', duration: 2000 }); } finally { processing.value = false; } }; // 页面加载 onMounted(() => { // 启动倒计时 const timer = setInterval(() => { if (countdown.value > 0) { countdown.value--; } else { clearInterval(timer); } }, 1000); // 从缓存获取openID openId.value = uni.getStorageSync('wechat_openid') || ''; }); </script> <style lang="scss"> /* 基础样式保持不变 */ .container { display: flex; flex-direction: column; min-height: 100vh; background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); padding: 20rpx; } .header { background: linear-gradient(135deg, #3498db 0%, #2c3e50 100%); color: white; text-align: center; padding: 40rpx 20rpx; border-radius: 0 0 30rpx 30rpx; margin-bottom: 20rpx; box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.15); } .header-title { display: block; font-size: 40rpx; font-weight: bold; margin-bottom: 15rpx; letter-spacing: 1rpx; } .header-subtitle { font-size: 28rpx; opacity: 0.9; } .welcome-section { padding: 30rpx; transition: all 0.5s ease; } .welcome-image { width: 100%; height: 300rpx; border-radius: 20rpx; overflow: hidden; box-shadow: 0 8rpx 25rpx rgba(0, 0, 0, 0.1); display: flex; justify-content: center; align-items: center; } .welcome-image image { width: 100%; height: 100%; object-fit: cover; } .welcome-content { background-color: #f8f9fa; border-radius: 20rpx; padding: 30rpx; line-height: 1.8; font-size: 28rpx; color: #333; box-shadow: inset 0 0 10rpx rgba(0, 0, 0, 0.05); margin-bottom: 30rpx; } .welcome-text { display: block; margin-bottom: 20rpx; } .accept-box { display: flex; justify-content: center; align-items: center; padding: 25rpx; background-color: #e3f2fd; border-radius: 20rpx; cursor: pointer; transition: all 0.3s ease; border: 2rpx solid #bbdefb; box-shadow: 0 4rpx 15rpx rgba(0, 0, 0, 0.1); } .accept-box.disabled { background-color: #f5f5f5; cursor: not-allowed; opacity: 0.7; } .accept-box .timer { background-color: #e74c3c; color: white; width: 70rpx; height: 70rpx; border-radius: 50%; display: flex; justify-content: center; align-items: center; margin-left: 20rpx; font-weight: bold; font-size: 30rpx; } .form-section { padding: 30rpx; } .section-title { font-size: 34rpx; color: #2c3e50; margin-bottom: 30rpx; padding-bottom: 15rpx; border-bottom: 2rpx solid #3498db; display: flex; align-items: center; text { margin-left: 15rpx; font-weight: bold; } } .form-group { margin-bottom: 35rpx; position: relative; } .form-row { &.full-width { display: flex; flex-wrap: wrap; margin: 0 -15rpx; margin-bottom: 40rpx; } } .form-col { flex: 1; padding: 0 15rpx; min-width: 300rpx; } label { display: block; margin-bottom: 15rpx; font-weight: 500; color: #2c3e50; font-size: 30rpx; } label.required::after { content: "*"; color: #e74c3c; margin-left: 8rpx; } input, select, textarea, .picker-content { width: 100%; padding: 25rpx; border: 2rpx solid #e0e0e0; border-radius: 15rpx; font-size: 28rpx; transition: all 0.3s ease; background-color: #fff; } input:focus, select:focus, textarea:focus, .picker-content:active { border-color: #3498db; outline: none; box-shadow: 0 0 0 3rpx rgba(52, 152, 219, 0.2); } textarea { min-height: 200rpx; resize: vertical; } /* 新增的内容计数器样式 */ .content-counter { text-align: right; font-size: 24rpx; color: #999; margin-top: 10rpx; } .picker-content { display: flex; align-items: center; min-height: 80rpx; /* 确保选择器高度与输入框一致 */ &.placeholder { color: #999; } } .upload-area { border: 2rpx dashed #3498db; border-radius: 20rpx; padding: 40rpx 30rpx; text-align: center; background-color: #e3f2fd; cursor: pointer; transition: all 0.3s ease; margin-bottom: 30rpx; } .upload-area.disabled { background-color: #f5f5f5; border-color: #bdc3c7; cursor: not-allowed; } .upload-area:active:not(.disabled) { background-color: #bbdefb; transform: scale(0.98); } .upload-icon { margin-bottom: 20rpx; } .upload-text { color: #2c3e50; margin-bottom: 20rpx; font-size: 30rpx; font-weight: 500; } .file-types { color: #7f8c8d; font-size: 26rpx; margin-bottom: 20rpx; } .upload-progress { width: 80%; margin: 20rpx auto 0; } .progress-text { display: block; margin-top: 10rpx; font-size: 26rpx; color: #3498db; } .file-list { margin-top: 20rpx; margin-bottom: 30rpx; } .file-item { display: flex; align-items: center; padding: 20rpx; background-color: #f8f9fa; border-radius: 15rpx; margin-bottom: 15rpx; border-left: 4rpx solid #3498db; } /* 优化文件信息显示 */ .file-info { flex: 1; margin: 0 15rpx; min-width: 0; /* 允许容器收缩 */ } .file-name { display: block; font-size: 28rpx; color: #2c3e50; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; /* 不换行,超出部分显示省略号 */ } .file-size { display: block; color: #7f8c8d; font-size: 24rpx; margin-top: 5rpx; } .delete-btn { width: 40rpx; height: 40rpx; border-radius: 50%; background-color: #e74c3c; display: flex; justify-content: center; align-items: center; margin-left: 10rpx; flex-shrink: 0; /* 确保按钮不被挤压 */ transition: all 0.2s ease; color: #ffffff; } .delete-btn:active { background-color: #c0392b; transform: scale(0.9); } .error-text { display: block; color: #e74c3c; font-size: 26rpx; margin-top: 10rpx; } input.error, textarea.error, .picker-content.error { border-color: #e74c3c; animation: shake 0.5s ease; } /* 错误字段高亮动画 */ .error-highlight { animation: highlight 1s ease; } @keyframes shake { 0%, 100% { transform: translateX(0); } 20%, 60% { transform: translateX(-5rpx); } 40%, 80% { transform: translateX(5rpx); } } @keyframes highlight { 0%, 100% { box-shadow: none; } 50% { box-shadow: 0 0 0 4rpx rgba(231, 76, 60, 0.3); } } .privacy-note { background-color: #f8f9fa; border-radius: 15rpx; padding: 25rpx; margin-top: 30rpx; font-size: 26rpx; color: #7f8c8d; text-align: center; border: 1rpx solid #e9ecef; line-height: 1.6; } .submit-btn { background: linear-gradient(to right, #3498db, #2980b9); color: white; border: none; padding: 25rpx 0; width: 100%; border-radius: 50rpx; font-size: 32rpx; font-weight: 600; margin-top: 30rpx; box-shadow: 0 5rpx 20rpx rgba(52, 152, 219, 0.4); transition: all 0.3s ease; } .submit-btn:disabled { background: #bdc3c7; box-shadow: none; opacity: 0.7; } .submit-btn:active:not(:disabled) { transform: translateY(-3rpx); box-shadow: 0 8rpx 25rpx rgba(52, 152, 219, 0.6); } /* 响应式设计 */ @media (min-width: 768px) { .container { max-width: 750px; margin: 0 auto; padding: 40rpx; } .welcome-image { height: 350rpx; } } .welcome-text.indent{ text-indent: 2em; } </style> 上传附件报错:Request URL: http://www.wyqg.top/api/file/upload Request Method: POST Status Code: 400 Bad Request Remote Address: 47.109.65.73:80 Referrer Policy: strict-origin-when-cross-origin Connection: keep-alive Content-Length: 0 Date: Mon, 01 Sep 2025 10:00:25 GMT Server: nginx/1.26.3 Accept: */* Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Connection: keep-alive Content-Length: 10485948 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryGFsvh0KKYAXIXI27 Host: www.wyqg.top Origin: http://www.wyqg.top Referer: http://www.wyqg.top/pages/OnlinComplaint/OnlinComplaint?openid=ohhFX6TzyGvbXFuIL0ZPjEIpsJOo User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1 wechatdevtools/1.06.2504030 MicroMessenger/8.0.5 Language/zh_CN webview/17566896873445375 webdebugger port/57256 token/84cac3393fe84eaa19bae7107a7c7659 file: (binary) 但是我用postman测试接口,用post方法 Body选择 form-data,键file是文件,值上传了一个MP4文件测试没问题
09-02
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值