FormInfo.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. <template>
  2. <div>
  3. <el-card>
  4. <template slot="header">
  5. <div
  6. class="card-title"
  7. style="display:flex;"
  8. v-if="currentTicket">
  9. <div style="display:flex;">
  10. <div>当前票种:<b>{{ currentTicket.name || '无' }}</b></div>
  11. <span v-if="currentTicket.isSale === 0" style="color: red">(停售)</span>
  12. <div v-if="category === 'batch'" style="margin-left: 20px;">当前场次:<b>{{ currentBatchName || '无' }}</b></div>
  13. </div>
  14. <div>{{ form.playDateBegin | formatDate }} 票价:<span class="price"><b>{{ currentPrice }}</b> 元</span></div>
  15. </div>
  16. <span
  17. v-else
  18. style="color:red">请选择门票</span>
  19. </template>
  20. <el-form
  21. v-loading="loading"
  22. class="sale-form-wrap"
  23. ref="form"
  24. :model="form"
  25. :inline="true"
  26. label-width="100px">
  27. <el-form-item
  28. v-if="isTeam"
  29. verify
  30. required
  31. label="团体">
  32. <el-select
  33. filterable
  34. v-model="form.teamId">
  35. <el-option
  36. v-for="item in teamList"
  37. :key="item.id"
  38. :label="item.name"
  39. :value="item.id">
  40. </el-option>
  41. </el-select>
  42. </el-form-item>
  43. <el-form-item
  44. verify
  45. can-be-empty
  46. label="顾客姓名"
  47. prop="buyerName">
  48. <el-input
  49. v-model="form.buyerName"></el-input>
  50. </el-form-item>
  51. <el-form-item
  52. can-be-empty
  53. :verify="verifyPhone"
  54. label="顾客电话"
  55. prop="buyerPhone">
  56. <el-input
  57. v-model="form.buyerPhone"></el-input>
  58. </el-form-item>
  59. <el-form-item
  60. label="证件类型"
  61. prop="buyerIdentifyType">
  62. <el-select
  63. :disabled="identifyLockCheckStyle"
  64. v-model="form.buyerIdentifyType"
  65. placeholder="请选择">
  66. <el-option
  67. v-for="(item, idx) in papersType"
  68. :key="`id-${idx}`"
  69. :label="item.label"
  70. :value="item.value">
  71. </el-option>
  72. </el-select>
  73. <el-checkbox
  74. style="margin-left:10px"
  75. v-model="identifyLockCheckStyle">
  76. 锁定
  77. </el-checkbox>
  78. </el-form-item>
  79. <!-- <el-form-item
  80. key="id1"
  81. :verify="form.buyerIdentifyType === 0 ? verifyId : ''"
  82. can-be-empty
  83. v-if="form.buyerIdentifyType === 0"
  84. label="证件号码"
  85. prop="buyerIdentify">
  86. <ReaderInput
  87. type="ID"
  88. @change="form.buyerName = $event.Name,Certificate=$event"
  89. @input="form.buyerIdentify = $event"
  90. v-model="form.buyerIdentify"></ReaderInput>
  91. </el-form-item> -->
  92. <el-form-item
  93. key="id2"
  94. label="证件号码"
  95. prop="buyerIdentify">
  96. <el-input v-model="form.buyerIdentify"></el-input>
  97. </el-form-item>
  98. <el-form-item
  99. v-if="Certificate.Nation"
  100. label="国家"
  101. >
  102. {{ Certificate.Nation }}
  103. </el-form-item>
  104. <el-form-item
  105. v-if="Certificate.Sex"
  106. label="性别"
  107. >
  108. {{ Certificate.Sex }}
  109. </el-form-item>
  110. <el-form-item
  111. v-if="Certificate.ValidDate"
  112. label="有效截止日期"
  113. >
  114. {{ Certificate.ValidDate }}
  115. </el-form-item>
  116. <el-form-item
  117. prop="playDateBegin"
  118. label="游玩日期">
  119. <el-date-picker
  120. :clearable="false"
  121. type="date"
  122. :editable="false"
  123. disabled
  124. :picker-options="dateOption"
  125. v-model="form.playDateBegin"
  126. >
  127. </el-date-picker>
  128. </el-form-item>
  129. <el-form-item
  130. prop="playDateEnd"
  131. label="结束日期">
  132. <el-date-picker
  133. :clearable="false"
  134. :editable="false"
  135. disabled
  136. :picker-options="dateOption"
  137. v-model="form.playDateEnd"
  138. type="date"
  139. >
  140. </el-date-picker>
  141. </el-form-item>
  142. <el-form-item
  143. verify
  144. number
  145. label="购买数量"
  146. prop="count"
  147. >
  148. <el-input-number
  149. ref="ticketCount"
  150. name="ticket-count"
  151. v-model.number="form.count"
  152. :min="1"
  153. :max="10000"
  154. :precision="0"
  155. ></el-input-number>
  156. </el-form-item>
  157. <el-form-item
  158. label="检票方式"
  159. prop="checkWay">
  160. <el-select
  161. v-model="form.checkWay"
  162. :disabled="retailLockCheckStyle">
  163. <el-option
  164. v-for="item in checkWayList"
  165. :key="item.val"
  166. :value="item.val"
  167. :label="item.name"
  168. ></el-option>
  169. </el-select>
  170. <el-checkbox
  171. style="margin-left:10px"
  172. v-model="retailLockCheckStyle">
  173. 锁定
  174. </el-checkbox>
  175. </el-form-item>
  176. <!-- v-if="form.checkWay === 3 || form.checkWay === 4" -->
  177. <el-form-item
  178. label="游客信息"
  179. >
  180. <div
  181. v-if="currentTicket"
  182. class="input-space"
  183. >
  184. <el-button
  185. type="primary"
  186. size="small"
  187. icon="el-icon-edit"
  188. @click="showTouristList"
  189. >
  190. 编辑查看
  191. </el-button>
  192. </div>
  193. </el-form-item>
  194. </el-form>
  195. <div class="btn-wrap">
  196. <el-button
  197. @click="resetForm"
  198. style="margin-right:20px">
  199. 重置表单
  200. </el-button>
  201. <el-button
  202. type="primary"
  203. @click="addToOrder">
  204. 加入订单
  205. </el-button>
  206. </div>
  207. </el-card>
  208. <!-- 游客列表 -->
  209. <TouristList
  210. :check-ticket-no="form.checkWay === 5"
  211. :check-face="form.checkWay === 4"
  212. :check-id="form.checkWay === 3"
  213. :tourist-list="touristList"
  214. :update-tourist="updateTourist"
  215. ref="touristList"></TouristList>
  216. </div>
  217. </template>
  218. <script>
  219. import { randomString } from '@/utils'
  220. import { verifyId, verifyPhone } from '@/utils/validate'
  221. import { checkWayList } from '@/assets/staticData'
  222. import { setTourist } from '@/pages/common'
  223. import TouristList from '../common/TouristList'
  224. // import ReaderInput from '@/components/ReaderInput'
  225. import moment from 'moment'
  226. import { IDENTIFY_TYPES } from '@/const'
  227. import { getTeamList } from '@/api/checker'
  228. export default {
  229. props: {
  230. currentTicket: {
  231. type: Object,
  232. default: () => null
  233. },
  234. orderItems: {
  235. type: Array,
  236. default: () => []
  237. }
  238. },
  239. components: {
  240. TouristList
  241. // ReaderInput
  242. },
  243. computed: {
  244. category () {
  245. return this.$route.meta.category || 'ticket'
  246. },
  247. isTeam () {
  248. return this.$route.meta.type === 'team'
  249. },
  250. totalPrice () {
  251. if (!this.currentTicket) {
  252. return 0
  253. }
  254. return this.$NP.times(this.currentPrice, this.form.count)
  255. },
  256. scenicName () {
  257. return this.$store.state.user.scenicName
  258. },
  259. currentBatchName () {
  260. if (!this.currentTicket) return ''
  261. const batchConfig = this.currentTicket.selectBatch || {}
  262. const { name, startTime, endTime } = batchConfig
  263. return name ? `${name} ${startTime} - ${endTime}` : ''
  264. }
  265. },
  266. data () {
  267. return {
  268. loading: false,
  269. keyControl: 2, // 键盘控制:1,2,3标识选中顺序
  270. isFocus: false, // 当前组件是否选中
  271. papersType: IDENTIFY_TYPES,
  272. checkWayList,
  273. pricePlan: [],
  274. currentPrice: 0,
  275. currentPlan: null,
  276. retailLockCheckStyle: this.$localStore.get('retailLockCheckStyle') || false,
  277. identifyLockCheckStyle: this.$localStore.get('identifyLockCheckStyle') || false,
  278. touristList: [{
  279. batchConfigId: randomString(),
  280. guestName: '',
  281. guestPhone: '',
  282. guestIdentify: '',
  283. guestIdentifyType: 0,
  284. ticketTypeId: 0,
  285. checkNum: 1,
  286. price: 0,
  287. seatId: 0,
  288. playDateBegin: new Date(moment().startOf('day').valueOf()),
  289. lock: false // 是否可编辑
  290. }],
  291. form: {
  292. buyerIdentify: '',
  293. buyerName: '',
  294. buyerIdentifyType: 0,
  295. buyerPhone: '',
  296. groupIndividual: 1,
  297. count: this.$localStore.get('initNum') === '空' ? undefined : 1,
  298. payChannel: '现金',
  299. playDateBegin: new Date(moment().startOf('day').valueOf()),
  300. playDateEnd: '',
  301. price: 0,
  302. teamId: '',
  303. checkWay: this.isTeam ? 2 : 1,
  304. actualMoney: 0 // 实付金额,不需要传给后台
  305. },
  306. Certificate: {
  307. },
  308. dateOption: {
  309. // 日历禁用日期
  310. disabledDate () {
  311. return false
  312. }
  313. },
  314. disablePlayDateBegin: false,
  315. teamList: []
  316. }
  317. },
  318. created () {
  319. if (this.isTeam) {
  320. getTeamList({ pageSize: -1, pageNum: 1 }).then(res => {
  321. this.teamList = res.data.records || []
  322. })
  323. }
  324. },
  325. methods: {
  326. verifyId,
  327. verifyPhone,
  328. randomString,
  329. setFocus () {
  330. this.$refs.ticketCount.focus()
  331. this.$refs.ticketCount.select()
  332. },
  333. resetForm () {
  334. this.touristList = [{
  335. batchConfigId: randomString(),
  336. guestName: '',
  337. guestPhone: '',
  338. guestIdentify: '',
  339. guestIdentifyType: 0,
  340. lock: false // 是否可编辑
  341. }]
  342. this.$refs.form.resetFields()
  343. this.form.checkWay = this.isTeam ? 2 : 1
  344. this.Certificate = {}
  345. },
  346. setEndDate (val) {
  347. const { category, currentTicket } = this
  348. const { useDateType, memberUseDateType, useDays } = currentTicket
  349. if ((category === 'ticket' && useDateType === 1) || (category === 'member' && memberUseDateType === 2)) {
  350. this.form.playDateEnd = moment(val).add((useDays || 1) - 1, 'days').format('YYYY-MM-DD')
  351. }
  352. if (category === 'batch') {
  353. this.form.playDateEnd = moment(val).format('YYYY-MM-DD')
  354. }
  355. if (category === 'member' && memberUseDateType === 1) {
  356. this.form.playDateEnd = moment(val).add((useDays || 1) - 1, 'days').format('YYYY-MM-DD')
  357. }
  358. },
  359. // 更新游客列表
  360. updateTourist (data) {
  361. this.form.count = data.length
  362. this.touristList = data
  363. },
  364. // 同步游客数据与游客数
  365. syncTouristList () {
  366. let num = this.form.count - this.touristList.length
  367. setTourist(this.touristList, num)
  368. },
  369. // 显示游客弹窗
  370. showTouristList () {
  371. this.syncTouristList()
  372. this.$refs.touristList.show()
  373. },
  374. isPlayToday () {
  375. const isBefore = moment(this.form.playDateBegin).isBefore(moment().format('YYYY-MM-DD'))
  376. const isAfter = moment(this.form.playDateBegin).isAfter(moment().format('YYYY-MM-DD'))
  377. return !(isBefore || isAfter)
  378. },
  379. addToOrder (callback) {
  380. if (this.currentTicket && this.currentTicket.isSale === 0) {
  381. return this.$message.error('当前票种已停售')
  382. }
  383. if (!this.isPlayToday()) {
  384. // 客户端下单的时候游玩日期不是当天,提示一下
  385. this.$message.warning('友情提示:您下单的游玩日期不是今天!')
  386. }
  387. if (!this.currentTicket) {
  388. return this.$message.error('请选择门票')
  389. }
  390. if (this.category === 'batch') {
  391. if (!this.currentTicket.batchConfigId) {
  392. return this.$message.error('请选场次')
  393. }
  394. if (this.currentTicket.selectBatch.leftNums <= 0) {
  395. return this.$message.error('当前场次已售罄')
  396. }
  397. if (this.currentTicket.selectBatch.leftNums < this.form.count) {
  398. return this.$message.error('当前场次剩余票数不足')
  399. }
  400. }
  401. if (this.loading) return
  402. this.$refs.form.validate((valid) => {
  403. if (valid) {
  404. this.syncTouristList()
  405. const { playDateBegin, playDateEnd } = this.form
  406. const { id, name, checkCount, selectBatch, isUseDateLimit } = this.currentTicket
  407. const price = this.currentPrice
  408. if (this.isTeam) {
  409. if (!this.form.teamId) {
  410. this.$message.error('请选择团队')
  411. return
  412. }
  413. }
  414. if (this.form.checkWay === 3) {
  415. // 是否存在未填写的内容
  416. let blankData = this.touristList.find(item => {
  417. return !item.guestName || !item.guestIdentify
  418. })
  419. if (blankData) {
  420. this.$message.error('游客信息不完整,请编辑查看')
  421. return
  422. }
  423. }
  424. if (this.form.checkWay === 4) {
  425. // 是否存在未填写的内容
  426. let blankData = this.touristList.find(item => {
  427. return !item.face
  428. })
  429. if (blankData) {
  430. this.$message.error('游客人脸信息不完整,请编辑查看')
  431. return
  432. }
  433. }
  434. const pdb = moment(playDateBegin).format('YYYY-MM-DD')
  435. const pde = moment(playDateEnd).format('YYYY-MM-DD')
  436. let tickets = this.touristList.map(item => {
  437. return {
  438. ...item,
  439. ticketTypeId: id,
  440. checkNum: checkCount,
  441. price: this.currentPrice,
  442. ticketNo: item.ticketNo,
  443. batchConfigId: selectBatch?.id || '',
  444. face: item.face,
  445. pricePlanId: this.currentPlan || 0,
  446. playDateBegin: pdb,
  447. playDateEnd: pde
  448. }
  449. })
  450. // 合并游玩日期相同的票种数据
  451. const existItem = this.orderItems.find(i => {
  452. let dateIsEque
  453. if (isUseDateLimit) {
  454. dateIsEque = JSON.stringify(i.playDateBegin) === JSON.stringify(pdb)
  455. } else {
  456. dateIsEque = true
  457. }
  458. // 日期对象不全等
  459. return i.id === id && dateIsEque && i.price === this.currentPrice
  460. })
  461. if (existItem) {
  462. Object.assign(existItem, this.form)
  463. existItem.tickets = existItem.tickets.concat(tickets)
  464. existItem.count = existItem.tickets.length
  465. existItem.total = this.$NP.plus(existItem.total, this.totalPrice)
  466. } else {
  467. this.orderItems.push({
  468. ...this.form,
  469. keyId: randomString(), // 唯一标识
  470. id, // 票种Id
  471. ticketName: name,
  472. count: tickets.length,
  473. price,
  474. total: this.totalPrice,
  475. playDateBegin: pdb,
  476. currentTicket: {
  477. ...this.currentTicket,
  478. pricePlanId:
  479. this.currentPlan,
  480. price,
  481. playDateBegin: pdb,
  482. playDateEnd: pde
  483. },
  484. tickets
  485. })
  486. }
  487. // 重置游客数量
  488. this.touristList = [{
  489. batchConfigId: randomString(),
  490. guestName: '',
  491. guestPhone: '',
  492. guestIdentify: '',
  493. guestIdentifyType: 0,
  494. playDateBegin: moment().format('YYYY-MM-DD'),
  495. lock: false // 是否可编辑
  496. }]
  497. this.form.count = this.$localStore.get('initNum') === '空' ? undefined : 1
  498. if (callback && typeof callback === 'function') {
  499. callback()
  500. }
  501. this.$nextTick(() => {
  502. this.$refs.form.clearValidate()
  503. })
  504. }
  505. })
  506. },
  507. setPrice (price) {
  508. this.currentPrice = price || this.currentTicket.price
  509. this.form.price = this.currentPrice
  510. },
  511. defaultPapersType () {
  512. if (process.env.VUE_APP_PROJECT === 'zql') {
  513. return 0
  514. } else if (this.$localStore.get('retail_identify')) {
  515. return this.$localStore.get('retail_identify')
  516. } else {
  517. return ''
  518. }
  519. }
  520. },
  521. watch: {
  522. async currentTicket (val) {
  523. if (!val) return
  524. await this.$nextTick()
  525. this.resetForm()
  526. const { useDateType, useDateStart, useDateEnd, useDays, memberUseDateType, playDate } = val
  527. this.disablePlayDateBegin = false
  528. switch (this.category) {
  529. case 'ticket':
  530. if (useDateType === 2) {
  531. this.form.playDateBegin = moment(useDateStart).format('YYYY-MM-DD')
  532. this.form.playDateEnd = moment(useDateEnd).format('YYYY-MM-DD')
  533. this.disablePlayDateBegin = true
  534. } else {
  535. this.form.playDateBegin = moment(playDate).format('YYYY-MM-DD')
  536. this.form.playDateEnd = moment(playDate).add((useDays || 1) - 1, 'days').format('YYYY-MM-DD')
  537. }
  538. break
  539. case 'batch':
  540. this.form.playDateBegin = moment(playDate).format('YYYY-MM-DD')
  541. this.form.playDateEnd = moment(playDate).format('YYYY-MM-DD')
  542. this.disablePlayDateBegin = true
  543. break
  544. case 'member':
  545. if (memberUseDateType === 1) {
  546. this.form.playDateBegin = moment(playDate).format('YYYY-MM-DD')
  547. this.form.playDateEnd = moment(playDate).add((useDays || 1) - 1, 'days').format('YYYY-MM-DD')
  548. } else {
  549. this.form.playDateBegin = moment(useDateStart).format('YYYY-MM-DD')
  550. this.form.playDateEnd = moment(useDateEnd).format('YYYY-MM-DD')
  551. }
  552. this.disablePlayDateBegin = true
  553. break
  554. default :
  555. break
  556. }
  557. // 设置不可选日期
  558. this.dateOption.disabledDate = (date) => {
  559. let beforeToday = moment().subtract(1, 'days').isAfter(date)
  560. let beforeStart = moment(val.useDateStart).isAfter(date)
  561. let afterEnd = moment(val.useDateEnd).isBefore(date)
  562. return beforeToday || beforeStart || afterEnd
  563. }
  564. this.timer && clearTimeout(this.timer)
  565. this.timer = setTimeout(() => {
  566. this.setPrice(this.currentTicket.price)
  567. }, 300)
  568. },
  569. retailLockCheckStyle (val) {
  570. if (val) {
  571. this.$localStore.set('retailCheckWay', this.form.checkWay)
  572. }
  573. this.$localStore.set('retailLockCheckStyle', val)
  574. },
  575. identifyLockCheckStyle (val) {
  576. if (val) {
  577. this.$localStore.set('retail_identify', this.form.buyerIdentifyType)
  578. }
  579. this.$localStore.set('identifyLockCheckStyle', val)
  580. },
  581. 'form.playDateBegin' (val) {
  582. if (val) {
  583. this.setEndDate(val)
  584. this.currentPlan = null
  585. this.$emit('changePlayDate', moment(val).format('YYYY-MM-DD'))
  586. this.timer && clearTimeout(this.timer)
  587. }
  588. }
  589. }
  590. }
  591. </script>
  592. <style lang="scss" scoped>
  593. .el-input,.el-select,.el-date-editor,.el-date-editor.el-input,.el-input-number {
  594. width: 200px;
  595. }
  596. </style>