LaveyD 1 неделя назад
Родитель
Сommit
af70c3aeef

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "ticket",
-  "version": "1.0.7",
+  "version": "1.0.9",
   "private": true,
   "description": "多巴胺科技",
   "author": "",

+ 15 - 15
src/pages/checkerManage/teamManage.vue

@@ -87,20 +87,6 @@
               width="350px"
               label="操作">
               <template slot-scope="scope">
-                <el-link
-                  type="primary"
-                  @click="openEditDialog(scope.row)"
-                >
-                  编辑
-                </el-link>
-
-                <el-link
-                  type="primary"
-                  @click="deleteGroup(scope.row)"
-                >
-                  删除
-                </el-link>
-
                 <el-link
                   type="primary"
                   v-if="hasPermission('team-money-manage')"
@@ -130,6 +116,20 @@
                 >
                   账户记录
                 </el-link>
+
+                <el-link
+                  type="primary"
+                  @click="openEditDialog(scope.row)"
+                >
+                  编辑
+                </el-link>
+
+                <el-link
+                  type="primary"
+                  @click="deleteGroup(scope.row)"
+                >
+                  删除
+                </el-link>
               </template>
             </el-table-column>
           </el-table>
@@ -221,7 +221,7 @@
           label="充值金额"
           verify
           prop="amount">
-          <el-input-number v-model="moneyForm.amount" :min="0.01" :precision="2" :step="1"></el-input-number>
+          <el-input-number v-model="moneyForm.amount" :min="undefined" :precision="2" :step="1"></el-input-number>
         </el-form-item>
         <el-form-item
           label="备注"

+ 17 - 0
src/pages/queryReport/OrderStatistic.vue

@@ -169,6 +169,16 @@
         </el-select>
       </el-form-item>
 
+      <el-form-item
+        label="是否去除小计"
+        prop="removeSubtotal">
+        <el-select
+          v-model="form.removeSubtotal">
+          <el-option :value="false" label="否"></el-option>
+          <el-option :value="true" label="是"></el-option>
+        </el-select>
+      </el-form-item>
+
       <el-form-item
         label="分组筛选"
         style="width:100%">
