index.vue 10.7 KB
<template>
  <view class="page">
    <view class="plan-list-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="search"
          @input="onSearchInput"
        />
        <view class="tool-icons">
          <image class="tool-icon" src="/static/images/dev_manage/filter_icon.png" @click="openFilter" />
          <image v-if="$auth.hasPermi('plan-manage:schedule-plan:add')" class="tool-icon" src="/static/images/dev_manage/add_icon.png" @click="onAdd" />
        </view>
      </view>
    </view>

    <view class="list-box">
      <card-list
        ref="cardRef"
        :fetch-fn="fetchList"
        :query="query"
        :extra="extraParams"
        row-key="id"
        :enable-refresh="true"
        :enable-load-more="true"
        @loaded="onCardLoaded"
        @error="onCardError"
      >
        <template v-slot="{ item }">
          <view class="card" @click="onCardClick(item)">
            <view class="card-header">
              <text class="title omit2">{{ item.code || '-' }}</text>
              <text :class="['status', `status_${item.approvalStatus}`]">{{ getAuditStatusText(item.approvalStatus) }}</text>
            </view>
            <view class="info-row">
              <text>公司名称</text>
              <text>{{item.companyShortName || '-' }}</text>
            </view>
            <view class="info-row">
              <text>供应商名称</text>
              <text>{{ item.supplierName || '-' }}</text>
            </view>
            <view class="info-row">
              <text>数量</text>
              <text>{{ formatQuantity(item.totalQuantity || 0) }}</text>
            </view>
            <view class="info-row">
              <text>采购处</text>
              <text>{{ item.purchaseDepartmentName || '-' }}</text>
            </view>
          </view>
        </template>
      </card-list>
    </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>
            <input class="input" v-model="model.code" placeholder="请输入编号" />
          </view>
          <view class="form-item">
            <view class="label">供应商名称</view>
            <input class="input" v-model="model.supplierName" placeholder="请输入供应商名称" />
          </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 { getDicByCodes, getDicName } from '@/utils/dic.js'
import { procurementPlanChangeQueryApi } from '@/api/procure-manage/procurementPlan.js'

export default {
  name: 'SchedulePlan',
  components: { CardList, FilterModal },
  data() {
    return {
      searchKeyword: '',
      searchKeywordDebounced: '',
      searchDebounceTimer: null,
      query: {
        code: '',
        supplierName: '',
        dateRange: []
      },
      extraParams: {},
      currentItems: [],
      filterVisible: false,
      filterForm: {
        code: '',
        supplierName: '',
        dateRange: []
      },
      dicOptions: {
        AUDIT_STATUS: []
      }
    }
  },
  computed: {
    extraCombined() {
      const keyword = this.searchKeywordDebounced || ''
      return {
        keyword: keyword || undefined
      }
    }
  },
  watch: {
    extraCombined: {
      deep: true,
      handler(v) {
        this.extraParams = v
      },
      immediate: true
    }
  },
  created() {
    this.loadDicData()
  },
  onShow() {
    let needRefresh = ''
    try { needRefresh = uni.getStorageSync('SCHEDULE_PLAN_LIST_NEED_REFRESH') } catch (e) {}
    if (!needRefresh) return
    try { uni.removeStorageSync('SCHEDULE_PLAN_LIST_NEED_REFRESH') } catch (e) {}
    if (this.$refs && this.$refs.cardRef && this.$refs.cardRef.reload) {
      this.$refs.cardRef.reload()
    }
  },
  onReachBottom() {
    if (this.$refs && this.$refs.cardRef && this.$refs.cardRef.onLoadMore) {
      this.$refs.cardRef.onLoadMore()
    }
  },
  beforeDestroy() {
    if (this.searchDebounceTimer) {
      clearTimeout(this.searchDebounceTimer)
      this.searchDebounceTimer = null
    }
  },
  methods: {
    onAdd() {
      uni.navigateTo({ url: '/pages/schedule-plan/add' })
    },
    async loadDicData() {
      try {
        const results = await getDicByCodes(['AUDIT_STATUS'])
        this.dicOptions.AUDIT_STATUS = (results.AUDIT_STATUS && results.AUDIT_STATUS.data) || []
      } catch (e) {
        this.dicOptions.AUDIT_STATUS = []
      }
    },
    getAuditStatusText(status) {
      const v = status == null ? '' : String(status)
      if (!v) return '-'
      return getDicName('AUDIT_STATUS', v, this.dicOptions.AUDIT_STATUS) || v
    },
    getTotalQuantity(item) {
      const lines = item && item.procurementPlanLineList && Array.isArray(item.procurementPlanLineList)
        ? item.procurementPlanLineList
        : []
      let total = 0
      lines.forEach(line => {
        const q = line && line.quantity != null ? Number(line.quantity) : 0
        if (!isNaN(q)) total += q
      })
      return total
    },
    formatQuantity(val) {
      if (val == null) return '-'
      try {
        const num = Number(val)
        if (isNaN(num)) return '-'
        return num.toLocaleString('zh-CN', { maximumFractionDigits: 4 })
      } catch (e) {
        return String(val)
      }
    },
    onCardLoaded(_ref) {
      var items = _ref.items
      this.currentItems = items
    },
    onCardError() {
      uni.showToast({ title: '列表加载失败', icon: 'none' })
    },
    onSearchInput() {
      var _this = this
      if (this.searchDebounceTimer) clearTimeout(this.searchDebounceTimer)
      this.searchDebounceTimer = setTimeout(function () {
        _this.searchKeywordDebounced = _this.searchKeyword
        _this.searchDebounceTimer = null
      }, 1200)
    },
    search(e) {
      const val = e && e.value != null ? e.value : this.searchKeyword
      this.searchKeyword = val
      this.searchKeywordDebounced = val
    },
    openFilter() {
      this.filterForm = {
        code: this.query.code || '',
        supplierName: this.query.supplierName || '',
        dateRange: Array.isArray(this.query.dateRange) ? this.query.dateRange.slice(0) : []
      }
      this.filterVisible = true
    },
    onFilterReset(payload) {
      this.filterForm = payload
    },
    onFilterConfirm(payload) {
      this.query = {
        code: payload.code || '',
        supplierName: payload.supplierName || '',
        dateRange: Array.isArray(payload.dateRange) ? payload.dateRange : []
      }
    },
    fetchList(_ref2) {
      var _this2 = this
      var pageIndex = _ref2.pageIndex
      var pageSize = _ref2.pageSize
      var query = _ref2.query
      var extra = _ref2.extra
      const q = query || {}
      const e = extra || {}
      const range = Array.isArray(q.dateRange) ? q.dateRange : []
      const keyword = e.keyword || ''
      const params = {
        pageIndex: pageIndex,
        pageSize: pageSize,
        code: q.code || '',
        supplierName: q.supplierName || '',
        startDate: range && range.length === 2 ? range[0] : '',
        endDate: range && range.length === 2 ? range[1] : ''
      }
      if (keyword) {
        if (!params.code) params.code = keyword
        if (!params.supplierName) params.supplierName = keyword
      }
      return procurementPlanChangeQueryApi(params).then(function (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
        records = records.map(function (it) { return Object.assign({}, it) })
        return { records: records, totalCount: totalCount, hasNext: hasNext }
      }).catch(function () {
        _this2.onCardError()
        return { records: [], totalCount: 0, hasNext: false }
      })
    },
    onCardClick(item) {
      uni.navigateTo({ url: '/pages/schedule-plan/detail?id=' + item.id })
    }
  }
}
</script>

