Prechádzať zdrojové kódy

feat: 首页重新写管理决策

Co-authored-by: Copilot <copilot@github.com>
liujt 3 mesiacov pred
rodič
commit
57ef943816
4 zmenil súbory, kde vykonal 1284 pridanie a 110 odobranie
  1. 9 0
      api/home/index.js
  2. 614 0
      pages/home/home copy.vue
  3. 135 110
      pages/home/home.vue
  4. 526 0
      pages/home/pages/manage/manage.vue

+ 9 - 0
api/home/index.js

@@ -78,3 +78,12 @@ export async function getHotProduct(params) {
 	return Promise.reject(new Error(data.message))
 }
 
+// 获取设备监测
+export async function getDeviceCount(params) {
+	const data = await get(Vue.prototype.apiUrl + `/main/asset/querySubstanceRunningMonitor`, params)
+	if (data.code == 0) {
+		return data.data
+	}
+	return Promise.reject(new Error(data.message))
+}
+

+ 614 - 0
pages/home/home copy.vue

@@ -0,0 +1,614 @@
+<template>
+	<view class="ss">
+		<!-- 头 -->
+		<uni-nav-bar fixed="true" statusBar="true" right-icon="scan" title="首页" @clickLeft="back"
+			@clickRight="HandlScanCode"></uni-nav-bar>
+		<!-- <uni-nav-bar fixed="true" statusBar="true" title="工作台"></uni-nav-bar> -->
+
+		<view class="container">
+			<navigation :workOrder="workOrder" ref="navigationRef"></navigation>
+			<!-- 设备主管 -->
+			<equipment v-if="roleId == 102" ref="equipmentRef"></equipment>
+			<!-- 运维 -->
+			<Mocha v-if="roleId == 157" ref="mochaRef"></Mocha>
+			<!-- 模具舟皿 -->
+			<mould v-if="roleId == 155" ref="mouldRef"></mould>
+			<!-- 备品备件 -->
+			<sparePart v-if="roleId == 150" ref="sparePartRef"></sparePart>
+
+			<view class="data-middle">
+				<view class="cell">
+					<view class="num">
+						{{homeData.totalFinishedProducts}}
+					</view>
+					<view class="label">
+						成品库存总量
+					</view>
+				</view>
+
+				<view class="cell">
+					<view class="num">
+						{{homeData.totalRawMaterials}}
+					</view>
+					<view class="label">
+						物料库存总量
+					</view>
+				</view>
+
+				<view class="cell">
+					<view class="num">
+						{{homeData.goodsCount}}
+					</view>
+					<view class="label">
+						商品列表
+					</view>
+				</view>
+
+				<view class="cell">
+					<view class="num">
+						{{homeData.yearAmount}}
+					</view>
+					<view class="label">
+						年度合同金额(万元)
+					</view>
+				</view>
+
+				<view class="cell">
+					<view class="num">
+						{{homeData.receivableAmount}}
+					</view>
+					<view class="label">
+						应收金额(万元)
+					</view>
+				</view>
+
+				<view class="cell">
+					<view class="num">
+						{{homeData.payableAmount}}
+					</view>
+					<view class="label">
+						应付金额(万元)
+					</view>
+				</view>
+			</view>
+
+			<view class="data-rank">
+				<view class="tab-top">
+					<view class="tab-item" v-for="(item,index) in tabList" :key="index">
+						<view :class="{active: activeIndex == item.index}" @click="handleTabChange(item)">
+							{{item.name}}
+						</view>
+					</view>
+				</view>
+
+
+				<!-- <uni-table class="table" ref="table" emptyText="暂无更多数据">
+					<uni-tr class="table-title">
+						<uni-th align="center" width="100">产品编码</uni-th>
+						<uni-th align="center">名称</uni-th>
+						<uni-th align="center" width="100">销量</uni-th>
+					</uni-tr>
+					<uni-tr v-for="(item, index) in tableData" :key="index">
+						<uni-td>{{index+1}}.{{item.productCode}}</uni-td>
+						<uni-td>{{item.productName}}</uni-td>
+						<uni-td align="center">{{item.totalSaleCount}}</uni-td>
+					</uni-tr>
+				</uni-table> -->
+
+				<view class="table-title" style="text-align: center;">
+					<view class="code">产品编码</view>
+					<view class="name">名称</view>
+					<view class="total">销量</view>
+				</view>
+				<view class="table-content" v-for="(item,index) in tableData">
+					<view class="code">
+						<view class="index">{{index+1}}.</view>
+						<view class="value">{{item.productCode}}</view>
+
+					</view>
+					<view class="name">{{item.productName}}</view>
+					<view class="total">{{item.totalSaleCount}}</view>
+				</view>
+
+
+
+			</view>
+
+
+
+
+
+			<view class="data-bottom">
+				<view class="cell">
+					<view class="num">
+						{{this.homeData.yearProduceAmount}}
+					</view>
+					<view class="label">
+						年度生产总量
+					</view>
+				</view>
+
+				<view class="cell">
+					<view class="num">
+						{{this.homeData.monthProduceAmount}}
+					</view>
+					<view class="label">
+						月生产总量
+					</view>
+				</view>
+
+				<view class="cell">
+					<view class="num">
+						{{homeData.pendingAmount}}
+					</view>
+					<view class="label">
+						待生产总量
+					</view>
+				</view>
+
+			</view>
+
+
+
+		</view>
+
+
+
+
+
+		<!-- <ScanCode ref="ScanCode" :model="'uni'"></ScanCode> -->
+	</view>
+</template>
+
+<script>
+	import navigation from './components/navigation.vue'
+	import equipment from './pages/equipment/equipment.vue'
+	import Mocha from './pages/Mocha/Mocha.vue'
+	import mould from './pages/mould/mould.vue'
+	import sparePart from './pages/sparePart/sparePart.vue'
+	import ScanCode from '@/components/ScanCode.vue'
+
+	import {
+		getDateNew
+	} from '@/utils/utils.js'
+	import {
+		postJ,
+		post,
+		get
+	} from '@/utils/api'
+	import {
+		statistics
+	} from '@/api/myTicket'
+	import {
+		getCount,
+		getYearAmount,
+		getPayableAmount,
+		getReceivableAmount,
+		getGoodsPage,
+		getProduceAmount,
+		// getPendingAmount,
+		getHotProduct
+	}
+	from '@/api/home'
+	export default {
+		components: {
+			navigation,
+			equipment,
+			Mocha,
+			mould,
+			sparePart,
+			ScanCode
+		},
+		onShow() {
+
+
+
+			this.init()
+			let _this = this
+			uni.$off('scancodedate') // 每次进来先 移除全局自定义事件监听器
+			uni.$on('scancodedate', function(data) {
+				console.log(data, '-----scancodedate')
+				_this.cbScancodedate(data)
+			})
+			// this.getStatistics()
+			// this.Scancodedate()
+		},
+		onLoad() {
+			this.getHomeData()
+			// this.getStatistics()
+		},
+		onUnload() {
+			uni.$off('scancodedate')
+		},
+		onHide() {
+			uni.$off('scancodedate')
+		},
+		data() {
+			return {
+				roleId: null,
+				timer: null,
+				qrContent: null,
+				barType: null,
+				// 扫码后的设备信息
+				equipmentInfo: '',
+				qrContent: '',
+				barType: '',
+				workOrder: {
+					maintenanceNum: 0,
+					patrolInspection: 0,
+					quantityNum: 0,
+					repairsNum: 0,
+					total: 0
+				}, // 工单统计数据
+				activeIndex: 0,
+				tabList: [{
+					name: '热销品排行榜',
+					index: 0,
+				}, {
+					name: '滞销品排行榜',
+					index: 1,
+				}],
+				tableData: [],
+				homeData: {
+					totalFinishedProducts: '',
+					totalRawMaterials: '',
+					goodsCount: '',
+					yearAmount: '',
+					receivableAmount: '',
+					payableAmount: '',
+					yearProduceAmount: '',
+					monthProduceAmount: '',
+					pendingAmount: ''
+				},
+				hotProductList: [],
+
+			}
+		},
+		created() {
+			uni.getStorage({
+				key: 'userInfo',
+				success: res => {
+					// console.log(res);
+					this.roleId = res.data.roleId[0]
+				}
+			})
+		},
+		onHide() {
+			clearTimeout(this.timer);
+		},
+		onUnload() {
+			clearTimeout(this.timer);
+		},
+
+
+		methods: {
+			init() {
+				this.getStatistics()
+				this.$refs.navigationRef && this.$refs.navigationRef.getCount()
+				this.$refs.equipmentRef && this.$refs.equipmentRef.getData()
+				this.$refs.mochaRef && this.$refs.mochaRef.getData()
+				this.$refs.mouldRef &&
+					(this.$refs.mouldRef.getBoatInventoryCountList(),
+						this.$refs.mouldRef.getModeInventoryCountList())
+				this.$refs.sparePartRef && this.$refs.sparePartRef.getData()
+				this.timer = setTimeout(() => {
+					this.init()
+				}, 12000)
+			},
+			// 获取工单统计数
+			getStatistics() {
+				statistics().then(res => {
+					this.workOrder = res
+				})
+			},
+
+			// 相机扫码
+			HandlScanCode() {
+				uni.scanCode({
+					success: (res) => {
+						this.Scancodedate(res.result)
+					}
+				})
+				//_this.Scancodedate('res')
+			},
+			// 根据条码请求设备数据
+			getData() {
+				let par = {
+					barType: this.barType,
+					qrContent: this.qrContent
+				}
+				this.equipmentInfo = {}
+				return postJ(this.apiUrl + '/scan/getAssetInfo', par)
+					.then(res => {
+						console.log(res.data)
+						this.equipmentInfo = res.data
+					})
+					.catch(err => {
+						console.log(err, 'err-----------------')
+						uni.showToast({
+							title: '系统错误!',
+							icon: 'none',
+							duration: 2000
+						})
+					})
+			},
+			// 扫码枪扫码
+			cbScancodedate(data) {
+				this.Scancodedate(data.code)
+			},
+			async Scancodedate(code) {
+				this.qrContent = code.trim()
+				// this.qrContent = 'w01000002100001@_@0' //code.trim()
+				//this.qrContent = '005/w01000001120041/锤锤专用02-不拆包-批量/规格01//成型/成型工序@_@0'
+				this.barType = this.setBarType(this.qrContent)
+				await this.getData()
+
+				if (this.equipmentInfo.assetCode) {
+					if (this.equipmentInfo.assetType != 1) {
+						uni.showToast({
+							title: '请扫描生产设备码!',
+							icon: 'none',
+							duration: 2000
+						})
+
+						return
+					}
+					let par = {
+						info: encodeURIComponent(JSON.stringify(this.equipmentInfo)),
+						qrContent: this.qrContent
+					}
+					par = this.URLSearchParams(par)
+					console.log('qrwewett')
+					uni.navigateTo({
+						url: '/pages/equipment_ledger/index?' + par
+					})
+				}
+			},
+			// 设置barType
+			setBarType(val) {
+				let index = val.indexOf('@_@')
+				let result = 0
+				if (index !== -1) {
+					let item = val.substr(index + 3, 1)
+					if (item) {
+						result = Number(item)
+					}
+				}
+				return result
+			},
+			// 切换排行版tab
+			handleTabChange(item) {
+				this.activeIndex = item.index
+				this.tableData
+				// console.log(item);
+			},
+			async getHomeData() {
+				uni.showLoading({
+					title: '加载中'
+				})
+
+				const res1 = await getCount()
+				const res2 = await getGoodsPage()
+				const res3 = await getYearAmount()
+				const res4 = await getReceivableAmount()
+				const res5 = await getPayableAmount()
+				// 年度
+				const res6 = await getProduceAmount({
+					createTimeStart: getDateNew('year') + '-01',
+					createTimeEnd: getDateNew('year') + '-12',
+					factoryId: null
+				})
+				// 本月
+				const res7 = await getProduceAmount({
+					createTimeStart:  getDateNew('month'),
+					createTimeEnd:  getDateNew('month'),
+					factoryId: null
+				})
+
+
+				const res9 = await getHotProduct()
+
+
+				this.homeData.totalFinishedProducts = res1?.totalFinishedProducts ?? 0
+				this.homeData.totalRawMaterials = res1?.totalRawMaterials ?? 0
+				this.homeData.goodsCount = res2?.count ?? 0
+				this.homeData.yearAmount = (res3 / 10000).toFixed(2)
+				this.homeData.receivableAmount = res4 ?? 0
+				this.homeData.payableAmount = res5 ?? 0
+				this.homeData.yearProduceAmount = res6?.reduce((pre, cur) => {
+					return pre + cur?.formedNum
+				}, 0) ?? 0
+
+				this.homeData.monthProduceAmount = res7?.reduce((pre, cur) => {
+					return pre + cur?.formedNum
+				}, 0) ?? 0
+				this.homeData.pendingAmount = res6?.reduce((pre, cur) => {
+					return pre + cur?.pendingProductionCount
+				}, 0) ?? 0
+				this.hotProductList = res9
+				this.tableData = this.hotProductList.slice(0, 4)
+				uni.hideLoading()
+
+			}
+
+		}
+	}
+</script>
+
+<style>
+	page {
+		background-color: #f0f0f0;
+	}
+</style>
+
+<style lang="scss" scoped>
+	// /deep/.uni-page-wrapper{
+	// 	background-color: pink;
+	// }
+
+	/deep/.uni-table {
+		min-width: auto !important;
+		border-radius: 0;
+	}
+
+	page {
+		background-color: #f0f0f0;
+	}
+
+	.container {
+		background-color: $page-bg;
+		padding: 20rpx;
+
+		.data-middle {
+			display: flex;
+			flex-wrap: wrap;
+			justify-content: space-between;
+
+			.cell {
+				display: flex;
+				width: 30%;
+				padding: 20rpx 0;
+				margin-bottom: 20rpx;
+				flex-direction: column;
+				background-color: rgb(22, 146, 50);
+				justify-content: center;
+				align-items: center;
+				border-radius: 10rpx;
+				color: #fff;
+
+				.num {
+					font-size: 28rpx;
+				}
+
+				.label {
+					font-size: 20rpx;
+				}
+			}
+
+		}
+
+		.data-rank {
+			width: 100%;
+			padding: 10rpx 20rpx 0 20rpx;
+			margin-bottom: 20rpx;
+			background-color: #fff;
+			border-radius: 20rpx 20rpx 0 0rpx;
+
+			.tab-top {
+				display: flex;
+				margin-bottom: 20rpx;
+
+				.tab-item {
+					margin-right: 20rpx;
+
+					.active {
+						border-bottom: 2px solid rgb(22, 146, 50);
+					}
+				}
+			}
+
+
+			.table-title {
+				width: 100%;
+				display: flex;
+				color: #333;
+				flex-wrap: nowrap;
+				justify-content: space-between;
+
+				.code {
+					width: 40%;
+					word-break: normal
+				}
+
+				.name {
+					width: 40%;
+					word-break: normal
+				}
+
+				.total {
+					width: 15%;
+					word-break: normal
+				}
+
+			}
+
+			.table-content {
+				font-size: 28rpx;
+				display: flex;
+				padding: 10rpx 0;
+				width: 100%;
+				color: #333;
+				flex-wrap: nowrap;
+				justify-content: space-between;
+				border-bottom: 1px solid #eee;
+
+
+				.code {
+					display: flex;
+					width: 40%;
+
+					.index {
+						width: 20rpx;
+						margin-right: 20rpx;
+					}
+
+					.value {
+						word-break: break-all;
+						flex: 1;
+					}
+				}
+
+				.name {
+					width: 40%;
+					white-space: nowarp;
+					over-flow: hidden;
+					text-overflow: ellipsip;
+					word-break: break-all;
+				}
+
+				.total {
+					text-align: center;
+					width: 15%;
+					white-space: nowarp;
+					over-flow: hidden;
+					text-overflow: ellipsip;
+					word-break: break-all;
+				}
+			}
+
+		}
+
+		.data-rank .table-content:last-child {
+			border-bottom: none;
+		}
+
+		.data-bottom {
+			display: flex;
+			flex-wrap: wrap;
+			justify-content: space-between;
+
+			.cell {
+				display: flex;
+				width: 30%;
+				padding: 20rpx 0;
+				margin-bottom: 20rpx;
+				flex-direction: column;
+				background-color: #fff;
+				justify-content: center;
+				align-items: center;
+				border-radius: 10rpx;
+				color: #000;
+
+				.num {
+					font-size: 28rpx;
+				}
+
+				.label {
+
+					font-size: 20rpx;
+				}
+			}
+		}
+
+	}
+</style>

