<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;
}
}
}
最新发布