index.vue 8.77 KB
<template>
	<view class="home-page">
		<!-- 自定义头部 -->
		<view class="custom-header">
			<view class="header-left" @click="navigateTo('/pages/message/index')">
				<!-- name:MsgCenter 消息中心权限-->
				<view class="bell-wrap" v-if="hasMenu('MsgCenter')">
					<image class="icon" src="/static/images/index/bell.png" mode="widthFix" />
					<view class="badge">99+</view>
				</view>
			</view>
			<view class="header-title">首页</view>
			<view class="header-right" />
		</view>

		<!-- 顶部统计卡片 -->
		<!-- name:BpmManage 流程中心权限 -->
		<view class="stats-row" v-if="hasMenu('BpmManage')">
			<view class="stat-card" @click="navigateTo('/pages/flow/approval')">
				<image class="card-bg" src="/static/images/index/card_wait.png" mode="aspectFill" />
				<view class="card-content">
					<text class="card-title">待审批的</text>
					<text class="card-num">{{ todoCount }}</text>
				</view>
			</view>
			<view class="stat-card" @click="navigateTo('/pages/flow/myflow')">
				<image class="card-bg" src="/static/images/index/card_launch.png" mode="aspectFill" />
				<view class="card-content">
					<text class="card-title">我发起的</text>
					<text class="card-num">{{ myCreateCount }}</text>
				</view>
			</view>
		</view>
		<!-- 分组入口 -->
		<view class="section" v-for="(it, index) in sectionList" :key="index" v-if="!it.hidden">
			<text class="section-title">{{ it.title }}</text>
			<view class="grid">
				<view class="grid-item" v-for="(g, idx) in it.items" :key="idx" v-if="!g.hidden"
					@click="navigateTo(g.link)">
					<image class="grid-icon" :src="g.icon" />
					<text class="grid-text omit1">{{ g.text }}</text>
				</view>
			</view>
		</view>
		<!-- todo 后续删掉 -->
		<view class="section">
			<text class="section-title">附件上传</text>
			<view class="upload-row">
				<FileUpload v-model="fileInfo" />
			</view>
			<view v-if="fileInfo && fileInfo.name" class="upload-show">{{ fileInfo.name }}</view>
		</view>
	</view>
</template>

<script>
import FileUpload from '@/components/file-upload/index.vue'
import {
	statisticsCountApi
} from '@/api/flow.js'
import { getMenusApi } from '@/api/base.js'
export default {
	components: { FileUpload },
	data() {
		return {
			todoCount: '',
			myCreateCount: '',
			fileInfo: { id: '', name: '' },
			sectionList: [{
				title: '客户开发管理',
				items: [{
					text: '开发管理',
					icon: '/static/images/index/dev_manage.png',
					link: '/pages/dev_manage/index',
					name: 'CustomerDevPlan'
				}]
			},
			{
				title: '客户资信管理',
				items: [{
					text: '资信管理',
					icon: '/static/images/index/credit_manage.png',
					link: '/pages/credit_manage/index',
					name: 'CustomerCreditPlan'
				}]
			},
			{
				title: '合同管理',
				items: [{
					text: '框架合同',
					icon: '/static/images/index/contract_framework.png',
					link: '/pages/contract_framework/index',
					name: 'ContractFramework'
				},
				{
					text: '经销标准合同',
					icon: '/static/images/index/contract_retail.png',
					link: '/pages/contract_retail/index',
					name: 'DistributionStandardContract'
				},
				{
					text: '经销库存合同',
					icon: '/static/images/index/contract_stock.png',
					link: '/pages/contract_stock/index',
					name: 'DistributionInventoryContract'
				},
				{
					text: '经销未锁规合同',
					icon: '/static/images/index/contract_unplan.png',
					link: '/pages/contract_unplan/index',
					name: 'DistributionUnlockedContract'
				},
				{
					text: '加工标准合同',
					icon: '/static/images/index/contract_process.png',
					link: '/pages/contract_process/index',
					name: 'ProcessedStandardContract'
				},
				{
					text: '外贸标准合同',
					icon: '/static/images/index/contract_foreign_std.png',
					link: '/pages/contract_foreign_std/index',
					name: 'ForeignTradeStandardContract'
				},
				{
					text: '外贸库存合同',
					icon: '/static/images/index/contract_foreign_stock.png',
					link: '/pages/contract_foreign_stock/index',
					name: 'ForeignTradeInventoryContract'
				},
				{
					text: '外贸未锁规合同',
					icon: '/static/images/index/contract_foreign_unplan.png',
					link: '/pages/contract_foreign_unplan/index',
					name: 'ForeignUnlockedContract'
				},
				{
					text: '锁价无规格操作申请单',
					icon: '/static/images/index/contract_foreign_unplan.png',
					link: '/pages/contract_foreign_unplan/index',
					name: 'UnlockedOperationApplication'
				},
				],
			},
			{
				title: '订货单管理',
				items: [{
					text: '订货单列表',
					icon: '/static/images/index/order_list.png',
					link: '/pages/order_list/index',
					name: 'OrderList'
				},{
					text: '规格变更单',
					icon: '/static/images/index/change_list.png',
					link: '/pages/change_list/index',
					name: 'ChangeList'
				},{
					text: '撤销单',
					icon: '/static/images/index/revoke_list.png',
					link: '/pages/revoke_list/index',
					name: 'RevokeList'
				}]
			}
			],
			menuNames: []
		}
	},
	created() {
		this.getStatisticsFun()
		this.loadMenusAndFilter()
	},
	methods: {
		getStatisticsFun() {
			statisticsCountApi().then((res) => {
				this.todoCount = res.data.todoCount || '';
				this.myCreateCount = res.data.myCreateCount || '';
			});
		},
		async loadMenusAndFilter() {
			try {
				const res = await getMenusApi()
				const data = res && res.data ? res.data : []
				const names = []
				const walk = (n) => {
					if (Array.isArray(n)) { n.forEach(walk); return }
					if (!n || typeof n !== 'object') return
					const nm = n.name != null ? String(n.name) : ''
					if (nm) names.push(nm)
					const cs = n.children
					if (Array.isArray(cs)) cs.forEach(walk)
				}
				walk(data)
				this.menuNames = names
				this.applyMenuFilter()
			} catch (e) {
				this.menuNames = []
				this.applyMenuFilter()
			}
		},
		applyMenuFilter() {
			this.sectionList = (this.sectionList || []).map(sec => {
				const items = Array.isArray(sec.items) ? sec.items : []
				const nextItems = items.map(it => ({ ...it, hidden: !this.hasMenu(it && it.name) }))
				const visibleCount = nextItems.filter(it => !it.hidden).length
				const hidden = items.length === 0 || visibleCount === 0
				return { ...sec, items: nextItems, hidden }
			})
		},
		hasMenu(name) {
			const names = Array.isArray(this.menuNames) ? this.menuNames : []
			if (name == null) return false
			return names.includes(String(name))
		},
		// 普通页面
		navigateTo(link) {
			if (!link || link === '#') {
				return;
			}
			// 规范化路径,确保以斜杠开头
			const url = link.startsWith('/') ? link : `/${link}`;

			// 普通页面使用 navigateTo
			uni.navigateTo({
				url,
				fail: () => {
					uni.showToast({
						title: '页面暂未开放',
						icon: 'none'
					});
				}
			});
		}
	}
}
</script>

