| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702 |
- <template>
- <Form :rules="fieldConfigs" v-model="formData" ref="formRef">
- <template v-if="step == 1">
- <up-table>
- <up-tr>
- <up-th>预约开始时间</up-th>
- <Td field="yykssj">
- <SsDatetimePicker v-model="formData.yykssj" mode="datetime" :min-date="yykssjMinDate"
- :z-index="99999" placeholder="YYYY-MM-DD HH:mm" @change="onYykssjChange" />
- </Td>
- </up-tr>
- <up-tr>
- <up-th width="150">预约结束时间</up-th>
- <Td field="yyjssj">
- <SsDatetimePicker v-model="formData.yyjssj" mode="datetime" :min-date="yyjssjMinDate"
- :z-index="99999" placeholder="YYYY-MM-DD HH:mm" @change="onYyjssjChange" />
- </Td>
- </up-tr>
- </up-table>
- <!-- 车辆选择列表 -->
- <view class="car-list">
- <SsCarCard v-for="car in carList" :key="car.id" :car-data="car" :status="car.status"
- @select="handleCarSelect" />
- <!-- 加载中提示 -->
- <view v-if="loadingMore" class="loading-more">
- <text>加载中...</text>
- </view>
- </view>
- </template>
- <template v-if="step == 2">
- <!-- 当前车辆信息卡片 -->
- <view class="current-car-section">
- <SsCarCard
- v-if="selectedCar"
- :car-data="selectedCar"
- :status="selectedCar.status"
- />
- </view>
- <!-- 预约详情(点击已预约车辆) -->
- <view v-if="reservationDetail" class="reservation-detail">
- <up-table>
- <up-tr>
- <up-th>开始时间</up-th>
- <up-td>{{ formatDate(reservationDetail.kssj,'yyyy-MM-dd HH:mm:ss') }}</up-td>
- </up-tr>
- <up-tr>
- <up-th>结束时间</up-th>
- <up-td>{{ formatDate(reservationDetail.jssj,'yyyy-MM-dd HH:mm:ss') }}</up-td>
- </up-tr>
- <up-tr>
- <up-th>使用人</up-th>
- <up-td>{{ reservationDetail.ydr }}</up-td>
- </up-tr>
- <up-tr>
- <up-th>联系电话</up-th>
- <up-td>
- <text class="phone-link" @click="makePhoneCall(reservationDetail.ydrdh)">
- {{ reservationDetail.ydrdh }}
- </text>
- </up-td>
- </up-tr>
- </up-table>
- </view>
- <!-- 预约表单(点击可预约车辆) -->
- <view v-if="!reservationDetail" class="reservation-form">
- <up-table>
- <up-tr>
- <up-th>开始时间</up-th>
- <up-td>{{ formData.yykssj }}</up-td>
- </up-tr>
- <up-tr>
- <up-th>结束时间</up-th>
- <up-td>{{ formData.yyjssj }}</up-td>
- </up-tr>
- <up-tr>
- <up-th>人数</up-th>
- <Td field="rs">
- <SsInput v-model="formData.rs" placeholder="请输入人数" />
- </Td>
- </up-tr>
- <up-tr>
- <up-th>办事地点</up-th>
- <Td field="bsdd">
- <SsInput v-model="formData.bsdd" placeholder="请输入办事地点" />
- </Td>
- </up-tr>
- <up-tr>
- <up-th>事由</up-th>
- <Td field="sy">
- <SsInput v-model="formData.sy" placeholder="请输入事由" />
- </Td>
- </up-tr>
- </up-table>
- </view>
- </template>
- </Form>
- <SsBottom v-if="step == 2" :buttons="step2BottomButtons" @button-click="step2HandleBottomAction"></SsBottom>
- <!-- 确认弹窗 -->
- <SsConfirm
- :visible="showConfirm"
- title="预约确认"
- width="90%"
- height="42vh"
- @button-click="handleConfirm"
- >
- <view class="confirm-content">
- <view class="confirm-item">
- <text class="label">预约开始时间:</text>
- <text class="value">{{ formData.yykssj }}</text>
- </view>
- <view class="confirm-item">
- <text class="label">预约结束时间:</text>
- <text class="value">{{ formData.yyjssj }}</text>
- </view>
- <view class="confirm-item">
- <text class="label">人数:</text>
- <text class="value">{{ formData.rs }}</text>
- </view>
- <view class="confirm-item">
- <text class="label">办事地点:</text>
- <text class="value">{{ formData.bsdd }}</text>
- </view>
- <view class="confirm-item">
- <text class="label">事由:</text>
- <text class="value">{{ formData.sy }}</text>
- </view>
- </view>
- </SsConfirm>
- </template>
- <script setup>
- import { ref, onMounted, computed } from 'vue'
- import { onReachBottom } from '@dcloudio/uni-app'
- import Form from '@/components/Form/index.vue'
- import Td from '@/components/Td/index.vue'
- import SsDatetimePicker from '@/components/SsDatetimePicker/index.vue'
- import SsInput from '@/components/SsInput/index.vue'
- import SsBottom from '@/components/SsBottom/index.vue'
- import SsConfirm from '@/components/SsConfirm/index.vue'
- import SsCarCard from '@/components/SsCarCard/index.vue'
- import { clyyApi } from '@/api/clyy'
- import { commonApi } from '@/api/common'
- import { formatDate } from '@/utils/date'
- import { goBack } from '@/utils/navigation'
- const step = ref(1)
- const formRef = ref(null)
- const formData = ref({
- yykssj: '',
- yyjssj: '',
- // 预约表单数据
- rs: '', // 人数
- bsdd: '', // 办事地点
- sy: '' // 事由
- })
- const yykssjMinDate = ref(new Date())
- const yyjssjMinDate = ref(new Date() + 3600 * 1000)
- // 车辆列表数据
- const carList = ref([])
- // 选中的车辆
- const selectedCar = ref(null)
- // 当前查看的预约详情(点击已预约车辆时)
- const reservationDetail = ref(null)
- // 加载状态
- const loading = ref(false)
- const loadingMore = ref(false) // 加载更多状态
- const hasMoreData = ref(false) // 是否还有更多数据
- const fieldConfigs = {
- yykssj: {
- rules: [{ required: true, message: '请选择预约开始时间' }]
- },
- yyjssj: {
- rules: [{ required: true, message: '请选择预约结束时间' }]
- },
- rs: {
- rules: [{ required: true, message: '请输入人数' }]
- },
- bsdd: {
- rules: [{ required: true, message: '请输入办事地点' }]
- },
- sy: {
- rules: [{ required: true, message: '请输入事由' }]
- }
- }
- const onYykssjChange = (val) => {
- console.log('开始时间变化:', val)
- yyjssjMinDate.value = new Date(val)
- // 开始时间变化时清空结束时间
- if (formData.value.yyjssj) {
- formData.value.yyjssj = ''
- }
- }
- const onYyjssjChange = (val) => {
- console.log('结束时间变化:', val)
- // 结束时间变化时查询车辆(但要排除清空操作)
- if (val && formData.value.yykssj) {
- queryAvailableCars()
- }
- }
- // 分页相关状态
- const pageInfo = ref({
- pageNo: 1,
- rowNumPer: 10,
- total: 0
- })
- /**
- * 查询可用车辆(支持分页)
- * @param {string} startTime 开始时间
- * @param {string} endTime 结束时间
- * @param {number} pageNo 页码
- */
- const queryAvailableCars = async (startTime = null, endTime = null, pageNo = 1) => {
- // 如果没有传入时间参数,使用表单中的时间
- const queryStartTime = startTime || formData.value.yykssj
- const queryEndTime = endTime || formData.value.yyjssj
- if (!queryStartTime || !queryEndTime) {
- return
- }
- loading.value = true
- console.log('查询车辆:', {
- beginTime: queryStartTime,
- endTime: queryEndTime,
- pageNo: pageNo
- })
- try {
- // 调用PC端的cl_search接口(分页)
- const response = await clyyApi.cl_search({
- beginTime: queryStartTime,
- endTime: queryEndTime,
- pageNo: pageNo,
- rowNumPer: pageInfo.value.rowNumPer
- })
- console.log('response.data内容:',JSON.stringify(response.data))
- if (response && response.data) {
- // 更新分页信息
- if (response.wdPage) {
- pageInfo.value = {
- pageNo: response.wdPage.pageNo,
- rowNumPer: response.wdPage.rowNumPer,
- total: response.wdPage.rowNum
- }
- }
- // 处理返回的车辆数据,转换为SsCarCard需要的格式
- const processedCars = await Promise.all(response.data.data.map(async (item) => {
- console.log('处理车辆数据:', item)
- // 简单的状态判断
- let status = 'available' // 默认可预约
- // 根据PC端返回的数据判断状态
- if (item.zt == 1) { // 使用中
- status = 'reserved'
- } else {
- status = 'available'
- }
- // 获取车辆类型
- let carType = '车辆' // 默认类型
- if (item.wplbm) {
- try {
- const typeResult = await commonApi.getDictByCbNameAndValue('wplb', item.wplbm)
- if (typeResult && typeResult.data && typeResult.data.result && typeResult.data.result[item.wplbm]) {
- carType = typeResult.data.result[item.wplbm]
- }
- } catch (error) {
- console.warn('获取车辆类型失败:', error)
- }
- }
- return {
- id: item.wpid || item.id,
- plateNumber: item.mc, // 使用解码后的车辆名称
- seats: 7, // 默认座位数
- name: item.mc, // 车辆名称
- color: '白色', // 默认颜色
- type: carType, // 从后台获取的真实类型
- image: item.sltwj, // 使用默认图片
- wph: item.wph,
- wpcsList: item.wpcsList,
- wplbm: item.wplbm,
- wpid: item.wpid,
- status: status,
- // 如果是已预约状态,添加预约信息
- // reservationInfo: status === 'reserved' ? {
- // startTime: queryStartTime,
- // endTime: queryEndTime,
- // userName: '已预约',
- // phone: '未知'
- // } : null
- }
- }))
- // 如果是第一页,直接替换;否则追加
- if (pageNo === 1) {
- carList.value = processedCars
- // 重置分页状态
- hasMoreData.value = pageInfo.value.total > pageInfo.value.rowNumPer
- } else {
- carList.value = [...carList.value, ...processedCars]
- // 检查是否还有更多数据
- hasMoreData.value = pageInfo.value.pageNo * pageInfo.value.rowNumPer < pageInfo.value.total
- }
- } else {
- console.log('数据格式不正确或为空')
- if (pageNo === 1) {
- carList.value = []
- }
- }
- } catch (error) {
- console.error('查询车辆失败:', error)
- uni.showToast({
- title: '查询车辆失败',
- icon: 'none'
- })
- if (pageNo === 1) {
- carList.value = []
- }
- } finally {
- loading.value = false
- }
- }
- /**
- * 加载更多车辆数据
- */
- const loadMoreCars = async () => {
- // 防止重复加载
- if (loadingMore.value || !hasMoreData.value) {
- return
- }
- // 检查是否还有更多数据
- if (pageInfo.value.pageNo * pageInfo.value.rowNumPer >= pageInfo.value.total) {
- hasMoreData.value = false
- return
- }
- loadingMore.value = true
- const nextPage = pageInfo.value.pageNo + 1
- try {
- await queryAvailableCars(null, null, nextPage)
- } catch (error) {
- console.error('加载更多失败:', error)
- uni.showToast({
- title: '加载失败',
- icon: 'none'
- })
- } finally {
- loadingMore.value = false
- }
- }
- /**
- * 处理车辆选择
- */
- const handleCarSelect = async(car) => {
- console.log('选择车辆:', car)
- // 检查是否已选择时间
- if (!formData.value.yykssj || !formData.value.yyjssj) {
- uni.showToast({
- title: '请先选择预约时间',
- icon: 'none'
- })
- return
- }
- selectedCar.value = car
- if (car.status === 'reserved') {
- const res = await clyyApi.cl_searchYd({wpid:car.wpid})
- console.log('查询预约响应:', res.data.wpydlist.length>0)
- if(res.data.wpydlist.length>0){
- // // 点击已预约车辆,显示预约详情
- // reservationDetail.value = car.reservationInfo
- reservationDetail.value = res.data.wpydlist[0]
- step.value = 2
- }
-
-
-
- } else if (car.status === 'available') {
- // 点击可预约车辆,显示预约表单
- reservationDetail.value = null
- step.value = 2
- }
- }
- /**
- * 初始化页面数据
- */
- const initPageData = () => {
- // 设置默认查询时间(一天后到两天后)
- const tomorrow = new Date()
- tomorrow.setDate(tomorrow.getDate() + 1)
- tomorrow.setHours(9, 0, 0, 0)
- const dayAfterTomorrow = new Date()
- dayAfterTomorrow.setDate(dayAfterTomorrow.getDate() + 2)
- dayAfterTomorrow.setHours(17, 0, 0, 0)
- // 格式化为PC端接口需要的格式:yyyy-MM-dd HH:mm
- const formatDateTime = (date) => {
- const year = date.getFullYear()
- const month = String(date.getMonth() + 1).padStart(2, '0')
- const day = String(date.getDate()).padStart(2, '0')
- const hours = String(date.getHours()).padStart(2, '0')
- const minutes = String(date.getMinutes()).padStart(2, '0')
- return `${year}-${month}-${day} ${hours}:${minutes}`
- }
- const defaultStartTime = formatDateTime(tomorrow)
- const defaultEndTime = formatDateTime(dayAfterTomorrow)
- console.log('初始化查询车辆:', { defaultStartTime, defaultEndTime })
- queryAvailableCars(defaultStartTime, defaultEndTime)
- }
- /**
- * 拨打电话
- */
- const makePhoneCall = (phoneNumber) => {
- uni.makePhoneCall({
- phoneNumber: phoneNumber,
- success: () => {
- console.log('拨打电话成功')
- },
- fail: (err) => {
- console.error('拨打电话失败:', err)
- uni.showToast({
- title: '拨打电话失败',
- icon: 'none'
- })
- }
- })
- }
- /**
- * 底部按钮配置
- */
- const step2BottomButtons = computed(() => {
- if (reservationDetail.value) {
- // 查看预约详情时只显示返回按钮
- return [{ text: '返回', action: 'back' }]
- } else {
- // 预约表单时显示取消和预约按钮
- return [
- { text: '取消', action: 'back' },
- { text: '预约', action: 'save' }
- ]
- }
- })
- /**
- * 底部按钮事件处理
- * @param {Object} data - 按钮数据
- * @param {string} data.action - 按钮动作类型
- */
- const step2HandleBottomAction = (data) => {
- switch (data.action) {
- case 'back':
- // 返回处理
- step.value = 1
- selectedCar.value = null
- reservationDetail.value = null
- break
- case 'save':
- // 校验预约表单
- if (formRef.value && formRef.value.validateForm) {
- try {
- const isValid = formRef.value.validateForm(formData.value, fieldConfigs)
- console.log('表单校验结果:', isValid)
- if (isValid) {
- // 显示二次确认弹窗
- showConfirmDialog()
- }
- } catch (error) {
- console.error('表单校验出错:', error)
- uni.showToast({
- title: '表单校验失败',
- icon: 'none'
- })
- }
- } else {
- console.log('formRef或validateForm不可用')
- uni.showToast({
- title: '表单初始化失败',
- icon: 'none'
- })
- }
- break
- }
- }
- // 确认弹窗显示状态
- const showConfirm = ref(false)
- /**
- * 显示确认弹窗
- */
- const showConfirmDialog = () => {
- showConfirm.value = true
- }
- /**
- * 确认弹窗回调
- */
- const handleConfirm = (button, index) => {
- console.log('确认弹窗按钮点击:', button)
- showConfirm.value = false
- // 根据按钮类型判断是确认还是取消
- if (button.button.type === 'primary' || button.button.text === '确认') {
- submitReservation()
- }
- }
- /**
- * 提交预约
- */
- const submitReservation = async () => {
- try {
- // uni.showLoading({ title: '预约中...' })
- console.log('预约中:')
- // 基于formData组织新的param对象,参考PC端cl_ydAdd.ss.jsp的数据结构
- const reservationParam = {
- // 车辆ID(对应PC端的cdids)
- cdids: selectedCar.value.id,
- // 开始时间和结束时间(对应PC端的beginTime和endTime)
- beginTime: formData.value.yykssj,
- endTime: formData.value.yyjssj,
- // 目的地(对应PC端的mdd)
- mdd: formData.value.bsdd,
- // 使用要求/事由(对应PC端的syyq)
- syyq: formData.value.sy,
- // 使用人数(对应PC端的syrs)
- syrs: formData.value.rs,
- // 是否预警(对应PC端的sfyj,默认否)
- sfyj: 0,
- // 预警时间长度(对应PC端的yjsjcd,默认空)
- yjsjcd: '',
- // 预警描述(对应PC端的yjms,默认空)
- yjms: ''
- }
- console.log('提交预约参数:', reservationParam)
- console.log('原始表单数据:', formData.value)
- // 调用PC端的cl_sureYd接口
- const response = await clyyApi.cl_sureYd(reservationParam)
- console.log('预约响应:', response)
- uni.hideLoading()
- if (response && response.success !== false) {
- uni.showToast({
- title: '预约成功',
- icon: 'success'
- })
- // 返回上一页
- setTimeout(() => {
- uni.navigateBack()
- }, 1500)
- } else {
- uni.showToast({
- title: response.message || '预约失败',
- icon: 'none'
- })
- }
- } catch (error) {
- uni.hideLoading()
- console.error('预约失败:', error)
- uni.showToast({
- title: '预约失败,请重试',
- icon: 'none'
- })
- }
- }
- /**
- * 页面初始化
- */
- onMounted(() => {
- initPageData()
- })
- /**
- * 页面触底事件
- */
- onReachBottom(() => {
- // 只在第一步且有更多数据时触发
- if (step.value === 1 && hasMoreData.value) {
- loadMoreCars()
- }
- })
- </script>
- <style lang="scss" scoped>
- :deep(.u-th) {
- max-width: 35% !important;
- }
- .car-list {
- margin-top: 34rpx;
- padding: 0 20rpx;
- display: flex;
- flex-direction: column;
- }
- .load-more {
- padding: 30rpx;
- text-align: center;
- color: #007AFF;
- font-size: 28rpx;
- border-top: 1rpx solid #f0f0f0;
- margin-top: 20rpx;
- &:active {
- background-color: #f8f8f8;
- }
- }
- .no-more {
- padding: 30rpx;
- text-align: center;
- color: #999;
- font-size: 26rpx;
- border-top: 1rpx solid #f0f0f0;
- margin-top: 20rpx;
- }
- .current-car-section {
- padding: 20rpx;
- padding-bottom: 0;
- }
- .reservation-detail,
- .reservation-form {
- margin: 20rpx 0;
- }
- .phone-link {
- color: #007AFF;
- text-decoration: underline;
- }
- .confirm-content {
- padding: 40rpx;
- display: flex;
- flex-direction: column;
- gap: 20rpx;
- }
- .confirm-item {
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 34rpx;
- color: #333;
- .label {
- text-align: right;
- flex: 1;
- margin-right: 20rpx;
- }
- .value {
- flex: 1;
- font-weight: 500;
- }
- }
- </style>
|