Commit d16c90b999251b72946674fe7415eef96f1728f6

Authored by 李宇
1 parent cad1ab95

feat(statistics): 新增品项门店统计页面

新增 form11.vue 页面,实现按品项统计门店相关数据的功能。
页面包含筛选条件(品项、时间范围)、统计数据展示卡片及详细表格。
支持默认时间范围设置、数据查询、重置等功能,并对金额进行格式化显示。

fix(dailyReport): 修复日报表请求参数缺失问题

在两个 fetch 请求中补充了 managerType 参数,确保接口调用时传递正确的管理类型。
antis-ncc-admin/src/views/statisticsList/form11.vue 0 → 100644
  1 +<template>
  2 + <div class="NCC-common-layout">
  3 + <div class="NCC-common-layout-center">
  4 + <!-- 筛选条件 -->
  5 + <el-row class="NCC-common-search-box" :gutter="16">
  6 + <el-form @submit.native.prevent>
  7 + <el-col :span="6">
  8 + <el-form-item label="品项" required>
  9 + <el-select
  10 + v-model="query.itemId"
  11 + placeholder="请选择品项"
  12 + filterable
  13 + :style='{"width":"100%"}'>
  14 + <el-option
  15 + v-for="item in itemOptions"
  16 + :key="item.id"
  17 + :label="item.xmmc"
  18 + :value="item.id">
  19 + </el-option>
  20 + </el-select>
  21 + </el-form-item>
  22 + </el-col>
  23 + <el-col :span="6">
  24 + <el-form-item label="开始时间" required>
  25 + <el-date-picker
  26 + v-model="query.startTime"
  27 + type="datetime"
  28 + value-format="yyyy-MM-dd HH:mm:ss"
  29 + format="yyyy-MM-dd HH:mm:ss"
  30 + placeholder="开始时间"
  31 + />
  32 + </el-form-item>
  33 + </el-col>
  34 + <el-col :span="6">
  35 + <el-form-item label="结束时间" required>
  36 + <el-date-picker
  37 + v-model="query.endTime"
  38 + type="datetime"
  39 + value-format="yyyy-MM-dd HH:mm:ss"
  40 + format="yyyy-MM-dd HH:mm:ss"
  41 + placeholder="结束时间"
  42 + />
  43 + </el-form-item>
  44 + </el-col>
  45 + <el-col :span="6">
  46 + <el-form-item>
  47 + <el-button type="primary" icon="el-icon-search" @click="search()">查询</el-button>
  48 + <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button>
  49 + </el-form-item>
  50 + </el-col>
  51 + </el-form>
  52 + </el-row>
  53 +
  54 + <!-- 统计卡片 -->
  55 + <div class="statistics-cards" v-if="summaryData">
  56 + <el-row :gutter="16">
  57 + <el-col :span="6">
  58 + <div class="statistics-card">
  59 + <div class="card-icon">
  60 + <i class="el-icon-shop"></i>
  61 + </div>
  62 + <div class="card-content">
  63 + <div class="card-title">门店总数</div>
  64 + <div class="card-value">{{ summaryData.totalStores || 0 }}</div>
  65 + </div>
  66 + </div>
  67 + </el-col>
  68 + <el-col :span="6">
  69 + <div class="statistics-card">
  70 + <div class="card-icon">
  71 + <i class="el-icon-document"></i>
  72 + </div>
  73 + <div class="card-content">
  74 + <div class="card-title">开单数合计</div>
  75 + <div class="card-value">{{ summaryData.totalBillingCount || 0 }}</div>
  76 + </div>
  77 + </div>
  78 + </el-col>
  79 + <el-col :span="6">
  80 + <div class="statistics-card">
  81 + <div class="card-icon">
  82 + <i class="el-icon-menu"></i>
  83 + </div>
  84 + <div class="card-content">
  85 + <div class="card-title">项目数合计</div>
  86 + <div class="card-value">{{ summaryData.totalProjectCount || 0 }}</div>
  87 + </div>
  88 + </div>
  89 + </el-col>
  90 + <el-col :span="6">
  91 + <div class="statistics-card">
  92 + <div class="card-icon">
  93 + <i class="el-icon-money"></i>
  94 + </div>
  95 + <div class="card-content">
  96 + <div class="card-title">实付金额合计</div>
  97 + <div class="card-value">¥{{ formatMoney(summaryData.totalActualAmount) }}</div>
  98 + </div>
  99 + </div>
  100 + </el-col>
  101 + </el-row>
  102 + <el-row :gutter="16" style="margin-top: 16px;">
  103 + <el-col :span="6">
  104 + <div class="statistics-card">
  105 + <div class="card-icon">
  106 + <i class="el-icon-delete"></i>
  107 + </div>
  108 + <div class="card-content">
  109 + <div class="card-title">退款金额合计</div>
  110 + <div class="card-value">¥{{ formatMoney(summaryData.totalRefundAmount) }}</div>
  111 + </div>
  112 + </div>
  113 + </el-col>
  114 + </el-row>
  115 + </div>
  116 +
  117 + <!-- 数据表格 -->
  118 + <div class="NCC-common-layout-main NCC-flex-main">
  119 + <NCC-table v-loading="listLoading" :data="list" has-c>
  120 + <el-table-column prop="StoreName" label="门店名称" >
  121 + <template slot-scope="scope">
  122 + <i class="el-icon-shop" style="margin-right: 4px; color: #409EFF;"></i>
  123 + <span>{{ scope.row.StoreName || '无' }}</span>
  124 + </template>
  125 + </el-table-column>
  126 + <el-table-column prop="BillingCount" label="开单数" >
  127 + <template slot-scope="scope">
  128 + <i class="el-icon-document" style="margin-right: 4px; color: #409EFF;"></i>
  129 + <span>{{ scope.row.BillingCount || 0 }}</span>
  130 + </template>
  131 + </el-table-column>
  132 + <el-table-column prop="ProjectCount" label="项目数" >
  133 + <template slot-scope="scope">
  134 + <i class="el-icon-menu" style="margin-right: 4px; color: #67C23A;"></i>
  135 + <span>{{ scope.row.ProjectCount || 0 }}</span>
  136 + </template>
  137 + </el-table-column>
  138 + <el-table-column prop="ActualAmount" label="实付金额" >
  139 + <template slot-scope="scope">
  140 + <i class="el-icon-money" style="margin-right: 4px; color: #67C23A;"></i>
  141 + <span class="amount-paid">¥{{ formatMoney(scope.row.ActualAmount) }}</span>
  142 + </template>
  143 + </el-table-column>
  144 + <el-table-column prop="RefundAmount" label="退款金额" >
  145 + <template slot-scope="scope">
  146 + <i class="el-icon-delete" style="margin-right: 4px; color: #F56C6C;"></i>
  147 + <span class="amount-debt">¥{{ formatMoney(scope.row.RefundAmount) }}</span>
  148 + </template>
  149 + </el-table-column>
  150 + </NCC-table>
  151 + </div>
  152 + </div>
  153 + </div>
  154 +</template>
  155 +
  156 +<script>
  157 +import request from '@/utils/request'
  158 +
  159 +export default {
  160 + name: 'StoreItemStatistics',
  161 + data() {
  162 + return {
  163 + query: {
  164 + itemId: undefined,
  165 + startTime: undefined,
  166 + endTime: undefined
  167 + },
  168 + itemOptions: [],
  169 + summaryData: null,
  170 + list: [],
  171 + listLoading: false
  172 + }
  173 + },
  174 + created() {
  175 + this.setDefaultTimeRange()
  176 + this.initItemOptions()
  177 + },
  178 + methods: {
  179 + // 设置默认时间范围(本月1号到现在)
  180 + setDefaultTimeRange() {
  181 + const now = new Date()
  182 + const firstDayOfMonth = new Date(now.getFullYear(), now.getMonth(), 1)
  183 +
  184 + this.query.startTime = this.formatDateTime(firstDayOfMonth.getTime())
  185 + this.query.endTime = this.formatDateTime(now.getTime())
  186 + },
  187 +
  188 + // 初始化品项选项
  189 + initItemOptions() {
  190 + request({
  191 + url: '/api/Extend/LqXmzl',
  192 + method: 'GET',
  193 + data: {
  194 + currentPage: 1,
  195 + pageSize: 1000
  196 + }
  197 + }).then(res => {
  198 + this.itemOptions = res.data.list || []
  199 + }).catch(err => {
  200 + console.error('获取品项列表失败:', err)
  201 + })
  202 + },
  203 +
  204 + // 查询数据
  205 + search() {
  206 + // 验证必填参数
  207 + if (!this.query.itemId) {
  208 + this.$message({
  209 + type: 'warning',
  210 + message: '请选择品项',
  211 + duration: 1500
  212 + })
  213 + return
  214 + }
  215 +
  216 + if (!this.query.startTime) {
  217 + this.$message({
  218 + type: 'warning',
  219 + message: '请选择开始时间',
  220 + duration: 1500
  221 + })
  222 + return
  223 + }
  224 +
  225 + if (!this.query.endTime) {
  226 + this.$message({
  227 + type: 'warning',
  228 + message: '请选择结束时间',
  229 + duration: 1500
  230 + })
  231 + return
  232 + }
  233 +
  234 + this.listLoading = true
  235 +
  236 + const params = {
  237 + itemId: this.query.itemId,
  238 + startTime: this.query.startTime,
  239 + endTime: this.query.endTime
  240 + }
  241 +
  242 + request({
  243 + url: '/api/Extend/lqxmzl/get-item-store-statistics',
  244 + method: 'POST',
  245 + data: params
  246 + }).then(res => {
  247 + // 兼容不同的返回格式:可能是 res.data.data 或 res.data 直接是数组
  248 + let dataList = []
  249 + if (res.data) {
  250 + if (Array.isArray(res.data)) {
  251 + dataList = res.data
  252 + } else if (res.data.data && Array.isArray(res.data.data)) {
  253 + dataList = res.data.data
  254 + } else if (Array.isArray(res.data.list)) {
  255 + dataList = res.data.list
  256 + }
  257 + }
  258 +
  259 + this.list = dataList
  260 + this.calculateSummary()
  261 + this.listLoading = false
  262 + }).catch(err => {
  263 + console.error('查询失败:', err)
  264 + this.$message({
  265 + type: 'error',
  266 + message: '查询失败,请重试',
  267 + duration: 1500
  268 + })
  269 + this.listLoading = false
  270 + })
  271 + },
  272 +
  273 + // 计算汇总数据
  274 + calculateSummary() {
  275 + if (!this.list || this.list.length === 0) {
  276 + this.summaryData = null
  277 + return
  278 + }
  279 +
  280 + const summary = {
  281 + totalStores: this.list.length,
  282 + totalBillingCount: this.list.reduce((sum, item) => sum + (item.BillingCount || 0), 0),
  283 + totalProjectCount: this.list.reduce((sum, item) => sum + (item.ProjectCount || 0), 0),
  284 + totalActualAmount: this.list.reduce((sum, item) => sum + (item.ActualAmount || 0), 0),
  285 + totalRefundAmount: this.list.reduce((sum, item) => sum + (item.RefundAmount || 0), 0)
  286 + }
  287 +
  288 + this.summaryData = summary
  289 + },
  290 +
  291 + // 重置查询条件
  292 + reset() {
  293 + this.query = {
  294 + itemId: undefined,
  295 + startTime: undefined,
  296 + endTime: undefined
  297 + }
  298 + this.setDefaultTimeRange()
  299 + this.summaryData = null
  300 + this.list = []
  301 + },
  302 +
  303 + // 格式化金额
  304 + formatMoney(amount) {
  305 + if (!amount && amount !== 0) return '0.00'
  306 + return Number(amount).toFixed(2)
  307 + },
  308 +
  309 + // 格式化日期时间(用于API传参)
  310 + formatDateTime(timestamp) {
  311 + if (!timestamp) return ''
  312 + const date = new Date(timestamp)
  313 + const year = date.getFullYear()
  314 + const month = String(date.getMonth() + 1).padStart(2, '0')
  315 + const day = String(date.getDate()).padStart(2, '0')
  316 + const hours = String(date.getHours()).padStart(2, '0')
  317 + const minutes = String(date.getMinutes()).padStart(2, '0')
  318 + const seconds = String(date.getSeconds()).padStart(2, '0')
  319 + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
  320 + }
  321 + }
  322 +}
  323 +</script>
  324 +
  325 +<style lang="scss" scoped>
  326 +.statistics-cards {
  327 + margin-bottom: 20px;
  328 +}
  329 +
  330 +.statistics-card {
  331 + height: 100px;
  332 + padding: 12px;
  333 + border-radius: 12px;
  334 + background: #fff;
  335 + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  336 + display: flex;
  337 + align-items: center;
  338 +
  339 + .card-icon {
  340 + width: 60px;
  341 + height: 60px;
  342 + border-radius: 8px;
  343 + background: linear-gradient(135deg, #409EFF, #67C23A);
  344 + display: flex;
  345 + align-items: center;
  346 + justify-content: center;
  347 + margin-right: 16px;
  348 +
  349 + i {
  350 + font-size: 24px;
  351 + color: #fff;
  352 + }
  353 + }
  354 +
  355 + .card-content {
  356 + flex: 1;
  357 +
  358 + .card-title {
  359 + font-size: 14px;
  360 + color: #909399;
  361 + margin-bottom: 8px;
  362 + }
  363 +
  364 + .card-value {
  365 + font-size: 24px;
  366 + font-weight: bold;
  367 + color: #303133;
  368 + }
  369 + }
  370 +}
  371 +
  372 +.amount-paid {
  373 + color: #67C23A;
  374 + font-weight: 500;
  375 +}
  376 +
  377 +.amount-debt {
  378 + color: #F56C6C;
  379 + font-weight: 500;
  380 +}
  381 +</style>
  382 +
... ...
绿纤html/dailyReportnew.html
... ... @@ -1210,7 +1210,8 @@
1210 1210 },
1211 1211 body: JSON.stringify({
1212 1212 startTime: startDate + " 00:00:00",
1213   - endTime: endDate + " 23:59:59"
  1213 + endTime: endDate + " 23:59:59",
  1214 + managerType:'0'
1214 1215 })
1215 1216 });
1216 1217 if (!response.ok) throw new Error('数据加载失败');
... ... @@ -1232,7 +1233,8 @@
1232 1233 },
1233 1234 body: JSON.stringify({
1234 1235 startTime: startDate + " 00:00:00",
1235   - endTime: endDate + " 23:59:59"
  1236 + endTime: endDate + " 23:59:59",
  1237 + managerType:'0'
1236 1238 })
1237 1239 });
1238 1240 if (!response.ok) throw new Error('数据加载失败');
... ...