修改小程序

This commit is contained in:
2025-10-17 17:29:11 +08:00
parent 434fa135d1
commit 212dffd0da
227 changed files with 12887 additions and 45341 deletions

View File

@@ -1,42 +0,0 @@
// pages/approval/approval.js
Page({
data: {
searchKeyword: '',
loading: false,
approvalList: []
},
onLoad() {
this.loadApprovalData()
},
loadApprovalData() {
// 模拟数据
this.setData({
approvalList: [
{
id: 1,
title: '养殖许可证申请',
description: '张三申请养殖许可证',
applicant: '张三',
createTime: '2024-01-15 09:00',
status: 'pending',
statusText: '待审批'
}
]
})
},
onSearchInput(e) {
this.setData({
searchKeyword: e.detail.value
})
},
handleAdd() {
wx.showToast({
title: '新增功能待实现',
icon: 'none'
})
}
})

View File

@@ -1,3 +0,0 @@
{
"usingComponents": {}
}

View File

@@ -1,58 +0,0 @@
<!--pages/approval/approval.wxml-->
<view class="approval-container">
<!-- 搜索栏 -->
<view class="search-section">
<view class="search-bar">
<input
value="{{searchKeyword}}"
type="text"
placeholder="搜索审批记录..."
class="search-input"
bindinput="onSearchInput"
/>
<view class="search-icon">🔍</view>
</view>
</view>
<!-- 审批列表 -->
<view class="approval-list">
<view class="list-header">
<view class="list-title">审批管理</view>
<view class="add-btn" bindtap="handleAdd">
<text class="add-text">新增</text>
<view class="add-icon">+</view>
</view>
</view>
<view wx:if="{{loading}}" class="loading">
<text class="loading-text">加载中...</text>
</view>
<view wx:elif="{{approvalList.length === 0}}" class="empty">
<view class="empty-icon">📋</view>
<view class="empty-text">暂无审批记录</view>
</view>
<view wx:else class="list-content">
<view
wx:for="{{approvalList}}"
wx:key="id"
class="approval-item"
>
<view class="item-header">
<view class="item-title">{{item.title}}</view>
<view class="item-status {{item.status}}">
{{item.statusText}}
</view>
</view>
<view class="item-content">
<view class="item-desc">{{item.description}}</view>
<view class="item-meta">
<view class="item-applicant">申请人: {{item.applicant}}</view>
<view class="item-time">{{item.createTime}}</view>
</view>
</view>
</view>
</view>
</view>
</view>

View File

