添加银行和政府端小程序

This commit is contained in:
2025-09-19 17:52:28 +08:00
parent e9f182f2d3
commit eb3c4604d3
318 changed files with 147971 additions and 2999 deletions

View File

@@ -0,0 +1,42 @@
// 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

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

View File

@@ -0,0 +1,58 @@
<!--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

@@ -0,0 +1,159 @@
/* 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

@@ -0,0 +1,137 @@
// 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

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

View File

@@ -0,0 +1,71 @@
<!--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

@@ -0,0 +1,210 @@
/* 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

@@ -0,0 +1,42 @@
// 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

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

View File

@@ -0,0 +1,58 @@
<!--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

@@ -0,0 +1,169 @@
/* 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,102 @@
// pages/index/index.js
const auth = require('../../utils/auth.js')
const dashboardService = require('../../services/dashboardService.js')
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: [
{
id: 1,
icon: '🔍',
title: '监管检查',
desc: '完成养殖场A的例行检查',
time: '2小时前'
},
{
id: 2,
icon: '✅',
title: '审批通过',
desc: '通过养殖许可证申请',
time: '4小时前'
},
{
id: 3,
icon: '👥',
title: '人员管理',
desc: '新增监管员张三',
time: '6小时前'
}
]
},
onLoad() {
this.initData()
},
onPullDownRefresh() {
this.loadData()
setTimeout(() => {
wx.stopPullDownRefresh()
}, 1000)
},
initData() {
// 获取用户信息
this.setData({
userInfo: auth.getUser() || {}
})
// 加载数据
this.loadData()
},
async loadData() {
try {
// 加载统计数据
const stats = await dashboardService.getStats()
if (stats) {
this.updateStatsData(stats)
}
} catch (error) {
console.error('加载数据失败:', error)
}
},
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
})
}
}
})

View File

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

View File

@@ -0,0 +1,75 @@
<!--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>
</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>
</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}}"
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>
</view>
</view>
</view>

View File

@@ -0,0 +1,187 @@
/* pages/index/index.wxss */
.home-container {
min-height: 100vh;
background: #f6f6f6;
}
.welcome-section {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 40rpx 30rpx 60rpx;
color: #fff;
}
.welcome-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.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 {
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 {
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;
}

View File

@@ -0,0 +1,85 @@
// pages/login/login.js
const authService = require('../../services/authService.js')
const auth = require('../../utils/auth.js')
Page({
data: {
username: '',
password: '',
showPassword: false,
loading: false
},
onLoad() {
// 检查是否已登录
if (auth.isAuthenticated()) {
wx.reLaunch({
url: '/pages/index/index'
})
}
},
onUsernameInput(e) {
this.setData({
username: e.detail.value
})
},
onPasswordInput(e) {
this.setData({
password: e.detail.value
})
},
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 })
try {
const response = await authService.login(username, password)
if (response && response.token) {
// 保存token
auth.setToken(response.token)
// 获取用户信息
const userInfo = await authService.getUserInfo()
if (userInfo) {
auth.setUser(userInfo)
}
// 显示成功提示
wx.showToast({
title: '登录成功',
icon: 'success',
duration: 2000
})
// 跳转到首页
setTimeout(() => {
wx.reLaunch({
url: '/pages/index/index'
})
}, 1000)
}
} catch (error) {
console.error('登录失败:', error)
wx.showToast({
title: error.message || '登录失败',
icon: 'error',
duration: 3000
})
} finally {
this.setData({ loading: false })
}
}
})

View File

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

View File

@@ -0,0 +1,53 @@
<!--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>
<view class="login-form">
<view class="form-item">
<input
value="{{username}}"
type="text"
placeholder="请输入用户名"
class="input"
bindinput="onUsernameInput"
/>
</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>
</view>
<view class="form-item">
<button
class="login-btn {{(!username || !password || loading) ? 'disabled' : ''}}"
disabled="{{!username || !password || loading}}"
bindtap="handleLogin"
>
{{loading ? '登录中...' : '登录'}}
</button>
</view>
<view class="login-tips">
<text class="tips-text">默认账号admin / 123456</text>
</view>
</view>
<view class="login-footer">
<text class="footer-text">© 2024 政府管理系统</text>
</view>
</view>

View File