+ 135 - 110
pages/home/home.vue

@@ -3,7 +3,6 @@
 		<!-- 头 -->
 		<uni-nav-bar fixed="true" statusBar="true" right-icon="scan" title="首页" @clickLeft="back"
 			@clickRight="HandlScanCode"></uni-nav-bar>
-		<!-- <uni-nav-bar fixed="true" statusBar="true" title="工作台"></uni-nav-bar> -->
 
 		<view class="container">
 			<navigation :workOrder="workOrder" ref="navigationRef"></navigation>
@@ -18,145 +17,79 @@
 
 			<view class="data-middle">
 				<view class="cell">
-					<view class="num">
-						{{homeData.totalFinishedProducts}}
-					</view>
 					<view class="label">
 						成品库存总量
 					</view>
+					<view class="num">
+						{{homeData.totalFinishedProducts}}
+					</view>
 				</view>
 
 				<view class="cell">
-					<view class="num">
-						{{homeData.totalRawMaterials}}
-					</view>
 					<view class="label">
 						物料库存总量
 					</view>
-				</view>
-
-				<view class="cell">
 					<view class="num">
-						{{homeData.goodsCount}}
-					</view>
-					<view class="label">
-						商品列表
+						{{homeData.totalRawMaterials}}
 					</view>
 				</view>
 
 				<view class="cell">
