瀏覽代碼

修改餐盒团长样式;

付晓文。 1 月之前
父節點
當前提交
ea2af5a8c0

+ 2 - 2
common/router/modules/routes.js

@@ -914,12 +914,12 @@ const routes = [{
 	{
 		path: '/pages/merchantDish/settings/box-form',
 		name: 'merchantDishBoxForm',
-		meta: { title: '餐盒' },
+		meta: { title: '新建餐盒' },
 	},
 	{
 		path: '/pages/merchantDish/settings/box-bind',
 		name: 'merchantDishBoxBind',
-		meta: { title: '绑定商品' },
+		meta: { title: '团餐商品列表' },
 	},
 	{
 		path: '/pages/merchantDish/orders/index',

+ 1 - 1
components/index_nav.vue

@@ -53,7 +53,7 @@
 						name: '菜品管理',
 						url: '/static/index/product.png',
 						path: '/pages/merchantDish/dish/list',
-						needGroupMeal: true
+						needTopUp: true
 					},
 					{
 						name: '商品管理',

+ 7 - 3
components/list-scroll-view/index.vue

@@ -8,7 +8,8 @@
 			@up="upCallback"
 			@down="downCallback"
 			@init="mescrollInit"
-			@scroll="onScroll">
+			@scroll="onScroll"
+			@emptyclick="onEmptyClick">
 			<view class="lsv-list">
 				<slot name="list" :data="dataList" :is-scrolling="isScrolling"></slot>
 			</view>
@@ -165,8 +166,11 @@ export default {
 			});
 		},
 		reloadList(p) {
-			this.params = p;
-			this.mescroll.resetUpScroll();
+			this.params = p || this.params;
+			if (this.mescroll) this.mescroll.resetUpScroll();
+		},
+		onEmptyClick() {
+			this.$emit('emptyclick');
 		}
 	}
 }

+ 2 - 2
pages.json