@@ -0,0 +1,116 @@
/* pages/login/login.wxss */
.login-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;
}
.login-header {
text-align: center;
margin-bottom: 80rpx;
}
.logo {
margin-bottom: 40rpx;
}
.logo-img {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
background: #fff;
}
.title {
font-size: 48rpx;
font-weight: 600;
color: #fff;
margin-bottom: 16rpx;
}
.subtitle {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.8);
}
.login-form {
width: 100%;
max-width: 600rpx;
}
.form-item {
margin-bottom: 40rpx;
position: relative;
}
.input {
width: 100%;
height: 88rpx;
background: rgba(255, 255, 255, 0.9);
border: none;
border-radius: 44rpx;
padding: 0 40rpx;
font-size: 32rpx;
color: #333;
}
.input::placeholder {
color: #999;
}
.password-toggle {
position: absolute;
right: 40rpx;
top: 50%;
transform: translateY(-50%);
padding: 20rpx;
}
.toggle-icon {
font-size: 32rpx;
color: #999;
}
.login-btn {
width: 100%;
height: 88rpx;
background: #1890ff;
color: #fff;
border: none;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
}
.login-btn.disabled {
background: #ccc;
color: #999;
}
.login-tips {
text-align: center;
margin-top: 40rpx;
}
.tips-text {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.8);
}
.login-footer {
position: absolute;
bottom: 40rpx;
text-align: center;
}
.footer-text {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
}

View File

@@ -0,0 +1,42 @@
// 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

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

View File

@@ -0,0 +1,61 @@
<!--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

@@ -0,0 +1,165 @@
/* 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

@@ -0,0 +1,88 @@
// pages/profile/profile.js
const auth = require('../../utils/auth.js')
Page({
data: {
userInfo: {},
menuItems: [
{
key: 'settings',
title: '设置',
icon: '⚙️',
path: ''
},
{
key: 'about',
title: '关于',
icon: '',
path: ''
},
{
key: 'help',
title: '帮助',
icon: '❓',
path: ''
},
{
key: 'feedback',
title: '反馈',
icon: '💬',
path: ''
}
]
},
onLoad() {
this.loadUserInfo()
},
loadUserInfo() {
const userInfo = auth.getUser()
this.setData({
userInfo: userInfo || {}
})
},
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
}
},
handleLogout() {
wx.showModal({
title: '确认退出',
content: '确定要退出登录吗?',
success: (res) => {
if (res.confirm) {
auth.logout()
}
}
})
}
})

View File

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

View File

@@ -0,0 +1,36 @@
<!--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="user-info">
<view class="username">{{userInfo.name || '管理员'}}</view>
<view class="user-role">{{userInfo.role || '系统管理员'}}</view>
<view class="user-phone">{{userInfo.phone || '13800138000'}}</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>
</view>
<!-- 退出登录 -->
<view class="logout-section">
<button class="logout-btn" bindtap="handleLogout">
退出登录
</button>
</view>
</view>

View File

@@ -0,0 +1,103 @@
/* pages/profile/profile.wxss */
.profile-container {
min-height: 100vh;
background: #f6f6f6;
}
.user-section {
background: #fff;
padding: 60rpx 30rpx;
margin-bottom: 20rpx;
display: flex;
align-items: center;
}
.user-avatar {
margin-right: 30rpx;
}
.avatar-img {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
background: #f0f0f0;
}
.user-info {
flex: 1;
}
.username {
font-size: 36rpx;
font-weight: 600;
color: #333;
margin-bottom: 12rpx;
}
.user-role {
font-size: 28rpx;
color: #666;
margin-bottom: 8rpx;
}
.user-phone {
font-size: 24rpx;
color: #999;
}
.menu-section {
background: #fff;
margin-bottom: 20rpx;
}
.menu-item {
display: flex;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
transition: background 0.3s;
}
.menu-item:last-child {
border-bottom: none;
}
.menu-item:active {
background: #f8f9fa;
}
.menu-icon {
font-size: 32rpx;
margin-right: 24rpx;
width: 40rpx;
text-align: center;
}
.menu-title {
flex: 1;
font-size: 30rpx;
color: #333;
}
.menu-arrow {
font-size: 24rpx;
color: #ccc;
}
.logout-section {
padding: 30rpx;
}
.logout-btn {
width: 100%;
height: 88rpx;
background: #ff4d4f;
color: #fff;
border: none;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
}

View File

@@ -0,0 +1,42 @@
// 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

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

View File

@@ -0,0 +1,57 @@
<!--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

@@ -0,0 +1,158 @@
/* 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,49 @@
// 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

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

View File

@@ -0,0 +1,58 @@
<!--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

@@ -0,0 +1,158 @@
/* 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

@@ -0,0 +1,42 @@
// 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

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

View File

@@ -0,0 +1,57 @@
<!--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

@@ -0,0 +1,158 @@
/* 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;
}