index.vue 9.19 KB
<template>
  <view class="page">
    <view class="fixed">
      <view class="search-row">
        <uni-search-bar v-model="searchKeyword" radius="6" placeholder="请输入订货单位或订单编号" clearButton="auto"
          cancelButton="none" bgColor="#F3F3F3" textColor="rgba(0,0,0,0.4)" @confirm="onSearchConfirm"
          @input="onSearchInput" />
        <image class="tool-icon" src="/static/images/dev_manage/add_icon.png" @click="onAdd" />
        <image class="tool-icon" src="/static/images/dev_manage/filter_icon.png" @click="openFilter" />
      </view>
    </view>

    <view class="list-box">
      <CardList ref="cardRef" :fetchFn="fetchList" :query="query" :extra="extraParams" :enable-refresh="true"
        :enable-load-more="true" row-key="id" @loaded="onLoaded" @error="onError">
        <template v-slot="{ item }">
          <view class="card">
            <view class="card-header">
              <text class="title omit2">{{ item.orderingUnitName }}</text>
              <text v-if="item.status" :class="['status', `status_${item.status}`]">{{ filterStatus(item.status)
              }}</text>
            </view>
            <view class="info-row"><text>订单编号</text><text>{{ item.orderNo }}</text></view>
            <view class="info-row"><text>订货日期</text><text>{{ item.orderDate }}</text></view>
            <view class="info-row"><text>申请撤销日期</text><text>{{ item.documentPreparationDate }}</text></view>
            <view class="info-row"><text>撤销数量</text><text>{{ item.totalRevokeQuantity }}</text></view>
          </view>
        </template>
      </CardList>
    </view>

    <filter-modal :visible.sync="filterVisible" :value.sync="filterForm" title="筛选" @reset="onFilterReset"
      @confirm="onFilterConfirm">
      <template v-slot="{ model }">
        <view class="filter-form">

          <view class="form-item">
            <view class="label">状态</view>
            <uni-data-checkbox mode="tag" :multiple="false" :value-field="'value'" :text-field="'text'"
              v-model="model.status" @change="onStatusChange" :localdata="statusOptions" />
          </view>

          <view class="form-item">
            <view class="label">订货日期</view>
            <uni-datetime-picker type="daterange" v-model="model.dateRange" start="2023-01-01" />
          </view>
        </view>
      </template>
    </filter-modal>

  </view>
</template>

<script>
import CardList from '@/components/card/index.vue'
import FilterModal from '@/components/filter/index.vue'
import { queryApi as revokeQueryApi } from '@/api/revoke_list.js'
import { getDicByCodeApi } from '@/api/base.js'

export default {
  name: 'RevokeList',
  components: { CardList, FilterModal },
  data() {
    return {
      searchKeyword: '',
      searchKeywordDebounced: '',
      searchDebounceTimer: null,
      filterVisible: false,
      filterForm: {
        status: '',
        dateRange: []
      },
      statusOptions: [],
      query: {},
      extraParams: {},
      currentItems: []
    }
  },
  created() {
    this.extraParams = {}
    this.loadStatusOptions()
  },
  onReachBottom() {
    if (this.$refs && this.$refs.cardRef && this.$refs.cardRef.onLoadMore) {
      this.$refs.cardRef.onLoadMore()
    }
  },
  methods: {
    async loadStatusOptions() {
      try {
        const res = await getDicByCodeApi('AUDIT_STATUS')
        const list = res.data || []
        this.statusOptions = list.map(it => ({ text: it.name || '', value: it.code || '' }))
      } catch (e) {
        this.statusOptions = []
      }
    },
    onSearchInput(val) {
      if (this.searchDebounceTimer) clearTimeout(this.searchDebounceTimer)
      this.searchDebounceTimer = setTimeout(() => {
        this.searchKeywordDebounced = this.searchKeyword
        this.searchDebounceTimer = null
        this.extraParams = {
          orderingUnitNameOrOrderNo: this.searchKeywordDebounced || '',
        }
      }, 800)
    },
    onSearchConfirm(e) {
      const val = e && e.value != null ? e.value : this.searchKeyword
      this.searchKeyword = val
      this.searchKeywordDebounced = val
      this.extraParams = {
        orderingUnitNameOrOrderNo: this.searchKeywordDebounced || '',
      }
    },
    openFilter() { this.filterVisible = true },
    onFilterReset(payload) { this.filterForm = payload },
    onFilterConfirm(payload) {
      // 兜底不同端 v-model 差异
      if ((payload.status === '' || payload.status == null) && this.filterForm.status !== '') {
        payload.status = this.filterForm.status
      }
      this.filterForm = payload
      // 将条件同步给 CardList
      const query = { ...payload }
      // 处理日期范围 -> 拆分到 orderDateStart/orderDateEnd
      if (Array.isArray(query.dateRange) && query.dateRange.length === 2) {
        query.orderDateStart = query.dateRange[0]
        query.orderDateEnd = query.dateRange[1]
        delete query.dateRange
      }
      this.query = query
      this.filterVisible = false
    },
    onStatusChange(e) {
      const raw = e && e.detail && e.detail.value !== undefined ? e.detail.value : (e && e.value !== undefined ? e.value : '')
      this.filterForm.status = raw
    },
    onLoaded({ items }) { this.currentItems = items || [] },
    onError() { uni.showToast({ title: '列表加载失败', icon: 'none' }) },
    fetchList({ pageIndex, pageSize, query, extra }) {
      const params = { pageIndex, pageSize, ...query, ...extra }
      return revokeQueryApi(params).then(res => {
        const _data = res && res.data ? res.data : {}
        let records = _data.datas || _data.list || _data.records || []
        const totalCount = _data.totalCount || _data.count || 0
        const hasNext = _data.hasNext || false
        const statusTextMap = (this.statusOptions || []).reduce((acc, it) => {
          acc[String(it.value)] = it.text
          return acc
        }, {})
        records = records.map(it => ({
          ...it,
          statusName: statusTextMap[String(it.status)] || ''
        }))
        return { records, totalCount, hasNext }
      }).catch(() => {
        this.onError()
        return { records: [], totalCount: 0, hasNext: false }
      })
    },
    filterStatus(status) {
      return this.statusOptions.filter(item => item.value === status)[0].text || '';
    },
    onAdd() {
      uni.navigateTo({
        url: '/pages/revoke_list/add'
      })
    },
  }
}
</script>