-					<view class="num">
-						{{homeData.yearAmount}}
-					</view>
 					<view class="label">
-						年度合同金额(万元)
+						设备监测
 					</view>
-				</view>
-
-				<view class="cell">
 					<view class="num">
-						{{homeData.receivableAmount}}
+						<text style="font-size: 22rpx;">在线:</text>{{homeData.deviceOnLineCount}} 
 					</view>
-					<view class="label">
-						应收金额(万元)
+					<view class="num">
+						<text style="font-size: 22rpx;">离线:</text>{{ homeData.deviceOffLineCount }}
 					</view>
 				</view>
 
 				<view class="cell">
-					<view class="num">
-						{{homeData.payableAmount}}
-					</view>
 					<view class="label">
-						应付金额(万元)
+						年度合同额(万元)
 					</view>
-				</view>
-			</view>
-
-			<view class="data-rank">
-				<view class="tab-top">
-					<view class="tab-item" v-for="(item,index) in tabList" :key="index">
-						<view :class="{active: activeIndex == item.index}" @click="handleTabChange(item)">
-							{{item.name}}
-						</view>
-					</view>
-				</view>
-
-
-				<!-- <uni-table class="table" ref="table" emptyText="暂无更多数据">
-					<uni-tr class="table-title">
-						<uni-th align="center" width="100">产品编码</uni-th>
-						<uni-th align="center">名称</uni-th>
-						<uni-th align="center" width="100">销量</uni-th>
-					</uni-tr>
-					<uni-tr v-for="(item, index) in tableData" :key="index">
-						<uni-td>{{index+1}}.{{item.productCode}}</uni-td>
-						<uni-td>{{item.productName}}</uni-td>
-						<uni-td align="center">{{item.totalSaleCount}}</uni-td>
-					</uni-tr>
-				</uni-table> -->
-
-				<view class="table-title" style="text-align: center;">
-					<view class="code">产品编码</view>
-					<view class="name">名称</view>
-					<view class="total">销量</view>
-				</view>
-				<view class="table-content" v-for="(item,index) in tableData">
-					<view class="code">
-						<view class="index">{{index+1}}.</view>
-						<view class="value">{{item.productCode}}</view>
-
+					<view class="num">
+						{{homeData.yearAmount}}
 					</view>