<style scoped lang="scss">
page {
	background-color: #fff;
}

.home-page {
	padding: 0 32rpx 32rpx;
	padding-top: 96rpx;
}

/* 自定义头部 */
.custom-header {
	height: 96rpx;
	display: flex;
	align-items: center;
	justify-content: space-between;
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	z-index: 1000;
	background-color: #fff;
	padding: 0 32rpx;

	.header-left {
		display: flex;
		align-items: center;
	}

	.header-title {
		font-size: 36rpx;
		font-weight: 600;
		color: rgba(0, 0, 0, 0.9);
	}

	.icon {
		width: 48rpx;
		height: 48rpx;
	}

	.bell-wrap {
		position: relative;
	}

	.header-right {
		width: 48rpx;
	}

	.badge {
		position: absolute;
		top: -8rpx;
		left: 30rpx;
		background-color: #ff3b30;
		color: #fff;
		font-size: 20rpx;
		height: 32rpx;
		min-width: 32rpx;
		padding: 0 8rpx;
		border-radius: 16rpx;
		display: flex;
		align-items: center;
		justify-content: center;
	}
}


/* 统计卡片 */
.stats-row {
	display: flex;
	gap: 26rpx;
	padding-top: 20rpx;

	.stat-card {
		flex: 1;
		height: 168rpx;
		position: relative;
		overflow: hidden;
		border-radius: 16rpx;
	}

	.card-bg {
		position: absolute;
		left: 0;
		top: 0;
		width: 100%;
		height: 100%;
	}

	.card-content {
		position: absolute;
		left: 32rpx;
		top: 36rpx;
		display: flex;
		flex-direction: column;
		gap: 12rpx;
	}

	.card-title {
		color: rgba(0, 0, 0, 0.9);
		font-size: 32rpx;
		line-height: 44rpx;
	}

	.card-num {
		color: rgba(0, 0, 0, 0.4);
		font-size: 32rpx;
		font-weight: 600;
		line-height: 44rpx;
	}
}


/* 分组入口 */
.section {
	margin-top: 32rpx;

	.section-title {
		font-size: 32rpx;
		font-weight: 600;
		color: rgba(0, 0, 0, 0.9);
		line-height: 44rpx;
	}

	.grid {
		margin-top: 28rpx;
		display: grid;
		grid-template-columns: repeat(4, 1fr);
		gap: 32rpx;
	}

	.grid-item {
		display: flex;
		flex-direction: column;
		align-items: center;
	}

	.grid-icon {
		width: 96rpx;
		height: 96rpx;
		border-radius: 24rpx;
	}

	.grid-text {
		margin-top: 16rpx;
		font-size: 24rpx;
		color: rgba(0, 0, 0, 0.6);
		text-align: center;
	}

}
</style>