Merge commit '547289a7' into order
Showing
18 changed files
with
1549 additions
and
1 deletions
| ... | @@ -30,6 +30,7 @@ | ... | @@ -30,6 +30,7 @@ |
| 30 | "jszip": "^3.10.1", | 30 | "jszip": "^3.10.1", |
| 31 | "katex": "^0.16.6", | 31 | "katex": "^0.16.6", |
| 32 | "lodash": "^4.17.21", | 32 | "lodash": "^4.17.21", |
| 33 | "md5js": "^1.0.7", | ||
| 33 | "nprogress": "0.2.0", | 34 | "nprogress": "0.2.0", |
| 34 | "pinia": "2.0.35", | 35 | "pinia": "2.0.35", |
| 35 | "qrcode": "^1.5.3", | 36 | "qrcode": "^1.5.3", | ... | ... |
pnpm-lock.yaml
0 → 100644
This diff could not be displayed because it is too large.
| ... | @@ -13,6 +13,7 @@ NProgress.configure({ showSpinner: false }) | ... | @@ -13,6 +13,7 @@ NProgress.configure({ showSpinner: false }) |
| 13 | const whiteList = ['/login', '/register', '/regulations'] | 13 | const whiteList = ['/login', '/register', '/regulations'] |
| 14 | 14 | ||
| 15 | router.beforeEach((to, from, next) => { | 15 | router.beforeEach((to, from, next) => { |
| 16 | console.log('to2', to) | ||
| 16 | NProgress.start() | 17 | NProgress.start() |
| 17 | if (getToken()) { | 18 | if (getToken()) { |
| 18 | // debugger | 19 | // debugger | ... | ... |
| ... | @@ -9,6 +9,7 @@ import useUserStore from '@/store/modules/user' | ... | @@ -9,6 +9,7 @@ import useUserStore from '@/store/modules/user' |
| 9 | NProgress.configure({ showSpinner: false }) | 9 | NProgress.configure({ showSpinner: false }) |
| 10 | 10 | ||
| 11 | router.beforeEach((to, from, next) => { | 11 | router.beforeEach((to, from, next) => { |
| 12 | console.log('to', to) | ||
| 12 | NProgress.start() | 13 | NProgress.start() |
| 13 | if (getToken()) { | 14 | if (getToken()) { |
| 14 | // 判断当前用户是否已拉取完user_info信息 | 15 | // 判断当前用户是否已拉取完user_info信息 | ... | ... |
| ... | @@ -500,6 +500,59 @@ export const constantRoutes = [ | ... | @@ -500,6 +500,59 @@ export const constantRoutes = [ |
| 500 | meta: { title: 'System messages' } | 500 | meta: { title: 'System messages' } |
| 501 | } | 501 | } |
| 502 | ] | 502 | ] |
| 503 | }, | ||
| 504 | { | ||
| 505 | path: 'seat', | ||
| 506 | component: () => import('@/viewsPc/seat/seat'), | ||
| 507 | name: 'seat', | ||
| 508 | redirect: '/seat/detail', | ||
| 509 | children: [ | ||
| 510 | { | ||
| 511 | path: 'detail', | ||
| 512 | name: 'seat_detail', | ||
| 513 | component: () => import('@/viewsPc/seat/ticket-detail'), | ||
| 514 | meta: { title: '购票详情' }, | ||
| 515 | props: route => ({ | ||
| 516 | activityId:route.query.id, | ||
| 517 | }) | ||
| 518 | }, | ||
| 519 | { | ||
| 520 | path: 'seat_picker', | ||
| 521 | name: 'seat_picker', | ||
| 522 | component: () => import('@/viewsPc/seat/seat-picker'), | ||
| 523 | meta: { title: '选座' } | ||
| 524 | }, | ||
| 525 | { | ||
| 526 | path: 'order', | ||
| 527 | name: 'seat_order', | ||
| 528 | component: () => import('@/viewsPc/seat/order-list'), | ||
| 529 | meta: { title: '我的订单' } | ||
| 530 | }, | ||
| 531 | { | ||
| 532 | path: 'order_detail', | ||
| 533 | name: 'order_detail', | ||
| 534 | component: () => import('@/viewsPc/seat/order-detail'), | ||
| 535 | meta: { title: '订单详情' } | ||
| 536 | }, | ||
| 537 | { | ||
| 538 | path: 'confirm_order', | ||
| 539 | name: 'confirm_order', | ||
| 540 | component: () => import('@/viewsPc/seat/confirm-order'), | ||
| 541 | meta: { title: '确认订单' } | ||
| 542 | }, | ||
| 543 | { | ||
| 544 | path: 'add_watch_people', | ||
| 545 | name: 'add_watch_people', | ||
| 546 | component: () => import('@/viewsPc/seat/add-watch-people'), | ||
| 547 | meta: { title: '新增观影人' } | ||
| 548 | }, | ||
| 549 | { | ||
| 550 | path: 'people_manage', | ||
| 551 | name: 'people_manage', | ||
| 552 | component: () => import('@/viewsPc/seat/people-manage'), | ||
| 553 | meta: { title: '观影人管理' } | ||
| 554 | }, | ||
| 555 | ] | ||
| 503 | } | 556 | } |
| 504 | ] | 557 | ] |
| 505 | }, | 558 | }, | ... | ... |
| ... | @@ -196,7 +196,7 @@ function submit() { | ... | @@ -196,7 +196,7 @@ function submit() { |
| 196 | return | 196 | return |
| 197 | } | 197 | } |
| 198 | 198 | ||
| 199 | if (!timeVal.value.id)return proxy.$modal.msgError('请选择预约时间!', ) | 199 | if (!timeVal.value.id)return proxy.$modal.msgError( language.value==0?'请选择预约时间!':"Please select an appointment time!" ) |
| 200 | 200 | ||
| 201 | proxy.$refs['formRef'].validate(valid=>{ | 201 | proxy.$refs['formRef'].validate(valid=>{ |
| 202 | if (valid){ | 202 | if (valid){ | ... | ... |
src/viewsPc/seat/add-watch-people.vue
0 → 100644
| 1 | <script setup> | ||
| 2 | import { ElMessage } from "element-plus"; | ||
| 3 | import { addViewPeople } from "./api/index.js"; | ||
| 4 | |||
| 5 | const router = useRouter(); | ||
| 6 | |||
| 7 | const people = reactive({ | ||
| 8 | form: { | ||
| 9 | name: "", | ||
| 10 | idCard: "", | ||
| 11 | }, | ||
| 12 | type: "身份证", | ||
| 13 | onConfirm() { | ||
| 14 | if (!people.form.name) | ||
| 15 | return ElMessage({ type: "warning", message: "请输入姓名" }); | ||
| 16 | if (!people.form.idCard) | ||
| 17 | return ElMessage({ type: "warning", message: "请输入证件号" }); | ||
| 18 | |||
| 19 | // 使用正则验证身份证号码格式 | ||
| 20 | const idCardRegex = | ||
| 21 | /^[1-9]\d{5}(19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[\dXx]$/; | ||
| 22 | if (!idCardRegex.test(people.form.idCard)) | ||
| 23 | return ElMessage({ type: "warning", message: "身份证号格式不正确" }); | ||
| 24 | |||
| 25 | addViewPeople(people.form).then((res) => { | ||
| 26 | ElMessage({ type: "success", message: "操作成功" }); | ||
| 27 | router.go(-2); | ||
| 28 | }); | ||
| 29 | }, | ||
| 30 | }); | ||
| 31 | </script> | ||
| 32 | |||
| 33 | <template> | ||
| 34 | <div class="container"> | ||
| 35 | <div class="title">新增观影人</div> | ||
| 36 | <div class="content"> | ||
| 37 | <div class="form-item"> | ||
| 38 | <div> | ||
| 39 | <div class="label">姓名</div> | ||
| 40 | <el-input | ||
| 41 | v-model="people.form.name" | ||
| 42 | style="width: 570px" | ||
| 43 | placeholder="请输入姓名" | ||
| 44 | /> | ||
| 45 | </div> | ||
| 46 | <div> | ||
| 47 | <div class="label">证件类型</div> | ||
| 48 | <el-input | ||
| 49 | v-model="people.type" | ||
| 50 | style="width: 570px" | ||
| 51 | placeholder="Please input" | ||
| 52 | readonly | ||
| 53 | /> | ||
| 54 | </div> | ||
| 55 | </div> | ||
| 56 | <div class="form-item"> | ||
| 57 | <div> | ||
| 58 | <div class="label">身份证号</div> | ||
| 59 | <el-input | ||
| 60 | v-model="people.form.idCard" | ||
| 61 | style="width: 570px" | ||
| 62 | placeholder="请输入身份证号" | ||
| 63 | /> | ||
| 64 | </div> | ||
| 65 | </div> | ||
| 66 | </div> | ||
| 67 | |||
| 68 | <div class="footer"> | ||
| 69 | <div class="can_pay">取消</div> | ||
| 70 | <div class="pay" @click="people.onConfirm()">确认</div> | ||
| 71 | </div> | ||
| 72 | </div> | ||
| 73 | </template> | ||
| 74 | |||
| 75 | <style scoped lang="scss"> | ||
| 76 | div { | ||
| 77 | box-sizing: border-box; | ||
| 78 | } | ||
| 79 | .container { | ||
| 80 | padding: 20px 0; | ||
| 81 | width: 1200px; | ||
| 82 | margin: 0 auto; | ||
| 83 | |||
| 84 | .title { | ||
| 85 | padding: 11px; | ||
| 86 | text-align: center; | ||
| 87 | background: linear-gradient(270deg, #493ceb 0%, #8623fc 100%); | ||
| 88 | font-size: 18px; | ||
| 89 | color: #ffffff; | ||
| 90 | } | ||
| 91 | |||
| 92 | .content { | ||
| 93 | padding: 46px 20px 24px; | ||
| 94 | background-color: #fff; | ||
| 95 | :deep(.el-input) { | ||
| 96 | height: 48px; | ||
| 97 | border-radius: 24px !important; | ||
| 98 | } | ||
| 99 | .form-item { | ||
| 100 | display: flex; | ||
| 101 | gap: 20px; | ||
| 102 | margin-bottom: 36px; | ||
| 103 | &:last-child { | ||
| 104 | margin: 0; | ||
| 105 | } | ||
| 106 | .label { | ||
| 107 | font-size: 18px; | ||
| 108 | color: #333333; | ||
| 109 | margin-bottom: 16px; | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | .footer { | ||
| 114 | display: flex; | ||
| 115 | justify-content: center; | ||
| 116 | align-items: center; | ||
| 117 | gap: 20px; | ||
| 118 | height: 70px; | ||
| 119 | background-color: #fff; | ||
| 120 | margin-top: 12px; | ||
| 121 | .pay { | ||
| 122 | width: 200px; | ||
| 123 | height: 40px; | ||
| 124 | background: linear-gradient(270deg, #493ceb 0%, #8623fc 100%); | ||
| 125 | border-radius: 20px; | ||
| 126 | font-weight: 500; | ||
| 127 | font-size: 16px; | ||
| 128 | color: #ffffff; | ||
| 129 | line-height: 40px; | ||
| 130 | text-align: center; | ||
| 131 | cursor: pointer; | ||
| 132 | } | ||
| 133 | .can_pay { | ||
| 134 | width: 200px; | ||
| 135 | height: 40px; | ||
| 136 | background: #f6f6f6; | ||
| 137 | border-radius: 20px; | ||
| 138 | font-weight: 500; | ||
| 139 | font-size: 16px; | ||
| 140 | color: #999; | ||
| 141 | line-height: 40px; | ||
| 142 | text-align: center; | ||
| 143 | box-sizing: border-box; | ||
| 144 | cursor: pointer; | ||
| 145 | } | ||
| 146 | } | ||
| 147 | } | ||
| 148 | </style> |
src/viewsPc/seat/api/index.js
0 → 100644
| 1 | import request from "../utils/request"; | ||
| 2 | |||
| 3 | export const loginFree = (data) => request("POST", "/login/loginFree", data); | ||
| 4 | /** 活动详情 */ | ||
| 5 | export const activityDetail = (data) => | ||
| 6 | request("GET", `/api/activity/detail/${data.actId}`, data); | ||
| 7 | /** 场次详情 */ | ||
| 8 | export const sessionDetail = (data) => | ||
| 9 | request("GET", `/api/activity/sessionDetail/${data.actId}`, data); | ||
| 10 | /** 获取场馆信息 */ | ||
| 11 | export const getSitePlaceInfo = (data) => | ||
| 12 | request("GET", `/api/activity/getSitePlaceInfo`, data); | ||
| 13 | /** 获取票档信息 */ | ||
| 14 | export const getPriceLevelInfo = (data) => | ||
| 15 | request("GET", `/api/activity/getPriceLevelInfo`, data); | ||
| 16 | /** 获取座位信息 */ | ||
| 17 | export const getSiteConfig = (data) => | ||
| 18 | request("GET", `/api/activity/getSiteConfig`, data); | ||
| 19 | /** 确认订单 */ | ||
| 20 | export const confirmOrder = (data) => | ||
| 21 | request("POST", `/api/order/confirmOrder`, data); | ||
| 22 | /** 订单支付 */ | ||
| 23 | export const payOrder = (data) => | ||
| 24 | request("POST", `/api/order/payment`, data); | ||
| 25 | /** 观众列表 */ | ||
| 26 | export const viewPeopleList = (data) => | ||
| 27 | request("GET", `/api/customer/list`, data); | ||
| 28 | /** 删除观众 */ | ||
| 29 | export const deleteViewPeople = (data) => | ||
| 30 | request("POST", `/api/customer/delete/${data.id}`, data); | ||
| 31 | /** 新增观众 */ | ||
| 32 | export const addViewPeople = (data) => | ||
| 33 | request("POST", `/api/customer/add`, data); | ||
| 34 | /** 订单列表 */ | ||
| 35 | export const getOrderList = (data) => | ||
| 36 | request("GET", `/api/order/list`, data); | ||
| 37 | /** 立即支付 */ | ||
| 38 | export const immediatePay = (data) => | ||
| 39 | request("POST", `/api/order/immediatePay`, data); | ||
| 40 | /** 取消支付 */ | ||
| 41 | export const cancelPay = (data) => | ||
| 42 | request("POST", `/api/order/cancelPay/${data.orderSn}`, data); | ||
| 43 | /** 退单 */ | ||
| 44 | export const cancelOrder = (data) => | ||
| 45 | request("POST", `/api/order/cancelOrder/${data.orderSn}`, data); | ||
| 46 | /** 订单详情 */ | ||
| 47 | export const getOrderDetail = (data) => | ||
| 48 | request("GET", `/api/order/detail/${data.orderSn}`, data); |
src/viewsPc/seat/components/qrCodeDialog.vue
0 → 100644
| 1 | <script setup> | ||
| 2 | const props = defineProps({ | ||
| 3 | showCodeDialog: { | ||
| 4 | type: Boolean, | ||
| 5 | default: false, | ||
| 6 | }, | ||
| 7 | qrCode: { | ||
| 8 | type: String, | ||
| 9 | default: '' | ||
| 10 | } | ||
| 11 | }); | ||
| 12 | </script> | ||
| 13 | |||
| 14 | <template> | ||
| 15 | <div> | ||
| 16 | <el-dialog v-model="props.showCodeDialog" title="支付" width="300"> | ||
| 17 | <div> | ||
| 18 | <img class="qrcode" :src="props.qrCode" /> | ||
| 19 | </div> | ||
| 20 | </el-dialog> | ||
| 21 | </div> | ||
| 22 | </template> | ||
| 23 | |||
| 24 | <style scoped lang="scss"> | ||
| 25 | .qrcode { | ||
| 26 | width: 150px; | ||
| 27 | height: 150px; | ||
| 28 | background-color: #8623fc; | ||
| 29 | margin: 0 auto; | ||
| 30 | } | ||
| 31 | </style> |
src/viewsPc/seat/confirm-order.vue
0 → 100644
| 1 | <script setup> | ||
| 2 | import { confirmOrder } from "./api/index.js"; | ||
| 3 | import { ElMessage } from "element-plus"; | ||
| 4 | import { payOrder, viewPeopleList } from "./api/index.js"; | ||
| 5 | import { reactive } from "vue"; | ||
| 6 | import qrCodeDialog from "./components/qrCodeDialog.vue"; | ||
| 7 | |||
| 8 | const route = useRoute(); | ||
| 9 | const router = useRouter(); | ||
| 10 | |||
| 11 | const payment = reactive({ | ||
| 12 | showCodeDialog: false, | ||
| 13 | btn_loading: false, | ||
| 14 | form: { | ||
| 15 | viewers: [], | ||
| 16 | phone: "", | ||
| 17 | }, | ||
| 18 | qrInfo: {}, | ||
| 19 | paymentHandle() { | ||
| 20 | if (payment.form.viewers.length != order.data?.seatInfo?.length) | ||
| 21 | return ElMessage({ type: "warning", message: "观看人与购买票数不符" }); | ||
| 22 | if (!payment.form.phone) | ||
| 23 | return ElMessage({ type: "warning", message: "请输入联系人电话" }); | ||
| 24 | payOrder({ | ||
| 25 | contactPhone: payment.form.phone, | ||
| 26 | customerIds: payment.form.viewers, | ||
| 27 | orderToken: order.data?.orderToken, | ||
| 28 | payType: 1, | ||
| 29 | paymentAmount: order.data?.paymentAmount, | ||
| 30 | }).then((res) => { | ||
| 31 | // TODO: 这里有一个二维码 | ||
| 32 | payment.qrInfo = res.data; | ||
| 33 | payment.showCodeDialog = true; | ||
| 34 | router.push({ | ||
| 35 | path: "/seat/order", | ||
| 36 | }); | ||
| 37 | }); | ||
| 38 | }, | ||
| 39 | }); | ||
| 40 | |||
| 41 | const order = reactive({ | ||
| 42 | data: null, | ||
| 43 | fetchData() { | ||
| 44 | confirmOrder({ | ||
| 45 | actId: route.query.actId ?? 1, | ||
| 46 | openType: route.query.openType, | ||
| 47 | sessionId: route.query.sessionId, | ||
| 48 | sitePlace: route.query.sitePlace, | ||
| 49 | ticketType: route.query.ticketType, | ||
| 50 | seatIds: route.query.seatIds.split(","), | ||
| 51 | }).then((res) => { | ||
| 52 | this.data = res.data; | ||
| 53 | }); | ||
| 54 | }, | ||
| 55 | }); | ||
| 56 | |||
| 57 | const audience = reactive({ | ||
| 58 | data: [], | ||
| 59 | fetchData() { | ||
| 60 | viewPeopleList().then((res) => { | ||
| 61 | audience.data = res.data; | ||
| 62 | }); | ||
| 63 | }, | ||
| 64 | }); | ||
| 65 | |||
| 66 | audience.fetchData(); | ||
| 67 | order.fetchData(); | ||
| 68 | </script> | ||
| 69 | |||
| 70 | <template> | ||
| 71 | <div class="container"> | ||
| 72 | <div class="title">订单确认</div> | ||
| 73 | <div class="content"> | ||
| 74 | <div class="left"> | ||
| 75 | <div class="info"> | ||
| 76 | <div class="name">{{ order.data?.activityName }}</div> | ||
| 77 | <div class="address">{{ order.data?.placeName }}</div> | ||
| 78 | </div> | ||
| 79 | |||
| 80 | <div class="ticket_info"> | ||
| 81 | <div class="tit_box"> | ||
| 82 | <div class="line"></div> | ||
| 83 | <div class="txt">订票信息</div> | ||
| 84 | </div> | ||
| 85 | |||
| 86 | <div class="form"> | ||
| 87 | <el-form> | ||
| 88 | <el-form-item label="联系人"> | ||
| 89 | <el-input | ||
| 90 | v-model="payment.form.phone" | ||
| 91 | placeholder="请输入联系人电话" | ||
| 92 | style="width: 260px" | ||
| 93 | /> | ||
| 94 | </el-form-item> | ||
| 95 | <el-form-item label="观看人"> | ||
| 96 | <div class="p_box"> | ||
| 97 | <div class="people"> | ||
| 98 | <el-checkbox-group | ||
| 99 | v-model="payment.form.viewers" | ||
| 100 | :max="order.data?.seatInfo?.length" | ||
| 101 | > | ||
| 102 | <div | ||
| 103 | v-for="(it, index) in audience.data" | ||
| 104 | :key="index" | ||
| 105 | class="prople_item" | ||
| 106 | > | ||
| 107 | <div> | ||
| 108 | <div class="name">{{ it.name }}</div> | ||
| 109 | <div class="idcard">{{ it.idCard }}</div> | ||
| 110 | </div> | ||
| 111 | <el-checkbox :value="it.id"> </el-checkbox> | ||
| 112 | </div> | ||
| 113 | </el-checkbox-group> | ||
| 114 | </div> | ||
| 115 | <!-- button --> | ||
| 116 | <div | ||
| 117 | class="btn" | ||
| 118 | @click="$router.push({ path: '/seat/people_manage' })" | ||
| 119 | > | ||
| 120 | 新增 | ||
| 121 | </div> | ||
| 122 | </div> | ||
| 123 | </el-form-item> | ||
| 124 | </el-form> | ||
| 125 | </div> | ||
| 126 | </div> | ||
| 127 | </div> | ||
| 128 | |||
| 129 | <div class="right"> | ||
| 130 | <div class="tit_box"> | ||
| 131 | <div class="line"></div> | ||
| 132 | <div class="txt">订单明细</div> | ||
| 133 | </div> | ||
| 134 | |||
| 135 | <div class="detail"> | ||
| 136 | <div class="detail_top"> | ||
| 137 | <div class="time">{{ order.data?.dateStr }}</div> | ||
| 138 | <div class="ticket"> | ||
| 139 | {{ order.data?.singlePrice }}元票档 x{{ | ||
| 140 | order.data?.seatInfo?.length | ||
| 141 | }}张 | ||
| 142 | </div> | ||
| 143 | </div> | ||
| 144 | <div class="detail_center"> | ||
| 145 | <div | ||
| 146 | v-for="(it, index) in order.data?.seatInfo" | ||
| 147 | :key="index" | ||
| 148 | class="ticket" | ||
| 149 | > | ||
| 150 | <span v-if="it.venueId == 1">{{ it.area }}区</span> | ||
| 151 | {{ it.pai }}排{{ it.no }}座 ({{ | ||
| 152 | it.venueId == 1 ? "B6" : "B4" | ||
| 153 | }}馆) | ||
| 154 | </div> | ||
| 155 | </div> | ||
| 156 | <div class="detail_b"> | ||
| 157 | <div class="sum_txt">共计</div> | ||
| 158 | <div class="price_num">¥{{ order.data?.paymentAmount }}</div> | ||
| 159 | </div> | ||
| 160 | </div> | ||
| 161 | </div> | ||
| 162 | </div> | ||
| 163 | <div class="footer"> | ||
| 164 | <div> | ||
| 165 | <span class="label">共计金额:</span><span class="value">¥900.00</span> | ||
| 166 | </div> | ||
| 167 | <div class="pay" @click="payment.paymentHandle()">立即支付</div> | ||
| 168 | </div> | ||
| 169 | |||
| 170 | <qrCodeDialog | ||
| 171 | :showCodeDialog="payment.showCodeDialog" | ||
| 172 | :qrCode="payment.qrInfo?.scanCodeUrl" | ||
| 173 | /> | ||
| 174 | </div> | ||
| 175 | </template> | ||
| 176 | |||
| 177 | <style scoped lang="scss"> | ||
| 178 | div { | ||
| 179 | box-sizing: border-box; | ||
| 180 | } | ||
| 181 | .qrcode { | ||
| 182 | width: 150px; | ||
| 183 | height: 150px; | ||
| 184 | background-color: #8623fc; | ||
| 185 | margin: 0 auto; | ||
| 186 | } | ||
| 187 | .container { | ||
| 188 | padding: 20px 0; | ||
| 189 | width: 1200px; | ||
| 190 | margin: 0 auto; | ||
| 191 | |||
| 192 | .title { | ||
| 193 | padding: 11px; | ||
| 194 | text-align: center; | ||
| 195 | background: linear-gradient(270deg, #493ceb 0%, #8623fc 100%); | ||
| 196 | font-size: 18px; | ||
| 197 | color: #ffffff; | ||
| 198 | } | ||
| 199 | |||
| 200 | .content { | ||
| 201 | display: flex; | ||
| 202 | background-color: #fff; | ||
| 203 | padding: 20px 0; | ||
| 204 | } | ||
| 205 | |||
| 206 | .line { | ||
| 207 | width: 4px; | ||
| 208 | height: 18px; | ||
| 209 | background: linear-gradient(180deg, #493ceb 0%, #8623fc 100%); | ||
| 210 | border-radius: 4px; | ||
| 211 | } | ||
| 212 | |||
| 213 | .left { | ||
| 214 | padding-left: 20px; | ||
| 215 | .info { | ||
| 216 | width: 640px; | ||
| 217 | background: rgba(69, 61, 234, 0.04); | ||
| 218 | border-radius: 8px; | ||
| 219 | border: 1px solid #d3d1f6; | ||
| 220 | padding: 20px 0 28px 33px; | ||
| 221 | margin-bottom: 20px; | ||
| 222 | .name { | ||
| 223 | font-weight: 500; | ||
| 224 | font-size: 18px; | ||
| 225 | color: #000000; | ||
| 226 | margin-bottom: 20px; | ||
| 227 | } | ||
| 228 | .address { | ||
| 229 | font-weight: 400; | ||
| 230 | font-size: 14px; | ||
| 231 | color: #929aa0; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | .ticket_info { | ||
| 236 | .tit_box { | ||
| 237 | display: flex; | ||
| 238 | align-items: center; | ||
| 239 | gap: 10px; | ||
| 240 | margin-bottom: 14px; | ||
| 241 | .txt { | ||
| 242 | font-weight: bold; | ||
| 243 | font-size: 16px; | ||
| 244 | color: #493ceb; | ||
| 245 | } | ||
| 246 | } | ||
| 247 | .form { | ||
| 248 | width: 640px; | ||
| 249 | min-height: 464px; | ||
| 250 | padding: 20px 60px; | ||
| 251 | border-radius: 5px; | ||
| 252 | border: 1px solid #dcdfe6; | ||
| 253 | |||
| 254 | .p_box { | ||
| 255 | display: flex; | ||
| 256 | gap: 10px; | ||
| 257 | .people { | ||
| 258 | width: 298px; | ||
| 259 | background: #fbfcfd; | ||
| 260 | border-radius: 2px; | ||
| 261 | border: 1px solid #dcdfe6; | ||
| 262 | padding: 0 14px; | ||
| 263 | .prople_item { | ||
| 264 | display: flex; | ||
| 265 | justify-content: space-between; | ||
| 266 | align-items: center; | ||
| 267 | padding: 14px 0; | ||
| 268 | border-bottom: 1px solid #dcdfe6; | ||
| 269 | &:last-child { | ||
| 270 | border: none; | ||
| 271 | } | ||
| 272 | .name { | ||
| 273 | font-size: 16px; | ||
| 274 | color: #929aa0; | ||
| 275 | margin-bottom: 20px; | ||
| 276 | } | ||
| 277 | .idcard { | ||
| 278 | font-size: 10px; | ||
| 279 | color: #929aa0; | ||
| 280 | } | ||
| 281 | } | ||
| 282 | } | ||
| 283 | .btn { | ||
| 284 | width: 90px; | ||
| 285 | height: 40px; | ||
| 286 | background: #fbfcfd; | ||
| 287 | border-radius: 20px; | ||
| 288 | border: 1px solid #493ceb; | ||
| 289 | margin-top: 10px; | ||
| 290 | font-size: 14px; | ||
| 291 | color: #493ceb; | ||
| 292 | line-height: 40px; | ||
| 293 | text-align: center; | ||
| 294 | cursor: pointer; | ||
| 295 | user-select: none; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | } | ||
| 299 | } | ||
| 300 | } | ||
| 301 | |||
| 302 | .right { | ||
| 303 | width: 460px; | ||
| 304 | margin-left: 36px; | ||
| 305 | .tit_box { | ||
| 306 | display: flex; | ||
| 307 | align-items: center; | ||
| 308 | gap: 10px; | ||
| 309 | margin-bottom: 20px; | ||
| 310 | .txt { | ||
| 311 | font-weight: bold; | ||
| 312 | font-size: 16px; | ||
| 313 | color: #493ceb; | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | .detail { | ||
| 318 | padding: 15px 26px; | ||
| 319 | border-radius: 5px; | ||
| 320 | border: 1px solid #dcdfe6; | ||
| 321 | .detail_top { | ||
| 322 | padding-bottom: 13px; | ||
| 323 | border-bottom: 1px solid #dcdfe6; | ||
| 324 | .time { | ||
| 325 | font-weight: 500; | ||
| 326 | font-size: 18px; | ||
| 327 | color: #2d373f; | ||
| 328 | line-height: 25px; | ||
| 329 | margin-bottom: 8px; | ||
| 330 | } | ||
| 331 | .ticket { | ||
| 332 | font-size: 16px; | ||
| 333 | color: #2d373f; | ||
| 334 | } | ||
| 335 | } | ||
| 336 | .detail_center { | ||
| 337 | margin-top: 14px; | ||
| 338 | display: flex; | ||
| 339 | flex-direction: column; | ||
| 340 | padding-bottom: 13px; | ||
| 341 | border-bottom: 1px solid #dcdfe6; | ||
| 342 | gap: 8px; | ||
| 343 | .ticket { | ||
| 344 | font-size: 16px; | ||
| 345 | color: #2d373f; | ||
| 346 | } | ||
| 347 | } | ||
| 348 | .detail_b { | ||
| 349 | display: flex; | ||
| 350 | justify-content: space-between; | ||
| 351 | align-items: center; | ||
| 352 | margin-top: 16px; | ||
| 353 | .sum_txt { | ||
| 354 | font-weight: 600; | ||
| 355 | font-size: 18px; | ||
| 356 | color: #2d373f; | ||
| 357 | line-height: 25px; | ||
| 358 | } | ||
| 359 | .price_num { | ||
| 360 | font-weight: 600; | ||
| 361 | font-size: 36px; | ||
| 362 | color: #ff8124; | ||
| 363 | line-height: 50px; | ||
| 364 | } | ||
| 365 | } | ||
| 366 | } | ||
| 367 | } | ||
| 368 | |||
| 369 | .footer { | ||
| 370 | display: flex; | ||
| 371 | justify-content: space-between; | ||
| 372 | height: 70px; | ||
| 373 | align-items: center; | ||
| 374 | background: #ffffff; | ||
| 375 | box-shadow: 0px 0px 46px 0px rgba(1, 16, 64, 0.08); | ||
| 376 | margin-top: 9px; | ||
| 377 | padding: 0 30px; | ||
| 378 | .label { | ||
| 379 | font-size: 16px; | ||
| 380 | color: #7b7f83; | ||
| 381 | line-height: 22px; | ||
| 382 | } | ||
| 383 | .value { | ||
| 384 | font-size: 22px; | ||
| 385 | color: #ff8124; | ||
| 386 | line-height: 30px; | ||
| 387 | font-weight: 600; | ||
| 388 | } | ||
| 389 | .pay { | ||
| 390 | width: 200px; | ||
| 391 | height: 40px; | ||
| 392 | background: linear-gradient(270deg, #493ceb 0%, #8623fc 100%); | ||
| 393 | border-radius: 20px; | ||
| 394 | font-weight: 500; | ||
| 395 | font-size: 16px; | ||
| 396 | color: #ffffff; | ||
| 397 | line-height: 40px; | ||
| 398 | text-align: center; | ||
| 399 | cursor: pointer; | ||
| 400 | } | ||
| 401 | } | ||
| 402 | } | ||
| 403 | </style> |
src/viewsPc/seat/order-detail.vue
0 → 100644
This diff is collapsed.
Click to expand it.
src/viewsPc/seat/order-list.vue
0 → 100644
| 1 | <script setup> | ||
| 2 | import { getOrderList, immediatePay, cancelPay } from "./api/index.js"; | ||
| 3 | import qrCodeDialog from "./components/qrCodeDialog.vue"; | ||
| 4 | import { ElMessageBox, ElMessage } from "element-plus"; | ||
| 5 | |||
| 6 | const status = reactive({ | ||
| 7 | 0: { | ||
| 8 | txt: "待支付", | ||
| 9 | color: "#F740A6", | ||
| 10 | bgColor: "#FFE2F2", | ||
| 11 | }, | ||
| 12 | 1: { | ||
| 13 | txt: "已支付", | ||
| 14 | color: "#757575", | ||
| 15 | bgColor: "#DDDDDD", | ||
| 16 | }, | ||
| 17 | 2: { | ||
| 18 | txt: "未支付", | ||
| 19 | color: "#34C759", | ||
| 20 | bgColor: "#D2FFDD", | ||
| 21 | }, | ||
| 22 | 3: { | ||
| 23 | txt: "已退款", | ||
| 24 | color: "#FFCC00", | ||
| 25 | bgColor: "#FFF7D9", | ||
| 26 | }, | ||
| 27 | }); | ||
| 28 | |||
| 29 | const order = reactive({ | ||
| 30 | showCodeDialog: false, | ||
| 31 | qrInfo: {}, | ||
| 32 | pay_loading: false, | ||
| 33 | pageNo: 1, | ||
| 34 | pageSize: 10, | ||
| 35 | total: 0, | ||
| 36 | data: [], | ||
| 37 | fetchData() { | ||
| 38 | getOrderList({ pageNo: order.pageNo, pageSize: order.pageSize }).then( | ||
| 39 | (res) => { | ||
| 40 | order.data = res.data.lists; | ||
| 41 | order.total = res.data.count; | ||
| 42 | } | ||
| 43 | ); | ||
| 44 | }, | ||
| 45 | payment(it) { | ||
| 46 | if (order.pay_loading) return; | ||
| 47 | order.pay_loading = true; | ||
| 48 | immediatePay({ orderSn: it.orderSn, payType: 1 }) | ||
| 49 | .then((res) => { | ||
| 50 | order.qrInfo = res.data; | ||
| 51 | order.showCodeDialog = true; | ||
| 52 | }) | ||
| 53 | .finally(() => (order.pay_loading = false)); | ||
| 54 | }, | ||
| 55 | // 取消支付 | ||
| 56 | cancelPayment(it) { | ||
| 57 | ElMessageBox.confirm("确定取消支付吗?", "提示", { | ||
| 58 | confirmButtonText: "确认", | ||
| 59 | cancelButtonText: "取消", | ||
| 60 | type: "warning", | ||
| 61 | draggable: true, | ||
| 62 | }) | ||
| 63 | .then(() => { | ||
| 64 | cancelPay({ orderSn: it.orderSn }).then(() => { | ||
| 65 | order.fetchData(); | ||
| 66 | ElMessage({ | ||
| 67 | type: "success", | ||
| 68 | message: "操作成功", | ||
| 69 | }); | ||
| 70 | }); | ||
| 71 | }) | ||
| 72 | .catch(() => {}); | ||
| 73 | }, | ||
| 74 | }); | ||
| 75 | |||
| 76 | order.fetchData(); | ||
| 77 | </script> | ||
| 78 | |||
| 79 | <template> | ||
| 80 | <div class="container"> | ||
| 81 | <div | ||
| 82 | v-for="(it, index) in order.data" | ||
| 83 | :key="index" | ||
| 84 | @click=" | ||
| 85 | $router.push({ | ||
| 86 | path: '/seat/order_detail', | ||
| 87 | query: { orderSn: it.orderSn }, | ||
| 88 | }) | ||
| 89 | " | ||
| 90 | class="order-item" | ||
| 91 | > | ||
| 92 | <div class="info_box"> | ||
| 93 | <img class="cover_img" :src="it.coverImg" /> | ||
| 94 | <div class="info"> | ||
| 95 | <div class="title">{{ it.name }}</div> | ||
| 96 | <div class="common">时间:{{ it.dateStr }}</div> | ||
| 97 | <div class="common">地址:{{ it.placeName }}</div> | ||
| 98 | <div class="common">订单编号:{{ it.orderSn }}</div> | ||
| 99 | <div class="common">张数:{{ it.ticketNum }}张</div> | ||
| 100 | <div class="common">金额:¥{{ it.payAmount }}</div> | ||
| 101 | <div class="status"> | ||
| 102 | <div class="label">订单状态:</div> | ||
| 103 | <div class="value"> | ||
| 104 | <div | ||
| 105 | :style="{ | ||
| 106 | borderColor: status[it.state].color, | ||
| 107 | background: status[it.state].bgColor, | ||
| 108 | color: status[it.state].color, | ||
| 109 | }" | ||
| 110 | class="tag" | ||
| 111 | > | ||
| 112 | {{ status[it.state].txt }} | ||
| 113 | </div> | ||
| 114 | <div v-if="it.state == 0" class="tip"> | ||
| 115 | 请尽快完成支付,还剩{{ it.min }}分{{ it.sec }}秒 | ||
| 116 | </div> | ||
| 117 | </div> | ||
| 118 | </div> | ||
| 119 | </div> | ||
| 120 | </div> | ||
| 121 | <div v-if="it.state == 0" class="btn_box"> | ||
| 122 | <div class="pay" @click.stop="order.payment(it)">立即支付</div> | ||
| 123 | <div class="can_pay" @click.stop="order.cancelPayment(it)"> | ||
| 124 | 取消支付 | ||
| 125 | </div> | ||
| 126 | </div> | ||
| 127 | </div> | ||
| 128 | |||
| 129 | <qrCodeDialog | ||
| 130 | :showCodeDialog="order.showCodeDialog" | ||
| 131 | :qrCode="order.qrInfo?.scanCodeUrl" | ||
| 132 | /> | ||
| 133 | |||
| 134 | <div class="pagination"> | ||
| 135 | <el-pagination | ||
| 136 | v-show="order.total > 0" | ||
| 137 | v-model:current-page="order.pageNo" | ||
| 138 | v-model:page-size="order.pageSize" | ||
| 139 | background | ||
| 140 | layout="prev, pager, next" | ||
| 141 | :total="order.total" | ||
| 142 | @current-change="order.fetchData()" | ||
| 143 | /> | ||
| 144 | </div> | ||
| 145 | </div> | ||
| 146 | </template> | ||
| 147 | |||
| 148 | <style scoped lang="scss"> | ||
| 149 | .container { | ||
| 150 | width: 1200px; | ||
| 151 | margin: 0 auto; | ||
| 152 | padding: 26px 0; | ||
| 153 | font-family: SourceHanSansCN, SourceHanSansCN; | ||
| 154 | .order-item { | ||
| 155 | display: flex; | ||
| 156 | justify-content: space-between; | ||
| 157 | align-items: center; | ||
| 158 | padding: 36px; | ||
| 159 | background: #fff; | ||
| 160 | box-shadow: 0px 0px 46px 0px rgba(1, 16, 64, 0.08); | ||
| 161 | border-radius: 8px; | ||
| 162 | margin-bottom: 30px; | ||
| 163 | cursor: pointer; | ||
| 164 | .info_box { | ||
| 165 | display: flex; | ||
| 166 | gap: 20px; | ||
| 167 | .cover_img { | ||
| 168 | width: 155px; | ||
| 169 | height: 200px; | ||
| 170 | object-fit: fill; | ||
| 171 | } | ||
| 172 | .info { | ||
| 173 | .title { | ||
| 174 | font-weight: bold; | ||
| 175 | font-size: 22px; | ||
| 176 | color: #000000; | ||
| 177 | line-height: 33px; | ||
| 178 | margin-bottom: 25px; | ||
| 179 | margin-bottom: 10px; | ||
| 180 | } | ||
| 181 | .common { | ||
| 182 | font-weight: 500; | ||
| 183 | font-size: 16px; | ||
| 184 | color: #4e4e4e; | ||
| 185 | margin-bottom: 6px; | ||
| 186 | } | ||
| 187 | .status { | ||
| 188 | display: flex; | ||
| 189 | |||
| 190 | .label { | ||
| 191 | font-weight: 500; | ||
| 192 | font-size: 16px; | ||
| 193 | color: #4e4e4e; | ||
| 194 | line-height: 24px; | ||
| 195 | } | ||
| 196 | .value { | ||
| 197 | display: flex; | ||
| 198 | align-items: center; | ||
| 199 | gap: 20px; | ||
| 200 | .tag { | ||
| 201 | padding: 6px 14px; | ||
| 202 | border-radius: 6px; | ||
| 203 | border: 1px solid #34c759; | ||
| 204 | } | ||
| 205 | .tip { | ||
| 206 | font-size: 16px; | ||
| 207 | color: #f740a6; | ||
| 208 | line-height: 24px; | ||
| 209 | } | ||
| 210 | } | ||
| 211 | } | ||
| 212 | } | ||
| 213 | } | ||
| 214 | .btn_box { | ||
| 215 | display: flex; | ||
| 216 | flex-direction: column; | ||
| 217 | gap: 12px; | ||
| 218 | .pay { | ||
| 219 | width: 175px; | ||
| 220 | height: 40px; | ||
| 221 | background: linear-gradient(270deg, #493ceb 0%, #8623fc 100%); | ||
| 222 | border-radius: 20px; | ||
| 223 | font-weight: 500; | ||
| 224 | font-size: 16px; | ||
| 225 | color: #ffffff; | ||
| 226 | line-height: 40px; | ||
| 227 | text-align: center; | ||
| 228 | cursor: pointer; | ||
| 229 | } | ||
| 230 | .can_pay { | ||
| 231 | width: 175px; | ||
| 232 | height: 40px; | ||
| 233 | background: #fff; | ||
| 234 | border-radius: 20px; | ||
| 235 | font-weight: 500; | ||
| 236 | font-size: 16px; | ||
| 237 | color: #493ceb; | ||
| 238 | line-height: 40px; | ||
| 239 | border: 1px solid #493ceb; | ||
| 240 | text-align: center; | ||
| 241 | box-sizing: border-box; | ||
| 242 | cursor: pointer; | ||
| 243 | } | ||
| 244 | } | ||
| 245 | } | ||
| 246 | } | ||
| 247 | |||
| 248 | .pagination { | ||
| 249 | display: flex; | ||
| 250 | justify-content: center; | ||
| 251 | } | ||
| 252 | </style> |
src/viewsPc/seat/people-manage.vue
0 → 100644
| 1 | <script setup> | ||
| 2 | import { deleteViewPeople, viewPeopleList } from "./api/index.js"; | ||
| 3 | import { ElMessageBox, ElMessage } from "element-plus"; | ||
| 4 | |||
| 5 | |||
| 6 | const audience = reactive({ | ||
| 7 | data: [], | ||
| 8 | fetchData() { | ||
| 9 | viewPeopleList().then((res) => { | ||
| 10 | audience.data = res.data; | ||
| 11 | }); | ||
| 12 | }, | ||
| 13 | |||
| 14 | deletePeople(id) { | ||
| 15 | ElMessageBox.confirm("确定删除该观看人吗?", "提示", { | ||
| 16 | confirmButtonText: "确认", | ||
| 17 | cancelButtonText: "取消", | ||
| 18 | type: "warning", | ||
| 19 | draggable: true, | ||
| 20 | }) | ||
| 21 | .then(() => { | ||
| 22 | deleteViewPeople({ id }).then(() => { | ||
| 23 | audience.fetchData(); | ||
| 24 | ElMessage({ | ||
| 25 | type: "success", | ||
| 26 | message: "操作成功", | ||
| 27 | }); | ||
| 28 | }); | ||
| 29 | }) | ||
| 30 | .catch(() => {}); | ||
| 31 | }, | ||
| 32 | }); | ||
| 33 | |||
| 34 | audience.fetchData(); | ||
| 35 | </script> | ||
| 36 | |||
| 37 | <template> | ||
| 38 | <div class="container"> | ||
| 39 | <div class="title"> | ||
| 40 | <div | ||
| 41 | class="add_btn" | ||
| 42 | @click="$router.push({ path: '/seat/add_watch_people' })" | ||
| 43 | > | ||
| 44 | 新增 | ||
| 45 | </div> | ||
| 46 | 观影人管理 | ||
| 47 | </div> | ||
| 48 | <div class="content"> | ||
| 49 | <div class="people_box"> | ||
| 50 | <div | ||
| 51 | v-for="(it, index) in audience.data" | ||
| 52 | :key="index" | ||
| 53 | class="people_item" | ||
| 54 | > | ||
| 55 | <div class="name">{{ it.name }}</div> | ||
| 56 | <div class="idcard">身份证:{{ it.idCard }}</div> | ||
| 57 | <div class="btn" @click="audience.deletePeople(it.id)">删除</div> | ||
| 58 | </div> | ||
| 59 | </div> | ||
| 60 | </div> | ||
| 61 | </div> | ||
| 62 | </template> | ||
| 63 | |||
| 64 | <style scoped lang="scss"> | ||
| 65 | div { | ||
| 66 | box-sizing: border-box; | ||
| 67 | } | ||
| 68 | .container { | ||
| 69 | padding: 20px 0; | ||
| 70 | width: 1200px; | ||
| 71 | margin: 0 auto; | ||
| 72 | |||
| 73 | .title { | ||
| 74 | position: relative; | ||
| 75 | padding: 11px; | ||
| 76 | text-align: center; | ||
| 77 | background: linear-gradient(270deg, #493ceb 0%, #8623fc 100%); | ||
| 78 | font-size: 18px; | ||
| 79 | color: #ffffff; | ||
| 80 | .add_btn { | ||
| 81 | position: absolute; | ||
| 82 | left: 20px; | ||
| 83 | top: 50%; | ||
| 84 | transform: translateY(-50%); | ||
| 85 | width: 68px; | ||
| 86 | height: 24px; | ||
| 87 | border-radius: 12px; | ||
| 88 | border: 1px solid #ffffff; | ||
| 89 | font-weight: 400; | ||
| 90 | font-size: 12px; | ||
| 91 | color: #ffffff; | ||
| 92 | text-align: center; | ||
| 93 | line-height: 24px; | ||
| 94 | box-sizing: border-box; | ||
| 95 | user-select: none; | ||
| 96 | cursor: pointer; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | .content { | ||
| 101 | min-height: 590px; | ||
| 102 | background-color: #fff; | ||
| 103 | box-shadow: 0px 0px 46px 0px rgba(1, 16, 64, 0.08); | ||
| 104 | padding: 20px; | ||
| 105 | .people_box { | ||
| 106 | display: flex; | ||
| 107 | flex-wrap: wrap; | ||
| 108 | gap: 20px; | ||
| 109 | .people_item { | ||
| 110 | width: 275px; | ||
| 111 | height: 137px; | ||
| 112 | background: #ffffff; | ||
| 113 | border: 1px solid #dcdfe6; | ||
| 114 | padding: 16px; | ||
| 115 | .name { | ||
| 116 | font-weight: 600; | ||
| 117 | font-size: 16px; | ||
| 118 | color: #2d373f; | ||
| 119 | line-height: 22px; | ||
| 120 | } | ||
| 121 | .idcard { | ||
| 122 | font-size: 16px; | ||
| 123 | color: #95a1a6; | ||
| 124 | line-height: 22px; | ||
| 125 | margin-top: 12px; | ||
| 126 | margin-bottom: 17px; | ||
| 127 | } | ||
| 128 | .btn { | ||
| 129 | width: 69px; | ||
| 130 | height: 32px; | ||
| 131 | background: #e7e6ff; | ||
| 132 | font-weight: 400; | ||
| 133 | font-size: 16px; | ||
| 134 | color: #493ceb; | ||
| 135 | line-height: 32px; | ||
| 136 | text-align: center; | ||
| 137 | cursor: pointer; | ||
| 138 | user-select: none; | ||
| 139 | } | ||
| 140 | } | ||
| 141 | } | ||
| 142 | } | ||
| 143 | } | ||
| 144 | </style> |
src/viewsPc/seat/seat-picker.vue
0 → 100644
| 1 | <script setup> | ||
| 2 | import { ElMessage } from "element-plus"; | ||
| 3 | import { getPriceLevelInfo, getSiteConfig } from "./api/index.js"; | ||
| 4 | const route = useRoute(); | ||
| 5 | const router = useRouter(); | ||
| 6 | |||
| 7 | const iframeRef = ref(); | ||
| 8 | |||
| 9 | // 获取票档 | ||
| 10 | const price = reactive({ | ||
| 11 | curPriceId: route.query.ticket_block, | ||
| 12 | data: [], | ||
| 13 | |||
| 14 | fetchData() { | ||
| 15 | getPriceLevelInfo({ | ||
| 16 | actId: route.query?.actId ?? 1, | ||
| 17 | sessionId: route.query.sessionId, | ||
| 18 | openType: route.query.openType, | ||
| 19 | sitePlace: route.query.sitePlace, | ||
| 20 | ticketType: route.query.ticketType, | ||
| 21 | }).then((res) => { | ||
| 22 | this.data = res.data; | ||
| 23 | // price.curPriceId = route.query.ticket_block | ||
| 24 | }); | ||
| 25 | }, | ||
| 26 | onClickPrice(e) { | ||
| 27 | // if (selectedSeats.value?.length) { | ||
| 28 | // return ElMessage({ type: "warning", message: "请先取消已选座位" }); | ||
| 29 | // } | ||
| 30 | price.curPriceId = e.priceId; | ||
| 31 | }, | ||
| 32 | }); | ||
| 33 | |||
| 34 | // 座位禁用时图标地址 | ||
| 35 | const disabledIconUrl = | ||
| 36 | "http://114.55.227.212:8083/images/20240511/unselect_default.png"; | ||
| 37 | |||
| 38 | const siteConfig = reactive({ | ||
| 39 | loading: false, | ||
| 40 | data: [], | ||
| 41 | fetchData() { | ||
| 42 | return getSiteConfig({ | ||
| 43 | actId: route.query.actId ?? 1, | ||
| 44 | openType: route.query.openType, | ||
| 45 | sessionId: route.query.sessionId, | ||
| 46 | sitePlace: route.query.sitePlace, | ||
| 47 | ticketType: route.query.ticketType, | ||
| 48 | }).then((res) => { | ||
| 49 | const gridSize = 40; | ||
| 50 | const seat_arr = res.data.map((it, index) => { | ||
| 51 | return { | ||
| 52 | ...it, | ||
| 53 | // 这几个是iframe引擎渲染座位必须的属性,规定好的 | ||
| 54 | x: gridSize * it.x, | ||
| 55 | y: gridSize * it.y, | ||
| 56 | w: gridSize, | ||
| 57 | h: gridSize, | ||
| 58 | icon: it.state == 1 ? it.selectIcon : disabledIconUrl, // 图片的url | ||
| 59 | active: 0, // 是否选中 | ||
| 60 | priceId: route.query.openType == 0 ? it.dayPriceId : it.nightPriceId, | ||
| 61 | }; | ||
| 62 | }); | ||
| 63 | siteConfig.data = seat_arr; | ||
| 64 | return seat_arr; | ||
| 65 | }); | ||
| 66 | }, | ||
| 67 | }); | ||
| 68 | |||
| 69 | watch( | ||
| 70 | () => price.curPriceId, | ||
| 71 | (priceId) => { | ||
| 72 | siteConfig.data.forEach((it) => { | ||
| 73 | sendMsg("update-seat", { | ||
| 74 | id: it.id, | ||
| 75 | data: { | ||
| 76 | icon: | ||
| 77 | it.state == 1 && priceId == it.priceId | ||
| 78 | ? it.active | ||
| 79 | ? it.unSelectIcon | ||
| 80 | : it.selectIcon | ||
| 81 | : disabledIconUrl, | ||
| 82 | }, | ||
| 83 | }); | ||
| 84 | }); | ||
| 85 | console.log("update完成"); | ||
| 86 | }, | ||
| 87 | { immediate: true } | ||
| 88 | ); | ||
| 89 | |||
| 90 | const sendMsg = (type, data) => | ||
| 91 | iframeRef.value?.contentWindow.postMessage({ type: type, data: data }, "*"); | ||
| 92 | |||
| 93 | /** | ||
| 94 | * 1. 加載iframe 3. 请求API的数据 | ||
| 95 | * 2. 等待iframe里面资源加载完毕并触发picker-ready事件 | ||
| 96 | * 4. 传递数据给iframe,等待渲染 | ||
| 97 | */ | ||
| 98 | |||
| 99 | window.addEventListener("message", (e) => { | ||
| 100 | const data = e.data; | ||
| 101 | console.log("[parent]", data); | ||
| 102 | |||
| 103 | if (data.type == "picker-ready") { | ||
| 104 | // apiPromise.then(() => {}) | ||
| 105 | |||
| 106 | siteConfig.fetchData().then((res) => { | ||
| 107 | const seat_arr = res.map((it) => { | ||
| 108 | return { | ||
| 109 | ...it, | ||
| 110 | active: 0, | ||
| 111 | icon: | ||
| 112 | it.state == 1 && price.curPriceId == it.priceId | ||
| 113 | ? it.selectIcon | ||
| 114 | : disabledIconUrl, | ||
| 115 | }; | ||
| 116 | }); | ||
| 117 | // 子页面加载完毕,这里iframeRef一定ok | ||
| 118 | iframeRef.value.contentWindow.postMessage( | ||
| 119 | { | ||
| 120 | type: "load-seats", | ||
| 121 | data: seat_arr, | ||
| 122 | }, | ||
| 123 | "*" | ||
| 124 | ); | ||
| 125 | }); | ||
| 126 | } else if (data.type == "seat-click") { | ||
| 127 | // 子页面点击了座位 | ||
| 128 | const seatData = data.data; | ||
| 129 | console.log("座位点击", seatData); | ||
| 130 | |||
| 131 | // 如果座位处于不可点击状态,就return | ||
| 132 | if (seatData.state != 1) return; | ||
| 133 | |||
| 134 | // 如果当前筛选了某种座位,点击的不是这种座位,也返回 | ||
| 135 | if (price.curPriceId && seatData.priceId != price.curPriceId) return; | ||
| 136 | |||
| 137 | const newActive = seatData.active == 0 ? 1 : 0; | ||
| 138 | const siteConfigItem = siteConfig.data.find((it) => it.id == seatData.id); | ||
| 139 | if (siteConfigItem) { | ||
| 140 | siteConfigItem.active = newActive; | ||
| 141 | } | ||
| 142 | sendMsg("update-seat", { | ||
| 143 | id: seatData.id, | ||
| 144 | data: { | ||
| 145 | active: newActive, | ||
| 146 | icon: newActive ? seatData.unSelectIcon : seatData.selectIcon, | ||
| 147 | }, | ||
| 148 | }); | ||
| 149 | } | ||
| 150 | }); | ||
| 151 | const deleteSiteConfigItem = (seatData) => { | ||
| 152 | const newActive = seatData.active == 0 ? 1 : 0; | ||
| 153 | const siteConfigItem = siteConfig.data.find((it) => it.id == seatData.id); | ||
| 154 | if (siteConfigItem) { | ||
| 155 | siteConfigItem.active = newActive; | ||
| 156 | } | ||
| 157 | sendMsg("update-seat", { | ||
| 158 | id: seatData.id, | ||
| 159 | data: { icon: newActive ? seatData.unSelectIcon : seatData.selectIcon }, | ||
| 160 | }); | ||
| 161 | }; | ||
| 162 | |||
| 163 | /** 所选座位 */ | ||
| 164 | const selectedSeats = | ||
| 165 | computed(() => siteConfig.data.filter((it) => it.active == 1)) ?? []; | ||
| 166 | |||
| 167 | /** 所选座位价格 */ | ||
| 168 | const sumPrice = computed(() => { | ||
| 169 | return selectedSeats.value.reduce((total, item) => { | ||
| 170 | const price = | ||
| 171 | route.query.openType == 0 | ||
| 172 | ? Number(item.dayPrice) | ||
| 173 | : Number(item.nightPrice); | ||
| 174 | return total + price; | ||
| 175 | }, 0); | ||
| 176 | }); | ||
| 177 | |||
| 178 | const toConfirmOrder = () => { | ||
| 179 | const seatIds = selectedSeats.value.map((it) => it.id); | ||
| 180 | if (!seatIds.length) | ||
| 181 | return ElMessage({ type: "warning", message: "请先选择座位" }); | ||
| 182 | |||
| 183 | router.push({ | ||
| 184 | path: "/seat/confirm_order", | ||
| 185 | query: { | ||
| 186 | openType: route.query.openType, | ||
| 187 | sessionId: route.query.sessionId, | ||
| 188 | sitePlace: route.query.sitePlace, | ||
| 189 | ticketType: route.query.ticketType, | ||
| 190 | seatIds: seatIds.join(","), | ||
| 191 | }, | ||
| 192 | }); | ||
| 193 | }; | ||
| 194 | |||
| 195 | price.fetchData(); | ||
| 196 | </script> | ||
| 197 | |||
| 198 | <template> | ||
| 199 | <div class="container"> | ||
| 200 | <div class="top"> | ||
| 201 | <div class="time"> | ||
| 202 | <span>{{ route.query?.time_txt }}</span> | ||
| 203 | <span class="place">{{ route.query.sitePlace }}</span> | ||
| 204 | </div> | ||
| 205 | <div class="price_tab"> | ||
| 206 | <div | ||
| 207 | v-for="(it, index) in price.data" | ||
| 208 | class="tab_item" | ||
| 209 | :class="{ tabActive: it.priceId == price.curPriceId }" | ||
| 210 | @click="price.onClickPrice(it)" | ||
| 211 | > | ||
| 212 | <img class="seat" :src="it.selectIcon" /> | ||
| 213 | <span class="price">{{ it.price }}¥</span> | ||
| 214 | </div> | ||
| 215 | </div> | ||
| 216 | </div> | ||
| 217 | |||
| 218 | <div v-if="selectedSeats?.length" class="bottom"> | ||
| 219 | <div class="seat_box"> | ||
| 220 | <!-- v-for="(it, index) in selectedSeats" --> | ||
| 221 | <div v-for="(it, index) in selectedSeats" class="seat_item"> | ||
| 222 | <img class="seat_icon" :src="it.selectIcon" /> | ||
| 223 | <span class="num">{{ it.area }}区 {{ it.pai }}排{{ it.no }}座</span> | ||
| 224 | <el-icon | ||
| 225 | style="cursor: pointer" | ||
| 226 | color="#ccc" | ||
| 227 | @click="deleteSiteConfigItem(it)" | ||
| 228 | ><CircleCloseFilled | ||
| 229 | /></el-icon> | ||
| 230 | </div> | ||
| 231 | </div> | ||
| 232 | <div class="pay"> | ||
| 233 | <div class="sum">¥{{ sumPrice?.toFixed(2) }}</div> | ||
| 234 | <div class="pay_btn" @click="toConfirmOrder()">立即购买</div> | ||
| 235 | </div> | ||
| 236 | </div> | ||
| 237 | |||
| 238 | <div class="iframeBox"> | ||
| 239 | <iframe | ||
| 240 | ref="iframeRef" | ||
| 241 | class="iframe" | ||
| 242 | id="iframe" | ||
| 243 | src="http://seat-choose.parent4relax.com/#/seat-picker" | ||
| 244 | ></iframe> | ||
| 245 | </div> | ||
| 246 | </div> | ||
| 247 | </template> | ||
| 248 | |||
| 249 | <style scoped lang="scss"> | ||
| 250 | .container { | ||
| 251 | width: 1200px; | ||
| 252 | margin: 0 auto; | ||
| 253 | padding: 20px; | ||
| 254 | |||
| 255 | .top { | ||
| 256 | width: 100%; | ||
| 257 | background-color: #fff; | ||
| 258 | padding: 20px; | ||
| 259 | margin-bottom: 10px; | ||
| 260 | border-radius: 6px; | ||
| 261 | .time { | ||
| 262 | font-size: 18px; | ||
| 263 | font-weight: 600; | ||
| 264 | margin-bottom: 10px; | ||
| 265 | .place { | ||
| 266 | color: #7e8489; | ||
| 267 | margin-left: 15px; | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | .price_tab { | ||
| 272 | display: flex; | ||
| 273 | align-items: center; | ||
| 274 | flex-wrap: wrap; | ||
| 275 | gap: 10px; | ||
| 276 | .tabActive { | ||
| 277 | background: #eeeeee !important; | ||
| 278 | border: 2px solid #7e8489 !important; | ||
| 279 | } | ||
| 280 | .tab_item { | ||
| 281 | display: flex; | ||
| 282 | align-items: center; | ||
| 283 | padding: 10px 14px; | ||
| 284 | background: #f5f7f8; | ||
| 285 | border-radius: 30px; | ||
| 286 | border: 2px solid #dcdedf; | ||
| 287 | font-size: 16px; | ||
| 288 | color: #646666; | ||
| 289 | cursor: pointer; | ||
| 290 | user-select: none; | ||
| 291 | |||
| 292 | .seat { | ||
| 293 | width: 14px; | ||
| 294 | height: 14px; | ||
| 295 | margin-right: 5px; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | .iframeBox { | ||
| 302 | border-radius: 6px; | ||
| 303 | background-color: #fff; | ||
| 304 | padding: 20px; | ||
| 305 | margin-bottom: 20px; | ||
| 306 | } | ||
| 307 | |||
| 308 | .iframe { | ||
| 309 | width: 100%; | ||
| 310 | height: 500px; | ||
| 311 | border: none; | ||
| 312 | background-color: #f7f8fa; | ||
| 313 | } | ||
| 314 | |||
| 315 | .bottom { | ||
| 316 | border-radius: 6px; | ||
| 317 | background-color: #fff; | ||
| 318 | padding: 20px; | ||
| 319 | margin-bottom: 20px; | ||
| 320 | .seat_box { | ||
| 321 | display: flex; | ||
| 322 | flex-wrap: wrap; | ||
| 323 | gap: 10px; | ||
| 324 | width: 100%; | ||
| 325 | .seat_item { | ||
| 326 | display: flex; | ||
| 327 | align-items: center; | ||
| 328 | padding: 10px 14px; | ||
| 329 | font-size: 16px; | ||
| 330 | color: #29343c; | ||
| 331 | background: #eeeeee; | ||
| 332 | border-radius: 30px; | ||
| 333 | border: 2px solid #7e8489; | ||
| 334 | user-select: none; | ||
| 335 | .seat_icon { | ||
| 336 | width: 14px; | ||
| 337 | height: 14px; | ||
| 338 | margin-right: 5px; | ||
| 339 | } | ||
| 340 | .num { | ||
| 341 | margin-right: 5px; | ||
| 342 | } | ||
| 343 | } | ||
| 344 | } | ||
| 345 | |||
| 346 | .pay { | ||
| 347 | display: flex; | ||
| 348 | justify-content: space-between; | ||
| 349 | align-items: center; | ||
| 350 | margin-top: 10px; | ||
| 351 | .sum { | ||
| 352 | font-weight: 600; | ||
| 353 | font-size: 22px; | ||
| 354 | color: #493ceb; | ||
| 355 | } | ||
| 356 | .pay_btn { | ||
| 357 | width: 200px; | ||
| 358 | height: 40px; | ||
| 359 | background: #493ceb; | ||
| 360 | border-radius: 20px; | ||
| 361 | margin-top: 10px; | ||
| 362 | font-size: 14px; | ||
| 363 | color: #fff; | ||
| 364 | line-height: 40px; | ||
| 365 | text-align: center; | ||
| 366 | cursor: pointer; | ||
| 367 | font-weight: 600; | ||
| 368 | user-select: none; | ||
| 369 | } | ||
| 370 | } | ||
| 371 | } | ||
| 372 | } | ||
| 373 | </style> |
src/viewsPc/seat/seat.vue
0 → 100644
src/viewsPc/seat/ticket-detail.vue
0 → 100644
This diff is collapsed.
Click to expand it.
src/viewsPc/seat/utils/local-store.js
0 → 100644
| 1 | /** 用户登录token储存的key */ | ||
| 2 | export const TOKEN_KEY = "SEAT_TOKEN"; | ||
| 3 | |||
| 4 | /** 设置token */ | ||
| 5 | export const setToken = (token) => localStorage.setItem(TOKEN_KEY, token); | ||
| 6 | |||
| 7 | /** | ||
| 8 | * 获取登录token | ||
| 9 | * @param drop 是否清空 | ||
| 10 | */ | ||
| 11 | export const getToken = (drop = false) => { | ||
| 12 | let token = localStorage.getItem(TOKEN_KEY); | ||
| 13 | if (!token) return null; | ||
| 14 | if (drop) localStorage.removeItem(TOKEN_KEY); | ||
| 15 | return token; | ||
| 16 | }; |
src/viewsPc/seat/utils/request.js
0 → 100644
| 1 | // http.js | ||
| 2 | |||
| 3 | import axios from "axios"; | ||
| 4 | import { getToken } from "./local-store"; | ||
| 5 | import { ElMessage } from "element-plus"; | ||
| 6 | |||
| 7 | const baseURL = "http://101.43.15.205:8084"; //"http://book.xiaojinyu.games"; // 这里填入你的基础 API URL | ||
| 8 | const timeout = 15000; // 请求超时时间 | ||
| 9 | |||
| 10 | const http = axios.create({ | ||
| 11 | baseURL, | ||
| 12 | timeout, | ||
| 13 | headers: { | ||
| 14 | "Content-Type": "application/json", | ||
| 15 | }, | ||
| 16 | }); | ||
| 17 | |||
| 18 | // 请求拦截器 | ||
| 19 | http.interceptors.request.use( | ||
| 20 | (config) => { | ||
| 21 | // 在发送请求之前做些什么 | ||
| 22 | const TOKEN = getToken(); | ||
| 23 | config.headers.Authorization = TOKEN; | ||
| 24 | if (config.method == "get") config.params = config.data; | ||
| 25 | return config; | ||
| 26 | }, | ||
| 27 | (error) => { | ||
| 28 | return Promise.reject(error); | ||
| 29 | } | ||
| 30 | ); | ||
| 31 | |||
| 32 | // 响应拦截器 | ||
| 33 | http.interceptors.response.use( | ||
| 34 | (response) => { | ||
| 35 | // 判断是否有异常 | ||
| 36 | let error = null; // 若无异常此值为null | ||
| 37 | if (response.status !== 200) { | ||
| 38 | error = Error(`Request failed with statuCode ${response.status}`); | ||
| 39 | } | ||
| 40 | |||
| 41 | if (response.data.code != 200) { | ||
| 42 | return ElMessage({ type: "error", message: response.data.msg }); | ||
| 43 | } | ||
| 44 | |||
| 45 | return response.data; | ||
| 46 | }, | ||
| 47 | (error) => { | ||
| 48 | // 对响应错误做点什么 | ||
| 49 | return Promise.reject(error); | ||
| 50 | } | ||
| 51 | ); | ||
| 52 | |||
| 53 | // 封装请求函数 | ||
| 54 | const request = (method, url, data = null) => { | ||
| 55 | return http({ | ||
| 56 | method, | ||
| 57 | url, | ||
| 58 | data, | ||
| 59 | }); | ||
| 60 | }; | ||
| 61 | |||
| 62 | export default request; |
-
Please register or sign in to post a comment