diff --git a/App.vue b/App.vue index 2605455..06337e4 100644 --- a/App.vue +++ b/App.vue @@ -39,6 +39,17 @@ }, onShow: function() { console.log('App Show') + // #ifdef H5 + // 在 onShow 中也检查一次登录,因为有时候 onLaunch 可能没有正确执行 + const isWeixinBrowser = /MicroMessenger/i.test(navigator.userAgent) + if (isWeixinBrowser && process.env.NODE_ENV !== 'development') { + const token = uni.getStorageSync('token') + if (!token) { + console.log('[App] onShow 检测到没有 token,重新尝试登录') + this.wxH5AuthLogin() + } + } + // #endif }, onHide: function() { console.log('App Hide') @@ -145,50 +156,108 @@ // 判断是否在微信客户端中打开 const isWeixinBrowser = /MicroMessenger/i.test(navigator.userAgent) if (!isWeixinBrowser) { - uni.showModal({ - title: '提示', - content: '请在微信客户端中打开', - showCancel: false - }) + console.log('[App] 非微信环境,跳过登录') return } + + // 检查是否已经有 token + const existingToken = uni.getStorageSync('token') + if (existingToken) { + console.log('[App] 已存在 token,跳过登录') + return + } + let link = window.location.href; - if (/code=/.test(link) || link.indexOf("code") > -1) { - let temp = decodeURIComponent((new RegExp('[?|&]' + 'code' + '=' + '([^&;]+?)(&|#|;|$)').exec( - link) || [, ''])[1].replace(/\+/g, '%20')) || null; - console.log("code",temp) + console.log('[App] 当前 URL:', link) + + // 改进 code 提取逻辑,支持多种 URL 格式 + let code = null; + try { + // 方法1: 使用 URLSearchParams (更可靠) + if (typeof URLSearchParams !== 'undefined') { + const url = new URL(link) + code = url.searchParams.get('code') + } + + // 方法2: 如果方法1失败,使用正则表达式 + if (!code) { + const match = link.match(/[?&]code=([^&?#]+)/) + if (match && match[1]) { + code = decodeURIComponent(match[1].replace(/\+/g, '%20')) + } + } + + // 方法3: 备用正则表达式 + if (!code) { + const regex = new RegExp('[?|&]' + 'code' + '=' + '([^&;]+?)(&|#|;|$)') + const match = regex.exec(link) + if (match && match[1]) { + code = decodeURIComponent(match[1].replace(/\+/g, '%20')) + } + } + } catch (e) { + console.error('[App] 提取 code 失败:', e) + } + + console.log('[App] 提取到的 code:', code) + + if (code) { + // 清理 URL 中的 code 参数,避免重复登录 + try { + const url = new URL(link) + url.searchParams.delete('code') + url.searchParams.delete('state') + // 使用 replaceState 更新 URL,但不刷新页面 + if (window.history && window.history.replaceState) { + window.history.replaceState({}, '', url.toString()) + } + } catch (e) { + console.warn('[App] 清理 URL 参数失败:', e) + } // 上传 code 到服务器获取 token + console.log('[App] 开始调用登录接口,code:', code) uni.request({ - url: API.WX_LOGIN, - method: 'POST', - data: { code: temp }, - success: (res) => { - const result = res.data - if (result.errcode === 0) { - const token = result.data.access_token - console.log('获取 token 成功:', token) - uni.setStorageSync('token', token) - } else { - console.error('登录失败:', result.errmsg) - uni.showToast({ title: result.errmsg, icon: 'none' }) - } - }, - fail: (err) => { - console.error('获取 token 失败:', err) + url: API.WX_LOGIN, + method: 'POST', + data: { code: code }, + success: (res) => { + console.log('[App] 登录接口响应:', res.data) + const result = res.data + if (result.errcode === 0) { + const token = result.data.access_token + console.log('[App] 获取 token 成功:', token) + uni.setStorageSync('token', token) + // 触发登录成功事件,通知其他页面 token 已就绪 + uni.$emit('loginSuccess', { token }) + } else { + console.error('[App] 登录失败:', result.errmsg) + uni.showToast({ title: result.errmsg || '登录失败', icon: 'none' }) } - }) - }else{ + }, + fail: (err) => { + console.error('[App] 获取 token 失败:', err) + uni.showToast({ title: '网络错误,请重试', icon: 'none' }) + } + }) + } else { + // 没有 code,需要跳转到授权页面 + console.log('[App] 未找到 code,跳转到授权页面') const appId = 'wx9538bc740fe87fce' - const currentUrl = window.location.href - const redirectUri = encodeURIComponent(currentUrl.replace(/#\//, "")); + // 获取当前 URL,去掉 hash 和已有的 code 参数 + let currentUrl = window.location.href.split('#')[0] + // 清理已有的 code 和 state 参数 + currentUrl = currentUrl.replace(/[?&]code=[^&]*/g, '').replace(/[?&]state=[^&]*/g, '') + // 如果清理后以 ? 结尾,去掉 ? + currentUrl = currentUrl.replace(/\?$/, '') + const redirectUri = encodeURIComponent(currentUrl) const scope = 'snsapi_userinfo' const state = 'STATE' - console.log(redirectUri) - const authUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirectUri}& - response_type=code&scope=${scope}&state=${state}#wechat_redirect` + console.log('[App] redirectUri:', redirectUri) + const authUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirectUri}&response_type=code&scope=${scope}&state=${state}#wechat_redirect` // 重定向到微信授权页面 + console.log('[App] 跳转到授权页面:', authUrl) window.location.href = authUrl } }, @@ -206,8 +275,11 @@ // this.handleWxH5Login(res.data.code) } else if (res.data && res.data.data.access_token) { // 或者直接返回 token - uni.setStorageSync('token', res.data.data.access_token) + const token = res.data.data.access_token + uni.setStorageSync('token', token) uni.showToast({ title: '登录成功', icon: 'success' }) + // 触发登录成功事件 + uni.$emit('loginSuccess', { token }) } else { uni.showToast({ title: '登录失败', icon: 'none' }) } diff --git a/config/index.js b/config/index.js index 384798f..621b977 100644 --- a/config/index.js +++ b/config/index.js @@ -35,5 +35,7 @@ export const API = { GET_INVOICE: `${BASE_URL}/api/customer/reservation/get-invoice`, GET_DAILY_RESERVATION_DEADLINE: `${BASE_URL}/api/customer/setting/get-daily-reservation-deadline`, GET_GEOFENCE_BY_DIRECTION: `${BASE_URL}/api/customer/geofence/get-by-direction`, - + GET_WATER_LEVEL: `${BASE_URL}/api/customer/setting/get-water-level`, + GET_UNIT_PRICE: `${BASE_URL}/api/customer/setting/get-unit-price`, + GET_SHIP_INSPECTION_EXAMPLES: `${BASE_URL}/api/customer/setting/get-ship-inspection-examples`, } \ No newline at end of file diff --git a/pages/index/index.vue b/pages/index/index.vue index cb21890..2fbbeb4 100644 --- a/pages/index/index.vue +++ b/pages/index/index.vue @@ -5,27 +5,58 @@ 胥口枢纽闸站状态 - 今日太湖水位: - {{ statistics.water_level.taihu }}m + 太湖 + + 水位深度:{{ + statistics.taihu_to_xujiang + ? statistics.taihu_to_xujiang.water_level + : "-" + }}m + 吃水深度:{{ + statistics.taihu_to_xujiang + ? statistics.taihu_to_xujiang.draft_depth + : "-" + }}m + - 今日胥江水位: - {{ statistics.water_level.xujiang }}m + 胥江 + + 水位深度:{{ + statistics.xujiang_to_taihu + ? statistics.xujiang_to_taihu.water_level + : "-" + }}m + 吃水深度:{{ + statistics.xujiang_to_taihu + ? statistics.xujiang_to_taihu.draft_depth + : "-" + }}m + - + - - {{ item.direction==='in'?'去胥江':'去太湖' }}{{ item.name }} - - - + + {{ + item.direction === "in" ? "去胥江" : "去太湖" + }} + {{ item.name }} + + + - - @@ -36,17 +67,29 @@ - + 先预约 > - + 再购票 > - + 排队过闸 @@ -55,7 +98,11 @@ - + 过闸预约 {{ statistics.total_count }} @@ -63,7 +110,11 @@ - + 在线付款 {{ statistics.unpaid_count }} @@ -71,7 +122,11 @@ - + 排队过闸 {{ statistics.paid_count }} @@ -79,7 +134,11 @@ - + 我的开票 {{ statistics.billed_count }} @@ -103,7 +162,7 @@ 北向南2025040102准备过闸 --> - + @@ -114,7 +173,11 @@ 姓名 - + 交款人类型 @@ -164,190 +227,303 @@ \ No newline at end of file diff --git a/pages/index/ship_detail.vue b/pages/index/ship_detail.vue index f0582f2..a1527e4 100644 --- a/pages/index/ship_detail.vue +++ b/pages/index/ship_detail.vue @@ -4,6 +4,17 @@ + + + + 计算单价: + {{ unitPrice }}元 + + + 计算规则: + {{ calculationDescription }} + + 基本信息 船舶所有人{{ ship.owner_name }} @@ -13,7 +24,7 @@ 船舶参数 - 总吨位{{ ship.total_tonnage }} + 载重吨位{{ ship.total_tonnage }} 总长度{{ ship.total_length }} 总宽{{ ship.total_width }} 型深{{ ship.molded_depth }} @@ -69,10 +80,13 @@ export default { picture1_file: {}, picture2_file: {}, picture3_file: {}, + unitPrice: '', // 单价 + calculationDescription: '', // 计算规则 } }, onLoad(options) { this.id = options.id; + this.fetchUnitPrice() if (options.item) { try { this.ship = JSON.parse(decodeURIComponent(options.item)); @@ -90,6 +104,25 @@ export default { }); }, methods: { + async fetchUnitPrice() { + const token = uni.getStorageSync('token') + if (!token) { + return + } + const res = await new Promise((resolve, reject) => { + uni.request({ + url: `${API.GET_UNIT_PRICE}?token=${token}`, + method: 'get', + success: resolve, + fail: reject + }) + }) + if (res.data && res.data.errcode === 0) { + const data = res.data.data; + this.unitPrice = data.unit_price || ''; + this.calculationDescription = data.calculation_description || ''; + } + }, getShipTypeName(type) { if (!Array.isArray(this.shipTypeEnum)) return type; const found = this.shipTypeEnum.find(item => item.value === type?.toString()); @@ -314,6 +347,38 @@ export default { padding-top: 90px; padding-bottom: 20px; } +.price-info-section { + background: #fff; + border-radius: 24rpx; + margin: 0 24rpx 32rpx 24rpx; + box-shadow: 0 4rpx 16rpx rgba(59,124,255,0.08); + padding: 32rpx 24rpx; + margin-top: 20px; +} +.price-info-item { + display: flex; + align-items: flex-start; + margin-bottom: 16rpx; + font-size: 28rpx; +} +.price-info-item:last-child { + margin-bottom: 0; +} +.price-label { + color: #666; + min-width: 140rpx; + flex-shrink: 0; +} +.price-value { + color: #217aff; + font-weight: 600; + flex: 1; +} +.price-desc { + color: #222; + flex: 1; + line-height: 1.6; +} .fixed-bottom-btn-bar { position: fixed; left: 0; diff --git a/pages/index/ship_manage.vue b/pages/index/ship_manage.vue index 2ef57fa..c1f46f8 100644 --- a/pages/index/ship_manage.vue +++ b/pages/index/ship_manage.vue @@ -21,7 +21,7 @@ 所有人: {{ item.owner_name }} 联系电话: {{ item.phone }} - 总吨位: {{ item.total_tonnage }} 吨 + 载重吨位: {{ item.total_tonnage }} 吨 diff --git a/pages/order/pay_order_detail.vue b/pages/order/pay_order_detail.vue index 1b3eea1..c990c9c 100644 --- a/pages/order/pay_order_detail.vue +++ b/pages/order/pay_order_detail.vue @@ -4,6 +4,17 @@ + + + + 计算单价: + {{ unitPrice }}元 + + + 计算规则: + {{ calculationDescription }} + + 预约信息 @@ -157,6 +168,8 @@ export default { shipTypeEnum: [], reservationStatusEnum: [], qrContent: "", + unitPrice: '', // 单价 + calculationDescription: '', // 计算规则 }; }, onLoad(options) { @@ -175,6 +188,8 @@ export default { // #endif }, onShow() { + // 获取单价和计算规则 + this.fetchUnitPrice(); this.fetchShipTypeEnum().then(() => { if (this.item.id && this.item.status === "unpaid") { this.fetchQrcode(this.item.id); @@ -184,6 +199,26 @@ export default { }, methods: { formatChinaDate: base.formatChinaDate, + // 获取单价和计算规则 + async fetchUnitPrice() { + const token = uni.getStorageSync('token') + if (!token) { + return + } + const res = await new Promise((resolve, reject) => { + uni.request({ + url: `${API.GET_UNIT_PRICE}?token=${token}`, + method: 'get', + success: resolve, + fail: reject + }) + }) + if (res.data && res.data.errcode === 0) { + const data = res.data.data; + this.unitPrice = data.unit_price || ''; + this.calculationDescription = data.calculation_description || ''; + } + }, async fetchQrcode(id) { const token = uni.getStorageSync("token"); if (!token || !id) return; @@ -639,4 +674,36 @@ export default { border: none; } } +.price-info-section { + background: #fff; + border-radius: 24rpx; + margin: 0 24rpx 32rpx 24rpx; + box-shadow: 0 4rpx 16rpx rgba(59, 124, 255, 0.08); + padding: 32rpx 24rpx; + margin-top: 20px; +} +.price-info-item { + display: flex; + align-items: flex-start; + margin-bottom: 16rpx; + font-size: 28rpx; +} +.price-info-item:last-child { + margin-bottom: 0; +} +.price-label { + color: #666; + min-width: 140rpx; + flex-shrink: 0; +} +.price-value { + color: #217aff; + font-weight: 600; + flex: 1; +} +.price-desc { + color: #222; + flex: 1; + line-height: 1.6; +} diff --git a/pages/reservation/index.vue b/pages/reservation/index.vue index bb833b1..37e9c38 100644 --- a/pages/reservation/index.vue +++ b/pages/reservation/index.vue @@ -4,7 +4,17 @@ - + + + + 计算单价: + {{ unitPrice }}元 + + + 计算规则: + {{ calculationDescription }} + + 船舶信息 @@ -118,6 +128,8 @@ export default { isDateDisabled: false, // 是否禁用日期选择 userLocation: null, // 用户位置信息 {latitude, longitude} isInGeofence: null, // 是否在围栏范围内,null表示未检查,true表示在范围内,false表示不在范围内 + unitPrice: '', // 单价 + calculationDescription: '', // 计算规则 } }, onLoad() { @@ -126,6 +138,8 @@ export default { // #endif }, async onShow() { + // 获取单价和计算规则 + this.fetchUnitPrice(); // 先拉取方向、船型和可用船舶信息,如果没有可用船舶,则直接返回,不再进行后续操作(如获取位置信息等) await this.fetchDirectionEnum(); await this.fetchShipTypeEnum(); @@ -159,6 +173,26 @@ export default { this.fetchDailyReservationDeadline(); }, methods: { + // 获取单价和计算规则 + async fetchUnitPrice() { + const token = uni.getStorageSync('token') + if (!token) { + return + } + const res = await new Promise((resolve, reject) => { + uni.request({ + url: `${API.GET_UNIT_PRICE}?token=${token}`, + method: 'get', + success: resolve, + fail: reject + }) + }) + if (res.data && res.data.errcode === 0) { + const data = res.data.data; + this.unitPrice = data.unit_price || ''; + this.calculationDescription = data.calculation_description || ''; + } + }, // 获取用户位置 getUserLocation() { // #ifdef H5 @@ -924,4 +958,36 @@ export default { background: #217aff; color: #fff; } +.price-info-section { + background: #fff; + border-radius: 18px; + margin: 0 16px 16px 16px; + box-shadow: 0 2px 8px rgba(0,0,0,0.04); + padding: 18px 18px 12px 18px; + margin-top: 20px; +} +.price-info-item { + display: flex; + align-items: flex-start; + margin-bottom: 12px; + font-size: 14px; +} +.price-info-item:last-child { + margin-bottom: 0; +} +.price-label { + color: #666; + min-width: 100px; + flex-shrink: 0; +} +.price-value { + color: #217aff; + font-weight: 600; + flex: 1; +} +.price-desc { + color: #222; + flex: 1; + line-height: 1.6; +} \ No newline at end of file diff --git a/收尾20251215.markdown b/收尾20251215.markdown new file mode 100644 index 0000000..03ea3fa --- /dev/null +++ b/收尾20251215.markdown @@ -0,0 +1,35 @@ +后台: + +1. 价格计算参数、吃水深度计算参数; +2. 短信通知、模版消息/订阅消息; +3. 初审之后就可以预约; +4. 退款处理流程 + +前台:(新增的三个接口在swagger的[用户端系统设置](https://xukoushuniu.115.langye.net/swagger/#/用户端系统设置)) + +-1. “总吨位”改成“载重吨位”,直接给出价格算法和结果,分别在填写船舶信息、查看船舶信息、预约界面展示; + +2. 船检簿示例及文字说明,通过接口获取,展示在录入船舶信息的页面; + +3. 订阅消息(优先级低,相关文档请参照公众号订阅消息文档,后端接口暂未提供):(1)_v39TguWNu6ALEpdXSuy8hHFep0m4NrARAutSvl5sRE,预约成功之后订阅,后台排班确认后触发 + + _v39TguWNu6ALEpdXSuy8hHFep0m4NrARAutSvl5sRE + + ![image-20251215231038839](/Users/weizongsong/Library/Application Support/typora-user-images/image-20251215231038839.png) + + + + (1)4ydgM4-nHNOfYiotYFmvxZR_fOwHlQcgc-aXu4KZ8Hs,支付成功之后订阅,后台开始检票后触发 + + ![image-20251215231103582](/Users/weizongsong/Library/Application Support/typora-user-images/image-20251215231103582.png) + +4. 初次进入页面未自动登录的BUG:初次进入首页的时候,携带了code但是没有自动登录。 + +5. 水位信息展示修改:通过/api/customer/setting/get-water-level获取数据,前端展示水位和吃水深度 + +其他: + +1. 操作手册 +2. 上线节点梳理 +3. 后台用户清单 + diff --git a/用户操作手册.md b/用户操作手册.md new file mode 100644 index 0000000..c1698e2 --- /dev/null +++ b/用户操作手册.md @@ -0,0 +1,406 @@ +# 胥口闸站购票系统 - 用户操作手册 + +## 目录 +1. [系统简介](#系统简介) +2. [首次使用](#首次使用) +3. [主要功能](#主要功能) + - [3.1 首页](#31-首页) + - [3.2 船舶管理](#32-船舶管理) + - [3.3 过闸预约](#33-过闸预约) + - [3.4 订单管理](#34-订单管理) + - [3.5 在线付款](#35-在线付款) + - [3.6 发票管理](#36-发票管理) + - [3.7 消息中心](#37-消息中心) + - [3.8 个人中心](#38-个人中心) + +--- + +## 系统简介 + +胥口闸站购票系统是一款为船舶过闸提供预约、购票、支付等服务的移动端应用。系统支持微信小程序和H5网页两种使用方式。 + +**核心流程:先预约 → 再购票 → 排队过闸** + +--- + +## 首次使用 + +### 1. 登录/授权 + +#### 微信小程序用户 +- 打开微信,搜索"胥口闸站购票"小程序 +- 首次打开会自动弹出微信授权提示 +- 点击"允许"完成授权登录 + +#### 微信H5用户 +- 在微信中打开系统链接 +- 系统会自动跳转到微信授权页面 +- 点击"同意"完成授权登录 + +**注意:** 系统需要获取您的位置信息用于预约验证,请允许位置权限。 + +--- + +## 主要功能 + +### 3.1 首页 + +首页是系统的核心入口,显示以下信息: + +#### 3.1.1 闸站状态信息 +- **今日太湖水位**:显示当前太湖水位高度(单位:米) +- **今日胥江水位**:显示当前胥江水位高度(单位:米) +- **当前批次**:显示正在进行的过闸批次信息(最多显示2个) + +#### 3.1.2 闸站流程指引 +系统提供清晰的流程指引: +1. **先预约** - 选择航行方向和过闸日期进行预约 +2. **再购票** - 预约成功后进行在线支付 +3. **排队过闸** - 支付完成后等待过闸 + +#### 3.1.3 功能入口卡片 +首页提供四个主要功能入口: + +1. **过闸预约** + - 显示预约总数 + - 点击进入预约页面 + +2. **在线付款** + - 显示待支付订单数量 + - 点击进入付款列表 + +3. **排队过闸** + - 显示已支付订单数量 + - 点击查看排队状态 + +4. **我的开票** + - 显示已开票数量 + - 点击进入发票管理 + +--- + +### 3.2 船舶管理 + +在预约过闸前,您需要先添加并审核通过船舶信息。 + +#### 3.2.1 进入船舶管理 +- 方式一:从首页点击"过闸预约",如果没有可用船舶会自动跳转到船舶管理 +- 方式二:从"我的"页面进入船舶管理 + +#### 3.2.2 添加船舶 +1. 点击"添加船只"按钮 +2. 按步骤填写信息: + + **第一步:基本信息** + - 船舶所有人(必填):姓名需与身份证一致 + - 身份证号(必填):18位身份证号码 + - 联系电话(必填):11位手机号码 + - 船舶编号(必填) + - 船舶类型(必填):选择货船或客船 + + **第二步:船舶参数** + - 总吨位(必填,单位:吨) + - 总长度(必填,单位:米) + - 总宽(必填,单位:米) + - 型深(必填,单位:米) + - 参考载重吨位(必填):选择A/B/C/D等级 + - 船型(必填):根据实际情况选择 + + **第三步:船检簿上传** + - 第一页(必填):上传船检簿第一页照片 + - 第二页(必填):上传船检簿第二页照片 + - 第三页(必填):上传船检簿第三页照片 + - 可点击"查看示例"了解上传要求 + + **第四步:签名确认** + - 勾选承诺声明:"本人承诺所提供材料皆真实有效;如有虚假,本人承担因此造成的全部责任。" + - 手写签名:在签名区域完成手写签名 + - 可点击"重新签名"清除重签 + - 可点击"预览签名"查看签名效果 + +3. 点击"提交"完成添加 +4. 提交后等待审核,审核状态可在船舶列表中查看 + +#### 3.2.3 查看船舶详情 +- 在船舶列表中点击任意船舶卡片 +- 查看船舶的详细信息、审核状态和签名图片 + +#### 3.2.4 编辑船舶 +- 在船舶详情页点击"编辑"按钮 +- 或从船舶管理列表点击"编辑" +- 修改信息后重新提交审核 + +**注意:** 编辑时如果已有签名,系统会显示原有签名,您可以选择使用原签名或重新签名。 + +#### 3.2.5 删除船舶 +- 在船舶详情页点击"删除"按钮 +- 确认删除后,该船舶信息将被永久删除 + +#### 3.2.6 船舶审核状态 +- **待审核**:已提交,等待审核 +- **已通过**:审核通过,可用于预约 +- **已拒绝**:审核未通过,需修改后重新提交 + +--- + +### 3.3 过闸预约 + +预约是过闸的第一步,需要选择航行方向和过闸日期。 + +#### 3.3.1 进入预约页面 +- 从首页点击"过闸预约"卡片 +- 系统会先检查是否有可用船舶: + - 如果没有可用船舶,会提示并跳转到船舶管理页面 + - 如果有可用船舶,继续预约流程 + +#### 3.3.2 位置信息获取 +- 进入预约页面时,系统会弹出提示:"预约前需要先获取您的位置信息" +- 点击"确定"允许获取位置 +- 系统会自动获取您当前的经纬度信息 +- **重要:** 位置信息用于验证您是否在闸站可预约范围内 + +#### 3.3.3 选择航行方向 +- 系统会显示可选的航行方向(如:去胥江、去太湖等) +- 点击选择您要前往的方向 +- **注意:** 必须选择航行方向才能继续 + +#### 3.3.4 选择过闸日期 +- 系统会根据当前时间和截止时间自动设置默认日期: + - 如果当前时间早于截止时间:默认选择"今天",可更改 + - 如果当前时间晚于截止时间:默认选择"明天",不可更改(按钮显示为橙色渐变背景) +- **注意:** 过闸日期一旦确定后不可更改 + +#### 3.3.5 位置范围验证 +- 选择航行方向后,系统会自动验证您的位置是否在可预约范围内 +- 如果不在范围内,会弹出提示:"您的当前位置不在闸站可预约范围内" +- 此时无法提交预约,请移动到可预约范围内后重试 + +#### 3.3.6 阅读并同意预约须知 +- 勾选"我已阅读并同意《过闸预约服务协议》" +- 必须同意才能提交预约 + +#### 3.3.7 提交预约 +- 确认所有信息无误后,点击底部"预约"按钮 +- 系统会验证: + - 是否同意预约须知 + - 是否选择航行方向 + - 位置是否在可预约范围内 + - 是否有可用船舶 +- 验证通过后提交成功,跳转到订单页面 + +--- + +### 3.4 订单管理 + +在"订单"页面可以查看所有预约订单,进行支付、取消、重新预约等操作。 + +#### 3.4.1 查看订单列表 +- 点击底部导航栏"订单"标签 +- 显示所有预约订单,按时间倒序排列 +- 每个订单卡片显示: + - 订单状态(待支付、已支付、已取消等) + - 开票状态(如已开票) + - 创建时间 + - 订单编号和船舶编号 + - 航行方向和批次信息 + +#### 3.4.2 订单状态说明 +- **待支付**:预约成功,等待支付 +- **已支付**:已支付,等待过闸 +- **已取消**:订单已取消 +- **其他状态**:根据实际情况显示 + +#### 3.4.3 订单操作 + +**取消预约** +- 仅"待支付"状态的订单可以取消 +- 点击"取消预约"按钮 +- 确认后订单状态变为"已取消" + +**重新预约** +- 某些状态下可以重新预约 +- 点击"重新预约"按钮 +- 跳转到预约页面,重新填写信息 + +**去支付** +- "待支付"状态的订单可以支付 +- 点击"去支付"按钮 +- 跳转到支付页面 + +**去开票** +- 已支付的订单可以开具发票 +- 点击"去开票"按钮 +- 跳转到发票开具页面 + +**查看详情** +- 点击"查看详情"按钮 +- 查看订单的完整信息 + +#### 3.4.4 下拉刷新和上拉加载 +- 下拉页面可以刷新订单列表 +- 上拉到底部可以加载更多订单 + +--- + +### 3.5 在线付款 + +#### 3.5.1 进入付款页面 +- 方式一:从首页点击"在线付款"卡片 +- 方式二:从订单列表点击"去支付"按钮 + +#### 3.5.2 查看待支付订单 +- 显示所有待支付的订单列表 +- 每个订单显示: + - 订单编号 + - 船舶信息 + - 航行方向 + - 过闸日期 + - 应付金额 + +#### 3.5.3 支付订单 +1. 选择要支付的订单 +2. 确认订单信息 +3. 选择支付方式 +4. 完成支付 +5. 支付成功后订单状态更新为"已支付" + +#### 3.5.4 查看支付详情 +- 点击订单可以查看支付详情 +- 包括支付时间、支付金额、支付方式等信息 + +--- + +### 3.6 发票管理 + +#### 3.6.1 进入发票管理 +- 方式一:从首页点击"我的开票"卡片 +- 方式二:从"我的"页面进入发票管理 + +#### 3.6.2 查看发票列表 +- 显示所有已开具的发票 +- 每个发票显示: + - 发票类型 + - 发票金额 + - 开具时间 + - 发票状态 + +#### 3.6.3 开具发票 +1. 从订单详情页点击"去开票" +2. 或从发票管理页面点击"开具发票" +3. 选择要开具发票的订单 +4. 填写发票信息: + - 发票类型(个人/单位) + - 发票抬头 + - 税号(单位必填) + - 联系方式 +5. 提交开具申请 +6. 开具成功后可在发票列表中查看 + +#### 3.6.4 查看发票详情 +- 点击发票卡片查看发票详情 +- 可以查看发票的完整信息 +- 可以下载或打印发票 + +--- + +### 3.7 消息中心 + +#### 3.7.1 进入消息中心 +- 点击底部导航栏"消息"标签 + +#### 3.7.2 查看消息 +- 显示所有系统通知和公告消息 +- 每条消息显示: + - 消息类型标签(带颜色区分) + - 消息标题 + - 消息内容 + - 发布时间 + +#### 3.7.3 消息类型 +- 系统会根据消息类型显示不同颜色的标签 +- 包括:公告、通知、提醒等类型 + +#### 3.7.4 刷新和加载 +- 下拉可以刷新消息列表 +- 上拉可以加载更多消息 + +--- + +### 3.8 个人中心 + +#### 3.8.1 进入个人中心 +- 点击底部导航栏"我的"标签 + +#### 3.8.2 个人信息 +- 显示头像、昵称、手机号等信息 +- 点击个人信息区域可以编辑 + +#### 3.8.3 编辑个人信息 +1. 点击个人信息区域或设置图标 +2. 在弹出的编辑窗口中修改: + - 姓名 + - 交款人类型(个人/单位) + - 手机号 + - 证件号(身份证号) +3. 点击"保存"完成修改 + +#### 3.8.4 功能菜单 +个人中心提供以下功能入口: +- **船舶管理**:管理您的船舶信息 +- **发票管理**:查看和管理发票 +- **关于我们**:查看系统相关信息 + +--- + +## 常见问题 + +### Q1: 为什么无法预约? +**A:** 可能的原因: +1. 没有可用船舶或船舶未审核通过 +2. 未选择航行方向 +3. 当前位置不在闸站可预约范围内 +4. 未同意预约须知 + +### Q2: 为什么过闸日期不能更改? +**A:** 系统根据当前时间和截止时间自动设置过闸日期,一旦确定后不可更改,这是为了确保预约的准确性。 + +### Q3: 如何知道我的位置是否在可预约范围内? +**A:** 选择航行方向后,系统会自动验证。如果不在范围内,会弹出提示信息。 + +### Q4: 船舶审核需要多长时间? +**A:** 审核时间由管理员决定,请耐心等待。审核结果会通过消息通知您。 + +### Q5: 可以取消已支付的订单吗? +**A:** 已支付的订单无法取消,如需取消请联系客服。 + +### Q6: 发票可以重复开具吗? +**A:** 每个订单只能开具一次发票,请谨慎操作。 + +### Q7: 忘记登录怎么办? +**A:** 系统使用微信授权登录,重新打开应用会自动登录。 + +--- + +## 注意事项 + +1. **位置权限**:预约功能需要获取您的位置信息,请务必允许位置权限 +2. **船舶信息**:请确保船舶信息真实有效,虚假信息将承担相应责任 +3. **预约时间**:请在截止时间前完成预约,过期将无法预约当日 +4. **支付时效**:预约成功后请及时支付,避免订单过期 +5. **网络环境**:建议在良好的网络环境下使用,避免操作失败 + +--- + +## 技术支持 + +如遇到问题,可通过以下方式联系: +- 查看系统消息中的通知 +- 联系客服(如有提供联系方式) + +--- + +**版本信息** +- 文档版本:v1.0 +- 更新日期:2025年 +- 适用平台:微信小程序、微信H5 +