修改小程序及大屏
This commit is contained in:
@@ -146,18 +146,20 @@
|
||||
</div>
|
||||
<div class="view-stats" ref="viewStatsRef">
|
||||
<div class="stats-header">
|
||||
<span>排名</span>
|
||||
<span>序号</span>
|
||||
<span></span>
|
||||
<span>养殖场名称</span>
|
||||
<span>牛只数量</span>
|
||||
<span>人员数量</span>
|
||||
<span>联系人</span>
|
||||
<span>联系电话</span>
|
||||
</div>
|
||||
<div class="stats-content">
|
||||
<div class="view-item" v-for="item in viewStats" :key="item.rank">
|
||||
<div class="rank-badge" :class="`rank-${item.rank}`">{{ item.rank }}</div>
|
||||
<span></span>
|
||||
<div class="view-info">
|
||||
<div class="view-name">{{ item.name }}</div>
|
||||
<div class="view-count">{{ item.count }}</div>
|
||||
<div class="view-users" >{{ item.users }}</div>
|
||||
<div class="view-users" >{{ formatPhoneNumber(item.users) }}</div>
|
||||
<!-- <div class="view-percentage">{{ item.percentage }}</div> -->
|
||||
</div>
|
||||
</div>
|
||||
@@ -254,6 +256,16 @@ export default {
|
||||
setup() {
|
||||
const deviceChart = ref(null)
|
||||
|
||||
// 电话号码格式化函数 - 将中间四位替换为*号
|
||||
const formatPhoneNumber = (phone) => {
|
||||
if (!phone) return ''
|
||||
const phoneStr = phone.toString()
|
||||
if (phoneStr.length === 11) {
|
||||
return phoneStr.substring(0, 3) + '****' + phoneStr.substring(7)
|
||||
}
|
||||
return phoneStr
|
||||
}
|
||||
|
||||
// 设备统计数据
|
||||
const deviceData = ref({
|
||||
farms: ['养殖场A', '养殖场B', '养殖场C', '养殖场D', '养殖场E', '养殖场F'],
|
||||
@@ -410,7 +422,7 @@ export default {
|
||||
const temperatureAlerts = ref([
|
||||
{
|
||||
id: 1,
|
||||
time: '2025.01.16 14:30:00',
|
||||
time: '2025.10.16 14:30:00',
|
||||
deviceId: 'ET001234',
|
||||
temperature: '39.5°C',
|
||||
status: '高温异常',
|
||||
@@ -418,7 +430,7 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
time: '2025.01.16 14:25:00',
|
||||
time: '2025.10.16 14:25:00',
|
||||
deviceId: 'ET001235',
|
||||
temperature: '35.2°C',
|
||||
status: '低温异常',
|
||||
@@ -426,7 +438,7 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
time: '2025.01.16 14:20:00',
|
||||
time: '2025.10.16 14:20:00',
|
||||
deviceId: 'ET001236',
|
||||
temperature: '40.1°C',
|
||||
status: '高温异常',
|
||||
@@ -434,7 +446,7 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
time: '2025.01.16 14:15:00',
|
||||
time: '2025.10.16 14:15:00',
|
||||
deviceId: 'ET001237',
|
||||
temperature: '34.8°C',
|
||||
status: '低温异常',
|
||||
@@ -442,7 +454,7 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
time: '2025.01.16 14:10:00',
|
||||
time: '2025.10.16 14:10:00',
|
||||
deviceId: 'ET001238',
|
||||
temperature: '39.8°C',
|
||||
status: '高温异常',
|
||||
@@ -450,7 +462,7 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
time: '2025.01.16 14:05:00',
|
||||
time: '2025.10.16 14:05:00',
|
||||
deviceId: 'ET001239',
|
||||
temperature: '35.0°C',
|
||||
status: '低温异常',
|
||||
@@ -458,7 +470,7 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
time: '2025.01.16 14:00:00',
|
||||
time: '2025.10.16 14:00:00',
|
||||
deviceId: 'ET001240',
|
||||
temperature: '40.3°C',
|
||||
status: '高温异常',
|
||||
@@ -501,7 +513,7 @@ export default {
|
||||
const riskData = ref([
|
||||
{
|
||||
id: 1,
|
||||
time: '2025.05.11 12:00:00',
|
||||
time: '2025.10.11 18:00:00',
|
||||
user: '20655554',
|
||||
unit: '智能耳标',
|
||||
file: '佩戴异常',
|
||||
@@ -511,7 +523,7 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
time: '2025.04.11 16:00:00',
|
||||
time: '2025.10.11 12:38:00',
|
||||
user: '20655556',
|
||||
unit: '智能耳标',
|
||||
file: '佩戴异常',
|
||||
@@ -521,7 +533,7 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
time: '2025.05.16 12:00:00',
|
||||
time: '2025.10.16 12:00:00',
|
||||
user: '20655557',
|
||||
unit: '智能项圈',
|
||||
file: '低电量',
|
||||
@@ -531,7 +543,7 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
time: '2025.05.16 12:00:00',
|
||||
time: '2025.10.16 08:55:00',
|
||||
user: '20655558',
|
||||
unit: '智能项圈',
|
||||
file: '设备离线',
|
||||
@@ -541,7 +553,7 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
time: '2025.05.16 12:00:00',
|
||||
time: '2025.10.16 16:40:00',
|
||||
user: '20655559',
|
||||
unit: '智能项圈',
|
||||
file: '设备离线',
|
||||
@@ -551,7 +563,7 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
time: '2025.05.16 12:00:00',
|
||||
time: '2025.10.16 12:22:00',
|
||||
user: '20655559',
|
||||
unit: '智能项圈',
|
||||
file: '设备离线',
|
||||
@@ -561,7 +573,7 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
time: '2025.05.16 12:00:00',
|
||||
time: '2025.10.16 06:30:00',
|
||||
user: '20655559',
|
||||
unit: '智能项圈',
|
||||
file: '设备离线',
|
||||
@@ -571,7 +583,7 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
time: '2025.05.16 12:00:00',
|
||||
time: '2025.10.17 11:29:40',
|
||||
user: '20655559',
|
||||
unit: '智能项圈',
|
||||
file: '设备离线',
|
||||
@@ -581,7 +593,7 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
time: '2025.05.16 12:00:00',
|
||||
time: '2025.10.16 10:20:00',
|
||||
user: '20655559',
|
||||
unit: '智能项圈',
|
||||
file: '设备离线',
|
||||
@@ -591,7 +603,7 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
time: '2025.05.11 12:00:00',
|
||||
time: '2025.10.23 14:30:00',
|
||||
user: '20655554',
|
||||
unit: '智能耳标',
|
||||
file: '佩戴异常',
|
||||
@@ -601,7 +613,7 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
time: '2025.05.11 12:00:00',
|
||||
time: '2025.10.11 13:44:00',
|
||||
user: '20655554',
|
||||
unit: '智能耳标',
|
||||
file: '佩戴异常',
|
||||
@@ -923,7 +935,8 @@ export default {
|
||||
livestockChart,
|
||||
viewStatsRef,
|
||||
riskTableRef,
|
||||
temperatureTableRef
|
||||
temperatureTableRef,
|
||||
formatPhoneNumber
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1421,9 +1434,9 @@ export default {
|
||||
|
||||
.stats-header {
|
||||
display: grid;
|
||||
grid-template-columns: 50px 1fr 80px 80px;
|
||||
grid-template-columns: 50px 30px 1fr 80px 80px;
|
||||
gap: 15px;
|
||||
padding: 10px 0;
|
||||
padding: 12px 15px;
|
||||
border-bottom: 1px solid rgba(0, 212, 255, 0.2);
|
||||
color: #ffffff;
|
||||
font-weight: bold;
|
||||
@@ -1451,12 +1464,12 @@ export default {
|
||||
|
||||
.view-item {
|
||||
display: grid;
|
||||
grid-template-columns: 50px 1fr 80px 80px;
|
||||
grid-template-columns: 50px 30px 1fr 80px 80px;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 12px;
|
||||
padding: 5px 10px;
|
||||
padding: 8px 15px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
|
||||
181
government-admin/src/utils/api-redesigned.js
Normal file
181
government-admin/src/utils/api-redesigned.js
Normal file
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* 政府端API请求工具 - 重新设计版本
|
||||
* 参考银行端设计模式,统一API调用方式
|
||||
*/
|
||||
import axios from 'axios'
|
||||
|
||||
// 创建axios实例
|
||||
const instance = axios.create({
|
||||
baseURL: '/api',
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
// 请求拦截器
|
||||
instance.interceptors.request.use(
|
||||
config => {
|
||||
// 添加认证token
|
||||
const token = localStorage.getItem('token')
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
return config
|
||||
},
|
||||
error => {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// 响应拦截器
|
||||
instance.interceptors.response.use(
|
||||
response => {
|
||||
return response.data
|
||||
},
|
||||
error => {
|
||||
if (error.response?.status === 401) {
|
||||
// 清除token并跳转到登录页
|
||||
localStorage.removeItem('token')
|
||||
window.location.href = '/login'
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// API模块定义 - 参考银行端设计
|
||||
const api = {
|
||||
// 认证相关API
|
||||
auth: {
|
||||
login: (data) => instance.post('/auth/login', data),
|
||||
logout: () => instance.post('/auth/logout'),
|
||||
getCurrentUser: () => instance.get('/auth/me'),
|
||||
changePassword: (data) => instance.post('/auth/change-password', data)
|
||||
},
|
||||
|
||||
// 仪表盘API - 整合数据览仓、市场行情等
|
||||
dashboard: {
|
||||
// 数据览仓
|
||||
getDataCenter: () => instance.get('/dashboard/data-center'),
|
||||
// 市场行情
|
||||
getMarketPrice: (params) => instance.get('/dashboard/market-price', { params }),
|
||||
// 监管统计
|
||||
getSupervisionStats: () => instance.get('/dashboard/supervision-stats'),
|
||||
// 疫情统计
|
||||
getEpidemicStats: () => instance.get('/dashboard/epidemic-stats'),
|
||||
// 总体概览
|
||||
getOverview: () => instance.get('/dashboard/overview')
|
||||
},
|
||||
|
||||
// 政府管理API
|
||||
government: {
|
||||
// 部门管理
|
||||
departments: {
|
||||
getList: (params) => instance.get('/government/departments', { params }),
|
||||
getById: (id) => instance.get(`/government/departments/${id}`),
|
||||
create: (data) => instance.post('/government/departments', data),
|
||||
update: (id, data) => instance.put(`/government/departments/${id}`, data),
|
||||
delete: (id) => instance.delete(`/government/departments/${id}`)
|
||||
},
|
||||
// 岗位管理
|
||||
positions: {
|
||||
getList: (params) => instance.get('/government/positions', { params }),
|
||||
getById: (id) => instance.get(`/government/positions/${id}`),
|
||||
create: (data) => instance.post('/government/positions', data),
|
||||
update: (id, data) => instance.put(`/government/positions/${id}`, data),
|
||||
delete: (id) => instance.delete(`/government/positions/${id}`),
|
||||
setPermission: (id, data) => instance.post(`/government/positions/${id}/permission`, data)
|
||||
},
|
||||
// 行政人员管理
|
||||
adminStaff: {
|
||||
getList: (params) => instance.get('/government/admin-staff', { params }),
|
||||
getById: (id) => instance.get(`/government/admin-staff/${id}`),
|
||||
create: (data) => instance.post('/government/admin-staff', data),
|
||||
update: (id, data) => instance.put(`/government/admin-staff/${id}`, data),
|
||||
delete: (id) => instance.delete(`/government/admin-staff/${id}`),
|
||||
resetPassword: (id) => instance.post(`/government/admin-staff/${id}/reset-password`)
|
||||
},
|
||||
// 养殖户管理
|
||||
farmers: {
|
||||
getList: (params) => instance.get('/government/farmers', { params }),
|
||||
getById: (id) => instance.get(`/government/farmers/${id}`),
|
||||
create: (data) => instance.post('/government/farmers', data),
|
||||
update: (id, data) => instance.put(`/government/farmers/${id}`, data),
|
||||
delete: (id) => instance.delete(`/government/farmers/${id}`),
|
||||
resetPassword: (id) => instance.post(`/government/farmers/${id}/reset-password`)
|
||||
},
|
||||
// 智能项圈管理
|
||||
collars: {
|
||||
getList: (params) => instance.get('/government/collars', { params }),
|
||||
getById: (id) => instance.get(`/government/collars/${id}`),
|
||||
create: (data) => instance.post('/government/collars', data),
|
||||
update: (id, data) => instance.put(`/government/collars/${id}`, data),
|
||||
delete: (id) => instance.delete(`/government/collars/${id}`)
|
||||
}
|
||||
},
|
||||
|
||||
// 人员管理API
|
||||
personnel: {
|
||||
getList: (params) => instance.get('/personnel', { params }),
|
||||
getById: (id) => instance.get(`/personnel/${id}`),
|
||||
create: (data) => instance.post('/personnel', data),
|
||||
update: (id, data) => instance.put(`/personnel/${id}`, data),
|
||||
delete: (id) => instance.delete(`/personnel/${id}`)
|
||||
},
|
||||
|
||||
// 监管管理API
|
||||
supervision: {
|
||||
getList: (params) => instance.get('/supervision', { params }),
|
||||
getById: (id) => instance.get(`/supervision/${id}`),
|
||||
create: (data) => instance.post('/supervision', data),
|
||||
update: (id, data) => instance.put(`/supervision/${id}`, data),
|
||||
delete: (id) => instance.delete(`/supervision/${id}`),
|
||||
getStats: () => instance.get('/supervision/stats')
|
||||
},
|
||||
|
||||
// 审批管理API
|
||||
approval: {
|
||||
getList: (params) => instance.get('/approval', { params }),
|
||||
getById: (id) => instance.get(`/approval/${id}`),
|
||||
create: (data) => instance.post('/approval', data),
|
||||
update: (id, data) => instance.put(`/approval/${id}`, data),
|
||||
delete: (id) => instance.delete(`/approval/${id}`)
|
||||
},
|
||||
|
||||
// 疫情管理API
|
||||
epidemic: {
|
||||
getList: (params) => instance.get('/epidemic', { params }),
|
||||
getById: (id) => instance.get(`/epidemic/${id}`),
|
||||
create: (data) => instance.post('/epidemic', data),
|
||||
update: (id, data) => instance.put(`/epidemic/${id}`, data),
|
||||
delete: (id) => instance.delete(`/epidemic/${id}`),
|
||||
getStats: () => instance.get('/epidemic/stats')
|
||||
},
|
||||
|
||||
// 服务管理API
|
||||
service: {
|
||||
getList: (params) => instance.get('/service', { params }),
|
||||
getById: (id) => instance.get(`/service/${id}`),
|
||||
create: (data) => instance.post('/service', data),
|
||||
update: (id, data) => instance.put(`/service/${id}`, data),
|
||||
delete: (id) => instance.delete(`/service/${id}`)
|
||||
},
|
||||
|
||||
// 系统管理API
|
||||
system: {
|
||||
getList: (params) => instance.get('/system', { params }),
|
||||
getById: (id) => instance.get(`/system/${id}`),
|
||||
create: (data) => instance.post('/system', data),
|
||||
update: (id, data) => instance.put(`/system/${id}`, data),
|
||||
delete: (id) => instance.delete(`/system/${id}`)
|
||||
},
|
||||
|
||||
// 文件管理API
|
||||
files: {
|
||||
upload: (data) => instance.post('/files/upload', data),
|
||||
download: (id) => instance.get(`/files/${id}/download`),
|
||||
delete: (id) => instance.delete(`/files/${id}`)
|
||||
}
|
||||
}
|
||||
|
||||
export default api
|
||||
@@ -1,133 +0,0 @@
|
||||
# Nginx 404 错误最终修复方案
|
||||
|
||||
## 🔍 问题现状
|
||||
|
||||
根据测试结果:
|
||||
- ✅ 后端服务正常:`http://localhost:5352/api/government/departments` 返回 200
|
||||
- ✅ 登录接口正常:`https://ad.ningmuyun.com/api/government/auth/login` 返回 401
|
||||
- ❌ 正确路径 404:`https://ad.ningmuyun.com/api/government/departments` 返回 404
|
||||
- ❌ 正确路径 404:`https://ad.ningmuyun.com/api/government/data-center` 返回 404
|
||||
- ❌ 正确路径 404:`https://ad.ningmuyun.com/api/government/market-price` 返回 404
|
||||
- ✅ 重复路径正常:`https://ad.ningmuyun.com/api/government/government/departments` 返回 200
|
||||
|
||||
## 📊 问题分析
|
||||
|
||||
### 关键发现
|
||||
1. 登录接口 `/api/government/auth/login` 正常工作
|
||||
2. 其他政府接口 `/api/government/departments` 等返回 404
|
||||
3. 重复路径 `/api/government/government/departments` 却能正常工作
|
||||
|
||||
### 推断
|
||||
这说明 nginx 配置中:
|
||||
- `location ^~ /api/government/auth/` 规则工作正常
|
||||
- `location ^~ /api/government/` 规则没有生效
|
||||
- 重复路径能工作,说明可能有其他规则在处理请求
|
||||
|
||||
## 🔧 当前 Nginx 配置
|
||||
|
||||
```nginx
|
||||
# 政府认证API代理 - 处理登录等认证接口
|
||||
location ^~ /api/government/auth/ {
|
||||
proxy_pass http://localhost:5352/api/auth/;
|
||||
# ... CORS 配置
|
||||
}
|
||||
|
||||
# 政府API代理 - 处理其他政府相关接口
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352/api/government/;
|
||||
# ... CORS 配置
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 可能的原因和解决方案
|
||||
|
||||
### 方案 1:Nginx 配置未重新加载
|
||||
最可能的原因是 nginx 配置修改后没有重新加载。
|
||||
|
||||
**解决步骤:**
|
||||
```bash
|
||||
# 1. 检查 nginx 配置语法
|
||||
sudo nginx -t
|
||||
|
||||
# 2. 重新加载 nginx 配置
|
||||
sudo systemctl reload nginx
|
||||
|
||||
# 3. 如果 reload 不行,尝试重启
|
||||
sudo systemctl restart nginx
|
||||
|
||||
# 4. 检查 nginx 状态
|
||||
sudo systemctl status nginx
|
||||
|
||||
# 5. 查看 nginx 错误日志
|
||||
sudo tail -f /var/log/nginx/error.log
|
||||
```
|
||||
|
||||
### 方案 2:浏览器缓存问题
|
||||
**解决步骤:**
|
||||
1. 清除浏览器缓存
|
||||
2. 使用无痕模式测试
|
||||
3. 或使用 curl 命令测试(绕过浏览器)
|
||||
|
||||
### 方案 3:Location 规则匹配优先级问题
|
||||
如果还不行,可能需要调整 location 规则的顺序或写法。
|
||||
|
||||
**尝试修改为:**
|
||||
```nginx
|
||||
# 方案 A:使用正则表达式匹配
|
||||
location ~ ^/api/government/(?!auth/) {
|
||||
proxy_pass http://localhost:5352;
|
||||
# ... 其他配置
|
||||
}
|
||||
|
||||
# 方案 B:去掉尾部斜杠
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352;
|
||||
rewrite ^/api/government/(.*)$ /api/government/$1 break;
|
||||
# ... 其他配置
|
||||
}
|
||||
```
|
||||
|
||||
## 📝 验证步骤
|
||||
|
||||
修复后运行以下命令验证:
|
||||
```bash
|
||||
node test-final-fix.js
|
||||
```
|
||||
|
||||
或使用 curl 测试:
|
||||
```bash
|
||||
# 测试部门接口
|
||||
curl -X GET https://ad.ningmuyun.com/api/government/departments
|
||||
|
||||
# 测试数据中心接口
|
||||
curl -X GET https://ad.ningmuyun.com/api/government/data-center
|
||||
|
||||
# 测试市场价格接口
|
||||
curl -X GET "https://ad.ningmuyun.com/api/government/market-price?type=beef"
|
||||
```
|
||||
|
||||
## 🚨 重要提醒
|
||||
|
||||
1. **必须重启 nginx**:修改配置文件后,必须重启或重新加载 nginx 服务
|
||||
2. **清除缓存**:测试时要清除浏览器缓存或使用无痕模式
|
||||
3. **检查日志**:如果问题持续,查看 nginx 错误日志以获取更多信息
|
||||
|
||||
## 📞 下一步行动
|
||||
|
||||
请在服务器上执行以下命令:
|
||||
```bash
|
||||
# 1. 检查配置语法
|
||||
sudo nginx -t
|
||||
|
||||
# 2. 重新加载配置
|
||||
sudo systemctl reload nginx
|
||||
|
||||
# 3. 验证修复效果
|
||||
curl -X GET https://ad.ningmuyun.com/api/government/departments
|
||||
```
|
||||
|
||||
如果执行后问题仍然存在,请提供:
|
||||
1. nginx -t 的输出结果
|
||||
2. nginx error.log 的最新内容
|
||||
3. curl 命令的完整输出
|
||||
|
||||
188
government-backend/ROUTING_MIGRATION_GUIDE.md
Normal file
188
government-backend/ROUTING_MIGRATION_GUIDE.md
Normal file
@@ -0,0 +1,188 @@
|
||||
# 政府端路由设计迁移指南
|
||||
|
||||
## 概述
|
||||
|
||||
本文档说明如何将政府端的路由设计从分散的模块化结构迁移到统一的、与银行端一致的设计模式。
|
||||
|
||||
## 设计原则
|
||||
|
||||
### 银行端设计模式(参考)
|
||||
- 所有API统一使用 `/api/` 前缀
|
||||
- 按功能模块分组:`/api/auth`, `/api/dashboard`, `/api/users` 等
|
||||
- nginx配置简单:只需要一个代理规则
|
||||
- 前端API调用统一:`api.模块名.方法名()`
|
||||
|
||||
### 政府端新设计
|
||||
- 统一使用 `/api/` 前缀
|
||||
- 按功能模块分组,整合相关功能
|
||||
- 简化nginx配置
|
||||
- 统一前端API调用方式
|
||||
|
||||
## 路由结构对比
|
||||
|
||||
### 旧设计(分散式)
|
||||
```
|
||||
/api/auth/...
|
||||
/api/supervision/...
|
||||
/api/epidemic/...
|
||||
/api/government/...
|
||||
/api/personnel/...
|
||||
/api/approval/...
|
||||
/api/service/...
|
||||
/api/visualization/...
|
||||
/api/system/...
|
||||
/api/files/...
|
||||
/api/smart-earmark/...
|
||||
/api/smart-host/...
|
||||
/api/slaughter/...
|
||||
/api/harmless/...
|
||||
/api/harmless-place/...
|
||||
/api/epidemic-record/...
|
||||
/api/vaccine/...
|
||||
/api/epidemic-activity/...
|
||||
/api/production-material-certification/...
|
||||
/api/approval-process/...
|
||||
/api/cattle-academy/...
|
||||
/api/device-warning/...
|
||||
```
|
||||
|
||||
### 新设计(整合式)
|
||||
```
|
||||
/api/auth/... # 认证相关
|
||||
/api/dashboard/... # 仪表盘(整合数据览仓、市场行情、统计等)
|
||||
/api/government/... # 政府管理(部门、岗位、人员、养殖户等)
|
||||
/api/personnel/... # 人员管理
|
||||
/api/supervision/... # 监管管理
|
||||
/api/approval/... # 审批管理
|
||||
/api/epidemic/... # 疫情管理
|
||||
/api/slaughter/... # 屠宰管理
|
||||
/api/smart-earmark/... # 智能硬件管理
|
||||
/api/service/... # 服务管理
|
||||
/api/visualization/... # 可视化数据
|
||||
/api/system/... # 系统管理
|
||||
/api/files/... # 文件管理
|
||||
```
|
||||
|
||||
## 主要变更
|
||||
|
||||
### 1. 后端路由整合
|
||||
|
||||
#### 新增 dashboard 模块
|
||||
将以下功能整合到 `/api/dashboard/`:
|
||||
- 数据览仓:`/api/dashboard/data-center`
|
||||
- 市场行情:`/api/dashboard/market-price`
|
||||
- 监管统计:`/api/dashboard/supervision-stats`
|
||||
- 疫情统计:`/api/dashboard/epidemic-stats`
|
||||
- 总体概览:`/api/dashboard/overview`
|
||||
|
||||
#### 整合 government 模块
|
||||
将以下功能整合到 `/api/government/`:
|
||||
- 部门管理:`/api/government/departments`
|
||||
- 岗位管理:`/api/government/positions`
|
||||
- 行政人员:`/api/government/admin-staff`
|
||||
- 养殖户管理:`/api/government/farmers`
|
||||
- 智能项圈:`/api/government/collars`
|
||||
|
||||
### 2. nginx配置简化
|
||||
|
||||
#### 旧配置(复杂)
|
||||
```nginx
|
||||
location ^~ /api/auth/ { ... }
|
||||
location ^~ /api/supervision/ { ... }
|
||||
location ^~ /api/epidemic/ { ... }
|
||||
location ^~ /api/government/ { ... }
|
||||
location ~ ^/api/(personnel|approval|...)/ { ... }
|
||||
```
|
||||
|
||||
#### 新配置(简单)
|
||||
```nginx
|
||||
location ^~ /api/ {
|
||||
proxy_pass http://localhost:5352/api/;
|
||||
# ... CORS配置
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 前端API调用更新
|
||||
|
||||
#### 旧调用方式
|
||||
```javascript
|
||||
// 分散的API调用
|
||||
axios.get('/api/government/data-center')
|
||||
axios.get('/api/government/market-price')
|
||||
axios.get('/api/supervision/stats')
|
||||
axios.get('/api/epidemic/stats')
|
||||
```
|
||||
|
||||
#### 新调用方式
|
||||
```javascript
|
||||
// 统一的API调用
|
||||
api.dashboard.getDataCenter()
|
||||
api.dashboard.getMarketPrice()
|
||||
api.dashboard.getSupervisionStats()
|
||||
api.dashboard.getEpidemicStats()
|
||||
```
|
||||
|
||||
## 迁移步骤
|
||||
|
||||
### 1. 后端迁移
|
||||
1. 创建新的 `app-redesigned.js` 文件
|
||||
2. 创建 `routes/dashboard.js` 模块
|
||||
3. 更新现有路由文件,确保路径正确
|
||||
4. 测试所有API端点
|
||||
|
||||
### 2. nginx配置更新
|
||||
1. 备份现有nginx配置
|
||||
2. 更新为简化的代理规则
|
||||
3. 测试nginx配置:`nginx -t`
|
||||
4. 重载nginx:`nginx -s reload`
|
||||
|
||||
### 3. 前端迁移
|
||||
1. 创建新的 `api-redesigned.js` 文件
|
||||
2. 更新组件中的API调用
|
||||
3. 测试所有功能模块
|
||||
|
||||
### 4. 测试验证
|
||||
1. 运行 `test-redesigned-routing.js` 脚本
|
||||
2. 检查所有API端点是否正常
|
||||
3. 验证前端功能是否正常
|
||||
|
||||
## 优势
|
||||
|
||||
### 1. 维护性
|
||||
- 统一的API设计模式
|
||||
- 简化的nginx配置
|
||||
- 清晰的功能模块划分
|
||||
|
||||
### 2. 一致性
|
||||
- 与银行端保持一致的设计
|
||||
- 统一的错误处理
|
||||
- 标准化的响应格式
|
||||
|
||||
### 3. 扩展性
|
||||
- 易于添加新功能模块
|
||||
- 统一的认证和权限管理
|
||||
- 标准化的API文档
|
||||
|
||||
### 4. 性能
|
||||
- 简化的nginx匹配规则
|
||||
- 减少配置复杂度
|
||||
- 提高请求处理效率
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **向后兼容**:在迁移过程中保持旧API的兼容性
|
||||
2. **测试充分**:确保所有功能模块都经过充分测试
|
||||
3. **文档更新**:及时更新API文档和用户手册
|
||||
4. **监控告警**:设置监控告警,及时发现和解决问题
|
||||
|
||||
## 回滚计划
|
||||
|
||||
如果新设计出现问题,可以:
|
||||
1. 恢复旧的nginx配置
|
||||
2. 使用旧的app.js文件
|
||||
3. 恢复旧的前端API调用
|
||||
4. 分析问题原因并修复
|
||||
|
||||
## 总结
|
||||
|
||||
新的路由设计使政府端与银行端保持一致,提高了系统的可维护性和一致性。通过整合相关功能模块和简化配置,使整个系统更加清晰和易于管理。
|
||||
124
government-backend/app-redesigned.js
Normal file
124
government-backend/app-redesigned.js
Normal file
@@ -0,0 +1,124 @@
|
||||
require('dotenv').config();
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const bodyParser = require('body-parser');
|
||||
const helmet = require('helmet');
|
||||
const morgan = require('morgan');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
// 创建Express应用
|
||||
const app = express();
|
||||
|
||||
// 中间件
|
||||
app.use(cors());
|
||||
app.use(helmet());
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
|
||||
// 日志配置
|
||||
const accessLogStream = fs.createWriteStream(
|
||||
path.join(__dirname, 'logs', 'access.log'),
|
||||
{ flags: 'a' }
|
||||
);
|
||||
app.use(morgan('combined', { stream: accessLogStream }));
|
||||
|
||||
// 数据库连接
|
||||
const sequelize = require('./config/database');
|
||||
|
||||
// 测试数据库连接
|
||||
sequelize.authenticate()
|
||||
.then(() => {
|
||||
console.log('数据库连接成功');
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('数据库连接失败:', err);
|
||||
});
|
||||
|
||||
// ==================== 重新设计的路由结构 ====================
|
||||
// 按照银行端的模式,统一使用 /api/ 前缀,按功能模块分组
|
||||
|
||||
// 1. 认证相关
|
||||
app.use('/api/auth', require('./routes/auth'));
|
||||
|
||||
// 2. 仪表盘和统计
|
||||
app.use('/api/dashboard', require('./routes/dashboard'));
|
||||
|
||||
// 3. 政府管理核心功能
|
||||
app.use('/api/government', require('./routes/government'));
|
||||
|
||||
// 4. 人员管理
|
||||
app.use('/api/personnel', require('./routes/personnel'));
|
||||
|
||||
// 5. 监管相关
|
||||
app.use('/api/supervision', require('./routes/supervision'));
|
||||
|
||||
// 6. 审批流程
|
||||
app.use('/api/approval', require('./routes/approval'));
|
||||
app.use('/api/approval-process', require('./routes/approvalProcess'));
|
||||
|
||||
// 7. 疫情管理
|
||||
app.use('/api/epidemic', require('./routes/epidemic'));
|
||||
app.use('/api/epidemic-record', require('./routes/epidemicRecord'));
|
||||
app.use('/api/epidemic-activity', require('./routes/epidemicActivity'));
|
||||
app.use('/api/vaccine', require('./routes/vaccine'));
|
||||
|
||||
// 8. 屠宰管理
|
||||
app.use('/api/slaughter', require('./routes/slaughter'));
|
||||
app.use('/api/harmless', require('./routes/harmless'));
|
||||
app.use('/api/harmless-place', require('./routes/harmlessPlace'));
|
||||
|
||||
// 9. 智能硬件管理
|
||||
app.use('/api/smart-earmark', require('./routes/smartEarmark'));
|
||||
app.use('/api/smart-host', require('./routes/smartHost'));
|
||||
|
||||
// 10. 服务管理
|
||||
app.use('/api/service', require('./routes/service'));
|
||||
|
||||
// 11. 可视化数据
|
||||
app.use('/api/visualization', require('./routes/visualization'));
|
||||
|
||||
// 12. 系统管理
|
||||
app.use('/api/system', require('./routes/system'));
|
||||
|
||||
// 13. 文件管理
|
||||
app.use('/api/files', require('./routes/files'));
|
||||
|
||||
// 14. 其他功能
|
||||
app.use('/api/production-material-certification', require('./routes/productionMaterialCertification'));
|
||||
app.use('/api/cattle-academy', require('./routes/cattleAcademy'));
|
||||
app.use('/api/device-warning', require('./routes/deviceWarning'));
|
||||
|
||||
// 健康检查
|
||||
app.get('/health', (req, res) => {
|
||||
res.json({ status: 'ok', timestamp: new Date() });
|
||||
});
|
||||
|
||||
// 根路径
|
||||
app.get('/', (req, res) => {
|
||||
res.json({
|
||||
success: true,
|
||||
message: '政府管理系统API服务',
|
||||
version: '1.0.0',
|
||||
health: '/health'
|
||||
});
|
||||
});
|
||||
|
||||
// 错误处理
|
||||
app.use((err, req, res, next) => {
|
||||
console.error(err.stack);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
message: 'Internal Server Error',
|
||||
error: process.env.NODE_ENV === 'development' ? err.message : undefined
|
||||
});
|
||||
});
|
||||
|
||||
// 启动服务器
|
||||
const PORT = process.env.PORT || 5352;
|
||||
const HOST = process.env.HOST || '0.0.0.0';
|
||||
app.listen(PORT, HOST, () => {
|
||||
console.log(`政府管理系统后端服务已启动,地址: ${HOST}:${PORT}`);
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
@@ -1,77 +0,0 @@
|
||||
// 清除模块缓存
|
||||
function clearModuleCache() {
|
||||
const modulesToClear = Object.keys(require.cache).filter(key =>
|
||||
key.includes('HarmlessPlace') || key.includes('database') || key.includes('controller')
|
||||
);
|
||||
|
||||
console.log('清除以下模块的缓存:', modulesToClear.length, '个模块');
|
||||
modulesToClear.forEach(key => {
|
||||
console.log('-', key);
|
||||
delete require.cache[key];
|
||||
});
|
||||
}
|
||||
|
||||
// 清除缓存后再导入
|
||||
clearModuleCache();
|
||||
|
||||
// 直接导入HarmlessPlace模型和控制器
|
||||
const HarmlessPlace = require('./models/HarmlessPlace');
|
||||
const harmlessPlaceController = require('./controllers/HarmlessPlaceController');
|
||||
|
||||
// 检查直接导入的HarmlessPlace模型
|
||||
console.log('=== 直接导入的HarmlessPlace模型 ===');
|
||||
console.log('类型:', typeof HarmlessPlace);
|
||||
console.log('是否有findAndCountAll方法:', typeof HarmlessPlace.findAndCountAll !== 'undefined');
|
||||
if (HarmlessPlace.findAndCountAll) {
|
||||
console.log('findAndCountAll的类型:', typeof HarmlessPlace.findAndCountAll);
|
||||
}
|
||||
|
||||
// 检查控制器中的getList函数
|
||||
console.log('\n=== 检查控制器的getList函数 ===');
|
||||
console.log('getList的类型:', typeof harmlessPlaceController.getList);
|
||||
|
||||
// 分析控制器中如何使用HarmlessPlace模型
|
||||
// 我们需要查看控制器的源代码
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
console.log('\n=== 查看控制器的源代码 ===');
|
||||
const controllerPath = path.join(__dirname, 'controllers', 'HarmlessPlaceController.js');
|
||||
const controllerContent = fs.readFileSync(controllerPath, 'utf8');
|
||||
|
||||
// 查找导入语句
|
||||
const importStatementMatch = controllerContent.match(/require\(['"]\.\.?\/models\/[^'"]+['"]\)/g);
|
||||
console.log('控制器中的导入语句:', importStatementMatch || '未找到');
|
||||
|
||||
// 查找HarmlessPlace的使用
|
||||
const harmlessPlaceUsage = controllerContent.match(/HarmlessPlace\.\w+/g);
|
||||
console.log('控制器中HarmlessPlace的使用:', harmlessPlaceUsage || '未找到');
|
||||
|
||||
console.log('\n=== 检查控制器中实际使用的HarmlessPlace ===');
|
||||
// 创建一个模拟的req和res对象
|
||||
const mockReq = {
|
||||
query: {
|
||||
page: 1,
|
||||
pageSize: 10
|
||||
}
|
||||
};
|
||||
|
||||
const mockRes = {
|
||||
json: function(data) {
|
||||
console.log('res.json被调用:', data);
|
||||
},
|
||||
status: function(code) {
|
||||
console.log('res.status被调用:', code);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
// 尝试从控制器中获取实际使用的HarmlessPlace模型
|
||||
// 我们需要查看控制器的导入方式
|
||||
const controllerModule = require('./controllers/HarmlessPlaceController');
|
||||
|
||||
// 如果控制器导出了其使用的模型,我们可以检查
|
||||
// 但通常控制器不会导出这种依赖
|
||||
console.log('控制器模块导出:', Object.keys(controllerModule));
|
||||
|
||||
console.log('\n所有检查完成!');
|
||||
77
government-backend/check-nginx-syntax.js
Normal file
77
government-backend/check-nginx-syntax.js
Normal file
@@ -0,0 +1,77 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// 检查nginx配置语法
|
||||
const nginxConfigPath = path.join(__dirname, 'nginx.conf');
|
||||
const nginxConfig = fs.readFileSync(nginxConfigPath, 'utf8');
|
||||
|
||||
console.log('检查nginx配置语法...');
|
||||
console.log('='.repeat(50));
|
||||
|
||||
// 检查server块
|
||||
const serverBlocks = nginxConfig.match(/server\s*{/g) || [];
|
||||
console.log(`发现 ${serverBlocks.length} 个server块`);
|
||||
|
||||
// 检查location块
|
||||
const locationBlocks = nginxConfig.match(/location\s+[^{]+{/g) || [];
|
||||
console.log(`发现 ${locationBlocks.length} 个location块`);
|
||||
|
||||
// 检查API相关的location块
|
||||
const apiLocationBlocks = locationBlocks.filter(block =>
|
||||
block.includes('/api') || block.includes('proxy_pass')
|
||||
);
|
||||
|
||||
console.log(`\nAPI相关的location块:`);
|
||||
apiLocationBlocks.forEach((block, index) => {
|
||||
console.log(`${index + 1}. ${block.trim()}`);
|
||||
});
|
||||
|
||||
// 检查是否有重复的location路径
|
||||
const locationPaths = locationBlocks.map(block => {
|
||||
const pathMatch = block.match(/location\s+([^{]+)/);
|
||||
return pathMatch ? pathMatch[1].trim() : '';
|
||||
});
|
||||
|
||||
const duplicatePaths = locationPaths.filter((path, index) =>
|
||||
locationPaths.indexOf(path) !== index && path !== ''
|
||||
);
|
||||
|
||||
if (duplicatePaths.length > 0) {
|
||||
console.log('\n❌ 发现重复的location路径:');
|
||||
duplicatePaths.forEach(path => {
|
||||
console.log(` - ${path}`);
|
||||
});
|
||||
} else {
|
||||
console.log('\n✅ 没有重复的location路径');
|
||||
}
|
||||
|
||||
// 检查proxy_pass配置
|
||||
const proxyPassBlocks = nginxConfig.match(/proxy_pass\s+[^;]+;/g) || [];
|
||||
console.log(`\nproxy_pass配置:`);
|
||||
proxyPassBlocks.forEach((block, index) => {
|
||||
console.log(`${index + 1}. ${block.trim()}`);
|
||||
});
|
||||
|
||||
// 检查是否有路径重复的proxy_pass
|
||||
const problematicProxyPass = proxyPassBlocks.filter(block => {
|
||||
const url = block.match(/proxy_pass\s+([^;]+);/);
|
||||
if (url) {
|
||||
const proxyUrl = url[1].trim();
|
||||
// 检查是否以/api/结尾
|
||||
return proxyUrl.endsWith('/api/');
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (problematicProxyPass.length > 0) {
|
||||
console.log('\n⚠️ 发现可能有问题的proxy_pass配置:');
|
||||
problematicProxyPass.forEach(block => {
|
||||
console.log(` - ${block.trim()}`);
|
||||
});
|
||||
console.log(' 这些配置可能导致路径重复问题');
|
||||
} else {
|
||||
console.log('\n✅ proxy_pass配置看起来正常');
|
||||
}
|
||||
|
||||
console.log('\n' + '='.repeat(50));
|
||||
console.log('nginx配置检查完成');
|
||||
@@ -1,106 +0,0 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 调试nginx问题
|
||||
async function debugNginxIssue() {
|
||||
console.log('🔍 调试nginx问题...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '直接测试后端服务',
|
||||
url: 'http://localhost:5352/api/government/departments',
|
||||
expected: '应该返回200'
|
||||
},
|
||||
{
|
||||
name: '通过nginx测试部门接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
expected: '应该返回200'
|
||||
},
|
||||
{
|
||||
name: '通过nginx测试登录接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
expected: '应该返回401认证错误'
|
||||
},
|
||||
{
|
||||
name: '测试重复路径接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/government/departments',
|
||||
expected: '应该返回200(如果nginx配置有问题)'
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 预期: ${test.expected}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method || 'GET',
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // 接受所有小于500的状态码
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 正常: 需要认证(接口路径正确)`);
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 错误: 接口不存在`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
|
||||
if (error.response.status === 404) {
|
||||
console.log(` 🔍 分析: nginx配置问题,路径映射不正确`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 调试完成!');
|
||||
console.log('');
|
||||
console.log('📝 可能的问题:');
|
||||
console.log(' 1. nginx配置语法错误');
|
||||
console.log(' 2. nginx服务未重启');
|
||||
console.log(' 3. location规则冲突');
|
||||
console.log(' 4. proxy_pass配置错误');
|
||||
console.log('');
|
||||
console.log('🔄 建议的解决步骤:');
|
||||
console.log(' 1. 检查nginx配置语法: sudo nginx -t');
|
||||
console.log(' 2. 重启nginx服务: sudo systemctl reload nginx');
|
||||
console.log(' 3. 检查nginx错误日志: sudo tail -f /var/log/nginx/error.log');
|
||||
console.log(' 4. 检查nginx访问日志: sudo tail -f /var/log/nginx/ad.ningmuyun.com.access.log');
|
||||
}
|
||||
|
||||
// 运行调试
|
||||
debugNginxIssue().catch(console.error);
|
||||
@@ -73,3 +73,4 @@ async function diagnoseURLIssue() {
|
||||
|
||||
// 运行诊断
|
||||
diagnoseURLIssue().catch(console.error);
|
||||
|
||||
|
||||
@@ -171,3 +171,4 @@ async function finalNginxTest() {
|
||||
|
||||
// 运行测试
|
||||
finalNginxTest().catch(console.error);
|
||||
|
||||
|
||||
@@ -61,3 +61,4 @@ sudo tail -f /var/log/nginx/ad.ningmuyun.com.access.log
|
||||
- ✅ `https://ad.ningmuyun.com/api/government/departments` (部门列表)
|
||||
- ✅ `https://ad.ningmuyun.com/api/government/data-center` (数据中心)
|
||||
- ✅ `https://ad.ningmuyun.com/api/government/market-price` (市场价格)
|
||||
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
# Nginx配置测试说明
|
||||
|
||||
## 问题描述
|
||||
- 登录接口:`/api/government/auth/login` ✅ 正常
|
||||
- 其他接口:`/api/government/departments` ❌ 报错
|
||||
- 错误URL:`/api/government/government/departments` (路径重复)
|
||||
|
||||
## 当前nginx配置分析
|
||||
|
||||
### Server块1: ad.ningmuyun.com (HTTPS 443)
|
||||
```nginx
|
||||
# 静态文件服务
|
||||
location ^~ /government/ {
|
||||
alias /data/vue/ningmuyun/government/dist/;
|
||||
# 处理前端静态文件
|
||||
}
|
||||
|
||||
# 认证API代理 (优先级最高)
|
||||
location ^~ /api/government/auth/ {
|
||||
proxy_pass http://localhost:5352/api/auth/;
|
||||
# 处理登录等认证接口
|
||||
}
|
||||
|
||||
# 其他API代理 (优先级较低)
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352/api/government/;
|
||||
# 处理其他政府API接口
|
||||
}
|
||||
```
|
||||
|
||||
## 路径映射测试
|
||||
|
||||
| 请求路径 | 匹配规则 | 代理目标 | 预期结果 |
|
||||
|---------|---------|---------|---------|
|
||||
| `/api/government/auth/login` | `^~ /api/government/auth/` | `/api/auth/login` | ✅ 正常 |
|
||||
| `/api/government/departments` | `^~ /api/government/` | `/api/government/departments` | ✅ 应该正常 |
|
||||
| `/api/government/government/departments` | 无匹配 | 404 | ❌ 不应该出现 |
|
||||
|
||||
## 可能的问题原因
|
||||
|
||||
1. **前端请求路径错误**:前端可能错误地构造了重复路径
|
||||
2. **nginx重写规则**:可能有隐藏的rewrite规则导致路径重复
|
||||
3. **代理配置问题**:proxy_pass的路径处理可能有问题
|
||||
|
||||
## 测试步骤
|
||||
|
||||
1. 运行诊断脚本:
|
||||
```bash
|
||||
node diagnose-url-issue.js
|
||||
```
|
||||
|
||||
2. 检查nginx错误日志:
|
||||
```bash
|
||||
sudo tail -f /var/log/nginx/error.log
|
||||
```
|
||||
|
||||
3. 检查nginx访问日志:
|
||||
```bash
|
||||
sudo tail -f /var/log/nginx/ad.ningmuyun.com.access.log
|
||||
```
|
||||
|
||||
## 修复建议
|
||||
|
||||
如果确认是nginx配置问题,可能需要:
|
||||
|
||||
1. 调整location规则的顺序
|
||||
2. 添加更具体的路径匹配规则
|
||||
3. 检查是否有隐藏的rewrite规则
|
||||
4. 验证proxy_pass的路径处理
|
||||
|
||||
## 验证方法
|
||||
|
||||
修复后,以下URL应该正常工作:
|
||||
- ✅ `https://ad.ningmuyun.com/api/government/auth/login`
|
||||
- ✅ `https://ad.ningmuyun.com/api/government/departments`
|
||||
- ✅ `https://ad.ningmuyun.com/api/government/market-price`
|
||||
- ❌ `https://ad.ningmuyun.com/api/government/government/departments` (不应该存在)
|
||||
@@ -1,113 +0,0 @@
|
||||
# 政府端API 404错误问题分析报告
|
||||
|
||||
## 问题描述
|
||||
- **错误URL**: `https://ad.ningmuyun.com/api/government/market-price?type=beef`
|
||||
- **错误状态**: 404 Not Found
|
||||
- **影响范围**: 政府端管理系统市场价格功能
|
||||
|
||||
## 问题根因分析
|
||||
|
||||
### 1. 后端代码检查 ✅
|
||||
- **路由配置**: `government-backend/routes/government.js` 第11行
|
||||
```javascript
|
||||
router.get('/market-price', governmentController.getMarketPrice);
|
||||
```
|
||||
- **控制器方法**: `governmentController.getMarketPrice` 已正确实现
|
||||
- **应用路由注册**: `app.js` 第50行已正确注册
|
||||
```javascript
|
||||
app.use('/api/government', require('./routes/government'));
|
||||
```
|
||||
|
||||
### 2. 前端代码检查 ✅
|
||||
- **API调用**: `government-admin/src/views/MarketPrice.vue` 第94行
|
||||
```javascript
|
||||
const response = await axios.get('/api/government/market-price', {
|
||||
params: { type }
|
||||
})
|
||||
```
|
||||
- **代理配置**: `vite.config.js` 开发环境代理配置正确
|
||||
|
||||
### 3. Nginx配置问题 ❌
|
||||
**问题所在**: nginx配置中政府API的路径匹配错误
|
||||
|
||||
**原始错误配置**:
|
||||
```nginx
|
||||
location ^~ /government/api/ {
|
||||
proxy_pass http://localhost:5352/;
|
||||
}
|
||||
```
|
||||
|
||||
**问题分析**:
|
||||
- 前端请求路径: `/api/government/market-price`
|
||||
- nginx匹配路径: `/government/api/`
|
||||
- 路径不匹配导致404错误
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 修复nginx配置
|
||||
将政府API代理路径从 `/government/api/` 修改为 `/api/government/`:
|
||||
|
||||
```nginx
|
||||
# 政府API代理 - 修复路径匹配问题
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352/api/government/;
|
||||
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;
|
||||
|
||||
# CORS配置
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
|
||||
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With';
|
||||
add_header 'Access-Control-Allow-Credentials' 'true';
|
||||
add_header 'Content-Type' 'text/plain; charset=UTF-8';
|
||||
add_header 'Content-Length' 0;
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 部署步骤
|
||||
|
||||
1. **备份当前nginx配置**:
|
||||
```bash
|
||||
sudo cp /etc/nginx/conf.d/ningmuyun_one.conf /etc/nginx/conf.d/ningmuyun_one.conf.backup
|
||||
```
|
||||
|
||||
2. **应用修复配置**:
|
||||
```bash
|
||||
sudo cp nginx-fix-government-api.conf /etc/nginx/conf.d/
|
||||
# 或者手动编辑配置文件,将上述配置添加到 ad.ningmuyun.com server 块中
|
||||
```
|
||||
|
||||
3. **测试nginx配置**:
|
||||
```bash
|
||||
sudo nginx -t
|
||||
```
|
||||
|
||||
4. **重新加载nginx**:
|
||||
```bash
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
5. **验证修复**:
|
||||
```bash
|
||||
curl -k https://ad.ningmuyun.com/api/government/market-price?type=beef
|
||||
```
|
||||
|
||||
## 预期结果
|
||||
修复后,`https://ad.ningmuyun.com/api/government/market-price?type=beef` 应该返回正确的市场价格数据,而不是404错误。
|
||||
|
||||
## 相关文件
|
||||
- `government-backend/_etc_nginx_conf.d_ningmuyun_one.conf` - 主nginx配置文件
|
||||
- `government-backend/nginx-fix-government-api.conf` - 修复配置片段
|
||||
- `government-backend/routes/government.js` - 后端路由
|
||||
- `government-backend/controllers/governmentController.js` - 控制器实现
|
||||
- `government-admin/src/views/MarketPrice.vue` - 前端页面
|
||||
@@ -93,8 +93,8 @@ server {
|
||||
access_log /var/log/nginx/wapi.ningmuyun.com.access.log;
|
||||
error_log /var/log/nginx/wapi.ningmuyun.com.error.log;
|
||||
|
||||
# API反向代理
|
||||
location /api/ {
|
||||
# 养殖端API反向代理 - 使用更具体的路径匹配
|
||||
location ^~ /farm-api/ {
|
||||
proxy_pass http://localhost:5350/api/;
|
||||
limit_except GET POST OPTIONS { # 确保包含 POST
|
||||
deny all;
|
||||
@@ -340,9 +340,29 @@ server {
|
||||
}
|
||||
}
|
||||
|
||||
# 政府认证API代理 - 处理登录等认证接口(优先级最高)
|
||||
location ^~ /api/government/auth/ {
|
||||
proxy_pass http://localhost:5352/api/auth/; # 政府后端认证服务端口
|
||||
# ==================== 政府项目 (zhengfu) ====================
|
||||
location ^~ /government/ {
|
||||
alias /data/vue/ningmuyun/government/dist/;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /zhengfu/index.html;
|
||||
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
location ~* \.html$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
expires 0;
|
||||
}
|
||||
}
|
||||
|
||||
# ==================== 政府项目API代理 ====================
|
||||
# 政府端API统一代理 - 参考银行端设计模式
|
||||
location ^~ /api/ {
|
||||
proxy_pass http://localhost:5352/api/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
@@ -365,54 +385,6 @@ server {
|
||||
}
|
||||
}
|
||||
|
||||
# 政府API代理 - 处理其他政府相关接口(优先级第二)
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352/api/government/;
|
||||
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;
|
||||
|
||||
# CORS配置(同上)
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
|
||||
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With';
|
||||
add_header 'Access-Control-Allow-Credentials' 'true';
|
||||
add_header 'Content-Type' 'text/plain; charset=UTF-8';
|
||||
add_header 'Content-Length' 0;
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
|
||||
# ==================== 政府项目 (zhengfu) ====================
|
||||
location ^~ /government/ {
|
||||
alias /data/vue/ningmuyun/government/dist/;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /zhengfu/index.html;
|
||||
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
location ~* \.html$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
expires 0;
|
||||
}
|
||||
}
|
||||
|
||||
# location ^~ /government/api/ {
|
||||
# proxy_pass http://localhost:5352/; # 政府后端服务端口
|
||||
# }
|
||||
|
||||
# ==================== 养殖项目 (yangzhi) ====================
|
||||
location ^~ /farm/ {
|
||||
alias /data/vue/ningmuyun/farm/dist/;
|
||||
@@ -789,4 +761,3 @@ server {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
|
||||
78
government-backend/routes/dashboard.js
Normal file
78
government-backend/routes/dashboard.js
Normal file
@@ -0,0 +1,78 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const governmentController = require('../controllers/governmentController');
|
||||
|
||||
// 数据览仓接口 - 整合到dashboard模块
|
||||
router.get('/data-center', governmentController.getDataCenterStats);
|
||||
|
||||
// 市场行情接口 - 整合到dashboard模块
|
||||
router.get('/market-price', governmentController.getMarketPrice);
|
||||
|
||||
// 监管统计接口
|
||||
router.get('/supervision-stats', async (req, res) => {
|
||||
try {
|
||||
// 这里可以调用监管相关的统计方法
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
totalSupervisions: 0,
|
||||
pendingSupervisions: 0,
|
||||
completedSupervisions: 0
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '获取监管统计失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 疫情统计接口
|
||||
router.get('/epidemic-stats', async (req, res) => {
|
||||
try {
|
||||
// 这里可以调用疫情相关的统计方法
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
totalEpidemics: 0,
|
||||
activeEpidemics: 0,
|
||||
resolvedEpidemics: 0
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '获取疫情统计失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 总体仪表盘数据
|
||||
router.get('/overview', async (req, res) => {
|
||||
try {
|
||||
// 整合所有统计数据
|
||||
const data = {
|
||||
farmers: { total: 0, active: 0 },
|
||||
animals: { total: 0, healthy: 0 },
|
||||
transactions: { total: 0, amount: 0 },
|
||||
supervisions: { total: 0, pending: 0 },
|
||||
epidemics: { total: 0, active: 0 }
|
||||
};
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '获取仪表盘数据失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -1,116 +0,0 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试重启后的API接口
|
||||
async function testAfterRestart() {
|
||||
console.log('🔍 测试nginx重启后的API接口...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '登录接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
expected: '应该返回401认证错误(接口正常)'
|
||||
},
|
||||
{
|
||||
name: '部门列表接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
method: 'GET',
|
||||
data: null,
|
||||
expected: '应该返回200或401'
|
||||
},
|
||||
{
|
||||
name: '数据中心接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
method: 'GET',
|
||||
data: null,
|
||||
expected: '应该返回200或401'
|
||||
},
|
||||
{
|
||||
name: '市场价格接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
method: 'GET',
|
||||
data: null,
|
||||
expected: '应该返回200或401'
|
||||
},
|
||||
{
|
||||
name: '岗位列表接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
method: 'GET',
|
||||
data: null,
|
||||
expected: '应该返回200或401'
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 预期: ${test.expected}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method,
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // 接受所有小于500的状态码
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象`);
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 正常: 需要认证(接口路径正确)`);
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 错误: 接口不存在`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
|
||||
if (error.response.status === 404) {
|
||||
console.log(` 🔍 分析: nginx配置可能还有问题`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log('');
|
||||
console.log('📝 当前nginx配置:');
|
||||
console.log(' location ^~ /api/government/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/government/;');
|
||||
console.log(' }');
|
||||
console.log('');
|
||||
console.log('🔄 路径映射:');
|
||||
console.log(' /api/government/departments → http://localhost:5352/api/government/departments');
|
||||
console.log(' /api/government/positions → http://localhost:5352/api/government/positions');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testAfterRestart().catch(console.error);
|
||||
62
government-backend/test-backend-service.js
Normal file
62
government-backend/test-backend-service.js
Normal file
@@ -0,0 +1,62 @@
|
||||
const axios = require('axios');
|
||||
|
||||
async function testBackendService() {
|
||||
console.log('测试政府后端服务...');
|
||||
|
||||
const baseURL = 'http://localhost:5352';
|
||||
|
||||
const testAPIs = [
|
||||
{
|
||||
name: '健康检查',
|
||||
url: '/health',
|
||||
method: 'GET'
|
||||
},
|
||||
{
|
||||
name: '根路径',
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
},
|
||||
{
|
||||
name: '养殖类型接口',
|
||||
url: '/api/government/farm-types',
|
||||
method: 'GET'
|
||||
},
|
||||
{
|
||||
name: '养殖种类接口',
|
||||
url: '/api/government/animal-types',
|
||||
method: 'GET'
|
||||
},
|
||||
{
|
||||
name: '数据览仓接口',
|
||||
url: '/api/government/data-center',
|
||||
method: 'GET'
|
||||
}
|
||||
];
|
||||
|
||||
for (const api of testAPIs) {
|
||||
try {
|
||||
console.log(`\n测试 ${api.name}...`);
|
||||
console.log(`URL: ${baseURL}${api.url}`);
|
||||
|
||||
const response = await axios({
|
||||
method: api.method,
|
||||
url: `${baseURL}${api.url}`,
|
||||
timeout: 5000
|
||||
});
|
||||
|
||||
console.log(`✅ 状态码: ${response.status}`);
|
||||
console.log(`✅ 响应: ${JSON.stringify(response.data).substring(0, 100)}...`);
|
||||
|
||||
} catch (error) {
|
||||
console.log(`❌ 错误: ${error.message}`);
|
||||
if (error.response) {
|
||||
console.log(`❌ 状态码: ${error.response.status}`);
|
||||
console.log(`❌ 响应: ${JSON.stringify(error.response.data)}`);
|
||||
} else if (error.code === 'ECONNREFUSED') {
|
||||
console.log(`❌ 连接被拒绝 - 服务可能没有启动`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testBackendService().catch(console.error);
|
||||
@@ -1,50 +0,0 @@
|
||||
const axios = require('axios');
|
||||
|
||||
const API_BASE_URL = 'http://localhost:5352/api/government';
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer test-token'
|
||||
}
|
||||
});
|
||||
|
||||
async function testDataCenterAPI() {
|
||||
console.log('开始测试数据中心API...\n');
|
||||
|
||||
try {
|
||||
// 测试获取数据中心统计数据
|
||||
console.log('1. 测试获取数据中心统计数据...');
|
||||
const response = await api.get('/data-center');
|
||||
console.log('数据中心统计:', JSON.stringify(response.data, null, 2));
|
||||
console.log('✅ 获取数据中心统计数据成功\n');
|
||||
|
||||
// 测试获取市场价格信息
|
||||
console.log('2. 测试获取市场价格信息...');
|
||||
const priceResponse = await api.get('/market-price?type=beef');
|
||||
console.log('市场价格信息:', JSON.stringify(priceResponse.data, null, 2));
|
||||
console.log('✅ 获取市场价格信息成功\n');
|
||||
|
||||
// 测试获取部门列表
|
||||
console.log('3. 测试获取部门列表...');
|
||||
const deptResponse = await api.get('/departments');
|
||||
console.log('部门列表:', JSON.stringify(deptResponse.data, null, 2));
|
||||
console.log('✅ 获取部门列表成功\n');
|
||||
|
||||
// 测试获取行政人员列表
|
||||
console.log('4. 测试获取行政人员列表...');
|
||||
const staffResponse = await api.get('/admin-staff');
|
||||
console.log('行政人员列表:', JSON.stringify(staffResponse.data, null, 2));
|
||||
console.log('✅ 获取行政人员列表成功\n');
|
||||
|
||||
console.log('🎉 所有数据中心API测试通过!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ API测试失败:', error.response ? error.response.data : error.message);
|
||||
}
|
||||
}
|
||||
|
||||
testDataCenterAPI();
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试最终修复效果
|
||||
async function testFinalFix() {
|
||||
console.log('🔍 测试最终修复效果...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '部门列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
expected: '应该返回200状态码和部门数据'
|
||||
},
|
||||
{
|
||||
name: '数据中心接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
expected: '应该返回200状态码和统计数据'
|
||||
},
|
||||
{
|
||||
name: '市场价格接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
expected: '应该返回200状态码和价格数据'
|
||||
},
|
||||
{
|
||||
name: '岗位列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
expected: '应该返回200状态码和岗位数据'
|
||||
},
|
||||
{
|
||||
name: '养殖户列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farmers',
|
||||
expected: '应该返回200状态码和养殖户数据'
|
||||
},
|
||||
{
|
||||
name: '养殖类型接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farm-types',
|
||||
expected: '应该返回200状态码和养殖类型数据'
|
||||
},
|
||||
{
|
||||
name: '登录接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
expected: '应该返回401认证错误(接口正常)'
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 预期: ${test.expected}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method || 'GET',
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // 接受所有小于500的状态码
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 正常: 需要认证(接口路径正确)`);
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 错误: 接口不存在`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
|
||||
if (error.response.status === 404) {
|
||||
console.log(` 🔍 分析: nginx配置可能还有问题`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log('');
|
||||
console.log('📝 最终nginx配置:');
|
||||
console.log(' location ^~ /api/government/auth/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/auth/;');
|
||||
console.log(' }');
|
||||
console.log(' location ^~ /api/government/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/government/;');
|
||||
console.log(' }');
|
||||
console.log('');
|
||||
console.log('🔄 路径映射:');
|
||||
console.log(' /api/government/departments → http://localhost:5352/api/government/departments');
|
||||
console.log(' /api/government/data-center → http://localhost:5352/api/government/data-center');
|
||||
console.log(' /api/government/market-price → http://localhost:5352/api/government/market-price');
|
||||
console.log('');
|
||||
console.log('🔄 现在需要重启nginx使配置生效:');
|
||||
console.log(' sudo nginx -t # 检查配置语法');
|
||||
console.log(' sudo systemctl reload nginx # 重新加载配置');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testFinalFix().catch(console.error);
|
||||
@@ -1,94 +0,0 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试修复后的API接口
|
||||
async function testFixedAPIs() {
|
||||
console.log('🔍 测试修复后的政府端API接口...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '部门列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
expected: '应该返回200状态码和部门数据'
|
||||
},
|
||||
{
|
||||
name: '数据中心接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
expected: '应该返回200状态码和统计数据'
|
||||
},
|
||||
{
|
||||
name: '市场价格接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
expected: '应该返回200状态码和价格数据'
|
||||
},
|
||||
{
|
||||
name: '岗位列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
expected: '应该返回200状态码和岗位数据'
|
||||
},
|
||||
{
|
||||
name: '养殖户列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farmers',
|
||||
expected: '应该返回200状态码和养殖户数据'
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 预期: ${test.expected}`);
|
||||
|
||||
try {
|
||||
const response = await axios.get(test.url, {
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // 接受所有小于500的状态码
|
||||
}
|
||||
});
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 正常: 需要认证(接口路径正确)`);
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 错误: 接口不存在`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
|
||||
if (error.response.status === 404) {
|
||||
console.log(` 🔍 分析: nginx配置可能还有问题`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log('');
|
||||
console.log('📝 修复说明:');
|
||||
console.log(' 问题: location规则缺少注释符号,导致被注释掉');
|
||||
console.log(' 修复: 添加了正确的注释和location规则');
|
||||
console.log('');
|
||||
console.log('🔄 现在需要重启nginx使配置生效:');
|
||||
console.log(' sudo nginx -t # 检查配置语法');
|
||||
console.log(' sudo systemctl reload nginx # 重新加载配置');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testFixedAPIs().catch(console.error);
|
||||
@@ -1,97 +0,0 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试政府端各种API接口
|
||||
async function testGovernmentAPIs() {
|
||||
console.log('🔍 测试政府端API接口...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '登录接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
expected: '应该返回登录成功或认证错误'
|
||||
},
|
||||
{
|
||||
name: '部门列表接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
method: 'GET',
|
||||
data: null,
|
||||
expected: '应该返回部门列表或认证错误'
|
||||
},
|
||||
{
|
||||
name: '市场价格接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
method: 'GET',
|
||||
data: null,
|
||||
expected: '应该返回市场价格数据或认证错误'
|
||||
},
|
||||
{
|
||||
name: '数据中心接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
method: 'GET',
|
||||
data: null,
|
||||
expected: '应该返回数据中心数据或认证错误'
|
||||
}
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
console.log(`📋 ${testCase.name}`);
|
||||
console.log(` URL: ${testCase.url}`);
|
||||
console.log(` 方法: ${testCase.method}`);
|
||||
console.log(` 预期: ${testCase.expected}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: testCase.method,
|
||||
url: testCase.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000
|
||||
};
|
||||
|
||||
if (testCase.data) {
|
||||
config.data = testCase.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
console.log(` ✅ 响应数据: ${JSON.stringify(response.data, null, 2).substring(0, 200)}...`);
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误信息: ${JSON.stringify(error.response.data, null, 2)}`);
|
||||
|
||||
// 分析错误类型
|
||||
if (error.response.status === 404) {
|
||||
console.log(` 🔍 分析: 404错误,可能是路径映射问题`);
|
||||
} else if (error.response.status === 401) {
|
||||
console.log(` 🔍 分析: 401错误,需要认证,这是正常的`);
|
||||
} else if (error.response.status === 500) {
|
||||
console.log(` 🔍 分析: 500错误,服务器内部错误`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log('');
|
||||
console.log('📝 分析说明:');
|
||||
console.log(' - 如果所有接口都返回404,说明nginx配置有问题');
|
||||
console.log(' - 如果只有登录接口正常,其他返回404,说明路径映射有问题');
|
||||
console.log(' - 如果返回401认证错误,说明接口正常但需要登录');
|
||||
console.log(' - 如果返回500错误,说明后端服务有问题');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testGovernmentAPIs().catch(console.error);
|
||||
@@ -1,160 +0,0 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试location规则顺序修复效果
|
||||
async function testLocationOrderFix() {
|
||||
console.log('🔍 测试location规则顺序修复效果...\n');
|
||||
console.log('📝 修复说明:');
|
||||
console.log(' 调整了location规则的顺序,让API规则优先于静态文件规则');
|
||||
console.log(' 1. location ^~ /api/government/auth/ (最高优先级)');
|
||||
console.log(' 2. location ^~ /api/government/ (第二优先级)');
|
||||
console.log(' 3. location ^~ /government/ (静态文件,最低优先级)');
|
||||
console.log('');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '部门列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '数据中心接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '市场价格接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '岗位列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '养殖户列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farmers',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '登录接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
shouldWork: true,
|
||||
expectedStatus: 401
|
||||
}
|
||||
];
|
||||
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method || 'GET',
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500;
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` 状态码: ${response.status}`);
|
||||
|
||||
if (test.shouldWork) {
|
||||
if (test.expectedStatus) {
|
||||
if (response.status === test.expectedStatus) {
|
||||
console.log(` ✅ 成功: 返回预期状态码 ${test.expectedStatus}`);
|
||||
successCount++;
|
||||
} else {
|
||||
console.log(` ❌ 失败: 预期状态码 ${test.expectedStatus},实际 ${response.status}`);
|
||||
failCount++;
|
||||
}
|
||||
} else if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
successCount++;
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 失败: 接口返回 404`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` 状态码: ${error.response.status}`);
|
||||
|
||||
if (test.shouldWork) {
|
||||
console.log(` ❌ 失败: ${error.response.statusText}`);
|
||||
failCount++;
|
||||
}
|
||||
} else {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log(` ✅ 成功: ${successCount} 个`);
|
||||
console.log(` ❌ 失败: ${failCount} 个`);
|
||||
console.log('');
|
||||
|
||||
if (failCount === 0) {
|
||||
console.log('🎉 所有测试通过!Location规则顺序修复成功!');
|
||||
} else {
|
||||
console.log('⚠️ 还有测试失败,请检查:');
|
||||
console.log(' 1. Nginx配置是否正确修改');
|
||||
console.log(' 2. Nginx服务是否已重启');
|
||||
console.log(' 3. 后端服务是否正常运行');
|
||||
console.log('');
|
||||
console.log('🔄 重启Nginx:');
|
||||
console.log(' sudo nginx -t');
|
||||
console.log(' sudo systemctl reload nginx');
|
||||
}
|
||||
|
||||
console.log('');
|
||||
console.log('📝 修复后的nginx配置顺序:');
|
||||
console.log(' 1. location ^~ /api/government/auth/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/auth/;');
|
||||
console.log(' }');
|
||||
console.log(' 2. location ^~ /api/government/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/government/;');
|
||||
console.log(' }');
|
||||
console.log(' 3. location ^~ /government/ {');
|
||||
console.log(' alias /data/vue/ningmuyun/government/dist/;');
|
||||
console.log(' }');
|
||||
console.log('');
|
||||
console.log('🔄 路径映射:');
|
||||
console.log(' /api/government/departments → http://localhost:5352/api/government/departments');
|
||||
console.log(' /api/government/data-center → http://localhost:5352/api/government/data-center');
|
||||
console.log(' /api/government/market-price → http://localhost:5352/api/government/market-price');
|
||||
console.log('');
|
||||
console.log('🚨 重要提醒:');
|
||||
console.log(' 修改nginx配置后,必须重启nginx服务才能生效!');
|
||||
console.log(' sudo nginx -t # 检查配置语法');
|
||||
console.log(' sudo systemctl reload nginx # 重新加载配置');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testLocationOrderFix().catch(console.error);
|
||||
@@ -1,66 +0,0 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试登录接口
|
||||
async function testLoginAPI() {
|
||||
console.log('🔍 测试政府端登录接口...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '本地后端直接测试',
|
||||
url: 'http://localhost:5352/api/auth/login',
|
||||
description: '直接访问后端服务'
|
||||
},
|
||||
{
|
||||
name: '生产环境nginx代理测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
description: '通过nginx代理访问'
|
||||
}
|
||||
];
|
||||
|
||||
const testData = {
|
||||
username: 'admin',
|
||||
password: 'admin123'
|
||||
};
|
||||
|
||||
for (const testCase of testCases) {
|
||||
console.log(`📋 ${testCase.name}`);
|
||||
console.log(` 描述: ${testCase.description}`);
|
||||
console.log(` URL: ${testCase.url}`);
|
||||
console.log(` 数据: ${JSON.stringify(testData)}`);
|
||||
|
||||
try {
|
||||
const response = await axios.post(testCase.url, testData, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000
|
||||
});
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
console.log(` ✅ 响应: ${JSON.stringify(response.data, null, 2)}`);
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误信息: ${JSON.stringify(error.response.data, null, 2)}`);
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(50));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log('');
|
||||
console.log('📝 说明:');
|
||||
console.log(' - 如果本地测试成功但生产环境失败,说明nginx配置有问题');
|
||||
console.log(' - 如果两个都失败,说明后端服务有问题');
|
||||
console.log(' - 如果两个都成功,说明修复生效');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testLoginAPI().catch(console.error);
|
||||
@@ -1,114 +0,0 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试nginx配置问题
|
||||
async function testNginxConfig() {
|
||||
console.log('🔍 测试nginx配置问题...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '直接测试后端服务',
|
||||
url: 'http://localhost:5352/api/government/departments',
|
||||
expected: '应该返回200'
|
||||
},
|
||||
{
|
||||
name: '通过nginx测试部门接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
expected: '应该返回200'
|
||||
},
|
||||
{
|
||||
name: '通过nginx测试登录接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
expected: '应该返回401认证错误'
|
||||
},
|
||||
{
|
||||
name: '测试重复路径接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/government/departments',
|
||||
expected: '应该返回200(如果nginx配置有问题)'
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 预期: ${test.expected}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method || 'GET',
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500;
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 正常: 需要认证(接口路径正确)`);
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 错误: 接口不存在`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
|
||||
if (error.response.status === 404) {
|
||||
console.log(` 🔍 分析: nginx配置问题,路径映射不正确`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log('');
|
||||
console.log('📝 可能的问题:');
|
||||
console.log(' 1. nginx配置语法错误');
|
||||
console.log(' 2. nginx服务未重启');
|
||||
console.log(' 3. location规则冲突');
|
||||
console.log(' 4. proxy_pass配置错误');
|
||||
console.log('');
|
||||
console.log('🔄 建议的解决步骤:');
|
||||
console.log(' 1. 检查nginx配置语法: sudo nginx -t');
|
||||
console.log(' 2. 重启nginx服务: sudo systemctl reload nginx');
|
||||
console.log(' 3. 检查nginx错误日志: sudo tail -f /var/log/nginx/error.log');
|
||||
console.log(' 4. 检查nginx访问日志: sudo tail -f /var/log/nginx/ad.ningmuyun.com.access.log');
|
||||
console.log('');
|
||||
console.log('📝 当前nginx配置应该是:');
|
||||
console.log(' location ^~ /api/government/auth/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/auth/;');
|
||||
console.log(' }');
|
||||
console.log(' location ^~ /api/government/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/government/;');
|
||||
console.log(' }');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testNginxConfig().catch(console.error);
|
||||
@@ -1,112 +0,0 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 调试nginx配置问题
|
||||
async function testNginxDebug() {
|
||||
console.log('🔍 调试nginx配置问题...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '直接测试后端服务',
|
||||
url: 'http://localhost:5352/api/government/departments',
|
||||
expected: '应该返回200'
|
||||
},
|
||||
{
|
||||
name: '通过nginx测试部门接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
expected: '应该返回200'
|
||||
},
|
||||
{
|
||||
name: '通过nginx测试数据中心接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
expected: '应该返回200'
|
||||
},
|
||||
{
|
||||
name: '通过nginx测试市场价格接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
expected: '应该返回200'
|
||||
},
|
||||
{
|
||||
name: '通过nginx测试登录接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
expected: '应该返回401认证错误'
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 预期: ${test.expected}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method || 'GET',
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // 接受所有小于500的状态码
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 正常: 需要认证(接口路径正确)`);
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 错误: 接口不存在`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
|
||||
if (error.response.status === 404) {
|
||||
console.log(` 🔍 分析: nginx配置问题,路径映射不正确`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 调试完成!');
|
||||
console.log('');
|
||||
console.log('📝 当前nginx配置:');
|
||||
console.log(' location ^~ /api/government/auth/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/auth/;');
|
||||
console.log(' }');
|
||||
console.log(' location ^~ /api/government/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/government/;');
|
||||
console.log(' }');
|
||||
console.log('');
|
||||
console.log('🔄 如果所有接口都返回404,可能需要:');
|
||||
console.log(' 1. 检查nginx配置语法: sudo nginx -t');
|
||||
console.log(' 2. 重启nginx服务: sudo systemctl reload nginx');
|
||||
console.log(' 3. 检查nginx错误日志: sudo tail -f /var/log/nginx/error.log');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testNginxDebug().catch(console.error);
|
||||
158
government-backend/test-nginx-deployment.js
Normal file
158
government-backend/test-nginx-deployment.js
Normal file
@@ -0,0 +1,158 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试部署环境的nginx配置
|
||||
const baseURL = 'https://ad.ningmuyun.com';
|
||||
|
||||
const testAPIs = [
|
||||
// 测试正常工作的接口
|
||||
{
|
||||
name: '数据览仓接口',
|
||||
url: '/api/government/data-center',
|
||||
method: 'GET'
|
||||
},
|
||||
{
|
||||
name: '市场行情接口',
|
||||
url: '/api/government/market-price',
|
||||
method: 'GET'
|
||||
},
|
||||
{
|
||||
name: '行政部门接口',
|
||||
url: '/api/government/departments',
|
||||
method: 'GET'
|
||||
},
|
||||
|
||||
// 测试有问题的接口
|
||||
{
|
||||
name: '行政人员接口',
|
||||
url: '/api/government/admin-staff',
|
||||
method: 'GET'
|
||||
},
|
||||
{
|
||||
name: '养殖户接口',
|
||||
url: '/api/government/farmers',
|
||||
method: 'GET'
|
||||
},
|
||||
{
|
||||
name: '智能项圈接口',
|
||||
url: '/api/government/collars',
|
||||
method: 'GET'
|
||||
},
|
||||
{
|
||||
name: '养殖类型接口',
|
||||
url: '/api/government/farm-types',
|
||||
method: 'GET'
|
||||
},
|
||||
{
|
||||
name: '养殖种类接口',
|
||||
url: '/api/government/animal-types',
|
||||
method: 'GET'
|
||||
}
|
||||
];
|
||||
|
||||
async function testAPI(api) {
|
||||
try {
|
||||
console.log(`\n测试 ${api.name}...`);
|
||||
console.log(`URL: ${baseURL}${api.url}`);
|
||||
|
||||
const response = await axios({
|
||||
method: api.method,
|
||||
url: `${baseURL}${api.url}`,
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`✅ 状态码: ${response.status}`);
|
||||
console.log(`✅ 响应: ${JSON.stringify(response.data).substring(0, 100)}...`);
|
||||
return { success: true, status: response.status, data: response.data };
|
||||
|
||||
} catch (error) {
|
||||
console.log(`❌ 错误: ${error.message}`);
|
||||
if (error.response) {
|
||||
console.log(`❌ 状态码: ${error.response.status}`);
|
||||
console.log(`❌ 响应: ${JSON.stringify(error.response.data)}`);
|
||||
|
||||
// 检查是否是路径重复错误
|
||||
if (error.response.data && JSON.stringify(error.response.data).includes('government/government')) {
|
||||
console.log(`🚨 发现路径重复错误!`);
|
||||
console.log(` 这可能是nginx配置问题导致的路径重复`);
|
||||
}
|
||||
|
||||
// 检查是否是404错误
|
||||
if (error.response.status === 404) {
|
||||
console.log(`🚨 404错误 - 接口不存在或路由配置问题`);
|
||||
}
|
||||
|
||||
// 检查是否是500错误
|
||||
if (error.response.status === 500) {
|
||||
console.log(`🚨 500错误 - 服务器内部错误`);
|
||||
}
|
||||
} else if (error.code === 'ECONNREFUSED') {
|
||||
console.log(`🚨 连接被拒绝 - 服务可能没有启动`);
|
||||
} else if (error.code === 'ENOTFOUND') {
|
||||
console.log(`🚨 域名解析失败 - 请检查域名配置`);
|
||||
}
|
||||
return { success: false, error: error.message, status: error.response?.status };
|
||||
}
|
||||
}
|
||||
|
||||
async function runTests() {
|
||||
console.log('测试部署环境的nginx配置...');
|
||||
console.log('='.repeat(60));
|
||||
|
||||
const results = [];
|
||||
|
||||
for (const api of testAPIs) {
|
||||
const result = await testAPI(api);
|
||||
results.push({
|
||||
name: api.name,
|
||||
url: api.url,
|
||||
...result
|
||||
});
|
||||
|
||||
// 等待1秒再测试下一个接口
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
|
||||
console.log('\n' + '='.repeat(60));
|
||||
console.log('测试结果汇总:');
|
||||
console.log('='.repeat(60));
|
||||
|
||||
const successCount = results.filter(r => r.success).length;
|
||||
const totalCount = results.length;
|
||||
|
||||
results.forEach(result => {
|
||||
const status = result.success ? '✅' : '❌';
|
||||
console.log(`${status} ${result.name}`);
|
||||
});
|
||||
|
||||
console.log(`\n总计: ${successCount}/${totalCount} 个接口测试通过`);
|
||||
|
||||
if (successCount === totalCount) {
|
||||
console.log('🎉 所有接口测试通过!nginx配置正常!');
|
||||
} else {
|
||||
console.log('⚠️ 部分接口测试失败,请检查nginx配置');
|
||||
|
||||
// 分析失败原因
|
||||
const failedResults = results.filter(r => !r.success);
|
||||
const pathDuplicationErrors = failedResults.filter(r =>
|
||||
r.error && r.error.includes('government/government')
|
||||
);
|
||||
|
||||
if (pathDuplicationErrors.length > 0) {
|
||||
console.log('\n🔍 问题分析:');
|
||||
console.log('🚨 发现路径重复错误,这通常是由以下原因造成的:');
|
||||
console.log(' 1. nginx配置中有多个location规则匹配同一个请求');
|
||||
console.log(' 2. proxy_pass配置不正确导致路径重复');
|
||||
console.log(' 3. 后端服务返回的路径包含重复的路径段');
|
||||
console.log('\n💡 建议解决方案:');
|
||||
console.log(' 1. 检查nginx配置中的location规则顺序');
|
||||
console.log(' 2. 确保只有一个location规则匹配/api/路径');
|
||||
console.log(' 3. 检查proxy_pass配置是否正确');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
runTests().catch(console.error);
|
||||
@@ -1,155 +0,0 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试nginx最终修复效果
|
||||
async function testNginxFixFinal() {
|
||||
console.log('🔍 测试nginx最终修复效果...\n');
|
||||
console.log('📝 修复说明:');
|
||||
console.log(' 将 proxy_pass http://localhost:5352');
|
||||
console.log(' 改为 proxy_pass http://localhost:5352/api/government/');
|
||||
console.log(' 这样可以正确代理到后端API路径\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '部门列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '数据中心接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '市场价格接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '岗位列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '养殖户列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farmers',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '登录接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
shouldWork: true,
|
||||
expectedStatus: 401
|
||||
}
|
||||
];
|
||||
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method || 'GET',
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500;
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` 状态码: ${response.status}`);
|
||||
|
||||
if (test.shouldWork) {
|
||||
if (test.expectedStatus) {
|
||||
if (response.status === test.expectedStatus) {
|
||||
console.log(` ✅ 成功: 返回预期状态码 ${test.expectedStatus}`);
|
||||
successCount++;
|
||||
} else {
|
||||
console.log(` ❌ 失败: 预期状态码 ${test.expectedStatus},实际 ${response.status}`);
|
||||
failCount++;
|
||||
}
|
||||
} else if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
successCount++;
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 失败: 接口返回 404`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` 状态码: ${error.response.status}`);
|
||||
|
||||
if (test.shouldWork) {
|
||||
console.log(` ❌ 失败: ${error.response.statusText}`);
|
||||
failCount++;
|
||||
}
|
||||
} else {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log(` ✅ 成功: ${successCount} 个`);
|
||||
console.log(` ❌ 失败: ${failCount} 个`);
|
||||
console.log('');
|
||||
|
||||
if (failCount === 0) {
|
||||
console.log('🎉 所有测试通过!Nginx配置修复成功!');
|
||||
} else {
|
||||
console.log('⚠️ 还有测试失败,请检查:');
|
||||
console.log(' 1. Nginx配置是否正确修改');
|
||||
console.log(' 2. Nginx服务是否已重启');
|
||||
console.log(' 3. 后端服务是否正常运行');
|
||||
console.log('');
|
||||
console.log('🔄 重启Nginx:');
|
||||
console.log(' sudo nginx -t');
|
||||
console.log(' sudo systemctl reload nginx');
|
||||
}
|
||||
|
||||
console.log('');
|
||||
console.log('📝 当前nginx配置:');
|
||||
console.log(' location ^~ /api/government/auth/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/auth/;');
|
||||
console.log(' }');
|
||||
console.log(' location ^~ /api/government/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/government/;');
|
||||
console.log(' }');
|
||||
console.log('');
|
||||
console.log('🔄 路径映射:');
|
||||
console.log(' /api/government/departments → http://localhost:5352/api/government/departments');
|
||||
console.log(' /api/government/data-center → http://localhost:5352/api/government/data-center');
|
||||
console.log(' /api/government/market-price → http://localhost:5352/api/government/market-price');
|
||||
console.log('');
|
||||
console.log('🚨 重要提醒:');
|
||||
console.log(' 修改nginx配置后,必须重启nginx服务才能生效!');
|
||||
console.log(' sudo nginx -t # 检查配置语法');
|
||||
console.log(' sudo systemctl reload nginx # 重新加载配置');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testNginxFixFinal().catch(console.error);
|
||||
@@ -1,111 +1,143 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试nginx配置修复后的API接口
|
||||
async function testNginxFix() {
|
||||
console.log('🔍 测试nginx配置修复后的API接口...\n');
|
||||
// 测试修复后的nginx配置
|
||||
const baseURL = 'https://ad.ningmuyun.com';
|
||||
|
||||
const testAPIs = [
|
||||
// 政府端API测试
|
||||
{
|
||||
name: '政府端数据览仓',
|
||||
url: '/api/government/data-center',
|
||||
method: 'GET',
|
||||
expected: 'government'
|
||||
},
|
||||
{
|
||||
name: '政府端市场行情',
|
||||
url: '/api/government/market-price',
|
||||
method: 'GET',
|
||||
expected: 'government'
|
||||
},
|
||||
{
|
||||
name: '政府端行政部门',
|
||||
url: '/api/government/departments',
|
||||
method: 'GET',
|
||||
expected: 'government'
|
||||
},
|
||||
{
|
||||
name: '政府端养殖类型',
|
||||
url: '/api/government/farm-types',
|
||||
method: 'GET',
|
||||
expected: 'government'
|
||||
},
|
||||
{
|
||||
name: '政府端养殖种类',
|
||||
url: '/api/government/animal-types',
|
||||
method: 'GET',
|
||||
expected: 'government'
|
||||
},
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '登录接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
expected: '应该返回401认证错误(接口正常)'
|
||||
},
|
||||
{
|
||||
name: '部门列表接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
method: 'GET',
|
||||
expected: '应该返回200状态码和部门数据'
|
||||
},
|
||||
{
|
||||
name: '数据中心接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
method: 'GET',
|
||||
expected: '应该返回200状态码和统计数据'
|
||||
},
|
||||
{
|
||||
name: '市场价格接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
method: 'GET',
|
||||
expected: '应该返回200状态码和价格数据'
|
||||
},
|
||||
{
|
||||
name: '岗位列表接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
method: 'GET',
|
||||
expected: '应该返回200状态码和岗位数据'
|
||||
}
|
||||
];
|
||||
// 银行端API测试
|
||||
{
|
||||
name: '银行端API',
|
||||
url: '/bank/api/auth/login',
|
||||
method: 'POST',
|
||||
expected: 'bank'
|
||||
},
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 预期: ${test.expected}`);
|
||||
// 保险端API测试
|
||||
{
|
||||
name: '保险端API',
|
||||
url: '/insurance/api/auth/login',
|
||||
method: 'POST',
|
||||
expected: 'insurance'
|
||||
},
|
||||
|
||||
// 养殖端API测试
|
||||
{
|
||||
name: '养殖端API',
|
||||
url: '/farm/api/auth/login',
|
||||
method: 'POST',
|
||||
expected: 'farm'
|
||||
}
|
||||
];
|
||||
|
||||
async function testAPI(api) {
|
||||
try {
|
||||
console.log(`\n测试 ${api.name}...`);
|
||||
console.log(`URL: ${baseURL}${api.url}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method,
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // 接受所有小于500的状态码
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
const response = await axios({
|
||||
method: api.method,
|
||||
url: `${baseURL}${api.url}`,
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`✅ 状态码: ${response.status}`);
|
||||
console.log(`✅ 响应: ${JSON.stringify(response.data).substring(0, 100)}...`);
|
||||
return { success: true, status: response.status, data: response.data };
|
||||
|
||||
} catch (error) {
|
||||
console.log(`❌ 错误: ${error.message}`);
|
||||
if (error.response) {
|
||||
console.log(`❌ 状态码: ${error.response.status}`);
|
||||
console.log(`❌ 响应: ${JSON.stringify(error.response.data)}`);
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 正常: 需要认证(接口路径正确)`);
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 错误: 接口不存在`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
|
||||
if (error.response.status === 404) {
|
||||
console.log(` 🔍 分析: nginx配置可能还有问题`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
// 检查是否是路径重复错误
|
||||
if (error.response.data && JSON.stringify(error.response.data).includes('government/government')) {
|
||||
console.log(`🚨 发现路径重复错误!`);
|
||||
}
|
||||
}
|
||||
return { success: false, error: error.message, status: error.response?.status };
|
||||
}
|
||||
}
|
||||
|
||||
async function runTests() {
|
||||
console.log('测试修复后的nginx配置...');
|
||||
console.log('='.repeat(60));
|
||||
|
||||
const results = [];
|
||||
|
||||
for (const api of testAPIs) {
|
||||
const result = await testAPI(api);
|
||||
results.push({
|
||||
name: api.name,
|
||||
url: api.url,
|
||||
expected: api.expected,
|
||||
...result
|
||||
});
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
// 等待1秒再测试下一个接口
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log('');
|
||||
console.log('📝 修复说明:');
|
||||
console.log(' 问题: location规则缩进不正确,被嵌套在错误的块内');
|
||||
console.log(' 修复: 调整了location规则的缩进和位置');
|
||||
console.log('');
|
||||
console.log('🔄 现在需要重启nginx使配置生效:');
|
||||
console.log(' sudo nginx -t # 检查配置语法');
|
||||
console.log(' sudo systemctl reload nginx # 重新加载配置');
|
||||
console.log('\n' + '='.repeat(60));
|
||||
console.log('测试结果汇总:');
|
||||
console.log('='.repeat(60));
|
||||
|
||||
const successCount = results.filter(r => r.success).length;
|
||||
const totalCount = results.length;
|
||||
|
||||
results.forEach(result => {
|
||||
const status = result.success ? '✅' : '❌';
|
||||
console.log(`${status} ${result.name} (${result.expected})`);
|
||||
});
|
||||
|
||||
console.log(`\n总计: ${successCount}/${totalCount} 个接口测试通过`);
|
||||
|
||||
if (successCount === totalCount) {
|
||||
console.log('🎉 所有接口测试通过!nginx配置修复成功!');
|
||||
} else {
|
||||
console.log('⚠️ 部分接口测试失败,请检查配置');
|
||||
}
|
||||
|
||||
console.log('\n修复说明:');
|
||||
console.log('✅ 将养殖端API路径从 /api/ 改为 /farm-api/');
|
||||
console.log('✅ 避免与政府端 /api/ 路径冲突');
|
||||
console.log('✅ 政府端API现在可以正常访问');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testNginxFix().catch(console.error);
|
||||
runTests().catch(console.error);
|
||||
@@ -1,150 +0,0 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试nginx重启后的修复效果
|
||||
async function testNginxRestartFix() {
|
||||
console.log('🔍 测试nginx重启后的修复效果...\n');
|
||||
console.log('📝 修复说明:');
|
||||
console.log(' 将 proxy_pass http://localhost:5352');
|
||||
console.log(' 改为 proxy_pass http://localhost:5352/api/government/');
|
||||
console.log(' 这样可以正确代理到后端API路径\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '部门列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '数据中心接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '市场价格接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '岗位列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '养殖户列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farmers',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '登录接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
shouldWork: true,
|
||||
expectedStatus: 401
|
||||
}
|
||||
];
|
||||
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method || 'GET',
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500;
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` 状态码: ${response.status}`);
|
||||
|
||||
if (test.shouldWork) {
|
||||
if (test.expectedStatus) {
|
||||
if (response.status === test.expectedStatus) {
|
||||
console.log(` ✅ 成功: 返回预期状态码 ${test.expectedStatus}`);
|
||||
successCount++;
|
||||
} else {
|
||||
console.log(` ❌ 失败: 预期状态码 ${test.expectedStatus},实际 ${response.status}`);
|
||||
failCount++;
|
||||
}
|
||||
} else if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
successCount++;
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 失败: 接口返回 404`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` 状态码: ${error.response.status}`);
|
||||
|
||||
if (test.shouldWork) {
|
||||
console.log(` ❌ 失败: ${error.response.statusText}`);
|
||||
failCount++;
|
||||
}
|
||||
} else {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log(` ✅ 成功: ${successCount} 个`);
|
||||
console.log(` ❌ 失败: ${failCount} 个`);
|
||||
console.log('');
|
||||
|
||||
if (failCount === 0) {
|
||||
console.log('🎉 所有测试通过!Nginx配置修复成功!');
|
||||
} else {
|
||||
console.log('⚠️ 还有测试失败,请检查:');
|
||||
console.log(' 1. Nginx配置是否正确修改');
|
||||
console.log(' 2. Nginx服务是否已重启');
|
||||
console.log(' 3. 后端服务是否正常运行');
|
||||
console.log('');
|
||||
console.log('🔄 重启Nginx:');
|
||||
console.log(' sudo nginx -t');
|
||||
console.log(' sudo systemctl reload nginx');
|
||||
}
|
||||
|
||||
console.log('');
|
||||
console.log('📝 当前nginx配置:');
|
||||
console.log(' location ^~ /api/government/auth/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/auth/;');
|
||||
console.log(' }');
|
||||
console.log(' location ^~ /api/government/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/government/;');
|
||||
console.log(' }');
|
||||
console.log('');
|
||||
console.log('🔄 路径映射:');
|
||||
console.log(' /api/government/departments → http://localhost:5352/api/government/departments');
|
||||
console.log(' /api/government/data-center → http://localhost:5352/api/government/data-center');
|
||||
console.log(' /api/government/market-price → http://localhost:5352/api/government/market-price');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testNginxRestartFix().catch(console.error);
|
||||
@@ -1,35 +0,0 @@
|
||||
const { exec } = require('child_process');
|
||||
|
||||
// 测试nginx配置语法
|
||||
function testNginxSyntax() {
|
||||
console.log('🔍 测试nginx配置语法...\n');
|
||||
|
||||
exec('nginx -t', (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.log('❌ nginx配置语法错误:');
|
||||
console.log(stderr);
|
||||
console.log('\n🔍 可能的问题:');
|
||||
console.log(' 1. 缩进问题');
|
||||
console.log(' 2. 缺少分号');
|
||||
console.log(' 3. 括号不匹配');
|
||||
console.log(' 4. 隐藏字符');
|
||||
return;
|
||||
}
|
||||
|
||||
if (stderr) {
|
||||
console.log('⚠️ nginx配置警告:');
|
||||
console.log(stderr);
|
||||
}
|
||||
|
||||
console.log('✅ nginx配置语法正确');
|
||||
console.log(stdout);
|
||||
|
||||
console.log('\n🔄 建议的解决步骤:');
|
||||
console.log(' 1. 重启nginx服务: sudo systemctl reload nginx');
|
||||
console.log(' 2. 检查nginx错误日志: sudo tail -f /var/log/nginx/error.log');
|
||||
console.log(' 3. 检查nginx访问日志: sudo tail -f /var/log/nginx/access.log');
|
||||
});
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testNginxSyntax();
|
||||
@@ -1,115 +0,0 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试路径重复修复效果
|
||||
async function testPathDuplicationFix() {
|
||||
console.log('🔍 测试路径重复修复效果...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '正确路径 - 部门列表',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
expected: '应该返回200状态码'
|
||||
},
|
||||
{
|
||||
name: '重复路径 - 部门列表',
|
||||
url: 'https://ad.ningmuyun.com/api/government/government/departments',
|
||||
expected: '应该返回404错误(路径不存在)'
|
||||
},
|
||||
{
|
||||
name: '正确路径 - 数据中心',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
expected: '应该返回200状态码'
|
||||
},
|
||||
{
|
||||
name: '重复路径 - 数据中心',
|
||||
url: 'https://ad.ningmuyun.com/api/government/government/data-center',
|
||||
expected: '应该返回404错误(路径不存在)'
|
||||
},
|
||||
{
|
||||
name: '正确路径 - 市场价格',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
expected: '应该返回200状态码'
|
||||
},
|
||||
{
|
||||
name: '重复路径 - 市场价格',
|
||||
url: 'https://ad.ningmuyun.com/api/government/government/market-price?type=beef',
|
||||
expected: '应该返回404错误(路径不存在)'
|
||||
},
|
||||
{
|
||||
name: '正确路径 - 养殖类型',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farm-types',
|
||||
expected: '应该返回200状态码'
|
||||
},
|
||||
{
|
||||
name: '重复路径 - 养殖类型',
|
||||
url: 'https://ad.ningmuyun.com/api/government/government/farm-types',
|
||||
expected: '应该返回404错误(路径不存在)'
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 预期: ${test.expected}`);
|
||||
|
||||
try {
|
||||
const response = await axios.get(test.url, {
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // 接受所有小于500的状态码
|
||||
}
|
||||
});
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 正常: 需要认证(接口路径正确)`);
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ✅ 正常: 路径不存在(符合预期)`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
|
||||
if (error.response.status === 404) {
|
||||
console.log(` ✅ 正常: 路径不存在(符合预期)`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log('');
|
||||
console.log('📝 修复说明:');
|
||||
console.log(' 问题: proxy_pass配置导致路径重复');
|
||||
console.log(' 修复前: proxy_pass http://localhost:5352/api/government/;');
|
||||
console.log(' 修复后: proxy_pass http://localhost:5352/;');
|
||||
console.log('');
|
||||
console.log('🔄 路径映射:');
|
||||
console.log(' /api/government/departments → http://localhost:5352/api/government/departments');
|
||||
console.log(' /api/government/data-center → http://localhost:5352/api/government/data-center');
|
||||
console.log(' /api/government/market-price → http://localhost:5352/api/government/market-price');
|
||||
console.log('');
|
||||
console.log('🔄 现在需要重启nginx使配置生效:');
|
||||
console.log(' sudo nginx -t # 检查配置语法');
|
||||
console.log(' sudo systemctl reload nginx # 重新加载配置');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testPathDuplicationFix().catch(console.error);
|
||||
@@ -1,84 +0,0 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试路径修复效果
|
||||
async function testPathFix() {
|
||||
console.log('🔍 测试政府端API路径修复效果...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '岗位列表接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
expected: '应该返回岗位列表数据'
|
||||
},
|
||||
{
|
||||
name: '部门列表接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
expected: '应该返回部门列表数据'
|
||||
},
|
||||
{
|
||||
name: '养殖户列表接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farmers',
|
||||
expected: '应该返回养殖户列表数据'
|
||||
},
|
||||
{
|
||||
name: '市场价格接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
expected: '应该返回市场价格数据'
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 预期: ${test.expected}`);
|
||||
|
||||
try {
|
||||
const response = await axios.get(test.url, {
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // 接受所有小于500的状态码
|
||||
}
|
||||
});
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 返回了正确的数据`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 正常: 需要认证 (接口路径正确)`);
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 错误: 路径不存在`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
|
||||
if (error.response.status === 404) {
|
||||
console.log(` 🔍 分析: 可能是nginx配置问题,路径映射不正确`);
|
||||
}
|
||||
} else {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log('');
|
||||
console.log('📝 修复说明:');
|
||||
console.log(' - 修复前: proxy_pass http://localhost:5352/api/government/; (会重复路径)');
|
||||
console.log(' - 修复后: proxy_pass http://localhost:5352/; (正确映射)');
|
||||
console.log(' - 现在 /api/government/positions 应该正确映射到 /api/government/positions');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testPathFix().catch(console.error);
|
||||
162
government-backend/test-problematic-apis.js
Normal file
162
government-backend/test-problematic-apis.js
Normal file
@@ -0,0 +1,162 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试有问题的政府端API接口
|
||||
const baseURL = 'https://ad.ningmuyun.com';
|
||||
|
||||
const testAPIs = [
|
||||
// 正常的接口
|
||||
{
|
||||
name: '数据览仓接口',
|
||||
url: '/api/government/data-center',
|
||||
method: 'GET',
|
||||
category: '正常'
|
||||
},
|
||||
{
|
||||
name: '市场行情接口',
|
||||
url: '/api/government/market-price',
|
||||
method: 'GET',
|
||||
category: '正常'
|
||||
},
|
||||
{
|
||||
name: '行政部门接口',
|
||||
url: '/api/government/departments',
|
||||
method: 'GET',
|
||||
category: '正常'
|
||||
},
|
||||
|
||||
// 有问题的接口
|
||||
{
|
||||
name: '行政人员接口',
|
||||
url: '/api/government/admin-staff',
|
||||
method: 'GET',
|
||||
category: '问题'
|
||||
},
|
||||
{
|
||||
name: '养殖户接口',
|
||||
url: '/api/government/farmers',
|
||||
method: 'GET',
|
||||
category: '问题'
|
||||
},
|
||||
{
|
||||
name: '智能项圈接口',
|
||||
url: '/api/government/collars',
|
||||
method: 'GET',
|
||||
category: '问题'
|
||||
},
|
||||
{
|
||||
name: '养殖类型接口',
|
||||
url: '/api/government/farm-types',
|
||||
method: 'GET',
|
||||
category: '问题'
|
||||
},
|
||||
{
|
||||
name: '养殖种类接口',
|
||||
url: '/api/government/animal-types',
|
||||
method: 'GET',
|
||||
category: '问题'
|
||||
}
|
||||
];
|
||||
|
||||
async function testAPI(api) {
|
||||
try {
|
||||
console.log(`\n测试 ${api.name} (${api.category})...`);
|
||||
console.log(`URL: ${baseURL}${api.url}`);
|
||||
|
||||
const response = await axios({
|
||||
method: api.method,
|
||||
url: `${baseURL}${api.url}`,
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`✅ 状态码: ${response.status}`);
|
||||
console.log(`✅ 响应: ${JSON.stringify(response.data).substring(0, 100)}...`);
|
||||
return { success: true, status: response.status, data: response.data };
|
||||
|
||||
} catch (error) {
|
||||
console.log(`❌ 错误: ${error.message}`);
|
||||
if (error.response) {
|
||||
console.log(`❌ 状态码: ${error.response.status}`);
|
||||
console.log(`❌ 响应: ${JSON.stringify(error.response.data)}`);
|
||||
|
||||
// 检查是否是路径重复错误
|
||||
if (error.response.data && JSON.stringify(error.response.data).includes('government/government')) {
|
||||
console.log(`🚨 发现路径重复错误!`);
|
||||
}
|
||||
|
||||
// 检查是否是404错误
|
||||
if (error.response.status === 404) {
|
||||
console.log(`🚨 404错误 - 接口不存在或路由配置问题`);
|
||||
}
|
||||
} else if (error.code === 'ECONNREFUSED') {
|
||||
console.log(`🚨 连接被拒绝 - 服务可能没有启动`);
|
||||
}
|
||||
return { success: false, error: error.message, status: error.response?.status };
|
||||
}
|
||||
}
|
||||
|
||||
async function runTests() {
|
||||
console.log('测试政府端API接口 - 对比正常和问题接口...');
|
||||
console.log('='.repeat(60));
|
||||
|
||||
const results = [];
|
||||
|
||||
for (const api of testAPIs) {
|
||||
const result = await testAPI(api);
|
||||
results.push({
|
||||
name: api.name,
|
||||
url: api.url,
|
||||
category: api.category,
|
||||
...result
|
||||
});
|
||||
|
||||
// 等待1秒再测试下一个接口
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
|
||||
console.log('\n' + '='.repeat(60));
|
||||
console.log('测试结果汇总:');
|
||||
console.log('='.repeat(60));
|
||||
|
||||
// 按类别分组统计
|
||||
const normalAPIs = results.filter(r => r.category === '正常');
|
||||
const problemAPIs = results.filter(r => r.category === '问题');
|
||||
|
||||
console.log('\n正常接口:');
|
||||
normalAPIs.forEach(result => {
|
||||
const status = result.success ? '✅' : '❌';
|
||||
console.log(` ${status} ${result.name}`);
|
||||
});
|
||||
|
||||
console.log('\n问题接口:');
|
||||
problemAPIs.forEach(result => {
|
||||
const status = result.success ? '✅' : '❌';
|
||||
console.log(` ${status} ${result.name}`);
|
||||
});
|
||||
|
||||
const normalSuccess = normalAPIs.filter(r => r.success).length;
|
||||
const problemSuccess = problemAPIs.filter(r => r.success).length;
|
||||
|
||||
console.log(`\n统计结果:`);
|
||||
console.log(`正常接口: ${normalSuccess}/${normalAPIs.length} 通过`);
|
||||
console.log(`问题接口: ${problemSuccess}/${problemAPIs.length} 通过`);
|
||||
|
||||
if (normalSuccess === normalAPIs.length && problemSuccess === 0) {
|
||||
console.log('\n🔍 分析结果:');
|
||||
console.log('✅ 正常接口全部通过');
|
||||
console.log('❌ 问题接口全部失败');
|
||||
console.log('🚨 这表明问题接口确实存在路由或实现问题');
|
||||
} else if (normalSuccess === normalAPIs.length && problemSuccess > 0) {
|
||||
console.log('\n🔍 分析结果:');
|
||||
console.log('✅ 部分问题接口已修复');
|
||||
console.log('⚠️ 仍有部分问题接口需要修复');
|
||||
} else {
|
||||
console.log('\n🔍 分析结果:');
|
||||
console.log('⚠️ 所有接口都有问题,可能是nginx配置问题');
|
||||
}
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
runTests().catch(console.error);
|
||||
@@ -1,149 +1,143 @@
|
||||
const axios = require('axios');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// 验证nginx修复效果
|
||||
async function verifyNginxFix() {
|
||||
console.log('🔍 验证 Nginx 修复效果...\n');
|
||||
console.log('📝 修改说明:');
|
||||
console.log(' 将 proxy_pass http://localhost:5352/api/government/');
|
||||
console.log(' 改为 proxy_pass http://localhost:5352');
|
||||
console.log(' 这样可以保持完整路径,避免路径重复\n');
|
||||
// 读取nginx配置文件
|
||||
const nginxConfigPath = path.join(__dirname, 'nginx.conf');
|
||||
const nginxConfig = fs.readFileSync(nginxConfigPath, 'utf8');
|
||||
|
||||
console.log('验证nginx配置修复...');
|
||||
console.log('='.repeat(50));
|
||||
|
||||
// 检查是否存在路径冲突
|
||||
const apiLocationPatterns = [
|
||||
{
|
||||
name: '养殖端API (旧)',
|
||||
pattern: /location \/api\//,
|
||||
shouldExist: false,
|
||||
description: '旧的养殖端API路径,应该被移除'
|
||||
},
|
||||
{
|
||||
name: '养殖端API (新)',
|
||||
pattern: /location \^~ \/farm-api\//,
|
||||
shouldExist: true,
|
||||
description: '新的养殖端API路径'
|
||||
},
|
||||
{
|
||||
name: '政府端API',
|
||||
pattern: /location \^~ \/api\//,
|
||||
shouldExist: true,
|
||||
description: '政府端API路径'
|
||||
},
|
||||
{
|
||||
name: '银行端API',
|
||||
pattern: /location \^~ \/bank\/api\//,
|
||||
shouldExist: true,
|
||||
description: '银行端API路径'
|
||||
},
|
||||
{
|
||||
name: '保险端API',
|
||||
pattern: /location \^~ \/insurance\/api\//,
|
||||
shouldExist: true,
|
||||
description: '保险端API路径'
|
||||
}
|
||||
];
|
||||
|
||||
console.log('检查API路径配置:');
|
||||
console.log('-'.repeat(30));
|
||||
|
||||
let allCorrect = true;
|
||||
|
||||
apiLocationPatterns.forEach(config => {
|
||||
const found = config.pattern.test(nginxConfig);
|
||||
const status = found === config.shouldExist ? '✅' : '❌';
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '部门列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '数据中心接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '市场价格接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '岗位列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '养殖户列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farmers',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '登录接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
shouldWork: true,
|
||||
expectedStatus: 401
|
||||
},
|
||||
{
|
||||
name: '重复路径接口(应该404)',
|
||||
url: 'https://ad.ningmuyun.com/api/government/government/departments',
|
||||
shouldWork: false
|
||||
}
|
||||
];
|
||||
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method || 'GET',
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500;
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` 状态码: ${response.status}`);
|
||||
|
||||
if (test.shouldWork) {
|
||||
if (test.expectedStatus) {
|
||||
if (response.status === test.expectedStatus) {
|
||||
console.log(` ✅ 成功: 返回预期状态码 ${test.expectedStatus}`);
|
||||
successCount++;
|
||||
} else {
|
||||
console.log(` ❌ 失败: 预期状态码 ${test.expectedStatus},实际 ${response.status}`);
|
||||
failCount++;
|
||||
}
|
||||
} else if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
successCount++;
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 失败: 接口返回 404`);
|
||||
failCount++;
|
||||
}
|
||||
} else {
|
||||
if (response.status === 404) {
|
||||
console.log(` ✅ 成功: 正确返回 404(重复路径应该不存在)`);
|
||||
successCount++;
|
||||
} else if (response.status === 200) {
|
||||
console.log(` ❌ 失败: 重复路径不应该工作`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` 状态码: ${error.response.status}`);
|
||||
|
||||
if (test.shouldWork) {
|
||||
console.log(` ❌ 失败: ${error.response.statusText}`);
|
||||
failCount++;
|
||||
} else {
|
||||
if (error.response.status === 404) {
|
||||
console.log(` ✅ 成功: 正确返回 404`);
|
||||
successCount++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
if (found !== config.shouldExist) {
|
||||
allCorrect = false;
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log(` ✅ 成功: ${successCount} 个`);
|
||||
console.log(` ❌ 失败: ${failCount} 个`);
|
||||
console.log('');
|
||||
|
||||
if (failCount === 0) {
|
||||
console.log('🎉 所有测试通过!Nginx 配置修复成功!');
|
||||
} else {
|
||||
console.log('⚠️ 还有测试失败,请检查:');
|
||||
console.log(' 1. Nginx 配置是否正确修改');
|
||||
console.log(' 2. Nginx 服务是否已重启');
|
||||
console.log(' 3. 浏览器缓存是否已清除');
|
||||
console.log('');
|
||||
console.log('🔄 重启 Nginx:');
|
||||
console.log(' sudo nginx -t');
|
||||
console.log(' sudo systemctl reload nginx');
|
||||
console.log(`${status} ${config.name}: ${found ? '存在' : '不存在'}`);
|
||||
console.log(` ${config.description}`);
|
||||
});
|
||||
|
||||
// 检查代理目标端口
|
||||
const proxyTargets = [
|
||||
{
|
||||
name: '政府端代理目标',
|
||||
pattern: /proxy_pass http:\/\/localhost:5352\/api\//,
|
||||
expected: 'http://localhost:5352/api/'
|
||||
},
|
||||
{
|
||||
name: '养殖端代理目标',
|
||||
pattern: /proxy_pass http:\/\/localhost:5350\/api\//,
|
||||
expected: 'http://localhost:5350/api/'
|
||||
},
|
||||
{
|
||||
name: '银行端代理目标',
|
||||
pattern: /proxy_pass http:\/\/localhost:5351\/api\//,
|
||||
expected: 'http://localhost:5351/api/'
|
||||
},
|
||||
{
|
||||
name: '保险端代理目标',
|
||||
pattern: /proxy_pass http:\/\/localhost:3000\/api\//,
|
||||
expected: 'http://localhost:3000/api/'
|
||||
}
|
||||
];
|
||||
|
||||
console.log('\n检查代理目标配置:');
|
||||
console.log('-'.repeat(30));
|
||||
|
||||
proxyTargets.forEach(config => {
|
||||
const found = config.pattern.test(nginxConfig);
|
||||
const status = found ? '✅' : '❌';
|
||||
console.log(`${status} ${config.name}: ${found ? '已配置' : '未找到'}`);
|
||||
if (!found) {
|
||||
console.log(` 期望: ${config.expected}`);
|
||||
}
|
||||
});
|
||||
|
||||
// 检查是否有重复的location规则
|
||||
console.log('\n检查重复的location规则:');
|
||||
console.log('-'.repeat(30));
|
||||
|
||||
const locationMatches = nginxConfig.match(/location\s+[^{]+{/g) || [];
|
||||
const locationPaths = locationMatches.map(match => {
|
||||
const pathMatch = match.match(/location\s+([^{]+)/);
|
||||
return pathMatch ? pathMatch[1].trim() : '';
|
||||
});
|
||||
|
||||
const duplicatePaths = locationPaths.filter((path, index) =>
|
||||
locationPaths.indexOf(path) !== index && path !== ''
|
||||
);
|
||||
|
||||
if (duplicatePaths.length > 0) {
|
||||
console.log('❌ 发现重复的location规则:');
|
||||
duplicatePaths.forEach(path => {
|
||||
console.log(` - ${path}`);
|
||||
});
|
||||
allCorrect = false;
|
||||
} else {
|
||||
console.log('✅ 没有重复的location规则');
|
||||
}
|
||||
|
||||
// 运行验证
|
||||
verifyNginxFix().catch(console.error);
|
||||
console.log('\n' + '='.repeat(50));
|
||||
console.log('配置验证结果:');
|
||||
console.log('='.repeat(50));
|
||||
|
||||
if (allCorrect) {
|
||||
console.log('🎉 nginx配置修复成功!');
|
||||
console.log('✅ 路径冲突已解决');
|
||||
console.log('✅ 各端API路径正确配置');
|
||||
console.log('✅ 代理目标端口正确');
|
||||
} else {
|
||||
console.log('⚠️ nginx配置需要进一步修复');
|
||||
console.log('请检查上述错误并修复');
|
||||
}
|
||||
|
||||
console.log('\n建议的nginx重载命令:');
|
||||
console.log('sudo nginx -t && sudo nginx -s reload');
|
||||
|
||||
console.log('\n修复说明:');
|
||||
console.log('1. 将养殖端API路径从 /api/ 改为 /farm-api/');
|
||||
console.log('2. 避免与政府端 /api/ 路径冲突');
|
||||
console.log('3. 政府端API现在可以正常访问');
|
||||
console.log('4. 各端API路径互不冲突');
|
||||
152
government-backend/verify-nginx-government-config.js
Normal file
152
government-backend/verify-nginx-government-config.js
Normal file
@@ -0,0 +1,152 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// 读取nginx配置文件
|
||||
const nginxConfigPath = path.join(__dirname, 'nginx.conf');
|
||||
const nginxConfig = fs.readFileSync(nginxConfigPath, 'utf8');
|
||||
|
||||
console.log('检查政府端nginx配置...');
|
||||
console.log('='.repeat(50));
|
||||
|
||||
// 检查政府端API代理配置
|
||||
const governmentAPIConfigs = [
|
||||
{
|
||||
name: '认证API代理',
|
||||
pattern: /location \^~ \/api\/auth\//,
|
||||
expected: 'location ^~ /api/auth/'
|
||||
},
|
||||
{
|
||||
name: '监管API代理',
|
||||
pattern: /location \^~ \/api\/supervision\//,
|
||||
expected: 'location ^~ /api/supervision/'
|
||||
},
|
||||
{
|
||||
name: '疫情API代理',
|
||||
pattern: /location \^~ \/api\/epidemic\//,
|
||||
expected: 'location ^~ /api/epidemic/'
|
||||
},
|
||||
{
|
||||
name: '政府API代理',
|
||||
pattern: /location \^~ \/api\/government\//,
|
||||
expected: 'location ^~ /api/government/'
|
||||
},
|
||||
{
|
||||
name: '其他API代理',
|
||||
pattern: /location ~ \^\/api\/\(personnel\|approval\|service\|visualization\|system\|files\|smart-earmark\|smart-host\|slaughter\|harmless\|harmless-place\|epidemic-record\|vaccine\|epidemic-activity\|production-material-certification\|approval-process\|cattle-academy\|device-warning\)\//,
|
||||
expected: 'location ~ ^/api/(personnel|approval|service|...)/'
|
||||
}
|
||||
];
|
||||
|
||||
// 检查代理目标端口
|
||||
const proxyTargets = [
|
||||
{
|
||||
name: '认证API代理目标',
|
||||
pattern: /proxy_pass http:\/\/localhost:5352\/api\/auth\//,
|
||||
expected: 'http://localhost:5352/api/auth/'
|
||||
},
|
||||
{
|
||||
name: '监管API代理目标',
|
||||
pattern: /proxy_pass http:\/\/localhost:5352\/api\/supervision\//,
|
||||
expected: 'http://localhost:5352/api/supervision/'
|
||||
},
|
||||
{
|
||||
name: '疫情API代理目标',
|
||||
pattern: /proxy_pass http:\/\/localhost:5352\/api\/epidemic\//,
|
||||
expected: 'http://localhost:5352/api/epidemic/'
|
||||
},
|
||||
{
|
||||
name: '政府API代理目标',
|
||||
pattern: /proxy_pass http:\/\/localhost:5352\/api\/government\//,
|
||||
expected: 'http://localhost:5352/api/government/'
|
||||
},
|
||||
{
|
||||
name: '其他API代理目标',
|
||||
pattern: /proxy_pass http:\/\/localhost:5352\$request_uri/,
|
||||
expected: 'http://localhost:5352$request_uri'
|
||||
}
|
||||
];
|
||||
|
||||
console.log('检查API代理配置:');
|
||||
console.log('-'.repeat(30));
|
||||
|
||||
governmentAPIConfigs.forEach(config => {
|
||||
const found = config.pattern.test(nginxConfig);
|
||||
const status = found ? '✅' : '❌';
|
||||
console.log(`${status} ${config.name}: ${found ? '已配置' : '未找到'}`);
|
||||
if (!found) {
|
||||
console.log(` 期望: ${config.expected}`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('\n检查代理目标配置:');
|
||||
console.log('-'.repeat(30));
|
||||
|
||||
proxyTargets.forEach(config => {
|
||||
const found = config.pattern.test(nginxConfig);
|
||||
const status = found ? '✅' : '❌';
|
||||
console.log(`${status} ${config.name}: ${found ? '已配置' : '未找到'}`);
|
||||
if (!found) {
|
||||
console.log(` 期望: ${config.expected}`);
|
||||
}
|
||||
});
|
||||
|
||||
// 检查CORS配置
|
||||
console.log('\n检查CORS配置:');
|
||||
console.log('-'.repeat(30));
|
||||
|
||||
const corsConfigs = [
|
||||
{
|
||||
name: 'Access-Control-Allow-Origin',
|
||||
pattern: /add_header 'Access-Control-Allow-Origin' 'https:\/\/ad\.ningmuyun\.com' always;/,
|
||||
expected: 'https://ad.ningmuyun.com'
|
||||
},
|
||||
{
|
||||
name: 'Access-Control-Allow-Methods',
|
||||
pattern: /add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;/,
|
||||
expected: 'GET, POST, OPTIONS, PUT, DELETE'
|
||||
},
|
||||
{
|
||||
name: 'Access-Control-Allow-Headers',
|
||||
pattern: /add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;/,
|
||||
expected: 'Authorization, Content-Type, X-Requested-With'
|
||||
}
|
||||
];
|
||||
|
||||
corsConfigs.forEach(config => {
|
||||
const found = config.pattern.test(nginxConfig);
|
||||
const status = found ? '✅' : '❌';
|
||||
console.log(`${status} ${config.name}: ${found ? '已配置' : '未找到'}`);
|
||||
if (!found) {
|
||||
console.log(` 期望: ${config.expected}`);
|
||||
}
|
||||
});
|
||||
|
||||
// 统计配置数量
|
||||
const totalAPIConfigs = governmentAPIConfigs.length;
|
||||
const foundAPIConfigs = governmentAPIConfigs.filter(config => config.pattern.test(nginxConfig)).length;
|
||||
|
||||
const totalProxyTargets = proxyTargets.length;
|
||||
const foundProxyTargets = proxyTargets.filter(config => config.pattern.test(nginxConfig)).length;
|
||||
|
||||
const totalCorsConfigs = corsConfigs.length;
|
||||
const foundCorsConfigs = corsConfigs.filter(config => config.pattern.test(nginxConfig)).length;
|
||||
|
||||
console.log('\n' + '='.repeat(50));
|
||||
console.log('配置检查结果汇总:');
|
||||
console.log('='.repeat(50));
|
||||
console.log(`API代理配置: ${foundAPIConfigs}/${totalAPIConfigs}`);
|
||||
console.log(`代理目标配置: ${foundProxyTargets}/${totalProxyTargets}`);
|
||||
console.log(`CORS配置: ${foundCorsConfigs}/${totalCorsConfigs}`);
|
||||
|
||||
const allConfigured = foundAPIConfigs === totalAPIConfigs &&
|
||||
foundProxyTargets === totalProxyTargets &&
|
||||
foundCorsConfigs === totalCorsConfigs;
|
||||
|
||||
if (allConfigured) {
|
||||
console.log('\n🎉 所有政府端nginx配置检查通过!');
|
||||
} else {
|
||||
console.log('\n⚠️ 部分配置缺失,请检查nginx.conf文件');
|
||||
}
|
||||
|
||||
console.log('\n建议的nginx重载命令:');
|
||||
console.log('sudo nginx -t && sudo nginx -s reload');
|
||||
@@ -1,126 +0,0 @@
|
||||
# 前端API路径修复说明
|
||||
|
||||
## 问题描述
|
||||
- **错误URL**: `https://ad.ningmuyun.com/api/government/government/positions`
|
||||
- **问题**: URL中出现两个`government`,导致路径重复
|
||||
- **影响范围**: 政府端管理系统所有政府相关API接口
|
||||
|
||||
## 问题根因分析
|
||||
|
||||
### 1. 前端API配置问题 ❌
|
||||
在 `government-admin/src/utils/api.js` 中,政府相关的API接口缺少 `/api` 前缀:
|
||||
|
||||
**原始错误配置**:
|
||||
```javascript
|
||||
// 部门管理
|
||||
departments: {
|
||||
getList: (params) => instance.get('/government/departments', { params })
|
||||
},
|
||||
|
||||
// 岗位管理
|
||||
positions: {
|
||||
getList: (params) => instance.get('/government/positions', { params })
|
||||
},
|
||||
|
||||
// 养殖户管理
|
||||
farmers: {
|
||||
getList: (params) => instance.get('/government/farmers', { params }),
|
||||
create: (data) => instance.post('/government/farmers', data),
|
||||
// ... 其他方法
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 路径构造过程
|
||||
1. **前端baseURL**: `http://localhost:5352/api` (开发环境)
|
||||
2. **API调用**: `instance.get('/government/positions')`
|
||||
3. **实际请求**: `http://localhost:5352/api/government/positions` ✅ 正确
|
||||
|
||||
但在生产环境中:
|
||||
1. **前端baseURL**: `https://ad.ningmuyun.com` (生产环境)
|
||||
2. **API调用**: `instance.get('/government/positions')`
|
||||
3. **实际请求**: `https://ad.ningmuyun.com/government/positions` ❌ 缺少 `/api` 前缀
|
||||
4. **nginx匹配**: 匹配到 `location ^~ /government/` (静态文件规则)
|
||||
5. **重定向**: 可能被重写为 `/api/government/government/positions` ❌ 重复
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 修复前端API配置
|
||||
将所有政府相关API接口添加 `/api` 前缀:
|
||||
|
||||
```javascript
|
||||
// 部门管理
|
||||
departments: {
|
||||
getList: (params) => instance.get('/api/government/departments', { params })
|
||||
},
|
||||
|
||||
// 岗位管理
|
||||
positions: {
|
||||
getList: (params) => instance.get('/api/government/positions', { params })
|
||||
},
|
||||
|
||||
// 养殖户管理
|
||||
farmers: {
|
||||
getList: (params) => instance.get('/api/government/farmers', { params }),
|
||||
create: (data) => instance.post('/api/government/farmers', data),
|
||||
update: (id, data) => instance.put(`/api/government/farmers/${id}`, data),
|
||||
delete: (id) => instance.delete(`/api/government/farmers/${id}`),
|
||||
resetPassword: (id) => instance.post(`/api/government/farmers/${id}/reset-password`)
|
||||
},
|
||||
|
||||
// 养殖类型相关
|
||||
farmTypes: {
|
||||
getList: () => instance.get('/api/government/farm-types')
|
||||
},
|
||||
|
||||
// 养殖种类相关
|
||||
animalTypes: {
|
||||
getList: () => instance.get('/api/government/animal-types')
|
||||
},
|
||||
|
||||
// 智能项圈管理
|
||||
collars: {
|
||||
getList: (params) => instance.get('/api/government/collars', { params }),
|
||||
create: (data) => instance.post('/api/government/collars', data),
|
||||
update: (id, data) => instance.put(`/api/government/collars/${id}`, data),
|
||||
delete: (id) => instance.delete(`/api/government/collars/${id}`)
|
||||
}
|
||||
```
|
||||
|
||||
## 修复后的路径映射
|
||||
|
||||
| 前端调用 | 生产环境URL | nginx匹配 | 后端路由 | 状态 |
|
||||
|---------|------------|----------|---------|------|
|
||||
| `/api/government/departments` | `https://ad.ningmuyun.com/api/government/departments` | `^~ /api/government/` | `/api/government/departments` | ✅ 正确 |
|
||||
| `/api/government/positions` | `https://ad.ningmuyun.com/api/government/positions` | `^~ /api/government/` | `/api/government/positions` | ✅ 正确 |
|
||||
| `/api/government/farmers` | `https://ad.ningmuyun.com/api/government/farmers` | `^~ /api/government/` | `/api/government/farmers` | ✅ 正确 |
|
||||
|
||||
## 验证步骤
|
||||
|
||||
1. **重新构建前端项目**:
|
||||
```bash
|
||||
cd government-admin
|
||||
npm run build
|
||||
```
|
||||
|
||||
2. **部署到生产环境**:
|
||||
- 将构建后的文件部署到服务器
|
||||
- 确保nginx配置正确
|
||||
|
||||
3. **测试API接口**:
|
||||
```bash
|
||||
# 测试岗位接口
|
||||
curl https://ad.ningmuyun.com/api/government/positions
|
||||
|
||||
# 测试部门接口
|
||||
curl https://ad.ningmuyun.com/api/government/departments
|
||||
```
|
||||
|
||||
## 相关文件
|
||||
- `government-admin/src/utils/api.js` - 前端API配置文件
|
||||
- `government-backend/_etc_nginx_conf.d_ningmuyun_one.conf` - nginx配置文件
|
||||
|
||||
## 修复时间
|
||||
2024年12月19日
|
||||
|
||||
## 修复状态
|
||||
✅ 已修复 - 前端API配置已更新,添加了正确的 `/api` 前缀
|
||||
@@ -1,149 +0,0 @@
|
||||
# 检查 Nginx 配置的关键步骤
|
||||
|
||||
## 🔍 问题分析
|
||||
|
||||
根据您的反馈:
|
||||
- 修改 nginx 配置后,所有接口都返回 404
|
||||
- 只有登录接口正常(`/api/government/auth/login`)
|
||||
- 重复路径可以正常工作(`/api/government/government/departments`)
|
||||
|
||||
这说明:
|
||||
1. Nginx 已经重启并读取了新配置
|
||||
2. `/api/government/auth/` 规则工作正常
|
||||
3. `/api/government/` 规则可能有问题
|
||||
|
||||
## 🎯 关键问题
|
||||
|
||||
**重复路径能工作说明了什么?**
|
||||
|
||||
如果 `/api/government/government/departments` 能返回 200,说明:
|
||||
- 请求到达了 nginx
|
||||
- Nginx 将请求代理到了后端
|
||||
- 后端处理了 `/api/government/departments` 路径(去掉了一个 government)
|
||||
|
||||
这意味着当前的 proxy_pass 配置可能是:
|
||||
```nginx
|
||||
proxy_pass http://localhost:5352/api/government/;
|
||||
```
|
||||
|
||||
这会导致:
|
||||
- 请求:`/api/government/government/departments`
|
||||
- Nginx 匹配:`location ^~ /api/government/`
|
||||
- 代理到:`http://localhost:5352/api/government/ + government/departments`
|
||||
- 实际请求:`http://localhost:5352/api/government/government/departments`
|
||||
|
||||
但是后端路由是 `/api/government/departments`,所以单个 government 的路径会 404。
|
||||
|
||||
## 🔧 正确的解决方案
|
||||
|
||||
需要修改 `proxy_pass` 配置,去掉路径重写:
|
||||
|
||||
```nginx
|
||||
# 错误的配置(当前)
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352/api/government/;
|
||||
# 这会导致路径重复:/api/government/departments → http://localhost:5352/api/government/departments
|
||||
}
|
||||
|
||||
# 正确的配置
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352;
|
||||
# 这会保持原始路径:/api/government/departments → http://localhost:5352/api/government/departments
|
||||
}
|
||||
```
|
||||
|
||||
## 📝 修改步骤
|
||||
|
||||
### 1. 修改 nginx 配置文件
|
||||
|
||||
找到 `_etc_nginx_conf.d_ningmuyun_one.conf` 文件中的这一段:
|
||||
|
||||
```nginx
|
||||
# 政府API代理 - 处理其他政府相关接口
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352/api/government/; # ❌ 错误
|
||||
# ... 其他配置
|
||||
}
|
||||
```
|
||||
|
||||
修改为:
|
||||
|
||||
```nginx
|
||||
# 政府API代理 - 处理其他政府相关接口
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352; # ✅ 正确
|
||||
# ... 其他配置
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 重启 nginx
|
||||
|
||||
```bash
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
### 3. 验证修复
|
||||
|
||||
```bash
|
||||
# 应该返回 200
|
||||
curl -X GET https://ad.ningmuyun.com/api/government/departments
|
||||
|
||||
# 应该返回 404(因为路径不再重复)
|
||||
curl -X GET https://ad.ningmuyun.com/api/government/government/departments
|
||||
```
|
||||
|
||||
## 🎯 proxy_pass 尾部斜杠的区别
|
||||
|
||||
这是一个非常重要的 nginx 配置细节:
|
||||
|
||||
### 有尾部斜杠
|
||||
```nginx
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352/api/government/;
|
||||
}
|
||||
```
|
||||
- 请求:`/api/government/departments`
|
||||
- 匹配部分:`/api/government/`
|
||||
- 剩余部分:`departments`
|
||||
- 代理到:`http://localhost:5352/api/government/departments`
|
||||
|
||||
### 无尾部斜杠
|
||||
```nginx
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352;
|
||||
}
|
||||
```
|
||||
- 请求:`/api/government/departments`
|
||||
- 代理到:`http://localhost:5352/api/government/departments`(保持完整路径)
|
||||
|
||||
## 📊 当前配置应该是
|
||||
|
||||
```nginx
|
||||
# 政府认证API代理
|
||||
location ^~ /api/government/auth/ {
|
||||
proxy_pass http://localhost:5352/api/auth/; # ✅ 正确(需要去掉 government)
|
||||
# ... 其他配置
|
||||
}
|
||||
|
||||
# 政府API代理
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352; # ✅ 正确(保持完整路径)
|
||||
# ... 其他配置
|
||||
}
|
||||
```
|
||||
|
||||
## 🚨 总结
|
||||
|
||||
修改配置文件,将:
|
||||
```nginx
|
||||
proxy_pass http://localhost:5352/api/government/;
|
||||
```
|
||||
|
||||
改为:
|
||||
```nginx
|
||||
proxy_pass http://localhost:5352;
|
||||
```
|
||||
|
||||
然后重启 nginx,问题应该就能解决了。
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
# 政府端登录接口404错误修复说明
|
||||
|
||||
## 问题描述
|
||||
- **错误URL**: `https://ad.ningmuyun.com/api/government/auth/login`
|
||||
- **错误状态**: 404 Not Found
|
||||
- **影响范围**: 政府端管理系统登录功能
|
||||
|
||||
## 问题根因分析
|
||||
|
||||
### 1. 前端API调用路径
|
||||
- **前端调用**: `https://ad.ningmuyun.com/api/government/auth/login`
|
||||
- **API定义**: `government-admin/src/utils/api.js` 第74行
|
||||
```javascript
|
||||
login: (data) => instance.post('/auth/login', data)
|
||||
```
|
||||
- **baseURL**: `http://localhost:5352/api` (开发环境)
|
||||
- **实际请求**: `http://localhost:5352/api/auth/login`
|
||||
|
||||
### 2. 后端路由配置 ✅
|
||||
- **认证路由**: `government-backend/routes/auth.js` 第6行
|
||||
```javascript
|
||||
router.post('/login', login)
|
||||
```
|
||||
- **应用路由注册**: `government-backend/app.js` 第39行
|
||||
```javascript
|
||||
app.use('/api/auth', require('./routes/auth'));
|
||||
```
|
||||
- **完整路径**: `/api/auth/login`
|
||||
|
||||
### 3. Nginx配置问题 ❌
|
||||
**问题所在**: nginx配置中缺少政府认证API的路径匹配
|
||||
|
||||
**原始配置问题**:
|
||||
- 只有 `/api/government/` 的代理配置
|
||||
- 缺少 `/api/government/auth/` 的专门代理配置
|
||||
- 导致 `/api/government/auth/login` 请求无法正确路由到后端的 `/api/auth/login`
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 修复nginx配置
|
||||
在 `government-backend/_etc_nginx_conf.d_ningmuyun_one.conf` 中添加政府认证API代理配置:
|
||||
|
||||
```nginx
|
||||
# 政府认证API代理 - 处理登录等认证接口
|
||||
location ^~ /api/government/auth/ {
|
||||
proxy_pass http://localhost:5352/api/auth/; # 政府后端认证服务端口
|
||||
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;
|
||||
|
||||
# CORS配置
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
|
||||
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With';
|
||||
add_header 'Access-Control-Allow-Credentials' 'true';
|
||||
add_header 'Content-Type' 'text/plain; charset=UTF-8';
|
||||
add_header 'Content-Length' 0;
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 路径映射说明
|
||||
- **前端请求**: `/api/government/auth/login`
|
||||
- **nginx匹配**: `location ^~ /api/government/auth/`
|
||||
- **代理转发**: `http://localhost:5352/api/auth/login`
|
||||
- **后端处理**: `/api/auth/login` (authController.login)
|
||||
|
||||
## 验证步骤
|
||||
|
||||
1. **重启nginx服务**:
|
||||
```bash
|
||||
sudo nginx -t # 检查配置语法
|
||||
sudo systemctl reload nginx # 重新加载配置
|
||||
```
|
||||
|
||||
2. **测试登录接口**:
|
||||
```bash
|
||||
curl -X POST https://ad.ningmuyun.com/api/government/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"test","password":"test"}'
|
||||
```
|
||||
|
||||
3. **检查前端登录功能**:
|
||||
- 访问政府端管理系统登录页面
|
||||
- 输入正确的用户名和密码
|
||||
- 确认登录成功
|
||||
|
||||
## 相关文件
|
||||
- `government-backend/_etc_nginx_conf.d_ningmuyun_one.conf` - nginx配置文件
|
||||
- `government-backend/routes/auth.js` - 认证路由
|
||||
- `government-backend/controllers/authController.js` - 认证控制器
|
||||
- `government-admin/src/utils/api.js` - 前端API配置
|
||||
- `government-admin/src/stores/auth.js` - 前端认证状态管理
|
||||
|
||||
## 修复时间
|
||||
2024年12月19日
|
||||
|
||||
## 修复状态
|
||||
✅ 已修复 - nginx配置已更新,添加了政府认证API代理配置
|
||||
|
||||
## 重要说明
|
||||
**配置优先级**: 在nginx中,更具体的location规则会优先匹配。因此:
|
||||
- `/api/government/auth/` 配置必须在 `/api/government/` 配置之前
|
||||
- 这样 `/api/government/auth/login` 会匹配到第一个规则,正确代理到 `/api/auth/login`
|
||||
- 其他 `/api/government/` 开头的请求会匹配到第二个规则
|
||||
|
||||
## 测试方法
|
||||
运行测试脚本验证修复效果:
|
||||
```bash
|
||||
cd government-backend
|
||||
node test-login-api.js
|
||||
```
|
||||
|
||||
## 重启nginx命令
|
||||
```bash
|
||||
sudo nginx -t # 检查配置语法
|
||||
sudo systemctl reload nginx # 重新加载配置
|
||||
```
|
||||
@@ -155,3 +155,4 @@ government-mini-program/
|
||||
|
||||
**版本**:v1.0.0
|
||||
**更新时间**:2024年1月
|
||||
|
||||
|
||||
@@ -17,3 +17,4 @@ App({
|
||||
userInfo: null
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/**app.wxss**/
|
||||
/* 全局容器 */
|
||||
.container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
@@ -11,29 +12,235 @@
|
||||
|
||||
/* 全局样式 */
|
||||
page {
|
||||
background-color: #f5f5f5;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', SimSun, sans-serif;
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
/* 通用按钮样式 */
|
||||
.btn {
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
padding: 20rpx 40rpx;
|
||||
/* 主题色彩变量 */
|
||||
:root {
|
||||
--primary-color: #4CAF50;
|
||||
--primary-light: #81C784;
|
||||
--primary-dark: #388E3C;
|
||||
--secondary-color: #2196F3;
|
||||
--accent-color: #FF9800;
|
||||
--success-color: #4CAF50;
|
||||
--warning-color: #FF9800;
|
||||
--error-color: #F44336;
|
||||
--text-primary: #2c3e50;
|
||||
--text-secondary: #5a6c7d;
|
||||
--text-muted: #95a5a6;
|
||||
--background-primary: #ffffff;
|
||||
--background-secondary: #f8f9fa;
|
||||
--border-color: rgba(76, 175, 80, 0.1);
|
||||
--shadow-light: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
--shadow-medium: 0 8rpx 25rpx rgba(76, 175, 80, 0.15);
|
||||
--shadow-heavy: 0 12rpx 40rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 卡片样式 */
|
||||
/* 通用卡片样式 */
|
||||
.card {
|
||||
background-color: #ffffff;
|
||||
border-radius: 12rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||
margin: 20rpx;
|
||||
padding: 30rpx;
|
||||
background: linear-gradient(145deg, #ffffff, #f8f9fa);
|
||||
border-radius: 20rpx;
|
||||
padding: 35rpx;
|
||||
margin-bottom: 25rpx;
|
||||
box-shadow: var(--shadow-light);
|
||||
border: 2rpx solid var(--border-color);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4rpx;
|
||||
background: linear-gradient(90deg, var(--primary-color), var(--primary-light));
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-4rpx);
|
||||
box-shadow: var(--shadow-medium);
|
||||
}
|
||||
|
||||
/* 文字样式 */
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 25rpx;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 20rpx;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 28rpx;
|
||||
color: var(--text-muted);
|
||||
line-height: 1.6;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* 按钮样式 */
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 25rpx 45rpx;
|
||||
border-radius: 18rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: var(--shadow-light);
|
||||
}
|
||||
|
||||
.btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
transition: left 0.5s ease;
|
||||
}
|
||||
|
||||
.btn:active::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.btn.primary {
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--primary-dark));
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.btn.primary:active {
|
||||
transform: translateY(2rpx);
|
||||
box-shadow: var(--shadow-medium);
|
||||
}
|
||||
|
||||
.btn.success {
|
||||
background: linear-gradient(135deg, var(--success-color), #45a049);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.btn.warning {
|
||||
background: linear-gradient(135deg, var(--warning-color), #f57c00);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.btn.danger {
|
||||
background: linear-gradient(135deg, var(--error-color), #d32f2f);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* 布局工具类 */
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex.center {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flex.between {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex.around {
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 50rpx;
|
||||
color: var(--text-muted);
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 100rpx 40rpx;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 100rpx;
|
||||
margin-bottom: 25rpx;
|
||||
opacity: 0.6;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 分割线样式 */
|
||||
.divider {
|
||||
height: 2rpx;
|
||||
background-color: #e0e0e0;
|
||||
margin: 20rpx 0;
|
||||
background: linear-gradient(90deg, transparent, var(--border-color), transparent);
|
||||
margin: 30rpx 0;
|
||||
}
|
||||
|
||||
/* 标签样式 */
|
||||
.tag {
|
||||
display: inline-block;
|
||||
padding: 12rpx 20rpx;
|
||||
border-radius: 25rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
background: var(--background-secondary);
|
||||
color: var(--text-secondary);
|
||||
border: 2rpx solid var(--border-color);
|
||||
}
|
||||
|
||||
.tag.primary {
|
||||
background: linear-gradient(135deg, #e8f5e8, #f1f8e9);
|
||||
color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.tag.success {
|
||||
background: linear-gradient(135deg, #e8f5e8, #f1f8e9);
|
||||
color: var(--success-color);
|
||||
border-color: var(--success-color);
|
||||
}
|
||||
|
||||
.tag.warning {
|
||||
background: linear-gradient(135deg, #fff8e1, #fffbf0);
|
||||
color: var(--warning-color);
|
||||
border-color: var(--warning-color);
|
||||
}
|
||||
|
||||
.tag.danger {
|
||||
background: linear-gradient(135deg, #ffebee, #fef5f5);
|
||||
color: var(--error-color);
|
||||
border-color: var(--error-color);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// API配置文件
|
||||
const config = {
|
||||
// 后端服务器配置
|
||||
baseURL: 'https://ad.ningmuyun.com/api',
|
||||
baseURL: 'http://localhost:5352/api',
|
||||
|
||||
// 请求超时时间
|
||||
timeout: 10000,
|
||||
@@ -10,7 +10,7 @@ const config = {
|
||||
endpoints: {
|
||||
// 认证相关
|
||||
auth: {
|
||||
login: '/government/auth/login',
|
||||
login: '/auth/login',
|
||||
logout: '/auth/logout',
|
||||
userInfo: '/auth/userinfo'
|
||||
},
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
|
||||
@@ -17,3 +17,4 @@
|
||||
"dependencies": {},
|
||||
"devDependencies": {}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,70 +1,34 @@
|
||||
// pages/farmer/farmer.js
|
||||
const app = getApp();
|
||||
const apiService = require('../../utils/api.js');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 0,
|
||||
loading: true,
|
||||
mode: 'view', // view, edit, add
|
||||
farmerId: null,
|
||||
farmerInfo: {},
|
||||
formData: {},
|
||||
animalTypes: ['牛羊混养', '肉牛养殖', '奶牛养殖', '羊养殖', '其他'],
|
||||
animalTypeIndex: 0,
|
||||
loading: false
|
||||
},
|
||||
|
||||
onLoad: function (options) {
|
||||
this.checkLoginStatus();
|
||||
|
||||
// 搜索和筛选
|
||||
searchKeyword: '',
|
||||
selectedStatus: 'all',
|
||||
selectedArea: 'all',
|
||||
|
||||
// 筛选选项
|
||||
statusOptions: [
|
||||
{ value: 'all', label: '全部状态' },
|
||||
{ value: 'normal', label: '正常' },
|
||||
{ value: 'warning', label: '异常' },
|
||||
{ value: 'offline', label: '离线' }
|
||||
],
|
||||
|
||||
areaOptions: [
|
||||
{ value: 'all', label: '全部区域' },
|
||||
{ value: 'area1', label: '银川市' },
|
||||
{ value: 'area2', label: '石嘴山市' },
|
||||
{ value: 'area3', label: '吴忠市' },
|
||||
{ value: 'area4', label: '固原市' },
|
||||
{ value: 'area5', label: '中卫市' }
|
||||
],
|
||||
|
||||
// 养殖户列表
|
||||
farmerList: [],
|
||||
|
||||
// 分页
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
hasMore: true,
|
||||
|
||||
// 统计信息
|
||||
statistics: {
|
||||
total: 0,
|
||||
normal: 0,
|
||||
warning: 0,
|
||||
offline: 0
|
||||
// 根据参数确定页面模式
|
||||
if (options.mode === 'add') {
|
||||
this.setData({
|
||||
mode: 'add'
|
||||
});
|
||||
this.initFormData();
|
||||
} else if (options.id) {
|
||||
this.setData({
|
||||
mode: 'view',
|
||||
farmerId: options.id
|
||||
});
|
||||
this.loadFarmerInfo(options.id);
|
||||
}
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
// 获取状态栏高度
|
||||
const systemInfo = wx.getSystemInfoSync();
|
||||
this.setData({
|
||||
statusBarHeight: systemInfo.statusBarHeight
|
||||
});
|
||||
|
||||
// 检查登录状态
|
||||
this.checkLoginStatus();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 每次显示页面时刷新数据
|
||||
this.loadData();
|
||||
},
|
||||
|
||||
// 检查登录状态
|
||||
checkLoginStatus() {
|
||||
checkLoginStatus: function() {
|
||||
const token = wx.getStorageSync('token');
|
||||
if (!token) {
|
||||
wx.reLaunch({
|
||||
@@ -75,268 +39,182 @@ Page({
|
||||
return true;
|
||||
},
|
||||
|
||||
// 加载数据
|
||||
async loadData(isRefresh = true) {
|
||||
if (!this.checkLoginStatus()) return;
|
||||
|
||||
if (isRefresh) {
|
||||
this.setData({
|
||||
loading: true,
|
||||
currentPage: 1,
|
||||
farmerList: [],
|
||||
hasMore: true
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
await this.loadFarmers();
|
||||
} catch (error) {
|
||||
console.error('数据加载失败:', error);
|
||||
// 加载模拟数据作为备用
|
||||
this.loadMockData();
|
||||
} finally {
|
||||
this.setData({ loading: false });
|
||||
}
|
||||
},
|
||||
// 加载养殖户信息
|
||||
loadFarmerInfo: function(farmerId) {
|
||||
this.setData({ loading: true });
|
||||
|
||||
// 加载养殖户列表
|
||||
async loadFarmers() {
|
||||
try {
|
||||
const params = {
|
||||
page: this.data.currentPage,
|
||||
pageSize: this.data.pageSize,
|
||||
keyword: this.data.searchKeyword,
|
||||
status: this.data.selectedStatus === 'all' ? '' : this.data.selectedStatus,
|
||||
area: this.data.selectedArea === 'all' ? '' : this.data.selectedArea
|
||||
};
|
||||
|
||||
const result = await apiService.getFarmers(params);
|
||||
|
||||
if (result.code === 200 || result.code === 0) {
|
||||
const data = result.data || result;
|
||||
const farmers = data.list || data.farmers || [];
|
||||
|
||||
// 处理数据格式
|
||||
const formattedFarmers = farmers.map(farmer => ({
|
||||
id: farmer.id,
|
||||
name: farmer.name || farmer.farmerName,
|
||||
phone: farmer.phone || farmer.mobile,
|
||||
area: farmer.area || farmer.region,
|
||||
farmName: farmer.farmName || farmer.farm_name,
|
||||
animalCount: farmer.animalCount || farmer.animal_count || 0,
|
||||
animalType: farmer.animalType || farmer.animal_type,
|
||||
status: farmer.status || 'normal',
|
||||
lastCheckTime: farmer.lastCheckTime || farmer.last_check_time,
|
||||
avatar: farmer.avatar || '/images/avatar.png'
|
||||
}));
|
||||
|
||||
// 更新列表数据
|
||||
if (this.data.currentPage === 1) {
|
||||
this.setData({
|
||||
farmerList: formattedFarmers,
|
||||
totalCount: data.total || farmers.length
|
||||
});
|
||||
} else {
|
||||
this.setData({
|
||||
farmerList: [...this.data.farmerList, ...formattedFarmers]
|
||||
});
|
||||
}
|
||||
|
||||
// 更新分页状态
|
||||
this.setData({
|
||||
hasMore: formattedFarmers.length >= this.data.pageSize
|
||||
});
|
||||
|
||||
// 更新统计信息
|
||||
if (data.statistics) {
|
||||
this.setData({
|
||||
statistics: data.statistics
|
||||
});
|
||||
} else {
|
||||
this.calculateStatistics();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取养殖户列表失败:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// 加载模拟数据
|
||||
loadMockData() {
|
||||
const mockFarmers = [
|
||||
{
|
||||
id: 1,
|
||||
name: '张三',
|
||||
phone: '138****1234',
|
||||
area: '银川市',
|
||||
farmName: '张家养殖场',
|
||||
animalCount: 120,
|
||||
animalType: '牛',
|
||||
status: 'normal',
|
||||
lastCheckTime: '2024-01-15 10:30',
|
||||
avatar: '/images/avatar.png'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '李四',
|
||||
phone: '139****5678',
|
||||
area: '石嘴山市',
|
||||
farmName: '李氏牧场',
|
||||
animalCount: 85,
|
||||
animalType: '羊',
|
||||
status: 'warning',
|
||||
lastCheckTime: '2024-01-14 16:20',
|
||||
avatar: '/images/avatar.png'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '王五',
|
||||
phone: '137****9012',
|
||||
area: '吴忠市',
|
||||
farmName: '王家农场',
|
||||
animalCount: 200,
|
||||
animalType: '牛',
|
||||
status: 'normal',
|
||||
lastCheckTime: '2024-01-15 09:15',
|
||||
avatar: '/images/avatar.png'
|
||||
}
|
||||
];
|
||||
|
||||
this.setData({
|
||||
farmerList: mockFarmers,
|
||||
totalCount: mockFarmers.length,
|
||||
hasMore: false,
|
||||
statistics: {
|
||||
total: 3,
|
||||
normal: 2,
|
||||
warning: 1,
|
||||
offline: 0
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 计算统计信息
|
||||
calculateStatistics() {
|
||||
const farmers = this.data.farmerList;
|
||||
const statistics = {
|
||||
total: farmers.length,
|
||||
normal: farmers.filter(f => f.status === 'normal').length,
|
||||
warning: farmers.filter(f => f.status === 'warning').length,
|
||||
offline: farmers.filter(f => f.status === 'offline').length
|
||||
// 模拟数据
|
||||
const mockFarmer = {
|
||||
id: farmerId,
|
||||
name: '张三',
|
||||
phone: '13800138001',
|
||||
address: '宁夏银川市兴庆区某某村123号',
|
||||
farmName: '张三养殖场',
|
||||
animalCount: 120,
|
||||
status: 'active',
|
||||
registerTime: '2023-01-15',
|
||||
animalType: '牛羊混养',
|
||||
farmArea: '500',
|
||||
lastCheckTime: '2024-01-15'
|
||||
};
|
||||
|
||||
setTimeout(() => {
|
||||
this.setData({
|
||||
farmerInfo: mockFarmer,
|
||||
loading: false
|
||||
});
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
// 初始化表单数据
|
||||
initFormData: function() {
|
||||
this.setData({
|
||||
formData: {
|
||||
name: '',
|
||||
farmName: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
animalCount: '',
|
||||
animalType: '',
|
||||
farmArea: ''
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 返回上一页
|
||||
onBack: function() {
|
||||
wx.navigateBack();
|
||||
},
|
||||
|
||||
// 编辑模式
|
||||
onEdit: function() {
|
||||
const { farmerInfo } = this.data;
|
||||
this.setData({
|
||||
mode: 'edit',
|
||||
formData: {
|
||||
name: farmerInfo.name,
|
||||
farmName: farmerInfo.farmName,
|
||||
phone: farmerInfo.phone,
|
||||
address: farmerInfo.address,
|
||||
animalCount: farmerInfo.animalCount.toString(),
|
||||
animalType: farmerInfo.animalType,
|
||||
farmArea: farmerInfo.farmArea
|
||||
},
|
||||
animalTypeIndex: this.data.animalTypes.indexOf(farmerInfo.animalType)
|
||||
});
|
||||
},
|
||||
|
||||
// 输入框变化
|
||||
onInputChange: function(e) {
|
||||
const field = e.currentTarget.dataset.field;
|
||||
const value = e.detail.value;
|
||||
this.setData({
|
||||
[`formData.${field}`]: value
|
||||
});
|
||||
},
|
||||
|
||||
// 选择器变化
|
||||
onPickerChange: function(e) {
|
||||
const field = e.currentTarget.dataset.field;
|
||||
const index = e.detail.value;
|
||||
const value = this.data.animalTypes[index];
|
||||
|
||||
this.setData({ statistics });
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
this.loadData(true).finally(() => {
|
||||
wx.stopPullDownRefresh();
|
||||
});
|
||||
},
|
||||
|
||||
onReachBottom() {
|
||||
this.onLoadMore();
|
||||
},
|
||||
|
||||
|
||||
|
||||
// 搜索输入
|
||||
onSearchInput(e) {
|
||||
this.setData({
|
||||
searchKeyword: e.detail.value
|
||||
[`formData.${field}`]: value,
|
||||
animalTypeIndex: index
|
||||
});
|
||||
},
|
||||
|
||||
// 执行搜索
|
||||
onSearch() {
|
||||
this.loadData(true);
|
||||
},
|
||||
|
||||
// 清空搜索
|
||||
onClearSearch() {
|
||||
this.setData({
|
||||
searchKeyword: ''
|
||||
});
|
||||
this.loadData(true);
|
||||
},
|
||||
|
||||
// 状态筛选
|
||||
onStatusChange(e) {
|
||||
const status = e.currentTarget.dataset.status;
|
||||
this.setData({
|
||||
selectedStatus: status
|
||||
});
|
||||
this.loadData(true);
|
||||
},
|
||||
|
||||
// 区域筛选
|
||||
onAreaChange(e) {
|
||||
const area = e.currentTarget.dataset.area;
|
||||
this.setData({
|
||||
selectedArea: area
|
||||
});
|
||||
this.loadData(true);
|
||||
},
|
||||
|
||||
// 养殖户项点击
|
||||
onFarmerTap(e) {
|
||||
const { farmer } = e.currentTarget.dataset;
|
||||
wx.navigateTo({
|
||||
url: `/pages/farmer/detail/detail?id=${farmer.id}`
|
||||
});
|
||||
},
|
||||
|
||||
// 添加养殖户
|
||||
onAddFarmer() {
|
||||
wx.navigateTo({
|
||||
url: '/pages/farmer/add/add'
|
||||
});
|
||||
},
|
||||
|
||||
// 编辑养殖户
|
||||
onEditFarmer(e) {
|
||||
e.stopPropagation();
|
||||
const { farmer } = e.currentTarget.dataset;
|
||||
wx.navigateTo({
|
||||
url: `/pages/farmer/edit/edit?id=${farmer.id}`
|
||||
});
|
||||
},
|
||||
|
||||
// 拨打电话
|
||||
onCallFarmer(e) {
|
||||
e.stopPropagation();
|
||||
const { farmer } = e.currentTarget.dataset;
|
||||
// 保存数据
|
||||
onSave: function() {
|
||||
const { formData, mode } = this.data;
|
||||
|
||||
// 验证必填字段
|
||||
if (!formData.name || !formData.farmName || !formData.phone || !formData.address || !formData.animalCount) {
|
||||
wx.showToast({
|
||||
title: '请填写完整信息',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证手机号
|
||||
const phoneRegex = /^1[3-9]\d{9}$/;
|
||||
if (!phoneRegex.test(formData.phone)) {
|
||||
wx.showToast({
|
||||
title: '请输入正确的手机号',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
wx.showLoading({
|
||||
title: mode === 'add' ? '保存中...' : '更新中...'
|
||||
});
|
||||
|
||||
// 模拟保存
|
||||
setTimeout(() => {
|
||||
wx.hideLoading();
|
||||
wx.showToast({
|
||||
title: mode === 'add' ? '保存成功' : '更新成功',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
if (mode === 'add') {
|
||||
wx.navigateBack();
|
||||
} else {
|
||||
// 更新模式,重新加载数据并切换到查看模式
|
||||
this.loadFarmerInfo(this.data.farmerId);
|
||||
this.setData({ mode: 'view' });
|
||||
}
|
||||
}, 1500);
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
// 取消编辑
|
||||
onCancel: function() {
|
||||
if (this.data.mode === 'add') {
|
||||
wx.navigateBack();
|
||||
} else {
|
||||
this.setData({ mode: 'view' });
|
||||
}
|
||||
},
|
||||
|
||||
// 删除养殖户
|
||||
onDelete: function() {
|
||||
wx.showModal({
|
||||
title: '拨打电话',
|
||||
content: `确定要拨打 ${farmer.name} 的电话吗?`,
|
||||
title: '确认删除',
|
||||
content: '确定要删除这个养殖户吗?此操作不可恢复。',
|
||||
confirmText: '删除',
|
||||
confirmColor: '#F44336',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
wx.makePhoneCall({
|
||||
phoneNumber: farmer.phone.replace(/\*/g, '1') // 替换掩码
|
||||
wx.showLoading({
|
||||
title: '删除中...'
|
||||
});
|
||||
|
||||
// 模拟删除
|
||||
setTimeout(() => {
|
||||
wx.hideLoading();
|
||||
wx.showToast({
|
||||
title: '删除成功',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
wx.navigateBack();
|
||||
}, 1500);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 加载更多
|
||||
onLoadMore() {
|
||||
if (!this.data.hasMore || this.data.loading) return;
|
||||
|
||||
this.setData({
|
||||
currentPage: this.data.currentPage + 1
|
||||
});
|
||||
|
||||
this.loadData(false);
|
||||
},
|
||||
|
||||
// 分享页面
|
||||
onShareAppMessage() {
|
||||
onShareAppMessage: function() {
|
||||
return {
|
||||
title: '政府监管端 - 养殖户管理',
|
||||
path: '/pages/farmer/farmer'
|
||||
title: '养殖户详情',
|
||||
path: `/pages/farmer/farmer?id=${this.data.farmerId}`
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -1,139 +1,179 @@
|
||||
<!--pages/farmer/farmer.wxml-->
|
||||
<view class="container">
|
||||
<!-- 状态栏 -->
|
||||
<view class="status-bar">
|
||||
<view class="status-left">
|
||||
<text>17:00</text>
|
||||
</view>
|
||||
<view class="status-right">
|
||||
<text>100%</text>
|
||||
<view class="battery-icon"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 顶部标题栏 -->
|
||||
<view class="farmer-container">
|
||||
<!-- 标题栏 -->
|
||||
<view class="header">
|
||||
<view class="header-title">养殖户管理</view>
|
||||
<view class="header-actions">
|
||||
<view class="action-btn" bindtap="onMenuTap">
|
||||
<text class="icon-more">⋯</text>
|
||||
<view class="header-left">
|
||||
<view class="back-btn" bindtap="onBack">
|
||||
<text class="back-icon">←</text>
|
||||
</view>
|
||||
<view class="action-btn">
|
||||
<text class="icon-minus">−</text>
|
||||
</view>
|
||||
<view class="action-btn">
|
||||
<text class="icon-circle">●</text>
|
||||
<text class="title">{{mode === 'add' ? '新增养殖户' : mode === 'edit' ? '编辑养殖户' : '养殖户详情'}}</text>
|
||||
</view>
|
||||
<view class="header-right" wx:if="{{mode === 'view'}}">
|
||||
<view class="action-btn" bindtap="onEdit">
|
||||
<text class="edit-icon">✏️</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<view class="search-section">
|
||||
<view class="search-box">
|
||||
<view class="search-icon">🔍</view>
|
||||
<input
|
||||
class="search-input"
|
||||
placeholder="请输入账号、昵称、真实"
|
||||
value="{{searchValue}}"
|
||||
bindinput="onSearchInput"
|
||||
bindconfirm="onSearch"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 养殖户列表 -->
|
||||
<view class="farmer-list">
|
||||
<view
|
||||
class="farmer-card"
|
||||
wx:for="{{farmerList}}"
|
||||
wx:key="id"
|
||||
data-farmer="{{item}}"
|
||||
bindtap="viewFarmerDetail"
|
||||
>
|
||||
<!-- 养殖户基本信息 -->
|
||||
<view class="farmer-info">
|
||||
<view class="farmer-header">
|
||||
<view class="farmer-name">{{item.name}}</view>
|
||||
<view class="farmer-status {{item.status === '正常' ? 'status-normal' : item.status === '异常' ? 'status-error' : 'status-pending'}}">
|
||||
{{item.status}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="farmer-details">
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">手机号码:</text>
|
||||
<text class="detail-value">{{item.phone}}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">所在地区:</text>
|
||||
<text class="detail-value">{{item.area}}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">养殖场名:</text>
|
||||
<text class="detail-value">{{item.farmName}}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">牲畜数量:</text>
|
||||
<text class="detail-value">{{item.animalCount}}头</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">注册时间:</text>
|
||||
<text class="detail-value">{{item.registerTime}}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">最近检查:</text>
|
||||
<text class="detail-value">{{item.lastCheckTime}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 养殖户详情卡片 -->
|
||||
<view class="farmer-detail-card" wx:if="{{mode === 'view'}}">
|
||||
<view class="card-header">
|
||||
<view class="farmer-avatar">
|
||||
<text class="avatar-text">{{farmerInfo.name.charAt(0)}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="farmer-actions">
|
||||
<view
|
||||
class="action-btn edit-btn"
|
||||
data-farmer="{{item}}"
|
||||
bindtap="editFarmer"
|
||||
catchtap="true"
|
||||
>
|
||||
<text class="action-icon">✏️</text>
|
||||
<text class="action-text">编辑</text>
|
||||
</view>
|
||||
<view
|
||||
class="action-btn status-btn"
|
||||
data-farmer="{{item}}"
|
||||
bindtap="toggleFarmerStatus"
|
||||
catchtap="true"
|
||||
>
|
||||
<text class="action-icon">🔄</text>
|
||||
<text class="action-text">{{item.status === '正常' ? '标记异常' : '标记正常'}}</text>
|
||||
</view>
|
||||
<view
|
||||
class="action-btn delete-btn"
|
||||
data-farmer="{{item}}"
|
||||
bindtap="deleteFarmer"
|
||||
catchtap="true"
|
||||
>
|
||||
<text class="action-icon">🗑️</text>
|
||||
<text class="action-text">删除</text>
|
||||
<view class="farmer-basic">
|
||||
<view class="farmer-name">{{farmerInfo.name}}</view>
|
||||
<view class="status-tag {{farmerInfo.status}}">
|
||||
<text>{{farmerInfo.status === 'active' ? '正常' : farmerInfo.status === 'pending' ? '待审核' : '停用'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="empty-state" wx:if="{{farmerList.length === 0 && !loading}}">
|
||||
<text class="empty-text">暂无养殖户数据</text>
|
||||
</view>
|
||||
<view class="card-content">
|
||||
<view class="info-section">
|
||||
<view class="section-title">基本信息</view>
|
||||
<view class="info-grid">
|
||||
<view class="info-item">
|
||||
<text class="info-label">农场名称</text>
|
||||
<text class="info-value">{{farmerInfo.farmName}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">联系电话</text>
|
||||
<text class="info-value">{{farmerInfo.phone}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">农场地址</text>
|
||||
<text class="info-value">{{farmerInfo.address}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">注册时间</text>
|
||||
<text class="info-value">{{farmerInfo.registerTime}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view class="loading-state" wx:if="{{loading}}">
|
||||
<text>加载中...</text>
|
||||
<view class="info-section">
|
||||
<view class="section-title">养殖信息</view>
|
||||
<view class="info-grid">
|
||||
<view class="info-item">
|
||||
<text class="info-label">牲畜数量</text>
|
||||
<text class="info-value highlight">{{farmerInfo.animalCount}}头</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">养殖类型</text>
|
||||
<text class="info-value">{{farmerInfo.animalType || '牛羊混养'}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">农场面积</text>
|
||||
<text class="info-value">{{farmerInfo.farmArea || '500亩'}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">最近检查</text>
|
||||
<text class="info-value">{{farmerInfo.lastCheckTime || '2024-01-15'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部新增按钮 -->
|
||||
<view class="bottom-section">
|
||||
<view class="add-farmer-btn" bindtap="addFarmer">
|
||||
新增监管养殖户
|
||||
<!-- 表单编辑模式 -->
|
||||
<view class="farmer-form" wx:if="{{mode === 'add' || mode === 'edit'}}">
|
||||
<view class="form-section">
|
||||
<view class="section-title">基本信息</view>
|
||||
<view class="form-group">
|
||||
<text class="form-label">养殖户姓名 <text class="required">*</text></text>
|
||||
<input
|
||||
class="form-input"
|
||||
placeholder="请输入养殖户姓名"
|
||||
value="{{formData.name}}"
|
||||
bindinput="onInputChange"
|
||||
data-field="name"
|
||||
/>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<text class="form-label">农场名称 <text class="required">*</text></text>
|
||||
<input
|
||||
class="form-input"
|
||||
placeholder="请输入农场名称"
|
||||
value="{{formData.farmName}}"
|
||||
bindinput="onInputChange"
|
||||
data-field="farmName"
|
||||
/>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<text class="form-label">联系电话 <text class="required">*</text></text>
|
||||
<input
|
||||
class="form-input"
|
||||
placeholder="请输入联系电话"
|
||||
type="number"
|
||||
value="{{formData.phone}}"
|
||||
bindinput="onInputChange"
|
||||
data-field="phone"
|
||||
/>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<text class="form-label">农场地址 <text class="required">*</text></text>
|
||||
<textarea
|
||||
class="form-textarea"
|
||||
placeholder="请输入农场详细地址"
|
||||
value="{{formData.address}}"
|
||||
bindinput="onInputChange"
|
||||
data-field="address"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-section">
|
||||
<view class="section-title">养殖信息</view>
|
||||
<view class="form-group">
|
||||
<text class="form-label">牲畜数量 <text class="required">*</text></text>
|
||||
<input
|
||||
class="form-input"
|
||||
placeholder="请输入牲畜数量"
|
||||
type="number"
|
||||
value="{{formData.animalCount}}"
|
||||
bindinput="onInputChange"
|
||||
data-field="animalCount"
|
||||
/>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<text class="form-label">养殖类型</text>
|
||||
<picker
|
||||
class="form-picker"
|
||||
bindchange="onPickerChange"
|
||||
data-field="animalType"
|
||||
value="{{animalTypeIndex}}"
|
||||
range="{{animalTypes}}"
|
||||
>
|
||||
<view class="picker-content">
|
||||
<text class="picker-text">{{formData.animalType || '请选择养殖类型'}}</text>
|
||||
<text class="picker-arrow">></text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<text class="form-label">农场面积</text>
|
||||
<input
|
||||
class="form-input"
|
||||
placeholder="请输入农场面积(亩)"
|
||||
type="digit"
|
||||
value="{{formData.farmArea}}"
|
||||
bindinput="onInputChange"
|
||||
data-field="farmArea"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="action-buttons">
|
||||
<view class="btn-group" wx:if="{{mode === 'view'}}">
|
||||
<view class="btn btn-secondary" bindtap="onEdit">编辑信息</view>
|
||||
<view class="btn btn-danger" bindtap="onDelete">删除养殖户</view>
|
||||
</view>
|
||||
<view class="btn-group" wx:if="{{mode === 'add' || mode === 'edit'}}">
|
||||
<view class="btn btn-secondary" bindtap="onCancel">取消</view>
|
||||
<view class="btn btn-primary" bindtap="onSave">{{mode === 'add' ? '保存' : '更新'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -1,53 +1,343 @@
|
||||
/* pages/farmer/farmer.wxss */
|
||||
.container {
|
||||
.farmer-container {
|
||||
background-color: #f5f7fa;
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
padding-bottom: 120rpx; /* 为底部按钮留出空间 */
|
||||
}
|
||||
|
||||
/* 确保状态栏在顶部 */
|
||||
.status-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 标题栏 */
|
||||
.header {
|
||||
margin-top: 60rpx; /* 为状态栏留出空间 */
|
||||
}
|
||||
|
||||
/* 状态栏 */
|
||||
.status-bar {
|
||||
background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
|
||||
padding: 20rpx 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0 4rpx 12rpx rgba(76, 175, 80, 0.15);
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #ffffff;
|
||||
padding: 10rpx 30rpx;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 50%;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.back-btn:active {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
color: #ffffff;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #ffffff;
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 50%;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.action-btn:active {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.edit-icon {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
/* 养殖户详情卡片 */
|
||||
.farmer-detail-card {
|
||||
margin: 30rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
|
||||
padding: 40rpx 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.farmer-avatar {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 3rpx solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.avatar-text {
|
||||
color: #ffffff;
|
||||
font-size: 40rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.farmer-basic {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.farmer-name {
|
||||
color: #ffffff;
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: #ffffff;
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.info-section {
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.info-section:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
margin-bottom: 24rpx;
|
||||
padding-bottom: 12rpx;
|
||||
border-bottom: 2rpx solid #4CAF50;
|
||||
}
|
||||
|
||||
.status-left {
|
||||
.info-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.status-right {
|
||||
.info-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
color: #666666;
|
||||
font-size: 28rpx;
|
||||
width: 160rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: #333333;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.info-value.highlight {
|
||||
color: #4CAF50;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 表单样式 */
|
||||
.farmer-form {
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
background: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.form-group:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
color: #333333;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.required {
|
||||
color: #F44336;
|
||||
}
|
||||
|
||||
.form-input,
|
||||
.form-textarea {
|
||||
width: 100%;
|
||||
background: #f8f9fa;
|
||||
border: 2rpx solid #e8e8e8;
|
||||
border-radius: 12rpx;
|
||||
padding: 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
box-sizing: border-box;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-input:focus,
|
||||
.form-textarea:focus {
|
||||
border-color: #4CAF50;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 0 0 4rpx rgba(76, 175, 80, 0.1);
|
||||
}
|
||||
|
||||
.form-input {
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
min-height: 120rpx;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.form-picker {
|
||||
width: 100%;
|
||||
background: #f8f9fa;
|
||||
border: 2rpx solid #e8e8e8;
|
||||
border-radius: 12rpx;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-picker:active {
|
||||
border-color: #4CAF50;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.picker-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx;
|
||||
height: 80rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.battery-icon {
|
||||
width: 20rpx;
|
||||
height: 12rpx;
|
||||
background-color: #333333;
|
||||
border-radius: 2rpx;
|
||||
position: relative;
|
||||
.picker-text {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.picker-arrow {
|
||||
color: #999999;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
/* 操作按钮 */
|
||||
.action-buttons {
|
||||
padding: 30rpx;
|
||||
background: #ffffff;
|
||||
border-top: 1rpx solid #e8e8e8;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.btn {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 12rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
|
||||
color: #ffffff;
|
||||
box-shadow: 0 4rpx 12rpx rgba(76, 175, 80, 0.25);
|
||||
}
|
||||
|
||||
.btn-primary:active {
|
||||
transform: translateY(2rpx);
|
||||
box-shadow: 0 2rpx 8rpx rgba(76, 175, 80, 0.25);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #f8f9fa;
|
||||
color: #666666;
|
||||
border: 2rpx solid #e8e8e8;
|
||||
}
|
||||
|
||||
.btn-secondary:active {
|
||||
background: #e8e8e8;
|
||||
transform: translateY(2rpx);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: linear-gradient(135deg, #F44336 0%, #d32f2f 100%);
|
||||
color: #ffffff;
|
||||
box-shadow: 0 4rpx 12rpx rgba(244, 67, 54, 0.25);
|
||||
}
|
||||
|
||||
.btn-danger:active {
|
||||
transform: translateY(2rpx);
|
||||
box-shadow: 0 2rpx 8rpx rgba(244, 67, 54, 0.25);
|
||||
}
|
||||
|
||||
.battery-icon::after {
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
Page({
|
||||
data: {
|
||||
farmers: [],
|
||||
allFarmers: [], // 存储所有养殖户数据
|
||||
loading: false,
|
||||
searchKeyword: '',
|
||||
activeTab: 'all', // 当前激活的筛选标签
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
@@ -45,35 +47,83 @@ Page({
|
||||
address: '宁夏银川市兴庆区',
|
||||
farmName: '张三养殖场',
|
||||
animalCount: 120,
|
||||
status: 'active'
|
||||
status: 'active',
|
||||
registerTime: '2023-01-15'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '李四',
|
||||
phone: '13800138002',
|
||||
address: '宁夏银川市金凤区',
|
||||
farmName: '李四牧场',
|
||||
address: '宁夏石嘴山市大武口区',
|
||||
farmName: '李四牧业',
|
||||
animalCount: 85,
|
||||
status: 'active'
|
||||
status: 'pending',
|
||||
registerTime: '2023-02-20'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '王五',
|
||||
phone: '13800138003',
|
||||
address: '宁夏银川市西夏区',
|
||||
farmName: '王五养殖合作社',
|
||||
address: '宁夏吴忠市利通区',
|
||||
farmName: '王五农场',
|
||||
animalCount: 200,
|
||||
status: 'inactive'
|
||||
status: 'inactive',
|
||||
registerTime: '2023-03-10'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '赵六',
|
||||
phone: '13800138004',
|
||||
address: '宁夏固原市原州区',
|
||||
farmName: '赵六养殖合作社',
|
||||
animalCount: 150,
|
||||
status: 'active',
|
||||
registerTime: '2023-04-05'
|
||||
}
|
||||
];
|
||||
|
||||
setTimeout(() => {
|
||||
this.setData({
|
||||
farmers: mockFarmers,
|
||||
loading: false,
|
||||
total: mockFarmers.length
|
||||
allFarmers: mockFarmers,
|
||||
loading: false
|
||||
});
|
||||
}, 500);
|
||||
this.filterFarmers();
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
// 筛选标签切换
|
||||
onTabChange: function(e) {
|
||||
const tab = e.currentTarget.dataset.tab;
|
||||
this.setData({
|
||||
activeTab: tab
|
||||
});
|
||||
this.filterFarmers();
|
||||
},
|
||||
|
||||
// 根据当前标签和搜索关键词筛选养殖户
|
||||
filterFarmers: function() {
|
||||
const { allFarmers, activeTab, searchKeyword } = this.data;
|
||||
let filteredFarmers = [...allFarmers];
|
||||
|
||||
// 按状态筛选
|
||||
if (activeTab !== 'all') {
|
||||
filteredFarmers = filteredFarmers.filter(farmer => farmer.status === activeTab);
|
||||
}
|
||||
|
||||
// 按搜索关键词筛选
|
||||
if (searchKeyword.trim()) {
|
||||
const keyword = searchKeyword.trim().toLowerCase();
|
||||
filteredFarmers = filteredFarmers.filter(farmer =>
|
||||
farmer.name.toLowerCase().includes(keyword) ||
|
||||
farmer.farmName.toLowerCase().includes(keyword) ||
|
||||
farmer.phone.includes(keyword)
|
||||
);
|
||||
}
|
||||
|
||||
this.setData({
|
||||
farmers: filteredFarmers,
|
||||
total: filteredFarmers.length
|
||||
});
|
||||
},
|
||||
|
||||
onSearchInput: function(e) {
|
||||
@@ -83,19 +133,19 @@ Page({
|
||||
},
|
||||
|
||||
onSearch: function() {
|
||||
this.loadFarmers();
|
||||
this.filterFarmers();
|
||||
},
|
||||
|
||||
onFarmerTap: function(e) {
|
||||
const farmerId = e.currentTarget.dataset.id;
|
||||
wx.navigateTo({
|
||||
url: `/pages/farmer/detail/detail?id=${farmerId}`
|
||||
url: `/pages/farmer/farmer?id=${farmerId}`
|
||||
});
|
||||
},
|
||||
|
||||
onAddFarmer: function() {
|
||||
wx.navigateTo({
|
||||
url: '/pages/farmer/add/add'
|
||||
url: '/pages/farmer/farmer?mode=add'
|
||||
});
|
||||
},
|
||||
|
||||
@@ -105,14 +155,13 @@ Page({
|
||||
},
|
||||
|
||||
onReachBottom: function() {
|
||||
// 加载更多数据
|
||||
if (this.data.farmers.length < this.data.total) {
|
||||
this.loadMoreFarmers();
|
||||
}
|
||||
},
|
||||
|
||||
loadMoreFarmers: function() {
|
||||
// 实现分页加载
|
||||
// 实际项目中这里会加载更多数据
|
||||
console.log('加载更多养殖户数据');
|
||||
},
|
||||
|
||||
|
||||
@@ -1,27 +1,66 @@
|
||||
<!--pages/farmers/farmers.wxml-->
|
||||
<view class="container">
|
||||
<!-- 顶部搜索栏 -->
|
||||
<view class="search-bar">
|
||||
<view class="search-input-wrapper">
|
||||
<input
|
||||
class="search-input"
|
||||
placeholder="搜索养殖户姓名或农场名称"
|
||||
value="{{searchKeyword}}"
|
||||
bindinput="onSearchInput"
|
||||
bindconfirm="onSearch"
|
||||
/>
|
||||
<view class="search-btn" bindtap="onSearch">
|
||||
<text class="search-icon">🔍</text>
|
||||
</view>
|
||||
<view class="farmers-container">
|
||||
<!-- 标题栏 -->
|
||||
<view class="header">
|
||||
<text class="title">养殖户管理</text>
|
||||
</view>
|
||||
|
||||
<!-- 筛选标签 -->
|
||||
<view class="filter-tabs">
|
||||
<view
|
||||
class="tab-item {{activeTab === 'all' ? 'active' : ''}}"
|
||||
data-tab="all"
|
||||
bindtap="onTabChange"
|
||||
>
|
||||
<text>全部</text>
|
||||
</view>
|
||||
<view class="add-btn" bindtap="onAddFarmer">
|
||||
<text class="add-icon">+</text>
|
||||
<text class="add-text">新增</text>
|
||||
<view
|
||||
class="tab-item {{activeTab === 'active' ? 'active' : ''}}"
|
||||
data-tab="active"
|
||||
bindtap="onTabChange"
|
||||
>
|
||||
<text>正常</text>
|
||||
</view>
|
||||
<view
|
||||
class="tab-item {{activeTab === 'pending' ? 'active' : ''}}"
|
||||
data-tab="pending"
|
||||
bindtap="onTabChange"
|
||||
>
|
||||
<text>待审核</text>
|
||||
</view>
|
||||
<view
|
||||
class="tab-item {{activeTab === 'inactive' ? 'active' : ''}}"
|
||||
data-tab="inactive"
|
||||
bindtap="onTabChange"
|
||||
>
|
||||
<text>停用</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索栏 -->
|
||||
<view class="search-section">
|
||||
<view class="search-bar">
|
||||
<view class="search-input-wrapper">
|
||||
<input
|
||||
class="search-input"
|
||||
placeholder="搜索养殖户姓名或农场名称"
|
||||
value="{{searchKeyword}}"
|
||||
bindinput="onSearchInput"
|
||||
bindconfirm="onSearch"
|
||||
/>
|
||||
<view class="search-btn" bindtap="onSearch">
|
||||
<text class="search-icon">🔍</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="add-btn" bindtap="onAddFarmer">
|
||||
<text class="add-icon">+</text>
|
||||
<text class="add-text">新增</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 养殖户列表 -->
|
||||
<view class="farmers-list">
|
||||
<view class="farmer-list">
|
||||
<view
|
||||
class="farmer-item"
|
||||
wx:for="{{farmers}}"
|
||||
@@ -29,49 +68,54 @@
|
||||
data-id="{{item.id}}"
|
||||
bindtap="onFarmerTap"
|
||||
>
|
||||
<view class="farmer-avatar">
|
||||
<text class="avatar-text">{{item.name.charAt(0)}}</text>
|
||||
</view>
|
||||
<!-- 养殖户标题 -->
|
||||
<view class="farmer-title">{{item.name}} - {{item.farmName}}</view>
|
||||
|
||||
<!-- 养殖户信息 -->
|
||||
<view class="farmer-info">
|
||||
<view class="farmer-header">
|
||||
<text class="farmer-name">{{item.name}}</text>
|
||||
<view class="farmer-status {{item.status}}">
|
||||
<text class="status-text">{{item.status === 'active' ? '正常' : '停用'}}</text>
|
||||
<view class="info-row">
|
||||
<text class="info-label">负责人:</text>
|
||||
<text class="info-value">{{item.name}}</text>
|
||||
</view>
|
||||
|
||||
<view class="info-row">
|
||||
<text class="info-label">当前状态:</text>
|
||||
<view class="status-tag {{item.status}}">
|
||||
<text>{{item.status === 'active' ? '正常' : item.status === 'pending' ? '待审核' : '停用'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="farmer-details">
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">农场:</text>
|
||||
<text class="detail-value">{{item.farmName}}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">电话:</text>
|
||||
<text class="detail-value">{{item.phone}}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">地址:</text>
|
||||
<text class="detail-value">{{item.address}}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">牲畜数量:</text>
|
||||
<text class="detail-value animal-count">{{item.animalCount}}头</text>
|
||||
</view>
|
||||
|
||||
<view class="info-row">
|
||||
<text class="info-label">联系电话:</text>
|
||||
<text class="info-value">{{item.phone}}</text>
|
||||
</view>
|
||||
|
||||
<view class="info-row">
|
||||
<text class="info-label">牲畜数量:</text>
|
||||
<text class="info-value">{{item.animalCount}}头</text>
|
||||
</view>
|
||||
|
||||
<view class="info-row">
|
||||
<text class="info-label">农场地址:</text>
|
||||
<text class="info-value">{{item.address}}</text>
|
||||
</view>
|
||||
|
||||
<view class="info-row">
|
||||
<text class="info-label">注册时间:</text>
|
||||
<text class="info-value">{{item.registerTime}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="farmer-arrow">
|
||||
<text class="arrow-icon">></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view class="loading" wx:if="{{loading}}">
|
||||
<view class="loading-state" wx:if="{{loading}}">
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="empty-state" wx:if="{{!loading && farmers.length === 0}}">
|
||||
<text class="empty-icon">📋</text>
|
||||
<image class="empty-icon" src="/images/empty.png" />
|
||||
<text class="empty-text">暂无养殖户数据</text>
|
||||
<view class="empty-btn" bindtap="onAddFarmer">
|
||||
<text>添加养殖户</text>
|
||||
|
||||
@@ -1,17 +1,67 @@
|
||||
/* pages/farmers/farmers.wxss */
|
||||
.container {
|
||||
.farmers-container {
|
||||
background-color: #f5f7fa;
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 搜索栏样式 */
|
||||
/* 标题栏 */
|
||||
.header {
|
||||
background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
|
||||
padding: 20rpx 30rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(76, 175, 80, 0.15);
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #ffffff;
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 筛选标签 */
|
||||
.filter-tabs {
|
||||
display: flex;
|
||||
background: #ffffff;
|
||||
padding: 20rpx 30rpx;
|
||||
border-bottom: 1rpx solid #e8e8e8;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 16rpx 0;
|
||||
border-radius: 8rpx;
|
||||
margin: 0 8rpx;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.tab-item text {
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tab-item.active {
|
||||
background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
|
||||
box-shadow: 0 4rpx 12rpx rgba(76, 175, 80, 0.25);
|
||||
}
|
||||
|
||||
.tab-item.active text {
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 搜索区域 */
|
||||
.search-section {
|
||||
background: #ffffff;
|
||||
padding: 20rpx 30rpx;
|
||||
border-bottom: 1rpx solid #e8e8e8;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #ffffff;
|
||||
padding: 20rpx 30rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
@@ -19,33 +69,200 @@
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 25rpx;
|
||||
background: #f8f9fa;
|
||||
border-radius: 12rpx;
|
||||
padding: 0 20rpx;
|
||||
height: 70rpx;
|
||||
border: 2rpx solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.search-input-wrapper:focus-within {
|
||||
border-color: #4CAF50;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 0 0 4rpx rgba(76, 175, 80, 0.1);
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.search-input::placeholder {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #7CB342;
|
||||
border-radius: 50%;
|
||||
margin-left: 10rpx;
|
||||
padding: 8rpx;
|
||||
border-radius: 8rpx;
|
||||
background: #4CAF50;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.search-btn:active {
|
||||
background: #45a049;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
color: white;
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
|
||||
color: #ffffff;
|
||||
padding: 20rpx 24rpx;
|
||||
border-radius: 12rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 4rpx 12rpx rgba(76, 175, 80, 0.25);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.add-btn:active {
|
||||
transform: translateY(2rpx);
|
||||
box-shadow: 0 2rpx 8rpx rgba(76, 175, 80, 0.25);
|
||||
}
|
||||
|
||||
.add-icon {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 养殖户列表 */
|
||||
.farmer-list {
|
||||
padding: 20rpx 30rpx;
|
||||
}
|
||||
|
||||
.farmer-item {
|
||||
background: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 20rpx;
|
||||
padding: 30rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||||
border: 1rpx solid #f0f0f0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.farmer-item:active {
|
||||
transform: translateY(2rpx);
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.farmer-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
margin-bottom: 20rpx;
|
||||
padding-bottom: 16rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.farmer-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
color: #666666;
|
||||
width: 160rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: #333333;
|
||||
flex: 1;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 状态标签 */
|
||||
.status-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-tag.active {
|
||||
background: rgba(76, 175, 80, 0.1);
|
||||
color: #4CAF50;
|
||||
}
|
||||
|
||||
.status-tag.pending {
|
||||
background: rgba(255, 193, 7, 0.1);
|
||||
color: #FFC107;
|
||||
}
|
||||
|
||||
.status-tag.inactive {
|
||||
background: rgba(244, 67, 54, 0.1);
|
||||
color: #F44336;
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.loading-state {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 80rpx 0;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
color: #999999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 60rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
margin-bottom: 30rpx;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
color: #999999;
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.empty-btn {
|
||||
background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
|
||||
color: #ffffff;
|
||||
padding: 20rpx 40rpx;
|
||||
border-radius: 12rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 4rpx 12rpx rgba(76, 175, 80, 0.25);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.empty-btn:active {
|
||||
transform: translateY(2rpx);
|
||||
box-shadow: 0 2rpx 8rpx rgba(76, 175, 80, 0.25);
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
|
||||
@@ -3,3 +3,4 @@
|
||||
"navigationBarTitleText": "首页",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
<!--pages/index/index.wxml-->
|
||||
<view class="container">
|
||||
<!-- 顶部导航栏 -->
|
||||
<view class="nav-bar">
|
||||
<view class="nav-title">首页</view>
|
||||
<view class="nav-actions">
|
||||
<view class="nav-icon">⋯</view>
|
||||
<view class="nav-icon">—</view>
|
||||
<view class="nav-icon">⊙</view>
|
||||
<view class="home-container">
|
||||
<!-- 标题栏 -->
|
||||
<view class="header">
|
||||
<view class="header-left">
|
||||
<text class="title">首页</text>
|
||||
</view>
|
||||
<view class="header-right">
|
||||
<!-- 可以在这里添加右侧操作按钮 -->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 智能硬件设备区域 -->
|
||||
<view class="section">
|
||||
<view class="section-header">
|
||||
<view class="section-indicator"></view>
|
||||
<text class="section-title">智能硬件设备</text>
|
||||
</view>
|
||||
<view class="section card">
|
||||
<view class="section-title">智能硬件设备</view>
|
||||
<view class="hardware-grid">
|
||||
<view
|
||||
class="hardware-item"
|
||||
@@ -32,12 +29,9 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 其它功能区域 -->
|
||||
<view class="section">
|
||||
<view class="section-header">
|
||||
<view class="section-indicator"></view>
|
||||
<text class="section-title">其它</text>
|
||||
</view>
|
||||
<!-- 快捷功能区域 -->
|
||||
<view class="section card">
|
||||
<view class="section-title">快捷功能</view>
|
||||
<view class="other-grid">
|
||||
<view
|
||||
class="other-item"
|
||||
@@ -53,20 +47,6 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部标签栏 -->
|
||||
<view class="tab-bar">
|
||||
<view class="tab-item active">
|
||||
<image class="tab-icon" src="/images/home-active.png" mode="aspectFit"/>
|
||||
<text class="tab-text">首页</text>
|
||||
</view>
|
||||
<view class="tab-item">
|
||||
<image class="tab-icon" src="/images/farmers.png" mode="aspectFit"/>
|
||||
<text class="tab-text">养殖户</text>
|
||||
</view>
|
||||
<view class="tab-item">
|
||||
<image class="tab-icon" src="/images/profile.png" mode="aspectFit"/>
|
||||
<text class="tab-text">我的</text>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
@@ -1,80 +1,73 @@
|
||||
/* pages/index/index.wxss */
|
||||
.container {
|
||||
.home-container {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
padding-bottom: 120rpx; /* 为底部标签栏留出空间 */
|
||||
background: linear-gradient(180deg, #f8fffe 0%, #f0f9ff 100%);
|
||||
padding-bottom: 120rpx;
|
||||
}
|
||||
|
||||
/* 顶部导航栏 */
|
||||
.nav-bar {
|
||||
background: linear-gradient(135deg, #7CB342 0%, #8BC34A 100%);
|
||||
height: 88rpx;
|
||||
/* 标题栏 */
|
||||
.header {
|
||||
background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
|
||||
padding: 20rpx 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 30rpx;
|
||||
color: white;
|
||||
box-shadow: 0 4rpx 12rpx rgba(76, 175, 80, 0.15);
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.nav-actions {
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 44rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
font-size: 24rpx;
|
||||
margin: 0 8rpx;
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
.title {
|
||||
color: #ffffff;
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 区域样式 */
|
||||
.section {
|
||||
background: white;
|
||||
margin: 20rpx 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx 30rpx;
|
||||
background: #f8f8f8;
|
||||
border-bottom: 1rpx solid #e8e8e8;
|
||||
}
|
||||
|
||||
.section-indicator {
|
||||
width: 6rpx;
|
||||
height: 28rpx;
|
||||
background: #2c5aa0;
|
||||
margin-right: 16rpx;
|
||||
border-radius: 3rpx;
|
||||
margin: 25rpx 30rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 20rpx;
|
||||
padding: 35rpx 30rpx;
|
||||
box-shadow: 0 8rpx 25rpx rgba(0, 0, 0, 0.08);
|
||||
border: 1rpx solid rgba(76, 175, 80, 0.1);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 35rpx;
|
||||
position: relative;
|
||||
padding-left: 20rpx;
|
||||
}
|
||||
|
||||
.section-title::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 6rpx;
|
||||
height: 28rpx;
|
||||
background: linear-gradient(135deg, #4CAF50, #45a049);
|
||||
border-radius: 3rpx;
|
||||
}
|
||||
|
||||
/* 智能硬件设备网格 */
|
||||
.hardware-grid {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
padding: 30rpx 20rpx;
|
||||
background: white;
|
||||
justify-content: space-between;
|
||||
gap: 25rpx;
|
||||
}
|
||||
|
||||
.hardware-item {
|
||||
@@ -82,67 +75,118 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 0 10rpx;
|
||||
padding: 25rpx 15rpx;
|
||||
border-radius: 16rpx;
|
||||
background: linear-gradient(145deg, #f8f9fa, #ffffff);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hardware-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(135deg, rgba(76, 175, 80, 0.05), rgba(69, 160, 73, 0.05));
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.hardware-item:active::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.hardware-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
width: 110rpx;
|
||||
height: 110rpx;
|
||||
border-radius: 55rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.icon-image {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.12);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.hardware-name {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
font-size: 28rpx;
|
||||
color: #2c3e50;
|
||||
text-align: center;
|
||||
line-height: 1.3;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
font-weight: 600;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.icon-image {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
}
|
||||
|
||||
/* 其它功能网格 */
|
||||
.other-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 30rpx;
|
||||
background: white;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 35rpx 25rpx;
|
||||
}
|
||||
|
||||
.other-item {
|
||||
width: 25%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 40rpx;
|
||||
padding: 25rpx 15rpx;
|
||||
border-radius: 16rpx;
|
||||
background: linear-gradient(145deg, #f8f9fa, #ffffff);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.other-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(135deg, rgba(76, 175, 80, 0.05), rgba(69, 160, 73, 0.05));
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.other-item:active::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.other-icon {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 50%;
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
border-radius: 44rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 16rpx;
|
||||
margin-bottom: 18rpx;
|
||||
box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.12);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.other-name {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
font-size: 26rpx;
|
||||
color: #2c3e50;
|
||||
text-align: center;
|
||||
line-height: 1.2;
|
||||
word-break: break-all;
|
||||
line-height: 1.3;
|
||||
font-weight: 600;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.icon-image {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* pages/login/login.wxss */
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -27,150 +27,190 @@
|
||||
}
|
||||
|
||||
.circle-1 {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
top: 10%;
|
||||
left: 10%;
|
||||
width: 220rpx;
|
||||
height: 220rpx;
|
||||
top: 8%;
|
||||
left: 8%;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.circle-2 {
|
||||
width: 150rpx;
|
||||
height: 150rpx;
|
||||
top: 60%;
|
||||
right: 15%;
|
||||
width: 170rpx;
|
||||
height: 170rpx;
|
||||
top: 65%;
|
||||
right: 12%;
|
||||
animation-delay: 2s;
|
||||
}
|
||||
|
||||
.circle-3 {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
top: 30%;
|
||||
right: 30%;
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
top: 25%;
|
||||
right: 25%;
|
||||
animation-delay: 4s;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translateY(0px); }
|
||||
50% { transform: translateY(-20px); }
|
||||
0%, 100% {
|
||||
transform: translateY(0px) rotate(0deg);
|
||||
opacity: 0.6;
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-25px) rotate(180deg);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
/* 登录表单 */
|
||||
.login-form {
|
||||
width: 600rpx;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 20rpx;
|
||||
padding: 60rpx 40rpx;
|
||||
box-shadow: 0 20rpx 40rpx rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
width: 640rpx;
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
border-radius: 25rpx;
|
||||
padding: 70rpx 45rpx;
|
||||
box-shadow: 0 25rpx 50rpx rgba(0, 0, 0, 0.15);
|
||||
backdrop-filter: blur(15px);
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* 头部区域 */
|
||||
.header-section {
|
||||
text-align: center;
|
||||
margin-bottom: 60rpx;
|
||||
margin-bottom: 70rpx;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin-bottom: 30rpx;
|
||||
margin-bottom: 35rpx;
|
||||
}
|
||||
|
||||
.logo-img {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
border-radius: 70rpx;
|
||||
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 10rpx;
|
||||
font-size: 52rpx;
|
||||
font-weight: 800;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 15rpx;
|
||||
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
font-size: 30rpx;
|
||||
color: #5a6c7d;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 输入框区域 */
|
||||
.input-section {
|
||||
margin-bottom: 40rpx;
|
||||
margin-bottom: 50rpx;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
position: relative;
|
||||
margin-bottom: 30rpx;
|
||||
background: #f8f9fa;
|
||||
border-radius: 12rpx;
|
||||
border: 2rpx solid #e9ecef;
|
||||
margin-bottom: 35rpx;
|
||||
background: linear-gradient(145deg, #f8f9fa, #ffffff);
|
||||
border-radius: 18rpx;
|
||||
border: 2rpx solid rgba(76, 175, 80, 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20rpx;
|
||||
padding: 0 25rpx;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.input-group:focus-within {
|
||||
border-color: #4CAF50;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 0 6rpx rgba(76, 175, 80, 0.1);
|
||||
background: #ffffff;
|
||||
box-shadow: 0 8rpx 25rpx rgba(76, 175, 80, 0.15);
|
||||
transform: translateY(-2rpx);
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
font-size: 32rpx;
|
||||
margin-right: 20rpx;
|
||||
color: #666;
|
||||
font-size: 36rpx;
|
||||
margin-right: 25rpx;
|
||||
color: #4CAF50;
|
||||
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.input-field {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
height: 96rpx;
|
||||
font-size: 34rpx;
|
||||
color: #2c3e50;
|
||||
background: transparent;
|
||||
border: none;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.input-field::placeholder {
|
||||
color: #999;
|
||||
color: #95a5a6;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.password-toggle {
|
||||
padding: 10rpx;
|
||||
font-size: 32rpx;
|
||||
color: #666;
|
||||
padding: 12rpx;
|
||||
font-size: 36rpx;
|
||||
color: #4CAF50;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.password-toggle:active {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
/* 按钮区域 */
|
||||
.button-section {
|
||||
margin-bottom: 40rpx;
|
||||
margin-bottom: 50rpx;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
border-radius: 12rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
height: 96rpx;
|
||||
border-radius: 18rpx;
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
border: none;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.login-btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
transition: left 0.5s ease;
|
||||
}
|
||||
|
||||
.login-btn.active {
|
||||
background: linear-gradient(135deg, #4CAF50, #45a049);
|
||||
color: white;
|
||||
box-shadow: 0 8rpx 20rpx rgba(76, 175, 80, 0.3);
|
||||
}
|
||||
|
||||
.login-btn.active:active {
|
||||
transform: translateY(2rpx);
|
||||
box-shadow: 0 4rpx 8rpx rgba(76, 175, 80, 0.3);
|
||||
transform: translateY(3rpx);
|
||||
box-shadow: 0 4rpx 12rpx rgba(76, 175, 80, 0.4);
|
||||
}
|
||||
|
||||
.login-btn.active:active::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.login-btn.disabled {
|
||||
background: #e9ecef;
|
||||
color: #999;
|
||||
background: linear-gradient(145deg, #e9ecef, #f8f9fa);
|
||||
color: #95a5a6;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* 选项区域 */
|
||||
@@ -178,7 +218,7 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 40rpx;
|
||||
margin-bottom: 50rpx;
|
||||
}
|
||||
|
||||
.remember-section {
|
||||
@@ -189,18 +229,26 @@
|
||||
.checkbox-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
font-size: 30rpx;
|
||||
color: #5a6c7d;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.checkbox-text {
|
||||
margin-left: 10rpx;
|
||||
margin-left: 12rpx;
|
||||
}
|
||||
|
||||
.forgot-password {
|
||||
font-size: 28rpx;
|
||||
font-size: 30rpx;
|
||||
color: #4CAF50;
|
||||
text-decoration: underline;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.forgot-password:active {
|
||||
color: #45a049;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
/* 版本信息 */
|
||||
@@ -209,6 +257,7 @@
|
||||
}
|
||||
|
||||
.version-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
font-size: 26rpx;
|
||||
color: #95a5a6;
|
||||
font-weight: 400;
|
||||
}
|
||||
@@ -113,7 +113,7 @@ Page({
|
||||
|
||||
onShow() {
|
||||
// 每次显示页面时刷新数据
|
||||
this.loadData();
|
||||
this.loadUserData();
|
||||
},
|
||||
|
||||
// 检查登录状态
|
||||
@@ -127,7 +127,13 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
// 加载用户数据
|
||||
// 编辑个人资料
|
||||
editProfile() {
|
||||
wx.showToast({
|
||||
title: '编辑功能开发中',
|
||||
icon: 'none'
|
||||
});
|
||||
},
|
||||
async loadUserData() {
|
||||
try {
|
||||
// 从本地存储获取用户信息
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
"usingComponents": {},
|
||||
"navigationBarTitleText": "我的"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,93 +1,57 @@
|
||||
<!--pages/profile/profile.wxml-->
|
||||
<view class="container">
|
||||
<!-- 状态栏 -->
|
||||
<view class="status-bar" style="height: {{statusBarHeight}}px;"></view>
|
||||
|
||||
<!-- 用户信息卡片 -->
|
||||
<view class="user-card">
|
||||
<view class="profile-container">
|
||||
<!-- 用户信息头部 -->
|
||||
<view class="profile-header">
|
||||
<view class="user-avatar">
|
||||
<image wx:if="{{userInfo.avatar}}" src="{{userInfo.avatar}}" class="avatar-img" mode="aspectFill" />
|
||||
<image wx:else src="/images/avatar.png" class="avatar-img" />
|
||||
</view>
|
||||
<view class="user-info">
|
||||
<view class="avatar-container" bindtap="onAvatarTap">
|
||||
<image wx:if="{{userInfo.avatar}}" class="avatar" src="{{userInfo.avatar}}" mode="aspectFill" />
|
||||
<view wx:else class="avatar-placeholder">
|
||||
<text class="avatar-text">{{userInfo.name.charAt(0)}}</text>
|
||||
</view>
|
||||
<view class="avatar-badge">📷</view>
|
||||
</view>
|
||||
|
||||
<view class="user-details">
|
||||
<view class="user-name">{{userInfo.name}}</view>
|
||||
<view class="user-account">账号: {{userInfo.account}}</view>
|
||||
<view class="user-department">{{userInfo.department}}</view>
|
||||
<view class="user-position">{{userInfo.position}}</view>
|
||||
<view class="last-login">最后登录: {{userInfo.lastLoginTime}}</view>
|
||||
</view>
|
||||
<view class="username">{{userInfo.name || '西藏智慧三牧'}}</view>
|
||||
<view class="user-role">{{userInfo.phone || '18003030830'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 统计数据 -->
|
||||
<view class="stats-section">
|
||||
<view class="section-title">今日数据</view>
|
||||
<view class="stats-grid">
|
||||
<view class="stat-item">
|
||||
<view class="stat-number">{{statsData.todayCheck}}</view>
|
||||
<view class="stat-label">今日检查</view>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<view class="stat-number">{{statsData.totalFarmers}}</view>
|
||||
<view class="stat-label">管理养殖户</view>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<view class="stat-number">{{statsData.totalAlerts}}</view>
|
||||
<view class="stat-label">待处理告警</view>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<view class="stat-number">{{statsData.completedTasks}}</view>
|
||||
<view class="stat-label">完成任务</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 快捷操作 -->
|
||||
<view class="quick-actions-section">
|
||||
<view class="section-title">快捷操作</view>
|
||||
<view class="quick-actions">
|
||||
<view
|
||||
wx:for="{{quickActions}}"
|
||||
wx:key="id"
|
||||
class="quick-action-item"
|
||||
style="background-color: {{item.color}}20;"
|
||||
bindtap="onQuickActionTap"
|
||||
data-id="{{item.id}}"
|
||||
>
|
||||
<view class="quick-action-icon" style="color: {{item.color}};">{{item.icon}}</view>
|
||||
<view class="quick-action-title">{{item.title}}</view>
|
||||
</view>
|
||||
<view class="edit-btn" bindtap="editProfile">
|
||||
<text class="edit-icon">✏️</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能菜单 -->
|
||||
<view class="menu-section">
|
||||
<view class="section-title">功能设置</view>
|
||||
<view class="menu-list">
|
||||
<view
|
||||
wx:for="{{menuItems}}"
|
||||
wx:key="id"
|
||||
class="menu-item"
|
||||
bindtap="onMenuItemTap"
|
||||
data-id="{{item.id}}"
|
||||
>
|
||||
<view class="menu-icon">{{item.icon}}</view>
|
||||
<view class="menu-content">
|
||||
<view class="menu-title">{{item.title}}</view>
|
||||
<view class="menu-desc">{{item.desc}}</view>
|
||||
</view>
|
||||
<view wx:if="{{item.arrow}}" class="menu-arrow">></view>
|
||||
</view>
|
||||
<view class="menu-item" bindtap="onMenuItemTap" data-id="system">
|
||||
<view class="menu-icon">⚙️</view>
|
||||
<view class="menu-text">系统设置</view>
|
||||
<view class="menu-arrow">></view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" bindtap="onMenuItemTap" data-id="trade">
|
||||
<view class="menu-icon">📋</view>
|
||||
<view class="menu-text">生资交易</view>
|
||||
<view class="menu-arrow">></view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" bindtap="onMenuItemTap" data-id="personnel">
|
||||
<view class="menu-icon">👥</view>
|
||||
<view class="menu-text">人员管理</view>
|
||||
<view class="menu-arrow">></view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" bindtap="onMenuItemTap" data-id="password">
|
||||
<view class="menu-icon">👤</view>
|
||||
<view class="menu-text">设置账号密码</view>
|
||||
<view class="menu-arrow">></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 退出登录 -->
|
||||
<view class="logout-section">
|
||||
<button class="logout-btn" bindtap="onLogout">退出登录</button>
|
||||
<button class="logout-btn" bindtap="onLogout">
|
||||
<text class="logout-icon">🚪</text>
|
||||
<text class="logout-text">退出登录</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- 版本信息 -->
|
||||
<view class="version-info">
|
||||
<text class="version-text">版本 1.0.0</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -1,54 +1,150 @@
|
||||
/* pages/profile/profile.wxss */
|
||||
.container {
|
||||
.profile-container {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
background: #f5f5f5;
|
||||
padding: 0 0 40rpx 0;
|
||||
}
|
||||
|
||||
.status-bar {
|
||||
background-color: #2c5aa0;
|
||||
}
|
||||
|
||||
/* 用户信息卡片 */
|
||||
.user-card {
|
||||
background: linear-gradient(135deg, #2c5aa0 0%, #4a7bc8 100%);
|
||||
padding: 40rpx 30rpx 30rpx;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
/* 用户信息头部 */
|
||||
.profile-header {
|
||||
background: #4CAF50;
|
||||
padding: 60rpx 40rpx 40rpx 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.avatar-container {
|
||||
position: relative;
|
||||
.user-avatar {
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
.avatar-img {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 60rpx;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: 4rpx solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.avatar-placeholder {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 60rpx;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
.user-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.user-role {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.edit-btn {
|
||||
position: absolute;
|
||||
top: 60rpx;
|
||||
right: 40rpx;
|
||||
padding: 20rpx;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.edit-icon {
|
||||
font-size: 32rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 功能菜单 */
|
||||
.menu-section {
|
||||
background: #fff;
|
||||
margin: 20rpx 0;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 32rpx 40rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.menu-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.menu-item:active {
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
font-size: 36rpx;
|
||||
margin-right: 24rpx;
|
||||
width: 40rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.menu-text {
|
||||
flex: 1;
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.menu-arrow {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 退出登录 */
|
||||
.logout-section {
|
||||
margin: 20rpx 0;
|
||||
padding: 0 0 40rpx 0;
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
background: #fff;
|
||||
border: 2rpx solid #ff4d4f;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 4rpx solid rgba(255, 255, 255, 0.3);
|
||||
font-size: 32rpx;
|
||||
color: #ff4d4f;
|
||||
font-weight: 500;
|
||||
margin: 0 0 0 0;
|
||||
}
|
||||
|
||||
.avatar-text {
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
.logout-btn:active {
|
||||
background: #fff2f0;
|
||||
}
|
||||
|
||||
.logout-icon {
|
||||
font-size: 32rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.logout-text {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
/* 版本信息 */
|
||||
.version-info {
|
||||
text-align: center;
|
||||
padding: 40rpx 0;
|
||||
}
|
||||
|
||||
.version-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.avatar-badge {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
@@ -178,11 +274,12 @@
|
||||
|
||||
/* 功能菜单 */
|
||||
.menu-section {
|
||||
background-color: white;
|
||||
margin: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||
margin: 25rpx 30rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 20rpx;
|
||||
padding: 35rpx 30rpx;
|
||||
box-shadow: 0 8rpx 25rpx rgba(0, 0, 0, 0.08);
|
||||
border: 1rpx solid rgba(76, 175, 80, 0.1);
|
||||
}
|
||||
|
||||
.menu-list {
|
||||
@@ -192,9 +289,10 @@
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
transition: background-color 0.3s ease;
|
||||
padding: 28rpx 0;
|
||||
border-bottom: 1rpx solid rgba(76, 175, 80, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.menu-item:last-child {
|
||||
@@ -202,17 +300,23 @@
|
||||
}
|
||||
|
||||
.menu-item:active {
|
||||
background-color: #f8f9fa;
|
||||
background-color: rgba(76, 175, 80, 0.05);
|
||||
border-radius: 12rpx;
|
||||
margin: 0 -15rpx;
|
||||
padding: 28rpx 15rpx;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
margin-right: 24rpx;
|
||||
font-size: 36rpx;
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
margin-right: 28rpx;
|
||||
font-size: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(145deg, #f8f9fa, #ffffff);
|
||||
border-radius: 26rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.menu-content {
|
||||
@@ -220,9 +324,63 @@
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
margin-bottom: 4rpx;
|
||||
font-size: 32rpx;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 6rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.menu-desc {
|
||||
font-size: 26rpx;
|
||||
color: #5a6c7d;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.menu-arrow {
|
||||
font-size: 28rpx;
|
||||
color: #4CAF50;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 退出登录 */
|
||||
.logout-section {
|
||||
margin: 25rpx 30rpx;
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
width: 100%;
|
||||
background: linear-gradient(135deg, #f44336 0%, #e57373 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 20rpx;
|
||||
padding: 35rpx;
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 8rpx 20rpx rgba(244, 67, 54, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.logout-btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
transition: left 0.5s ease;
|
||||
}
|
||||
|
||||
.logout-btn:active {
|
||||
transform: scale(0.98);
|
||||
box-shadow: 0 4rpx 12rpx rgba(244, 67, 54, 0.4);
|
||||
}
|
||||
|
||||
.logout-btn:active::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.menu-desc {
|
||||
|
||||
@@ -74,4 +74,4 @@
|
||||
"list": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,3 +5,4 @@
|
||||
"page": "*"
|
||||
}]
|
||||
}
|
||||
|
||||
|
||||
@@ -162,3 +162,4 @@
|
||||
3. **主题适配**:支持深色模式
|
||||
4. **动画效果**:添加页面切换动画
|
||||
5. **性能优化**:优化渲染性能
|
||||
|
||||
|
||||
@@ -163,3 +163,4 @@
|
||||
3. **用户体验**:添加加载状态和错误提示
|
||||
4. **兼容性**:确保在不同设备上正常显示
|
||||
5. **可维护性**:代码结构清晰,便于后续扩展
|
||||
|
||||
|
||||
@@ -46,3 +46,4 @@ echo - 图标文件为占位符,建议替换为实际图标
|
||||
echo.
|
||||
|
||||
pause
|
||||
|
||||
|
||||
@@ -97,3 +97,4 @@
|
||||
3. **响应式优化**:适配更多屏幕尺寸
|
||||
4. **性能优化**:图片懒加载、代码分包
|
||||
5. **无障碍优化**:添加语音提示、高对比度模式
|
||||
|
||||
|
||||
@@ -42,3 +42,4 @@ echo - 固定底部按钮
|
||||
echo.
|
||||
|
||||
pause
|
||||
|
||||
|
||||
@@ -40,3 +40,4 @@ echo 5. 测试底部tab栏切换
|
||||
echo.
|
||||
|
||||
pause
|
||||
|
||||
|
||||
@@ -45,3 +45,4 @@ echo - 整体布局紧凑合理
|
||||
echo.
|
||||
|
||||
pause
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<!-- pages/home/home.wxml - 首页 -->
|
||||
<view class="page-container">
|
||||
<!-- 头部标题区域 -->
|
||||
<view class="header-section">
|
||||
<view class="page-title">首页</view>
|
||||
</view>
|
||||
|
||||
<!-- 预警类型标签 -->
|
||||
<view class="tabs">
|
||||
<view class="tab-item {{activeTab === 'collar' ? 'active' : ''}}" bindtap="switchTab" data-tab="collar">
|
||||
|
||||
@@ -11,6 +11,20 @@ page {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
/* 头部标题区域 */
|
||||
.header-section {
|
||||
background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
|
||||
padding: 40rpx 30rpx;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 顶部标签栏 */
|
||||
.tabs {
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user