6fe7ecb0 by yyx

1

1 parent 89a307bd
......@@ -514,6 +514,12 @@ export const constantRoutes = [
meta: { title: '购票详情' }
},
{
path: 'seat_picker',
name: 'seat_picker',
component: () => import('@/viewsPc/seat/seat-picker'),
meta: { title: '选座' }
},
{
path: 'order',
name: 'seat_order',
component: () => import('@/viewsPc/seat/order-list'),
......
<script setup></script>
<script setup>
import { ElMessage } from "element-plus";
import { addViewPeople } from "./api/index.js";
const router = useRouter();
const people = reactive({
form: {
name: "",
idCard: "",
},
type: "身份证",
onConfirm() {
if (!people.form.name)
return ElMessage({ type: "warning", message: "请输入姓名" });
if (!people.form.idCard)
return ElMessage({ type: "warning", message: "请输入证件号" });
// 使用正则验证身份证号码格式
const idCardRegex =
/^[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]$/;
if (!idCardRegex.test(people.form.idCard))
return ElMessage({ type: "warning", message: "身份证号格式不正确" });
addViewPeople(people.form).then((res) => {
ElMessage({ type: "success", message: "操作成功" });
router.go(-2);
});
},
});
</script>
<template>
<div class="container">
......@@ -8,15 +38,15 @@
<div>
<div class="label">姓名</div>
<el-input
v-model="input"
v-model="people.form.name"
style="width: 570px"
placeholder="Please input"
placeholder="请输入姓名"
/>
</div>
<div>
<div class="label">证件类型</div>
<el-input
v-model="input"
v-model="people.type"
style="width: 570px"
placeholder="Please input"
readonly
......@@ -27,9 +57,9 @@
<div>
<div class="label">身份证号</div>
<el-input
v-model="input"
v-model="people.form.idCard"
style="width: 570px"
placeholder="Please input"
placeholder="请输入身份证号"
/>
</div>
</div>
......@@ -37,7 +67,7 @@
<div class="footer">
<div class="can_pay">取消</div>
<div class="pay">确认</div>
<div class="pay" @click="people.onConfirm()">确认</div>
</div>
</div>
</template>
......
import request from "../utils/request";
export const loginFree = (data) => request("POST", "/login/loginFree", data);
/** 活动详情 */
export const activityDetail = (data) =>
request("GET", `/api/activity/detail/${data.actId}`, data);
/** 场次详情 */
export const sessionDetail = (data) =>
request("GET", `/api/activity/sessionDetail/${data.actId}`, data);
/** 获取场馆信息 */
export const getSitePlaceInfo = (data) =>
request("GET", `/api/activity/getSitePlaceInfo`, data);
/** 获取票档信息 */
export const getPriceLevelInfo = (data) =>
request("GET", `/api/activity/getPriceLevelInfo`, data);
/** 获取座位信息 */
export const getSiteConfig = (data) =>
request("GET", `/api/activity/getSiteConfig`, data);
/** 确认订单 */
export const confirmOrder = (data) =>
request("POST", `/api/order/confirmOrder`, data);
/** 订单支付 */
export const payOrder = (data) =>
request("POST", `/api/order/payment`, data);
/** 观众列表 */
export const viewPeopleList = (data) =>
request("GET", `/api/customer/list`, data);
/** 删除观众 */
export const deleteViewPeople = (data) =>
request("POST", `/api/customer/delete/${data.id}`, data);
/** 新增观众 */
export const addViewPeople = (data) =>
request("POST", `/api/customer/add`, data);
/** 订单列表 */
export const getOrderList = (data) =>
request("GET", `/api/order/list`, data);
/** 立即支付 */
export const immediatePay = (data) =>
request("POST", `/api/order/immediatePay`, data);
/** 取消支付 */
export const cancelPay = (data) =>
request("POST", `/api/order/cancelPay/${data.orderSn}`, data);
/** 退单 */
export const cancelOrder = (data) =>
request("POST", `/api/order/cancelOrder/${data.orderSn}`, data);
/** 订单详情 */
export const getOrderDetail = (data) =>
request("GET", `/api/order/detail/${data.orderSn}`, data);
<script setup>
const props = defineProps({
showCodeDialog: {
type: Boolean,
default: false,
},
qrCode: {
type: String,
default: ''
}
});
</script>
<template>
<div>
<el-dialog v-model="props.showCodeDialog" title="支付" width="300">
<div>
<img class="qrcode" :src="props.qrCode" />
</div>
</el-dialog>
</div>
</template>
<style scoped lang="scss">
.qrcode {
width: 150px;
height: 150px;
background-color: #8623fc;
margin: 0 auto;
}
</style>
<script setup></script>
<script setup>
import { confirmOrder } from "./api/index.js";
import { ElMessage } from "element-plus";
import { payOrder, viewPeopleList } from "./api/index.js";
import { reactive } from "vue";
import qrCodeDialog from "./components/qrCodeDialog.vue";
const route = useRoute();
const router = useRouter();
const payment = reactive({
showCodeDialog: false,
btn_loading: false,
form: {
viewers: [],
phone: "",
},
qrInfo: {},
paymentHandle() {
if (payment.form.viewers.length != order.data?.seatInfo?.length)
return ElMessage({ type: "warning", message: "观看人与购买票数不符" });
if (!payment.form.phone)
return ElMessage({ type: "warning", message: "请输入联系人电话" });
payOrder({
contactPhone: payment.form.phone,
customerIds: payment.form.viewers,
orderToken: order.data?.orderToken,
payType: 1,
paymentAmount: order.data?.paymentAmount,
}).then((res) => {
// TODO: 这里有一个二维码
payment.qrInfo = res.data;
payment.showCodeDialog = true;
router.push({
path: "/seat/order",
});
});
},
});
const order = reactive({
data: null,
fetchData() {
confirmOrder({
actId: route.query.actId ?? 1,
openType: route.query.openType,
sessionId: route.query.sessionId,
sitePlace: route.query.sitePlace,
ticketType: route.query.ticketType,
seatIds: route.query.seatIds.split(","),
}).then((res) => {
this.data = res.data;
});
},
});
const audience = reactive({
data: [],
fetchData() {
viewPeopleList().then((res) => {
audience.data = res.data;
});
},
});
audience.fetchData();
order.fetchData();
</script>
<template>
<div class="container">
......@@ -6,8 +73,8 @@
<div class="content">
<div class="left">
<div class="info">
<div class="name">2024亚洲舞蹈比赛无锡站</div>
<div class="address">无锡会展中心</div>
<div class="name">{{ order.data?.activityName }}</div>
<div class="address">{{ order.data?.placeName }}</div>
</div>
<div class="ticket_info">
......@@ -19,27 +86,39 @@
<div class="form">
<el-form>
<el-form-item label="联系人">
<el-input placeholder="请输入手机号" style="width: 260px" />
<el-input
v-model="payment.form.phone"
placeholder="请输入联系人电话"
style="width: 260px"
/>
</el-form-item>
<el-form-item label="观看人">
<div class="p_box">
<div class="people">
<el-checkbox-group @change="">
<el-checkbox-group
v-model="payment.form.viewers"
:max="order.data?.seatInfo?.length"
>
<div
v-for="(it, index) in 5"
v-for="(it, index) in audience.data"
:key="index"
class="prople_item"
>
<div>
<div class="name">张三</div>
<div class="idcard">412************073</div>
<div class="name">{{ it.name }}</div>
<div class="idcard">{{ it.idCard }}</div>
</div>
<el-checkbox> </el-checkbox>
<el-checkbox :value="it.id"> </el-checkbox>
</div>
</el-checkbox-group>
</div>
<!-- button -->
<div class="btn">新增</div>
<div
class="btn"
@click="$router.push({ path: '/seat/people_manage' })"
>
新增
</div>
</div>
</el-form-item>
</el-form>
......@@ -55,17 +134,28 @@
<div class="detail">
<div class="detail_top">
<div class="time">2024.03.03 周六</div>
<div class="ticket">80.00元票档 X2张</div>
<div class="time">{{ order.data?.dateStr }}</div>
<div class="ticket">
{{ order.data?.singlePrice }}元票档 x{{
order.data?.seatInfo?.length
}}
</div>
</div>
<div class="detail_center">
<div v-for="(it, index) in 4" :key="index" class="ticket">
A区 6排14座 (B6馆)
<div
v-for="(it, index) in order.data?.seatInfo"
:key="index"
class="ticket"
>
<span v-if="it.venueId == 1">{{ it.area }}</span>
{{ it.pai }}{{ it.no }}座 ({{
it.venueId == 1 ? "B6" : "B4"
}}馆)
</div>
</div>
<div class="detail_b">
<div class="sum_txt">共计</div>
<div class="price_num">¥160.00</div>
<div class="price_num">¥{{ order.data?.paymentAmount }}</div>
</div>
</div>
</div>
......@@ -74,8 +164,13 @@
<div>
<span class="label">共计金额:</span><span class="value">¥900.00</span>
</div>
<div class="pay">立即支付</div>
<div class="pay" @click="payment.paymentHandle()">立即支付</div>
</div>
<qrCodeDialog
:showCodeDialog="payment.showCodeDialog"
:qrCode="payment.qrInfo?.scanCodeUrl"
/>
</div>
</template>
......@@ -83,6 +178,12 @@
div {
box-sizing: border-box;
}
.qrcode {
width: 150px;
height: 150px;
background-color: #8623fc;
margin: 0 auto;
}
.container {
padding: 20px 0;
width: 1200px;
......@@ -286,17 +387,17 @@ div {
font-weight: 600;
}
.pay {
width: 200px;
height: 40px;
background: linear-gradient(270deg, #493ceb 0%, #8623fc 100%);
border-radius: 20px;
font-weight: 500;
font-size: 16px;
color: #ffffff;
line-height: 40px;
text-align: center;
cursor: pointer;
}
width: 200px;
height: 40px;
background: linear-gradient(270deg, #493ceb 0%, #8623fc 100%);
border-radius: 20px;
font-weight: 500;
font-size: 16px;
color: #ffffff;
line-height: 40px;
text-align: center;
cursor: pointer;
}
}
}
</style>
......
<script setup></script>
<script setup>
import { reactive } from "vue";
import { cancelOrder, getOrderDetail } from "./api/index.js";
import qrCodeDialog from "./components/qrCodeDialog.vue";
import { ElMessageBox, ElMessage } from "element-plus";
const route = useRoute();
const router = useRouter();
const status = reactive({
0: {
txt: "待支付",
color: "#F740A6",
bgColor: "#FFE2F2",
},
1: {
txt: "已支付",
color: "#757575",
bgColor: "#DDDDDD",
},
2: {
txt: "未支付",
color: "#34C759",
bgColor: "#D2FFDD",
},
3: {
txt: "已退款",
color: "#FFCC00",
bgColor: "#FFF7D9",
},
});
const detail = reactive({
data: null,
fetchData() {
getOrderDetail({ orderSn: route.query.orderSn }).then((res) => {
detail.data = res.data;
});
},
});
detail.fetchData();
</script>
<template>
<div class="container">
......@@ -14,11 +56,17 @@
</div>
<div class="line"></div>
<div class="tr">
<div style="width: 30%" class="td">2024年亚洲舞蹈大赛无锡站</div>
<div style="width: 25%" class="td">无锡太湖国际博览中心</div>
<div style="width: 20%" class="td">¥80.00</div>
<div style="width: 12%" class="td">x3</div>
<div style="width: 13%; text-align: right" class="td">¥240.00</div>
<div style="width: 30%" class="td">{{ detail.data?.name }}</div>
<div style="width: 25%" class="td">{{ detail.data?.placeName }}</div>
<div style="width: 20%" class="td">
¥{{ detail.data?.singlePrice }}
</div>
<div style="width: 12%" class="td">
x{{ detail.data?.seatList?.length }}
</div>
<div style="width: 13%; text-align: right" class="td">
¥{{ detail.data?.payAmount }}
</div>
</div>
</div>
<!-- 座位 -->
......@@ -30,17 +78,20 @@
</div>
<div class="tr">
<div style="width: 30.33%" class="td flex-col">
<div>2024.05.03 周六</div>
<div>A区 6排16座 (B6馆)</div>
<div>A区 6排16座 (B6馆)</div>
<div>A区 6排16座 (B6馆)</div>
<div>{{ detail.data?.dateStr }}</div>
<div v-for="(it, index) in detail.data?.seatList" :key="index">
<span v-if="it.venueId == 1">{{ it.area }}</span
>{{ it.pai }}{{ it.no }}座 ({{
it.venueId == 1 ? "B6" : "B4"
}}馆)
</div>
</div>
<div style="width: 30.33%" class="td flex-col">
<div>订单编号:12783893435</div>
<div>创建时间:2024.05.07 16:3</div>
<div>订单编号:{{ detail.data?.orderSn }}</div>
<div>创建时间:{{ detail.data?.orderTime }}</div>
</div>
<div style="width: 30.33%" class="td">
<div>联系电话:12783893435</div>
<div>联系电话:{{ detail.data?.contactPhone }}</div>
</div>
</div>
</div>
......@@ -48,9 +99,13 @@
<div class="pay_ticket">
<div class="title">购票人</div>
<div class="people">
<div v-for="(it, index) in 4" :key="index" class="p_info">
<div>张三</div>
<div class="idcard">身份证:244************0</div>
<div
v-for="(it, index) in detail.data?.customerList"
:key="index"
class="p_info"
>
<div>{{ it.name }}</div>
<div class="idcard">身份证:{{ it.idCard }}</div>
</div>
</div>
</div>
......@@ -61,19 +116,31 @@
<div class="title">结算信息</div>
<div class="cell">
<div class="label">订单状态</div>
<div class="value">待支付</div>
<div class="value">{{ status[detail.data?.state]?.txt }}</div>
</div>
<div class="cell">
<div class="label">订单金额</div>
<div class="value">待支付</div>
<div class="value">¥{{ detail.data?.payAmount }}</div>
</div>
<!-- button -->
<div class="btn_box">
<div v-if="detail.data?.state == 0" class="btn_box">
<div class="can_pay">取消支付</div>
<div class="pay">立即支付</div>
</div>
<div v-else>
<div v-if="detail.data?.state == 1 && detail.data?.isRefund" class="btn_box">
<div class="can_pay">取消购票</div>
<div class="pay">再来一单</div>
</div>
<div v-else class="btn_box">
<div class="pay_dis">请联系工作人员</div>
</div>
</div>
</div>
<div v-if="detail.data?.state == 0" class="tip">
请尽快完成支付,还剩15分00秒
</div>
<div class="tip">请尽快完成支付,还剩15分00秒</div>
</div>
</div>
</template>
......@@ -216,6 +283,18 @@
padding-top: 20px;
display: flex;
gap: 20px;
.pay_dis {
width: 360px;
height: 40px;
background: #A09DFF;
border-radius: 20px;
font-weight: 500;
font-size: 16px;
color: #ffffff;
line-height: 40px;
text-align: center;
cursor: pointer;
}
.pay {
width: 170px;
height: 40px;
......
<script setup>
import { getOrderList, immediatePay, cancelPay } from "./api/index.js";
import qrCodeDialog from "./components/qrCodeDialog.vue";
import { ElMessageBox, ElMessage } from "element-plus";
const status = reactive({
0: {
txt: "待支付",
......@@ -21,48 +25,122 @@ const status = reactive({
bgColor: "#FFF7D9",
},
});
const order = reactive({
showCodeDialog: false,
qrInfo: {},
pay_loading: false,
pageNo: 1,
pageSize: 10,
total: 0,
data: [],
fetchData() {
getOrderList({ pageNo: order.pageNo, pageSize: order.pageSize }).then(
(res) => {
order.data = res.data.lists;
order.total = res.data.count;
}
);
},
payment(it) {
if (order.pay_loading) return;
order.pay_loading = true;
immediatePay({ orderSn: it.orderSn, payType: 1 })
.then((res) => {
order.qrInfo = res.data;
order.showCodeDialog = true;
})
.finally(() => (order.pay_loading = false));
},
cancelPayment(it) {
ElMessageBox.confirm("确定取消支付吗?", "Warning", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "warning",
draggable: true,
})
.then(() => {
cancelPay({ orderSn: it.orderSn }).then(() => {
order.fetchData();
ElMessage({
type: "success",
message: "操作成功",
});
});
})
.catch(() => {});
},
});
order.fetchData();
</script>
<template>
<div class="container">
<div
v-for="(it, index) in 10"
v-for="(it, index) in order.data"
:key="index"
@click="$router.push({ path: '/seat/order_detail' })"
@click="
$router.push({
path: '/seat/order_detail',
query: { orderSn: it.orderSn },
})
"
class="order-item"
>
<div class="info_box">
<img class="cover_img" />
<img class="cover_img" :src="it.coverImg" />
<div class="info">
<div class="title">2024年亚洲舞蹈大赛无锡站</div>
<div class="common">时间:2024.05.06 周六</div>
<div class="common">地址:无锡太湖博览中心</div>
<div class="common">订单编号:739274039504</div>
<div class="common">张数:2</div>
<div class="common">金额:¥728.00</div>
<div class="title">{{ it.name }}</div>
<div class="common">时间:{{ it.dateStr }}</div>
<div class="common">地址:{{ it.placeName }}</div>
<div class="common">订单编号:{{ it.orderSn }}</div>
<div class="common">张数:{{ it.ticketNum }}</div>
<div class="common">金额:¥{{ it.payAmount }}</div>
<div class="status">
<div class="label">订单状态:</div>
<div class="value">
<div
:style="{
borderColor: status[0].color,
background: status[0].bgColor,
color: status[0].color,
borderColor: status[it.state].color,
background: status[it.state].bgColor,
color: status[it.state].color,
}"
class="tag"
>
{{ status[0].txt }}
{{ status[it.state].txt }}
</div>
<div v-if="it.state == 0" class="tip">
请尽快完成支付,还剩15分00秒
</div>
<div v-if="true" class="tip">请尽快完成支付,还剩15分00秒</div>
</div>
</div>
</div>
</div>
<div class="btn_box">
<div class="pay">立即支付</div>
<div class="can_pay">取消支付</div>
<div v-if="it.state == 0" class="btn_box">
<div class="pay" @click.stop="order.payment(it)">立即支付</div>
<div class="can_pay" @click.stop="order.cancelPayment(it)">
取消支付
</div>
</div>
</div>
<qrCodeDialog
:showCodeDialog="order.showCodeDialog"
:qrCode="order.qrInfo?.scanCodeUrl"
/>
<div class="pagination">
<el-pagination
v-show="order.total > 0"
v-model:current-page="order.pageNo"
v-model:page-size="order.pageSize"
background
layout="prev, pager, next"
:total="order.total"
@current-change="order.fetchData()"
/>
</div>
</div>
</template>
......@@ -87,6 +165,7 @@ const status = reactive({
.cover_img {
width: 155px;
height: 200px;
object-fit: fill;
}
.info {
.title {
......@@ -163,4 +242,9 @@ const status = reactive({
}
}
}
.pagination {
display: flex;
justify-content: center;
}
</style>
......
<script setup></script>
<script setup>
import { deleteViewPeople, viewPeopleList } from "./api/index.js";
import { ElMessageBox, ElMessage } from "element-plus";
const audience = reactive({
data: [],
fetchData() {
viewPeopleList().then((res) => {
audience.data = res.data;
});
},
deletePeople(id) {
ElMessageBox.confirm("确定删除该观看人吗?", "Warning", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "warning",
draggable: true,
})
.then(() => {
deleteViewPeople({ id }).then(() => {
audience.fetchData();
ElMessage({
type: "success",
message: "操作成功",
});
});
})
.catch(() => {});
},
});
audience.fetchData();
</script>
<template>
<div class="container">
<div class="title">观影人管理</div>
<div class="title">
<div
class="add_btn"
@click="$router.push({ path: '/seat/add_watch_people' })"
>
新增
</div>
观影人管理
</div>
<div class="content">
<div class="people_box">
<div v-for="(it, index) in 10" :key="index" class="people_item">
<div class="name">朱育杰</div>
<div class="idcard">身份证:244************074</div>
<div class="btn">删除</div>
<div
v-for="(it, index) in audience.data"
:key="index"
class="people_item"
>
<div class="name">{{ it.name }}</div>
<div class="idcard">身份证:{{ it.idCard }}</div>
<div class="btn" @click="audience.deletePeople(it.id)">删除</div>
</div>
</div>
</div>
......@@ -25,11 +71,30 @@ div {
margin: 0 auto;
.title {
position: relative;
padding: 11px;
text-align: center;
background: linear-gradient(270deg, #493ceb 0%, #8623fc 100%);
font-size: 18px;
color: #ffffff;
.add_btn {
position: absolute;
left: 20px;
top: 50%;
transform: translateY(-50%);
width: 68px;
height: 24px;
border-radius: 12px;
border: 1px solid #ffffff;
font-weight: 400;
font-size: 12px;
color: #ffffff;
text-align: center;
line-height: 24px;
box-sizing: border-box;
user-select: none;
cursor: pointer;
}
}
.content {
......
<script setup>
import { ElMessage } from "element-plus";
import { getPriceLevelInfo, getSiteConfig } from "./api/index.js";
const route = useRoute();
const router = useRouter()
const iframeRef = ref();
// 获取票档
const price = reactive({
curPriceId: route.query.ticket_block,
data: [],
fetchData() {
getPriceLevelInfo({
actId: route.query?.actId ?? 1,
sessionId: route.query.sessionId,
openType: route.query.openType,
sitePlace: route.query.sitePlace,
ticketType: route.query.ticketType,
}).then((res) => {
this.data = res.data;
// price.curPriceId = route.query.ticket_block
});
},
onClickPrice(e) {
// if (selectedSeats.value?.length) {
// return ElMessage({ type: "warning", message: "请先取消已选座位" });
// }
price.curPriceId = e.priceId;
},
});
// 座位禁用时图标地址
const disabledIconUrl =
"http://book.xiaojinyu.games/api/uploads/image/20240511/unselect_default.png";
const siteConfig = reactive({
loading: false,
data: [],
fetchData() {
return getSiteConfig({
actId: route.query.actId ?? 1,
openType: route.query.openType,
sessionId: route.query.sessionId,
sitePlace: route.query.sitePlace,
ticketType: route.query.ticketType,
}).then((res) => {
const gridSize = 40;
const seat_arr = res.data.map((it, index) => {
return {
...it,
// 这几个是iframe引擎渲染座位必须的属性,规定好的
x: gridSize * it.x,
y: gridSize * it.y,
w: gridSize,
h: gridSize,
icon: it.state == 1 ? it.selectIcon : disabledIconUrl, // 图片的url
active: 0, // 是否选中
priceId: route.query.openType == 0 ? it.dayPriceId : it.nightPriceId,
};
});
siteConfig.data = seat_arr;
return seat_arr;
});
},
});
watch(
() => price.curPriceId,
(priceId) => {
siteConfig.data.forEach((it) => {
sendMsg("update-seat", {
id: it.id,
data: {
icon:
it.state == 1 && priceId == it.priceId
? it.active
? it.unSelectIcon
: it.selectIcon
: disabledIconUrl,
},
});
});
console.log("update完成");
},
{ immediate: true }
);
const sendMsg = (type, data) =>
iframeRef.value?.contentWindow.postMessage({ type: type, data: data }, "*");
/**
* 1. 加載iframe 3. 请求API的数据
* 2. 等待iframe里面资源加载完毕并触发picker-ready事件
* 4. 传递数据给iframe,等待渲染
*/
window.addEventListener("message", (e) => {
const data = e.data;
console.log("[parent]", data);
if (data.type == "picker-ready") {
// apiPromise.then(() => {})
siteConfig.fetchData().then((res) => {
const seat_arr = res.map((it) => {
return {
...it,
active: 0,
icon:
it.state == 1 && price.curPriceId == it.priceId
? it.selectIcon
: disabledIconUrl,
};
});
// 子页面加载完毕,这里iframeRef一定ok
iframeRef.value.contentWindow.postMessage(
{
type: "load-seats",
data: seat_arr,
},
"*"
);
});
} else if (data.type == "seat-click") {
// 子页面点击了座位
const seatData = data.data;
console.log("座位点击", seatData);
// 如果座位处于不可点击状态,就return
if (seatData.state != 1) return;
// 如果当前筛选了某种座位,点击的不是这种座位,也返回
if (price.curPriceId && seatData.priceId != price.curPriceId) return;
const newActive = seatData.active == 0 ? 1 : 0;
const siteConfigItem = siteConfig.data.find((it) => it.id == seatData.id);
if (siteConfigItem) {
siteConfigItem.active = newActive;
}
sendMsg("update-seat", {
id: seatData.id,
data: {
active: newActive,
icon: newActive ? seatData.unSelectIcon : seatData.selectIcon,
},
});
}
});
const deleteSiteConfigItem = (seatData) => {
const newActive = seatData.active == 0 ? 1 : 0;
const siteConfigItem = siteConfig.data.find((it) => it.id == seatData.id);
if (siteConfigItem) {
siteConfigItem.active = newActive;
}
sendMsg("update-seat", {
id: seatData.id,
data: { icon: newActive ? seatData.unSelectIcon : seatData.selectIcon },
});
};
/** 所选座位 */
const selectedSeats =
computed(() => siteConfig.data.filter((it) => it.active == 1)) ?? [];
/** 所选座位价格 */
const sumPrice = computed(() => {
return selectedSeats.value.reduce((total, item) => {
const price =
route.query.openType == 0
? Number(item.dayPrice)
: Number(item.nightPrice);
return total + price;
}, 0);
});
const toConfirmOrder = () => {
const seatIds = selectedSeats.value.map((it) => it.id);
if (!seatIds.length)
return ElMessage({ type: "warning", message: "请先选择座位" });
router.push({
path: "/seat/confirm_order",
query: {
openType: route.query.openType,
sessionId: route.query.sessionId,
sitePlace: route.query.sitePlace,
ticketType: route.query.ticketType,
seatIds: seatIds.join(","),
},
});
};
price.fetchData();
</script>
<template>
<div class="container">
<div class="top">
<div class="time">
<span>{{ route.query?.time_txt }}</span>
<span class="place">{{ route.query.sitePlace }}</span>
</div>
<div class="price_tab">
<div
v-for="(it, index) in price.data"
class="tab_item"
:class="{ tabActive: it.priceId == price.curPriceId }"
@click="price.onClickPrice(it)"
>
<img class="seat" :src="it.selectIcon" />
<span class="price">{{ it.price }}¥</span>
</div>
</div>
</div>
<div v-if="selectedSeats?.length" class="bottom">
<div class="seat_box">
<!-- v-for="(it, index) in selectedSeats" -->
<div v-for="(it, index) in selectedSeats" class="seat_item">
<img class="seat_icon" :src="it.selectIcon" />
<span class="num">{{ it.area }}{{ it.pai }}{{ it.no }}</span>
<el-icon
style="cursor: pointer"
color="#ccc"
@click="deleteSiteConfigItem(it)"
><CircleCloseFilled
/></el-icon>
</div>
</div>
<div class="pay">
<div class="sum">¥{{ sumPrice?.toFixed(2) }}</div>
<div class="pay_btn" @click="toConfirmOrder()">立即购买</div>
</div>
</div>
<div class="iframeBox">
<iframe
ref="iframeRef"
class="iframe"
id="iframe"
src="http://seat-choose.parent4relax.com/#/seat-picker"
></iframe>
</div>
</div>
</template>
<style scoped lang="scss">
.container {
width: 1200px;
margin: 0 auto;
padding: 20px;
.top {
width: 100%;
background-color: #fff;
padding: 20px;
margin-bottom: 10px;
border-radius: 6px;
.time {
font-size: 18px;
font-weight: 600;
margin-bottom: 10px;
.place {
color: #7e8489;
margin-left: 15px;
}
}
.price_tab {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 10px;
.tabActive {
background: #eeeeee !important;
border: 2px solid #7e8489 !important;
}
.tab_item {
display: flex;
align-items: center;
padding: 10px 14px;
background: #f5f7f8;
border-radius: 30px;
border: 2px solid #dcdedf;
font-size: 16px;
color: #646666;
cursor: pointer;
user-select: none;
.seat {
width: 14px;
height: 14px;
margin-right: 5px;
}
}
}
}
.iframeBox {
border-radius: 6px;
background-color: #fff;
padding: 20px;
margin-bottom: 20px;
}
.iframe {
width: 100%;
height: 500px;
border: none;
background-color: #f7f8fa;
}
.bottom {
border-radius: 6px;
background-color: #fff;
padding: 20px;
margin-bottom: 20px;
.seat_box {
display: flex;
flex-wrap: wrap;
gap: 10px;
width: 100%;
.seat_item {
display: flex;
align-items: center;
padding: 10px 14px;
font-size: 16px;
color: #29343c;
background: #eeeeee;
border-radius: 30px;
border: 2px solid #7e8489;
user-select: none;
.seat_icon {
width: 14px;
height: 14px;
margin-right: 5px;
}
.num {
margin-right: 5px;
}
}
}
.pay {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10px;
.sum {
font-weight: 600;
font-size: 22px;
color: #493ceb;
}
.pay_btn {
width: 200px;
height: 40px;
background: #493ceb;
border-radius: 20px;
margin-top: 10px;
font-size: 14px;
color: #fff;
line-height: 40px;
text-align: center;
cursor: pointer;
font-weight: 600;
user-select: none;
}
}
}
}
</style>
<script setup></script>
<script setup>
import dayjs from "dayjs";
import useUserStore from "@/store/modules/user";
import { setToken, getToken } from "./utils/local-store.js";
import {
loginFree,
activityDetail,
sessionDetail,
getSitePlaceInfo,
getPriceLevelInfo,
} from "./api/index.js";
import { ElMessage } from "element-plus";
import { reactive } from "vue";
const route = useRoute();
const router = useRouter();
const userStore = useUserStore();
const actId = 1;
// 用户免登录
const login = () => {
return new Promise((resolve, reject) => {
return loginFree({
userId: 1,
sign: "e00363b5016dbb6ee6cf78626a149f9c",
}).then((res) => {
setToken(res.data.token);
resolve(res.data);
});
});
};
const select_form = reactive({
venueItem: {
id: 0,
dateStr: "",
dayOpen: 1,
nightOpen: 1,
type: 0,
}, // 所选场次
session: -1, // 日/夜场 0:日场 1:夜场
place: "", // 场馆
ticket_block: 0, // 票档
onClickVenue(e, index) {
if (e.state == 1) return; // 表示
select_form.venueItem = e;
if (
(e.dayOpen == 0 && select_form.session == 0) ||
(e.nightOpen == 0 && select_form.session == 1)
) {
select_form.session = -1;
select_form.place = "";
select_form.ticket_block = 0;
}
},
// 选择日/夜场
onClickSession(e) {
if (
(e == 0 && select_form.venueItem?.dayOpen == 1) ||
(e == 1 && select_form.venueItem?.nightOpen == 1)
) {
select_form.session = e;
// select_form.place = "";
select_form.ticket_block = 0;
}
},
// 选择场馆
onClickPlace(e) {
if (e.state == 1) return;
select_form.place = e.placeName;
select_form.ticket_block = 0;
},
// 选择票档
onClickPrice(e) {
if (e.state == 1) return;
select_form.ticket_block = e.priceId;
},
// 去选座
toSelectSeat() {
if (!select_form.venueItem?.id)
return ElMessage({ type: "warning", message: "请选择时间" });
if (select_form.session == -1)
return ElMessage({ type: "warning", message: "请选择场次" });
if (!select_form.place)
return ElMessage({ type: "warning", message: "请选择场馆" });
if (!select_form.ticket_block)
return ElMessage({ type: "warning", message: "请选择票档" });
router.push({
path: "/seat/seat_picker",
query: {
openType: select_form.session,
sessionId: select_form.venueItem?.id,
sitePlace: select_form.place,
ticketType: select_form.venueItem?.type,
ticket_block: select_form.ticket_block,
time_txt: select_form.venueItem?.dateStr,
},
});
},
});
// 活动详情
const detail = reactive({
loading: false,
data: null,
fetchData() {
this.loading = true;
activityDetail({ actId: route.query?.actId ?? actId })
.then((res) => {
this.data = res.data;
})
.finally(() => (this.loding = false));
},
});
// 获取场次信息
const timeVenue = reactive({
loading: false,
data: [],
fetchData() {
this.loading = true;
sessionDetail({ actId: route.query?.actId ?? actId })
.then((res) => {
this.data = res.data;
})
.finally(() => (this.loading = false));
},
});
// 获取场馆
const sitePlaceInfo = reactive({
data: [
{ placeName: "B4", state: "0" },
{ placeName: "B6", state: "0" },
],
fetchData() {
console.log(select_form.venueItem?.id, select_form.session);
getSitePlaceInfo({
sessionId: select_form.venueItem?.id,
openType: select_form.session,
}).then((res) => {
this.data = res.data;
});
},
});
// 获取票档
const price = reactive({
data: [],
fetchData() {
getPriceLevelInfo({
actId: route.query?.actId ?? actId,
sessionId: select_form.venueItem?.id,
openType: select_form.session,
sitePlace: select_form.place,
ticketType: select_form.venueItem?.type,
}).then((res) => {
this.data = res.data;
});
},
});
watchEffect(() => {
if (select_form.session != -1 && select_form.venueItem?.id) {
if (select_form.session == 1 && select_form.place == "B4") {
select_form.place = "";
}
sitePlaceInfo.fetchData();
}
});
watchEffect(() => {
if (
select_form.venueItem?.id &&
select_form.session != -1 &&
select_form.place
) {
price.fetchData();
}
});
login().then((res) => {
detail.fetchData();
timeVenue.fetchData();
});
</script>
<template>
<div>
<!-- top -->
<div class="container top">
<img class="cover_img" />
<img class="cover_img" :src="detail.data?.coverImg" />
<div class="info">
<div class="title">2024年亚洲舞蹈大赛无锡站</div>
<div class="time">时间:2024.05.02 周六 — 2024.05.08 周一</div>
<div class="address">地址:无锡太湖博览中心</div>
<div class="title">{{ detail.data?.name }}</div>
<div class="time">
时间:{{
detail.data?.startTime
? dayjs(detail.data?.startTime).format("YYYY.MM.DD")
: ""
}}
{{ detail.data?.startTime ? dayjs().format("ddd") : "" }}
{{
detail.data?.endTime
? dayjs(detail.data?.endTime).format("YYYY.MM.DD")
: ""
}}
{{
detail.data?.endTime
? dayjs(detail.data?.endTime).format("ddd")
: ""
}}
</div>
<div class="address">地址:{{ detail.data?.address }}</div>
<!-- 时间 -->
<div class="select_item_box">
<div class="label">时间</div>
<div class="select_item">
<div
v-for="(it, index) in 4"
v-for="(it, index) in timeVenue.data"
:key="index"
:class="[false ? 'tagActive' : 'tag']"
:class="[
it.id == select_form.venueItem?.id ? 'tagActive' : 'tag',
]"
@click="select_form.onClickVenue(it)"
>
2024.05.02 周六
{{ it.dateStr }}
<div v-if="it.type == 1" class="tag_t">套票</div>
</div>
</div>
</div>
......@@ -26,11 +233,29 @@
<div class="select_item_box">
<div class="label">场次</div>
<div class="select_item">
<div :class="[true ? 'tagDisabled' : false ? 'tagActive' : 'tag']">
<div
:class="[
select_form.venueItem?.dayOpen == 1
? select_form.session == 0
? 'tagActive'
: 'tag'
: 'tagDisabled',
]"
@click="select_form.onClickSession(0)"
>
日场
</div>
<div :class="[true ? 'tagDisabled' : false ? 'tagActive' : 'tag']">
日场
<div
:class="[
select_form.venueItem?.nightOpen == 1
? select_form.session == 1
? 'tagActive'
: 'tag'
: 'tagDisabled',
]"
@click="select_form.onClickSession(1)"
>
夜场
</div>
</div>
</div>
......@@ -39,39 +264,56 @@
<div class="label">场馆</div>
<div class="select_item">
<div
v-for="(it, index) in 2"
v-for="(it, index) in sitePlaceInfo.data"
:key="index"
:class="[true ? 'tagDisabled' : false ? 'tagActive' : 'tag']"
:class="[
it.state == 0
? it.placeName == select_form.place
? 'tagActive'
: 'tag'
: 'tagDisabled',
]"
@click="select_form.onClickPlace(it)"
>
B6
{{ it.placeName }}
</div>
</div>
</div>
<!-- 票档 -->
<div class="select_item_box">
<div
v-if="price.data?.length && select_form.place"
class="select_item_box"
>
<div class="label">票档</div>
<div class="select_item">
<div
v-for="(it, index) in 3"
v-for="(it, index) in price.data"
:key="index"
:class="[true ? 'tagDisabled' : false ? 'tagActive' : 'tag']"
:class="[
it.state == 0
? it.priceId == select_form.ticket_block
? 'tagActive'
: 'tag'
: 'tagDisabled',
]"
@click="select_form.onClickPrice(it)"
>
200.00
{{ it.price }}
</div>
</div>
</div>
<!-- button -->
<div class="btn">选座购票</div>
<div class="btn" @click="select_form.toSelectSeat()">选座购票</div>
</div>
</div>
<!-- bottom -->
<div class="container bottom">
<div class="title">活动介绍</div>
<div class="rich_content" v-html="'123123123123123123'"></div>
<div class="rich_content" v-html="detail.data?.introduceInfo"></div>
<div class="title" style="margin-top: 30px">购票须知</div>
<div class="rich_content" v-html="'123123123123123123'"></div>
<div class="rich_content" v-html="detail.data?.buyNotice"></div>
</div>
</div>
</template>
......@@ -142,7 +384,18 @@
display: flex;
flex-wrap: wrap;
gap: 10px;
user-select: none;
.tag_t {
padding: 1px 15px;
font-weight: 400;
font-size: 14px;
color: #493ceb;
border-radius: 6px;
border: 1px solid #453dea;
margin-left: 5px;
}
.tag {
display: flex;
padding: 12px 18px;
background: #eeeeee;
border-radius: 4px;
......@@ -151,7 +404,9 @@
color: #4a4a4a;
cursor: pointer;
}
.tagActive {
display: flex;
padding: 12px 18px;
background: #fff;
border-radius: 4px;
......
/** 用户登录token储存的key */
export const TOKEN_KEY = "SEAT_TOKEN";
/** 设置token */
export const setToken = (token) => localStorage.setItem(TOKEN_KEY, token);
/**
* 获取登录token
* @param drop 是否清空
*/
export const getToken = (drop = false) => {
let token = localStorage.getItem(TOKEN_KEY);
if (!token) return null;
if (drop) localStorage.removeItem(TOKEN_KEY);
return token;
};
// http.js
import axios from "axios";
import { getToken } from "./local-store";
import { ElMessage } from "element-plus";
const baseURL = "http://101.43.15.205:8084"; //"http://book.xiaojinyu.games"; // 这里填入你的基础 API URL
const timeout = 15000; // 请求超时时间
const http = axios.create({
baseURL,
timeout,
headers: {
"Content-Type": "application/json",
},
});
// 请求拦截器
http.interceptors.request.use(
(config) => {
// 在发送请求之前做些什么
const TOKEN = getToken();
config.headers.Authorization = TOKEN;
if (config.method == "get") config.params = config.data;
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器
http.interceptors.response.use(
(response) => {
// 判断是否有异常
let error = null; // 若无异常此值为null
if (response.status !== 200) {
error = Error(`Request failed with statuCode ${response.status}`);
}
if (response.data.code != 200) {
return ElMessage({ type: "error", message: response.data.msg });
}
return response.data;
},
(error) => {
// 对响应错误做点什么
return Promise.reject(error);
}
);
// 封装请求函数
const request = (method, url, data = null) => {
return http({
method,
url,
data,
});
};
export default request;
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!