@@ -1,159 +0,0 @@
/* pages/approval/approval.wxss */
.approval-container {
min-height: 100vh;
background: #f6f6f6;
}
.search-section {
padding: 20rpx;
background: #fff;
margin-bottom: 20rpx;
}
.search-bar {
position: relative;
}
.search-input {
width: 100%;
height: 72rpx;
background: #f8f9fa;
border: none;
border-radius: 36rpx;
padding: 0 60rpx 0 30rpx;
font-size: 28rpx;
color: #333;
}
.search-icon {
position: absolute;
right: 30rpx;
top: 50%;
transform: translateY(-50%);
font-size: 28rpx;
color: #999;
}
.approval-list {
background: #fff;
margin: 0 20rpx;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.list-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.list-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.add-btn {
display: flex;
align-items: center;
padding: 16rpx 24rpx;
background: #1890ff;
border-radius: 24rpx;
color: #fff;
}
.add-text {
font-size: 24rpx;
margin-right: 8rpx;
}
.add-icon {
font-size: 24rpx;
font-weight: 600;
}
.loading,
.empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx;
color: #999;
}
.loading-text,
.empty-text {
font-size: 28rpx;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.list-content {
space-y: 0;
}
.approval-item {
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
transition: background 0.3s;
}
.approval-item:last-child {
border-bottom: none;
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.item-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
flex: 1;
}
.item-status {
font-size: 20rpx;
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-weight: 500;
}
.item-status.pending {
background: #fff7e6;
color: #faad14;
}
.item-content {
margin-bottom: 20rpx;
}
.item-desc {
font-size: 26rpx;
color: #666;
margin-bottom: 12rpx;
line-height: 1.5;
}
.item-meta {
display: flex;
justify-content: space-between;
align-items: center;
}
.item-applicant,
.item-time {
font-size: 22rpx;
color: #999;
}

View File

@@ -1,137 +0,0 @@
// pages/dashboard/dashboard.js
const dashboardService = require('../../services/dashboardService.js')
Page({
data: {
overviewCards: [
{
key: 'supervision',
icon: '🔍',
label: '监管记录',
value: '0',
trend: 'up',
trendText: '+0%',
type: 'primary'
},
{
key: 'approval',
icon: '✅',
label: '待审批',
value: '0',
trend: 'up',
trendText: '+0%',
type: 'success'
},
{
key: 'personnel',
icon: '👥',
label: '人员总数',
value: '0',
trend: 'up',
trendText: '+0%',
type: 'warning'
},
{
key: 'epidemic',
icon: '🦠',
label: '疫情预警',
value: '0',
trend: 'down',
trendText: '-0%',
type: 'danger'
}
],
supervisionStats: [
{ key: 'total', name: '总监管数', desc: '累计监管记录', value: '0' },
{ key: 'today', name: '今日监管', desc: '今日新增记录', value: '0' },
{ key: 'pending', name: '待处理', desc: '待处理事项', value: '0' },
{ key: 'completed', name: '已完成', desc: '已完成事项', value: '0' }
],
recentActivities: [
{
id: 1,
icon: '🔍',
title: '监管检查',
desc: '完成养殖场A的例行检查',
time: '2小时前',
status: 'success',
statusText: '已完成'
},
{
id: 2,
icon: '✅',
title: '审批通过',
desc: '通过养殖许可证申请',
time: '4小时前',
status: 'success',
statusText: '已通过'
},
{
id: 3,
icon: '⏳',
title: '待审批',
desc: '养殖场B的扩建申请',
time: '6小时前',
status: 'pending',
statusText: '待处理'
}
]
},
onLoad() {
this.loadDashboardData()
},
onPullDownRefresh() {
this.loadDashboardData()
setTimeout(() => {
wx.stopPullDownRefresh()
}, 1000)
},
async loadDashboardData() {
try {
// 加载统计数据
const [stats, supervisionStats] = await Promise.all([
dashboardService.getStats(),
dashboardService.getSupervisionStats()
])
if (stats) {
this.updateOverviewCards(stats)
}
if (supervisionStats) {
this.updateSupervisionStats(supervisionStats)
}
} catch (error) {
console.error('加载看板数据失败:', error)
wx.showToast({
title: '加载数据失败',
icon: 'error'
})
}
},
updateOverviewCards(data) {
const overviewCards = this.data.overviewCards.map(card => {
const newValue = data[card.key] || card.value
return {
...card,
value: newValue
}
})
this.setData({ overviewCards })
},
updateSupervisionStats(data) {
const supervisionStats = this.data.supervisionStats.map(stat => {
const newValue = data[stat.key] || stat.value
return {
...stat,
value: newValue
}
})
this.setData({ supervisionStats })
}
})

View File

@@ -1,3 +0,0 @@
{
"usingComponents": {}
}

View File

@@ -1,71 +0,0 @@
<!--pages/dashboard/dashboard.wxml-->
<view class="dashboard-container">
<!-- 数据概览卡片 -->
<view class="overview-cards">
<view
wx:for="{{overviewCards}}"
wx:key="key"
class="overview-card {{item.type}}"
>
<view class="card-icon">{{item.icon}}</view>
<view class="card-content">
<view class="card-value">{{item.value}}</view>
<view class="card-label">{{item.label}}</view>
<view class="card-trend {{item.trend}}">
{{item.trendText}}
</view>
</view>
</view>
</view>
<!-- 图表区域 -->
<view class="charts-section">
<view class="section-title">数据图表</view>
<view class="chart-container">
<view class="chart-placeholder">
<view class="chart-icon">📊</view>
<view class="chart-text">图表数据加载中...</view>
</view>
</view>
</view>
<!-- 监管统计 -->
<view class="supervision-stats">
<view class="section-title">监管统计</view>
<view class="stats-list">
<view
wx:for="{{supervisionStats}}"
wx:key="key"
class="stat-item"
>
<view class="stat-info">
<view class="stat-name">{{item.name}}</view>
<view class="stat-desc">{{item.desc}}</view>
</view>
<view class="stat-value">{{item.value}}</view>
</view>
</view>
</view>
<!-- 最近活动 -->
<view class="recent-activities">
<view class="section-title">最近活动</view>
<view class="activity-list">
<view
wx:for="{{recentActivities}}"
wx:key="id"
class="activity-item"
>
<view class="activity-icon">{{item.icon}}</view>
<view class="activity-content">
<view class="activity-title">{{item.title}}</view>
<view class="activity-desc">{{item.desc}}</view>
<view class="activity-time">{{item.time}}</view>
</view>
<view class="activity-status {{item.status}}">
{{item.statusText}}
</view>
</view>
</view>
</view>
</view>

View File

@@ -1,210 +0,0 @@
/* pages/dashboard/dashboard.wxss */
.dashboard-container {
min-height: 100vh;
background: #f6f6f6;
padding: 20rpx;
}
.overview-cards {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
margin-bottom: 30rpx;
}
.overview-card {
background: #fff;
border-radius: 16rpx;
padding: 30rpx;
display: flex;
align-items: center;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.overview-card.primary {
border-left: 8rpx solid #1890ff;
}
.overview-card.success {
border-left: 8rpx solid #52c41a;
}
.overview-card.warning {
border-left: 8rpx solid #faad14;
}
.overview-card.danger {
border-left: 8rpx solid #ff4d4f;
}
.card-icon {
font-size: 48rpx;
margin-right: 20rpx;
}
.card-content {
flex: 1;
}
.card-value {
font-size: 36rpx;
font-weight: 600;
color: #333;
margin-bottom: 8rpx;
}
.card-label {
font-size: 24rpx;
color: #666;
margin-bottom: 8rpx;
}
.card-trend {
font-size: 20rpx;
font-weight: 500;
}
.card-trend.up {
color: #52c41a;
}
.card-trend.down {
color: #ff4d4f;
}
.charts-section,
.supervision-stats,
.recent-activities {
background: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 30rpx;
}
.chart-container {
height: 400rpx;
background: #f8f9fa;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
}
.chart-placeholder {
text-align: center;
}
.chart-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.chart-text {
font-size: 28rpx;
color: #999;
}
.stats-list {
space-y: 20rpx;
}
.stat-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.stat-item:last-child {
border-bottom: none;
}
.stat-info {
flex: 1;
}
.stat-name {
font-size: 28rpx;
color: #333;
margin-bottom: 8rpx;
}
.stat-desc {
font-size: 24rpx;
color: #999;
}
.stat-value {
font-size: 32rpx;
font-weight: 600;
color: #1890ff;
}
.activity-list {
space-y: 20rpx;
}
.activity-item {
display: flex;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.activity-item:last-child {
border-bottom: none;
}
.activity-icon {
font-size: 32rpx;
margin-right: 20rpx;
width: 60rpx;
text-align: center;
}
.activity-content {
flex: 1;
}
.activity-title {
font-size: 28rpx;
color: #333;
margin-bottom: 8rpx;
}
.activity-desc {
font-size: 24rpx;
color: #666;
margin-bottom: 8rpx;
}
.activity-time {
font-size: 20rpx;
color: #999;
}
.activity-status {
font-size: 20rpx;
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-weight: 500;
}
.activity-status.success {
background: #f6ffed;
color: #52c41a;
}
.activity-status.pending {
background: #fff7e6;
color: #faad14;
}

View File

@@ -1,42 +0,0 @@
// pages/epidemic/epidemic.js
Page({
data: {
searchKeyword: '',
loading: false,
epidemicList: []
},
onLoad() {
this.loadEpidemicData()
},
loadEpidemicData() {
// 模拟数据
this.setData({
epidemicList: [
{
id: 1,
title: '疫情预警',
description: '发现疑似疫情情况',
location: '宁夏银川市',
level: 'high',
levelText: '高风险',
createTime: '2024-01-15 14:30'
}
]
})
},
onSearchInput(e) {
this.setData({
searchKeyword: e.detail.value
})
},
handleAdd() {
wx.showToast({
title: '新增功能待实现',
icon: 'none'
})
}
})

View File

@@ -1,3 +0,0 @@
{
"usingComponents": {}
}

View File

@@ -1,58 +0,0 @@
<!--pages/epidemic/epidemic.wxml-->
<view class="epidemic-container">
<!-- 搜索栏 -->
<view class="search-section">
<view class="search-bar">
<input
value="{{searchKeyword}}"
type="text"
placeholder="搜索疫情记录..."
class="search-input"
bindinput="onSearchInput"
/>
<view class="search-icon">🔍</view>
</view>
</view>
<!-- 疫情列表 -->
<view class="epidemic-list">
<view class="list-header">
<view class="list-title">疫情监控</view>
<view class="add-btn" bindtap="handleAdd">
<text class="add-text">新增</text>
<view class="add-icon">+</view>
</view>
</view>
<view wx:if="{{loading}}" class="loading">
<text class="loading-text">加载中...</text>
</view>
<view wx:elif="{{epidemicList.length === 0}}" class="empty">
<view class="empty-icon">🦠</view>
<view class="empty-text">暂无疫情记录</view>
</view>
<view wx:else class="list-content">
<view
wx:for="{{epidemicList}}"
wx:key="id"
class="epidemic-item"
>
<view class="item-header">
<view class="item-title">{{item.title}}</view>
<view class="item-level {{item.level}}">
{{item.levelText}}
</view>
</view>
<view class="item-content">
<view class="item-desc">{{item.description}}</view>
<view class="item-meta">
<view class="item-location">📍 {{item.location}}</view>
<view class="item-time">{{item.createTime}}</view>
</view>
</view>
</view>
</view>
</view>
</view>

View File

@@ -1,169 +0,0 @@
/* pages/epidemic/epidemic.wxss */
.epidemic-container {
min-height: 100vh;
background: #f6f6f6;
}
.search-section {
padding: 20rpx;
background: #fff;
margin-bottom: 20rpx;
}
.search-bar {
position: relative;
}
.search-input {
width: 100%;
height: 72rpx;
background: #f8f9fa;
border: none;
border-radius: 36rpx;
padding: 0 60rpx 0 30rpx;
font-size: 28rpx;
color: #333;
}
.search-icon {
position: absolute;
right: 30rpx;
top: 50%;
transform: translateY(-50%);
font-size: 28rpx;
color: #999;
}
.epidemic-list {
background: #fff;
margin: 0 20rpx;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.list-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.list-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.add-btn {
display: flex;
align-items: center;
padding: 16rpx 24rpx;
background: #1890ff;
border-radius: 24rpx;
color: #fff;
}
.add-text {
font-size: 24rpx;
margin-right: 8rpx;
}
.add-icon {
font-size: 24rpx;
font-weight: 600;
}
.loading,
.empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx;
color: #999;
}
.loading-text,
.empty-text {
font-size: 28rpx;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.list-content {
space-y: 0;
}
.epidemic-item {
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
transition: background 0.3s;
}
.epidemic-item:last-child {
border-bottom: none;
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.item-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
flex: 1;
}
.item-level {
font-size: 20rpx;
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-weight: 500;
}
.item-level.high {
background: #fff2f0;
color: #ff4d4f;
}
.item-level.medium {
background: #fff7e6;
color: #faad14;
}
.item-level.low {
background: #f6ffed;
color: #52c41a;
}
.item-content {
margin-bottom: 20rpx;
}
.item-desc {
font-size: 26rpx;
color: #666;
margin-bottom: 12rpx;
line-height: 1.5;
}
.item-meta {
display: flex;
justify-content: space-between;
align-items: center;
}
.item-location,
.item-time {
font-size: 22rpx;
color: #999;
}

View File

@@ -0,0 +1,273 @@
// pages/farmer/add/add.js
Page({
data: {
statusBarHeight: 0,
// 表单数据
formData: {
name: '',
phone: '',
idCard: '',
area: '',
farmName: '',
farmAddress: '',
animalCount: '',
animalType: '',
email: '',
emergencyContact: '',
emergencyPhone: '',
remark: ''
},
// 选择器数据
areaList: [
{ id: 1, name: '银川市' },
{ id: 2, name: '石嘴山市' },
{ id: 3, name: '吴忠市' },
{ id: 4, name: '固原市' },
{ id: 5, name: '中卫市' }
],
areaIndex: -1,
animalTypeList: [
{ id: 1, name: '牛' },
{ id: 2, name: '羊' },
{ id: 3, name: '猪' },
{ id: 4, name: '鸡' },
{ id: 5, name: '其他' }
],
animalTypeIndex: -1,
// 提交状态
isSubmitting: false,
canSubmit: false
},
onLoad(options) {
// 获取状态栏高度
const systemInfo = wx.getSystemInfoSync();
this.setData({
statusBarHeight: systemInfo.statusBarHeight
});
// 检查登录状态
this.checkLoginStatus();
},
// 检查登录状态
checkLoginStatus() {
const token = wx.getStorageSync('token');
if (!token) {
wx.redirectTo({
url: '/pages/login/login'
});
return;
}
},
// 输入框变化处理
onInputChange(e) {
const { field } = e.currentTarget.dataset;
const { value } = e.detail;
this.setData({
[`formData.${field}`]: value
}, () => {
this.validateForm();
});
},
// 区域选择
onAreaChange(e) {
const index = e.detail.value;
const area = this.data.areaList[index];
this.setData({
areaIndex: index,
'formData.area': area ? area.name : ''
}, () => {
this.validateForm();
});
},
// 养殖类型选择
onAnimalTypeChange(e) {
const index = e.detail.value;
const animalType = this.data.animalTypeList[index];
this.setData({
animalTypeIndex: index,
'formData.animalType': animalType ? animalType.name : ''
}, () => {
this.validateForm();
});
},
// 表单验证
validateForm() {
const { formData } = this.data;
// 必填字段验证
const requiredFields = ['name', 'phone', 'area', 'farmName'];
const isValid = requiredFields.every(field => {
return formData[field] && formData[field].trim() !== '';
});
// 手机号格式验证
const phoneRegex = /^1[3-9]\d{9}$/;
const isPhoneValid = phoneRegex.test(formData.phone);
// 身份证号验证(如果填写了)
let isIdCardValid = true;
if (formData.idCard) {
const idCardRegex = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
isIdCardValid = idCardRegex.test(formData.idCard);
}
// 邮箱验证(如果填写了)
let isEmailValid = true;
if (formData.email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
isEmailValid = emailRegex.test(formData.email);
}
this.setData({
canSubmit: isValid && isPhoneValid && isIdCardValid && isEmailValid
});
},
// 取消操作
onCancel() {
// 检查是否有未保存的数据
const hasData = Object.values(this.data.formData).some(value => value && value.trim() !== '');
if (hasData) {
wx.showModal({
title: '提示',
content: '确定要放弃当前编辑的内容吗?',
success: (res) => {
if (res.confirm) {
wx.navigateBack();
}
}
});
} else {
wx.navigateBack();
}
},
// 提交表单
async onSubmit() {
if (!this.data.canSubmit || this.data.isSubmitting) {
return;
}
// 最终验证
if (!this.validateFormData()) {
return;
}
this.setData({ isSubmitting: true });
try {
// 模拟API调用
await this.submitFarmerData();
wx.showToast({
title: '添加成功',
icon: 'success'
});
// 延迟返回,让用户看到成功提示
setTimeout(() => {
wx.navigateBack();
}, 1500);
} catch (error) {
console.error('添加养殖户失败:', error);
wx.showToast({
title: '添加失败,请重试',
icon: 'none'
});
} finally {
this.setData({ isSubmitting: false });
}
},
// 验证表单数据
validateFormData() {
const { formData } = this.data;
if (!formData.name.trim()) {
wx.showToast({ title: '请输入姓名', icon: 'none' });
return false;
}
if (!formData.phone.trim()) {
wx.showToast({ title: '请输入手机号', icon: 'none' });
return false;
}
const phoneRegex = /^1[3-9]\d{9}$/;
if (!phoneRegex.test(formData.phone)) {
wx.showToast({ title: '手机号格式不正确', icon: 'none' });
return false;
}
if (!formData.area.trim()) {
wx.showToast({ title: '请选择所属区域', icon: 'none' });
return false;
}
if (!formData.farmName.trim()) {
wx.showToast({ title: '请输入养殖场名称', icon: 'none' });
return false;
}
// 身份证号验证
if (formData.idCard && formData.idCard.trim()) {
const idCardRegex = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
if (!idCardRegex.test(formData.idCard)) {
wx.showToast({ title: '身份证号格式不正确', icon: 'none' });
return false;
}
}
// 邮箱验证
if (formData.email && formData.email.trim()) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(formData.email)) {
wx.showToast({ title: '邮箱格式不正确', icon: 'none' });
return false;
}
}
// 紧急联系电话验证
if (formData.emergencyPhone && formData.emergencyPhone.trim()) {
if (!phoneRegex.test(formData.emergencyPhone)) {
wx.showToast({ title: '紧急联系电话格式不正确', icon: 'none' });
return false;
}
}
return true;
},
// 提交养殖户数据
submitFarmerData() {
return new Promise((resolve, reject) => {
// 模拟API请求
setTimeout(() => {
// 模拟成功/失败
const success = Math.random() > 0.1; // 90%成功率
if (success) {
console.log('提交的数据:', this.data.formData);
resolve();
} else {
reject(new Error('网络错误'));
}
}, 2000);
});
}
});

View File

@@ -0,0 +1,6 @@
{
"navigationBarTitleText": "添加养殖户",
"navigationStyle": "custom",
"backgroundColor": "#4CAF50",
"backgroundTextStyle": "light"
}

View File

@@ -0,0 +1,205 @@
<!--pages/farmer/add/add.wxml-->
<view class="container">
<!-- 状态栏 -->
<view class="status-bar" style="height: {{statusBarHeight}}px;"></view>
<!-- 表单内容 -->
<scroll-view class="form-container" scroll-y="true">
<!-- 基本信息 -->
<view class="form-section">
<view class="section-title">基本信息</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
<text>姓名</text>
</view>
<input
class="form-input"
placeholder="请输入养殖户姓名"
value="{{formData.name}}"
bindinput="onInputChange"
data-field="name"
/>
</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
<text>手机号</text>
</view>
<input
class="form-input"
placeholder="请输入手机号"
type="number"
maxlength="11"
value="{{formData.phone}}"
bindinput="onInputChange"
data-field="phone"
/>
</view>
<view class="form-item">
<view class="form-label">身份证号</view>
<input
class="form-input"
placeholder="请输入身份证号"
maxlength="18"
value="{{formData.idCard}}"
bindinput="onInputChange"
data-field="idCard"
/>
</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
<text>所属区域</text>
</view>
<picker
mode="selector"
range="{{areaList}}"
range-key="name"
value="{{areaIndex}}"
bindchange="onAreaChange"
>
<view class="form-picker">
<text class="{{formData.area ? '' : 'placeholder'}}">
{{formData.area || '请选择所属区域'}}
</text>
<text class="picker-arrow">></text>
</view>
</picker>
</view>
</view>
<!-- 养殖场信息 -->
<view class="form-section">
<view class="section-title">养殖场信息</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
<text>养殖场名称</text>
</view>
<input
class="form-input"
placeholder="请输入养殖场名称"
value="{{formData.farmName}}"
bindinput="onInputChange"
data-field="farmName"
/>
</view>
<view class="form-item">
<view class="form-label">养殖场地址</view>
<input
class="form-input"
placeholder="请输入养殖场地址"
value="{{formData.farmAddress}}"
bindinput="onInputChange"
data-field="farmAddress"
/>
</view>
<view class="form-item">
<view class="form-label">养殖规模</view>
<input
class="form-input"
placeholder="请输入养殖数量"
type="number"
value="{{formData.animalCount}}"
bindinput="onInputChange"
data-field="animalCount"
/>
</view>
<view class="form-item">
<view class="form-label">养殖类型</view>
<picker
mode="selector"
range="{{animalTypeList}}"
range-key="name"
value="{{animalTypeIndex}}"
bindchange="onAnimalTypeChange"
>
<view class="form-picker">
<text class="{{formData.animalType ? '' : 'placeholder'}}">
{{formData.animalType || '请选择养殖类型'}}
</text>
<text class="picker-arrow">></text>
</view>
</picker>
</view>
</view>
<!-- 联系信息 -->
<view class="form-section">
<view class="section-title">联系信息</view>
<view class="form-item">
<view class="form-label">邮箱</view>
<input
class="form-input"
placeholder="请输入邮箱地址"
value="{{formData.email}}"
bindinput="onInputChange"
data-field="email"
/>
</view>
<view class="form-item">
<view class="form-label">紧急联系人</view>
<input
class="form-input"
placeholder="请输入紧急联系人姓名"
value="{{formData.emergencyContact}}"
bindinput="onInputChange"
data-field="emergencyContact"
/>
</view>
<view class="form-item">
<view class="form-label">紧急联系电话</view>
<input
class="form-input"
placeholder="请输入紧急联系电话"
type="number"
maxlength="11"
value="{{formData.emergencyPhone}}"
bindinput="onInputChange"
data-field="emergencyPhone"
/>
</view>
</view>
<!-- 备注信息 -->
<view class="form-section">
<view class="section-title">备注信息</view>
<view class="form-item">
<view class="form-label">备注</view>
<textarea
class="form-textarea"
placeholder="请输入备注信息"
maxlength="200"
value="{{formData.remark}}"
bindinput="onInputChange"
data-field="remark"
/>
<view class="char-count">{{formData.remark.length}}/200</view>
</view>
</view>
<!-- 底部间距 -->
<view class="bottom-space"></view>
</scroll-view>
<!-- 底部操作栏 -->
<view class="bottom-bar">
<button class="cancel-btn" bindtap="onCancel">取消</button>
<button class="submit-btn" bindtap="onSubmit" disabled="{{!canSubmit}}">
{{isSubmitting ? '提交中...' : '确认添加'}}
</button>
</view>
</view>

View File

@@ -0,0 +1,176 @@
/* pages/farmer/add/add.wxss */
.container {
height: 100vh;
background-color: #f5f5f5;
display: flex;
flex-direction: column;
}
.status-bar {
background-color: #4CAF50;
}
.form-container {
flex: 1;
padding: 20rpx;
}
/* 表单区块 */
.form-section {
background-color: white;
border-radius: 16rpx;
margin-bottom: 20rpx;
overflow: hidden;
}
.section-title {
padding: 30rpx;
font-size: 32rpx;
font-weight: bold;
color: #333;
background-color: #f8f9fa;
border-bottom: 1rpx solid #eee;
}
/* 表单项 */
.form-item {
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.form-item:last-child {
border-bottom: none;
}
.form-label {
display: flex;
align-items: center;
font-size: 28rpx;
color: #333;
margin-bottom: 20rpx;
}
.required {
color: #f44336;
margin-right: 8rpx;
}
/* 输入框 */
.form-input {
width: 100%;
height: 80rpx;
padding: 0 20rpx;
background-color: #f8f9fa;
border: 1rpx solid #e0e0e0;
border-radius: 8rpx;
font-size: 28rpx;
color: #333;
}
.form-input:focus {
border-color: #4CAF50;
background-color: white;
}
/* 选择器 */
.form-picker {
display: flex;
align-items: center;
justify-content: space-between;
height: 80rpx;
padding: 0 20rpx;
background-color: #f8f9fa;
border: 1rpx solid #e0e0e0;
border-radius: 8rpx;
font-size: 28rpx;
}
.placeholder {
color: #999;
}
.picker-arrow {
color: #999;
font-size: 24rpx;
transform: rotate(90deg);
}
/* 文本域 */
.form-textarea {
width: 100%;
min-height: 160rpx;
padding: 20rpx;
background-color: #f8f9fa;
border: 1rpx solid #e0e0e0;
border-radius: 8rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
}
.form-textarea:focus {
border-color: #4CAF50;
background-color: white;
}
.char-count {
text-align: right;
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
}
/* 底部间距 */
.bottom-space {
height: 120rpx;
}
/* 底部操作栏 */
.bottom-bar {
display: flex;
padding: 20rpx;
background-color: white;
border-top: 1rpx solid #eee;
gap: 20rpx;
}
.cancel-btn {
flex: 1;
height: 88rpx;
line-height: 88rpx;
background-color: #f5f5f5;
color: #666;
border: none;
border-radius: 44rpx;
font-size: 32rpx;
text-align: center;
}
.cancel-btn:active {
background-color: #e0e0e0;
}
.submit-btn {
flex: 2;
height: 88rpx;
line-height: 88rpx;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 44rpx;
font-size: 32rpx;
text-align: center;
}
.submit-btn:active {
background-color: #45a049;
}
.submit-btn[disabled] {
background-color: #ccc;
color: #999;
}
.submit-btn[disabled]:active {
background-color: #ccc;
}

View File

@@ -0,0 +1,195 @@
// pages/farmer/detail/detail.js
Page({
data: {
statusBarHeight: 0,
loading: true,
farmerId: '',
farmerInfo: {},
checkRecords: []
},
onLoad(options) {
// 检查登录状态
this.checkLoginStatus();
// 获取状态栏高度
const systemInfo = wx.getSystemInfoSync();
this.setData({
statusBarHeight: systemInfo.statusBarHeight
});
// 获取养殖户ID
if (options.id) {
this.setData({
farmerId: options.id
});
this.loadFarmerDetail();
} else {
wx.showToast({
title: '参数错误',
icon: 'error'
});
setTimeout(() => {
wx.navigateBack();
}, 1500);
}
},
onShow() {
// 检查登录状态
this.checkLoginStatus();
},
// 检查登录状态
checkLoginStatus() {
const token = wx.getStorageSync('token');
if (!token) {
wx.redirectTo({
url: '/pages/login/login'
});
return false;
}
return true;
},
// 加载养殖户详情
async loadFarmerDetail() {
try {
this.setData({ loading: true });
// 模拟API调用
const farmerData = await this.fetchFarmerDetail(this.data.farmerId);
const checkData = await this.fetchCheckRecords(this.data.farmerId);
this.setData({
farmerInfo: farmerData,
checkRecords: checkData,
loading: false
});
} catch (error) {
console.error('加载养殖户详情失败:', error);
wx.showToast({
title: '加载失败',
icon: 'error'
});
this.setData({ loading: false });
}
},
// 模拟获取养殖户详情API
fetchFarmerDetail(farmerId) {
return new Promise((resolve) => {
setTimeout(() => {
const mockData = {
id: farmerId,
name: '张三',
phone: '13800138000',
idCard: '610104199001011234',
area: '西夏区',
status: 'normal',
farmName: '张三养殖场',
farmAddress: '宁夏银川市西夏区兴泾镇',
animalCount: 120,
animalType: '肉牛',
email: 'zhangsan@example.com',
emergencyContact: '李四 13900139000',
registerTime: '2023-01-15',
lastCheckTime: '2024-01-15 14:30',
remarks: '该养殖户管理规范,牲畜健康状况良好,配合度高。'
};
resolve(mockData);
}, 1000);
});
},
// 模拟获取检查记录API
fetchCheckRecords(farmerId) {
return new Promise((resolve) => {
setTimeout(() => {
const mockRecords = [
{
id: '1',
checkTime: '2024-01-15 14:30',
inspector: '王检查员',
result: 'normal',
description: '牲畜健康状况良好,饲料充足,环境卫生达标。'
},
{
id: '2',
checkTime: '2024-01-10 09:15',
inspector: '李检查员',
result: 'warning',
description: '发现部分牲畜有轻微感冒症状,已建议及时治疗。'
},
{
id: '3',
checkTime: '2024-01-05 16:20',
inspector: '张检查员',
result: 'normal',
description: '整体情况良好,建议继续保持现有管理水平。'
},
{
id: '4',
checkTime: '2023-12-28 11:45',
inspector: '王检查员',
result: 'error',
description: '发现饲料存储不当,已要求立即整改。'
}
];
resolve(mockRecords);
}, 800);
});
},
// 编辑养殖户信息
onEdit() {
wx.navigateTo({
url: `/pages/farmer/edit/edit?id=${this.data.farmerId}`
});
},
// 拨打电话
onCall() {
const phone = this.data.farmerInfo.phone;
if (!phone) {
wx.showToast({
title: '无联系电话',
icon: 'error'
});
return;
}
wx.showModal({
title: '拨打电话',
content: `确定要拨打 ${phone} 吗?`,
success: (res) => {
if (res.confirm) {
wx.makePhoneCall({
phoneNumber: phone,
fail: (error) => {
console.error('拨打电话失败:', error);
wx.showToast({
title: '拨打失败',
icon: 'error'
});
}
});
}
}
});
},
// 添加检查记录
onAddCheck() {
wx.navigateTo({
url: `/pages/check/add/add?farmerId=${this.data.farmerId}&farmerName=${this.data.farmerInfo.name}`
});
},
// 下拉刷新
onPullDownRefresh() {
this.loadFarmerDetail().finally(() => {
wx.stopPullDownRefresh();
});
}
});

View File

@@ -0,0 +1,7 @@
{
"navigationBarTitleText": "养殖户详情",
"navigationStyle": "custom",
"backgroundColor": "#f5f5f5",
"backgroundTextStyle": "dark",
"enablePullDownRefresh": true
}

View File

@@ -0,0 +1,125 @@
<!--pages/farmer/detail/detail.wxml-->
<view class="container">
<!-- 状态栏 -->
<view class="status-bar" style="height: {{statusBarHeight}}px;"></view>
<!-- 加载状态 -->
<view wx:if="{{loading}}" class="loading-container">
<view class="loading-spinner"></view>
<text class="loading-text">加载中...</text>
</view>
<!-- 详情内容 -->
<view wx:else class="detail-content">
<!-- 基本信息卡片 -->
<view class="info-card">
<view class="card-header">
<view class="farmer-name">{{farmerInfo.name}}</view>
<view class="status-badge status-{{farmerInfo.status}}">
{{farmerInfo.status === 'normal' ? '正常' : farmerInfo.status === 'error' ? '异常' : '待检查'}}
</view>
</view>
<view class="info-grid">
<view class="info-item">
<view class="info-label">联系电话</view>
<view class="info-value">{{farmerInfo.phone}}</view>
</view>
<view class="info-item">
<view class="info-label">身份证号</view>
<view class="info-value">{{farmerInfo.idCard}}</view>
</view>
<view class="info-item">
<view class="info-label">所属区域</view>
<view class="info-value">{{farmerInfo.area}}</view>
</view>
<view class="info-item">
<view class="info-label">注册时间</view>
<view class="info-value">{{farmerInfo.registerTime}}</view>
</view>
</view>
</view>
<!-- 养殖场信息 -->
<view class="info-card">
<view class="card-title">养殖场信息</view>
<view class="info-grid">
<view class="info-item">
<view class="info-label">养殖场名称</view>
<view class="info-value">{{farmerInfo.farmName}}</view>
</view>
<view class="info-item">
<view class="info-label">养殖场地址</view>
<view class="info-value">{{farmerInfo.farmAddress}}</view>
</view>
<view class="info-item">
<view class="info-label">牲畜数量</view>
<view class="info-value">{{farmerInfo.animalCount}}头</view>
</view>
<view class="info-item">
<view class="info-label">牲畜类型</view>
<view class="info-value">{{farmerInfo.animalType}}</view>
</view>
</view>
</view>
<!-- 联系信息 -->
<view class="info-card">
<view class="card-title">联系信息</view>
<view class="info-grid">
<view class="info-item">
<view class="info-label">邮箱地址</view>
<view class="info-value">{{farmerInfo.email || '未填写'}}</view>
</view>
<view class="info-item">
<view class="info-label">紧急联系人</view>
<view class="info-value">{{farmerInfo.emergencyContact || '未填写'}}</view>
</view>
</view>
</view>
<!-- 检查记录 -->
<view class="info-card">
<view class="card-title">
<text>检查记录</text>
<text class="record-count">({{checkRecords.length}}条)</text>
</view>
<view wx:if="{{checkRecords.length === 0}}" class="empty-records">
<text>暂无检查记录</text>
</view>
<view wx:else class="records-list">
<view
wx:for="{{checkRecords}}"
wx:key="id"
class="record-item"
>
<view class="record-header">
<view class="record-time">{{item.checkTime}}</view>
<view class="record-status status-{{item.result}}">
{{item.result === 'normal' ? '正常' : item.result === 'warning' ? '警告' : '异常'}}
</view>
</view>
<view class="record-content">
<view class="record-inspector">检查员:{{item.inspector}}</view>
<view class="record-desc">{{item.description}}</view>
</view>
</view>
</view>
</view>
<!-- 备注信息 -->
<view wx:if="{{farmerInfo.remarks}}" class="info-card">
<view class="card-title">备注信息</view>
<view class="remarks-content">{{farmerInfo.remarks}}</view>
</view>
</view>
<!-- 底部操作栏 -->
<view class="bottom-bar">
<button class="action-btn edit-btn" bindtap="onEdit">编辑信息</button>
<button class="action-btn call-btn" bindtap="onCall">拨打电话</button>
<button class="action-btn check-btn" bindtap="onAddCheck">添加检查</button>
</view>
</view>

View File

@@ -0,0 +1,241 @@
/* pages/farmer/detail/detail.wxss */
.container {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 120rpx;
}
.status-bar {
background-color: #2c5aa0;
}
/* 加载状态 */
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 60vh;
}
.loading-spinner {
width: 60rpx;
height: 60rpx;
border: 4rpx solid #f3f3f3;
border-top: 4rpx solid #2c5aa0;
border-radius: 50%;
animation: spin 1s linear infinite;
}
.loading-text {
margin-top: 20rpx;
font-size: 28rpx;
color: #666;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 详情内容 */
.detail-content {
padding: 20rpx;
}
/* 信息卡片 */
.info-card {
background-color: white;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30rpx;
}
.farmer-name {
font-size: 36rpx;
font-weight: bold;
color: #333;
}
.status-badge {
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-size: 24rpx;
font-weight: bold;
}
.status-normal {
background-color: #e8f5e8;
color: #52c41a;
}
.status-error {
background-color: #fff2f0;
color: #ff4d4f;
}
.status-pending {
background-color: #fff7e6;
color: #fa8c16;
}
.card-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 24rpx;
display: flex;
align-items: center;
}
.record-count {
font-size: 24rpx;
color: #999;
font-weight: normal;
margin-left: 8rpx;
}
/* 信息网格 */
.info-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24rpx;
}
.info-item {
display: flex;
flex-direction: column;
}
.info-label {
font-size: 26rpx;
color: #666;
margin-bottom: 8rpx;
}
.info-value {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
/* 备注信息 */
.remarks-content {
font-size: 28rpx;
color: #333;
line-height: 1.6;
background-color: #f8f9fa;
padding: 20rpx;
border-radius: 12rpx;
}
/* 检查记录 */
.empty-records {
text-align: center;
padding: 60rpx 0;
color: #999;
font-size: 28rpx;
}
.records-list {
max-height: 600rpx;
overflow-y: auto;
}
.record-item {
border-bottom: 1rpx solid #f0f0f0;
padding: 20rpx 0;
}
.record-item:last-child {
border-bottom: none;
}
.record-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12rpx;
}
.record-time {
font-size: 26rpx;
color: #666;
}
.record-status {
padding: 4rpx 12rpx;
border-radius: 12rpx;
font-size: 22rpx;
font-weight: bold;
}
.record-content {
margin-left: 0;
}
.record-inspector {
font-size: 24rpx;
color: #999;
margin-bottom: 8rpx;
}
.record-desc {
font-size: 26rpx;
color: #333;
line-height: 1.5;
}
/* 底部操作栏 */
.bottom-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: white;
padding: 20rpx;
border-top: 1rpx solid #f0f0f0;
display: flex;
gap: 20rpx;
box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.action-btn {
flex: 1;
height: 80rpx;
border-radius: 40rpx;
font-size: 28rpx;
font-weight: bold;
border: none;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.action-btn:active {
transform: scale(0.95);
}
.edit-btn {
background-color: #2c5aa0;
color: white;
}
.call-btn {
background-color: #52c41a;
color: white;
}
.check-btn {
background-color: #fa8c16;
color: white;
}

View File

@@ -0,0 +1,406 @@
// pages/farmer/edit/edit.js
Page({
data: {
statusBarHeight: 0,
loading: true,
farmerId: '',
// 表单数据
formData: {
name: '',
phone: '',
idCard: '',
area: '',
status: '',
farmName: '',
farmAddress: '',
animalCount: '',
animalType: '',
email: '',
emergencyContact: '',
emergencyPhone: '',
remark: ''
},
// 原始数据(用于比较是否有修改)
originalData: {},
// 选择器数据
areaList: [
{ id: 1, name: '银川市' },
{ id: 2, name: '石嘴山市' },
{ id: 3, name: '吴忠市' },
{ id: 4, name: '固原市' },
{ id: 5, name: '中卫市' }
],
areaIndex: -1,
statusList: [
{ id: 1, name: '正常' },
{ id: 2, name: '异常' },
{ id: 3, name: '待审核' }
],
statusIndex: -1,
animalTypeList: [
{ id: 1, name: '牛' },
{ id: 2, name: '羊' },
{ id: 3, name: '猪' },
{ id: 4, name: '鸡' },
{ id: 5, name: '其他' }
],
animalTypeIndex: -1,
// 提交状态
isSubmitting: false,
canSubmit: false
},
onLoad(options) {
// 获取状态栏高度
const systemInfo = wx.getSystemInfoSync();
this.setData({
statusBarHeight: systemInfo.statusBarHeight,
farmerId: options.id || ''
});
// 检查登录状态
this.checkLoginStatus();
// 加载养殖户数据
if (this.data.farmerId) {
this.loadFarmerData();
} else {
wx.showToast({
title: '参数错误',
icon: 'none'
});
setTimeout(() => {
wx.navigateBack();
}, 1500);
}
},
// 检查登录状态
checkLoginStatus() {
const token = wx.getStorageSync('token');
if (!token) {
wx.redirectTo({
url: '/pages/login/login'
});
return;
}
},
// 加载养殖户数据
async loadFarmerData() {
try {
wx.showLoading({ title: '加载中...' });
// 模拟API调用
const farmerData = await this.fetchFarmerData(this.data.farmerId);
// 设置表单数据
this.setData({
formData: { ...farmerData },
originalData: { ...farmerData },
loading: false
});
// 设置选择器索引
this.setPickerIndexes(farmerData);
wx.hideLoading();
} catch (error) {
console.error('加载养殖户数据失败:', error);
wx.hideLoading();
wx.showToast({
title: '加载失败',
icon: 'none'
});
setTimeout(() => {
wx.navigateBack();
}, 1500);
}
},
// 模拟获取养殖户数据
fetchFarmerData(id) {
return new Promise((resolve) => {
setTimeout(() => {
// 模拟数据
const mockData = {
id: id,
name: '张三',
phone: '13800138000',
idCard: '640100199001011234',
area: '银川市',
status: '正常',
farmName: '张三养殖场',
farmAddress: '银川市兴庆区某某村',
animalCount: '100',
animalType: '牛',
email: 'zhangsan@example.com',
emergencyContact: '李四',
emergencyPhone: '13900139000',
remark: '优质养殖户,管理规范'
};
resolve(mockData);
}, 1000);
});
},
// 设置选择器索引
setPickerIndexes(data) {
// 设置区域索引
const areaIndex = this.data.areaList.findIndex(item => item.name === data.area);
// 设置状态索引
const statusIndex = this.data.statusList.findIndex(item => item.name === data.status);
// 设置养殖类型索引
const animalTypeIndex = this.data.animalTypeList.findIndex(item => item.name === data.animalType);
this.setData({
areaIndex: areaIndex >= 0 ? areaIndex : -1,
statusIndex: statusIndex >= 0 ? statusIndex : -1,
animalTypeIndex: animalTypeIndex >= 0 ? animalTypeIndex : -1
});
this.validateForm();
},
// 输入框变化处理
onInputChange(e) {
const { field } = e.currentTarget.dataset;
const { value } = e.detail;
this.setData({
[`formData.${field}`]: value
}, () => {
this.validateForm();
});
},
// 区域选择
onAreaChange(e) {
const index = e.detail.value;
const area = this.data.areaList[index];
this.setData({
areaIndex: index,
'formData.area': area ? area.name : ''
}, () => {
this.validateForm();
});
},
// 状态选择
onStatusChange(e) {
const index = e.detail.value;
const status = this.data.statusList[index];
this.setData({
statusIndex: index,
'formData.status': status ? status.name : ''
}, () => {
this.validateForm();
});
},
// 养殖类型选择
onAnimalTypeChange(e) {
const index = e.detail.value;
const animalType = this.data.animalTypeList[index];
this.setData({
animalTypeIndex: index,
'formData.animalType': animalType ? animalType.name : ''
}, () => {
this.validateForm();
});
},
// 表单验证
validateForm() {
const { formData } = this.data;
// 必填字段验证
const requiredFields = ['name', 'phone', 'area', 'farmName'];
const isValid = requiredFields.every(field => {
return formData[field] && formData[field].trim() !== '';
});
// 手机号格式验证
const phoneRegex = /^1[3-9]\d{9}$/;
const isPhoneValid = phoneRegex.test(formData.phone);
// 身份证号验证(如果填写了)
let isIdCardValid = true;
if (formData.idCard) {
const idCardRegex = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
isIdCardValid = idCardRegex.test(formData.idCard);
}
// 邮箱验证(如果填写了)
let isEmailValid = true;
if (formData.email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
isEmailValid = emailRegex.test(formData.email);
}
// 检查是否有修改
const hasChanges = this.hasDataChanged();
this.setData({
canSubmit: isValid && isPhoneValid && isIdCardValid && isEmailValid && hasChanges
});
},
// 检查数据是否有修改
hasDataChanged() {
const { formData, originalData } = this.data;
return Object.keys(formData).some(key => {
return formData[key] !== originalData[key];
});
},
// 取消操作
onCancel() {
// 检查是否有未保存的修改
if (this.hasDataChanged()) {
wx.showModal({
title: '提示',
content: '确定要放弃当前修改的内容吗?',
success: (res) => {
if (res.confirm) {
wx.navigateBack();
}
}
});
} else {
wx.navigateBack();
}
},
// 提交表单
async onSubmit() {
if (!this.data.canSubmit || this.data.isSubmitting) {
return;
}
// 最终验证
if (!this.validateFormData()) {
return;
}
this.setData({ isSubmitting: true });
try {
// 模拟API调用
await this.updateFarmerData();
wx.showToast({
title: '保存成功',
icon: 'success'
});
// 延迟返回,让用户看到成功提示
setTimeout(() => {
wx.navigateBack();
}, 1500);
} catch (error) {
console.error('保存养殖户数据失败:', error);
wx.showToast({
title: '保存失败,请重试',
icon: 'none'
});
} finally {
this.setData({ isSubmitting: false });
}
},
// 验证表单数据
validateFormData() {
const { formData } = this.data;
if (!formData.name.trim()) {
wx.showToast({ title: '请输入姓名', icon: 'none' });
return false;
}
if (!formData.phone.trim()) {
wx.showToast({ title: '请输入手机号', icon: 'none' });
return false;
}
const phoneRegex = /^1[3-9]\d{9}$/;
if (!phoneRegex.test(formData.phone)) {
wx.showToast({ title: '手机号格式不正确', icon: 'none' });
return false;
}
if (!formData.area.trim()) {
wx.showToast({ title: '请选择所属区域', icon: 'none' });
return false;
}
if (!formData.farmName.trim()) {
wx.showToast({ title: '请输入养殖场名称', icon: 'none' });
return false;
}
// 身份证号验证
if (formData.idCard && formData.idCard.trim()) {
const idCardRegex = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
if (!idCardRegex.test(formData.idCard)) {
wx.showToast({ title: '身份证号格式不正确', icon: 'none' });
return false;
}
}
// 邮箱验证
if (formData.email && formData.email.trim()) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(formData.email)) {
wx.showToast({ title: '邮箱格式不正确', icon: 'none' });
return false;
}
}
// 紧急联系电话验证
if (formData.emergencyPhone && formData.emergencyPhone.trim()) {
const phoneRegex = /^1[3-9]\d{9}$/;
if (!phoneRegex.test(formData.emergencyPhone)) {
wx.showToast({ title: '紧急联系电话格式不正确', icon: 'none' });
return false;
}
}
return true;
},
// 更新养殖户数据
updateFarmerData() {
return new Promise((resolve, reject) => {
// 模拟API请求
setTimeout(() => {
// 模拟成功/失败
const success = Math.random() > 0.1; // 90%成功率
if (success) {
console.log('更新的数据:', {
id: this.data.farmerId,
...this.data.formData
});
resolve();
} else {
reject(new Error('网络错误'));
}
}, 2000);
});
}
});

