| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- <template>
- <view class="page">
- <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="搜索"
- bg-color="#F5F5F5"
- @search="fetchPackages"
- @custom="fetchPackages"
- ></u-search>
- </view>
- <view class="body">
- <view class="cats">
- <view
- v-for="(c, i) in cats"
- :key="c"
- :class="['cat', catIndex === i ? 'on' : '']"
- @click="onCatChange(i)"
- >{{ 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)">
- <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">¥{{ 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="['check', isAll ? 'on' : '']">
- <u-icon v-if="isAll" name="checkmark" color="#fff" size="22"></u-icon>
- </view>
- <text>全选</text>
- </view>
- <view :class="['bind-btn', saving ? 'disabled' : '']" @click="onConfirm">{{ saving ? '绑定中...' : '绑定' }}</view>
- </view>
- </view>
- </template>
- <script>
- import merchantDishApi, { mapPackageToListItem } from '@/api/merchantDish.js';
- export default {
- data() {
- return {
- boxId: null,
- fromForm: false,
- keyword: '',
- cats: ['早餐', '午餐', '晚餐'],
- catIndex: 0,
- list: [],
- selected: [],
- loading: false,
- saving: false,
- eventChannel: null
- };
- },
- computed: {
- isAll() {
- const ids = this.list.map((i) => i.id);
- return ids.length > 0 && ids.every((id) => this.selected.includes(id));
- },
- isTempBox() {
- return !this.boxId || this.boxId === 'new';
- }
- },
- onLoad(query) {
- this.boxId = query.id;
- this.fromForm = query.from === 'form';
- this.eventChannel = this.getOpenerEventChannel && this.getOpenerEventChannel();
- if (this.eventChannel && this.eventChannel.on) {
- this.eventChannel.on('initBind', (data) => {
- this.selected = [...((data && data.bindIds) || [])];
- });
- }
- 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) {
- return Promise.resolve();
- }
- // 从列表进入:拉接口已绑定套餐
- return merchantDishApi.mealBoxPackages(this.boxId).then((res) => {
- if (res.data.code === 200) {
- const pkgs = res.data.result || [];
- this.selected = pkgs.map((p) => p.packageId).filter(Boolean);
- }
- });
- },
- onCatChange(i) {
- this.catIndex = i;
- this.fetchPackages();
- },
- fetchPackages() {
- this.loading = true;
- merchantDishApi
- .packagePageList({
- pageNo: 1,
- pageSize: 100,
- productBelonging: 2,
- category: String(this.catIndex + 1),
- name: this.keyword || undefined
- })
- .then((res) => {
- if (res.data.code === 200) {
- const page = res.data.result || {};
- this.list = (page.records || []).map((item) => {
- const mapped = mapPackageToListItem(item);
- const mealBoxNames = item.mealBoxNames || '';
- return {
- ...mapped,
- desc: mealBoxNames || mapped.desc || '',
- mealBoxNames
- };
- });
- } else {
- this.$tip.toast(res.data.message || '加载失败');
- }
- })
- .finally(() => {
- this.loading = false;
- });
- },
- toggle(id) {
- const i = this.selected.indexOf(id);
- if (i >= 0) this.selected.splice(i, 1);
- else this.selected.push(id);
- },
- toggleAll() {
- if (this.isAll) {
- const ids = this.list.map((i) => i.id);
- this.selected = this.selected.filter((id) => !ids.includes(id));
- } else {
- const set = new Set(this.selected);
- this.list.forEach((i) => set.add(i.id));
- this.selected = Array.from(set);
- }
- },
- onConfirm() {
- if (this.saving) return;
- // 从表单进入:回写上一页,随表单保存一起提交接口
- if (this.fromForm || this.isTempBox) {
- if (this.eventChannel && this.eventChannel.emit) {
- this.eventChannel.emit('onBindConfirm', { bindIds: [...this.selected] });
- } else {
- const pages = getCurrentPages();
- const prev = pages[pages.length - 2];
- if (prev && prev.$vm && typeof prev.$vm.applyBindIds === 'function') {
- prev.$vm.applyBindIds([...this.selected]);
- }
- }
- uni.showToast({ title: '已选择', icon: 'success' });
- setTimeout(() => uni.navigateBack(), 400);
- return;
- }
- // 从列表直接绑定:立即调接口全量替换
- this.saving = true;
- merchantDishApi
- .mealBoxBind(this.boxId, this.selected)
- .then((res) => {
- const ok = res.data.code === 200;
- this.$tip.toast(res.data.message || (ok ? '绑定成功' : '绑定失败'));
- if (ok) setTimeout(() => uni.navigateBack(), 400);
- })
- .finally(() => {
- this.saving = false;
- });
- }
- }
- };
- </script>
- <style lang="scss" scoped>
- .page {
- min-height: 100vh;
- background: #fff;
- display: flex;
- flex-direction: column;
- padding-bottom: 140rpx;
- }
- .search {
- padding: 16rpx 24rpx;
- background: #fff;
- }
- .body {
- flex: 1;
- display: flex;
- min-height: 0;
- background: #fff;
- }
- .cats {
- width: 160rpx;
- background: #f5f5f5;
- .cat {
- height: 96rpx;
- line-height: 96rpx;
- text-align: center;
- font-size: 28rpx;
- color: #333;
- &.on {
- background: #fff;
- color: #449aff;
- font-weight: 500;
- }
- }
- }
- .goods {
- flex: 1;
- height: calc(100vh - 280rpx);
- 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;
- 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: 600;
- color: #333;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .desc {
- font-size: 22rpx;
- color: #999;
- margin-top: 8rpx;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .price {
- margin-top: 10rpx;
- font-size: 28rpx;
- font-weight: 600;
- color: #ff4d6a;
- }
- }
- }
- .check {
- width: 40rpx;
- height: 40rpx;
- border-radius: 50%;
- 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;
- }
- }
- .footer {
- position: fixed;
- left: 0;
- right: 0;
- bottom: 0;
- z-index: 20;
- display: flex;
- align-items: center;
- 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: 28rpx;
- color: #333;
- margin-right: 24rpx;
- .check {
- margin-right: 12rpx;
- }
- }
- .bind-btn {
- flex: 1;
- height: 80rpx;
- line-height: 80rpx;
- text-align: center;
- color: #fff;
- font-size: 30rpx;
- border-radius: 40rpx;
- background: linear-gradient( 89deg, #10ABFE 0%, #2260F6 100%);
- &.disabled {
- opacity: 0.6;
- }
- }
- }
- </style>
|