@@ -682,13 +682,13 @@
 		{
 			"path": "pages/merchantDish/settings/box-form",
 			"style": {
-				"navigationBarTitleText": "餐盒"
+				"navigationBarTitleText": "新建餐盒"
 			}
 		},
 		{
 			"path": "pages/merchantDish/settings/box-bind",
 			"style": {
-				"navigationBarTitleText": "绑定商品"
+				"navigationBarTitleText": "团餐商品列表"
 			}
 		},
 		{

+ 95 - 45
pages/merchantDish/settings/box-bind.vue

@@ -1,9 +1,19 @@
 <template>
 	<view class="page">
-		<u-navbar title="团餐商品列表" :autoBack="true" :placeholder="true"></u-navbar>
+		<u-navbar title="团餐商品列表" :autoBack="true" :placeholder="true" :border-bottom="true"></u-navbar>
+
 		<view class="search">
-			<u-search v-model="keyword" placeholder="搜索菜品" :show-action="true" action-text="搜索" @search="fetchPackages" @custom="fetchPackages"></u-search>
+			<u-search
+				v-model="keyword"
+				placeholder="请输入商品名称"
+				:show-action="true"
+				action-text="搜索"
+				bg-color="#F5F5F5"
+				@search="fetchPackages"
+				@custom="fetchPackages"
+			></u-search>
 		</view>
+
 		<view class="body">
 			<view class="cats">
 				<view
@@ -14,25 +24,30 @@
 				>{{ c }}</view>
 			</view>
 			<scroll-view scroll-y class="goods">
+				<view class="cat-title">{{ cats[catIndex] }}</view>
 				<view class="g-item" v-for="item in list" :key="item.id" @click="toggle(item.id)">
-					<view :class="['dot', selected.includes(item.id) ? 'on' : '']"></view>
-					<image :src="item.image" mode="aspectFill"></image>
+					<image class="thumb" :src="item.image" mode="aspectFill"></image>
 					<view class="info">
 						<view class="name">{{ item.name }}</view>
-						<view class="desc">{{ item.desc }}</view>
-						<view class="price">¥{{ item.price }}</view>
-						<view class="box-tip">{{ item.mealBoxNames ? `餐盒名称${item.mealBoxNames}` : '未绑定' }}</view>
+						<view class="desc">{{ item.desc || '暂无描述' }}</view>
+						<view class="price">¥{{ formatPrice(item.price) }}</view>
+					</view>
+					<view :class="['check', selected.includes(item.id) ? 'on' : '']">
+						<u-icon v-if="selected.includes(item.id)" name="checkmark" color="#fff" size="22"></u-icon>
 					</view>
 				</view>
 				<u-empty v-if="!loading && !list.length" text="暂无团餐商品" mode="list"></u-empty>
 			</scroll-view>
 		</view>
+
 		<view class="footer">
 			<view class="all" @click="toggleAll">
-				<view :class="['dot', isAll ? 'on' : '']"></view>
-				全选
+				<view :class="['check', isAll ? 'on' : '']">
+					<u-icon v-if="isAll" name="checkmark" color="#fff" size="22"></u-icon>
+				</view>
+				<text>全选</text>
 			</view>
-			<u-button type="primary" shape="circle" :loading="saving" @click="onConfirm">确定</u-button>
+			<view :class="['bind-btn', saving ? 'disabled' : '']" @click="onConfirm">{{ saving ? '绑定中...' : '绑定' }}</view>
 		</view>
 	</view>
 </template>
@@ -76,6 +91,11 @@ export default {
 		this.initSelected().then(() => this.fetchPackages());
 	},
 	methods: {
+		formatPrice(price) {
+			const n = Number(price);
+			if (Number.isNaN(n)) return '0.00';
+			return n.toFixed(2);
+		},
 		initSelected() {
 			// 从表单进入:等待 eventChannel 传入已选;新建无接口数据
 			if (this.fromForm || this.isTempBox) {
@@ -108,9 +128,11 @@ export default {
 						const page = res.data.result || {};
 						this.list = (page.records || []).map((item) => {
 							const mapped = mapPackageToListItem(item);
+							const mealBoxNames = item.mealBoxNames || '';
 							return {
 								...mapped,
-								mealBoxNames: item.mealBoxNames || ''
+								desc: mealBoxNames || mapped.desc || '',
+								mealBoxNames
 							};
 						});
 					} else {
@@ -173,7 +195,7 @@ export default {
 <style lang="scss" scoped>
 .page {
 	min-height: 100vh;
-	background: #f7f8fc;
+	background: #fff;
 	display: flex;
 	flex-direction: column;
 	padding-bottom: 140rpx;
@@ -186,16 +208,17 @@ export default {
 	flex: 1;
 	display: flex;
 	min-height: 0;
+	background: #fff;
 }
 .cats {
 	width: 160rpx;
-	background: #f0f1f5;
+	background: #f5f5f5;
 	.cat {
-		height: 88rpx;
-		line-height: 88rpx;
+		height: 96rpx;
+		line-height: 96rpx;
 		text-align: center;
-		font-size: 26rpx;
-		color: #666;
+		font-size: 28rpx;
+		color: #333;
 		&.on {
 			background: #fff;
 			color: #449aff;
@@ -206,51 +229,65 @@ export default {
 .goods {
 	flex: 1;
 	height: calc(100vh - 280rpx);
-	padding: 16rpx;
+	padding: 0 24rpx 24rpx;
 	box-sizing: border-box;
+	background: #fff;
+}
+.cat-title {
+	padding: 20rpx 0 8rpx;
+	font-size: 30rpx;
+	font-weight: 600;
+	color: #333;
 }
 .g-item {
 	display: flex;
 	align-items: center;
-	background: #fff;
-	border-radius: 12rpx;
-	padding: 16rpx;
-	margin-bottom: 12rpx;
-	image {
-		width: 100rpx;
-		height: 100rpx;
-		border-radius: 8rpx;
-		margin: 0 16rpx;
+	padding: 20rpx 0;
+	.thumb {
+		width: 120rpx;
+		height: 120rpx;
+		border-radius: 12rpx;
 		background: #eee;
+		flex-shrink: 0;
 	}
 	.info {
 		flex: 1;
+		margin: 0 20rpx;
+		min-width: 0;
 		.name {
 			font-size: 28rpx;
-			font-weight: 500;
+			font-weight: 600;
+			color: #333;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
 		}
 		.desc {
 			font-size: 22rpx;
 			color: #999;
-			margin-top: 4rpx;
+			margin-top: 8rpx;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
 		}
 		.price {
-			color: #ff4d4f;
-			margin-top: 6rpx;
-		}
-		.box-tip {
-			font-size: 22rpx;
-			color: #999;
-			margin-top: 4rpx;
+			margin-top: 10rpx;
+			font-size: 28rpx;
+			font-weight: 600;
+			color: #ff4d6a;
 		}
 	}
 }
-.dot {
-	width: 36rpx;
-	height: 36rpx;
+.check {
+	width: 40rpx;
+	height: 40rpx;
 	border-radius: 50%;
-	border: 2rpx solid #ccc;
+	border: 2rpx solid #d0d0d0;
 	flex-shrink: 0;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	box-sizing: border-box;
 	&.on {
 		background: #449aff;
 		border-color: #449aff;
@@ -261,21 +298,34 @@ export default {
 	left: 0;
 	right: 0;
 	bottom: 0;
+	z-index: 20;
 	display: flex;
 	align-items: center;
-	gap: 20rpx;
 	padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom));
 	background: #fff;
+	border-top: 1rpx solid #f0f0f0;
 	.all {
 		display: flex;
 		align-items: center;
-		font-size: 26rpx;
-		.dot {
-			margin-right: 10rpx;
+		font-size: 28rpx;
+		color: #333;
+		margin-right: 24rpx;
+		.check {
+			margin-right: 12rpx;
 		}
 	}
-	::v-deep .u-btn {
+	.bind-btn {
 		flex: 1;
+		height: 80rpx;
+		line-height: 80rpx;
+		text-align: center;
+		color: #fff;
+		font-size: 30rpx;
+		border-radius: 40rpx;
+		background: linear-gradient(90deg, #77b6ff 0%, #449aff 100%);
+		&.disabled {
+			opacity: 0.6;
+		}
 	}
 }
 </style>

+ 111 - 57
pages/merchantDish/settings/box-form.vue

@@ -1,40 +1,38 @@
 <template>
 	<view class="page">
-		<u-navbar :title="isEdit ? '编辑餐盒' : '新增餐盒'" :autoBack="true" :placeholder="true"></u-navbar>
-		<view class="card">
+		<u-navbar :title="isEdit ? '编辑餐盒' : '新建餐盒'" :autoBack="true" :placeholder="true" :border-bottom="true"></u-navbar>
+
+		<view class="section-head">规格1</view>
+		<view class="form">
 			<view class="row">
 				<text class="label">餐盒名称</text>
-				<input v-model="form.name" placeholder="请输入" />
+				<input class="input" v-model="form.name" placeholder="请输入" placeholder-class="ph" />
 			</view>
 			<view class="row">
 				<text class="label">餐盒价格</text>
-				<input v-model="form.price" type="digit" placeholder="默认0" />
+				<input class="input" v-model="form.price" type="digit" placeholder="请输入金额 元" placeholder-class="ph" />
 			</view>
-			<view class="row" @click="goBind">
-				<text class="label">绑定商品</text>
-				<text class="value">{{ form.bindCount ? `已绑${form.bindCount}个` : '去绑定' }}</text>
-				<u-icon name="arrow-right" color="#C7C6CA"></u-icon>
+			<view class="row link" @click="goBind">
+				<text class="label">已绑商品</text>
+				<view class="right">
+					<text class="action">{{ form.bindCount ? `已绑${form.bindCount}个` : '去绑定' }}</text>
+					<u-icon name="arrow-right" color="#C7C6CA" size="24"></u-icon>
+				</view>
 			</view>
-			<view class="upload-row">
+			<view class="row link last" @click="pickImage">
 				<text class="label">餐盒图片</text>
-				<u-upload
-					:action="uploadAction"
-					:file-list="fileList"
-					:form-data="uploadFormData"
-					:max-count="1"
-					:deletable="true"
-					del-bg-color="#fa3534"
-					upload-text="上传"
-					width="160"
-					height="160"
-					preview-full-image
-					@on-success="onUploadSuccess"
-					@on-remove="onUploadRemove"
-				/>
+				<view class="right">
+					<image v-if="form.image" class="thumb" :src="form.image" mode="aspectFill" @click.stop="previewImage"></image>
+					<template v-else>
+						<text class="action">去上传</text>
+						<u-icon name="arrow-right" color="#C7C6CA" size="24"></u-icon>
+					</template>
+				</view>
 			</view>
 		</view>
+
 		<view class="footer">
-			<u-button type="primary" shape="circle" :loading="saving" @click="onSave">保存</u-button>
+			<view :class="['primary-btn', saving ? 'disabled' : '']" @click="onSave">{{ saving ? '保存中...' : '保存' }}</view>
 		</view>
 	</view>
 </template>
@@ -50,12 +48,10 @@ export default {
 			saving: false,
 			uploadAction: configService.apiUrl + '/sys/common/upload',
 			imageHttp: configService.apiUrl + '/',
-			uploadFormData: { biz: 'temp' },
-			fileList: [],
 			form: {
 				id: null,
 				name: '',
-				price: '0',
+				price: '',
 				image: '',
 				bindIds: [],
 				bindCount: 0,
@@ -90,12 +86,11 @@ export default {
 							...this.form,
 							id: item.id,
 							name: item.name || '',
-							price: String(item.price != null ? item.price : 0),
+							price: item.price != null && item.price !== '' ? String(item.price) : '',
 							image: item.image || '',
 							status: item.status != null ? item.status : 1,
 							sort: item.sort || 0
 						};
-						this.fileList = item.image ? [{ url: item.image }] : [];
 					}
 					if (pkgRes.data.code === 200) {
 						const pkgs = pkgRes.data.result || [];
@@ -105,19 +100,45 @@ export default {
 				})
 				.finally(() => uni.hideLoading());
 		},
-		onUploadSuccess(res, index, lists) {
-			this.fileList = lists;
-			const urls = lists
-				.map((item) => {
-					if (item.response && item.response.message) return this.imageHttp + item.response.message;
-					return item.url || '';
-				})
-				.filter(Boolean);
-			this.form.image = urls[0] || '';
+		pickImage() {
+			uni.chooseImage({
+				count: 1,
+				sizeType: ['compressed'],
+				sourceType: ['album', 'camera'],
+				success: (res) => {
+					const filePath = (res.tempFilePaths || [])[0];
+					if (!filePath) return;
+					uni.showLoading({ title: '上传中' });
+					uni.uploadFile({
+						url: this.uploadAction,
+						filePath,
+						name: 'file',
+						formData: { biz: 'temp' },
+						success: (uploadRes) => {
+							try {
+								const data = typeof uploadRes.data === 'string' ? JSON.parse(uploadRes.data) : uploadRes.data;
+								if (data && data.success !== false && data.message) {
+									this.form.image = this.imageHttp + data.message;
+								} else if (data && data.url) {
+									this.form.image = data.url;
+								} else {
+									this.$tip.toast((data && data.message) || '上传失败');
+								}
+							} catch (e) {
+								this.$tip.toast('上传失败');
+							}
+						},
+						fail: () => {
+							this.$tip.toast('上传失败');
+						},
+						complete: () => uni.hideLoading()
+					});
+				}
+			});
 		},
-		onUploadRemove(index, lists) {
-			this.fileList = lists;
-			this.form.image = '';
+		previewImage() {
+			if (!this.form.image) return;
+			uni.previewImage({ urls: [this.form.image] });
 		},
 		goBind() {
 			const boxId = this.form.id || 'new';
@@ -187,50 +208,83 @@ export default {
 <style lang="scss" scoped>
 .page {
 	min-height: 100vh;
-	background: #f7f8fc;
+	background: #f7f8fa;
 	padding-bottom: 160rpx;
 }
-.card {
-	margin: 24rpx;
+.section-head {
+	padding: 20rpx 24rpx;
+	font-size: 30rpx;
+	font-weight: 600;
+	color: #333;
+	background: #eef3f8;
+}
+.form {
 	background: #fff;
-	border-radius: 16rpx;
-	padding: 8rpx 24rpx;
 }
 .row {
 	display: flex;
 	align-items: center;
 	min-height: 96rpx;
+	padding: 0 24rpx;
 	border-bottom: 1rpx solid #f0f0f0;
+	&.last {
+		border-bottom: none;
+	}
+	&.link:active {
+		background: #fafafa;
+	}
 	.label {
 		width: 180rpx;
 		font-size: 28rpx;
+		color: #333;
+		flex-shrink: 0;
 	}
-	input {
+	.input {
 		flex: 1;
 		text-align: right;
 		font-size: 28rpx;
+		color: #333;
 	}
-	.value {
+	.right {
 		flex: 1;
-		text-align: right;
-		color: #449aff;
-		font-size: 28rpx;
+		display: flex;
+		align-items: center;
+		justify-content: flex-end;
 	}
-}
-.upload-row {
-	padding: 24rpx 0;
-	.label {
+	.action {
 		font-size: 28rpx;
-		display: block;
-		margin-bottom: 16rpx;
+		color: #999;
+		margin-right: 4rpx;
 	}
+	.thumb {
+		width: 72rpx;
+		height: 72rpx;
+		border-radius: 8rpx;
+		background: #eee;
+	}
+}
+.ph {
+	color: #c0c4cc;
 }
 .footer {
 	position: fixed;
 	left: 0;
 	right: 0;
 	bottom: 0;
+	z-index: 20;
 	padding: 20rpx 40rpx calc(20rpx + env(safe-area-inset-bottom));
 	background: #fff;
 }
+.primary-btn {
+	height: 88rpx;
+	line-height: 88rpx;
+	text-align: center;
+	color: #fff;
+	font-size: 32rpx;
+	border-radius: 44rpx;
+	background: linear-gradient(90deg, #77b6ff 0%, #449aff 100%);
+	&.disabled {
+		opacity: 0.6;
+	}
+}
 </style>

+ 194 - 72
pages/merchantDish/settings/box-list.vue

@@ -1,56 +1,116 @@
 <template>
 	<view class="page">
-		<u-navbar title="餐盒设置" :autoBack="true" :placeholder="true"></u-navbar>
-		<view class="list">
-			<view class="card" v-for="item in list" :key="item.id">
-				<image class="img" :src="item.image" mode="aspectFill"></image>
-				<view class="info">
-					<view class="name">{{ item.name }}</view>
-					<view class="meta">价格:¥{{ item.price }}</view>
-					<view class="meta link" @click="goBind(item)">绑定商品:{{ item.bindCount }}个 ></view>
-				</view>
-				<view class="ops">
-					<text @click="goEdit(item)">编辑</text>
-					<text class="danger" @click="onDelete(item)">删除</text>
-				</view>
-			</view>
-			<u-empty v-if="!loading && !list.length" text="暂无餐盒" mode="list"></u-empty>
+		<u-navbar title="餐盒设置" :autoBack="true" :placeholder="true" :border-bottom="true"></u-navbar>
+
+		<view class="list-wrap" :style="{ paddingBottom: listCount ? '160rpx' : '0' }">
+			<ListScrollView
+				ref="listScroll"
+				:api="api"
+				:init="initParams"
+				:pageSize="50"
+				emptyText="暂无内容~"
+				emptyBtnText="去新建餐盒"
+				@afterFetch="afterFetch"
+				@emptyclick="goEdit()"
+			>
+				<template v-slot:list="{ data }">
+					<view class="spec-block" v-for="(item, index) in data" :key="item.id">
+						<view class="spec-head">
+							<text class="spec-title">规格{{ index + 1 }}</text>
+							<view class="spec-ops">
+								<view class="op" @click="goEdit(item)">
+									<u-icon name="edit-pen" color="#449AFF" size="28"></u-icon>
+									<text>编辑</text>
+								</view>
+								<view class="op" @click="onDelete(item)">
+									<u-icon name="trash" color="#449AFF" size="28"></u-icon>
+									<text>删除</text>
+								</view>
+							</view>
+						</view>
+						<view class="spec-body">
+							<view class="row">
+								<text class="label">餐盒名称</text>
+								<text class="value">{{ item.name || '-' }}</text>
+							</view>
+							<view class="row">
+								<text class="label">餐盒价格</text>
+								<text class="value">{{ item.price != null ? item.price : 0 }} 元</text>
+							</view>
+							<view class="row link" @click="goBind(item)">
+								<text class="label">已绑商品</text>
+								<view class="value-wrap">
+									<text class="value">{{ item.bindCount || 0 }}个</text>
+									<u-icon name="arrow-right" color="#C7C6CA" size="24"></u-icon>
+								</view>
+							</view>
+							<view class="row last">
+								<text class="label">餐盒图片</text>
+								<image v-if="item.image" class="thumb" :src="item.image" mode="aspectFill"></image>
+								<text v-else class="value muted">暂无图片</text>
+							</view>
+						</view>
+					</view>
+				</template>
+			</ListScrollView>
 		</view>
-		<view class="footer">
-			<u-button type="primary" shape="circle" @click="goEdit()">新增餐盒</u-button>
+
+		<view class="footer" v-if="listCount">
+			<view class="primary-btn" @click="goEdit()">新建餐盒</view>
 		</view>
 	</view>
 </template>
 
 <script>
-import merchantDishApi, { mapMealBoxItem } from '@/api/merchantDish.js';
+import ListScrollView from '@/components/list-scroll-view/index.vue';
+import merchantDishApi, { mapMealBoxItem, getMerchantId } from '@/api/merchantDish.js';
 
 export default {
+	components: {
+		ListScrollView
+	},
 	data() {
 		return {
-			list: [],
-			loading: false
+			api: {
+				url: '/groupmeal/gmMerchantMealBox/list',
+				method: 'GET'
+			},
+			listCount: 0,
+			listReady: false
 		};
 	},
+	computed: {
+		initParams() {
+			return {
+				merchantId: getMerchantId()
+			};
+		}
+	},
 	onShow() {
-		this.fetchList();
+		if (this.listReady) {
+			this.reloadList();
+		} else {
+			this.listReady = true;
+		}
 	},
 	methods: {
-		fetchList() {
-			this.loading = true;
-			merchantDishApi
-				.mealBoxList({ pageNo: 1, pageSize: 100 })
-				.then((res) => {
-					if (res.data.code === 200) {
-						const page = res.data.result || {};
-						this.list = (page.records || []).map(mapMealBoxItem);
-					} else {
-						this.$tip.toast(res.data.message || '加载失败');
-					}
-				})
-				.finally(() => {
-					this.loading = false;
-				});
+		afterFetch(result) {
+			const records = Array.isArray(result.records) ? result.records : [];
+			records.forEach((item) => {
+				Object.assign(item, mapMealBoxItem(item));
+			});
+			this.$nextTick(() => {
+				const scroll = this.$refs.listScroll;
+				this.listCount = (scroll && scroll.dataList && scroll.dataList.length) || 0;
+			});
+		},
+		reloadList() {
+			this.$nextTick(() => {
+				const scroll = this.$refs.listScroll;
+				if (scroll && scroll.mescroll) {
+					scroll.reloadList(this.initParams);
+				}
+			});
 		},
 		goEdit(item) {
 			const q = item ? `?id=${item.id}` : '';
@@ -67,7 +127,7 @@ export default {
 					if (!res.confirm) return;
 					merchantDishApi.mealBoxDelete(item.id).then((r) => {
 						this.$tip.toast(r.data.message || (r.data.code === 200 ? '删除成功' : '删除失败'));
-						if (r.data.code === 200) this.fetchList();
+						if (r.data.code === 200) this.reloadList();
 					});
 				}
 			});
@@ -78,59 +138,121 @@ export default {
 
 <style lang="scss" scoped>
 .page {
-	min-height: 100vh;
-	background: #f7f8fc;
-	padding-bottom: 160rpx;
+	height: 100vh;
+	background: #f7f8fa;
+	display: flex;
+	flex-direction: column;
 }
-.list {
-	padding: 24rpx;
+.list-wrap {
+	flex: 1;
+	height: 0;
+	box-sizing: border-box;
 }
-.card {
-	display: flex;
-	background: #fff;
-	border-radius: 16rpx;
-	padding: 20rpx;
+.spec-block {
 	margin-bottom: 16rpx;
-	.img {
-		width: 120rpx;
-		height: 120rpx;
-		border-radius: 12rpx;
-		background: #eee;
+}
+.spec-head {
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	padding: 20rpx 24rpx;
+	background: #eef3f8;
+	.spec-title {
+		font-size: 30rpx;
+		font-weight: 600;
+		color: #333;
 	}
-	.info {
-		flex: 1;
-		margin-left: 16rpx;
-		.name {
-			font-size: 30rpx;
-			font-weight: 500;
+	.spec-ops {
+		display: flex;
+		align-items: center;
+		.op {
+			display: flex;
+			align-items: center;
+			margin-left: 28rpx;
+			font-size: 26rpx;
+			color: #449aff;
+			text {
+				margin-left: 6rpx;
+			}
 		}
-		.meta {
-			font-size: 24rpx;
+	}
+}
+.spec-body {
+	background: #fff;
+}
+.row {
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	min-height: 96rpx;
+	padding: 0 24rpx;
+	border-bottom: 1rpx solid #f0f0f0;
+	&.last {
+		border-bottom: none;
+	}
+	&.link:active {
+		background: #fafafa;
+	}
+	.label {
+		font-size: 28rpx;
+		color: #333;
+		flex-shrink: 0;
+	}
+	.value {
+		font-size: 28rpx;
+		color: #333;
+		text-align: right;
+		&.muted {
 			color: #999;
-			margin-top: 8rpx;
-			&.link {
-				color: #449aff;
-			}
 		}
 	}
-	.ops {
+	.value-wrap {
 		display: flex;
-		flex-direction: column;
-		justify-content: center;
-		gap: 16rpx;
-		font-size: 26rpx;
-		color: #449aff;
-		.danger {
-			color: #ff4d4f;
+		align-items: center;
+		.value {
+			margin-right: 4rpx;
 		}
 	}
+	.thumb {
+		width: 72rpx;
+		height: 72rpx;
+		border-radius: 8rpx;
+		background: #eee;
+	}
 }
 .footer {
 	position: fixed;
 	left: 0;
 	right: 0;
 	bottom: 0;
+	z-index: 20;
 	padding: 20rpx 40rpx calc(20rpx + env(safe-area-inset-bottom));
 	background: #fff;
 }
+.primary-btn {
+	height: 88rpx;
+	line-height: 88rpx;
+	text-align: center;
+	color: #fff;
+	font-size: 32rpx;
+	border-radius: 44rpx;
+	background: linear-gradient(90deg, #77b6ff 0%, #449aff 100%);
+}
+
+/* 空态按钮对齐设计:蓝描边胶囊 */
+::v-deep .mescroll-empty .empty-tip {
+	font-size: 28rpx;
+	color: #999;
+	margin-top: 0;
+}
+::v-deep .mescroll-empty .empty-btn {
+	margin-top: 40rpx;
+	min-width: 260rpx;
+	padding: 16rpx 40rpx;
+	font-size: 28rpx;
+	color: #449aff;
+	border: 2rpx solid #449aff;
+	border-radius: 36rpx;
+	background: #fff;
+}
 </style>

+ 34 - 5
pages/merchantDish/settings/index.vue

@@ -1,9 +1,17 @@
 <template>
 	<view class="page">
-		<u-navbar title="团餐设置" :autoBack="true" :placeholder="true"></u-navbar>
-		<u-cell-group :border="false">
-			<u-cell-item v-for="(item, i) in menus" :key="i" :title="item.name" :arrow="true" @click="go(item)"></u-cell-item>
-		</u-cell-group>
+		<u-navbar title="团餐设置" :autoBack="true" :placeholder="true" :border-bottom="true"></u-navbar>
+		<view class="menu">
+			<view
+				v-for="(item, i) in menus"
+				:key="i"
+				:class="['row', i === menus.length - 1 ? 'last' : '']"
+				@click="go(item)"
+			>
+				<text class="label">{{ item.name }}</text>
+				<u-icon name="arrow-right" color="#C7C6CA" size="28"></u-icon>
+			</view>
+		</view>
 	</view>
 </template>
 
@@ -28,6 +36,27 @@ export default {
 <style lang="scss" scoped>
 .page {
 	min-height: 100vh;
-	background: #f7f8fc;
+	background: #f7f8fa;
+}
+.menu {
+	background: #fff;
+}
+.row {
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	min-height: 104rpx;
+	padding: 0 32rpx;
+	border-bottom: 1rpx solid #f0f0f0;
+	&.last {
+		border-bottom: none;
+	}
+	&:active {
+		background: #fafafa;
+	}
+	.label {
+		font-size: 30rpx;
+		color: #333;
+	}
 }
 </style>

+ 59 - 20
pages/merchantDish/settings/leader.vue

@@ -1,13 +1,18 @@
 <template>
 	<view class="page">
-		<u-navbar title="我的团长" :autoBack="true" :placeholder="true"></u-navbar>
-		<view class="list">
-			<ListScrollView ref="listScroll" :api="api" :noPage="true">
+		<u-navbar title="我的团长" :autoBack="true" :placeholder="true" :border-bottom="true"></u-navbar>
+		<view class="list-wrap">
+			<ListScrollView
+				ref="listScroll"
+				:api="api"
+				:noPage="true"
+				emptyText="暂无团长"
+			>
 				<template v-slot:list="{ data }">
 					<view class="item" v-for="(item, i) in data" :key="item.id || i">
-						<u-avatar size="80" src="/static/product/user.png"></u-avatar>
+						<image class="avatar" :src="avatarSrc(item)" mode="aspectFill"></image>
 						<view class="info">
-							<view class="name">{{ item.headName || '' }}</view>
+							<view class="name">{{ item.headName || '用户昵称' }}</view>
 							<view class="phone">{{ item.headPhone || '' }}</view>
 						</view>
 					</view>
@@ -29,41 +34,75 @@ export default {
 			api: {
 				url: '/groupmeal/gmMerchantHead/regimentalCommander',
 				method: 'GET'
-			}
+			},
+			defaultAvatar: '/static/product/user.png',
+			listReady: false
 		};
+	},
+	onShow() {
+		if (this.listReady) {
+			this.reloadList();
+		} else {
+			this.listReady = true;
+		}
+	},
+	methods: {
+		avatarSrc(item) {
+			return item.headAvatar || item.avatar || item.headImg || this.defaultAvatar;
+		},
+		reloadList() {
+			this.$nextTick(() => {
+				const scroll = this.$refs.listScroll;
+				if (scroll && scroll.mescroll) {
+					scroll.reloadList();
+				}
+			});
+		}
 	}
 };
 </script>
 
 <style lang="scss" scoped>
 .page {
-	min-height: 100vh;
-	background: linear-gradient(180deg, #e8f4ff 0%, #f7f8fc 30%);
+	height: 100vh;
+	background: #f7f8fa;
+	display: flex;
+	flex-direction: column;
 }
-.list {
-	height: calc(100vh - 88px);
-	padding: 24rpx;
-	box-sizing: border-box;
+.list-wrap {
+	flex: 1;
+	height: 0;
+	background: #fff;
 }
 .item {
 	display: flex;
 	align-items: center;
-	background: #fff;
-	border-radius: 16rpx;
-	padding: 24rpx;
-	margin-bottom: 16rpx;
+	padding: 24rpx 32rpx;
+	.avatar {
+		width: 88rpx;
+		height: 88rpx;
+		border-radius: 50%;
+		background: #e8f1ff;
+		flex-shrink: 0;
+	}
 	.info {
-		margin-left: 20rpx;
+		flex: 1;
+		margin-left: 24rpx;
+		padding: 8rpx 0;
+		border-bottom: 1rpx solid #f0f0f0;
 		.name {
 			font-size: 30rpx;
 			font-weight: 500;
-			color: #222;
+			color: #333;
 		}
 		.phone {
-			font-size: 24rpx;
+			font-size: 26rpx;
 			color: #999;
-			margin-top: 8rpx;
+			margin-top: 10rpx;
 		}
 	}
+	&:last-child .info {
+		border-bottom: none;
+	}
 }
 </style>