-					<view class="name">{{item.productName}}</view>
-					<view class="total">{{item.totalSaleCount}}</view>
 				</view>
 
-
-
-			</view>
-
-
-
-
-
-			<view class="data-bottom">
 				<view class="cell">
-					<view class="num">
-						{{this.homeData.yearProduceAmount}}
-					</view>
 					<view class="label">
-						年度生产总量
+						年度应收款(万元)
 					</view>
-				</view>
-
-				<view class="cell">
 					<view class="num">
-						{{this.homeData.monthProduceAmount}}
-					</view>
-					<view class="label">
-						月生产总量
+						{{homeData.receivableAmount}}
 					</view>
 				</view>
 
 				<view class="cell">
-					<view class="num">
-						{{homeData.pendingAmount}}
-					</view>
 					<view class="label">
-						待生产总量
+						年度应付款(万元)
+					</view>
+					<view class="num">
+						{{homeData.payableAmount}}
 					</view>
 				</view>
-
+				
 			</view>
 
+			<!-- 通知公告 -->
+			<view class="notice-bar" v-if="noticeList.length > 0">
+				<u-icon name="volume" size="30rpx" color="#ff9900"></u-icon>
+				<swiper class="notice-swiper" vertical autoplay circular :interval="3000">
+					<swiper-item v-for="(item, index) in noticeList" :key="index">
+					<view class="notice-content">
+						<text class="notice-text">{{ item.title }}</text>
+						<text class="notice-date">{{ item.date }}</text>
+					</view>
+					</swiper-item>
+				</swiper>
+			</view>
 
-
+			<manage ref="manageRef"></manage>
 		</view>
-
-
-
-
-
-		<!-- <ScanCode ref="ScanCode" :model="'uni'"></ScanCode> -->
 	</view>
 </template>
 
@@ -167,6 +100,7 @@
 	import mould from './pages/mould/mould.vue'
 	import sparePart from './pages/sparePart/sparePart.vue'
 	import ScanCode from '@/components/ScanCode.vue'