View File

@@ -0,0 +1,6 @@
{
"navigationBarTitleText": "编辑养殖户",
"navigationStyle": "custom",
"backgroundColor": "#4CAF50",
"backgroundTextStyle": "light"
}

View File

@@ -0,0 +1,229 @@
<!--pages/farmer/edit/edit.wxml-->
<view class="container">
<!-- 状态栏 -->
<view class="status-bar" style="height: {{statusBarHeight}}px;"></view>
<!-- 加载状态 -->
<view wx:if="{{loading}}" class="loading-container">
<view class="loading-spinner"></view>
<text class="loading-text">加载中...</text>
</view>
<!-- 表单内容 -->
<scroll-view wx:else class="form-container" scroll-y="true">
<!-- 基本信息 -->
<view class="form-section">
<view class="section-title">基本信息</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
<text>姓名</text>
</view>
<input
class="form-input"
placeholder="请输入养殖户姓名"
value="{{formData.name}}"
bindinput="onInputChange"
data-field="name"
/>
</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
<text>手机号</text>
</view>
<input
class="form-input"
placeholder="请输入手机号"
type="number"
maxlength="11"
value="{{formData.phone}}"
bindinput="onInputChange"
data-field="phone"
/>
</view>
<view class="form-item">
<view class="form-label">身份证号</view>
<input
class="form-input"
placeholder="请输入身份证号"
maxlength="18"
value="{{formData.idCard}}"
bindinput="onInputChange"
data-field="idCard"
/>
</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
<text>所属区域</text>
</view>
<picker
mode="selector"
range="{{areaList}}"
range-key="name"
value="{{areaIndex}}"
bindchange="onAreaChange"
>
<view class="form-picker">
<text class="{{formData.area ? '' : 'placeholder'}}">
{{formData.area || '请选择所属区域'}}
</text>
<text class="picker-arrow">></text>
</view>
</picker>
</view>
<view class="form-item">
<view class="form-label">状态</view>
<picker
mode="selector"
range="{{statusList}}"
range-key="name"
value="{{statusIndex}}"
bindchange="onStatusChange"
>
<view class="form-picker">
<text class="{{formData.status ? '' : 'placeholder'}}">
{{formData.status || '请选择状态'}}
</text>
<text class="picker-arrow">></text>
</view>
</picker>
</view>
</view>
<!-- 养殖场信息 -->
<view class="form-section">
<view class="section-title">养殖场信息</view>
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
<text>养殖场名称</text>
</view>
<input
class="form-input"
placeholder="请输入养殖场名称"
value="{{formData.farmName}}"
bindinput="onInputChange"
data-field="farmName"
/>
</view>
<view class="form-item">
<view class="form-label">养殖场地址</view>
<input
class="form-input"
placeholder="请输入养殖场地址"
value="{{formData.farmAddress}}"
bindinput="onInputChange"
data-field="farmAddress"
/>
</view>
<view class="form-item">
<view class="form-label">养殖规模</view>
<input
class="form-input"
placeholder="请输入养殖数量"
type="number"
value="{{formData.animalCount}}"
bindinput="onInputChange"
data-field="animalCount"
/>
</view>
<view class="form-item">
<view class="form-label">养殖类型</view>
<picker
mode="selector"
range="{{animalTypeList}}"
range-key="name"
value="{{animalTypeIndex}}"
bindchange="onAnimalTypeChange"
>
<view class="form-picker">
<text class="{{formData.animalType ? '' : 'placeholder'}}">
{{formData.animalType || '请选择养殖类型'}}
</text>
<text class="picker-arrow">></text>
</view>
</picker>
</view>
</view>
<!-- 联系信息 -->
<view class="form-section">
<view class="section-title">联系信息</view>
<view class="form-item">
<view class="form-label">邮箱</view>
<input
class="form-input"
placeholder="请输入邮箱地址"
value="{{formData.email}}"
bindinput="onInputChange"
data-field="email"
/>
</view>
<view class="form-item">
<view class="form-label">紧急联系人</view>
<input
class="form-input"
placeholder="请输入紧急联系人姓名"
value="{{formData.emergencyContact}}"
bindinput="onInputChange"
data-field="emergencyContact"
/>
</view>
<view class="form-item">
<view class="form-label">紧急联系电话</view>
<input
class="form-input"
placeholder="请输入紧急联系电话"
type="number"
maxlength="11"
value="{{formData.emergencyPhone}}"
bindinput="onInputChange"
data-field="emergencyPhone"
/>
</view>
</view>
<!-- 备注信息 -->
<view class="form-section">
<view class="section-title">备注信息</view>
<view class="form-item">
<view class="form-label">备注</view>
<textarea
class="form-textarea"
placeholder="请输入备注信息"
maxlength="200"
value="{{formData.remark}}"
bindinput="onInputChange"
data-field="remark"
/>
<view class="char-count">{{formData.remark.length}}/200</view>
</view>
</view>
<!-- 底部间距 -->
<view class="bottom-space"></view>
</scroll-view>
<!-- 底部操作栏 -->
<view wx:if="{{!loading}}" class="bottom-bar">
<button class="cancel-btn" bindtap="onCancel">取消</button>
<button class="submit-btn" bindtap="onSubmit" disabled="{{!canSubmit}}">
{{isSubmitting ? '保存中...' : '保存修改'}}
</button>
</view>
</view>

View File

@@ -0,0 +1,205 @@
/* pages/farmer/edit/edit.wxss */
.container {
height: 100vh;
background-color: #f5f5f5;
display: flex;
flex-direction: column;
}
.status-bar {
background-color: #4CAF50;
}
/* 加载状态 */
.loading-container {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.loading-spinner {
width: 60rpx;
height: 60rpx;
border: 4rpx solid #f3f3f3;
border-top: 4rpx solid #4CAF50;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-text {
margin-top: 20rpx;
font-size: 28rpx;
color: #666;
}
.form-container {
flex: 1;
padding: 20rpx;
}
/* 表单区块 */
.form-section {
background-color: white;
border-radius: 16rpx;
margin-bottom: 20rpx;
overflow: hidden;
}
.section-title {
padding: 30rpx;
font-size: 32rpx;
font-weight: bold;
color: #333;
background-color: #f8f9fa;
border-bottom: 1rpx solid #eee;
}
/* 表单项 */
.form-item {
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.form-item:last-child {
border-bottom: none;
}
.form-label {
display: flex;
align-items: center;
font-size: 28rpx;
color: #333;
margin-bottom: 20rpx;
}
.required {
color: #f44336;
margin-right: 8rpx;
}
/* 输入框 */
.form-input {
width: 100%;
height: 80rpx;
padding: 0 20rpx;
background-color: #f8f9fa;
border: 1rpx solid #e0e0e0;
border-radius: 8rpx;
font-size: 28rpx;
color: #333;
}
.form-input:focus {
border-color: #4CAF50;
background-color: white;
}
/* 选择器 */
.form-picker {
display: flex;
align-items: center;
justify-content: space-between;
height: 80rpx;
padding: 0 20rpx;
background-color: #f8f9fa;
border: 1rpx solid #e0e0e0;
border-radius: 8rpx;
font-size: 28rpx;
}
.placeholder {
color: #999;
}
.picker-arrow {
color: #999;
font-size: 24rpx;
transform: rotate(90deg);
}
/* 文本域 */
.form-textarea {
width: 100%;
min-height: 160rpx;
padding: 20rpx;
background-color: #f8f9fa;
border: 1rpx solid #e0e0e0;
border-radius: 8rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
}
.form-textarea:focus {
border-color: #4CAF50;
background-color: white;
}
.char-count {
text-align: right;
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
}
/* 底部间距 */
.bottom-space {
height: 120rpx;
}
/* 底部操作栏 */
.bottom-bar {
display: flex;
padding: 20rpx;
background-color: white;
border-top: 1rpx solid #eee;
gap: 20rpx;
}
.cancel-btn {
flex: 1;
height: 88rpx;
line-height: 88rpx;
background-color: #f5f5f5;
color: #666;
border: none;
border-radius: 44rpx;
font-size: 32rpx;
text-align: center;
}
.cancel-btn:active {
background-color: #e0e0e0;
}
.submit-btn {
flex: 2;
height: 88rpx;
line-height: 88rpx;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 44rpx;
font-size: 32rpx;
text-align: center;
}
.submit-btn:active {
background-color: #45a049;
}
.submit-btn[disabled] {
background-color: #ccc;
color: #999;
}
.submit-btn[disabled]:active {
background-color: #ccc;
}

View File

@@ -0,0 +1,342 @@
// pages/farmer/farmer.js
const app = getApp();
const apiService = require('../../utils/api.js');
Page({
data: {
statusBarHeight: 0,
loading: true,
// 搜索和筛选
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
}
},
onLoad() {
// 获取状态栏高度
const systemInfo = wx.getSystemInfoSync();
this.setData({
statusBarHeight: systemInfo.statusBarHeight
});
// 检查登录状态
this.checkLoginStatus();
},
onShow() {
// 每次显示页面时刷新数据
this.loadData();
},
// 检查登录状态
checkLoginStatus() {
const token = wx.getStorageSync('token');
if (!token) {
wx.reLaunch({
url: '/pages/login/login'
});
return false;
}
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 });
}
},
// 加载养殖户列表
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
};
this.setData({ statistics });
},
onPullDownRefresh() {
this.loadData(true).finally(() => {
wx.stopPullDownRefresh();
});
},
onReachBottom() {
this.onLoadMore();
},
// 搜索输入
onSearchInput(e) {
this.setData({
searchKeyword: e.detail.value
});
},
// 执行搜索
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;
wx.showModal({
title: '拨打电话',
content: `确定要拨打 ${farmer.name} 的电话吗?`,
success: (res) => {
if (res.confirm) {
wx.makePhoneCall({
phoneNumber: farmer.phone.replace(/\*/g, '1') // 替换掩码
});
}
}
});
},
// 加载更多
onLoadMore() {
if (!this.data.hasMore || this.data.loading) return;
this.setData({
currentPage: this.data.currentPage + 1
});
this.loadData(false);
},
// 分享页面
onShareAppMessage() {
return {
title: '政府监管端 - 养殖户管理',
path: '/pages/farmer/farmer'
};
}
});

View File

@@ -0,0 +1,7 @@
{
"usingComponents": {},
"navigationBarTitleText": "养殖户管理",
"navigationStyle": "custom",
"enablePullDownRefresh": true,
"onReachBottomDistance": 50
}

View File

@@ -0,0 +1,139 @@
<!--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="header">
<view class="header-title">养殖户管理</view>
<view class="header-actions">
<view class="action-btn" bindtap="onMenuTap">
<text class="icon-more">⋯</text>
</view>
<view class="action-btn">
<text class="icon-minus"></text>
</view>
<view class="action-btn">
<text class="icon-circle">●</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>
<!-- 操作按钮 -->
<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>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" wx:if="{{farmerList.length === 0 && !loading}}">
<text class="empty-text">暂无养殖户数据</text>
</view>
<!-- 加载状态 -->
<view class="loading-state" wx:if="{{loading}}">
<text>加载中...</text>
</view>
</view>
<!-- 底部新增按钮 -->
<view class="bottom-section">
<view class="add-farmer-btn" bindtap="addFarmer">
新增监管养殖户
</view>
</view>
</view>

View File