<style lang="scss" scoped>
.page {
  display: flex;
  flex-direction: column;
  height: 100vh;
}

.fixed {
  position: fixed;
  top: 96rpx;
  left: 0;
  right: 0;
  z-index: 2;
  background: #fff;
}

.search-row {
  display: flex;
  align-items: center;
  padding: 16rpx 32rpx;
}

.search-row .tool-icon {
  width: 48rpx;
  height: 48rpx;
  margin-left: 16rpx;
}

.search-row .uni-searchbar {
  padding: 0;
  flex: 1;
}

::v-deep .uni-searchbar__box {
  height: 80rpx !important;
  justify-content: start;
}

::v-deep .uni-searchbar__box .uni-searchbar__box-search-input {
  font-size: 32rpx !important;
}

.list-box {
  flex: 1;
  padding: 120rpx 0 0;
}

.card {
  position: relative;

  .card-header {
    margin-bottom: 28rpx;
    position: relative;

    .title {
      font-size: 36rpx;
      font-weight: 600;
      line-height: 50rpx;
      color: rgba(0, 0, 0, 0.9);
      width: 578rpx;
    }

    .status {
      font-weight: 600;
      position: absolute;
      top: -32rpx;
      right: -32rpx;
      height: 48rpx;
      line-height: 48rpx;
      color: #fff;
      font-size: 24rpx;
      padding: 0 14rpx;
      border-radius: 6rpx;

      // 审核中
      &.status_AUDIT {
        background: $theme-primary;
      }

      // 审核通过
      &.status_PASS {
        background: #2BA471;
      }

      // 已驳回
      &.status_REFUSE {
        background: #d54941;
      }

      // 已取消
      &.status_CANCEL {
        background: #e7e7e7;
        color: rgba(0, 0, 0, 0.6);
      }

    }
  }

  .info-row {
    display: flex;
    align-items: center;
    color: rgba(0, 0, 0, 0.6);
    font-size: 28rpx;
    margin-bottom: 24rpx;

    &:last-child {
      margin-bottom: 0;
    }

    text {
      width: 60%;
      line-height: 32rpx;

      &:last-child {
        color: rgba(0, 0, 0, 0.9);
        width: 40%;
      }
    }
  }

}

.filter-form {
  .form-item {
    margin-bottom: 24rpx;
  }

  .label {
    margin-bottom: 20rpx;
    color: rgba(0, 0, 0, 0.9);
    height: 44rpx;
    line-height: 44rpx;
    font-size: 30rpx;
  }
}

/* 深度覆盖 uni-data-checkbox(mode=tag)内部的 tag 展示与间距 */
::v-deep .filter-form .uni-data-checklist .checklist-group {
  .checklist-box {
    &.is--tag {
      width: 212rpx;
      margin-top: 0;
      margin-bottom: 24rpx;
      margin-right: 24rpx;
      height: 80rpx;
      padding: 0;
      border-radius: 12rpx;
      background-color: #f3f3f3;
      border-color: #f3f3f3;

      &:nth-child(3n) {
        margin-right: 0;
      }

      .checklist-content {
        display: flex;
        justify-content: center;
      }

      .checklist-text {
        color: rgba(0, 0, 0, 0.9);
        font-size: 28rpx;
      }
    }

    &.is-checked {
      background-color: $theme-primary-plain-bg !important;
      border-color: $theme-primary-plain-bg !important;

      .checklist-text {
        color: $theme-primary !important;
      }
    }
  }

}
</style>