form15.vue 14.1 KB
<template>
  <div class="NCC-common-layout">
    <div class="NCC-common-layout-center">
      <!-- 筛选条件 -->
      <el-row class="NCC-common-search-box" :gutter="16">
        <el-form @submit.native.prevent>
          <el-col :span="6">
            <el-form-item label="开始时间">
              <el-date-picker 
                v-model="query.startTime" 
                type="datetime" 
                value-format="timestamp" 
                format="yyyy-MM-dd HH:mm:ss" 
                placeholder="开始时间"
              />
            </el-form-item>
          </el-col>
          <el-col :span="6">
            <el-form-item label="结束时间">
              <el-date-picker 
                v-model="query.endTime" 
                type="datetime" 
                value-format="timestamp" 
                format="yyyy-MM-dd HH:mm:ss" 
                placeholder="结束时间"
              />
            </el-form-item>
          </el-col>
          <el-col :span="6">
            <el-form-item label="门店">
              <el-select
                v-model="query.storeIds"
                placeholder="请选择门店"
                multiple
                filterable
                clearable
                :style='{"width":"100%"}'>
                <el-option
                  v-for="store in storeOptions"
                  :key="store.id"
                  :label="store.dm"
                  :value="store.id">
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="6">
            <el-form-item label="拓客活动">
              <el-select
                v-model="query.eventId"
                placeholder="请选择拓客活动"
                filterable
                clearable
                :style='{"width":"100%"}'>
                <el-option
                  v-for="event in eventOptions"
                  :key="event.id"
                  :label="event.eventName"
                  :value="event.id">
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="6">
            <el-form-item>
              <el-button type="primary" icon="el-icon-search" @click="search()">查询</el-button>
              <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button>
            </el-form-item>
          </el-col>
        </el-form>
      </el-row>

      <!-- 数据表格 -->
      <div class="NCC-common-layout-main NCC-flex-main">
        <NCC-table v-loading="listLoading" :data="list" has-c>
          <el-table-column prop="LeadCustomerId" label="线索池客户ID" min-width="170">
            <template slot-scope="scope">
              <i class="el-icon-user-solid" style="margin-right: 4px; color: #409EFF;"></i>
              <span>{{ scope.row.LeadCustomerId || '无' }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="CustomerName" label="客户名称" min-width="120">
            <template slot-scope="scope">
              <i class="el-icon-user" style="margin-right: 4px; color: #409EFF;"></i>
              <span>{{ scope.row.CustomerName || '无' }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="Phone" label="电话" min-width="130">
            <template slot-scope="scope">
              <i class="el-icon-phone" style="margin-right: 4px; color: #409EFF;"></i>
              <span>{{ scope.row.Phone || '无' }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="StoreName" label="门店名称" min-width="150">
            <template slot-scope="scope">
              <i class="el-icon-shop" style="margin-right: 4px; color: #409EFF;"></i>
              <span>{{ scope.row.StoreName || '无' }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="ExpansionTime" label="拓客时间" min-width="160">
            <template slot-scope="scope">
              <i class="el-icon-time" style="margin-right: 4px; color: #409EFF;"></i>
              <span>{{ formatDateTime(scope.row.ExpansionTime) || '无' }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="HasInvite" label="是否邀约" min-width="100">
            <template slot-scope="scope">
              <el-tag 
                :type="scope.row.HasInvite === '是' ? 'success' : 'info'" 
                size="small">
                {{ scope.row.HasInvite || '无' }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="HasAppointment" label="是否预约" min-width="100">
            <template slot-scope="scope">
              <el-tag 
                :type="scope.row.HasAppointment === '是' ? 'success' : 'info'" 
                size="small">
                {{ scope.row.HasAppointment || '无' }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="HasConsume" label="是否消耗" min-width="100">
            <template slot-scope="scope">
              <el-tag 
                :type="scope.row.HasConsume === '是' ? 'success' : 'info'" 
                size="small">
                {{ scope.row.HasConsume || '无' }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="HasBilling" label="是否开单" min-width="100">
            <template slot-scope="scope">
              <el-tag 
                :type="scope.row.HasBilling === '是' ? 'success' : 'info'" 
                size="small">
                {{ scope.row.HasBilling || '无' }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="NoBillingReason" label="未开单原因" min-width="150" show-overflow-tooltip>
            <template slot-scope="scope">
              <i class="el-icon-warning-outline" style="margin-right: 4px; color: #F56C6C;"></i>
              <span>{{ scope.row.NoBillingReason || '无' }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="BillingAmount" label="开单金额" min-width="120">
            <template slot-scope="scope">
              <i class="el-icon-money" style="margin-right: 4px; color: #67C23A;"></i>
              <span class="amount-paid">¥{{ formatMoney(scope.row.BillingAmount) }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="BillingItems" label="开单项目" min-width="200" show-overflow-tooltip>
            <template slot-scope="scope">
              <i class="el-icon-goods" style="margin-right: 4px; color: #409EFF;"></i>
              <span>{{ scope.row.BillingItems || '无' }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="ActualAppointmentCount" label="实际预约次数" min-width="130">
            <template slot-scope="scope">
              <i class="el-icon-date" style="margin-right: 4px; color: #67C23A;"></i>
              <span>{{ scope.row.ActualAppointmentCount !== null && scope.row.ActualAppointmentCount !== undefined ? scope.row.ActualAppointmentCount : 0 }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="ActualConsumeCount" label="实际消耗次数" min-width="130">
            <template slot-scope="scope">
              <i class="el-icon-shopping-cart-2" style="margin-right: 4px; color: #67C23A;"></i>
              <span>{{ scope.row.ActualConsumeCount !== null && scope.row.ActualConsumeCount !== undefined ? scope.row.ActualConsumeCount : 0 }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="ActualBillingCount" label="实际开单次数" min-width="130">
            <template slot-scope="scope">
              <i class="el-icon-document" style="margin-right: 4px; color: #67C23A;"></i>
              <span>{{ scope.row.ActualBillingCount !== null && scope.row.ActualBillingCount !== undefined ? scope.row.ActualBillingCount : 0 }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="Analysis" label="分析说明" width="350" show-overflow-tooltip>
            <template slot-scope="scope">
              <span>{{ scope.row.Analysis || '无' }}</span>
            </template>
          </el-table-column>
        </NCC-table>
        <pagination 
          v-show="total > 0" 
          :total="total" 
          :page.sync="listQuery.pageIndex"
          :limit.sync="listQuery.pageSize" 
          @pagination="handlePagination" 
        />
      </div>
    </div>
  </div>
</template>

<script>
import request from '@/utils/request'
import Pagination from '@/components/Pagination'
import { getLqEventList } from '@/api/extend/lqevent'

export default {
  name: 'LeadCustomerStatistics',
  components: {
    Pagination
  },
  data() {
    return {
      query: {
        startTime: undefined,
        endTime: undefined,
        storeIds: [],
        eventId: undefined
      },
      storeOptions: [],
      eventOptions: [],
      list: [],
      listLoading: false,
      total: 0,
      listQuery: {
        pageIndex: 1,
        pageSize: 10
      }
    }
  },
  created() {
    this.setDefaultTimeRange()
    this.initStoreOptions()
    this.initEventOptions()
    this.search()
  },
  methods: {
    // 设置默认时间范围(本月1号到现在)
    setDefaultTimeRange() {
      const now = new Date()
      const firstDayOfMonth = new Date(now.getFullYear(), now.getMonth(), 1)
      
      this.query.startTime = firstDayOfMonth.getTime()
      this.query.endTime = now.getTime()
    },

    // 初始化门店选项
    initStoreOptions() {
      request({
        url: '/api/Extend/LqMdxx',
        method: 'GET',
        data: {
          currentPage: 1,
          pageSize: 1000
        }
      }).then(res => {
        if (res.data && res.data.list) {
          this.storeOptions = res.data.list
        }
      }).catch(() => {
        this.storeOptions = []
      })
    },

    // 初始化拓客活动选项
    initEventOptions() {
      getLqEventList({
        page: 1,
        rows: 1000
      }).then(res => {
        if (res.code == 200 && res.data && res.data.list) {
          this.eventOptions = res.data.list
        } else {
          this.eventOptions = []
        }
      }).catch(() => {
        this.eventOptions = []
      })
    },

    // 查询数据
    search() {
      this.listLoading = true

      const params = {
        pageIndex: this.listQuery.pageIndex,
        pageSize: this.listQuery.pageSize
      }

      if (this.query.startTime) {
        params.startTime = this.formatDateTimeISO(this.query.startTime)
      }

      if (this.query.endTime) {
        params.endTime = this.formatDateTimeISO(this.query.endTime)
      }

      if (this.query.storeIds && this.query.storeIds.length > 0) {
        params.storeIds = this.query.storeIds
      }

      if (this.query.eventId) {
        params.eventId = this.query.eventId
      }

      request({
        url: '/api/Extend/lqstatistics/get-lead-customer-statistics-list',
        method: 'POST',
        data: params
      }).then(res => {
        if (res.data && res.data.list) {
          this.list = res.data.list
          this.total = (res.data.pagination && res.data.pagination.total) || res.data.list.length || 0
        } else {
          this.list = []
          this.total = 0
        }
        this.listLoading = false
      }).catch(err => {
        console.error('查询失败:', err)
        this.$message({
          type: 'error',
          message: '查询失败,请重试',
          duration: 1500
        })
        this.listLoading = false
        this.list = []
        this.total = 0
      })
    },

    // 处理分页变化
    handlePagination({ page, limit }) {
      this.listQuery.pageIndex = page
      this.listQuery.pageSize = limit
      this.search()
    },

    // 重置查询条件
    reset() {
      this.query = {
        startTime: undefined,
        endTime: undefined,
        storeIds: [],
        eventId: undefined
      }
      this.setDefaultTimeRange()
      this.listQuery.pageIndex = 1
      this.listQuery.pageSize = 10
      this.list = []
      this.total = 0
      this.search()
    },

    // 格式化金额
    formatMoney(amount) {
      if (!amount && amount !== 0) return '0.00'
      return Number(amount).toFixed(2)
    },

    // 格式化日期时间(用于API传参,ISO格式,保持本地时间不转换时区)
    formatDateTimeISO(timestamp) {
      if (!timestamp) return ''
      const date = new Date(timestamp)
      // 生成本地时间的 ISO 格式字符串,不进行时区转换
      // 使用本地时间的各个部分,格式化为 ISO 格式,末尾加 Z 以符合接口要求
      // 注意:虽然 Z 通常表示 UTC,但这里时间值是本地的,后端应该按本地时间处理
      const year = date.getFullYear()
      const month = String(date.getMonth() + 1).padStart(2, '0')
      const day = String(date.getDate()).padStart(2, '0')
      const hours = String(date.getHours()).padStart(2, '0')
      const minutes = String(date.getMinutes()).padStart(2, '0')
      const seconds = String(date.getSeconds()).padStart(2, '0')
      const milliseconds = String(date.getMilliseconds()).padStart(3, '0')
      // 使用本地时间的值,格式化为 ISO 格式(末尾加 Z 以符合接口格式要求)
      return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${milliseconds}Z`
    },

    // 格式化日期时间(用于显示)
    formatDateTime(dateTime) {
      if (!dateTime) return '无'
      try {
        const date = new Date(dateTime)
        const year = date.getFullYear()
        const month = String(date.getMonth() + 1).padStart(2, '0')
        const day = String(date.getDate()).padStart(2, '0')
        const hours = String(date.getHours()).padStart(2, '0')
        const minutes = String(date.getMinutes()).padStart(2, '0')
        const seconds = String(date.getSeconds()).padStart(2, '0')
        return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
      } catch (e) {
        return dateTime
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.amount-paid {
  color: #67C23A;
  font-weight: 500;
}
</style>