@@ -0,0 +1,371 @@
/* pages/farmer/farmer.wxss */
.container {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 120rpx; /* 为底部按钮留出空间 */
}
/* 确保状态栏在顶部 */
.status-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
}
.header {
margin-top: 60rpx; /* 为状态栏留出空间 */
}
/* 状态栏 */
.status-bar {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #ffffff;
padding: 10rpx 30rpx;
height: 60rpx;
box-sizing: border-box;
font-size: 24rpx;
color: #333333;
}
.status-left {
display: flex;
align-items: center;
}
.status-right {
display: flex;
align-items: center;
gap: 10rpx;
}
.battery-icon {
width: 20rpx;
height: 12rpx;
background-color: #333333;
border-radius: 2rpx;
position: relative;
}
.battery-icon::after {
content: '';
position: absolute;
right: -4rpx;
top: 3rpx;
width: 2rpx;
height: 6rpx;
background-color: #333333;
border-radius: 0 1rpx 1rpx 0;
}
/* 顶部标题栏 */
.header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #ffffff;
padding: 20rpx 30rpx;
height: 100rpx;
box-sizing: border-box;
position: relative;
border-bottom: 1rpx solid #f0f0f0;
}
.header-title {
color: #333333;
font-size: 36rpx;
font-weight: 500;
position: absolute;
left: 50%;
transform: translateX(-50%);
line-height: 1.2;
}
.header-actions {
display: flex;
gap: 15rpx;
margin-left: auto;
}
.action-btn {
width: 30rpx;
height: 30rpx;
display: flex;
align-items: center;
justify-content: center;
color: #999999;
font-size: 20rpx;
font-weight: bold;
}
/* 搜索区域 */
.search-section {
background-color: #ffffff;
padding: 20rpx 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.search-box {
display: flex;
align-items: center;
background-color: #f8f8f8;
border-radius: 20rpx;
padding: 0 20rpx;
height: 70rpx;
border: 1rpx solid #e0e0e0;
}
.search-icon {
font-size: 26rpx;
color: #999999;
margin-right: 12rpx;
}
.search-input {
flex: 1;
font-size: 26rpx;
color: #333333;
background: transparent;
}
/* 养殖户列表 */
.farmer-list {
padding: 20rpx 30rpx;
}
.farmer-card {
background-color: white;
border-radius: 16rpx;
margin: 0 20rpx 20rpx;
padding: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.farmer-card:active {
transform: scale(0.98);
}
/* 养殖户信息 */
.farmer-info {
margin-bottom: 20rpx;
}
.farmer-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.farmer-name {
font-size: 36rpx;
font-weight: bold;
color: #333;
}
.farmer-status {
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-size: 24rpx;
color: white;
}
.status-normal {
background-color: #4CAF50;
}
.status-error {
background-color: #f44336;
}
.status-pending {
background-color: #ff9800;
}
.farmer-details {
display: flex;
flex-direction: column;
gap: 12rpx;
}
.detail-item {
display: flex;
align-items: center;
}
.detail-label {
font-size: 28rpx;
color: #666;
width: 160rpx;
flex-shrink: 0;
}
.detail-value {
font-size: 28rpx;
color: #333;
flex: 1;
}
/* 操作按钮 */
.farmer-actions {
display: flex;
justify-content: space-between;
gap: 20rpx;
padding-top: 20rpx;
border-top: 1rpx solid #eee;
}
.action-btn {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20rpx 10rpx;
border-radius: 12rpx;
transition: all 0.3s ease;
}
.edit-btn {
background-color: #e3f2fd;
color: #1976d2;
}
.status-btn {
background-color: #fff3e0;
color: #f57c00;
}
.delete-btn {
background-color: #ffebee;
color: #d32f2f;
}
.action-btn:active {
transform: scale(0.95);
opacity: 0.8;
}
.action-icon {
font-size: 32rpx;
margin-bottom: 8rpx;
}
.action-text {
font-size: 24rpx;
text-align: center;
}
/* 卡片头部 */
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 25rpx 25rpx 20rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.farmer-name {
display: flex;
align-items: center;
}
.farmer-name .label {
font-size: 26rpx;
color: #999999;
margin-right: 8rpx;
}
.farmer-name .value {
font-size: 30rpx;
font-weight: 500;
color: #333333;
}
.edit-btn {
background-color: #4CAF50;
color: #ffffff;
padding: 10rpx 20rpx;
border-radius: 4rpx;
font-size: 22rpx;
font-weight: 500;
}
/* 卡片内容 */
.card-content {
padding: 20rpx 25rpx 25rpx;
}
.info-row {
display: flex;
align-items: flex-start;
margin-bottom: 14rpx;
line-height: 1.3;
}
.info-row:last-child {
margin-bottom: 0;
}
.info-row .label {
font-size: 24rpx;
color: #999999;
min-width: 140rpx;
margin-right: 8rpx;
}
.info-row .value {
font-size: 24rpx;
color: #333333;
flex: 1;
word-break: break-all;
}
/* 空状态 */
.empty-state {
display: flex;
justify-content: center;
align-items: center;
padding: 100rpx 0;
}
.empty-text {
font-size: 28rpx;
color: #999999;
}
/* 加载状态 */
.loading-state {
display: flex;
justify-content: center;
padding: 40rpx 0;
font-size: 28rpx;
color: #666666;
}
/* 底部新增按钮 */
.bottom-section {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #ffffff;
padding: 20rpx 30rpx;
border-top: 1rpx solid #f0f0f0;
z-index: 100;
}
.add-farmer-btn {
background-color: #4CAF50;
color: #ffffff;
text-align: center;
padding: 20rpx 0;
border-radius: 6rpx;
font-size: 28rpx;
font-weight: 500;
width: 100%;
}

View File

@@ -0,0 +1,125 @@
// pages/farmers/farmers.js
Page({
data: {
farmers: [],
loading: false,
searchKeyword: '',
currentPage: 1,
pageSize: 10,
total: 0
},
onLoad: function (options) {
this.checkLoginStatus();
this.loadFarmers();
},
onShow: function () {
this.loadFarmers();
},
checkLoginStatus: function() {
const token = wx.getStorageSync('token');
if (!token) {
wx.reLaunch({
url: '/pages/login/login'
});
return false;
}
return true;
},
loadFarmers: function() {
if (!this.checkLoginStatus()) return;
this.setData({
loading: true
});
// 模拟数据
const mockFarmers = [
{
id: 1,
name: '张三',
phone: '13800138001',
address: '宁夏银川市兴庆区',
farmName: '张三养殖场',
animalCount: 120,
status: 'active'
},
{
id: 2,
name: '李四',
phone: '13800138002',
address: '宁夏银川市金凤区',
farmName: '李四牧场',
animalCount: 85,
status: 'active'
},
{
id: 3,
name: '王五',
phone: '13800138003',
address: '宁夏银川市西夏区',
farmName: '王五养殖合作社',
animalCount: 200,
status: 'inactive'
}
];
setTimeout(() => {
this.setData({
farmers: mockFarmers,
loading: false,
total: mockFarmers.length
});
}, 500);
},
onSearchInput: function(e) {
this.setData({
searchKeyword: e.detail.value
});
},
onSearch: function() {
this.loadFarmers();
},
onFarmerTap: function(e) {
const farmerId = e.currentTarget.dataset.id;
wx.navigateTo({
url: `/pages/farmer/detail/detail?id=${farmerId}`
});
},
onAddFarmer: function() {
wx.navigateTo({
url: '/pages/farmer/add/add'
});
},
onPullDownRefresh: function() {
this.loadFarmers();
wx.stopPullDownRefresh();
},
onReachBottom: function() {
// 加载更多数据
if (this.data.farmers.length < this.data.total) {
this.loadMoreFarmers();
}
},
loadMoreFarmers: function() {
// 实现分页加载
console.log('加载更多养殖户数据');
},
onShareAppMessage: function() {
return {
title: '养殖户管理',
path: '/pages/farmers/farmers'
};
}
});

View File

@@ -0,0 +1,8 @@
{
"navigationBarTitleText": "养殖户管理",
"navigationBarBackgroundColor": "#7CB342",
"navigationBarTextStyle": "white",
"backgroundColor": "#f5f5f5",
"enablePullDownRefresh": true,
"onReachBottomDistance": 50
}

View File

@@ -0,0 +1,80 @@
<!--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>
<view class="add-btn" bindtap="onAddFarmer">
<text class="add-icon">+</text>
<text class="add-text">新增</text>
</view>
</view>
<!-- 养殖户列表 -->
<view class="farmers-list">
<view
class="farmer-item"
wx:for="{{farmers}}"
wx:key="id"
data-id="{{item.id}}"
bindtap="onFarmerTap"
>
<view class="farmer-avatar">
<text class="avatar-text">{{item.name.charAt(0)}}</text>
</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>
</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>
</view>
<view class="farmer-arrow">
<text class="arrow-icon">></text>
</view>
</view>
</view>
<!-- 加载状态 -->
<view class="loading" wx:if="{{loading}}">
<text class="loading-text">加载中...</text>
</view>
<!-- 空状态 -->
<view class="empty-state" wx:if="{{!loading && farmers.length === 0}}">
<text class="empty-icon">📋</text>
<text class="empty-text">暂无养殖户数据</text>
<view class="empty-btn" bindtap="onAddFarmer">
<text>添加养殖户</text>
</view>
</view>
</view>

View File

@@ -0,0 +1,220 @@
/* pages/farmers/farmers.wxss */
.container {
min-height: 100vh;
background-color: #f5f5f5;
padding: 0;
}
/* 搜索栏样式 */
.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;
}
.search-input-wrapper {
flex: 1;
display: flex;
align-items: center;
background-color: #f8f8f8;
border-radius: 25rpx;
padding: 0 20rpx;
height: 70rpx;
}
.search-input {
flex: 1;
font-size: 28rpx;
color: #333333;
height: 100%;
}
.search-btn {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #7CB342;
border-radius: 50%;
margin-left: 10rpx;
}
.search-icon {
color: white;
font-size: 24rpx;
}
.add-btn {
display: flex;
align-items: center;
background-color: #7CB342;
color: white;
padding: 15rpx 25rpx;
border-radius: 35rpx;
gap: 8rpx;
}
.add-icon {
font-size: 28rpx;
font-weight: bold;
}
.add-text {
font-size: 26rpx;
}
/* 养殖户列表样式 */
.farmers-list {
padding: 20rpx;
}
.farmer-item {
display: flex;
align-items: center;
background-color: #ffffff;
border-radius: 12rpx;
padding: 25rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
gap: 20rpx;
}
.farmer-avatar {
width: 80rpx;
height: 80rpx;
background-color: #7CB342;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.avatar-text {
color: white;
font-size: 32rpx;
font-weight: bold;
}
.farmer-info {
flex: 1;
min-width: 0;
}
.farmer-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 15rpx;
}
.farmer-name {
font-size: 32rpx;
font-weight: 500;
color: #333333;
}
.farmer-status {
padding: 6rpx 16rpx;
border-radius: 20rpx;
font-size: 22rpx;
}
.farmer-status.active {
background-color: #e8f5e8;
color: #4CAF50;
}
.farmer-status.inactive {
background-color: #ffeaea;
color: #f44336;
}
.farmer-details {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.detail-item {
display: flex;
align-items: center;
font-size: 24rpx;
}
.detail-label {
color: #666666;
width: 120rpx;
flex-shrink: 0;
}
.detail-value {
color: #333333;
flex: 1;
}
.animal-count {
color: #7CB342;
font-weight: 500;
}
.farmer-arrow {
width: 30rpx;
height: 30rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.arrow-icon {
color: #cccccc;
font-size: 24rpx;
}
/* 加载状态 */
.loading {
display: flex;
justify-content: center;
align-items: center;
padding: 40rpx;
}
.loading-text {
color: #999999;
font-size: 28rpx;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 40rpx;
text-align: center;
}
.empty-icon {
font-size: 120rpx;
margin-bottom: 30rpx;
opacity: 0.5;
}
.empty-text {
color: #999999;
font-size: 28rpx;
margin-bottom: 40rpx;
}
.empty-btn {
background-color: #7CB342;
color: white;
padding: 20rpx 40rpx;
border-radius: 25rpx;
font-size: 28rpx;
}

View File

@@ -1,102 +1,251 @@
// pages/index/index.js
const auth = require('../../utils/auth.js')
const dashboardService = require('../../services/dashboardService.js')
const app = getApp();
Page({
data: {
userInfo: {},
quickActions: [
{ key: 'dashboard', name: '数据看板', icon: '📊', path: '/pages/dashboard/dashboard' },
{ key: 'supervision', name: '监管管理', icon: '🔍', path: '/pages/supervision/supervision' },
{ key: 'approval', name: '审批管理', icon: '✅', path: '/pages/approval/approval' },
{ key: 'personnel', name: '人员管理', icon: '👥', path: '/pages/personnel/personnel' },
{ key: 'epidemic', name: '疫情监控', icon: '🦠', path: '/pages/epidemic/epidemic' },
{ key: 'service', name: '服务管理', icon: '🛠️', path: '/pages/service/service' },
{ key: 'warehouse', name: '仓库管理', icon: '📦', path: '/pages/warehouse/warehouse' },
{ key: 'profile', name: '个人中心', icon: '👤', path: '/pages/profile/profile' }
],
statsData: [
{ key: 'supervision', label: '监管记录', value: '0', trend: 'up', trendText: '+0%' },
{ key: 'approval', label: '待审批', value: '0', trend: 'up', trendText: '+0%' },
{ key: 'personnel', label: '人员总数', value: '0', trend: 'up', trendText: '+0%' },
{ key: 'epidemic', label: '疫情预警', value: '0', trend: 'down', trendText: '-0%' }
],
recentActivities: [
currentTime: '',
// 用户信息
userInfo: {
name: '政府管理',
role: '监管人员'
},
// 智能硬件设备(图一样式:使用图片图标)
hardwareDevices: [
{
id: 1,
icon: '🔍',
title: '监管检查',
desc: '完成养殖场A的例行检查',
time: '2小时前'
name: '项圈仓库',
img: '/images/collar.png',
color: '#1890ff',
type: 'collar'
},
{
id: 2,
icon: '',
title: '审批通过',
desc: '通过养殖许可证申请',
time: '4小时前'
name: '耳标仓库',
img: '/images/ear-tag.png',
color: '#ff7a45',
type: 'eartag'
},
{
id: 3,
icon: '👥',
title: '人员管理',
desc: '新增监管员张三',
time: '6小时前'
name: '脚环仓库',
img: '/images/ankle-ring.png',
color: '#52c41a',
type: 'anklet'
},
{
id: 4,
name: '主机仓库',
img: '/images/host.png',
color: '#faad14',
type: 'host'
}
],
// 其它功能(图一样式:使用图片图标)
otherFunctions: [
{
id: 1,
name: '牲畜身份认证',
img: '/images/identity.png',
color: '#1890ff',
type: 'livestock_auth'
},
{
id: 2,
name: '无害化检验',
img: '/images/epidemic.png',
color: '#ff7a45',
type: 'harmless_check'
},
{
id: 3,
name: '检疫证查询',
img: '/images/quarantine.png',
color: '#52c41a',
type: 'quarantine_query'
},
{
id: 4,
name: '金融保险监管',
img: '/images/finance.png',
color: '#faad14',
type: 'finance_insurance'
},
{
id: 5,
name: '屠宰场监管',
img: '/images/slaughter.png',
color: '#722ed1',
type: 'slaughter_supervision'
},
{
id: 6,
name: '检疫站监管',
img: '/images/epidemic-station.png',
color: '#eb2f96',
type: 'quarantine_supervision'
},
{
id: 7,
name: '无害化防疫',
img: '/images/certificate.png',
color: '#13c2c2',
type: 'harmless_prevention'
},
{
id: 8,
name: '防疫站监管',
img: '/images/station.png',
color: '#1890ff',
type: 'prevention_supervision'
}
]
},
onLoad() {
this.initData()
},
onPullDownRefresh() {
this.loadData()
console.log('首页加载完成');
// 延迟检查登录状态,避免与登录跳转冲突
setTimeout(() => {
wx.stopPullDownRefresh()
}, 1000)
this.checkLoginStatus();
}, 100);
this.updateTime();
this.startTimeUpdate();
},
initData() {
// 获取用户信息
this.setData({
userInfo: auth.getUser() || {}
})
onShow() {
console.log('首页显示');
// 延迟检查登录状态,避免与登录跳转冲突
setTimeout(() => {
this.checkLoginStatus();
}, 100);
this.updateTime();
},
onUnload() {
// 清除定时器
if (this.timeInterval) {
clearInterval(this.timeInterval);
}
},
// 检查登录状态
checkLoginStatus() {
const token = wx.getStorageSync('token');
console.log('首页检查登录状态token:', token);
if (!token) {
console.log('未找到token跳转到登录页');
// 未登录,跳转到登录页
wx.reLaunch({
url: '/pages/login/login'
});
return false;
}
console.log('token验证通过用户已登录');
return true;
},
// 更新时间显示
updateTime() {
const now = new Date();
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
const timeString = `${hours}:${minutes}`;
// 加载数据
this.loadData()
this.setData({
currentTime: timeString
});
},
async loadData() {
try {
// 加载统计数据
const stats = await dashboardService.getStats()
if (stats) {
this.updateStatsData(stats)
}
} catch (error) {
console.error('加载数据失败:', error)
// 开始时间更新定时器
startTimeUpdate() {
this.timeInterval = setInterval(() => {
this.updateTime();
}, 60000); // 每分钟更新一次
},
// 智能硬件设备点击
onHardwareDeviceTap(e) {
const { type } = e.currentTarget.dataset;
console.log('点击硬件设备:', type);
wx.showToast({
title: `进入${type}管理`,
icon: 'none'
});
// 根据不同类型跳转到相应页面
switch (type) {
case 'collar':
// 跳转到项圈管理页面
break;
case 'eartag':
// 跳转到耳标管理页面
break;
case 'anklet':
// 跳转到脚环管理页面
break;
case 'host':
// 跳转到主机管理页面
break;
default:
wx.showToast({
title: '功能开发中',
icon: 'none'
});
}
},
updateStatsData(data) {
const statsData = this.data.statsData.map(stat => {
const newValue = data[stat.key] || stat.value
return {
...stat,
value: newValue
}
})
this.setData({ statsData })
},
handleAction(e) {
const { action } = e.currentTarget.dataset
const actionItem = this.data.quickActions.find(item => item.key === action)
if (actionItem && actionItem.path) {
wx.navigateTo({
url: actionItem.path
})
// 其它功能点击
onOtherFunctionTap(e) {
const { type } = e.currentTarget.dataset;
console.log('点击其它功能:', type);
wx.showToast({
title: `进入${type}`,
icon: 'none'
});
// 根据不同类型跳转到相应页面
switch (type) {
case 'livestock_auth':
// 跳转到牲畜身份认证页面
break;
case 'harmless_check':
// 跳转到无害化检验页面
break;
case 'quarantine_query':
// 跳转到检疫证查询页面
break;
case 'finance_insurance':
// 跳转到金融保险监管页面
break;
case 'slaughter_supervision':
// 跳转到屠宰场监管页面
break;
case 'quarantine_supervision':
// 跳转到检疫站监管页面
break;
case 'harmless_prevention':
// 跳转到无害化防疫页面
break;
case 'prevention_supervision':
// 跳转到防疫站监管页面
break;
default:
wx.showToast({
title: '功能开发中',
icon: 'none'
});
}
},
// 分享功能
onShareAppMessage() {
return {
title: '政府监管系统',
path: '/pages/index/index'
};
}
})

View File

@@ -1,3 +1,5 @@
{
"usingComponents": {}
}
"usingComponents": {},
"navigationBarTitleText": "首页",
"navigationStyle": "custom"
}

View File

@@ -1,75 +1,72 @@
<!--pages/index/index.wxml-->
<view class="home-container">
<!-- 头部欢迎区域 -->
<view class="welcome-section">
<view class="welcome-content">
<view class="user-info">
<view class="avatar">
<image src="/images/avatar.png" class="avatar-img" />
</view>
<view class="user-details">
<view class="username">{{userInfo.name || '管理员'}}</view>
<view class="user-role">{{userInfo.role || '系统管理员'}}</view>
</view>
</view>
<view class="weather-info">
<view class="weather-icon">☀️</view>
<view class="weather-text">晴 25°C</view>
</view>
<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>
</view>
<!-- 快捷功能区域 -->
<view class="quick-actions">
<view class="section-title">快捷功能</view>
<view class="action-grid">
<view
wx:for="{{quickActions}}"
wx:key="key"
class="action-item"
data-action="{{item.key}}"
bindtap="handleAction"
>
<view class="action-icon">{{item.icon}}</view>
<view class="action-text">{{item.name}}</view>
</view>
<!-- 智能硬件设备区域 -->
<view class="section">
<view class="section-header">
<view class="section-indicator"></view>
<text class="section-title">智能硬件设备</text>
</view>
</view>
<!-- 数据概览区域 -->
<view class="stats-section">
<view class="section-title">数据概览</view>
<view class="stats-grid">
<view
wx:for="{{statsData}}"
wx:key="key"
class="stat-item"
>
<view class="stat-value">{{item.value}}</view>
<view class="stat-label">{{item.label}}</view>
<view class="stat-trend {{item.trend}}">
{{item.trendText}}
</view>
</view>
</view>
</view>
<!-- 最近活动区域 -->
<view class="recent-activities">
<view class="section-title">最近活动</view>
<view class="activity-list">
<view
wx:for="{{recentActivities}}"
<view class="hardware-grid">
<view
class="hardware-item"
wx:for="{{hardwareDevices}}"
wx:key="id"
class="activity-item"
data-type="{{item.type}}"
bindtap="onHardwareDeviceTap"
>
<view class="activity-icon">{{item.icon}}</view>
<view class="activity-content">
<view class="activity-title">{{item.title}}</view>
<view class="activity-desc">{{item.desc}}</view>
<view class="activity-time">{{item.time}}</view>
<view class="hardware-icon" style="background-color: {{item.color}}">
<image class="icon-image" src="{{item.img}}" mode="aspectFit"/>
</view>
<text class="hardware-name">{{item.name}}</text>
</view>
</view>
</view>
</view>
<!-- 其它功能区域 -->
<view class="section">
<view class="section-header">
<view class="section-indicator"></view>
<text class="section-title">其它</text>
</view>
<view class="other-grid">
<view
class="other-item"
wx:for="{{otherFunctions}}"
wx:key="id"
data-type="{{item.type}}"
bindtap="onOtherFunctionTap"
>
<view class="other-icon" style="background-color: {{item.color}}">
<image class="icon-image" src="{{item.img}}" mode="aspectFit"/>
</view>
<text class="other-name">{{item.name}}</text>
</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>

View File

@@ -1,187 +1,199 @@
/* pages/index/index.wxss */
.home-container {
.container {
min-height: 100vh;
background: #f6f6f6;
background: #f5f5f5;
padding-bottom: 120rpx; /* 为底部标签栏留出空间 */
}
.welcome-section {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 40rpx 30rpx 60rpx;
color: #fff;
}
.welcome-content {
/* 顶部导航栏 */
.nav-bar {
background: linear-gradient(135deg, #7CB342 0%, #8BC34A 100%);
height: 88rpx;
display: flex;
align-items: center;
justify-content: space-between;
align-items: center;
padding: 0 30rpx;
color: white;
}
.user-info {
display: flex;
align-items: center;
}
.avatar {
margin-right: 24rpx;
}
.avatar-img {
width: 80rpx;
height: 80rpx;
border-radius: 40rpx;
background: rgba(255, 255, 255, 0.2);
}
.username {
font-size: 36rpx;
font-weight: 600;
margin-bottom: 8rpx;
}
.user-role {
font-size: 24rpx;
opacity: 0.8;
}
.weather-info {
text-align: right;
}
.weather-icon {
.nav-title {
font-size: 32rpx;
margin-bottom: 8rpx;
}
.weather-text {
font-size: 24rpx;
opacity: 0.8;
}
.quick-actions,
.stats-section,
.recent-activities {
padding: 30rpx;
background: #fff;
margin-bottom: 20rpx;
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 30rpx;
}
.action-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 30rpx;
}
.action-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 30rpx 20rpx;
background: #f8f9fa;
border-radius: 16rpx;
transition: all 0.3s;
}
.action-item:active {
transform: scale(0.95);
background: #e9ecef;
}
.action-icon {
font-size: 48rpx;
margin-bottom: 16rpx;
}
.action-text {
font-size: 24rpx;
color: #666;
text-align: center;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
}
.stat-item {
padding: 30rpx;
background: #f8f9fa;
border-radius: 16rpx;
text-align: center;
}
.stat-value {
font-size: 48rpx;
font-weight: 600;
color: #1890ff;
margin-bottom: 8rpx;
}
.stat-label {
font-size: 24rpx;
color: #666;
margin-bottom: 8rpx;
}
.stat-trend {
font-size: 20rpx;
font-weight: 500;
}
.stat-trend.up {
color: #52c41a;
}
.stat-trend.down {
color: #ff4d4f;
}
.activity-list {
space-y: 20rpx;
}
.activity-item {
.nav-actions {
display: flex;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #f0f0f0;
background: rgba(255, 255, 255, 0.2);
border-radius: 44rpx;
padding: 8rpx 16rpx;
}
.activity-item:last-child {
border-bottom: none;
}
.activity-icon {
font-size: 32rpx;
margin-right: 20rpx;
width: 60rpx;
text-align: center;
}
.activity-content {
flex: 1;
}
.activity-title {
font-size: 28rpx;
color: #333;
margin-bottom: 8rpx;
}
.activity-desc {
.nav-icon {
font-size: 24rpx;
color: #666;
margin: 0 8rpx;
width: 24rpx;
height: 24rpx;
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;
}
.section-title {
font-size: 28rpx;
font-weight: 500;
color: #333;
}
/* 智能硬件设备网格 */
.hardware-grid {
display: flex;
flex-wrap: nowrap;
padding: 30rpx 20rpx;
background: white;
justify-content: space-between;
}
.hardware-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
margin: 0 10rpx;
}
.hardware-icon {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
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;
}
.hardware-name {
font-size: 26rpx;
color: #333;
text-align: center;
line-height: 1.3;
font-weight: 500;
}
/* 其它功能网格 */
.other-grid {
display: flex;
flex-wrap: wrap;
padding: 30rpx;
background: white;
}
.other-item {
width: 25%;
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 40rpx;
}
.other-icon {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16rpx;
}
.other-name {
font-size: 24rpx;
color: #333;
text-align: center;
line-height: 1.2;
word-break: break-all;
}
.icon-image {
width: 60rpx;
height: 60rpx;
}
/* 底部标签栏 */
.tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 120rpx;
background: white;
display: flex;
align-items: center;
justify-content: space-around;
border-top: 1rpx solid #e8e8e8;
padding-bottom: env(safe-area-inset-bottom);
}
.tab-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex: 1;
height: 100%;
}
.tab-icon {
width: 48rpx;
height: 48rpx;
margin-bottom: 8rpx;
}
.activity-time {
.tab-text {
font-size: 20rpx;
color: #999;
}
}
.tab-item.active .tab-text {
color: #7CB342;
}
/* 点击效果 */
.hardware-item:active,
.other-item:active {
transform: scale(0.95);
transition: transform 0.1s;
}
.tab-item:active {
background: rgba(0, 0, 0, 0.05);
}

View File

@@ -1,85 +1,274 @@
// pages/login/login.js
const authService = require('../../services/authService.js')
const auth = require('../../utils/auth.js')
const app = getApp();
const apiService = require('../../utils/api.js');
Page({
data: {
username: '',
password: '',
showPassword: false,
loading: false
rememberMe: false,
loading: false,
canLogin: false
},
onLoad() {
onLoad(options) {
console.log('登录页面加载');
this.loadSavedCredentials();
},
onShow() {
// 检查是否已登录
if (auth.isAuthenticated()) {
wx.reLaunch({
url: '/pages/index/index'
})
this.checkLoginStatus();
},
// 检查登录状态
checkLoginStatus() {
const token = wx.getStorageSync('token');
if (token) {
// 已登录,跳转到首页(数据统计页面)
wx.switchTab({
url: '/pages/statistics/statistics'
});
}
},
// 加载保存的登录信息
loadSavedCredentials() {
try {
const savedUsername = wx.getStorageSync('savedUsername');
const savedPassword = wx.getStorageSync('savedPassword');
const rememberMe = wx.getStorageSync('rememberMe');
if (rememberMe && savedUsername) {
this.setData({
username: savedUsername,
password: savedPassword || '',
rememberMe: rememberMe
});
this.checkCanLogin();
}
} catch (error) {
console.error('加载保存的登录信息失败:', error);
}
},
// 用户名输入
onUsernameInput(e) {
this.setData({
username: e.detail.value
})
});
this.checkCanLogin();
},
// 密码输入
onPasswordInput(e) {
this.setData({
password: e.detail.value
})
});
this.checkCanLogin();
},
// 切换密码显示
togglePassword() {
this.setData({
showPassword: !this.data.showPassword
})
});
},
async handleLogin() {
const { username, password, loading } = this.data
if (!username.trim() || !password.trim() || loading) return
this.setData({ loading: true })
// 记住密码选择
onRememberChange(e) {
this.setData({
rememberMe: e.detail.value.includes('remember')
});
},
// 检查是否可以登录
checkCanLogin() {
const { username, password } = this.data;
const canLogin = username.trim().length > 0 && password.trim().length > 0;
this.setData({ canLogin });
},
// 登录
async onLogin() {
if (!this.data.canLogin || this.data.loading) {
return;
}
const { username, password, rememberMe } = this.data;
// 基本验证
if (username.trim().length < 3) {
wx.showToast({
title: '用户名至少3个字符',
icon: 'none'
});
return;
}
if (password.trim().length < 6) {
wx.showToast({
title: '密码至少6个字符',
icon: 'none'
});
return;
}
this.setData({ loading: true });
try {
const response = await authService.login(username, password)
// 模拟登录请求
const loginResult = await this.performLogin(username, password);
if (response && response.token) {
// 保存token
auth.setToken(response.token)
if (loginResult.success) {
// 保存登录状态
wx.setStorageSync('token', loginResult.token);
wx.setStorageSync('userInfo', loginResult.userInfo);
// 获取用户信息
const userInfo = await authService.getUserInfo()
if (userInfo) {
auth.setUser(userInfo)
// 保存登录信息(如果选择记住密码)
if (rememberMe) {
wx.setStorageSync('savedUsername', username);
wx.setStorageSync('savedPassword', password);
wx.setStorageSync('rememberMe', true);
} else {
wx.removeStorageSync('savedUsername');
wx.removeStorageSync('savedPassword');
wx.removeStorageSync('rememberMe');
}
// 显示成功提示
wx.showToast({
title: '登录成功',
icon: 'success',
duration: 2000
})
duration: 1000
});
// 跳转到首页
setTimeout(() => {
wx.reLaunch({
url: '/pages/index/index'
})
}, 1000)
console.log('开始跳转流程当前loading状态:', this.data.loading);
// 直接使用reLaunch跳转不使用setTimeout延迟
wx.reLaunch({
url: '/pages/index/index',
success: () => {
console.log('reLaunch跳转成功');
// 确保登录页面状态重置
this.setData({
loading: false,
username: '',
password: ''
});
},
fail: (err) => {
console.error('reLaunch跳转失败:', err);
// 备用方案使用navigateTo
wx.navigateTo({
url: '/pages/index/index'
});
}
});
} else {
wx.showToast({
title: loginResult.message || '登录失败',
icon: 'none'
});
}
} catch (error) {
console.error('登录失败:', error)
console.error('登录错误:', error);
wx.showToast({
title: error.message || '登录失败',
icon: 'error',
duration: 3000
})
title: '网络错误,请重试',
icon: 'none'
});
} finally {
this.setData({ loading: false })
this.setData({ loading: false });
}
},
// 执行登录请求
async performLogin(username, password) {
try {
// 调用真实的登录API
const response = await apiService.login(username, password);
console.log('登录API响应:', response);
if (response.success || response.code === 200 || response.code === 0) {
// 处理不同的响应数据结构
let token, userInfo;
if (response.data) {
token = response.data.token || response.token;
userInfo = response.data.userInfo || response.data.user || response.userInfo;
} else {
token = response.token;
userInfo = response.userInfo || response.user;
}
// 如果没有token生成一个临时token
if (!token) {
token = 'temp_token_' + Date.now();
console.log('API未返回token生成临时token:', token);
}
// 如果没有用户信息,使用默认信息
if (!userInfo) {
userInfo = {
id: 1,
username: username,
name: '政府管理员',
phone: '13800138000',
department: '政府管理部门',
avatar: '/images/avatar.png',
role: 'admin'
};
console.log('API未返回用户信息使用默认信息:', userInfo);
}
return {
success: true,
token: token,
userInfo: userInfo
};
} else {
return {
success: false,
message: response.message || '登录失败'
};
}
} catch (error) {
console.error('API登录请求失败:', error);
// 如果网络请求失败,提供离线登录模式
if (username === 'admin' && password === '123456') {
console.log('使用离线登录模式');
return {
success: true,
token: 'offline_token_' + Date.now(),
userInfo: {
id: 1,
username: 'admin',
name: '系统管理员',
phone: '13800138000',
department: '政府管理部门',
avatar: '/images/avatar.png',
role: 'admin'
}
};
} else {
return {
success: false,
message: '网络连接失败,请检查网络后重试'
};
}
}
},
// 忘记密码
onForgotPassword() {
wx.showModal({
title: '忘记密码',
content: '请联系系统管理员重置密码\n联系电话400-123-4567',
showCancel: false,
confirmText: '我知道了'
});
}
})
});