<style lang="scss" scoped>
.page {
  display: flex;
  flex-direction: column;
  height: 100vh;
}
.plan-list-fixed {
  position: fixed;
  top: 96rpx;
  left: 0;
  right: 0;
  z-index: 2;
  background: #fff;
  .search-row {
    display: flex;
    align-items: center;
    padding: 16rpx 32rpx;
    .uni-searchbar { padding: 0; flex: 1; }
    .tool-icons {
      display: flex;
      .tool-icon { width: 48rpx; height: 48rpx; display: block; margin-left: 32rpx; }
    }
  }
}
::v-deep .uni-searchbar__box { height: 80rpx !important; justify-content: start; .uni-searchbar__box-search-input { font-size: 32rpx !important; } }
.list-box {
  flex: 1;
  padding-top: 140rpx;
  .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 {
      position: absolute;
      top: -32rpx;
      right: -12rpx;
      height: 48rpx;
      line-height: 48rpx;
      font-weight: 600;
      color: #fff;
      font-size: 24rpx;
      padding: 0 14rpx;
      border-radius: 6rpx;
      box-sizing: content-box;

      &.status_AUDIT {
        background: $theme-primary;
      }

      &.status_PASS {
        background: #2BA471;
      }

      &.status_REFUSE {
        background: #D54941;
      }

      &.status_CANCEL {
        background: #E7E7E7;
        color: rgba(0, 0, 0, 0.9);
      }
    }
  }
  .info-row {
    display: flex;
    align-items: center;
    color: rgba(0,0,0,0.6);
    font-size: 28rpx;
    margin-bottom: 24rpx;
    height: 32rpx;
    &:last-child { margin-bottom: 0; }
    text {
      width: 50%;
      &:last-child { color: rgba(0,0,0,0.9); width: 50%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    }
  }
}
.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; }
  .input {
    width: 100%;
    height: 72rpx;
    line-height: 72rpx;
    padding: 0 24rpx;
    border: 1rpx solid #E7E7E7;
    border-radius: 8rpx;
    font-size: 28rpx;
    color: rgba(0,0,0,0.9);
    background: #fff;
    box-sizing: border-box;
  }
}
</style>