@@ -455,6 +465,7 @@ export default {
         orderCategory: '',
         otaSourceNameList: [], // 渠道id
         payChannelList: [],
+        removeSubtotal: false,
         adminIdList: [], // 售票员id
         scenicIdList: [], // 景点id
         groupByList: ['ticketTypeName'],
@@ -739,6 +750,12 @@ export default {
       }
     },
     objectSpanMethod ({ row, column, rowIndex, columnIndex }) {
+      if (this.form.removeSubtotal) {
+        return {
+          rowspan: 1,
+          colspan: 1
+        }
+      }
       if (columnIndex < this.finalGroup.length) {
         if (!(this.OrderIndexArr[columnIndex] && this.OrderIndexArr[columnIndex].length)) return
 

+ 2 - 0
src/pages/sellManage/common/orderItemMixin.js

@@ -51,6 +51,8 @@ export default {
     handleConfirm () {
       const currentBatch = this.$parent.$refs.formInfo.currentBatch
 
+      if (!this.currentTicket) return
+
       if (this.category === 'batch') {
         console.log('当前选择的场次', this.currentTicket)
         console.log('当前选择的批次', currentBatch)

+ 3 - 3
src/pages/sellManage/retail/FormInfo.vue

@@ -291,15 +291,15 @@ export default {
     },
     needCheckId () {
       const { category, currentTicket } = this
-      return category === 'member' && currentTicket.checkType.includes('idcard')
+      return category === 'member' && currentTicket?.checkType?.includes('idcard')
     },
     needCheckFace () {
       const { category, currentTicket } = this
-      return category === 'member' && currentTicket.checkType.includes('face')
+      return category === 'member' && currentTicket?.checkType?.includes('face')
     },
     needCheckIc () {
       const { category, currentTicket } = this
-      return category === 'member' && currentTicket.checkType.includes('card')
+      return category === 'member' && currentTicket?.checkType?.includes('card')
     }
   },
   data () {

+ 242 - 1
src/pages/sellManage/retail/OrderInfo.vue

@@ -73,6 +73,13 @@
             @click="handleConfirm">
             确认出票
           </el-button>
+          <el-button
+            type="primary"
+            class="danger-btn item"
+            v-if="payChannel === '现金' && currentTicket"
+            @click="showBatchDialog">
+            批量出票
+          </el-button>
         </div>
       </div>
     </div>
@@ -206,6 +213,69 @@
       :tourist-list="touristList"
       :update-tourist="updateTourist"
       ref="touristList"></TouristList>
+
+    <!-- 批量出票弹框 -->
+    <el-dialog
+      title="批量出票"
+      :visible.sync="batchDialogVisible"
+      width="500px"
+      :close-on-click-modal="false"
+      @close="batchDialogVisible = false">
+      <div v-loading="batchLoading" element-loading-text="正在批量出票中...">
+        <el-alert
+          title="批量出票过程中不要操作!!!"
+          type="error"
+          :closable="false"
+          show-icon>
+        </el-alert>
+
+        <el-form label-width="140px" style="margin-top: 20px;">
+          <el-form-item label="出票模式">
+            <el-radio-group v-model="batchMode">
+              <el-radio value="single" label="single">一码一人</el-radio>
+              <el-radio value="multi" label="multi">一码多人</el-radio>
+            </el-radio-group>
+          </el-form-item>
+
+          <el-form-item label="每张票人数">
+            <el-input-number
+              v-model="batchPeoplePerTicket"
+              :min="1"
+              :max="50"
+              :disabled="batchMode === 'single'"
+              style="width: 200px">
+            </el-input-number>
+            <span style="margin-left: 10px; color: #909399;">人</span>
+            <el-tag v-if="batchMode === 'single'" type="info" style="margin-left: 10px;">一码一人固定为1</el-tag>
+          </el-form-item>
+
+          <el-form-item label="订单数量">
+            <el-input-number
+              v-model="batchOrderCount"
+              :min="1"
+              :max="500"
+              style="width: 200px">
+            </el-input-number>
+            <span style="margin-left: 10px; color: #909399;">个订单</span>
+          </el-form-item>
+
+          <el-form-item v-if="batchLoading" label="出票进度">
+            <el-progress :percentage="Math.round(batchProgress / batchTotal * 100)" :status="batchProgress === batchTotal ? 'success' : undefined"></el-progress>
+            <span style="margin-left: 10px;">{{ batchProgress }} / {{ batchTotal }}</span>
+          </el-form-item>
+        </el-form>
+      </div>
+
+      <span slot="footer">
+        <el-button @click="batchDialogVisible = false" :disabled="batchLoading">取消</el-button>
+        <el-button
+          type="primary"
+          :loading="batchLoading"
+          @click="batchCreateOrderLoop">
+          {{ batchLoading ? '出票中...' : '开始批量出票' }}
+        </el-button>
+      </span>
+    </el-dialog>
   </el-card>
 </template>
 
@@ -216,6 +286,7 @@ import Confirm from './Confirm'
 import PrintSmallTicketCheckbox from '../common/PrintSmallTicketCheckbox'
 import UploadImgList from '../common/uploadImgList'
 import orderItemMixin from '../common/orderItemMixin'
+import { createOrder, getSingleOrder } from '@/api/order'
 
 /** 封装一个公共方法numCalc() 用于浮点数运算 */
 // num1 num2传入两个值 symbol +-*/符号
@@ -320,6 +391,14 @@ export default {
       autoPrintSmallTicket: this.$localStore.get('autoPrintSmallTicket'),
       showDiscountDialog: false,
       currentItem: null,
+      // 批量出票
+      batchDialogVisible: false,
+      batchMode: 'single', // 'single' 一码一人, 'multi' 一码多人
+      batchPeoplePerTicket: 1,
+      batchOrderCount: 1,
+      batchLoading: false,
+      batchProgress: 0,
+      batchTotal: 0,
       discountInfo: {
         ticketPrice: 0,
         discountPrice: 0,
@@ -408,6 +487,166 @@ export default {
       const payment = this.$localStore.get('defaultPayWay') || this.payChannelOptions[0]
 
       this.payChannel = this.payChannelOptions.indexOf(payment) < 0 ? this.payChannelOptions[0] : payment
+    },
+    // 显示批量出票对话框
+    showBatchDialog () {
+      this.formData = this.$parent.$refs.formInfo.form
+      this.batchDialogVisible = true
+      this.batchMode = 'single'
+      this.batchPeoplePerTicket = 1
+      this.batchOrderCount = 1
+      this.batchProgress = 0
+      this.batchTotal = 0
+    },
+    // 构建批量出票参数
+    buildBatchParams () {
+      const params = JSON.parse(JSON.stringify(this.formData))
+      const moment = require('moment')
+
+      params.playDateBegin = moment(this.formData.playDateBegin).format('YYYY-MM-DD')
+      params.playDateEnd = moment(this.formData.playDateEnd).format('YYYY-MM-DD')
+
+      // 从currentTicket构建票务,不使用全局tickets
+      const tickets = []
+      const singlePrice = this.currentTicket.price
+      const playDateBegin = params.playDateBegin
+      const playDateEnd = params.playDateEnd
+
+      if (this.batchMode === 'single') {
+        // 一码一人:每张票一个人
+        for (let i = 0; i < this.batchPeoplePerTicket; i++) {
+          tickets.push({
+            ticketTypeId: this.currentTicket.id,
+            price: singlePrice,
+            originalPrice: singlePrice,
+            checkNum: 1,
+            playDateBegin,
+            playDateEnd,
+            isDiscount: this.currentTicket.isDiscount || 0,
+            batchConfigId: params.batchConfigId || '',
+            guestName: '',
+            guestPhone: '',
+            guestIdentify: '',
+            guestIdentifyType: 0,
+            face: null,
+            ticketNo: '',
+            isStorage: 0,
+            discountPrice: 0,
+            discountDescription: ''
+          })
+        }
+        params.price = this.$NP.times(singlePrice, this.batchPeoplePerTicket)
+      } else {
+        // 一码多人:一张票对应N人
+        tickets.push({
+          ticketTypeId: this.currentTicket.id,
+          price: this.$NP.times(singlePrice, this.batchPeoplePerTicket),
+          originalPrice: singlePrice,
+          checkNum: this.batchPeoplePerTicket,
+          playDateBegin,
+          playDateEnd,
+          isDiscount: this.currentTicket.isDiscount || 0,
+          batchConfigId: params.batchConfigId || '',
+          guestName: '',
+          guestPhone: '',
+          guestIdentify: '',
+          guestIdentifyType: 0,
+          face: null,
+          ticketNo: '',
+          isStorage: 0,
+          discountPrice: 0,
+          discountDescription: '',
+          ticketOrderTravellers: [{
+            name: '',
+            idType: 0,
+            idNum: '',
+            mobile: ''
+          }]
+        })
+        params.price = this.$NP.times(singlePrice, this.batchPeoplePerTicket)
+      }
+
+      params.tickets = tickets
+      params.payChannel = '现金'
+      params.sellDevice = (this.$parent.$refs.formInfo.sellerDevice && this.$parent.$refs.formInfo.sellerDevice.ip4) || ''
+      params.createOrderRemark = this.createOrderRemark
+
+      // 处理ticketNo和face
+      params.tickets.forEach(ticket => {
+        if (params.checkWay !== 5) {
+          ticket.ticketNo = ''
+          ticket.isStorage = 0
+        } else {
+          ticket.isStorage = 1
+        }
+        ticket.face = {
+          faceImg: ticket.face
+        }
+        ticket.batchConfigId = params.batchConfigId || ticket.batchConfigId || ''
+      })
+
+      return params
+    },
+    // 校验批量出票输入
+    validateBatchInput () {
+      if (!this.currentTicket) {
+        this.$message.error('请先选择票种')
+        return false
+      }
+      if (!this.batchPeoplePerTicket || this.batchPeoplePerTicket < 1) {
+        this.$message.error('请输入正确的每张票人数')
+        return false
+      }
+      if (this.batchMode === 'multi' && this.batchPeoplePerTicket > 50) {
+        this.$message.error('一码多人模式下每张票人数不能超过50')
+        return false
+      }
+      if (!this.batchOrderCount || this.batchOrderCount < 1) {
+        this.$message.error('请输入正确的订单数量')
+        return false
+      }
+      return true
+    },
+    // 批量出票循环
+    async batchCreateOrderLoop () {
+      if (!this.validateBatchInput()) return
+
+      const baseParams = this.buildBatchParams()
+      this.batchLoading = true
+      this.batchTotal = this.batchOrderCount
+      this.batchProgress = 0
+
+      for (let i = 0; i < this.batchOrderCount; i++) {
+        try {
+          const freshParams = JSON.parse(JSON.stringify(baseParams))
+          const res = await createOrder(freshParams)
+          this.batchProgress = i + 1
+
+          // 每个订单成功后调用打印
+          const order = res.data?.order
+          if (order?.id) {
+            getSingleOrder(order.id).then(res2 => {
+              this.autoPrintSmallTicket && this.$printSmallTicket(order)
+              const order2 = res2.data
+              this.autoPrint && this.$print(order2)
+            })
+          }
+
+          // 少量延时,避免并发问题
+          if (i < this.batchOrderCount - 1) {
+            await new Promise(resolve => setTimeout(resolve, 300))
+          }
+        } catch (e) {
+          console.error('批量出票失败', e)
+          this.$message.error(`第${i + 1}次出票失败: ${e.message || e.msg || '未知错误'}`)
+          // 继续下一张
+        }
+      }
+
+      this.batchLoading = false
+      this.$message.success(`批量出票完成,成功出票 ${this.batchProgress} 次`)
+      this.batchDialogVisible = false
+      this.$emit('clear')
     }
   },
   watch: {
@@ -424,7 +663,9 @@ export default {
     }
   },
   mounted () {
-    this.payChannel = this.$localStore.get('defaultPayWay') || this.$localStore.get('retailPayment') || this.payChannelOptions[0]
+    const payment = this.$localStore.get('defaultPayWay') || this.$localStore.get('retailPayment') || this.payChannelOptions[0]
+
+    this.payChannel = this.payChannelOptions.indexOf(payment) < 0 ? this.payChannelOptions[0] : payment
   }
 }
 </script>