View File

@@ -1,3 +1,6 @@
{
"usingComponents": {}
"navigationBarTitleText": "登录",
"navigationStyle": "custom",
"backgroundColor": "#667eea",
"backgroundTextStyle": "light"
}

View File

@@ -1,53 +1,83 @@
<!--pages/login/login.wxml-->
<view class="login-container">
<view class="login-header">
<view class="logo">
<image src="/images/logo.png" class="logo-img" />
</view>
<view class="title">政府管理系统</view>
<view class="subtitle">Government Management System</view>
<view class="container">
<!-- 背景装饰 -->
<view class="bg-decoration">
<view class="circle circle-1"></view>
<view class="circle circle-2"></view>
<view class="circle circle-3"></view>
</view>
<!-- 登录表单 -->
<view class="login-form">
<view class="form-item">
<input
value="{{username}}"
type="text"
placeholder="请输入用户名"
class="input"
bindinput="onUsernameInput"
/>
<!-- Logo和标题 -->
<view class="header-section">
<view class="logo">
<image src="/images/logo.png" class="logo-img" mode="aspectFit"></image>
</view>
<view class="title">政府监管系统</view>
<view class="subtitle">智慧牧场管理平台</view>
</view>
<view class="form-item">
<input
value="{{password}}"
type="{{showPassword ? 'text' : 'password'}}"
placeholder="请输入密码"
class="input"
bindinput="onPasswordInput"
/>
<view class="password-toggle" bindtap="togglePassword">
<text class="toggle-icon">{{showPassword ? '👁️' : '👁️‍🗨️'}}</text>
<!-- 输入框区域 -->
<view class="input-section">
<view class="input-group">
<view class="input-icon">👤</view>
<input
class="input-field"
placeholder="请输入用户名"
value="{{username}}"
bindinput="onUsernameInput"
maxlength="20"
/>
</view>
<view class="input-group">
<view class="input-icon">🔒</view>
<input
class="input-field"
placeholder="请输入密码"
password="{{!showPassword}}"
value="{{password}}"
bindinput="onPasswordInput"
maxlength="20"
/>
<view class="password-toggle" bindtap="togglePassword">
<text>{{showPassword ? '👁️' : '👁️‍🗨️'}}</text>
</view>
</view>
</view>
<view class="form-item">
<button
class="login-btn {{(!username || !password || loading) ? 'disabled' : ''}}"
disabled="{{!username || !password || loading}}"
bindtap="handleLogin"
<!-- 登录按钮 -->
<view class="button-section">
<button
class="login-btn {{canLogin ? 'active' : 'disabled'}}"
bindtap="onLogin"
disabled="{{!canLogin || loading}}"
>
{{loading ? '登录中...' : '登录'}}
<text wx:if="{{!loading}}">登录</text>
<text wx:else>登录中...</text>
</button>
</view>
<view class="login-tips">
<text class="tips-text">默认账号admin / 123456</text>
<!-- 其他选项 -->
<view class="options-section">
<view class="remember-section">
<checkbox-group bindchange="onRememberChange">
<label class="checkbox-label">
<checkbox value="remember" checked="{{rememberMe}}"/>
<text class="checkbox-text">记住密码</text>
</label>
</checkbox-group>
</view>
<view class="forgot-password" bindtap="onForgotPassword">
忘记密码?
</view>
</view>
<!-- 版本信息 -->
<view class="version-info">
<text class="version-text">版本 v1.0.0</text>
</view>
</view>
<view class="login-footer">
<text class="footer-text">© 2024 政府管理系统</text>
</view>
</view>

View File

