Просмотр исходного кода

feat(ticket): 新增票种多选下拉组件并替换原有选择器

- 创建 TicketTypeSelect 组件支持搜索过滤和一键全选功能
- 在查询报表相关页面中替换原有的 el-select 为新的票种选择组件
- 优化支付渠道API参数传递方式,统一使用params对象
- 修复工行分账信息显示条件,仅在工行支付时显示
- 临时注释导出按钮功能等待后续实现
- 优化部分退单中的票数和金额计算逻辑,避免直接修改原数据
- 修复退款统计中全选逻辑的历史代码问题
LaveyD 1 неделя назад
Родитель
Сommit
f319a890e3

+ 2 - 2
src/api/payChannel.js

@@ -25,8 +25,8 @@ export function deletePayChannel (id) {
 }
 
 // 更新
-export function updatePayChannel ({ status, id }) {
-  return http.post(`/admin/payChannel/updateStatus`, { data: { status, id } })
+export function updatePayChannel (params) {
+  return http.post(`/admin/payChannel/updateStatus`, { data: params })
 }
 
 // 更新

+ 1 - 1
src/components/GlobalComponents/OrderDetail.vue

@@ -401,7 +401,7 @@
               </el-table-column>
             </el-table>
             <!-- 新增功能:工行分账信息,但if判断需要后续改动 -->
-            <div class="title" v-if="orderData.payContext" style="display: flex;justify-content: space-between;margin: 10px 0;">
+            <div class="title" v-if="orderData.payContext && orderData.payChannel === '工行支付'" style="display: flex;justify-content: space-between;margin: 10px 0;">
               工行分账明细
             </div>
             <el-table border :data="orderData.payContext.payGonghangSplitRecordList || []" :max-height="200">

+ 107 - 0
src/components/TicketTypeSelect.vue

@@ -0,0 +1,107 @@
+<template>
+  <el-select
+    :value="displayValue"
+    multiple
+    filterable
+    clearable
+    :filter-method="handleFilter"
+    @input="handleInput"
+    @visible-change="handleVisibleChange">
+    <el-option
+      v-if="!query || filteredOptions.length"
+      :value="0"
+      :label="allLabel">
+    </el-option>
+    <el-option
+      v-for="item in filteredOptions"
+      :key="item.id"
+      :value="item.id"
+      :label="item.name">
+    </el-option>
+  </el-select>
+</template>
+
+<script>
+// 票种多选下拉:支持搜索过滤,且可通过顶部“全部票种”选项一键全选当前搜索结果
+// 对外的 v-model 只包含真实票种id(不含“全部”标记0),页面可直接提交
+export default {
+  name: 'TicketTypeSelect',
+  props: {
+    value: {
+      type: Array,
+      default: () => []
+    },
+    // 票种列表,由外部传入(支持页面按订单类型等先过滤)
+    options: {
+      type: Array,
+      default: () => []
+    }
+  },
+  data () {
+    return {
+      query: ''
+    }
+  },
+  computed: {
+    // 当前搜索词过滤后的票种
+    filteredOptions () {
+      if (!this.query) return this.options
+      const q = String(this.query).toLowerCase()
+      return this.options.filter(i => i.name && String(i.name).toLowerCase().includes(q))
+    },
+    // 是否已全选(全部票种都在已选值中)
+    allChecked () {
+      return this.options.length > 0 && this.options.every(i => this.value.includes(i.id))
+    },
+    allLabel () {
+      return this.query ? `全选搜索结果(${this.filteredOptions.length})` : '全部票种'
+    },
+    // 全选时附加“全部票种”标记用于展示,兼容历史数据中可能存在的0
+    displayValue () {
+      const realIds = this.value.filter(id => id !== 0)
+      return this.allChecked ? [0, ...realIds] : realIds
+    }
+  },
+  methods: {
+    handleFilter (q) {
+      this.query = q
+    },
+    handleVisibleChange (visible) {
+      if (!visible) this.query = ''
+    },
+    handleInput (val) {
+      val = Array.isArray(val) ? val : []
+      const emitVal = (v) => {
+        this.$emit('input', v)
+        this.$emit('change', v)
+      }
+
+      // 清空
+      if (!val.length) {
+        emitVal([])
+        return
+      }
+
+      const hadAll = this.allChecked
+      const hasAll = val.includes(0)
+
+      if (hasAll && !hadAll) {
+        // 勾选“全选”:选中当前搜索结果内的全部票种(保留之前已勾选项)
+        const visibleIds = this.filteredOptions.map(i => i.id)
+        emitVal(Array.from(new Set([...this.value.filter(id => id !== 0), ...visibleIds])))
+      } else if (hadAll && !hasAll) {
+        // 取消“全选”:有搜索词时仅移除当前展示的票种,无搜索词时全部清空
+        if (this.query) {
+          const visibleIds = this.filteredOptions.map(i => i.id)
+          emitVal(this.value.filter(id => id !== 0 && !visibleIds.includes(id)))
+        } else {
+          emitVal([])
+        }
+      } else {
+        // 普通勾选/取消勾选,或在全选状态下取消某一项(此时去掉全选标记)
+        emitVal(val.filter(id => id !== 0))
+      }
+    }
+  }
+}
+</script>

+ 7 - 48
src/pages/queryReport/OrderStatistic.vue

@@ -52,25 +52,12 @@
         label="票种列表"
         prop="ticketTypeIdList"
       >
-        <el-select
+        <TicketTypeSelect
           v-model="form.ticketTypeIdList"
-          multiple
+          :options="ticketTypeList"
           collapse-tags
-          filterable
-          clearable
-          placeholder="请选择"
-          @change="selectAll">
-          <el-option
-            :value="0"
-            label="全部票种">
-          </el-option>
-          <el-option
-            v-for="item in ticketTypeList"
-            :key="item.id"
-            :value="item.id"
-            :label="item.name">
-          </el-option>
-        </el-select>
+          placeholder="请选择">
+        </TicketTypeSelect>
       </el-form-item>
 
       <el-form-item
@@ -413,6 +400,7 @@ import { getTeamList } from '@/api/checker'
 import { addUserConfig, getUserConfig } from '@/api/configSetting'
 import draggable from 'vuedraggable'
 import Tip from '@/components/Tip'
+import TicketTypeSelect from '@/components/TicketTypeSelect'
 import moment from 'moment'
 
 const groupKeys = [
@@ -468,7 +456,8 @@ export default {
   },
   components: {
     Tip,
-    draggable
+    draggable,
+    TicketTypeSelect
   },
   data () {
     return {
@@ -484,7 +473,6 @@ export default {
       selectKeys: ['ticketTypeName', 'adminName', 'payChannel'],
       ticketStatusDic,
       ticketTypeList: [],
-      oldTicketTypeOptions: [[]],
       oldTicketCateOptions: [[]],
       scenicList: [],
       ticketSpotList: [],
@@ -621,35 +609,6 @@ export default {
         return false
       }
     },
-    selectAll (val) {
-      let allValues = this.ticketTypeList.map(i => i.id)
-
-      allValues.unshift(0) // 添加全部
-      // 用来储存上一次的值,可以进行对比
-      const oldVal = this.oldTicketTypeOptions.length === 1 ? [] : this.oldTicketTypeOptions[1]
-
-      // 若是全部选择
-      if (val.includes(0)) this.form.ticketTypeIdList = allValues
-
-      // 取消全部选中  上次有 当前没有 表示取消全选
-      if (oldVal.includes(0) && !val.includes(0)) this.form.ticketTypeIdList = []
-
-      // 点击非全部选中  需要排除全部选中 以及 当前点击的选项
-      // 新老数据都有全部选中
-      if (oldVal.includes(0) && val.includes(0)) {
-        const index = val.indexOf(0)
-        val.splice(index, 1) // 排除全选选项
-        this.form.ticketTypeIdList = val
-      }
-
-      // 全选未选 但是其他选项全部选上 则全选选上 上次和当前 都没有全选
-      if (!oldVal.includes(0) && !val.includes(0)) {
-        if (val.length === allValues.length - 1) this.form.ticketTypeIdList = [0].concat(val)
-      }
-
-      // 储存当前最后的结果 作为下次的老数据
-      this.oldTicketTypeOptions[1] = this.form.ticketTypeIdList
-    },
     selectAllCate (val) {
       let allValues = this.orderCategory.map(i => i.id)
 

+ 8 - 13
src/pages/queryReport/checkQuery.vue

@@ -128,18 +128,11 @@
       <el-form-item
         label="票种"
         prop="ticketTypeIdList">
-        <el-select
+        <TicketTypeSelect
           v-model="form.ticketTypeIdList"
-          multiple
-          clearable
+          :options="ticketTypeList"
           placeholder="请选择">
-          <el-option
-            v-for="item in ticketTypeList"
-            :key="item.id"
-            :label="item.name"
-            :value="item.id">
-          </el-option>
-        </el-select>
+        </TicketTypeSelect>
       </el-form-item>
       <el-form-item
         label="身份证号"
@@ -226,11 +219,11 @@
         >
           搜索
         </el-button>
-        <el-button
+        <!-- <el-button
           type="primary"
           @click="reportExport">
           导出
-        </el-button>
+        </el-button> -->
       </div>
     </el-form>
 
@@ -337,6 +330,7 @@ import { getCheckList } from '@/api/queryReport'
 import { getSaleChannelList, getSingleOrder } from '@/api/order'
 import { getDeviceList } from '@/api/checker'
 import ReaderInput from '@/components/ReaderInput'
+import TicketTypeSelect from '@/components/TicketTypeSelect'
 import moment from 'moment'
 // import DetailModal from './checkQuery/DetailModal'
 import { orderStatusDic, ticketStatusDic, orderCategories } from '@/assets/staticData'
@@ -344,7 +338,8 @@ import { orderStatusDic, ticketStatusDic, orderCategories } from '@/assets/stati
 export default {
   name: 'checkQuery',
   components: {
-    ReaderInput
+    ReaderInput,
+    TicketTypeSelect
   },
   data () {
     return {

+ 7 - 44
src/pages/queryReport/checkStatistic.vue

@@ -49,25 +49,12 @@
         label="票种列表"
         prop="ticketTypeIdList"
       >
-        <el-select
+        <TicketTypeSelect
           v-model="form.ticketTypeIdList"
-          multiple
+          :options="ticketTypeList"
           collapse-tags
-          filterable
-          clearable
-          placeholder="请选择"
-          @change="selectAll">
-          <el-option
-            :value="0"
-            label="全部票种">
-          </el-option>
-          <el-option
-            v-for="item in ticketTypeList"
-            :key="item.id"
-            :value="item.id"
-            :label="item.name">
-          </el-option>
-        </el-select>
+          placeholder="请选择">
+        </TicketTypeSelect>
       </el-form-item>
       <el-form-item
         label="景点"
@@ -269,11 +256,9 @@ import { getCheckerVerifyStatistics } from '@/api/queryReport'
 import { getScenic, getTicketTypeList } from '@/api/ticketType'
 import { getSaleChannelList } from '@/api/order'
 import { totalRowClassName } from '@/utils'
-import { ticketStatusDic } from '@/assets/staticData'
-import { getSystemConfigList } from '@/api/systemConfig'
 import { addUserConfig, getUserConfig } from '@/api/configSetting'
 import draggable from 'vuedraggable'
-import Tip from '@/components/Tip'
+import TicketTypeSelect from '@/components/TicketTypeSelect'
 import moment from 'moment'
 
 const groupKeys = [
@@ -331,8 +316,8 @@ export default {
     }
   },
   components: {
-    Tip,
-    draggable
+    draggable,
+    TicketTypeSelect
   },
   data () {
     return {
@@ -375,7 +360,6 @@ export default {
       saleChannels: [],
       ticketTypeListData: [],
       scenicListData: [],
-      oldTicketTypeOptions: [[]],
       loading: false
     }
   },
@@ -467,27 +451,6 @@ export default {
       this.$refs.form.resetFields()
       this.form.playDate = []
     },
-    selectAll (val) {
-      let allValues = this.ticketTypeList.map(i => i.id)
-      allValues.unshift(0)
-      const oldVal = this.oldTicketTypeOptions.length === 1 ? [] : this.oldTicketTypeOptions[1]
-
-      if (val.includes(0)) this.form.ticketTypeIdList = allValues
-
-      if (oldVal.includes(0) && !val.includes(0)) this.form.ticketTypeIdList = []
-
-      if (oldVal.includes(0) && val.includes(0)) {
-        const index = val.indexOf(0)
-        val.splice(index, 1)
-        this.form.ticketTypeIdList = val
-      }
-
-      if (!oldVal.includes(0) && !val.includes(0)) {
-        if (val.length === allValues.length - 1) this.form.ticketTypeIdList = [0].concat(val)
-      }
-
-      this.oldTicketTypeOptions[1] = this.form.ticketTypeIdList
-    },
     getSaleChannelList () {
       getSaleChannelList({ pageSize: -1, pageNum: 1 }).then((res) => {
         this.saleChannels = res?.data || []

+ 6 - 11
src/pages/queryReport/refundQuery/RefundQuery.vue

@@ -128,18 +128,11 @@
       <el-form-item
         label="票种"
         prop="ticketTypeIdList">
-        <el-select
+        <TicketTypeSelect
           v-model="form.ticketTypeIdList"
-          multiple
-          clearable
+          :options="ticketTypeList"
           placeholder="请选择">
-          <el-option
-            v-for="item in ticketTypeList"
-            :key="item.id"
-            :label="item.name"
-            :value="item.id">
-          </el-option>
-        </el-select>
+        </TicketTypeSelect>
       </el-form-item>
       <el-form-item
         label="身份证号"
@@ -325,6 +318,7 @@
 import { getCancelOrderList } from '@/api/queryReport'
 import { getSaleChannelList, getSingleOrder } from '@/api/order'
 import ReaderInput from '@/components/ReaderInput'
+import TicketTypeSelect from '@/components/TicketTypeSelect'
 import moment from 'moment'
 // import DetailModal from './DetailModal'
 import { orderStatusDic, ticketStatusDic, orderCategories } from '@/assets/staticData'
@@ -332,7 +326,8 @@ import { orderStatusDic, ticketStatusDic, orderCategories } from '@/assets/stati
 export default {
   name: 'refundQuery',
   components: {
-    ReaderInput
+    ReaderInput,
+    TicketTypeSelect
     // DetailModal
   },
   data () {

+ 6 - 11
src/pages/queryReport/refundStatistic.vue

@@ -31,18 +31,11 @@
       <el-form-item
         label="票种"
         prop="ticketTypeIdList">
-        <el-select
+        <TicketTypeSelect
           v-model="form.ticketTypeIdList"
-          multiple
-          clearable
+          :options="ticketTypeList"
           placeholder="请选择">
-          <el-option
-            v-for="item in ticketTypeList"
-            :key="item.id"
-            :label="item.name"
-            :value="item.id">
-          </el-option>
-        </el-select>
+        </TicketTypeSelect>
       </el-form-item>
       <el-form-item
         label="销售来源"
@@ -128,12 +121,14 @@ import { getCancelOrderStatistics } from '@/api/queryReport'
 import { getSaleChannelList } from '@/api/order'
 import { totalRowClassName } from '@/utils'
 import draggable from 'vuedraggable'
+import TicketTypeSelect from '@/components/TicketTypeSelect'
 import moment from 'moment'
 
 export default {
   name: 'refundStatistics',
   components: {
-    draggable
+    draggable,
+    TicketTypeSelect
   },
   data () {
     return {

+ 20 - 25
src/pages/queryReport/salesQuery.vue

@@ -160,19 +160,11 @@
         label="票种"
         prop="ticketTypeIdList"
       >
-        <el-select
+        <TicketTypeSelect
           v-model="form.ticketTypeIdList"
-          placeholder="请选择"
-          multiple
-          clearable
-          filterable>
-          <el-option
-            v-for="item in ticketTypeList"
-            :key="item.id"
-            :value="item.id"
-            :label="item.name">
-          </el-option>
-        </el-select>
+          :options="ticketTypeList"
+          placeholder="请选择">
+        </TicketTypeSelect>
       </el-form-item>
       <el-form-item
         label="景点"
@@ -1195,12 +1187,12 @@
               controls-position="right"
               size="small"
               :min="1"
-              :max="scope.row.checkNum"
+              :max="scope.row.checkNum - (scope.row.cancelCheckNum || 0)"
               :precision="0"
-              v-model="scope.row.cancelCheckNum"
+              v-model="scope.row.thisCancelCheckNum"
               @change="handlePartRefundCheckNumChange(scope.row)"
             ></el-input-number>
-            <span v-else>{{ scope.row.cancelCheckNum }}</span>
+            <span v-else>{{ scope.row.thisCancelCheckNum }}</span>
           </template>
         </el-table-column>
         <el-table-column width="200" label="退票金额(¥)">
@@ -1210,9 +1202,9 @@
               controls-position="right"
               size="small"
               :min="0"
-              :max="scope.row.price"
+              :max="scope.row.price - (scope.row.cancelPrice || 0)"
               :precision="2"
-              v-model="scope.row.cancelPrice"
+              v-model="scope.row.thisCancelPrice"
             ></el-input-number>
             <el-input-number
               v-else
@@ -1221,7 +1213,7 @@
               :min="0.01"
               :max="scope.row.price"
               :precision="2"
-              v-model="scope.row.cancelPrice"
+              v-model="scope.row.thisCancelPrice"
             ></el-input-number>
           </template>
         </el-table-column>
@@ -1778,6 +1770,7 @@ import { orderStatusDic, ticketStatusDic, channelList, papersType, orderCategori
 import { getPayStatus } from '@/utils/index'
 // import Ellipsis from '@/components/Ellipsis'
 import ReaderInput from '@/components/ReaderInput'
+import TicketTypeSelect from '@/components/TicketTypeSelect'
 import moment from 'moment'
 import { payPOS } from '@/api/pay'
 import { getTeamList, getGuideList } from '@/api/checker'
@@ -1834,7 +1827,8 @@ const COLUMN_SCHEMA = [
   { key: 'cancelPrice', label: '退款金额', group: '退款', column: 'extra', apiKey: 'cancelPrice' },
   { key: 'cancelAdminName', label: '退款操作人', group: '退款', column: 'extra', apiKey: 'cancelAdminName' },
   { key: 'cancelCompleteTime', label: '退款完成时间', group: '退款', column: 'extra', apiKey: 'cancelCompleteTime' },
-  { key: 'isForceCancel', label: '是否强制取消', group: '退款', column: 'extra', apiKey: 'isForceCancel' }
+  { key: 'isForceCancel', label: '是否强制取消', group: '退款', column: 'extra', apiKey: 'isForceCancel' },
+  { key: 'cancelCheckNum', label: '取消票人数', group: '退款', column: 'extra', apiKey: 'cancelCheckNum' }
 ]
 
 const API_KEY_TO_FIELD = COLUMN_SCHEMA.reduce((acc, item) => {
@@ -1901,7 +1895,8 @@ export default {
     }
   },
   components: {
-    ReaderInput
+    ReaderInput,
+    TicketTypeSelect
     // Ellipsis
   },
   data () {
@@ -2355,8 +2350,8 @@ export default {
       // 初始化部分退单票列表,添加可编辑字段
       this.partRefundTicketList = (this.currentOrder.ticketList || []).map(v => ({
         ...v,
-        cancelCheckNum: v.checkNum || 1,
-        cancelPrice: v.price
+        thisCancelCheckNum: v.checkNum || 1,
+        thisCancelPrice: v.price
       }))
       this.partRefundSelection = []
     },
@@ -2366,7 +2361,7 @@ export default {
     handlePartRefundCheckNumChange (row) {
       // 一码多人:根据退票人数自动计算退票金额
       const unitPrice = row.price / row.checkNum
-      row.cancelPrice = Math.round(unitPrice * row.cancelCheckNum * 100) / 100
+      row.thisCancelPrice = Math.round(unitPrice * row.thisCancelCheckNum * 100) / 100
     },
     submitPartRefundOrder () {
       if (!this.partRefundSelection?.length) {
@@ -2375,8 +2370,8 @@ export default {
 
       this.partRefundOrderInfo.ticketCancelRequestList = this.partRefundSelection.map(v => ({
         ticketId: v.id,
-        cancelCheckNum: v.cancelCheckNum,
-        cancelPrice: v.cancelPrice
+        cancelCheckNum: v.thisCancelCheckNum,
+        cancelPrice: v.thisCancelPrice
       }))
 
       this.$confirm('确定要部分退单吗?', '提示', {