+	import manage from './pages/manage/manage.vue'
 
 	import {
 		getDateNew
@@ -187,7 +121,8 @@
 		getGoodsPage,
 		getProduceAmount,
 		// getPendingAmount,
-		getHotProduct
+		getHotProduct,
+		getDeviceCount
 	}
 	from '@/api/home'
 	export default {
@@ -197,12 +132,10 @@
 			Mocha,
 			mould,
 			sparePart,
-			ScanCode
+			ScanCode,
+			manage
 		},
 		onShow() {
-
-
-
 			this.init()
 			let _this = this
 			uni.$off('scancodedate') // 每次进来先 移除全局自定义事件监听器
@@ -213,8 +146,14 @@
 			// this.getStatistics()
 			// this.Scancodedate()
 		},
-		onLoad() {
+		onReady() {
 			this.getHomeData()
+			setTimeout(() => {
+				this.$refs.manageRef?.initCharts()
+			}, 300)
+		},
+		onLoad() {
+			// this.getHomeData()
 			// this.getStatistics()
 		},
 		onUnload() {
@@ -225,6 +164,10 @@
 		},
 		data() {
 			return {
+				noticeList: [
+					{ title: '关于企业实行数字化管理安全办法的通知', date: '2026-3-30' },
+					{ title: '关于五一劳动节放假安排的通知', date: '2026-4-25' }
+				],
 				roleId: null,
 				timer: null,
 				qrContent: null,
@@ -258,7 +201,9 @@
 					payableAmount: '',
 					yearProduceAmount: '',
 					monthProduceAmount: '',
-					pendingAmount: ''
+					pendingAmount: '',
+					deviceOnLineCount: '',
+					deviceOffLineCount: '',
 				},
 				hotProductList: [],
 
@@ -408,6 +353,41 @@
 
 				const res9 = await getHotProduct()
 
+				const res10 = await getDeviceCount({
+					size: 9999
+				})
+
+				let deviceData = res10.list.map((item) => {
+					let iotList = [];
+					if (item.iotPointDataList) {
+					item.iotPointDataList.forEach((element) => {
+						let data = item.iotModel.properties.find(
+						(iotModel) => iotModel.identifier == element.identifier
+						);
+						if (data) {
+						iotList.push({
+							...element,
+							dataType: data.dataType
+						});
+						}
+					});
+					}
+
+					item['iotList'] = item.iotDashboardPoint.length
+					? iotList.filter((iotListItem) =>
+						item.iotDashboardPoint.find(
+							(Point) =>
+							Point.identifier == iotListItem.identifier &&
+							Point.checked1
+						)
+						)
+					: iotList.filter((iotListItem, index) => index < 4);
+					return item;
+				});
+
+				this.homeData.deviceOnLineCount = deviceData.filter((item) => item.iotList.length > 0).length
+
+				this.homeData.deviceOffLineCount = deviceData.filter((item) => item.iotList.length == 0).length
 
 				this.homeData.totalFinishedProducts = res1?.totalFinishedProducts ?? 0
 				this.homeData.totalRawMaterials = res1?.totalRawMaterials ?? 0
@@ -466,27 +446,72 @@
 
 			.cell {
 				display: flex;
-				width: 30%;
+				width: 32%;
 				padding: 20rpx 0;
-				margin-bottom: 20rpx;
+				margin-bottom: 16rpx;
 				flex-direction: column;
 				background-color: rgb(22, 146, 50);
 				justify-content: center;
 				align-items: center;
-				border-radius: 10rpx;
+				border-radius: 8rpx;
 				color: #fff;
-
+				min-height: 164rpx;
 				.num {
 					font-size: 28rpx;
 				}
 
 				.label {
-					font-size: 20rpx;
+					font-size: 24rpx;
+					margin-bottom: 10rpx;
 				}
 			}
 
 		}
 
+		/* 通知公告 */
+		.notice-bar {
+			display: flex;
+			align-items: center;
+			padding: 18rpx 20rpx;
+			background: #fff;
+			margin-bottom: 16rpx;
+
+			.notice-swiper {
+				flex: 1;
+				height: 40rpx;
+				margin-left: 12rpx;
+
+				swiper-item {
+				height: 40rpx;
+				display: flex;
+				align-items: center;
+				}
+
+				.notice-content {
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				width: 100%;
+				}
+
+				.notice-text {
+				font-size: 24rpx;
+				color: #333;
+				flex: 1;
+				overflow: hidden;
+				text-overflow: ellipsis;
+				white-space: nowrap;
+				}
+
+				.notice-date {
+				font-size: 22rpx;
+				color: #999;
+				margin-left: 16rpx;
+				flex-shrink: 0;
+				}
+			}
+		}
+
 		.data-rank {
 			width: 100%;
 			padding: 10rpx 20rpx 0 20rpx;

+ 526 - 0
pages/home/pages/manage/manage.vue

@@ -0,0 +1,526 @@
+<template>
+  <view class="manage-container">
+    <!-- 营销数据 -->
+    <view class="section">
+      <view class="section-header">
+        <view class="section-title">营销数据</view>
+      </view>
+      <view class="section-content">
+        <view class="data-cards">
+          <view class="data-card orange">
+            <view class="data-header">本年度</view>
+            <view class="data-divider"></view>
+            <view class="data-row">
+              <text class="data-label">订单数量</text>
+              <text class="data-value">{{ marketing.yearOrderCount }}</text>
+            </view>
+            <view class="data-divider"></view>
+            <view class="data-row">
+              <text class="data-label">订单金额</text>
+              <text class="data-value">{{ marketing.yearOrderAmount }}<text class="data-unit">万元</text></text>
+            </view>
+          </view>
+          <view class="data-card orange">
+            <view class="data-header">本月</view>
+            <view class="data-divider"></view>
+            <view class="data-row">
+              <text class="data-label">订单数量</text>
+              <text class="data-value">{{ marketing.monthOrderCount }}</text>
+            </view>
+            <view class="data-divider"></view>
+            <view class="data-row">
+              <text class="data-label">订单金额</text>
+              <text class="data-value">{{ marketing.monthOrderAmount }}<text class="data-unit">万元</text></text>
+            </view>
+          </view>
+        </view>
+        <view class="chart-wrapper">
+          <view class="chart-box">
+             <view id="marketingChart" class="chart"></view>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <!-- 生产数据 -->
+    <view class="section">
+      <view class="section-header">
+        <view class="section-title">生产数据</view>
+      </view>
+      <view class="section-content">
+        <view class="data-cards">
+          <view class="data-card orange">
+            <view class="data-header">本年度</view>
+            <view class="data-row">
+              <text class="data-label">生产工单</text>
+              <text class="data-value">{{ production.yearWorkOrder }}</text>
+            </view>
+            <view class="data-divider"></view>
+            <view class="data-row">
+              <text class="data-label">生产总量</text>
+              <text class="data-value">{{ production.yearTotal }}<text class="data-unit">件</text></text>
+            </view>
+          </view>
+          <view class="data-card orange">
+            <view class="data-header">本月</view>
+            <view class="data-row">
+              <text class="data-label">生产工单</text>
+              <text class="data-value">{{ production.monthWorkOrder }}</text>
+            </view>
+            <view class="data-divider"></view>
+            <view class="data-row">
+              <text class="data-label">生产总量</text>
+              <text class="data-value">{{ production.monthTotal }}<text class="data-unit">件</text></text>
+            </view>
+          </view>
+        </view>
+        <view class="chart-wrapper">
+          <view class="chart-box">
+            <!-- <canvas canvas-id="productionChart" type="2d" class="chart"></canvas> -->
+             <view id="productionChart" class="chart"></view>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <!-- 采购数据 -->
+    <view class="section">
+      <view class="section-header">
+        <view class="section-title">采购数据</view>
+      </view>
+      <view class="section-content">
+        <view class="data-cards">
+          <view class="data-card orange">
+            <view class="data-header">本年度</view>
+            <view class="data-divider"></view>
+            <view class="data-row">
+              <text class="data-label">采购数量</text>
+              <text class="data-value">{{ purchase.yearCount }}</text>
+            </view>
+            <view class="data-divider"></view>
+            <view class="data-row">
+              <text class="data-label">采购金额</text>
+              <text class="data-value">{{ purchase.yearAmount }}<text class="data-unit">万元</text></text>
+            </view>
+          </view>
+          <view class="data-card orange">
+            <view class="data-header">本月</view>
+            <view class="data-divider"></view>
+            <view class="data-row">
+              <text class="data-label">采购数量</text>
+              <text class="data-value">{{ purchase.monthCount }}</text>
+            </view>
+            <view class="data-divider"></view>
+            <view class="data-row">
+              <text class="data-label">采购金额</text>
+              <text class="data-value">{{ purchase.monthAmount }}<text class="data-unit">万元</text></text>
+            </view>
+          </view>
+        </view>
+        <view class="chart-wrapper">
+          <view class="chart-title">采购走势</view>
+          <view class="chart-box">
+            <!-- <canvas canvas-id="purchaseChart" type="2d" class="chart"></canvas> -->
+             <view id="purchaseChart" class="chart"></view>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <!-- 仓储数据 -->
+    <view class="section">
+      <view class="section-header">
+        <view class="section-title">仓储数据</view>
+      </view>
+      <view class="section-content">
+        <view class="data-cards">
+          <view class="data-card orange">
+            <view class="data-header">年度出入库</view>
+            <view class="data-divider"></view>
+            <view class="data-row small">
+              <text class="data-label">采购入库</text>
+              <text class="data-value">{{ warehouse.yearPurchaseIn }}</text>
+            </view>
+            <view class="data-divider"></view>
+            <view class="data-row small">
+              <text class="data-label">领料出库</text>
+              <text class="data-value">{{ warehouse.yearMaterialOut }}</text>
+            </view>
+            <view class="data-divider"></view>
+            <view class="data-row small">
+              <text class="data-label">生产入库</text>
+              <text class="data-value">{{ warehouse.yearProduceIn }}</text>
+            </view>
+            <view class="data-divider"></view>
+            <view class="data-row small">
+              <text class="data-label">发货出库</text>
+              <text class="data-value">{{ warehouse.yearDeliveryOut }}</text>
+            </view>
+          </view>
+          <view class="data-card orange">
+            <view class="data-header">本月出入库</view>
+            <view class="data-divider"></view>
+            <view class="data-row small">
+              <text class="data-label">采购入库</text>
+              <text class="data-value">{{ warehouse.monthPurchaseIn }}</text>
+            </view>
+            <view class="data-divider"></view>
+            <view class="data-row small">
+              <text class="data-label">领料出库</text>
+              <text class="data-value">{{ warehouse.monthMaterialOut }}</text>
+            </view>
+            <view class="data-divider"></view>
+            <view class="data-row small">
+              <text class="data-label">生产入库</text>
+              <text class="data-value">{{ warehouse.monthProduceIn }}</text>
+            </view>
+            <view class="data-divider"></view>
+            <view class="data-row small">
+              <text class="data-label">发货出库</text>
+              <text class="data-value">{{ warehouse.monthDeliveryOut }}</text>
+            </view>
+          </view>
+        </view>
+        <view class="chart-wrapper">
+          <view class="chart-title">物料走势</view>
+          <view class="chart-box">
+            <!-- <canvas canvas-id="materialChart" type="2d" class="chart"></canvas> -->
+             <view id="materialChart" class="chart"></view>
+          </view>
+        </view>
+        <view class="chart-wrapper">
+          <view class="chart-title">成品走势</view>
+          <view class="chart-box">
+            <!-- <canvas canvas-id="finishedChart" type="2d" class="chart"></canvas> -->
+             <view id="finishedChart" class="chart"></view>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <view class="bottom-safe"></view>
+  </view>
+</template>
+
+<script>
+import * as echarts from 'echarts'
+let chartInstances = {}
+
+export default {
+  data() {
+    return {
+      marketing: {
+        yearOrderCount: 23,
+        yearOrderAmount: 98,
+        monthOrderCount: 23,
+        monthOrderAmount: 98
+      },
+      production: {
+        yearWorkOrder: 23,
+        yearTotal: 98,
+        monthWorkOrder: 23,
+        monthTotal: 98
+      },
+      purchase: {
+        yearCount: 23,
+        yearAmount: 98,
+        monthCount: 23,
+        monthAmount: 98
+      },
+      warehouse: {
+        yearPurchaseIn: 23,
+        yearMaterialOut: 23,
+        yearProduceIn: 98,
+        yearDeliveryOut: 98,
+        monthPurchaseIn: 23,
+        monthMaterialOut: 23,
+        monthProduceIn: 98,
+        monthDeliveryOut: 98
+      },
+      chartData: {
+        marketingChart: {
+          categories: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'],
+          // 混合图表:柱状图(订单量) + 折线图(订单金额)
+          series: [
+            { name: '月订单量', data: [15, 20, 25, 30, 45, 50, 48, 40, 35, 42, 48, 52], type: 'bar' },
+            { name: '去年同期订单量', data: [12, 18, 22, 28, 40, 45, 42, 38, 32, 38, 45, 48], type: 'bar' },
+            { name: '月订单金额', data: [8.5, 10.2, 12.5, 15.0, 22.5, 25.0, 24.0, 20.0, 17.5, 21.0, 24.0, 26.0], type: 'line' },
+            { name: '去年同期订单金额', data: [6.0, 9.0, 11.0, 14.0, 20.0, 22.5, 21.0, 19.0, 16.0, 19.0, 22.5, 24.0], type: 'line' }
+          ]
+        },
+        productionChart: {
+          categories: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'],
+          series: [
+            { name: '生产工单', data: [18, 22, 28, 35, 42, 48, 45, 40, 38, 45, 50, 55], type: 'bar' },
+            { name: '去年同期工单', data: [15, 20, 25, 30, 38, 42, 40, 35, 32, 38, 45, 48], type: 'bar' },
+            { name: '生产总量', data: [180, 220, 280, 350, 420, 480, 450, 400, 380, 450, 500, 550], type: 'line' },
+            { name: '去年同期总量', data: [150, 200, 250, 300, 380, 420, 400, 350, 320, 380, 450, 480], type: 'line' }
+          ]
+        },
+        purchaseChart: {
+          categories: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'],
+          series: [
+            { name: '采购数量', data: [20, 25, 30, 38, 45, 52, 48, 42, 38, 45, 52, 58], type: 'bar' },
+            { name: '去年同期数量', data: [18, 22, 28, 35, 42, 48, 45, 38, 35, 42, 48, 52], type: 'bar' },
+            { name: '采购金额', data: [10.5, 12.5, 15.0, 19.0, 22.5, 26.0, 24.0, 21.0, 19.0, 22.5, 26.0, 29.0], type: 'line' },
+            { name: '去年同期金额', data: [9.0, 11.0, 14.0, 17.5, 21.0, 24.0, 22.5, 19.0, 17.5, 21.0, 24.0, 26.0], type: 'line' }
+          ]
+        },
+        materialChart: {
+          categories: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'],
+          series: [
+            { name: '采购入库', data: [22, 28, 35, 42, 50, 58, 52, 45, 42, 50, 58, 65], type: 'bar' },
+            { name: '去年同期入库', data: [18, 22, 28, 35, 42, 48, 45, 38, 35, 42, 48, 52], type: 'bar' },
+            { name: '领料出库', data: [20, 25, 32, 38, 45, 52, 48, 42, 38, 45, 52, 58], type: 'line' },
+            { name: '去年同期出库', data: [15, 20, 25, 32, 38, 45, 42, 38, 35, 42, 48, 52], type: 'line' }
+          ]
+        },
+        finishedChart: {
+          categories: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'],
+          series: [
+            { name: '生产入库', data: [18, 22, 28, 35, 42, 48, 45, 40, 38, 45, 50, 55], type: 'bar' },
+            { name: '去年同期入库', data: [15, 20, 25, 32, 38, 45, 42, 38, 35, 42, 48, 52], type: 'bar' },
+            { name: '发货出库', data: [15, 20, 25, 32, 38, 45, 42, 38, 35, 42, 48, 52], type: 'line' },
+            { name: '去年同期发货', data: [12, 18, 22, 28, 35, 42, 40, 35, 32, 38, 45, 48], type: 'line' }
+          ]
+        }
+      }
+    }
+  },
+  onReady() {
+    console.log('onReady11111111111111111111111111111111111111')
+    this.$nextTick(() => {
+      console.log('onReady - initCharts')
+      this.initCharts()
+    })
+  },
+  onLoad() {
+    console.log('onLoad22222222222222222222222222222222222222')
+  },
+  onUnload() {
+    Object.keys(chartInstances).forEach(key => {
+      chartInstances[key]?.dispose()
+    })
+  },
+  methods: {
+    initCharts() {
+      console.log('initCharts')
+      Object.keys(this.chartData).forEach(chartId => {
+        this.initChart(chartId, this.chartData[chartId])
+      })
+    },
+    initChart(domId, data) {
+      const query = uni.createSelectorQuery().in(this)
+      query.select(`#${domId}`).boundingClientRect()
+      query.exec(res => {
+        if (!res[0]) {
+          console.error(`Element #${domId} not found`)
+          return
+        }
+        const width = res[0].width || 355
+        const height = res[0].height || 200
+        console.log('init chart:', domId, width, height)
+
+        const chart = echarts.init(document.getElementById(domId))
+        chart.resize({ width, height })
+
+        const option = {
+          grid: {
+            left: 35,
+            right: 15,
+            top: 35,
+            bottom: 30
+          },
+          tooltip: {
+            show: true,
+            trigger: 'axis',
+            axisPointer: {
+              type: 'cross',
+              label: {
+                backgroundColor: '#6a7985'
+              }
+            },
+            backgroundColor: 'rgba(50, 50, 50, 0.9)',
+            borderColor: '#333',
+            textStyle: {
+              color: '#fff',
+              fontSize: 12
+            }
+          },
+          xAxis: {
+            type: 'category',
+            data: data.categories,
+            axisLine: { lineStyle: { color: '#e5e5e5' } },
+            axisLabel: { fontSize: 10, color: '#666' },
+            axisTick: { show: false }
+          },
+          yAxis: {
+            type: 'value',
+            min: 0,
+            splitNumber: 4,
+            axisLine: { show: false },
+            axisTick: { show: false },
+            splitLine: {
+              lineStyle: { color: '#e5e5e5', type: 'dashed' }
+            },
+            axisLabel: { fontSize: 10, color: '#666' }
+          },
+          series: data.series.map((item, index) => {
+            const chartType = item.type || 'line'
+            const barColors = ['#34c759', '#8bc34a', '#00bcd4', '#9c27b0']
+            const lineColors = ['#ff9500', '#faad14', '#e91e63', '#9c27b0']
+
+            if (chartType === 'bar') {
+              return {
+                name: item.name,
+                type: 'bar',
+                barWidth: 10,
+                itemStyle: {
+                  color: barColors[index % barColors.length]
+                },
+                data: item.data
+              }
+            } else {
+              return {
+                name: item.name,
+                type: 'line',
+                smooth: true,
+                symbol: 'circle',
+                symbolSize: 4,
+                lineStyle: {
+                  width: 2,
+                  color: lineColors[index % lineColors.length]
+                },
+                itemStyle: {
+                  color: lineColors[index % lineColors.length]
+                },
+                data: item.data
+              }
+            }
+          })
+        }
+
+        chart.setOption(option)
+        chartInstances[domId] = chart
+      })
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.manage-container {
+  min-height: 100vh;
+  // background: #f0f0f0;
+  padding-bottom: constant(safe-area-inset-bottom);
+  padding-bottom: env(safe-area-inset-bottom);
+}
+
+/* 数据区块 */
+.section {
+  margin-top: 2rpx;
+  padding-bottom: 8rpx;
+
+  .section-header {
+    padding: 24rpx 0 24rpx;
+
+    .section-title {
+      font-size: 30rpx;
+      font-weight: 600;
+      color: #333;
+      padding-left: 12rpx;
+      border-left: 6rpx solid #34c759;
+    }
+  }
+  .section-content {
+    background: #fff;
+  }
+}
+
+/* 数据卡片 */
+.data-cards {
+  display: grid;
+  grid-template-columns: repeat(2, 1fr);
+  gap: 16rpx;
+  padding: 20rpx;
+  border-radius: 4rpx;
+
+  .data-card {
+    border-radius: 12rpx;
+    overflow: hidden;
+    background: linear-gradient(135deg, #ff9500 0%, #ff6b00 100%);
+    box-shadow: 0 4rpx 16rpx rgba(255, 149, 0, 0.25);
+    padding: 20rpx;
+
+    .data-header {
+      font-size: 26rpx;
+      font-weight: 600;
+      color: #fff;
+      margin-bottom: 6rpx;
+    }
+
+    .data-divider {
+      height: 1rpx;
+      background: rgba(255, 255, 255, 0.2);
+    }
+
+    .data-row {
+      display: flex;
+      align-items: baseline;
+      justify-content: space-between;
+      padding: 8rpx 0;
+
+      &.small {
+        padding: 6rpx 0;
+      }
+
+      .data-label {
+        font-size: 24rpx;
+        color: rgba(255, 255, 255, 0.9);
+      }
+
+      .data-value {
+        font-size: 34rpx;
+        font-weight: 700;
+        color: #fff;
+      }
+
+      .data-unit {
+        font-size: 22rpx;
+        margin-left: 4rpx;
+        opacity: 0.85;
+      }
+    }
+  }
+}
+
+/* 图表 */
+.chart-wrapper {
+  padding: 16rpx 20rpx 8rpx;
+
+  .chart-title {
+    font-size: 26rpx;
+    color: #333;
+    margin-bottom: 10rpx;
+    font-weight: 500;
+  }
+
+  .chart-box {
+    // background: #f7f7f7;
+    border-radius: 10rpx;
+    overflow: hidden;
+  }
+
+  .chart {
+    width: 100%;
+    height: 280px;
+    min-height: 200px;
+    display: block;
+  }
+}
+
+.bottom-safe {
+  height: constant(safe-area-inset-bottom);
+  height: env(safe-area-inset-bottom);
+}
+</style>