2
0

2 کامیت‌ها 48b823940d ... af70c3aeef

نویسنده SHA1 پیام تاریخ
  LaveyD af70c3aeef feat: 批量快捷出票 1 هفته پیش
  LaveyD 03e3bcf111 feat: 团队相关调整 1 هفته پیش

+ 1 - 1
package.json

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

+ 20 - 0
src/api/checker.js

@@ -136,6 +136,26 @@ export function updateSubProject (params) {
   return http.post('/admin/subProject/update', { data: params })
 }
 
+// 团队充值
+export function teamRecharge (params) {
+  return http.post('/admin/team/recharge', { data: params })
+}
+
+// 团队扣款
+export function teamDeduct (params) {
+  return http.post('/admin/team/deduct', { data: params })
+}
+
+// 调整授信额度
+export function teamAdjust (params) {
+  return http.post('/admin/team/adjust', { data: params })
+}
+
+// 团队账户记录
+export function getTeamAccountFlow (params) {
+  return http.post('/admin/team/teamAccountFlow', { data: params })
+}
+
 // 查询某个统计的配置字段
 export function getFiledByStatKey (params) {
   return http.post('/admin/statFieldConfig/getFiledByStatKey', { data: params })

+ 250 - 0
src/pages/checkerManage/TeamAccountFlow.vue

@@ -0,0 +1,250 @@
+<template>
+  <div class="form-wrap">
+    <el-form
+      class="search-box"
+      ref="form"
+      :model="form"
+      :inline="true"
+    >
+      <div class="block-title">
+        查询条件
+      </div>
+      <el-form-item
+        label="团队名称"
+        prop="teamName">
+        <el-input v-model="form.teamName" clearable></el-input>
+      </el-form-item>
+      <el-form-item
+        label="交易类型"
+        prop="transactionType">
+        <el-select v-model="form.transactionType" clearable placeholder="请选择">
+          <el-option label="充值" value="RECHARGE"></el-option>
+          <el-option label="扣款" value="DEDUCT"></el-option>
+          <el-option label="消费" value="CONSUME"></el-option>
+          <el-option label="退款" value="REFUND"></el-option>
+          <el-option label="调整授信额度" value="ADJUST"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item
+        label="操作时间起"
+        prop="operateTimeBegin">
+        <el-date-picker
+          v-model="form.operateTimeBegin"
+          type="datetime"
+          placeholder="选择开始时间"
+          value-format="yyyy-MM-dd HH:mm:ss"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item
+        label="操作时间止"
+        prop="operateTimeEnd">
+        <el-date-picker
+          v-model="form.operateTimeEnd"
+          type="datetime"
+          placeholder="选择结束时间"
+          value-format="yyyy-MM-dd HH:mm:ss"
+        ></el-date-picker>
+      </el-form-item>
+      <div class="btn-wrap">
+        <el-button
+          @click="reset">
+          重置
+        </el-button>
+        <el-button
+          type="primary"
+          @click="getList(true)">
+          搜索
+        </el-button>
+        <el-button
+          type="primary"
+          @click="reportExport">
+          导出
+        </el-button>
+      </div>
+    </el-form>
+
+    <div class="table-box">
+      <el-table
+        border
+        v-loading="loading"
+        :data="tableData">
+        <el-table-column
+          prop="createTime"
+          label="创建时间"
+          width="180">
+        </el-table-column>
+        <el-table-column
+          prop="teamName"
+          label="团队名称">
+        </el-table-column>
+        <el-table-column
+          prop="transactionType"
+          label="交易类型"
+          width="120">
+          <template slot-scope="scope">
+            {{ transactionTypeMap[scope.row.transactionType] || scope.row.transactionType }}
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="amount"
+          label="金额"
+          width="100">
+        </el-table-column>
+        <el-table-column
+          prop="balanceBefore"
+          label="变动前余额"
+          width="120">
+        </el-table-column>
+        <el-table-column
+          prop="balanceAfter"
+          label="变动后余额"
+          width="120">
+        </el-table-column>
+        <el-table-column
+          prop="creditLimitBefore"
+          label="变动前授信额度"
+          width="130">
+        </el-table-column>
+        <el-table-column
+          prop="creditLimitAfter"
+          label="变动后授信额度"
+          width="130">
+        </el-table-column>
+        <el-table-column
+          prop="operatorName"
+          label="操作人"
+          width="100">
+        </el-table-column>
+        <el-table-column
+          prop="operateTime"
+          label="操作时间"
+          width="180">
+        </el-table-column>
+        <el-table-column
+          prop="remark"
+          label="备注">
+        </el-table-column>
+        <el-table-column
+          prop="linkOrderId"
+          label="关联订单"
+          width="120">
+          <template slot-scope="scope">
+            <el-link
+              v-if="scope.row.linkOrderId"
+              type="primary"
+              @click="showOrderDetail(scope.row.linkOrderId)"
+            >
+              查看
+            </el-link>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <el-pagination
+        background
+        layout="total, sizes, prev, pager, next, jumper"
+        :current-page.sync="form.pageNum"
+        :page-sizes="[10, 20, 50, 100]"
+        :page-size="form.pageSize"
+        @size-change="handleSizeChange"
+        @current-change="getList()"
+        :total="total"
+      >
+      </el-pagination>
+    </div>
+
+    <OrderDetail ref="orderDetail"></OrderDetail>
+  </div>
+</template>
+
+<script>
+import { getTeamAccountFlow } from '@/api/checker'
+
+export default {
+  name: 'TeamAccountFlow',
+  props: {
+    teamId: { type: [Number, String], default: null },
+    teamName: { type: String, default: '' }
+  },
+  watch: {
+    teamName (val) {
+      this.form.teamName = val
+      this.getList(true)
+    },
+    teamId (val) {
+      this.form.teamId = val
+      this.getList(true)
+    }
+  },
+  data () {
+    return {
+      form: {
+        teamId: this.teamId,
+        teamName: this.teamName,
+        transactionType: '',
+        operateTimeBegin: '',
+        operateTimeEnd: '',
+        pageNum: 1,
+        pageSize: 10
+      },
+      loading: false,
+      tableData: [],
+      total: 0,
+      transactionTypeMap: {
+        RECHARGE: '充值',
+        DEDUCT: '扣款',
+        CONSUME: '消费',
+        REFUND: '退款',
+        ADJUST: '调整授信额度'
+      }
+    }
+  },
+  mounted () {
+    this.getList()
+  },
+  methods: {
+    reset () {
+      this.$refs.form.resetFields()
+      this.form.operateTimeBegin = ''
+      this.form.operateTimeEnd = ''
+    },
+    handleSizeChange (size) {
+      this.form.pageSize = size
+      this.getList()
+    },
+    getList (goFirst) {
+      let params = { ...this.form }
+      goFirst && (params.pageNum = 1)
+      this.loading = true
+      getTeamAccountFlow(params).then(res => {
+        const data = res.data || {}
+        this.tableData = data.records || []
+        this.total = data.total || 0
+        this.loading = false
+      }).catch(() => {
+        this.loading = false
+      })
+    },
+    reportExport () {
+      const params = {
+        ...this.form,
+        export: true
+      }
+      getTeamAccountFlow(params).then(() => {
+        this.$message.success('导出成功。')
+        this.goToDownload()
+      }).catch(() => {
+        this.$message.error('导出失败。')
+      })
+    },
+    async showOrderDetail (orderId) {
+      const { getSingleOrder } = await import('@/api/order')
+      const res = await getSingleOrder(orderId)
+      this.$refs.orderDetail.show(res.data)
+    }
+  }
+}
+</script>
+
+<style scoped>
+</style>

+ 351 - 114
src/pages/checkerManage/teamManage.vue

@@ -1,109 +1,157 @@
 <template>
   <div class="form-wrap">
-    <el-form
-      class="search-box"
-      ref="form"
-      :model="form"
-      :inline="true"
-    >
-      <div class="block-title">
-        查询条件
-      </div>
-      <el-form-item
-        label="团队名称"
-        prop="name">
-        <el-input v-model="form.name" clearable></el-input>
-      </el-form-item>
-      <el-form-item
-        label="联系人"
-        prop="contactName">
-        <el-input v-model="form.contactName" clearable></el-input>
-      </el-form-item>
-      <el-form-item
-        label="联系方式"
-        prop="contactPhone">
-        <el-input v-model="form.contactPhone" clearable></el-input>
-      </el-form-item>
-      <el-form-item
-        label="备注"
-        prop="remark">
-        <el-input v-model="form.remark" clearable></el-input>
-      </el-form-item>
-      <div class="btn-wrap">
-        <el-button
-          @click="reset">
-          重置
-        </el-button>
-        <el-button
-          type="primary"
-          @click="getList(true)">
-          搜索
-        </el-button>
-        <el-button
-          type="primary"
-          @click="openAddDialog(true)">
-          新增团队
-        </el-button>
-      </div>
-    </el-form>
-
-    <div class="table-box">
-      <!-- <div class="block-title">
-        团队列表
-      </div> -->
-      <el-table
-        border
-        v-loading="loading"
-        :data="tableData">
-        <el-table-column
-          prop="name"
-          label="团队名称">
-        </el-table-column>
-        <el-table-column
-          prop="contactName"
-          label="联系人">
-        </el-table-column>
-        <el-table-column
-          prop="contactPhone"
-          label="联系方式">
-        </el-table-column>
-        <el-table-column
-          prop="remark"
-          label="备注">
-        </el-table-column>
-        <el-table-column
-          width="150px"
-          label="操作">
-          <template slot-scope="scope">
-            <el-link
+    <el-tabs v-model="activeTab">
+      <el-tab-pane label="团队管理" name="teamManage">
+        <el-form
+          class="search-box"
+          ref="form"
+          :model="form"
+          :inline="true"
+        >
+          <div class="block-title">
+            查询条件
+          </div>
+          <el-form-item
+            label="团队名称"
+            prop="name">
+            <el-input v-model="form.name" clearable></el-input>
+          </el-form-item>
+          <el-form-item
+            label="联系人"
+            prop="contactName">
+            <el-input v-model="form.contactName" clearable></el-input>
+          </el-form-item>
+          <el-form-item
+            label="联系方式"
+            prop="contactPhone">
+            <el-input v-model="form.contactPhone" clearable></el-input>
+          </el-form-item>
+          <el-form-item
+            label="备注"
+            prop="remark">
+            <el-input v-model="form.remark" clearable></el-input>
+          </el-form-item>
+          <div class="btn-wrap">
+            <el-button
+              @click="reset">
+              重置
+            </el-button>
+            <el-button
               type="primary"
-              @click="openEditDialog(scope.row)"
-            >
-              编辑
-            </el-link>
-
-            <el-link
+              @click="getList(true)">
+              搜索
+            </el-button>
+            <el-button
+              type="primary"
+              @click="openAddDialog">
+              新增团队
+            </el-button>
+            <el-button
               type="primary"
-              @click="deleteGroup(scope.row)">
-              删除
-            </el-link>
-          </template>
-        </el-table-column>
-      </el-table>
-      <el-pagination
-        background
-        layout="total, sizes, prev, pager, next, jumper"
-        :current-page.sync="form.pageNum"
-        :page-sizes="[10, 20, 50, 100]"
-        :page-size="form.pageSize"
-        @size-change="handleSizeChange"
-        @current-change="getList()"
-        :total="total"
-      >
-      </el-pagination>
-    </div>
-    <!-- 弹框 -->
+              @click="reportExport">
+              导出
+            </el-button>
+          </div>
+        </el-form>
+
+        <div class="table-box">
+          <el-table
+            border
+            v-loading="loading"
+            :data="tableData">
+            <el-table-column
+              prop="name"
+              label="团队名称">
+            </el-table-column>
+            <el-table-column
+              prop="contactName"
+              label="联系人">
+            </el-table-column>
+            <el-table-column
+              prop="contactPhone"
+              label="联系方式">
+            </el-table-column>
+            <el-table-column
+              prop="balance"
+              label="余额">
+            </el-table-column>
+            <el-table-column
+              prop="creditLimit"
+              label="授信额度">
+            </el-table-column>
+            <el-table-column
+              prop="remark"
+              label="备注">
+            </el-table-column>
+            <el-table-column
+              width="350px"
+              label="操作">
+              <template slot-scope="scope">
+                <el-link
+                  type="primary"
+                  v-if="hasPermission('team-money-manage')"
+                  @click="openRechargeDialog(scope.row)"
+                >
+                  充值
+                </el-link>
 
+                <el-link
+                  type="primary"
+                  v-if="hasPermission('team-money-manage')"
+                  @click="openDeductDialog(scope.row)"
+                >
+                  扣款
+                </el-link>
+
+                <el-link
+                  type="primary"
+                  v-if="hasPermission('team-money-manage')"
+                  @click="openAdjustDialog(scope.row)"
+                >
+                  调整授信额度
+                </el-link>
+                <el-link
+                  type="primary"
+                  @click="gotoRecord(scope.row)"
+                >
+                  账户记录
+                </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>
+          <el-pagination
+            background
+            layout="total, sizes, prev, pager, next, jumper"
+            :current-page.sync="form.pageNum"
+            :page-sizes="[10, 20, 50, 100]"
+            :page-size="form.pageSize"
+            @size-change="handleSizeChange"
+            @current-change="getList()"
+            :total="total"
+          >
+          </el-pagination>
+        </div>
+      </el-tab-pane>
+      <el-tab-pane label="账户记录" name="accountFlow">
+        <TeamAccountFlow v-if="activeTab === 'accountFlow'" :teamId="selectedTeamId" :teamName="selectedTeamName" />
+      </el-tab-pane>
+    </el-tabs>
+
+    <!-- 新增/编辑团队弹框 -->
     <ElDialog
       :title="`${currentItem.id ? '编辑' : '新增'}团队`"
       width="500px"
@@ -112,7 +160,7 @@
       <el-form
         inline
         :model="currentItem"
-        ref="form"
+        ref="editForm"
         class="form-wrap"
         label-width="150px"
         style="margin-top: 20px">
@@ -132,6 +180,18 @@
           prop="contactPhone">
           <el-input v-model="currentItem.contactPhone"></el-input>
         </el-form-item>
+        <el-form-item
+          v-if="!currentItem.id"
+          label="余额"
+          prop="balance">
+          <el-input-number v-model="currentItem.balance" :precision="2" :step="1" :controls="false" style="width: 200px"></el-input-number>
+        </el-form-item>
+        <el-form-item
+          v-if="!currentItem.id"
+          label="授信额度"
+          prop="creditLimit">
+          <el-input-number v-model="currentItem.creditLimit" :min="0" :precision="2" :step="100" :controls="false" style="width: 200px"></el-input-number>
+        </el-form-item>
         <el-form-item
           label="备注"
           prop="remark">
@@ -143,45 +203,161 @@
         <el-button type="primary" @click="confirmOptGroup">确定</el-button>
       </div>
     </ElDialog>
+
+    <!-- 充值弹框 -->
+    <ElDialog
+      title="充值"
+      width="500px"
+      v-model="rechargeDialogVisible"
+    >
+      <el-form
+        inline
+        :model="moneyForm"
+        ref="moneyForm"
+        class="form-wrap"
+        label-width="150px"
+        style="margin-top: 20px">
+        <el-form-item
+          label="充值金额"
+          verify
+          prop="amount">
+          <el-input-number v-model="moneyForm.amount" :min="undefined" :precision="2" :step="1"></el-input-number>
+        </el-form-item>
+        <el-form-item
+          label="备注"
+          prop="remark">
+          <el-input v-model="moneyForm.remark"></el-input>
+        </el-form-item>
+      </el-form>
+      <div style="margin: 20px; float: right;">
+        <el-button @click="rechargeDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="confirmRecharge">确定</el-button>
+      </div>
+    </ElDialog>
+
+    <!-- 扣款弹框 -->
+    <ElDialog
+      title="扣款"
+      width="500px"
+      v-model="deductDialogVisible"
+    >
+      <el-form
+        inline
+        :model="moneyForm"
+        ref="deductForm"
+        class="form-wrap"
+        label-width="150px"
+        style="margin-top: 20px">
+        <el-form-item
+          label="扣款金额"
+          verify
+          prop="amount">
+          <el-input-number v-model="moneyForm.amount" :min="0.01" :precision="2" :step="1"></el-input-number>
+        </el-form-item>
+        <el-form-item
+          label="备注"
+          prop="remark">
+          <el-input v-model="moneyForm.remark"></el-input>
+        </el-form-item>
+      </el-form>
+      <div style="margin: 20px; float: right;">
+        <el-button @click="deductDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="confirmDeduct">确定</el-button>
+      </div>
+    </ElDialog>
+
+    <!-- 调整授信额度弹框 -->
+    <ElDialog
+      title="调整授信额度"
+      width="500px"
+      v-model="adjustDialogVisible"
+    >
+      <el-form
+        inline
+        :model="adjustForm"
+        ref="adjustForm"
+        class="form-wrap"
+        label-width="150px"
+        style="margin-top: 20px">
+        <el-form-item
+          label="调整后额度"
+          verify
+          prop="amount">
+          <el-input-number v-model="adjustForm.amount" :min="0" :precision="2" :step="100"></el-input-number>
+        </el-form-item>
+        <el-form-item
+          label="备注"
+          prop="remark">
+          <el-input v-model="adjustForm.remark"></el-input>
+        </el-form-item>
+      </el-form>
+      <div style="margin: 20px; float: right;">
+        <el-button @click="adjustDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="confirmAdjust">确定</el-button>
+      </div>
+    </ElDialog>
   </div>
 </template>
 
 <script>
-import { getTeamList, addTeam, updateTeam, deleteTeam } from '@/api/checker'
+import { getTeamList, addTeam, updateTeam, deleteTeam, teamRecharge, teamDeduct, teamAdjust } from '@/api/checker'
 import ElDialog from '@/components/Dialog'
 
-// import ConfigSetting from './deviceManage/ConfigSetting'
+const TeamAccountFlow = () => import('./TeamAccountFlow.vue')
+
 export default {
   data () {
     return {
+      activeTab: 'teamManage',
       form: {
-        name: '', // 名称
+        name: '',
         pageNum: 1,
         pageSize: 10,
-        contactName: '', // 联系人
-        contactPhone: '', // 联系电话
-        remark: '' // 备注
+        contactName: '',
+        contactPhone: '',
+        remark: ''
       },
       loading: false,
+      selectedTeamId: null,
+      selectedTeamName: '',
       tableData: [],
       total: 0,
       dialogVisible: false,
-      configDialogVisible: false,
-      currentItem: {}
+      currentItem: {},
+      rechargeDialogVisible: false,
+      moneyForm: {
+        teamId: null,
+        amount: 0,
+        remark: ''
+      },
+      deductDialogVisible: false,
+      adjustDialogVisible: false,
+      adjustForm: {
+        teamId: null,
+        amount: 0,
+        remark: ''
+      }
     }
   },
   computed: {
     scenicList () {
       return this.$store.state.app.scenicList
+    },
+    permissionList () {
+      return this.$store.state.user.menuList || []
     }
   },
   created () {
     this.getList()
   },
   components: {
-    ElDialog
+    ElDialog,
+    TeamAccountFlow
   },
   methods: {
+    hasPermission (key) {
+      return !!this.permissionList.some(item => item.code === key)
+    },
     reset () {
       this.$refs.form.resetFields()
     },
@@ -189,7 +365,6 @@ export default {
       this.form.pageSize = size
       this.getList()
     },
-    // 获取通道列表
     getList (goFirst) {
       let params = { ...this.form }
 
@@ -202,13 +377,14 @@ export default {
         this.loading = false
       })
     },
-    // 打开弹框
     openAddDialog () {
       this.currentItem = {
         id: '',
         name: '',
         contactName: '',
         contactPhone: '',
+        balance: 0,
+        creditLimit: 0,
         remark: ''
       }
       this.dialogVisible = true
@@ -236,11 +412,72 @@ export default {
         this.dialogVisible = false
         this.getList()
       })
+    },
+    openRechargeDialog (row) {
+      Object.assign(this.moneyForm, {
+        teamId: row.id,
+        amount: 0,
+        remark: ''
+      })
+      this.rechargeDialogVisible = true
+    },
+    confirmRecharge () {
+      teamRecharge(this.moneyForm).then(() => {
+        this.$message.success('充值成功')
+        this.rechargeDialogVisible = false
+        this.getList()
+      })
+    },
+    openDeductDialog (row) {
+      Object.assign(this.moneyForm, {
+        teamId: row.id,
+        amount: 0,
+        remark: ''
+      })
+      this.deductDialogVisible = true
+    },
+    confirmDeduct () {
+      teamDeduct(this.moneyForm).then(() => {
+        this.$message.success('扣款成功')
+        this.deductDialogVisible = false
+        this.getList()
+      })
+    },
+    openAdjustDialog (row) {
+      Object.assign(this.adjustForm, {
+        teamId: row.id,
+        amount: row.creditLimit || 0,
+        remark: ''
+      })
+      this.adjustDialogVisible = true
+    },
+    confirmAdjust () {
+      teamAdjust(this.adjustForm).then(() => {
+        this.$message.success('调整授信额度成功')
+        this.adjustDialogVisible = false
+        this.getList()
+      })
+    },
+    gotoRecord (row) {
+      this.selectedTeamId = row.id
+      this.selectedTeamName = row.name
+      this.activeTab = 'accountFlow'
+    },
+    reportExport () {
+      const params = {
+        ...this.form,
+        export: true
+      }
+      getTeamList(params).then(() => {
+        this.$message.success('导出成功。')
+        this.goToDownload()
+      }).catch(() => {
+        this.$message.error('导出失败。')
+      })
     }
   }
 }
 </script>
 
 <style scoped>
