534 lines
13 KiB
Vue
534 lines
13 KiB
Vue
|
|
<template>
|
|||
|
|
<view class="container">
|
|||
|
|
<!-- 自定义日历选择器 -->
|
|||
|
|
<view class="date-selector">
|
|||
|
|
<view class="calendar-header">
|
|||
|
|
<text class="month-title">{{ currentDateStr }}</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="calendar-weekdays">
|
|||
|
|
<text v-for="day in weekdays" :key="day" class="weekday">{{ day }}</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="calendar-days">
|
|||
|
|
<view v-for="(day, index) in calendarDays" :key="day.dateStr || `placeholder-${index}`" class="calendar-day"
|
|||
|
|
:class="{
|
|||
|
|
'placeholder': day.isPlaceholder,
|
|||
|
|
'selected': day.dateStr === selectedDate,
|
|||
|
|
'today': day.isToday,
|
|||
|
|
'disabled': !day.hasBookings && !day.isPlaceholder,
|
|||
|
|
'has-bookings': day.hasBookings
|
|||
|
|
}" @click="!day.isPlaceholder && selectDate(day)">
|
|||
|
|
<text v-if="!day.isPlaceholder" class="day-number">{{ day.day }}</text>
|
|||
|
|
<text v-if="!day.isPlaceholder && day.bookingCount > 0" class="booking-count">
|
|||
|
|
{{ day.bookingCount }}人
|
|||
|
|
</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 时间槽列表 -->
|
|||
|
|
<scroll-view class="timeslots-scroll" scroll-y="true">
|
|||
|
|
<view class="timeslots-section">
|
|||
|
|
<view class="section-title">
|
|||
|
|
{{ selectedDate }} 可选时间段
|
|||
|
|
<text v-if="totalBookingCount > 0" class="total-booking">
|
|||
|
|
(总预约: {{ totalBookingCount }}人)
|
|||
|
|
</text>
|
|||
|
|
</view>
|
|||
|
|
<t-loading v-if="loading" loading />
|
|||
|
|
<view v-else-if="timeslots.length === 0" class="empty-state">
|
|||
|
|
<t-empty description="暂无可预约时间段" />
|
|||
|
|
</view>
|
|||
|
|
<view v-else class="timeslots-list">
|
|||
|
|
<view v-for="slot in timeslots" :key="slot.id" class="timeslot-card"
|
|||
|
|
:class="{ disabled: !slot.is_active || slot.current_people >= slot.max_people }">
|
|||
|
|
<view class="timeslot-info">
|
|||
|
|
<view class="time-range">
|
|||
|
|
{{ formatTime(slot.start_time) }} - {{ formatTime(slot.end_time) }}
|
|||
|
|
</view>
|
|||
|
|
<view class="slot-status">
|
|||
|
|
<t-tag :theme="slot.current_people >= slot.max_people ? 'danger' : 'success'" size="small">
|
|||
|
|
{{ slot.current_people }}/{{ slot.max_people }}
|
|||
|
|
</t-tag>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
<t-button t-class="btn-primary" size="small" theme="primary"
|
|||
|
|
:disabled="!slot.is_active || slot.current_people >= slot.max_people"
|
|||
|
|
@click.stop="showBookingModal(slot)">
|
|||
|
|
{{ slot.current_people >= slot.max_people ? '已满' : '预约' }}
|
|||
|
|
</t-button>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</scroll-view>
|
|||
|
|
|
|||
|
|
<!-- 预约弹窗 -->
|
|||
|
|
<t-dialog v-model:visible="showDialog" title="预约确认" cancelBtn="取消"
|
|||
|
|
confirmBtn="确认预约" t-class-confirm="btn-primary" @confirm="confirmBooking"
|
|||
|
|
@cancel="showDialog = false">
|
|||
|
|
<template #content>
|
|||
|
|
<!-- 适配skyline,增加type="list" -->
|
|||
|
|
<scroll-view type="list" scroll-y class="long-content">
|
|||
|
|
<view class="booking-dialog">
|
|||
|
|
<view class="dialog-item">
|
|||
|
|
<text class="dialog-label">日期:</text>
|
|||
|
|
<text class="dialog-value">{{ selectedDate }}</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="dialog-item">
|
|||
|
|
<text class="dialog-label">时间段:</text>
|
|||
|
|
<text class="dialog-value">{{ selectedSlot?.start_time ? formatTime(selectedSlot.start_time) : '' }} - {{
|
|||
|
|
selectedSlot?.end_time ? formatTime(selectedSlot.end_time) : '' }}</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="dialog-item">
|
|||
|
|
<text class="dialog-label">人数:</text>
|
|||
|
|
<t-stepper v-model="peopleCount" :min="1"
|
|||
|
|
:max="selectedSlot?.max_people - selectedSlot?.current_people || 1" :disabled="true" size="small" />
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</scroll-view>
|
|||
|
|
</template>
|
|||
|
|
</t-dialog>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import { ref, onMounted, computed } from 'vue'
|
|||
|
|
import { api, type TimeSlot } from '@/utils/api'
|
|||
|
|
|
|||
|
|
// 获取本地时区的日期字符串(东八区)
|
|||
|
|
const getLocalDateStr = () => {
|
|||
|
|
const now = new Date()
|
|||
|
|
const year = now.getFullYear()
|
|||
|
|
const month = String(now.getMonth() + 1).padStart(2, '0')
|
|||
|
|
const day = String(now.getDate()).padStart(2, '0')
|
|||
|
|
return `${year}-${month}-${day}`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const selectedDate = ref(getLocalDateStr())
|
|||
|
|
const timeslots = ref<TimeSlot[]>([])
|
|||
|
|
const loading = ref(false)
|
|||
|
|
const showDialog = ref(false)
|
|||
|
|
const selectedSlot = ref<TimeSlot | null>(null)
|
|||
|
|
const peopleCount = ref(1)
|
|||
|
|
const notes = ref('')
|
|||
|
|
const isBooking = ref(false) // 防止重复点击
|
|||
|
|
|
|||
|
|
// 日历相关
|
|||
|
|
const currentDate = ref(new Date())
|
|||
|
|
const weekdays = ['日', '一', '二', '三', '四', '五', '六']
|
|||
|
|
const dateBookings = ref<Record<string, number>>({})
|
|||
|
|
const dateHasSlots = ref<Record<string, boolean>>({})
|
|||
|
|
|
|||
|
|
// 计算当前月份显示
|
|||
|
|
const currentDateStr = computed(() => {
|
|||
|
|
const year = currentDate.value.getFullYear()
|
|||
|
|
const month = currentDate.value.getMonth() + 1
|
|||
|
|
return `${year}年${month}月`
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 计算总预约人数
|
|||
|
|
const totalBookingCount = computed(() => {
|
|||
|
|
return timeslots.value.reduce((sum, slot) => sum + slot.current_people, 0)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 生成日历数据 - 只显示本月(但保留上个月的占位以对齐星期)
|
|||
|
|
const calendarDays = computed(() => {
|
|||
|
|
const year = currentDate.value.getFullYear()
|
|||
|
|
const month = currentDate.value.getMonth()
|
|||
|
|
|
|||
|
|
// 获取当月第一天和最后一天
|
|||
|
|
const firstDay = new Date(year, month, 1)
|
|||
|
|
const lastDay = new Date(year, month + 1, 0)
|
|||
|
|
const today = new Date()
|
|||
|
|
|
|||
|
|
// 获取当月第一天是星期几
|
|||
|
|
const startWeekday = firstDay.getDay()
|
|||
|
|
|
|||
|
|
const days: any[] = []
|
|||
|
|
|
|||
|
|
// 添加上个月的日期占位(用于对齐),但设置为不可点击
|
|||
|
|
for (let i = startWeekday - 1; i >= 0; i--) {
|
|||
|
|
days.push({
|
|||
|
|
day: '',
|
|||
|
|
dateStr: '',
|
|||
|
|
isPlaceholder: true,
|
|||
|
|
isOtherMonth: false,
|
|||
|
|
isToday: false,
|
|||
|
|
bookingCount: 0,
|
|||
|
|
hasBookings: false
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取当月的日期
|
|||
|
|
for (let i = 1; i <= lastDay.getDate(); i++) {
|
|||
|
|
const date = new Date(year, month, i)
|
|||
|
|
|
|||
|
|
// 使用本地时间获取日期部分,避免时区问题
|
|||
|
|
const yearNum = date.getFullYear()
|
|||
|
|
const monthNum = date.getMonth() + 1
|
|||
|
|
const dayNum = date.getDate()
|
|||
|
|
|
|||
|
|
const dateStr = `${yearNum}-${String(monthNum).padStart(2, '0')}-${String(dayNum).padStart(2, '0')}`
|
|||
|
|
|
|||
|
|
// 使用本地时间比较是否为今天
|
|||
|
|
const todayYear = today.getFullYear()
|
|||
|
|
const todayMonth = today.getMonth()
|
|||
|
|
const todayDay = today.getDate()
|
|||
|
|
const isToday = yearNum === todayYear && monthNum - 1 === todayMonth && dayNum === todayDay
|
|||
|
|
|
|||
|
|
days.push({
|
|||
|
|
day: i,
|
|||
|
|
dateStr,
|
|||
|
|
isPlaceholder: false,
|
|||
|
|
isOtherMonth: false,
|
|||
|
|
isToday,
|
|||
|
|
bookingCount: dateBookings.value[dateStr] || 0,
|
|||
|
|
hasBookings: dateHasSlots.value[dateStr] || false
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return days
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 格式化日期字符串
|
|||
|
|
const formatDateStr = (date: Date) => {
|
|||
|
|
const year = date.getFullYear()
|
|||
|
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
|||
|
|
const day = String(date.getDate()).padStart(2, '0')
|
|||
|
|
const dateStr = `${year}-${month}-${day}`
|
|||
|
|
return dateStr
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查登录状态
|
|||
|
|
onMounted(() => {
|
|||
|
|
const token = uni.getStorageSync('token')
|
|||
|
|
if (!token) {
|
|||
|
|
uni.redirectTo({ url: '/pages/login/login' })
|
|||
|
|
} else {
|
|||
|
|
loadTimeSlots()
|
|||
|
|
loadDateBookings()
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 加载日期预约统计
|
|||
|
|
const loadDateBookings = async () => {
|
|||
|
|
try {
|
|||
|
|
const slots = await api.timeslots.getList({ is_active: true })
|
|||
|
|
|
|||
|
|
const bookings: Record<string, number> = {}
|
|||
|
|
const slotsMap: Record<string, boolean> = {}
|
|||
|
|
|
|||
|
|
slots.forEach(slot => {
|
|||
|
|
const dateStr = slot.date.split('T')[0]
|
|||
|
|
bookings[dateStr] = (bookings[dateStr] || 0) + slot.current_people
|
|||
|
|
slotsMap[dateStr] = true
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
dateBookings.value = bookings
|
|||
|
|
dateHasSlots.value = slotsMap
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('加载日期预约统计失败', error)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 加载时间槽
|
|||
|
|
const loadTimeSlots = async () => {
|
|||
|
|
loading.value = true
|
|||
|
|
try {
|
|||
|
|
const slots = await api.timeslots.getList({
|
|||
|
|
date: selectedDate.value,
|
|||
|
|
is_active: true
|
|||
|
|
})
|
|||
|
|
timeslots.value = slots
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('加载时间槽失败', error)
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 选择日期
|
|||
|
|
const selectDate = (day: any) => {
|
|||
|
|
selectedDate.value = day.dateStr
|
|||
|
|
loadTimeSlots()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 格式化时间
|
|||
|
|
const formatTime = (timeStr: string) => {
|
|||
|
|
// 直接提取时间部分,避免时区转换问题
|
|||
|
|
const timePart = timeStr.split('T')[1] || timeStr
|
|||
|
|
const timeWithoutZone = timePart.split('+')[0].split('Z')[0]
|
|||
|
|
const [hours, minutes] = timeWithoutZone.split(':')
|
|||
|
|
return `${hours}:${minutes}`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 显示预约弹窗
|
|||
|
|
const showBookingModal = (slot: TimeSlot) => {
|
|||
|
|
|
|||
|
|
// 防止重复点击
|
|||
|
|
if (isBooking.value) {
|
|||
|
|
uni.showToast({
|
|||
|
|
title: '正在处理,请稍候',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!slot.is_active || slot.current_people >= slot.max_people) {
|
|||
|
|
console.log('时间槽不可用', slot)
|
|||
|
|
uni.showToast({
|
|||
|
|
title: '该时间段已不可用',
|
|||
|
|
icon: 'none'
|
|||
|
|
})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
selectedSlot.value = slot
|
|||
|
|
peopleCount.value = 1
|
|||
|
|
notes.value = ''
|
|||
|
|
showDialog.value = true
|
|||
|
|
console.log('打开弹窗', showDialog.value)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 确认预约
|
|||
|
|
const confirmBooking = async () => {
|
|||
|
|
if (!selectedSlot.value) return
|
|||
|
|
|
|||
|
|
isBooking.value = true
|
|||
|
|
try {
|
|||
|
|
await api.appointments.create(selectedSlot.value.id, peopleCount.value, notes.value)
|
|||
|
|
uni.showToast({
|
|||
|
|
title: '预约成功',
|
|||
|
|
icon: 'success'
|
|||
|
|
})
|
|||
|
|
showDialog.value = false
|
|||
|
|
loadTimeSlots()
|
|||
|
|
loadDateBookings()
|
|||
|
|
} catch (error) {
|
|||
|
|
showDialog.value = false
|
|||
|
|
|
|||
|
|
} finally {
|
|||
|
|
isBooking.value = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
.container {
|
|||
|
|
height: calc(100vh - var(--window-top));
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
background: #f5f5f5;
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.date-selector {
|
|||
|
|
flex-shrink: 0;
|
|||
|
|
background: #ffffff;
|
|||
|
|
margin: 24rpx 32rpx;
|
|||
|
|
border-radius: 16rpx;
|
|||
|
|
padding: 32rpx;
|
|||
|
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.06);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
.calendar-header {
|
|||
|
|
text-align: center;
|
|||
|
|
margin-bottom: 24rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.month-title {
|
|||
|
|
font-size: 32rpx;
|
|||
|
|
font-weight: bold;
|
|||
|
|
color: #333;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.calendar-weekdays {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: repeat(7, 1fr);
|
|||
|
|
gap: 8rpx;
|
|||
|
|
margin-bottom: 16rpx;
|
|||
|
|
padding: 0 10rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.weekday {
|
|||
|
|
font-size: 24rpx;
|
|||
|
|
color: #999;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.calendar-days {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: repeat(7, 1fr);
|
|||
|
|
gap: 8rpx;
|
|||
|
|
padding: 0 10rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.calendar-day {
|
|||
|
|
height: 100rpx;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
border-radius: 8rpx;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.3s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.calendar-day.placeholder {
|
|||
|
|
visibility: hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.calendar-day.disabled {
|
|||
|
|
opacity: 0.4;
|
|||
|
|
pointer-events: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.calendar-day.has-bookings {
|
|||
|
|
background: rgba(255, 122, 0, 0.08);
|
|||
|
|
border: 2rpx solid #FF7A00;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.calendar-day.today {
|
|||
|
|
background: rgba(255, 122, 0, 0.12);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.calendar-day.selected {
|
|||
|
|
background: linear-gradient(135deg, #FF7A00 0%, #FF9500 100%);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.calendar-day.selected .day-number {
|
|||
|
|
color: #ffffff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.calendar-day.selected .booking-count {
|
|||
|
|
color: #ffffff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.day-number {
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
color: #333;
|
|||
|
|
margin-bottom: 4rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.booking-count {
|
|||
|
|
font-size: 20rpx;
|
|||
|
|
color: #FF7A00;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
.timeslots-scroll {
|
|||
|
|
flex: 1;
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.timeslots-section {
|
|||
|
|
padding: 32rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.section-title {
|
|||
|
|
font-size: 32rpx;
|
|||
|
|
font-weight: bold;
|
|||
|
|
color: #333;
|
|||
|
|
margin-bottom: 24rpx;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.total-booking {
|
|||
|
|
font-size: 24rpx;
|
|||
|
|
color: #FF7A00;
|
|||
|
|
margin-left: 16rpx;
|
|||
|
|
font-weight: normal;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-state {
|
|||
|
|
padding: 80rpx 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.timeslots-list {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 20rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.timeslot-card {
|
|||
|
|
background: #ffffff;
|
|||
|
|
border-radius: 16rpx;
|
|||
|
|
padding: 32rpx;
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.timeslot-card.disabled {
|
|||
|
|
opacity: 0.6;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.timeslot-info {
|
|||
|
|
flex: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.time-range {
|
|||
|
|
font-size: 32rpx;
|
|||
|
|
font-weight: bold;
|
|||
|
|
color: #333;
|
|||
|
|
margin-bottom: 16rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.slot-status {
|
|||
|
|
margin-top: 8rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.booking-dialog {
|
|||
|
|
padding: 32rpx 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dialog-item {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
padding: 20rpx 0;
|
|||
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dialog-item:last-child {
|
|||
|
|
border-bottom: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dialog-label {
|
|||
|
|
width: 120rpx;
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
color: #666;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dialog-value {
|
|||
|
|
flex: 1;
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
color: #333;
|
|||
|
|
font-weight: 500;
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
|
|||
|
|
<style>
|
|||
|
|
/* 按钮自定义样式 - 使用全局样式 */
|
|||
|
|
.btn-primary {
|
|||
|
|
background: linear-gradient(135deg, #FF7A00 0%, #FF9500 100%) !important;
|
|||
|
|
border: none !important;
|
|||
|
|
border-radius: 8rpx !important;
|
|||
|
|
color: #FFFFFF !important;
|
|||
|
|
box-shadow: 0 4rpx 12rpx rgba(255, 122, 0, 0.3) !important;
|
|||
|
|
outline: none !important;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-primary::after {
|
|||
|
|
border: none !important;
|
|||
|
|
box-shadow: none !important;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-primary:active {
|
|||
|
|
background: linear-gradient(135deg, #FF6900 0%, #FF8500 100%) !important;
|
|||
|
|
}
|
|||
|
|
</style>
|