@@ -1,116 +1,214 @@
/* pages/login/login.wxss */
.login-container {
.container {
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40rpx;
position: relative;
overflow: hidden;
}
.login-header {
/* 背景装饰 */
.bg-decoration {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
.circle {
position: absolute;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
animation: float 6s ease-in-out infinite;
}
.circle-1 {
width: 200rpx;
height: 200rpx;
top: 10%;
left: 10%;
animation-delay: 0s;
}
.circle-2 {
width: 150rpx;
height: 150rpx;
top: 60%;
right: 15%;
animation-delay: 2s;
}
.circle-3 {
width: 100rpx;
height: 100rpx;
top: 30%;
right: 30%;
animation-delay: 4s;
}
@keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-20px); }
}
/* 登录表单 */
.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);
}
/* 头部区域 */
.header-section {
text-align: center;
margin-bottom: 80rpx;
margin-bottom: 60rpx;
}
.logo {
margin-bottom: 40rpx;
margin-bottom: 30rpx;
}
.logo-img {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
background: #fff;
}
.title {
font-size: 48rpx;
font-weight: 600;
color: #fff;
margin-bottom: 16rpx;
font-weight: bold;
color: #333;
margin-bottom: 10rpx;
}
.subtitle {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.8);
color: #666;
}
.login-form {
width: 100%;
max-width: 600rpx;
}
.form-item {
/* 输入框区域 */
.input-section {
margin-bottom: 40rpx;
position: relative;
}
.input {
width: 100%;
.input-group {
position: relative;
margin-bottom: 30rpx;
background: #f8f9fa;
border-radius: 12rpx;
border: 2rpx solid #e9ecef;
display: flex;
align-items: center;
padding: 0 20rpx;
transition: all 0.3s ease;
}
.input-group:focus-within {
border-color: #4CAF50;
background: #fff;
box-shadow: 0 0 0 6rpx rgba(76, 175, 80, 0.1);
}
.input-icon {
font-size: 32rpx;
margin-right: 20rpx;
color: #666;
}
.input-field {
flex: 1;
height: 88rpx;
background: rgba(255, 255, 255, 0.9);
border: none;
border-radius: 44rpx;
padding: 0 40rpx;
font-size: 32rpx;
color: #333;
background: transparent;
border: none;
}
.input::placeholder {
.input-field::placeholder {
color: #999;
}
.password-toggle {
position: absolute;
right: 40rpx;
top: 50%;
transform: translateY(-50%);
padding: 20rpx;
padding: 10rpx;
font-size: 32rpx;
color: #666;
}
.toggle-icon {
font-size: 32rpx;
color: #999;
/* 按钮区域 */
.button-section {
margin-bottom: 40rpx;
}
.login-btn {
width: 100%;
height: 88rpx;
background: #1890ff;
color: #fff;
border: none;
border-radius: 44rpx;
border-radius: 12rpx;
font-size: 32rpx;
font-weight: 600;
font-weight: bold;
border: none;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
.login-btn.active {
background: linear-gradient(135deg, #4CAF50, #45a049);
color: white;
}
.login-btn.active:active {
transform: translateY(2rpx);
box-shadow: 0 4rpx 8rpx rgba(76, 175, 80, 0.3);
}
.login-btn.disabled {
background: #ccc;
background: #e9ecef;
color: #999;
}
.login-tips {
text-align: center;
margin-top: 40rpx;
/* 选项区域 */
.options-section {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 40rpx;
}
.tips-text {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.8);
.remember-section {
display: flex;
align-items: center;
}
.login-footer {
position: absolute;
bottom: 40rpx;
.checkbox-label {
display: flex;
align-items: center;
font-size: 28rpx;
color: #666;
}
.checkbox-text {
margin-left: 10rpx;
}
.forgot-password {
font-size: 28rpx;
color: #4CAF50;
text-decoration: underline;
}
/* 版本信息 */
.version-info {
text-align: center;
}
.footer-text {
.version-text {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
color: #999;
}

View File

@@ -0,0 +1,326 @@
// pages/notification/detail/detail.js
Page({
data: {
statusBarHeight: 0,
loading: true,
notificationId: '',
notificationInfo: {}
},
onLoad(options) {
// 检查登录状态
this.checkLoginStatus();
// 获取状态栏高度
const systemInfo = wx.getSystemInfoSync();
this.setData({
statusBarHeight: systemInfo.statusBarHeight
});
// 获取通知ID
if (options.id) {
this.setData({
notificationId: options.id
});
this.loadNotificationDetail();
} else {
wx.showToast({
title: '参数错误',
icon: 'error'
});
setTimeout(() => {
wx.navigateBack();
}, 1500);
}
},
onShow() {
// 检查登录状态
this.checkLoginStatus();
},
// 检查登录状态
checkLoginStatus() {
const token = wx.getStorageSync('token');
if (!token) {
wx.redirectTo({
url: '/pages/login/login'
});
return false;
}
return true;
},
// 加载通知详情
async loadNotificationDetail() {
try {
this.setData({ loading: true });
// 模拟API调用
const notificationData = await this.fetchNotificationDetail(this.data.notificationId);
this.setData({
notificationInfo: notificationData,
loading: false
});
// 标记为已读
this.markAsRead();
} catch (error) {
console.error('加载通知详情失败:', error);
wx.showToast({
title: '加载失败',
icon: 'error'
});
this.setData({ loading: false });
}
},
// 模拟获取通知详情API
fetchNotificationDetail(notificationId) {
return new Promise((resolve) => {
setTimeout(() => {
// 根据ID生成不同类型的模拟数据
const mockData = this.generateMockNotification(notificationId);
resolve(mockData);
}, 1000);
});
},
// 生成模拟通知数据
generateMockNotification(id) {
const types = ['system', 'alert', 'task'];
const type = types[parseInt(id) % 3];
const baseData = {
id: id,
type: type,
time: this.formatTime(new Date(Date.now() - Math.random() * 7 * 24 * 60 * 60 * 1000)),
isRead: false
};
switch (type) {
case 'system':
return {
...baseData,
title: '系统维护通知',
content: '系统将于今晚22:00-24:00进行例行维护期间可能影响部分功能使用请提前做好相关准备工作。维护期间如有紧急情况请联系技术支持热线。',
attachments: [
{
id: '1',
name: '维护说明文档.pdf',
size: '2.3MB'
}
],
processRecords: [
{
id: '1',
time: '10:30',
action: '发布通知',
operator: '系统管理员',
remark: '定期系统维护通知'
}
]
};
case 'alert':
return {
...baseData,
title: '牲畜异常告警',
content: '检测到张三养殖场出现牲畜异常情况,请及时处理。系统监测显示该养殖场的牲畜活动异常,可能存在健康风险,建议立即派遣检查人员前往现场核实情况。',
alertDetails: {
level: 'high',
farmerName: '张三',
location: '西夏区兴泾镇张三养殖场',
reason: '牲畜活动异常,疑似健康问题'
},
processRecords: [
{
id: '1',
time: '14:25',
action: '系统自动告警',
operator: '监控系统',
remark: '检测到异常数据'
}
]
};
case 'task':
return {
...baseData,
title: '月度检查任务',
content: '请在本月底前完成辖区内所有养殖户的月度例行检查工作。检查内容包括:牲畜健康状况、饲料质量、环境卫生、防疫措施等。请及时提交检查报告。',
taskDetails: {
taskType: '月度例行检查',
deadline: '2024-01-31 18:00',
status: 'pending',
priority: 'medium'
},
attachments: [
{
id: '1',
name: '检查清单.xlsx',
size: '156KB'
},
{
id: '2',
name: '报告模板.docx',
size: '89KB'
}
],
processRecords: [
{
id: '1',
time: '09:00',
action: '任务分配',
operator: '管理员',
remark: '月度例行检查任务'
}
]
};
default:
return baseData;
}
},
// 格式化时间
formatTime(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hour = String(date.getHours()).padStart(2, '0');
const minute = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}`;
},
// 标记为已读
async markAsRead() {
try {
// 模拟API调用
await this.markNotificationAsRead(this.data.notificationId);
console.log('通知已标记为已读');
} catch (error) {
console.error('标记已读失败:', error);
}
},
// 模拟标记已读API
markNotificationAsRead(notificationId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ success: true });
}, 500);
});
},
// 处理告警
onHandleAlert() {
const alertDetails = this.data.notificationInfo.alertDetails;
wx.showModal({
title: '处理告警',
content: `确定要处理来自${alertDetails.farmerName}的告警吗?`,
success: (res) => {
if (res.confirm) {
// 这里可以跳转到具体的处理页面
wx.showToast({
title: '正在处理...',
icon: 'loading'
});
setTimeout(() => {
wx.showToast({
title: '处理成功',
icon: 'success'
});
}, 2000);
}
}
});
},
// 处理任务
onHandleTask() {
const taskDetails = this.data.notificationInfo.taskDetails;
wx.showModal({
title: '处理任务',
content: `确定要开始处理"${taskDetails.taskType}"任务吗?`,
success: (res) => {
if (res.confirm) {
// 这里可以跳转到具体的任务处理页面
wx.showToast({
title: '任务已接受',
icon: 'success'
});
}
}
});
},
// 分享通知
onShare() {
wx.showActionSheet({
itemList: ['复制链接', '发送给同事', '保存到本地'],
success: (res) => {
switch (res.tapIndex) {
case 0:
wx.setClipboardData({
data: `通知详情:${this.data.notificationInfo.title}`,
success: () => {
wx.showToast({
title: '已复制到剪贴板',
icon: 'success'
});
}
});
break;
case 1:
wx.showToast({
title: '功能开发中',
icon: 'none'
});
break;
case 2:
wx.showToast({
title: '保存成功',
icon: 'success'
});
break;
}
}
});
},
// 查看附件
onAttachmentTap(e) {
const attachment = e.currentTarget.dataset.attachment;
wx.showModal({
title: '查看附件',
content: `确定要查看附件"${attachment.name}"吗?`,
success: (res) => {
if (res.confirm) {
wx.showToast({
title: '正在打开...',
icon: 'loading'
});
// 这里可以实现附件预览或下载功能
setTimeout(() => {
wx.showToast({
title: '功能开发中',
icon: 'none'
});
}, 1500);
}
}
});
},
// 下拉刷新
onPullDownRefresh() {
this.loadNotificationDetail().finally(() => {
wx.stopPullDownRefresh();
});
}
});

View File

@@ -0,0 +1,7 @@
{
"navigationBarTitleText": "通知详情",
"navigationStyle": "custom",
"backgroundColor": "#f5f5f5",
"backgroundTextStyle": "dark",
"enablePullDownRefresh": true
}

View File

@@ -0,0 +1,144 @@
<!--pages/notification/detail/detail.wxml-->
<view class="container">
<!-- 状态栏 -->
<view class="status-bar" style="height: {{statusBarHeight}}px;"></view>
<!-- 加载状态 -->
<view wx:if="{{loading}}" class="loading-container">
<view class="loading-spinner"></view>
<text class="loading-text">加载中...</text>
</view>
<!-- 通知详情内容 -->
<view wx:else class="detail-content">
<!-- 通知头部 -->
<view class="notification-header">
<view class="notification-title">{{notificationInfo.title}}</view>
<view class="notification-meta">
<view class="notification-time">{{notificationInfo.time}}</view>
<view class="notification-type type-{{notificationInfo.type}}">
{{notificationInfo.type === 'system' ? '系统通知' : notificationInfo.type === 'alert' ? '告警通知' : '任务通知'}}
</view>
</view>
</view>
<!-- 通知内容 -->
<view class="notification-content">
<view class="content-section">
<view class="section-title">通知内容</view>
<view class="content-text">{{notificationInfo.content}}</view>
</view>
<!-- 告警详情 (仅告警通知显示) -->
<view wx:if="{{notificationInfo.type === 'alert' && notificationInfo.alertDetails}}" class="content-section">
<view class="section-title">告警详情</view>
<view class="alert-details">
<view class="detail-item">
<view class="detail-label">告警级别</view>
<view class="detail-value level-{{notificationInfo.alertDetails.level}}">
{{notificationInfo.alertDetails.level === 'high' ? '高级' : notificationInfo.alertDetails.level === 'medium' ? '中级' : '低级'}}
</view>
</view>
<view class="detail-item">
<view class="detail-label">涉及养殖户</view>
<view class="detail-value">{{notificationInfo.alertDetails.farmerName}}</view>
</view>
<view class="detail-item">
<view class="detail-label">告警位置</view>
<view class="detail-value">{{notificationInfo.alertDetails.location}}</view>
</view>
<view class="detail-item">
<view class="detail-label">告警原因</view>
<view class="detail-value">{{notificationInfo.alertDetails.reason}}</view>
</view>
</view>
</view>
<!-- 任务详情 (仅任务通知显示) -->
<view wx:if="{{notificationInfo.type === 'task' && notificationInfo.taskDetails}}" class="content-section">
<view class="section-title">任务详情</view>
<view class="task-details">
<view class="detail-item">
<view class="detail-label">任务类型</view>
<view class="detail-value">{{notificationInfo.taskDetails.taskType}}</view>
</view>
<view class="detail-item">
<view class="detail-label">截止时间</view>
<view class="detail-value">{{notificationInfo.taskDetails.deadline}}</view>
</view>
<view class="detail-item">
<view class="detail-label">任务状态</view>
<view class="detail-value status-{{notificationInfo.taskDetails.status}}">
{{notificationInfo.taskDetails.status === 'pending' ? '待处理' : notificationInfo.taskDetails.status === 'processing' ? '处理中' : '已完成'}}
</view>
</view>
<view class="detail-item">
<view class="detail-label">优先级</view>
<view class="detail-value priority-{{notificationInfo.taskDetails.priority}}">
{{notificationInfo.taskDetails.priority === 'high' ? '高' : notificationInfo.taskDetails.priority === 'medium' ? '中' : '低'}}
</view>
</view>
</view>
</view>
<!-- 附件信息 -->
<view wx:if="{{notificationInfo.attachments && notificationInfo.attachments.length > 0}}" class="content-section">
<view class="section-title">相关附件</view>
<view class="attachments-list">
<view
wx:for="{{notificationInfo.attachments}}"
wx:key="id"
class="attachment-item"
bindtap="onAttachmentTap"
data-attachment="{{item}}"
>
<view class="attachment-icon">📎</view>
<view class="attachment-info">
<view class="attachment-name">{{item.name}}</view>
<view class="attachment-size">{{item.size}}</view>
</view>
<view class="attachment-action">查看</view>
</view>
</view>
</view>
<!-- 处理记录 -->
<view wx:if="{{notificationInfo.processRecords && notificationInfo.processRecords.length > 0}}" class="content-section">
<view class="section-title">处理记录</view>
<view class="process-records">
<view
wx:for="{{notificationInfo.processRecords}}"
wx:key="id"
class="record-item"
>
<view class="record-time">{{item.time}}</view>
<view class="record-content">
<view class="record-action">{{item.action}}</view>
<view class="record-operator">操作人:{{item.operator}}</view>
<view wx:if="{{item.remark}}" class="record-remark">{{item.remark}}</view>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 底部操作栏 -->
<view wx:if="{{!loading}}" class="bottom-bar">
<button
wx:if="{{notificationInfo.type === 'alert'}}"
class="action-btn handle-btn"
bindtap="onHandleAlert"
>
处理告警
</button>
<button
wx:if="{{notificationInfo.type === 'task'}}"
class="action-btn task-btn"
bindtap="onHandleTask"
>
处理任务
</button>
<button class="action-btn share-btn" bindtap="onShare">分享</button>
</view>
</view>

View File

@@ -0,0 +1,336 @@
/* pages/notification/detail/detail.wxss */
.container {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 120rpx;
}
.status-bar {
background-color: #2c5aa0;
}
/* 加载状态 */
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 60vh;
}
.loading-spinner {
width: 60rpx;
height: 60rpx;
border: 4rpx solid #f3f3f3;
border-top: 4rpx solid #2c5aa0;
border-radius: 50%;
animation: spin 1s linear infinite;
}
.loading-text {
margin-top: 20rpx;
font-size: 28rpx;
color: #666;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 详情内容 */
.detail-content {
padding: 20rpx;
}
/* 通知头部 */
.notification-header {
background-color: white;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.notification-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
line-height: 1.4;
margin-bottom: 20rpx;
}
.notification-meta {
display: flex;
align-items: center;
justify-content: space-between;
}
.notification-time {
font-size: 26rpx;
color: #666;
}
.notification-type {
padding: 6rpx 12rpx;
border-radius: 12rpx;
font-size: 22rpx;
font-weight: bold;
}
.type-system {
background-color: #e6f7ff;
color: #1890ff;
}
.type-alert {
background-color: #fff2f0;
color: #ff4d4f;
}
.type-task {
background-color: #fff7e6;
color: #fa8c16;
}
/* 通知内容 */
.notification-content {
background-color: white;
border-radius: 16rpx;
padding: 30rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.content-section {
margin-bottom: 40rpx;
}
.content-section:last-child {
margin-bottom: 0;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
padding-bottom: 10rpx;
border-bottom: 2rpx solid #f0f0f0;
}
.content-text {
font-size: 28rpx;
color: #333;
line-height: 1.6;
}
/* 详情项 */
.detail-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.detail-item:last-child {
border-bottom: none;
}
.detail-label {
font-size: 28rpx;
color: #666;
flex-shrink: 0;
width: 160rpx;
}
.detail-value {
font-size: 28rpx;
color: #333;
flex: 1;
text-align: right;
}
/* 告警级别 */
.level-high {
color: #ff4d4f;
font-weight: bold;
}
.level-medium {
color: #fa8c16;
font-weight: bold;
}
.level-low {
color: #52c41a;
font-weight: bold;
}
/* 任务状态 */
.status-pending {
color: #fa8c16;
font-weight: bold;
}
.status-processing {
color: #1890ff;
font-weight: bold;
}
.status-completed {
color: #52c41a;
font-weight: bold;
}
/* 优先级 */
.priority-high {
color: #ff4d4f;
font-weight: bold;
}
.priority-medium {
color: #fa8c16;
font-weight: bold;
}
.priority-low {
color: #52c41a;
font-weight: bold;
}
/* 附件列表 */
.attachments-list {
margin-top: 20rpx;
}
.attachment-item {
display: flex;
align-items: center;
padding: 20rpx;
background-color: #f8f9fa;
border-radius: 12rpx;
margin-bottom: 12rpx;
transition: background-color 0.3s ease;
}
.attachment-item:active {
background-color: #e9ecef;
}
.attachment-icon {
font-size: 32rpx;
margin-right: 16rpx;
}
.attachment-info {
flex: 1;
}
.attachment-name {
font-size: 28rpx;
color: #333;
margin-bottom: 4rpx;
}
.attachment-size {
font-size: 24rpx;
color: #999;
}
.attachment-action {
font-size: 26rpx;
color: #2c5aa0;
}
/* 处理记录 */
.process-records {
margin-top: 20rpx;
}
.record-item {
display: flex;
padding: 20rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.record-item:last-child {
border-bottom: none;
}
.record-time {
font-size: 24rpx;
color: #999;
width: 120rpx;
flex-shrink: 0;
}
.record-content {
flex: 1;
margin-left: 20rpx;
}
.record-action {
font-size: 28rpx;
color: #333;
font-weight: bold;
margin-bottom: 8rpx;
}
.record-operator {
font-size: 24rpx;
color: #666;
margin-bottom: 4rpx;
}
.record-remark {
font-size: 26rpx;
color: #333;
line-height: 1.4;
}
/* 底部操作栏 */
.bottom-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: white;
padding: 20rpx;
border-top: 1rpx solid #f0f0f0;
display: flex;
gap: 20rpx;
box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.action-btn {
flex: 1;
height: 80rpx;
border-radius: 40rpx;
font-size: 28rpx;
font-weight: bold;
border: none;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.action-btn:active {
transform: scale(0.95);
}
.handle-btn {
background-color: #ff4d4f;
color: white;
}
.task-btn {
background-color: #fa8c16;
color: white;
}
.share-btn {
background-color: #2c5aa0;
color: white;
}

View File

@@ -0,0 +1,397 @@
const app = getApp();
const apiService = require('../../utils/api.js');
Page({
data: {
statusBarHeight: 0,
loading: true,
// 筛选状态
selectedType: 'all',
selectedStatus: 'all',
// 筛选选项
typeOptions: [
{ value: 'all', label: '全部类型' },
{ value: 'system', label: '系统通知' },
{ value: 'alert', label: '预警通知' },
{ value: 'task', label: '任务通知' }
],
statusOptions: [
{ value: 'all', label: '全部状态' },
{ value: 'unread', label: '未读' },
{ value: 'read', label: '已读' }
],
// 通知列表
notificationList: [],
// 分页
currentPage: 1,
pageSize: 10,
totalCount: 0,
hasMore: true,
// 统计信息
statistics: {
total: 0,
unread: 0,
system: 0,
alert: 0,
task: 0
}
},
onLoad() {
// 获取状态栏高度
const systemInfo = wx.getSystemInfoSync();
this.setData({
statusBarHeight: systemInfo.statusBarHeight
});
// 检查登录状态
this.checkLoginStatus();
},
onShow() {
// 每次显示页面时刷新数据
this.loadData();
},
// 检查登录状态
checkLoginStatus() {
const token = wx.getStorageSync('token');
if (!token) {
wx.reLaunch({
url: '/pages/login/login'
});
return false;
}
return true;
},
// 加载数据
async loadData(isRefresh = true) {
if (!this.checkLoginStatus()) return;
if (isRefresh) {
this.setData({
loading: true,
currentPage: 1,
notificationList: [],
hasMore: true
});
}
try {
await this.loadNotifications();
} catch (error) {
console.error('数据加载失败:', error);
// 加载模拟数据作为备用
this.loadMockData();
} finally {
this.setData({ loading: false });
}
},
// 加载通知列表
async loadNotifications() {
try {
const params = {
page: this.data.currentPage,
pageSize: this.data.pageSize,
type: this.data.selectedType === 'all' ? '' : this.data.selectedType,
status: this.data.selectedStatus === 'all' ? '' : this.data.selectedStatus
};
// 这里可以调用实际的通知API
// const result = await apiService.getNotifications(params);
// 暂时使用模拟数据
this.loadMockData();
} catch (error) {
console.error('获取通知列表失败:', error);
throw error;
}
},
// 加载模拟数据
loadMockData() {
const mockNotifications = [
{
id: 1,
type: 'alert',
title: '养殖户异常预警',
content: '张三养殖场检测到异常情况,请及时处理',
status: 'unread',
priority: 'high',
createTime: '2024-01-15 10:30:00',
farmerId: 1,
farmerName: '张三',
farmName: '张家养殖场'
},
{
id: 2,
type: 'system',
title: '系统维护通知',
content: '系统将于今晚22:00-24:00进行维护期间可能影响正常使用',
status: 'read',
priority: 'medium',
createTime: '2024-01-15 09:15:00'
},
{
id: 3,
type: 'task',
title: '检查任务提醒',
content: '您有一个待处理的检查任务:李氏牧场定期检查',
status: 'unread',
priority: 'medium',
createTime: '2024-01-15 08:45:00',
farmerId: 2,
farmerName: '李四',
farmName: '李氏牧场'
},
{
id: 4,
type: 'alert',
title: '设备离线预警',
content: '王家农场的监控设备已离线超过2小时',
status: 'unread',
priority: 'high',
createTime: '2024-01-14 16:20:00',
farmerId: 3,
farmerName: '王五',
farmName: '王家农场'
},
{
id: 5,
type: 'system',
title: '数据同步完成',
content: '今日数据同步已完成共同步1250条记录',
status: 'read',
priority: 'low',
createTime: '2024-01-14 15:30:00'
}
];
// 根据筛选条件过滤数据
let filteredNotifications = mockNotifications;
if (this.data.selectedType !== 'all') {
filteredNotifications = filteredNotifications.filter(n => n.type === this.data.selectedType);
}
if (this.data.selectedStatus !== 'all') {
filteredNotifications = filteredNotifications.filter(n => n.status === this.data.selectedStatus);
}
this.setData({
notificationList: filteredNotifications,
totalCount: filteredNotifications.length,
hasMore: false,
statistics: {
total: mockNotifications.length,
unread: mockNotifications.filter(n => n.status === 'unread').length,
system: mockNotifications.filter(n => n.type === 'system').length,
alert: mockNotifications.filter(n => n.type === 'alert').length,
task: mockNotifications.filter(n => n.type === 'task').length
}
});
},
// 类型筛选
onTypeChange(e) {
const type = e.currentTarget.dataset.type;
this.setData({
selectedType: type
});
this.loadData(true);
},
// 状态筛选
onStatusChange(e) {
const status = e.currentTarget.dataset.status;
this.setData({
selectedStatus: status
});
this.loadData(true);
},
// 生成模拟通知数据
generateMockNotifications() {
const types = ['system', 'alert', 'task']
const typeNames = { system: '系统通知', alert: '告警通知', task: '任务通知' }
const icons = { system: '🔔', alert: '⚠️', task: '📋' }
const notifications = []
const pageSize = this.data.pageSize
for (let i = 0; i < pageSize; i++) {
const type = types[Math.floor(Math.random() * types.length)]
const isRead = Math.random() > 0.3
const id = Date.now() + i
notifications.push({
id,
type,
typeName: typeNames[type],
icon: icons[type],
title: this.getNotificationTitle(type),
content: this.getNotificationContent(type),
time: this.formatTime(new Date(Date.now() - Math.random() * 7 * 24 * 60 * 60 * 1000)),
isRead
})
}
return notifications.filter(item => {
if (this.data.selectedTab === 'all') return true
return item.type === this.data.selectedTab
})
},
// 获取通知标题
getNotificationTitle(type) {
const titles = {
system: ['系统维护通知', '版本更新提醒', '账户安全提醒', '数据备份完成'],
alert: ['设备离线告警', '异常数据告警', '温度超标告警', '网络连接异常'],
task: ['检疫任务提醒', '报告提交截止', '数据统计任务', '设备巡检任务']
}
const typeList = titles[type] || titles.system
return typeList[Math.floor(Math.random() * typeList.length)]
},
// 获取通知内容
getNotificationContent(type) {
const contents = {
system: ['系统将于今晚进行维护预计持续2小时', '新版本已发布,请及时更新', '检测到异常登录,请注意账户安全', '数据备份已完成,请查看备份报告'],
alert: ['监控摄像头#001已离线超过30分钟', '养殖场温度传感器检测到异常数据', '养殖区域温度超过安全范围', '网络连接不稳定,请检查网络设备'],
task: ['您有新的检疫任务需要处理', '月度报告提交截止日期临近', '请完成本周数据统计工作', '设备巡检任务已分配给您']
}
const typeList = contents[type] || contents.system
return typeList[Math.floor(Math.random() * typeList.length)]
},
// 格式化时间
formatTime(date) {
const now = new Date()
const diff = now - date
const minutes = Math.floor(diff / (1000 * 60))
const hours = Math.floor(diff / (1000 * 60 * 60))
const days = Math.floor(diff / (1000 * 60 * 60 * 24))
if (minutes < 1) return '刚刚'
if (minutes < 60) return `${minutes}分钟前`
if (hours < 24) return `${hours}小时前`
if (days < 7) return `${days}天前`
return `${date.getMonth() + 1}-${date.getDate()}`
},
// 更新标签计数
updateTabCounts() {
const tabs = this.data.tabs.map(tab => {
if (tab.key === 'all') {
tab.count = this.data.notificationList.filter(item => !item.isRead).length
} else {
tab.count = this.data.notificationList.filter(item => item.type === tab.key && !item.isRead).length
}
return tab
})
this.setData({ tabs })
},
// 标签切换
onTabChange(e) {
const { key } = e.currentTarget.dataset
this.setData({
selectedTab: key,
currentPage: 1,
notificationList: [],
hasMore: true
})
this.loadNotifications()
},
// 通知点击事件
onNotificationTap(e) {
const { item } = e.currentTarget.dataset
console.log('点击通知:', item)
// 标记为已读
if (!item.isRead) {
const notificationList = this.data.notificationList.map(notification => {
if (notification.id === item.id) {
notification.isRead = true
}
return notification
})
this.setData({ notificationList })
this.updateTabCounts()
}
// 根据通知类型跳转到相应页面
switch (item.type) {
case 'alert':
wx.navigateTo({
url: '/pages/alert/detail/detail?id=' + item.id
})
break
case 'task':
wx.navigateTo({
url: '/pages/task/detail/detail?id=' + item.id
})
break
default:
wx.showToast({
title: '查看详情功能开发中',
icon: 'none'
})
}
},
// 删除通知
onDeleteNotification(e) {
const { item } = e.currentTarget.dataset
wx.showModal({
title: '确认删除',
content: '确定要删除这条通知吗?',
success: (res) => {
if (res.confirm) {
const notificationList = this.data.notificationList.filter(notification => notification.id !== item.id)
this.setData({ notificationList })
this.updateTabCounts()
wx.showToast({
title: '删除成功',
icon: 'success'
})
}
}
})
},
// 全部已读
onMarkAllRead() {
const notificationList = this.data.notificationList.map(item => {
item.isRead = true
return item
})
this.setData({ notificationList })
this.updateTabCounts()
wx.showToast({
title: '已全部标记为已读',
icon: 'success'
})
},
// 加载更多
onLoadMore() {
this.loadNotifications()
}
})

View File

@@ -0,0 +1,6 @@
{
"navigationBarTitleText": "消息通知",
"enablePullDownRefresh": true,
"backgroundColor": "#f5f5f5",
"backgroundTextStyle": "dark"
}

View File

@@ -0,0 +1,79 @@
<!-- 状态栏 -->
<view class="status-bar" style="height: {{statusBarHeight}}px;"></view>
<!-- 页面容器 -->
<view class="container">
<!-- 头部 -->
<view class="header">
<view class="header-title">消息通知</view>
<view class="header-actions">
<view class="action-btn" bindtap="onMarkAllRead">
<text class="action-text">全部已读</text>
</view>
</view>
</view>
<!-- 筛选标签 -->
<view class="filter-tabs">
<view
class="tab-item {{selectedTab === item.key ? 'active' : ''}}"
wx:for="{{tabs}}"
wx:key="key"
data-key="{{item.key}}"
bindtap="onTabChange"
>
<text class="tab-text">{{item.name}}</text>
<view class="tab-badge" wx:if="{{item.count > 0}}">{{item.count}}</view>
</view>
</view>
<!-- 通知列表 -->
<scroll-view class="notification-list" scroll-y="{{true}}" bindscrolltolower="onLoadMore">
<view
class="notification-item {{item.isRead ? 'read' : 'unread'}}"
wx:for="{{notificationList}}"
wx:key="id"
data-item="{{item}}"
bindtap="onNotificationTap"
>
<!-- 通知图标 -->
<view class="notification-icon">
<text class="icon-text">{{item.icon}}</text>
<view class="unread-dot" wx:if="{{!item.isRead}}"></view>
</view>
<!-- 通知内容 -->
<view class="notification-content">
<view class="notification-title">{{item.title}}</view>
<view class="notification-desc">{{item.content}}</view>
<view class="notification-meta">
<text class="notification-time">{{item.time}}</text>
<text class="notification-type">{{item.typeName}}</text>
</view>
</view>
<!-- 操作按钮 -->
<view class="notification-actions">
<view class="action-btn" data-item="{{item}}" bindtap="onDeleteNotification" catchtap="true">
<text class="action-icon">🗑️</text>
</view>
</view>
</view>
<!-- 加载更多 -->
<view class="load-more" wx:if="{{hasMore}}">
<text class="load-text">{{loading ? '加载中...' : '上拉加载更多'}}</text>
</view>
<!-- 没有更多数据 -->
<view class="no-more" wx:if="{{!hasMore && notificationList.length > 0}}">
<text class="no-more-text">没有更多通知了</text>
</view>
<!-- 空状态 -->
<view class="empty-state" wx:if="{{notificationList.length === 0 && !loading}}">
<text class="empty-icon">📭</text>
<text class="empty-text">暂无通知消息</text>
</view>
</scroll-view>
</view>

View File

@@ -0,0 +1,250 @@
/* 页面容器 */
.container {
min-height: 100vh;
background-color: #f5f5f5;
}
/* 状态栏 */
.status-bar {
background-color: #4CAF50;
}
/* 头部 */
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
background-color: #4CAF50;
color: white;
}
.header-title {
font-size: 36rpx;
font-weight: bold;
}
.header-actions {
display: flex;
align-items: center;
}
.action-btn {
padding: 10rpx 20rpx;
background-color: rgba(255, 255, 255, 0.2);
border-radius: 20rpx;
}
.action-text {
font-size: 28rpx;
color: white;
}
/* 筛选标签 */
.filter-tabs {
display: flex;
background-color: white;
padding: 0 30rpx;
border-bottom: 1rpx solid #eee;
}
.tab-item {
position: relative;
display: flex;
align-items: center;
padding: 30rpx 20rpx;
margin-right: 40rpx;
border-bottom: 4rpx solid transparent;
transition: all 0.3s ease;
}
.tab-item.active {
border-bottom-color: #4CAF50;
}
.tab-text {
font-size: 30rpx;
color: #666;
}
.tab-item.active .tab-text {
color: #4CAF50;
font-weight: bold;
}
.tab-badge {
position: absolute;
top: 20rpx;
right: 10rpx;
min-width: 32rpx;
height: 32rpx;
line-height: 32rpx;
text-align: center;
font-size: 20rpx;
color: white;
background-color: #ff4757;
border-radius: 16rpx;
padding: 0 8rpx;
}
/* 通知列表 */
.notification-list {
height: calc(100vh - 200rpx);
padding: 20rpx 0;
}
.notification-item {
display: flex;
align-items: flex-start;
padding: 30rpx;
margin: 0 20rpx 20rpx;
background-color: white;
border-radius: 16rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.notification-item.unread {
border-left: 6rpx solid #4CAF50;
}
.notification-item.read {
opacity: 0.7;
}
/* 通知图标 */
.notification-icon {
position: relative;
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #f0f9ff;
border-radius: 50%;
margin-right: 20rpx;
flex-shrink: 0;
}
.icon-text {
font-size: 40rpx;
}
.unread-dot {
position: absolute;
top: 10rpx;
right: 10rpx;
width: 16rpx;
height: 16rpx;
background-color: #ff4757;
border-radius: 50%;
}
/* 通知内容 */
.notification-content {
flex: 1;
min-width: 0;
}
.notification-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 10rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.notification-desc {
font-size: 28rpx;
color: #666;
line-height: 1.5;
margin-bottom: 15rpx;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.notification-meta {
display: flex;
justify-content: space-between;
align-items: center;
}
.notification-time {
font-size: 24rpx;
color: #999;
}
.notification-type {
font-size: 24rpx;
color: #4CAF50;
background-color: rgba(76, 175, 80, 0.1);
padding: 4rpx 12rpx;
border-radius: 12rpx;
}
/* 操作按钮 */
.notification-actions {
display: flex;
flex-direction: column;
align-items: center;
margin-left: 20rpx;
}
.notification-actions .action-btn {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #f5f5f5;
border-radius: 50%;
}
.action-icon {
font-size: 28rpx;
}
/* 加载更多 */
.load-more {
text-align: center;
padding: 40rpx;
}
.load-text {
font-size: 28rpx;
color: #999;
}
/* 没有更多 */
.no-more {
text-align: center;
padding: 40rpx;
}
.no-more-text {
font-size: 28rpx;
color: #ccc;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 40rpx;
}
.empty-icon {
font-size: 120rpx;
margin-bottom: 30rpx;
}
.empty-text {
font-size: 32rpx;
color: #999;
}

View File

@@ -1,42 +0,0 @@
// pages/personnel/personnel.js
Page({
data: {
searchKeyword: '',
loading: false,
personnelList: []
},
onLoad() {
this.loadPersonnelData()
},
loadPersonnelData() {
// 模拟数据
this.setData({
personnelList: [
{
id: 1,
name: '张三',
position: '监管员',
department: '监管部',
phone: '13800138000',
status: 'active',
statusText: '在职'
}
]
})
},
onSearchInput(e) {
this.setData({
searchKeyword: e.detail.value
})
},
handleAdd() {
wx.showToast({
title: '新增功能待实现',
icon: 'none'
})
}
})

View File

@@ -1,3 +0,0 @@
{
"usingComponents": {}
}

View File

@@ -1,61 +0,0 @@
<!--pages/personnel/personnel.wxml-->
<view class="personnel-container">
<!-- 搜索栏 -->
<view class="search-section">
<view class="search-bar">
<input
value="{{searchKeyword}}"
type="text"
placeholder="搜索人员..."
class="search-input"
bindinput="onSearchInput"
/>
<view class="search-icon">🔍</view>
</view>
</view>
<!-- 人员列表 -->
<view class="personnel-list">
<view class="list-header">
<view class="list-title">人员管理</view>
<view class="add-btn" bindtap="handleAdd">
<text class="add-text">新增</text>
<view class="add-icon">+</view>
</view>
</view>
<view wx:if="{{loading}}" class="loading">
<text class="loading-text">加载中...</text>
</view>
<view wx:elif="{{personnelList.length === 0}}" class="empty">
<view class="empty-icon">👥</view>
<view class="empty-text">暂无人员记录</view>
</view>
<view wx:else class="list-content">
<view
wx:for="{{personnelList}}"
wx:key="id"
class="personnel-item"
>
<view class="item-avatar">
<image src="/images/avatar.png" class="avatar-img" />
</view>
<view class="item-content">
<view class="item-header">
<view class="item-name">{{item.name}}</view>
<view class="item-status {{item.status}}">
{{item.statusText}}
</view>
</view>
<view class="item-info">
<view class="item-position">{{item.position}}</view>
<view class="item-department">{{item.department}}</view>
<view class="item-phone">{{item.phone}}</view>
</view>
</view>
</view>
</view>
</view>
</view>

View File

@@ -1,165 +0,0 @@
/* pages/personnel/personnel.wxss */
.personnel-container {
min-height: 100vh;
background: #f6f6f6;
}
.search-section {
padding: 20rpx;
background: #fff;
margin-bottom: 20rpx;
}
.search-bar {
position: relative;
}
.search-input {
width: 100%;
height: 72rpx;
background: #f8f9fa;
border: none;
border-radius: 36rpx;
padding: 0 60rpx 0 30rpx;
font-size: 28rpx;
color: #333;
}
.search-icon {
position: absolute;
right: 30rpx;
top: 50%;
transform: translateY(-50%);
font-size: 28rpx;
color: #999;
}
.personnel-list {
background: #fff;
margin: 0 20rpx;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.list-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.list-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.add-btn {
display: flex;
align-items: center;
padding: 16rpx 24rpx;
background: #1890ff;
border-radius: 24rpx;
color: #fff;
}
.add-text {
font-size: 24rpx;
margin-right: 8rpx;
}
.add-icon {
font-size: 24rpx;
font-weight: 600;
}
.loading,
.empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx;
color: #999;
}
.loading-text,
.empty-text {
font-size: 28rpx;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.list-content {
space-y: 0;
}
.personnel-item {
display: flex;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
transition: background 0.3s;
}
.personnel-item:last-child {
border-bottom: none;
}
.item-avatar {
margin-right: 20rpx;
}
.avatar-img {
width: 80rpx;
height: 80rpx;
border-radius: 40rpx;
background: #f0f0f0;
}
.item-content {
flex: 1;
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12rpx;
}
.item-name {
font-size: 30rpx;
font-weight: 600;
color: #333;
}
.item-status {
font-size: 20rpx;
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-weight: 500;
}
.item-status.active {
background: #f6ffed;
color: #52c41a;
}
.item-info {
display: flex;
flex-direction: column;
space-y: 8rpx;
}
.item-position,
.item-department,
.item-phone {
font-size: 24rpx;
color: #666;
}

View File

@@ -1,88 +1,584 @@
// pages/profile/profile.js
const auth = require('../../utils/auth.js')
const app = getApp();
const apiService = require('../../utils/api.js');
Page({
data: {
userInfo: {},
menuItems: [
statusBarHeight: 0,
loading: true,
// 用户信息
userInfo: {
id: '',
username: '',
name: '',
avatar: '/images/avatar.png',
phone: '',
email: '',
department: '',
position: '',
lastLoginTime: ''
},
// 统计数据
statistics: {
totalFarmers: 0,
totalChecks: 0,
totalAlerts: 0,
totalTasks: 0
},
// 快捷操作
quickActions: [
{
key: 'settings',
title: '设置',
icon: '⚙️',
path: ''
id: 'farmer_manage',
title: '养殖户管理',
icon: 'farmer',
color: '#1890ff',
path: '/pages/farmer/farmer'
},
{
key: 'about',
title: '关于',
icon: '',
path: ''
id: 'data_stats',
title: '数据统计',
icon: 'chart',
color: '#52c41a',
path: '/pages/statistics/statistics'
},
{
key: 'help',
title: '帮助',
icon: '',
path: ''
id: 'notification',
title: '消息通知',
icon: 'notification',
color: '#fa8c16',
path: '/pages/notification/notification'
},
{
key: 'feedback',
title: '反馈',
icon: '💬',
path: ''
id: 'settings',
title: '系统设置',
icon: 'settings',
color: '#722ed1',
path: '/pages/settings/settings'
}
],
// 功能菜单
menuList: [
{
id: 'profile_edit',
title: '个人资料',
icon: 'user',
arrow: true,
path: '/pages/profile/edit/edit'
},
{
id: 'password_change',
title: '修改密码',
icon: 'lock',
arrow: true,
path: '/pages/profile/password/password'
},
{
id: 'notification_settings',
title: '通知设置',
icon: 'bell',
arrow: true,
path: '/pages/profile/notification/notification'
},
{
id: 'about',
title: '关于我们',
icon: 'info',
arrow: true,
path: '/pages/profile/about/about'
},
{
id: 'help',
title: '帮助中心',
icon: 'help',
arrow: true,
path: '/pages/profile/help/help'
}
]
},
onLoad() {
this.loadUserInfo()
},
loadUserInfo() {
const userInfo = auth.getUser()
// 获取状态栏高度
const systemInfo = wx.getSystemInfoSync();
this.setData({
userInfo: userInfo || {}
})
statusBarHeight: systemInfo.statusBarHeight
});
// 检查登录状态
this.checkLoginStatus();
},
handleMenuTap(e) {
const { key } = e.currentTarget.dataset
switch (key) {
case 'settings':
wx.showToast({
title: '设置功能待实现',
icon: 'none'
})
break
case 'about':
wx.showToast({
title: '关于功能待实现',
icon: 'none'
})
break
case 'help':
wx.showToast({
title: '帮助功能待实现',
icon: 'none'
})
break
case 'feedback':
wx.showToast({
title: '反馈功能待实现',
icon: 'none'
})
break
onShow() {
// 每次显示页面时刷新数据
this.loadData();
},
// 检查登录状态
checkLoginStatus() {
const token = wx.getStorageSync('token');
if (!token) {
wx.redirectTo({
url: '/pages/login/login'
});
return;
}
},
handleLogout() {
wx.showModal({
title: '确认退出',
content: '确定要退出登录吗?',
// 加载用户数据
async loadUserData() {
try {
// 从本地存储获取用户信息
const userInfo = wx.getStorageSync('userInfo');
if (userInfo) {
this.setData({
userInfo: {
...this.data.userInfo,
...userInfo
}
});
}
// 模拟从服务器获取最新数据
await this.fetchUserProfile();
} catch (error) {
console.error('加载用户数据失败:', error);
}
},
// 获取用户资料
fetchUserProfile() {
return new Promise((resolve) => {
setTimeout(() => {
// 模拟API返回的用户数据
const profileData = {
name: '张管理员',
department: '宁夏畜牧管理局',
position: '高级管理员',
lastLoginTime: this.formatTime(new Date())
};
this.setData({
userInfo: {
...this.data.userInfo,
...profileData
}
});
resolve();
}, 500);
});
},
// 刷新统计数据
async refreshStats() {
try {
const stats = await this.fetchStatsData();
this.setData({ statsData: stats });
} catch (error) {
console.error('刷新统计数据失败:', error);
}
},
// 获取统计数据
fetchStatsData() {
return new Promise((resolve) => {
setTimeout(() => {
const stats = {
todayCheck: Math.floor(Math.random() * 20) + 5,
totalFarmers: Math.floor(Math.random() * 50) + 150,
totalAlerts: Math.floor(Math.random() * 10),
completedTasks: Math.floor(Math.random() * 20) + 20
};
resolve(stats);
}, 300);
});
},
// 头像点击
onAvatarTap() {
wx.showActionSheet({
itemList: ['查看头像', '更换头像'],
success: (res) => {
if (res.confirm) {
auth.logout()
if (res.tapIndex === 0) {
this.viewAvatar();
} else if (res.tapIndex === 1) {
this.changeAvatar();
}
}
})
});
},
// 查看头像
viewAvatar() {
if (this.data.userInfo.avatar) {
wx.previewImage({
urls: [this.data.userInfo.avatar]
});
} else {
wx.showToast({
title: '暂无头像',
icon: 'none'
});
}
},
// 更换头像
changeAvatar() {
wx.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFilePath = res.tempFilePaths[0];
// 模拟上传头像
wx.showLoading({ title: '上传中...' });
setTimeout(() => {
this.setData({
'userInfo.avatar': tempFilePath
});
wx.hideLoading();
wx.showToast({
title: '头像更新成功',
icon: 'success'
});
}, 2000);
}
});
},
// 菜单项点击
onMenuItemTap(e) {
const { id } = e.currentTarget.dataset;
switch (id) {
case 'personal_info':
this.editPersonalInfo();
break;
case 'change_password':
this.changePassword();
break;
case 'notification_settings':
this.openNotificationSettings();
break;
case 'work_report':
this.viewWorkReport();
break;
case 'help_feedback':
this.openHelpFeedback();
break;
case 'about':
this.showAbout();
break;
default:
wx.showToast({
title: '功能开发中',
icon: 'none'
});
}
},
// 编辑个人信息
editPersonalInfo() {
wx.showModal({
title: '个人信息',
content: '此功能将跳转到个人信息编辑页面',
showCancel: false
});
},
// 修改密码
changePassword() {
wx.showModal({
title: '修改密码',
content: '请输入原密码和新密码',
editable: true,
placeholderText: '请输入原密码',
success: (res) => {
if (res.confirm && res.content) {
wx.showModal({
title: '新密码',
content: '请输入新密码',
editable: true,
placeholderText: '请输入新密码',
success: (newRes) => {
if (newRes.confirm && newRes.content) {
wx.showLoading({ title: '修改中...' });
setTimeout(() => {
wx.hideLoading();
wx.showToast({
title: '密码修改成功',
icon: 'success'
});
}, 2000);
}
}
});
}
}
});
},
// 消息设置
openNotificationSettings() {
wx.showActionSheet({
itemList: ['推送通知', '声音提醒', '震动提醒', '免打扰时间'],
success: (res) => {
const settings = ['推送通知', '声音提醒', '震动提醒', '免打扰时间'];
wx.showToast({
title: `${settings[res.tapIndex]}设置`,
icon: 'none'
});
}
});
},
// 工作报告
viewWorkReport() {
wx.showModal({
title: '工作报告',
content: `本月检查: ${this.data.statsData.todayCheck * 30}\n管理养殖户: ${this.data.statsData.totalFarmers}\n处理告警: ${this.data.statsData.totalAlerts * 10}\n完成任务: ${this.data.statsData.completedTasks * 5}`,
showCancel: false
});
},
// 帮助反馈
openHelpFeedback() {
wx.showActionSheet({
itemList: ['使用帮助', '问题反馈', '联系客服'],
success: (res) => {
const actions = ['使用帮助', '问题反馈', '联系客服'];
if (res.tapIndex === 2) {
wx.makePhoneCall({
phoneNumber: '400-123-4567'
});
} else {
wx.showToast({
title: `${actions[res.tapIndex]}功能开发中`,
icon: 'none'
});
}
}
});
},
// 关于应用
showAbout() {
wx.showModal({
title: '关于应用',
content: '宁夏智慧牧场政府端\n版本: v1.0.0\n更新时间: 2024-01-15\n\n智慧畜牧管理系统助力现代化养殖业发展',
showCancel: false
});
},
// 快捷操作点击
onQuickActionTap(e) {
const { id } = e.currentTarget.dataset;
switch (id) {
case 'scan_qr':
this.scanQRCode();
break;
case 'emergency_report':
this.emergencyReport();
break;
case 'data_export':
this.exportData();
break;
case 'system_settings':
this.systemSettings();
break;
default:
wx.showToast({
title: '功能开发中',
icon: 'none'
});
}
},
// 扫码检查
scanQRCode() {
wx.scanCode({
success: (res) => {
wx.showModal({
title: '扫码结果',
content: `扫码内容: ${res.result}`,
showCancel: false
});
},
fail: () => {
wx.showToast({
title: '扫码失败',
icon: 'none'
});
}
});
},
// 紧急上报
emergencyReport() {
wx.showActionSheet({
itemList: ['疫情报告', '设备故障', '安全事故', '其他紧急情况'],
success: (res) => {
const types = ['疫情报告', '设备故障', '安全事故', '其他紧急情况'];
wx.showModal({
title: '紧急上报',
content: `上报类型: ${types[res.tapIndex]}\n请描述具体情况`,
editable: true,
placeholderText: '请输入详细描述',
success: (modalRes) => {
if (modalRes.confirm) {
wx.showLoading({ title: '上报中...' });
setTimeout(() => {
wx.hideLoading();
wx.showToast({
title: '上报成功',
icon: 'success'
});
}, 2000);
}
}
});
}
});
},
// 数据导出
exportData() {
wx.showActionSheet({
itemList: ['养殖户数据', '检查记录', '告警记录', '统计报表'],
success: (res) => {
const types = ['养殖户数据', '检查记录', '告警记录', '统计报表'];
wx.showLoading({ title: '导出中...' });
setTimeout(() => {
wx.hideLoading();
wx.showToast({
title: `${types[res.tapIndex]}导出成功`,
icon: 'success'
});
}, 3000);
}
});
},
// 系统设置
systemSettings() {
wx.showActionSheet({
itemList: ['清除缓存', '检查更新', '重置设置'],
success: (res) => {
if (res.tapIndex === 0) {
this.clearCache();
} else if (res.tapIndex === 1) {
this.checkUpdate();
} else if (res.tapIndex === 2) {
this.resetSettings();
}
}
});
},
// 清除缓存
clearCache() {
wx.showModal({
title: '清除缓存',
content: '确定要清除应用缓存吗?',
success: (res) => {
if (res.confirm) {
wx.showLoading({ title: '清除中...' });
setTimeout(() => {
wx.hideLoading();
wx.showToast({
title: '缓存清除成功',
icon: 'success'
});
}, 2000);
}
}
});
},
// 检查更新
checkUpdate() {
wx.showLoading({ title: '检查中...' });
setTimeout(() => {
wx.hideLoading();
wx.showModal({
title: '检查更新',
content: '当前已是最新版本',
showCancel: false
});
}, 2000);
},
// 重置设置
resetSettings() {
wx.showModal({
title: '重置设置',
content: '确定要重置所有设置吗?此操作不可恢复',
success: (res) => {
if (res.confirm) {
wx.showLoading({ title: '重置中...' });
setTimeout(() => {
wx.hideLoading();
wx.showToast({
title: '设置重置成功',
icon: 'success'
});
}, 2000);
}
}
});
},
// 退出登录
onLogout() {
wx.showModal({
title: '退出登录',
content: '确定要退出当前账号吗?',
success: (res) => {
if (res.confirm) {
// 清除本地存储
wx.removeStorageSync('token');
wx.removeStorageSync('userInfo');
wx.showToast({
title: '已退出登录',
icon: 'success'
});
// 跳转到登录页
setTimeout(() => {
wx.reLaunch({
url: '/pages/login/login'
});
}, 1500);
}
}
});
},
// 格式化时间
formatTime(date) {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
const hour = date.getHours().toString().padStart(2, '0');
const minute = date.getMinutes().toString().padStart(2, '0');
const second = date.getSeconds().toString().padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}
})
});

View File

@@ -1,3 +1,4 @@
{
"usingComponents": {}
}
"usingComponents": {},
"navigationBarTitleText": "我的"
}

View File

@@ -1,36 +1,93 @@
<!--pages/profile/profile.wxml-->
<view class="profile-container">
<!-- 用户信息区域 -->
<view class="user-section">
<view class="user-avatar">
<image src="/images/avatar.png" class="avatar-img" />
</view>
<view class="container">
<!-- 状态栏 -->
<view class="status-bar" style="height: {{statusBarHeight}}px;"></view>
<!-- 用户信息卡片 -->
<view class="user-card">
<view class="user-info">
<view class="username">{{userInfo.name || '管理员'}}</view>
<view class="user-role">{{userInfo.role || '系统管理员'}}</view>
<view class="user-phone">{{userInfo.phone || '13800138000'}}</view>
<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>
</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>
</view>
<!-- 功能菜单 -->
<view class="menu-section">
<view
wx:for="{{menuItems}}"
wx:key="key"
class="menu-item"
data-key="{{item.key}}"
bindtap="handleMenuTap"
>
<view class="menu-icon">{{item.icon}}</view>
<view class="menu-title">{{item.title}}</view>
<view class="menu-arrow">></view>
<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>
</view>
<!-- 退出登录 -->
<view class="logout-section">
<button class="logout-btn" bindtap="handleLogout">
退出登录
</button>
<button class="logout-btn" bindtap="onLogout">退出登录</button>
</view>
</view>
</view>

View File

@@ -1,61 +1,200 @@
/* pages/profile/profile.wxss */
.profile-container {
.container {
min-height: 100vh;
background: #f6f6f6;
background-color: #f5f5f5;
}
.user-section {
background: #fff;
padding: 60rpx 30rpx;
margin-bottom: 20rpx;
.status-bar {
background-color: #2c5aa0;
}
/* 用户信息卡片 */
.user-card {
background: linear-gradient(135deg, #2c5aa0 0%, #4a7bc8 100%);
padding: 40rpx 30rpx 30rpx;
color: white;
}
.user-info {
display: flex;
align-items: center;
}
.user-avatar {
.avatar-container {
position: relative;
margin-right: 30rpx;
}
.avatar-img {
.avatar {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
background: #f0f0f0;
border: 4rpx solid rgba(255, 255, 255, 0.3);
}
.user-info {
.avatar-placeholder {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
background-color: rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
border: 4rpx solid rgba(255, 255, 255, 0.3);
}
.avatar-text {
font-size: 48rpx;
font-weight: bold;
color: white;
}
.avatar-badge {
position: absolute;
bottom: 0;
right: 0;
width: 36rpx;
height: 36rpx;
background-color: #fff;
border-radius: 18rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 20rpx;
}
.user-details {
flex: 1;
}
.username {
.user-name {
font-size: 36rpx;
font-weight: 600;
color: #333;
margin-bottom: 12rpx;
}
.user-role {
font-size: 28rpx;
color: #666;
font-weight: bold;
margin-bottom: 8rpx;
}
.user-phone {
font-size: 24rpx;
color: #999;
.user-account {
font-size: 28rpx;
opacity: 0.9;
margin-bottom: 6rpx;
}
.menu-section {
background: #fff;
.user-department {
font-size: 26rpx;
opacity: 0.8;
margin-bottom: 4rpx;
}
.user-position {
font-size: 26rpx;
opacity: 0.8;
margin-bottom: 4rpx;
}
.last-login {
font-size: 24rpx;
opacity: 0.7;
}
/* 统计数据 */
.stats-section {
background-color: white;
margin: 20rpx;
border-radius: 16rpx;
padding: 30rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
}
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20rpx;
}
.stat-item {
text-align: center;
padding: 20rpx;
background-color: #f8f9fa;
border-radius: 12rpx;
}
.stat-number {
font-size: 48rpx;
font-weight: bold;
color: #2c5aa0;
margin-bottom: 8rpx;
}
.stat-label {
font-size: 26rpx;
color: #666;
}
/* 快捷操作 */
.quick-actions-section {
background-color: white;
margin: 20rpx;
border-radius: 16rpx;
padding: 30rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.quick-actions {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20rpx;
}
.quick-action-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx;
border-radius: 12rpx;
transition: all 0.3s ease;
}
.quick-action-item:active {
transform: scale(0.95);
}
.quick-action-icon {
font-size: 40rpx;
margin-bottom: 8rpx;
}
.quick-action-title {
font-size: 24rpx;
color: #333;
text-align: center;
}
/* 功能菜单 */
.menu-section {
background-color: white;
margin: 20rpx;
border-radius: 16rpx;
padding: 30rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.menu-list {
margin-top: 20rpx;
}
.menu-item {
display: flex;
align-items: center;
padding: 30rpx;
padding: 24rpx 0;
border-bottom: 1rpx solid #f0f0f0;
transition: background 0.3s;
transition: background-color 0.3s ease;
}
.menu-item:last-child {
@@ -63,41 +202,60 @@
}
.menu-item:active {
background: #f8f9fa;
background-color: #f8f9fa;
}
.menu-icon {
font-size: 32rpx;
width: 48rpx;
height: 48rpx;
margin-right: 24rpx;
width: 40rpx;
text-align: center;
font-size: 36rpx;
display: flex;
align-items: center;
justify-content: center;
}
.menu-content {
flex: 1;
}
.menu-title {
flex: 1;
font-size: 30rpx;
color: #333;
margin-bottom: 4rpx;
}
.menu-desc {
font-size: 24rpx;
color: #999;
}
.menu-arrow {
font-size: 24rpx;
font-size: 28rpx;
color: #ccc;
}
/* 退出登录 */
.logout-section {
padding: 30rpx;
padding: 40rpx 20rpx;
}
.logout-btn {
width: 100%;
height: 88rpx;
background: #ff4d4f;
color: #fff;
background-color: #ff4757;
color: white;
border: none;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 600;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
}
transition: all 0.3s ease;
}
.logout-btn:active {
background-color: #ff3742;
transform: scale(0.98);
}

View File

@@ -1,42 +0,0 @@
// pages/service/service.js
Page({
data: {
searchKeyword: '',
loading: false,
serviceList: []
},
onLoad() {
this.loadServiceData()
},
loadServiceData() {
// 模拟数据
this.setData({
serviceList: [
{
id: 1,
title: '技术服务',
description: '为养殖户提供技术指导',
type: 'technical',
typeText: '技术服务',
status: 'active',
statusText: '进行中'
}
]
})
},
onSearchInput(e) {
this.setData({
searchKeyword: e.detail.value
})
},
handleAdd() {
wx.showToast({
title: '新增功能待实现',
icon: 'none'
})
}
})

View File

@@ -1,3 +0,0 @@
{
"usingComponents": {}
}

View File

@@ -1,57 +0,0 @@
<!--pages/service/service.wxml-->
<view class="service-container">
<!-- 搜索栏 -->
<view class="search-section">
<view class="search-bar">
<input
value="{{searchKeyword}}"
type="text"
placeholder="搜索服务记录..."
class="search-input"
bindinput="onSearchInput"
/>
<view class="search-icon">🔍</view>
</view>
</view>
<!-- 服务列表 -->
<view class="service-list">
<view class="list-header">
<view class="list-title">服务管理</view>
<view class="add-btn" bindtap="handleAdd">
<text class="add-text">新增</text>
<view class="add-icon">+</view>
</view>
</view>
<view wx:if="{{loading}}" class="loading">
<text class="loading-text">加载中...</text>
</view>
<view wx:elif="{{serviceList.length === 0}}" class="empty">
<view class="empty-icon">🛠️</view>
<view class="empty-text">暂无服务记录</view>
</view>
<view wx:else class="list-content">
<view
wx:for="{{serviceList}}"
wx:key="id"
class="service-item"
>
<view class="item-header">
<view class="item-title">{{item.title}}</view>
<view class="item-status {{item.status}}">
{{item.statusText}}
</view>
</view>
<view class="item-content">
<view class="item-desc">{{item.description}}</view>
<view class="item-meta">
<view class="item-type">{{item.typeText}}</view>
</view>
</view>
</view>
</view>
</view>
</view>

View File

@@ -1,158 +0,0 @@
/* pages/service/service.wxss */
.service-container {
min-height: 100vh;
background: #f6f6f6;
}
.search-section {
padding: 20rpx;
background: #fff;
margin-bottom: 20rpx;
}
.search-bar {
position: relative;
}
.search-input {
width: 100%;
height: 72rpx;
background: #f8f9fa;
border: none;
border-radius: 36rpx;
padding: 0 60rpx 0 30rpx;
font-size: 28rpx;
color: #333;
}
.search-icon {
position: absolute;
right: 30rpx;
top: 50%;
transform: translateY(-50%);
font-size: 28rpx;
color: #999;
}
.service-list {
background: #fff;
margin: 0 20rpx;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.list-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.list-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.add-btn {
display: flex;
align-items: center;
padding: 16rpx 24rpx;
background: #1890ff;
border-radius: 24rpx;
color: #fff;
}
.add-text {
font-size: 24rpx;
margin-right: 8rpx;
}
.add-icon {
font-size: 24rpx;
font-weight: 600;
}
.loading,
.empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx;
color: #999;
}
.loading-text,
.empty-text {
font-size: 28rpx;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.list-content {
space-y: 0;
}
.service-item {
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
transition: background 0.3s;
}
.service-item:last-child {
border-bottom: none;
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.item-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
flex: 1;
}
.item-status {
font-size: 20rpx;
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-weight: 500;
}
.item-status.active {
background: #f6ffed;
color: #52c41a;
}
.item-content {
margin-bottom: 20rpx;
}
.item-desc {
font-size: 26rpx;
color: #666;
margin-bottom: 12rpx;
line-height: 1.5;
}
.item-meta {
display: flex;
justify-content: space-between;
align-items: center;
}
.item-type {
font-size: 22rpx;
color: #999;
}

View File

@@ -0,0 +1,296 @@
// pages/statistics/statistics.js
const app = getApp();
const apiService = require('../../utils/api.js');
Page({
data: {
statusBarHeight: 0,
loading: true,
// 统计数据
todayStats: {
totalFarmers: 0,
activeFarmers: 0,
totalAnimals: 0,
alertCount: 0
},
// 趋势数据
trendData: {
farmerGrowth: 0,
animalGrowth: 0,
alertTrend: 0
},
// 快捷操作
quickActions: [
{
id: 'farmer_manage',
title: '养殖户管理',
icon: 'farmer',
color: '#2c5aa0',
path: '/pages/farmer/farmer'
},
{
id: 'device_monitor',
title: '设备监控',
icon: 'settings',
color: '#52c41a',
path: '/pages/statistics/statistics'
},
{
id: 'epidemic_control',
title: '疫情防控',
icon: 'epidemic',
color: '#fa8c16',
path: '/pages/statistics/statistics'
},
{
id: 'data_analysis',
title: '数据分析',
icon: 'home',
color: '#722ed1',
path: '/pages/statistics/statistics'
}
],
// 最新动态
recentActivities: [],
// 市场价格
marketPrice: {
beef: { price: 0, trend: 0 },
mutton: { price: 0, trend: 0 },
milk: { price: 0, trend: 0 }
}
},
onLoad() {
// 获取状态栏高度
const systemInfo = wx.getSystemInfoSync();
this.setData({
statusBarHeight: systemInfo.statusBarHeight
});
// 检查登录状态
this.checkLoginStatus();
},
onShow() {
// 每次显示页面时刷新数据
this.loadData();
},
// 检查登录状态
checkLoginStatus() {
const token = wx.getStorageSync('token');
if (!token) {
wx.reLaunch({
url: '/pages/login/login'
});
return false;
}
return true;
},
// 加载数据
async loadData() {
if (!this.checkLoginStatus()) return;
this.setData({ loading: true });
try {
// 并行加载多个数据
const [statsResult, marketResult] = await Promise.allSettled([
this.loadStatisticsData(),
this.loadMarketPrice()
]);
// 处理统计数据结果
if (statsResult.status === 'fulfilled') {
console.log('统计数据加载成功');
} else {
console.error('统计数据加载失败:', statsResult.reason);
this.loadMockStatistics();
}
// 处理市场价格结果
if (marketResult.status === 'fulfilled') {
console.log('市场价格加载成功');
} else {
console.error('市场价格加载失败:', marketResult.reason);
this.loadMockMarketPrice();
}
} catch (error) {
console.error('数据加载失败:', error);
// 加载模拟数据作为备用
this.loadMockData();
} finally {
this.setData({ loading: false });
}
},
// 加载统计数据
async loadStatisticsData() {
try {
const result = await apiService.getDataCenterStats();
if (result.code === 200 || result.code === 0) {
const data = result.data || result;
this.setData({
todayStats: {
totalFarmers: data.totalFarmers || 0,
activeFarmers: data.activeFarmers || 0,
totalAnimals: data.totalAnimals || 0,
alertCount: data.alertCount || 0
},
trendData: {
farmerGrowth: data.farmerGrowth || 0,
animalGrowth: data.animalGrowth || 0,
alertTrend: data.alertTrend || 0
}
});
// 如果有最新动态数据
if (data.recentActivities) {
this.setData({
recentActivities: data.recentActivities
});
}
}
} catch (error) {
console.error('获取统计数据失败:', error);
throw error;
}
},
// 加载市场价格
async loadMarketPrice() {
try {
const result = await apiService.getMarketPrice();
if (result.code === 200 || result.code === 0) {
const data = result.data || result;
this.setData({
marketPrice: {
beef: data.beef || { price: 0, trend: 0 },
mutton: data.mutton || { price: 0, trend: 0 },
milk: data.milk || { price: 0, trend: 0 }
}
});
}
} catch (error) {
console.error('获取市场价格失败:', error);
throw error;
}
},
// 加载模拟统计数据
loadMockStatistics() {
this.setData({
todayStats: {
totalFarmers: 156,
activeFarmers: 142,
totalAnimals: 3248,
alertCount: 8
},
trendData: {
farmerGrowth: 12,
animalGrowth: 156,
alertTrend: -3
},
recentActivities: [
{
id: 1,
type: 'farmer',
title: '新增养殖户',
content: '张三申请加入养殖户管理',
time: '2小时前'
},
{
id: 2,
type: 'alert',
title: '设备告警',
content: '智能项圈001离线超过30分钟',
time: '3小时前'
},
{
id: 3,
type: 'epidemic',
title: '疫情防控',
content: '完成第三季度疫苗接种统计',
time: '5小时前'
}
]
});
},
// 加载模拟市场价格
loadMockMarketPrice() {
this.setData({
marketPrice: {
beef: { price: 68.5, trend: 2.3 },
mutton: { price: 72.8, trend: -1.2 },
milk: { price: 4.2, trend: 0.8 }
}
});
},
// 加载所有模拟数据
loadMockData() {
this.loadMockStatistics();
this.loadMockMarketPrice();
},
// 快捷操作点击
onQuickActionTap(e) {
const { action } = e.currentTarget.dataset;
const actionItem = this.data.quickActions.find(item => item.id === action);
if (actionItem && actionItem.path) {
if (actionItem.path.startsWith('/pages/')) {
// 如果是tabBar页面使用switchTab
if (actionItem.path.includes('/farmer/farmer')) {
wx.switchTab({
url: actionItem.path
});
} else {
wx.navigateTo({
url: actionItem.path
});
}
}
} else {
wx.showToast({
title: '功能开发中',
icon: 'none'
});
}
},
// 查看更多动态
onViewMoreActivities() {
wx.showToast({
title: '功能开发中',
icon: 'none'
});
},
// 下拉刷新
onPullDownRefresh() {
this.loadData().finally(() => {
wx.stopPullDownRefresh();
});
},
// 分享页面
onShareAppMessage() {
return {
title: '政府监管端 - 数据统计',
path: '/pages/statistics/statistics'
};
}
});

View File

@@ -0,0 +1,6 @@
{
"navigationBarTitleText": "数据统计",
"enablePullDownRefresh": true,
"backgroundColor": "#f5f5f5",
"backgroundTextStyle": "dark"
}

View File

@@ -0,0 +1,121 @@
<!--pages/statistics/statistics.wxml-->
<view class="container">
<!-- 状态栏 -->
<view class="status-bar">
<view class="status-left">
<text>{{currentTime || '17:05'}}</text>
</view>
<view class="status-right">
<text>100%</text>
<view class="battery-icon"></view>
</view>
</view>
<!-- 顶部导航栏 -->
<view class="header">
<view class="header-title">数据统计</view>
<view class="header-actions">
<view class="action-btn" bindtap="onRefresh">
<text class="icon-refresh">🔄</text>
</view>
<view class="action-btn" bindtap="onExport">
<text class="icon-export">📊</text>
</view>
</view>
</view>
<!-- 统计卡片区域 -->
<view class="stats-cards">
<view
class="stat-card"
wx:for="{{statsData}}"
wx:key="id"
data-item="{{item}}"
bindtap="onStatCardTap"
>
<view class="card-icon" style="background-color: {{item.color}}">
<text class="icon">{{item.icon}}</text>
</view>
<view class="card-content">
<view class="card-title">{{item.title}}</view>
<view class="card-value">{{item.value}}</view>
<view class="card-trend {{item.trend > 0 ? 'up' : item.trend < 0 ? 'down' : 'stable'}}">
<text class="trend-icon">{{item.trend > 0 ? '↗' : item.trend < 0 ? '↘' : '→'}}</text>
<text class="trend-text">{{item.trendText}}</text>
</view>
</view>
</view>
</view>
<!-- 图表区域 -->
<view class="chart-section">
<view class="section-header">
<text class="section-title">监管趋势</text>
<view class="time-filter">
<view
class="filter-item {{selectedPeriod === item ? 'active' : ''}}"
wx:for="{{timePeriods}}"
wx:key="*this"
data-period="{{item}}"
bindtap="onPeriodChange"
>
{{item}}
</view>
</view>
</view>
<!-- 模拟图表 -->
<view class="chart-container">
<view class="chart-placeholder">
<text class="chart-text">📈 监管数据趋势图</text>
<text class="chart-desc">显示最近{{selectedPeriod}}的监管活动统计</text>
</view>
</view>
</view>
<!-- 详细列表 -->
<view class="detail-section">
<view class="section-header">
<text class="section-title">最新监管记录</text>
<view class="more-btn" bindtap="onViewMore">
查看更多 >
</view>
</view>
<view class="record-list">
<view
class="record-item"
wx:for="{{recentRecords}}"
wx:key="id"
data-item="{{item}}"
bindtap="onRecordTap"
>
<view class="record-icon">
<text class="status-dot {{item.status}}"></text>
</view>
<view class="record-content">
<view class="record-title">{{item.title}}</view>
<view class="record-desc">{{item.description}}</view>
<view class="record-time">{{item.time}}</view>
</view>
<view class="record-arrow">></view>
</view>
</view>
</view>
<!-- 快捷操作 -->
<view class="quick-actions">
<view
class="action-item"
wx:for="{{quickActions}}"
wx:key="id"
data-item="{{item}}"
bindtap="onQuickAction"
>
<view class="action-icon" style="background-color: {{item.color}}">
<text>{{item.icon}}</text>
</view>
<text class="action-name">{{item.name}}</text>
</view>
</view>
</view>

View File

@@ -0,0 +1,313 @@
/* pages/statistics/statistics.wxss */
.container {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 20rpx;
}
/* 状态栏样式 */
.status-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 30rpx;
background-color: #4CAF50;
color: white;
font-size: 28rpx;
font-weight: bold;
}
.status-right {
display: flex;
align-items: center;
gap: 10rpx;
}
.battery-icon {
width: 40rpx;
height: 20rpx;
border: 2rpx solid white;
border-radius: 4rpx;
position: relative;
}
.battery-icon::after {
content: '';
position: absolute;
right: -6rpx;
top: 6rpx;
width: 4rpx;
height: 8rpx;
background-color: white;
border-radius: 0 2rpx 2rpx 0;
}
/* 顶部导航栏 */
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15rpx 30rpx;
background-color: #4CAF50;
color: white;
height: 80rpx;
}
.header-title {
font-size: 36rpx;
font-weight: bold;
}
.header-actions {
display: flex;
gap: 20rpx;
}
.action-btn {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.2);
font-size: 28rpx;
}
/* 统计卡片 */
.stats-cards {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20rpx;
padding: 20rpx;
}
.stat-card {
background-color: white;
border-radius: 12rpx;
padding: 30rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
gap: 20rpx;
}
.card-icon {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 36rpx;
}
.card-content {
flex: 1;
}
.card-title {
font-size: 24rpx;
color: #666;
margin-bottom: 8rpx;
}
.card-value {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.card-trend {
display: flex;
align-items: center;
gap: 4rpx;
font-size: 22rpx;
}
.card-trend.up {
color: #4CAF50;
}
.card-trend.down {
color: #f44336;
}
.card-trend.stable {
color: #666;
}
/* 图表区域 */
.chart-section {
margin: 20rpx;
background-color: white;
border-radius: 12rpx;
padding: 30rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.time-filter {
display: flex;
gap: 10rpx;
}
.filter-item {
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-size: 24rpx;
color: #666;
background-color: #f5f5f5;
transition: all 0.3s ease;
}
.filter-item.active {
background-color: #4CAF50;
color: white;
}
.chart-container {
height: 400rpx;
background-color: #f8f9fa;
border-radius: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border: 2rpx dashed #ddd;
}
.chart-text {
font-size: 32rpx;
color: #666;
margin-bottom: 10rpx;
}
.chart-desc {
font-size: 24rpx;
color: #999;
}
/* 详细列表 */
.detail-section {
margin: 20rpx;
background-color: white;
border-radius: 12rpx;
padding: 30rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
}
.more-btn {
font-size: 28rpx;
color: #4CAF50;
}
.record-list {
margin-top: 20rpx;
}
.record-item {
display: flex;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.record-item:last-child {
border-bottom: none;
}
.record-icon {
margin-right: 20rpx;
}
.status-dot {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
display: inline-block;
}
.status-dot.success {
background-color: #4CAF50;
}
.status-dot.warning {
background-color: #ff9800;
}
.status-dot.error {
background-color: #f44336;
}
.record-content {
flex: 1;
}
.record-title {
font-size: 28rpx;
color: #333;
margin-bottom: 8rpx;
}
.record-desc {
font-size: 24rpx;
color: #666;
margin-bottom: 8rpx;
}
.record-time {
font-size: 22rpx;
color: #999;
}
.record-arrow {
color: #ccc;
font-size: 24rpx;
}
/* 快捷操作 */
.quick-actions {
display: flex;
justify-content: space-around;
padding: 30rpx 20rpx;
background-color: white;
margin: 20rpx;
border-radius: 12rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
}
.action-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 10rpx;
}
.action-icon {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 36rpx;
}
.action-name {
font-size: 24rpx;
color: #666;
}

View File

@@ -1,49 +0,0 @@
// pages/supervision/supervision.js
Page({
data: {
searchKeyword: '',
loading: false,
supervisionList: []
},
onLoad() {
this.loadSupervisionData()
},
onPullDownRefresh() {
this.loadSupervisionData()
setTimeout(() => {
wx.stopPullDownRefresh()
}, 1000)
},
loadSupervisionData() {
// 模拟数据
this.setData({
supervisionList: [
{
id: 1,
title: '养殖场A例行检查',
description: '完成对养殖场A的例行监管检查',
location: '宁夏银川市',
createTime: '2024-01-15 10:30',
status: 'completed',
statusText: '已完成'
}
]
})
},
onSearchInput(e) {
this.setData({
searchKeyword: e.detail.value
})
},
handleAdd() {
wx.showToast({
title: '新增功能待实现',
icon: 'none'
})
}
})

View File

@@ -1,3 +0,0 @@
{
"usingComponents": {}
}

View File

@@ -1,58 +0,0 @@
<!--pages/supervision/supervision.wxml-->
<view class="supervision-container">
<!-- 搜索栏 -->
<view class="search-section">
<view class="search-bar">
<input
value="{{searchKeyword}}"
type="text"
placeholder="搜索监管记录..."
class="search-input"
bindinput="onSearchInput"
/>
<view class="search-icon">🔍</view>
</view>
</view>
<!-- 监管列表 -->
<view class="supervision-list">
<view class="list-header">
<view class="list-title">监管记录</view>
<view class="add-btn" bindtap="handleAdd">
<text class="add-text">新增</text>
<view class="add-icon">+</view>
</view>
</view>
<view wx:if="{{loading}}" class="loading">
<text class="loading-text">加载中...</text>
</view>
<view wx:elif="{{supervisionList.length === 0}}" class="empty">
<view class="empty-icon">📋</view>
<view class="empty-text">暂无监管记录</view>
</view>
<view wx:else class="list-content">
<view
wx:for="{{supervisionList}}"
wx:key="id"
class="supervision-item"
>
<view class="item-header">
<view class="item-title">{{item.title}}</view>
<view class="item-status {{item.status}}">
{{item.statusText}}
</view>
</view>
<view class="item-content">
<view class="item-desc">{{item.description}}</view>
<view class="item-meta">
<view class="item-location">📍 {{item.location}}</view>
<view class="item-time">{{item.createTime}}</view>
</view>
</view>
</view>
</view>
</view>
</view>

View File

@@ -1,158 +0,0 @@
/* pages/supervision/supervision.wxss */
.supervision-container {
min-height: 100vh;
background: #f6f6f6;
}
.search-section {
padding: 20rpx;
background: #fff;
margin-bottom: 20rpx;
}
.search-bar {
position: relative;
}
.search-input {
width: 100%;
height: 72rpx;
background: #f8f9fa;
border: none;
border-radius: 36rpx;
padding: 0 60rpx 0 30rpx;
font-size: 28rpx;
color: #333;
}
.search-icon {
position: absolute;
right: 30rpx;
top: 50%;
transform: translateY(-50%);
font-size: 28rpx;
color: #999;
}
.supervision-list {
background: #fff;
margin: 0 20rpx;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.list-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.list-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.add-btn {
display: flex;
align-items: center;
padding: 16rpx 24rpx;
background: #1890ff;
border-radius: 24rpx;
color: #fff;
}
.add-text {
font-size: 24rpx;
margin-right: 8rpx;
}
.add-icon {
font-size: 24rpx;
font-weight: 600;
}
.loading,
.empty {
display: flex;
align-items: center;
justify-content: center;
padding: 80rpx;
color: #999;
}
.loading-text,
.empty-text {
font-size: 28rpx;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.list-content {
space-y: 0;
}
.supervision-item {
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
transition: background 0.3s;
}
.supervision-item:last-child {
border-bottom: none;
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.item-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
flex: 1;
}
.item-status {
font-size: 20rpx;
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-weight: 500;
}
.item-status.completed {
background: #f6ffed;
color: #52c41a;
}
.item-content {
margin-bottom: 20rpx;
}
.item-desc {
font-size: 26rpx;
color: #666;
margin-bottom: 12rpx;
line-height: 1.5;
}
.item-meta {
display: flex;
justify-content: space-between;
align-items: center;
}
.item-location,
.item-time {
font-size: 22rpx;
color: #999;
}

View File

@@ -1,42 +0,0 @@
// pages/warehouse/warehouse.js
Page({
data: {
searchKeyword: '',
loading: false,
warehouseList: []
},
onLoad() {
this.loadWarehouseData()
},
loadWarehouseData() {
// 模拟数据
this.setData({
warehouseList: [
{
id: 1,
title: '疫苗库存',
description: '各类疫苗库存管理',
quantity: 100,
unit: '支',
status: 'normal',
statusText: '正常'
}
]
})
},
onSearchInput(e) {
this.setData({
searchKeyword: e.detail.value
})
},
handleAdd() {
wx.showToast({
title: '新增功能待实现',
icon: 'none'
})
}
})

View File

@@ -1,3 +0,0 @@
{
"usingComponents": {}
}

View File

@@ -1,57 +0,0 @@
<!--pages/warehouse/warehouse.wxml-->
<view class="warehouse-container">
<!-- 搜索栏 -->
<view class="search-section">
<view class="search-bar">
<input
value="{{searchKeyword}}"
type="text"
placeholder="搜索库存记录..."
class="search-input"
bindinput="onSearchInput"
/>
<view class="search-icon">🔍</view>
</view>
</view>
<!-- 库存列表 -->
<view class="warehouse-list">
<view class="list-header">
<view class="list-title">仓库管理</view>
<view class="add-btn" bindtap="handleAdd">
<text class="add-text">新增</text>
<view class="add-icon">+</view>
</view>
</view>
<view wx:if="{{loading}}" class="loading">
<text class="loading-text">加载中...</text>
</view>
<view wx:elif="{{warehouseList.length === 0}}" class="empty">
<view class="empty-icon">📦</view>
<view class="empty-text">暂无库存记录</view>
</view>
<view wx:else class="list-content">
<view
wx:for="{{warehouseList}}"
wx:key="id"
class="warehouse-item"
>
<view class="item-header">
<view class="item-title">{{item.title}}</view>
<view class="item-status {{item.status}}">
{{item.statusText}}
</view>
</view>
<view class="item-content">
<view class="item-desc">{{item.description}}</view>
<view class="item-meta">
<view class="item-quantity">{{item.quantity}} {{item.unit}}</view>
</view>
</view>
</view>
</view>
</view>
</view>

View File

@@ -1,158 +0,0 @@
/* pages/warehouse/warehouse.wxss */
.warehouse-container {
min-height: 100vh;
background: #f6f6f6;
}
.search-section {
padding: 20rpx;
background: #fff;
margin-bottom: 20rpx;
}
.search-bar {
position: relative;
}
.search-input {
width: 100%;
height: 72rpx;
background: #f8f9fa;
border: none;
border-radius: 36rpx;
padding: 0 60rpx 0 30rpx;
font-size: 28rpx;
color: #333;
}
.search-icon {
position: absolute;
right: 30rpx;
top: 50%;
transform: translateY(-50%);
font-size: 28rpx;
color: #999;
}
.warehouse-list {
background: #fff;
margin: 0 20rpx;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.list-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.list-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.add-btn {
display: flex;
align-items: center;
padding: 16rpx 24rpx;
background: #1890ff;
border-radius: 24rpx;
color: #fff;
}
.add-text {
font-size: 24rpx;
margin-right: 8rpx;
}
.add-icon {
font-size: 24rpx;
font-weight: 600;
}
.loading,
.empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx;
color: #999;
}
.loading-text,
.empty-text {
font-size: 28rpx;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.list-content {
space-y: 0;
}
.warehouse-item {
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
transition: background 0.3s;
}
.warehouse-item:last-child {
border-bottom: none;
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.item-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
flex: 1;
}
.item-status {
font-size: 20rpx;
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-weight: 500;
}
.item-status.normal {
background: #f6ffed;
color: #52c41a;
}
.item-content {
margin-bottom: 20rpx;
}
.item-desc {
font-size: 26rpx;
color: #666;
margin-bottom: 12rpx;
line-height: 1.5;
}
.item-meta {
display: flex;
justify-content: space-between;
align-items: center;
}
.item-quantity {
font-size: 22rpx;
color: #999;
}