-
 </style>

+ 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
 

+ 13 - 0
src/pages/queryReport/salesQuery.vue

@@ -449,6 +449,19 @@
           prop="ticketNum"
           label="所含票数"
         >
+          <template slot-scope="scope">
+            <div class="order-info-item">
+              <div class="label">
+                总张数:{{ scope.row.ticketNum }}
+              </div>
+            </div>
+            <div
+              class="order-info-item">
+              <div class="label">
+                总人数:{{ scope.row.ticketCheckNum }}
+              </div>
+            </div>
+          </template>
         </el-table-column>
         <!-- <el-table-column
           width="80"

+ 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)

+ 17 - 7
src/pages/sellManage/retail/FormInfo.vue

@@ -36,7 +36,7 @@
             <el-option
               v-for="item in teamList"
               :key="item.id"
-              :label="item.name"
+              :label="`${item.name}-余额¥${item.balance}-信用额度¥${item.creditLimit}`"
               :value="item.id">
             </el-option>
           </el-select>
@@ -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 () {
@@ -383,9 +383,7 @@ export default {
   },
   created () {
     if (this.isTeam) {
-      getTeamList({ pageSize: -1, pageNum: 1 }).then(res => {
-        this.teamList = res.data.records || []
-      })
+      this.getTeams()
       getGuideList({ pageSize: -1, pageNum: 1 }).then(res => {
         this.guideList = res.data.records || []
       })
@@ -395,6 +393,11 @@ export default {
     verifyId,
     verifyPhone,
     randomString,
+    getTeams () {
+      getTeamList({ pageSize: -1, pageNum: 1 }).then(res => {
+        this.teamList = res.data.records || []
+      })
+    },
     setFocus () {
       this.$refs.ticketCount.focus()
       this.$refs.ticketCount.select()
@@ -646,6 +649,12 @@ export default {
       }, 600)
     })
 
+    EventBus.$on('orderCreated', () => {
+      if (this.isTeam) {
+        this.getTeams()
+      }
+    })
+
     if (this.retailLockCheckStyle) {
       this.form.checkWay = this.$localStore.get(`${this.saleType}_checkWay`) || (this.isTeam ? 2 : 1)
     } else {
@@ -654,6 +663,7 @@ export default {
   },
   beforeDestroy () {
     EventBus.$off('ticketChanged')
+    EventBus.$off('orderCreated')
   },
 
   watch: {

+ 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>

+ 8 - 1
src/pages/sellManage/retail/mixin.js

@@ -1,7 +1,14 @@
 export default {
   computed: {
     payChannelOptions () {
-      return this.$store.state.app.payChannelOptions.filter(i => i.indexOf('旅行社') < 0)
+      const options = this.$store.state.app.payChannelOptions.filter(i => i.indexOf('旅行社') < 0) || []
+      const type = this.$route.meta.type
+
+      if (type === 'team') {
+        return options
+      }
+
+      return options.filter(i => i !== '团队授信')
     }
   }
 }