From d4470f1be3134cc219b6a529bc22567540d01255 Mon Sep 17 00:00:00 2001
From: liujie <1793218484@qq.com>
Date: 星期五, 14 八月 2026 14:00:39 +0800
Subject: [PATCH] feat(guide): add withdrawal settlement flow

---
 mingdaojingjiang_guide_app/constants/mock.js                          |   90 +
 mingdaojingjiang_guide_app/pages/bank-card-select/index.vue           |  186 +++
 mingdaojingjiang_guide_app/pages/withdrawal-success/index.vue         |  114 ++
 mingdaojingjiang_guide_app/pages/bank-branch-select/index.vue         |  257 +++++
 mingdaojingjiang_guide_app/pages/withdrawal-detail/index.vue          |  342 ++++++
 mingdaojingjiang_guide_app/pages/withdrawal/index.vue                 |  267 +++++
 mingdaojingjiang_guide_app/components/guide-nav-bar/guide-nav-bar.vue |   77 +
 mingdaojingjiang_guide_app/static/icons/clock-3.svg                   |    1 
 mingdaojingjiang_guide_app/services/withdrawal.js                     |   49 
 mingdaojingjiang_guide_app/static/icons/search.svg                    |    1 
 mingdaojingjiang_guide_app/services/settlement.js                     |  141 ++
 mingdaojingjiang_guide_app/pages.json                                 |   84 +
 mingdaojingjiang_guide_app/docs/开发说明.md                               |   13 
 mingdaojingjiang_guide_app/pages/earnings/index.vue                   |    7 
 mingdaojingjiang_guide_app/pages/onboarding/index.vue                 |  687 +++++++++++++
 mingdaojingjiang_guide_app/constants/area-codes.js                    |   16 
 mingdaojingjiang_guide_app/pages/bank-card-add/index.vue              |  426 ++++++++
 mingdaojingjiang_guide_app/pages/onboarding-pending/index.vue         |  223 ++++
 mingdaojingjiang_guide_app/static/icons/chevron-left.svg              |    1 
 19 files changed, 2,966 insertions(+), 16 deletions(-)

diff --git a/mingdaojingjiang_guide_app/components/guide-nav-bar/guide-nav-bar.vue b/mingdaojingjiang_guide_app/components/guide-nav-bar/guide-nav-bar.vue
index 079f102..c80cee1 100644
--- a/mingdaojingjiang_guide_app/components/guide-nav-bar/guide-nav-bar.vue
+++ b/mingdaojingjiang_guide_app/components/guide-nav-bar/guide-nav-bar.vue
@@ -1,10 +1,13 @@
 <template>
-  <view class="guide-nav-bar" :style="navStyle">
+  <view class="guide-nav-bar" :class="'is-' + theme" :style="navStyle">
     <view class="guide-nav-bar__row">
-      <button v-if="showBack" class="guide-nav-bar__back" @tap="handleBack">
-        <image class="guide-nav-bar__icon" src="/static/icons/chevron-left-white.svg" mode="aspectFit" />
-      </button>
+      <view v-if="showBack" class="guide-nav-bar__back" @tap="handleBack">
+        <image class="guide-nav-bar__icon" :src="backIcon" mode="aspectFit" />
+      </view>
       <text class="guide-nav-bar__title">{{ title }}</text>
+      <view v-if="actionText" class="guide-nav-bar__action" @tap="handleAction">
+        <text class="guide-nav-bar__action-text">{{ actionText }}</text>
+      </view>
     </view>
   </view>
 </template>
@@ -26,6 +29,14 @@
     backgroundColor: {
       type: String,
       default: '#4d8fe8'
+    },
+    actionText: {
+      type: String,
+      default: ''
+    },
+    theme: {
+      type: String,
+      default: 'brand'
     }
   },
   data() {
@@ -34,6 +45,18 @@
     }
   },
   computed: {
+    isLight() {
+      return this.theme === 'light'
+    },
+    backIcon() {
+      return this.isLight ? '/static/icons/chevron-left.svg' : '/static/icons/chevron-left-white.svg'
+    },
+    barBackground() {
+      if (this.isLight && this.backgroundColor === '#4d8fe8') {
+        return '#f4f6f9'
+      }
+      return this.backgroundColor
+    },
     navHeightPx() {
       return this.statusBarHeight + upxToPx(98)
     },
@@ -41,13 +64,16 @@
       return {
         paddingTop: `${this.statusBarHeight}px`,
         height: `${this.navHeightPx}px`,
-        background: this.backgroundColor
+        background: this.barBackground
       }
     }
   },
   methods: {
     handleBack() {
       this.$emit('back')
+    },
+    handleAction() {
+      this.$emit('action')
     }
   }
 }
@@ -82,13 +108,7 @@
   width: 88rpx;
   height: 64rpx;
   padding: 0;
-  border: 0;
-  background: transparent;
   transform: translateY(-50%);
-}
-
-.guide-nav-bar__back::after {
-  border: 0;
 }
 
 .guide-nav-bar__icon {
@@ -97,7 +117,7 @@
 }
 
 .guide-nav-bar__title {
-  max-width: 460rpx;
+  max-width: 360rpx;
   color: #ffffff;
   font-size: 32rpx;
   font-weight: 800;
@@ -105,4 +125,37 @@
   text-overflow: ellipsis;
   white-space: nowrap;
 }
+
+.guide-nav-bar__action {
+  position: absolute;
+  right: 24rpx;
+  top: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  min-width: 104rpx;
+  height: 52rpx;
+  padding: 0 22rpx;
+  border-radius: 10rpx;
+  background: #ffffff;
+  transform: translateY(-50%);
+}
+
+.guide-nav-bar__action-text {
+  color: #4d8fe8;
+  font-size: 26rpx;
+  font-weight: 700;
+  line-height: 1;
+}
+
+.guide-nav-bar.is-light .guide-nav-bar__title {
+  color: #202b3e;
+  font-size: 34rpx;
+  font-weight: 700;
+}
+
+.guide-nav-bar.is-light .guide-nav-bar__icon {
+  width: 38rpx;
+  height: 38rpx;
+}
 </style>
diff --git a/mingdaojingjiang_guide_app/constants/area-codes.js b/mingdaojingjiang_guide_app/constants/area-codes.js
new file mode 100644
index 0000000..5b9f9b0
--- /dev/null
+++ b/mingdaojingjiang_guide_app/constants/area-codes.js
@@ -0,0 +1,16 @@
+export const areaProvinceNames = ['北京', '上海', '重庆', '四川省', '广东省']
+export const areaProvinceCodes = ['110000', '310000', '500000', '510000', '440000']
+export const areaCityNames = [
+  ['北京市'],
+  ['上海市'],
+  ['重庆市'],
+  ['成都市', '自贡市', '攀枝花市', '泸州市', '德阳市', '绵阳市', '广元市', '遂宁市', '内江市', '乐山市', '南充市', '眉山市', '宜宾市', '广安市', '达州市', '雅安市', '巴中市', '资阳市'],
+  ['广州市', '深圳市', '珠海市', '佛山市', '东莞市']
+]
+export const areaCityCodes = [
+  ['110100'],
+  ['310100'],
+  ['500100'],
+  ['510100', '510300', '510400', '510500', '510600', '510700', '510800', '510900', '511000', '511100', '511300', '511400', '511500', '511600', '511700', '511800', '511900', '512000'],
+  ['440100', '440300', '440400', '440600', '441900']
+]
diff --git a/mingdaojingjiang_guide_app/constants/mock.js b/mingdaojingjiang_guide_app/constants/mock.js
index e70f126..a058a34 100644
--- a/mingdaojingjiang_guide_app/constants/mock.js
+++ b/mingdaojingjiang_guide_app/constants/mock.js
@@ -142,3 +142,93 @@
   { id: 'E20260616008', title: '拼团 · 精讲', orderNo: 'MJ20260616008', amountCent: 26880, status: '已入账', time: '昨日 16:36' },
   { id: 'E20260615003', title: '包团 · 快讲', orderNo: 'MJ20260615003', amountCent: 32200, status: '已入账', time: '06-15 11:12' }
 ]
+
+export const GUIDE_WITHDRAWALS_KEY = 'mingdao_guide_withdrawals'
+export const GUIDE_ONBOARDING_KEY = 'mingdao_guide_onboarding'
+export const GUIDE_BANK_CARDS_KEY = 'mingdao_guide_bank_cards'
+export const GUIDE_SELECTED_BANK_KEY = 'mingdao_guide_selected_bank_id'
+export const GUIDE_SELECTED_BRANCH_KEY = 'mingdao_guide_selected_branch'
+
+export const withdrawalSummary = {
+  availableAmount: 590.80
+}
+
+export const initialOnboarding = {
+  status: 'unsubmitted',
+  reason: '',
+  submittedAt: ''
+}
+
+export const initialBankCards = [
+  {
+    id: 'card_001',
+    bankName: '招商银行',
+    branchName: '招商银行成都高新支行',
+    cardMasked: '****5678',
+    status: 'approved',
+    reason: ''
+  },
+  {
+    id: 'card_002',
+    bankName: '中国银行',
+    branchName: '中国银行都江堰支行',
+    cardMasked: '****1024',
+    status: 'pending',
+    reason: ''
+  },
+  {
+    id: 'card_003',
+    bankName: '工商银行',
+    branchName: '工商银行青城山支行',
+    cardMasked: '****8899',
+    status: 'rejected',
+    reason: '开户名与进件主体不一致'
+  }
+]
+
+export const bankBranches = [
+  { id: 'br_001', bankName: '招商银行', branchName: '招商银行成都高新支行', branchCode: 'CMB51010001' },
+  { id: 'br_002', bankName: '招商银行', branchName: '招商银行都江堰支行', branchCode: 'CMB51010002' },
+  { id: 'br_003', bankName: '中国银行', branchName: '中国银行成都天府支行', branchCode: 'BOC51010001' },
+  { id: 'br_004', bankName: '中国银行', branchName: '中国银行都江堰支行', branchCode: 'BOC51010002' },
+  { id: 'br_005', bankName: '工商银行', branchName: '工商银行成都春熙路支行', branchCode: 'ICBC51010001' },
+  { id: 'br_006', bankName: '工商银行', branchName: '工商银行青城山支行', branchCode: 'ICBC51010002' },
+  { id: 'br_007', bankName: '建设银行', branchName: '建设银行成都高新支行', branchCode: 'CCB51010001' },
+  { id: 'br_008', bankName: '农业银行', branchName: '农业银行都江堰支行', branchCode: 'ABC51010001' }
+]
+
+export const initialWithdrawals = [
+  {
+    id: 'WD20260808001',
+    withdrawalNo: 'WD202608081643001',
+    amount: 200.00,
+    status: 'APPROVED',
+    reason: '',
+    bankName: '招商银行',
+    cardMasked: '****5678',
+    appliedAt: '2026-08-08 16:43:42',
+    processedAt: '2026-08-09 09:12:18'
+  },
+  {
+    id: 'WD20260805002',
+    withdrawalNo: 'WD202608051020002',
+    amount: 86.50,
+    status: 'PENDING',
+    reason: '',
+    bankName: '招商银行',
+    cardMasked: '****5678',
+    appliedAt: '2026-08-05 10:20:11',
+    processedAt: ''
+  },
+  {
+    id: 'WD20260728003',
+    withdrawalNo: 'WD202607281530003',
+    amount: 120.00,
+    status: 'REJECTED',
+    reason: '收款账户信息与实名信息不一致',
+    bankName: '招商银行',
+    cardMasked: '****5678',
+    appliedAt: '2026-07-28 15:30:06',
+    processedAt: '2026-07-28 18:02:41'
+  }
+]
diff --git "a/mingdaojingjiang_guide_app/docs/\345\274\200\345\217\221\350\257\264\346\230\216.md" "b/mingdaojingjiang_guide_app/docs/\345\274\200\345\217\221\350\257\264\346\230\216.md"
index 53d8a58..c50c830 100644
--- "a/mingdaojingjiang_guide_app/docs/\345\274\200\345\217\221\350\257\264\346\230\216.md"
+++ "b/mingdaojingjiang_guide_app/docs/\345\274\200\345\217\221\350\257\264\346\230\216.md"
@@ -11,7 +11,15 @@
 - `pages/receiving/index.vue`:选择景区、汇合点并开启 / 停止接单。
 - `pages/order-detail/index.vue`:订单详情,支持接单、确认客户到场、开始讲解、结束讲解。
 - `pages/orders/index.vue`:历史订单页,按全部讲解、接单中、讲解中、已完成筛选。
-- `pages/earnings/index.vue`:业绩明细页,展示今日收益、本月收益、筛选栏和收益明细列表。
+- `pages/earnings/index.vue`:业绩明细页,展示今日收益、本月收益、筛选栏和收益明细列表;右上角「提现」进入导游提现。
+- `pages/withdrawal/index.vue`:导游提现列表,展示当前可提、提现按钮和提现记录;按进件状态跳转进件、审核中或提现详情。
+- `pages/onboarding/index.vue`:导游进件表单,支持企业/个人主体资料提交审核。
+- `pages/onboarding-pending/index.vue`:进件审核中 / 审核未通过状态页;静态 mock 可通过「刷新审核状态」变为已通过。
+- `pages/withdrawal-detail/index.vue`:填写提现金额,并选择 / 更换审核通过的银行卡后提交。
+- `pages/bank-card-select/index.vue`:银行卡列表,仅已通过的卡可选用,支持添加银行卡。
+- `pages/bank-card-add/index.vue`:填写卡号、开户省份/城市和开户行。
+- `pages/bank-branch-select/index.vue`:搜索并选择开户行。
+- `pages/withdrawal-success/index.vue`:提现提交成功页。
 - `pages/rating/index.vue`:我的评分页,展示综合评分、日期筛选和评价列表。
 - `pages/profile/index.vue`:个人中心信息页,展示头像、手机号,并支持更换头像和进入更换手机号流程。
 - `pages/change-phone-old/index.vue`:更换手机号第一步,验证旧手机号。
@@ -138,3 +146,6 @@
 | 2026-06-17 | 优化历史订单页列表设计:订单状态改为彩色标签,接单中/讲解中/已完成用不同颜色区分,并增强订单信息层级。 |
 | 2026-07-28 | 汇合点接入地图展示:为景区汇合点补充经纬度与地址,接单选择页改为真地图预览,首页/订单详情/接单页点击“查看汇合点”后直接拉起系统地图导航。 |
 | 2026-07-29 | 导游意见反馈接入 `POST /api/guide/feedback`,提交人身份从 GUIDE Token 获取,反馈统一进入后台意见反馈管理。 |
+| 2026-08-14 | 业绩明细右上角增加「提现」入口,复用商户端提现静态流程:列表、填写金额、提交成功;当前为 App 本地 mock。 |
+| 2026-08-14 | 补齐提现完整静态流程:导游进件、审核状态、选择/填写银行卡、选择开户行;提现详情支持选择或更换已通过银行卡。 |
+| 2026-08-14 | 提现复用页顶栏改为浅色(灰底深色标题),与原商户端一致;业绩明细等导游主流程页仍用蓝底导航。 |
diff --git a/mingdaojingjiang_guide_app/pages.json b/mingdaojingjiang_guide_app/pages.json
index 8635d18..fb76f90 100644
--- a/mingdaojingjiang_guide_app/pages.json
+++ b/mingdaojingjiang_guide_app/pages.json
@@ -69,6 +69,78 @@
       }
     },
     {
+      "path": "pages/withdrawal/index",
+      "style": {
+        "navigationStyle": "custom",
+        "navigationBarTitleText": "导游提现",
+        "navigationBarBackgroundColor": "#4d8fe8",
+        "navigationBarTextStyle": "white"
+      }
+    },
+    {
+      "path": "pages/withdrawal-detail/index",
+      "style": {
+        "navigationStyle": "custom",
+        "navigationBarTitleText": "导游提现",
+        "navigationBarBackgroundColor": "#4d8fe8",
+        "navigationBarTextStyle": "white"
+      }
+    },
+    {
+      "path": "pages/withdrawal-success/index",
+      "style": {
+        "navigationStyle": "custom",
+        "navigationBarTitleText": "导游提现",
+        "navigationBarBackgroundColor": "#4d8fe8",
+        "navigationBarTextStyle": "white"
+      }
+    },
+    {
+      "path": "pages/onboarding/index",
+      "style": {
+        "navigationStyle": "custom",
+        "navigationBarTitleText": "导游进件",
+        "navigationBarBackgroundColor": "#4d8fe8",
+        "navigationBarTextStyle": "white"
+      }
+    },
+    {
+      "path": "pages/onboarding-pending/index",
+      "style": {
+        "navigationStyle": "custom",
+        "navigationBarTitleText": "导游进件",
+        "navigationBarBackgroundColor": "#4d8fe8",
+        "navigationBarTextStyle": "white"
+      }
+    },
+    {
+      "path": "pages/bank-card-select/index",
+      "style": {
+        "navigationStyle": "custom",
+        "navigationBarTitleText": "选择银行卡",
+        "navigationBarBackgroundColor": "#4d8fe8",
+        "navigationBarTextStyle": "white"
+      }
+    },
+    {
+      "path": "pages/bank-card-add/index",
+      "style": {
+        "navigationStyle": "custom",
+        "navigationBarTitleText": "填写银行卡",
+        "navigationBarBackgroundColor": "#4d8fe8",
+        "navigationBarTextStyle": "white"
+      }
+    },
+    {
+      "path": "pages/bank-branch-select/index",
+      "style": {
+        "navigationStyle": "custom",
+        "navigationBarTitleText": "选择开户行",
+        "navigationBarBackgroundColor": "#4d8fe8",
+        "navigationBarTextStyle": "white"
+      }
+    },
+    {
       "path": "pages/rating/index",
       "style": {
         "navigationStyle": "custom",
@@ -162,5 +234,15 @@
     "navigationBarBackgroundColor": "#F6F8FB",
     "backgroundColor": "#F6F8FB"
   },
-  "uniIdRouter": {}
+  "uniIdRouter": {},
+	"condition" : { //模式配置,仅开发期间生效
+		"current": 0, //当前激活的模式(list 的索引项)
+		"list": [
+			{
+				"name": "", //模式名称
+				"path": "", //启动页面,必选
+				"query": "" //启动参数,在页面的onLoad函数里面得到
+			}
+		]
+	}
 }
diff --git a/mingdaojingjiang_guide_app/pages/bank-branch-select/index.vue b/mingdaojingjiang_guide_app/pages/bank-branch-select/index.vue
new file mode 100644
index 0000000..057d5a6
--- /dev/null
+++ b/mingdaojingjiang_guide_app/pages/bank-branch-select/index.vue
@@ -0,0 +1,257 @@
+<template>
+  <view class="page" :class="{ leaving: isLeaving }">
+    <guide-nav-bar title="选择开户行" @back="goBack" />
+
+    <view class="search-section">
+      <view class="search-box">
+        <input
+          class="search-input"
+          v-model="searchKeyword"
+          maxlength="30"
+          placeholder="搜索银行或支行名称"
+          placeholder-class="search-placeholder"
+          @input="onSearchInput"
+        />
+        <image class="search-icon" src="/static/icons/search.svg" mode="aspectFit" />
+      </view>
+      <text class="city-tip">当前开户城市:{{ cityName }}</text>
+    </view>
+
+    <scroll-view class="branch-scroll" scroll-y>
+      <view class="branch-card">
+        <view v-for="(branch, index) in branches" :key="branch.id" class="branch-item" @tap="selectBranch(branch)">
+          <text class="branch-name">{{ branch.branchName }}</text>
+          <image class="branch-arrow" src="/static/icons/chevron-right.svg" mode="aspectFit" />
+          <view v-if="index < branches.length - 1" class="branch-divider"></view>
+        </view>
+      </view>
+      <view v-if="loading" class="empty-state">
+        <text class="empty-title">正在加载开户行</text>
+      </view>
+      <view v-else-if="loadError" class="empty-state" @tap="loadBranches">
+        <text class="empty-title">开户行加载失败</text>
+        <text class="empty-tip">点击重新加载</text>
+      </view>
+      <view v-else-if="!branches.length" class="empty-state">
+        <text class="empty-title">未找到匹配的开户行</text>
+        <text class="empty-tip">请尝试输入银行简称或支行名称</text>
+      </view>
+      <view class="safe-space"></view>
+    </scroll-view>
+  </view>
+</template>
+
+<script>
+import GuideNavBar from '../../components/guide-nav-bar/guide-nav-bar.vue'
+import { saveSelectedBranch, searchBankBranches } from '../../services/settlement'
+import { backOrHome } from '../../utils/router'
+
+export default {
+  components: {
+    GuideNavBar
+  },
+  data() {
+    return {
+      cityName: '',
+      cityCode: '',
+      searchKeyword: '',
+      branches: [],
+      loading: false,
+      loadError: false,
+      searchTimer: null,
+      isLeaving: false
+    }
+  },
+  onLoad(options) {
+    if (options && options.city) {
+      this.cityName = decodeURIComponent(options.city)
+    }
+    if (options && options.cityCode) {
+      this.cityCode = decodeURIComponent(options.cityCode)
+    }
+    this.loadBranches()
+  },
+  onUnload() {
+    if (this.searchTimer) {
+      clearTimeout(this.searchTimer)
+    }
+  },
+  methods: {
+    goBack() {
+      if (this.isLeaving) {
+        return
+      }
+      this.isLeaving = true
+      setTimeout(() => {
+        backOrHome('/pages/bank-card-add/index')
+      }, 280)
+    },
+    onSearchInput() {
+      if (this.searchTimer) {
+        clearTimeout(this.searchTimer)
+      }
+      this.searchTimer = setTimeout(() => {
+        this.loadBranches()
+      }, 250)
+    },
+    loadBranches() {
+      this.loading = true
+      this.loadError = false
+      searchBankBranches(this.searchKeyword.trim()).then((items) => {
+        this.branches = items || []
+        this.loading = false
+      }).catch(() => {
+        this.loading = false
+        this.loadError = true
+      })
+    },
+    selectBranch(branch) {
+      saveSelectedBranch({
+        branchName: branch.branchName,
+        bankName: branch.bankName,
+        branchCode: branch.branchCode
+      })
+      uni.navigateBack()
+    }
+  }
+}
+</script>
+
+<style scoped>
+.page {
+  display: flex;
+  flex-direction: column;
+  min-height: 100vh;
+  background: #f6f8fb;
+  padding: 0;
+  padding-bottom: env(safe-area-inset-bottom);
+  animation: pageSlideIn 260ms cubic-bezier(0.22, 1, 0.36, 1) both;
+}
+
+.page.leaving {
+  animation: pageSlideOut 280ms cubic-bezier(0.4, 0, 1, 1) both;
+}
+
+@keyframes pageSlideIn {
+  from { opacity: 0.35; transform: translateX(72rpx); }
+  to { opacity: 1; transform: translateX(0); }
+}
+
+@keyframes pageSlideOut {
+  from { opacity: 1; transform: translateX(0); }
+  to { opacity: 0.2; transform: translateX(260rpx); }
+}
+
+.search-section {
+  padding: 28rpx 28rpx 24rpx;
+  flex: none;
+  box-sizing: border-box;
+}
+
+.search-box {
+  display: flex;
+  height: 88rpx;
+  padding: 0 24rpx;
+  flex-direction: row;
+  align-items: center;
+  border: 1rpx solid #cfd5dd;
+  border-radius: 12rpx;
+  background: #ffffff;
+  box-sizing: border-box;
+}
+
+.search-input {
+  flex: 1;
+  height: 86rpx;
+  color: #1d2939;
+  font-size: 27rpx;
+}
+
+.search-placeholder {
+  color: #a8b1be;
+  font-size: 27rpx;
+}
+
+.search-icon {
+  width: 38rpx;
+  height: 38rpx;
+  margin-left: 18rpx;
+}
+
+.city-tip {
+  display: block;
+  margin: 18rpx 8rpx 0;
+  color: #667085;
+  font-size: 23rpx;
+}
+
+.branch-scroll {
+  flex: 1;
+  min-height: 0;
+  padding: 0 28rpx;
+  box-sizing: border-box;
+}
+
+.branch-card {
+  overflow: hidden;
+  border: 1rpx solid #edf0f4;
+  border-radius: 12rpx;
+  background: #ffffff;
+}
+
+.branch-item {
+  display: flex;
+  min-height: 104rpx;
+  padding: 0 26rpx;
+  flex-direction: row;
+  align-items: center;
+  position: relative;
+  box-sizing: border-box;
+}
+
+.branch-name {
+  flex: 1;
+  padding: 26rpx 0;
+  color: #1d2939;
+  font-size: 27rpx;
+}
+
+.branch-arrow {
+  width: 30rpx;
+  height: 30rpx;
+  margin-left: 18rpx;
+  opacity: 0.58;
+}
+
+.branch-divider {
+  height: 1rpx;
+  position: absolute;
+  left: 26rpx;
+  right: 26rpx;
+  bottom: 0;
+  background: #edf0f4;
+}
+
+.empty-state {
+  display: flex;
+  padding: 110rpx 30rpx;
+  flex-direction: column;
+  align-items: center;
+}
+
+.empty-title {
+  color: #4b5667;
+  font-size: 28rpx;
+  font-weight: 700;
+}
+
+.empty-tip {
+  margin-top: 12rpx;
+  color: #9aa4b2;
+  font-size: 24rpx;
+}
+
+.safe-space {
+  height: calc(60rpx + env(safe-area-inset-bottom));
+}
+</style>
diff --git a/mingdaojingjiang_guide_app/pages/bank-card-add/index.vue b/mingdaojingjiang_guide_app/pages/bank-card-add/index.vue
new file mode 100644
index 0000000..8131572
--- /dev/null
+++ b/mingdaojingjiang_guide_app/pages/bank-card-add/index.vue
@@ -0,0 +1,426 @@
+<template>
+  <view class="page" :class="{ leaving: isLeaving }">
+    <guide-nav-bar title="填写银行卡" @back="goBack" />
+
+    <scroll-view class="form-scroll" scroll-y>
+      <view class="form-card">
+        <view class="form-row" @tap="showCardTypeTip">
+          <text class="field-label">卡类型</text>
+          <view class="field-control readonly-control">
+            <text class="field-value">本人</text>
+            <image class="down-icon" src="/static/icons/chevron-down.svg" mode="aspectFit" />
+          </view>
+        </view>
+
+        <view class="row-divider"></view>
+
+        <view class="field-block">
+          <view class="form-row">
+            <text class="field-label">卡号</text>
+            <view class="field-control input-control" :class="{ 'field-invalid': cardNumberError }">
+              <input
+                class="card-input"
+                type="number"
+                maxlength="19"
+                v-model="cardNumber"
+                placeholder="请输入银行卡号"
+                placeholder-class="input-placeholder"
+                @input="cardNumberError = ''"
+              />
+            </view>
+          </view>
+          <text v-if="cardNumberError" class="field-error">{{ cardNumberError }}</text>
+        </view>
+
+        <view class="row-divider"></view>
+
+        <view class="field-block">
+          <view class="form-row">
+            <text class="field-label">开户省份</text>
+            <picker class="region-picker" mode="selector" :range="areaProvinceNames" @change="onProvinceChange">
+              <view class="field-control" :class="{ 'field-invalid': cityError && !selectedProvince }">
+                <text class="field-value" :class="{ 'field-placeholder': !selectedProvince }">{{ selectedProvince || '选择开户省份' }}</text>
+                <image class="down-icon" src="/static/icons/chevron-down.svg" mode="aspectFit" />
+              </view>
+            </picker>
+          </view>
+        </view>
+
+        <view class="row-divider"></view>
+
+        <view class="field-block">
+          <view class="form-row" @tap="handleCityRowTap">
+            <text class="field-label">开户城市</text>
+            <picker class="region-picker" mode="selector" :range="currentCityNames" :disabled="!selectedProvince" @change="onCityChange">
+              <view class="field-control" :class="{ 'field-invalid': cityError && selectedProvince }">
+                <text class="field-value" :class="{ 'field-placeholder': !selectedCity }">{{ selectedCity || '选择开户城市' }}</text>
+                <image class="down-icon" src="/static/icons/chevron-down.svg" mode="aspectFit" />
+              </view>
+            </picker>
+          </view>
+          <text v-if="cityError" class="field-error">{{ cityError }}</text>
+        </view>
+
+        <view class="row-divider"></view>
+
+        <view class="field-block">
+          <view class="form-row" @tap="chooseBranch">
+            <text class="field-label">开户行</text>
+            <view class="field-control" :class="{ 'field-invalid': branchError }">
+              <text class="field-value branch-value" :class="{ 'field-placeholder': !selectedBranch }">{{ selectedBranch || '选择开户行' }}</text>
+              <image class="down-icon" src="/static/icons/chevron-down.svg" mode="aspectFit" />
+            </view>
+          </view>
+          <text v-if="branchError" class="field-error">{{ branchError }}</text>
+        </view>
+      </view>
+
+      <view class="security-tip">
+        <text>银行卡仅用于导游提现,请填写本人名下银行卡</text>
+      </view>
+      <view class="submit-button" :class="{ disabled: submitting }" @tap="submitBankCard">
+        <text>{{ submitting ? '提交中' : '添加银行卡' }}</text>
+      </view>
+      <view class="safe-space"></view>
+    </scroll-view>
+
+    <guide-dialog
+      :visible="confirmVisible"
+      title="确认添加银行卡"
+      :content="confirmContent"
+      confirm-text="确认添加"
+      @cancel="confirmVisible = false"
+      @confirm="confirmAdd"
+    />
+  </view>
+</template>
+
+<script>
+import GuideNavBar from '../../components/guide-nav-bar/guide-nav-bar.vue'
+import GuideDialog from '../../components/guide-dialog/guide-dialog.vue'
+import { areaCityCodes, areaCityNames, areaProvinceCodes, areaProvinceNames } from '../../constants/area-codes'
+import { addBankCard, consumeSelectedBranch } from '../../services/settlement'
+import { backOrHome, routeTo } from '../../utils/router'
+
+export default {
+  components: {
+    GuideNavBar,
+    GuideDialog
+  },
+  data() {
+    return {
+      areaProvinceNames,
+      submitting: false,
+      cardNumber: '',
+      selectedProvince: '',
+      selectedProvinceCode: '',
+      selectedCity: '',
+      selectedCityCode: '',
+      currentCityNames: [],
+      currentCityCodes: [],
+      selectedBranch: '',
+      selectedBankName: '',
+      selectedBranchCode: '',
+      cardNumberError: '',
+      cityError: '',
+      branchError: '',
+      confirmVisible: false,
+      isLeaving: false
+    }
+  },
+  computed: {
+    confirmContent() {
+      const numberText = this.cardNumber.trim()
+      return this.selectedBranch + '\n尾号 ' + numberText.slice(-4)
+    }
+  },
+  onShow() {
+    const branch = consumeSelectedBranch()
+    if (branch) {
+      this.selectedBranch = branch.branchName || ''
+      this.selectedBankName = branch.bankName || ''
+      this.selectedBranchCode = branch.branchCode || ''
+      this.branchError = ''
+    }
+  },
+  methods: {
+    goBack() {
+      if (this.isLeaving) {
+        return
+      }
+      this.isLeaving = true
+      setTimeout(() => {
+        backOrHome('/pages/bank-card-select/index')
+      }, 280)
+    },
+    showCardTypeTip() {
+      uni.showToast({ title: '当前仅支持本人银行卡', icon: 'none' })
+    },
+    onProvinceChange(event) {
+      const provinceIndex = parseInt(event.detail.value, 10)
+      this.selectedProvince = areaProvinceNames[provinceIndex]
+      this.selectedProvinceCode = areaProvinceCodes[provinceIndex]
+      this.currentCityNames = areaCityNames[provinceIndex] || []
+      this.currentCityCodes = areaCityCodes[provinceIndex] || []
+      this.selectedCity = ''
+      this.selectedCityCode = ''
+      this.selectedBranch = ''
+      this.selectedBranchCode = ''
+      this.cityError = ''
+      this.branchError = ''
+    },
+    onCityChange(event) {
+      const cityIndex = parseInt(event.detail.value, 10)
+      this.selectedCity = this.currentCityNames[cityIndex]
+      this.selectedCityCode = this.currentCityCodes[cityIndex]
+      this.selectedBranch = ''
+      this.selectedBranchCode = ''
+      this.cityError = ''
+      this.branchError = ''
+    },
+    handleCityRowTap() {
+      if (!this.selectedProvince) {
+        uni.showToast({ title: '请先选择开户省份', icon: 'none' })
+      }
+    },
+    chooseBranch() {
+      this.branchError = ''
+      if (!this.selectedCity) {
+        this.cityError = '请先选择开户城市'
+        uni.showToast({ title: '请先选择开户城市', icon: 'none' })
+        return
+      }
+      routeTo(
+        '/pages/bank-branch-select/index?city=' +
+          encodeURIComponent(this.selectedProvince + ' ' + this.selectedCity) +
+          '&cityCode=' +
+          encodeURIComponent(this.selectedCityCode)
+      )
+    },
+    submitBankCard() {
+      if (this.submitting) {
+        return
+      }
+      this.cardNumberError = ''
+      this.cityError = ''
+      this.branchError = ''
+      let hasError = false
+      const numberText = this.cardNumber.trim()
+      if (!numberText) {
+        this.cardNumberError = '请输入银行卡号'
+        hasError = true
+      } else if (!(/^\d{16,19}$/).test(numberText)) {
+        this.cardNumberError = '请输入 16 至 19 位银行卡号'
+        hasError = true
+      }
+      if (!this.selectedCity) {
+        this.cityError = '请选择开户城市'
+        hasError = true
+      }
+      if (!this.selectedProvinceCode || !this.selectedCityCode) {
+        this.cityError = '请选择有效的省份和城市'
+        hasError = true
+      }
+      if (!this.selectedBranch || !this.selectedBranchCode) {
+        this.branchError = '请选择开户行'
+        hasError = true
+      }
+      if (hasError) {
+        return
+      }
+      this.confirmVisible = true
+    },
+    confirmAdd() {
+      if (this.submitting) {
+        return
+      }
+      this.submitting = true
+      addBankCard({
+        cardNumber: this.cardNumber.trim(),
+        cityCode: this.selectedCityCode,
+        bankName: this.selectedBankName,
+        branchName: this.selectedBranch,
+        branchCode: this.selectedBranchCode
+      }).then(() => {
+        this.submitting = false
+        uni.showToast({ title: '已提交审核', icon: 'success' })
+        setTimeout(() => {
+          uni.navigateBack()
+        }, 500)
+      }).catch((error) => {
+        this.submitting = false
+        uni.showToast({ title: (error && error.message) || '提交失败', icon: 'none' })
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.page {
+  display: flex;
+  flex-direction: column;
+  min-height: 100vh;
+  background: #f6f8fb;
+  padding: 0;
+  padding-bottom: env(safe-area-inset-bottom);
+  animation: pageSlideIn 260ms cubic-bezier(0.22, 1, 0.36, 1) both;
+}
+
+.page.leaving {
+  animation: pageSlideOut 280ms cubic-bezier(0.4, 0, 1, 1) both;
+}
+
+@keyframes pageSlideIn {
+  from { opacity: 0.35; transform: translateX(72rpx); }
+  to { opacity: 1; transform: translateX(0); }
+}
+
+@keyframes pageSlideOut {
+  from { opacity: 1; transform: translateX(0); }
+  to { opacity: 0.2; transform: translateX(260rpx); }
+}
+
+.form-scroll {
+  flex: 1;
+  min-height: 0;
+  padding: 28rpx 28rpx 0;
+  box-sizing: border-box;
+}
+
+.form-card {
+  padding: 8rpx 30rpx;
+  border: 1rpx solid #edf0f4;
+  border-radius: 12rpx;
+  background: #ffffff;
+  box-sizing: border-box;
+}
+
+.form-row {
+  display: flex;
+  min-height: 112rpx;
+  flex-direction: row;
+  align-items: center;
+}
+
+.field-block {
+  display: flex;
+  flex-direction: column;
+}
+
+.field-label {
+  width: 150rpx;
+  flex: none;
+  color: #1f3b5b;
+  font-size: 28rpx;
+  font-weight: 700;
+}
+
+.field-control {
+  display: flex;
+  min-height: 76rpx;
+  padding: 0 22rpx;
+  flex: 1;
+  flex-direction: row;
+  align-items: center;
+  border: 1rpx solid #cfd5dd;
+  border-radius: 12rpx;
+  background: #ffffff;
+  box-sizing: border-box;
+}
+
+.region-picker {
+  flex: 1;
+}
+
+.readonly-control {
+  background: #f0f2f5;
+  border-color: #f0f2f5;
+}
+
+.input-control {
+  padding-right: 14rpx;
+}
+
+.field-value {
+  flex: 1;
+  color: #1d2939;
+  font-size: 27rpx;
+}
+
+.branch-value {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.field-placeholder {
+  color: #a8b1be;
+}
+
+.card-input {
+  flex: 1;
+  height: 74rpx;
+  color: #1d2939;
+  font-size: 27rpx;
+}
+
+.input-placeholder {
+  color: #a8b1be;
+  font-size: 27rpx;
+}
+
+.down-icon {
+  width: 30rpx;
+  height: 30rpx;
+  margin-left: 12rpx;
+  flex: none;
+}
+
+.row-divider {
+  height: 1rpx;
+  margin-left: 150rpx;
+  background: #eef1f5;
+}
+
+.field-invalid {
+  border-color: #f0746d;
+  background: #fff9f8;
+}
+
+.field-error {
+  margin: -8rpx 0 14rpx 150rpx;
+  color: #e95b55;
+  font-size: 23rpx;
+}
+
+.security-tip {
+  display: flex;
+  margin: 24rpx 16rpx 0;
+  color: #8e98a7;
+  font-size: 23rpx;
+  line-height: 34rpx;
+}
+
+.submit-button {
+  display: flex;
+  width: 100%;
+  height: 88rpx;
+  margin-top: 58rpx;
+  justify-content: center;
+  align-items: center;
+  border-radius: 12rpx;
+  color: #ffffff;
+  font-size: 29rpx;
+  font-weight: 700;
+  background: #4d8fe8;
+}
+
+.submit-button.disabled {
+  opacity: 0.58;
+}
+
+.safe-space {
+  height: calc(70rpx + env(safe-area-inset-bottom));
+}
+</style>
diff --git a/mingdaojingjiang_guide_app/pages/bank-card-select/index.vue b/mingdaojingjiang_guide_app/pages/bank-card-select/index.vue
new file mode 100644
index 0000000..e22611a
--- /dev/null
+++ b/mingdaojingjiang_guide_app/pages/bank-card-select/index.vue
@@ -0,0 +1,186 @@
+<template>
+  <view class="page" :class="{ leaving: isLeaving }">
+    <guide-nav-bar title="选择银行卡" @back="goBack" />
+
+    <view class="add" @tap="addCard">添加银行卡</view>
+    <scroll-view class="scroll" scroll-y>
+      <view v-if="loading" class="state">正在加载银行卡</view>
+      <view v-else-if="!cards.length" class="state">暂无银行卡</view>
+      <view v-for="card in cards" :key="card.id" class="card" @tap="selectCard(card)">
+        <view class="row">
+          <text class="name">{{ card.bankName }}</text>
+          <text :class="'status ' + card.status">{{ statusText(card.status) }}</text>
+        </view>
+        <text class="number">{{ card.cardMasked }}</text>
+        <text class="branch">{{ card.branchName }}</text>
+        <text v-if="card.reason" class="reason">{{ card.reason }}</text>
+      </view>
+    </scroll-view>
+  </view>
+</template>
+
+<script>
+import GuideNavBar from '../../components/guide-nav-bar/guide-nav-bar.vue'
+import { getBankCards, saveSelectedBankId } from '../../services/settlement'
+import { backOrHome, routeTo } from '../../utils/router'
+
+export default {
+  components: {
+    GuideNavBar
+  },
+  data() {
+    return {
+      loading: false,
+      cards: [],
+      isLeaving: false
+    }
+  },
+  onShow() {
+    this.load()
+  },
+  methods: {
+    goBack() {
+      if (this.isLeaving) {
+        return
+      }
+      this.isLeaving = true
+      setTimeout(() => {
+        backOrHome('/pages/withdrawal-detail/index')
+      }, 280)
+    },
+    addCard() {
+      routeTo('/pages/bank-card-add/index')
+    },
+    statusText(value) {
+      if (value === 'approved') return '已通过'
+      if (value === 'rejected') return '未通过'
+      return '审核中'
+    },
+    selectCard(card) {
+      if (card.status !== 'approved') {
+        uni.showToast({
+          title: card.reason || '该银行卡尚未审核通过',
+          icon: 'none'
+        })
+        return
+      }
+      saveSelectedBankId(card.id)
+      uni.navigateBack()
+    },
+    load() {
+      this.loading = true
+      getBankCards().then((data) => {
+        this.cards = data || []
+      }).catch((error) => {
+        uni.showToast({ title: (error && error.message) || '银行卡加载失败', icon: 'none' })
+      }).finally(() => {
+        this.loading = false
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.page {
+  display: flex;
+  flex-direction: column;
+  min-height: 100vh;
+  background: #f6f8fb;
+  padding: 0;
+  padding-bottom: env(safe-area-inset-bottom);
+  animation: pageSlideIn 260ms cubic-bezier(0.22, 1, 0.36, 1) both;
+}
+
+.page.leaving {
+  animation: pageSlideOut 280ms cubic-bezier(0.4, 0, 1, 1) both;
+}
+
+@keyframes pageSlideIn {
+  from { opacity: 0.35; transform: translateX(72rpx); }
+  to { opacity: 1; transform: translateX(0); }
+}
+
+@keyframes pageSlideOut {
+  from { opacity: 1; transform: translateX(0); }
+  to { opacity: 0.2; transform: translateX(260rpx); }
+}
+
+.add {
+  height: 88rpx;
+  margin: 28rpx 28rpx 24rpx;
+  border-radius: 12rpx;
+  background: #4d8fe8;
+  color: #ffffff;
+  text-align: center;
+  line-height: 88rpx;
+  font-weight: 700;
+}
+
+.scroll {
+  flex: 1;
+  min-height: 0;
+  padding: 0 28rpx;
+  box-sizing: border-box;
+}
+
+.card {
+  padding: 28rpx;
+  margin-bottom: 16rpx;
+  border: 1rpx solid #edf0f4;
+  background: #ffffff;
+  border-radius: 12rpx;
+}
+
+.row {
+  display: flex;
+  justify-content: space-between;
+}
+
+.name {
+  font-size: 30rpx;
+  font-weight: 700;
+  color: #1f3b5b;
+}
+
+.number {
+  display: block;
+  margin-top: 20rpx;
+  font-size: 28rpx;
+  color: #344054;
+}
+
+.branch,
+.reason {
+  display: block;
+  margin-top: 10rpx;
+  color: #667085;
+  font-size: 23rpx;
+}
+
+.reason {
+  color: #e95b5b;
+}
+
+.status {
+  font-size: 23rpx;
+}
+
+.approved {
+  color: #249765;
+}
+
+.pending {
+  color: #e58b28;
+}
+
+.rejected {
+  color: #e95b5b;
+}
+
+.state {
+  padding: 150rpx 0;
+  text-align: center;
+  color: #8993a1;
+}
+</style>
diff --git a/mingdaojingjiang_guide_app/pages/earnings/index.vue b/mingdaojingjiang_guide_app/pages/earnings/index.vue
index 4154364..4706b5e 100644
--- a/mingdaojingjiang_guide_app/pages/earnings/index.vue
+++ b/mingdaojingjiang_guide_app/pages/earnings/index.vue
@@ -1,6 +1,6 @@
 <template>
   <view class="performance-page" :class="{ leaving: isLeaving }">
-    <guide-nav-bar title="业绩明细" @back="goBack" />
+    <guide-nav-bar title="业绩明细" action-text="提现" @back="goBack" @action="goWithdraw" />
 
     <view class="summary-area">
       <view class="income-card">
@@ -50,7 +50,7 @@
 <script>
 import GuideNavBar from '../../components/guide-nav-bar/guide-nav-bar.vue'
 import { getEarnings } from '../../services/order'
-import { backOrHome } from '../../utils/router'
+import { backOrHome, routeTo } from '../../utils/router'
 
 export default {
   components: {
@@ -104,6 +104,9 @@
         backOrHome()
       }, 280)
     },
+    goWithdraw() {
+      routeTo('/pages/withdrawal/index')
+    },
     loadData() {
       if (this.loading) {
         return
diff --git a/mingdaojingjiang_guide_app/pages/onboarding-pending/index.vue b/mingdaojingjiang_guide_app/pages/onboarding-pending/index.vue
new file mode 100644
index 0000000..6139bb8
--- /dev/null
+++ b/mingdaojingjiang_guide_app/pages/onboarding-pending/index.vue
@@ -0,0 +1,223 @@
+<template>
+  <view class="page" :class="{ leaving: isLeaving }">
+    <guide-nav-bar title="导游进件" @back="goBack" />
+
+    <view class="result-content">
+      <view class="status-icon-wrap" :class="{ rejected: isRejected, approved: status === 'approved' }">
+        <image class="status-icon" :src="statusIcon" mode="aspectFit" />
+      </view>
+      <text class="result-title">{{ resultTitle }}</text>
+      <text v-if="isRejected && reason" class="result-reason">{{ reason }}</text>
+      <text v-else class="result-tip">{{ resultTip }}</text>
+
+      <view v-if="isRejected" class="return-button" @tap="resubmit">
+        <text>重新提交资料</text>
+      </view>
+      <view v-else-if="status === 'approved'" class="return-button" @tap="goWithdraw">
+        <text>去提现</text>
+      </view>
+      <view v-else class="return-button ghost" :class="{ disabled: refreshing }" @tap="refreshStatus">
+        <text>{{ refreshing ? '刷新中' : '刷新审核状态' }}</text>
+      </view>
+      <view class="return-button" :class="{ secondary: isRejected || status === 'approved' }" @tap="goBack">
+        <text>返回提现</text>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import GuideNavBar from '../../components/guide-nav-bar/guide-nav-bar.vue'
+import { getOnboardingStatus, refreshOnboardingStatus } from '../../services/settlement'
+import { backOrHome, routeTo } from '../../utils/router'
+
+export default {
+  components: {
+    GuideNavBar
+  },
+  data() {
+    return {
+      status: 'pending',
+      reason: '',
+      refreshing: false,
+      isLeaving: false
+    }
+  },
+  computed: {
+    isRejected() {
+      return this.status === 'rejected'
+    },
+    resultTitle() {
+      if (this.isRejected) return '进件审核未通过'
+      if (this.status === 'approved') return '进件审核已通过'
+      return '进件信息审核中'
+    },
+    resultTip() {
+      if (this.status === 'approved') return '审核已通过,可以继续添加银行卡和提现'
+      return '审核通过后才能继续添加银行卡和提现'
+    },
+    statusIcon() {
+      if (this.isRejected) return '/static/icons/message-warning.svg'
+      if (this.status === 'approved') return '/static/icons/shield-check.svg'
+      return '/static/icons/clock-3.svg'
+    }
+  },
+  onShow() {
+    this.loadStatus()
+  },
+  methods: {
+    goBack() {
+      if (this.isLeaving) {
+        return
+      }
+      this.isLeaving = true
+      setTimeout(() => {
+        backOrHome('/pages/withdrawal/index')
+      }, 280)
+    },
+    loadStatus() {
+      getOnboardingStatus().then((item) => {
+        this.status = (item && item.status) || 'pending'
+        this.reason = (item && item.reason) || ''
+      })
+    },
+    refreshStatus() {
+      if (this.refreshing || this.isRejected) {
+        return
+      }
+      this.refreshing = true
+      refreshOnboardingStatus().then((item) => {
+        this.refreshing = false
+        this.status = (item && item.status) || this.status
+        this.reason = (item && item.reason) || ''
+        if (this.status === 'approved') {
+          uni.showToast({ title: '审核已通过', icon: 'success' })
+        } else {
+          uni.showToast({ title: '仍在审核中', icon: 'none' })
+        }
+      }).catch(() => {
+        this.refreshing = false
+        uni.showToast({ title: '刷新失败,请重试', icon: 'none' })
+      })
+    },
+    resubmit() {
+      routeTo('/pages/onboarding/index')
+    },
+    goWithdraw() {
+      routeTo('/pages/withdrawal-detail/index')
+    }
+  }
+}
+</script>
+
+<style scoped>
+.page {
+  display: flex;
+  flex-direction: column;
+  min-height: 100vh;
+  background: #f6f8fb;
+  padding: 0;
+  padding-bottom: env(safe-area-inset-bottom);
+  animation: pageSlideIn 260ms cubic-bezier(0.22, 1, 0.36, 1) both;
+}
+
+.page.leaving {
+  animation: pageSlideOut 280ms cubic-bezier(0.4, 0, 1, 1) both;
+}
+
+@keyframes pageSlideIn {
+  from { opacity: 0.35; transform: translateX(72rpx); }
+  to { opacity: 1; transform: translateX(0); }
+}
+
+@keyframes pageSlideOut {
+  from { opacity: 1; transform: translateX(0); }
+  to { opacity: 0.2; transform: translateX(260rpx); }
+}
+
+.result-content {
+  display: flex;
+  padding: 76rpx 48rpx 0;
+  flex: 1;
+  flex-direction: column;
+  align-items: center;
+  box-sizing: border-box;
+}
+
+.status-icon-wrap {
+  display: flex;
+  width: 176rpx;
+  height: 176rpx;
+  justify-content: center;
+  align-items: center;
+  border-radius: 88rpx;
+  background: #eef6ff;
+}
+
+.status-icon-wrap.rejected {
+  background: #fff1f2;
+}
+
+.status-icon-wrap.approved {
+  background: #eafaf0;
+}
+
+.status-icon {
+  width: 82rpx;
+  height: 82rpx;
+}
+
+.result-title {
+  margin-top: 40rpx;
+  color: #1f3b5b;
+  font-size: 34rpx;
+  font-weight: 700;
+}
+
+.result-tip,
+.result-reason {
+  margin-top: 16rpx;
+  color: #667085;
+  font-size: 25rpx;
+  text-align: center;
+  line-height: 1.5;
+}
+
+.result-reason {
+  color: #e95b5b;
+}
+
+.return-button {
+  display: flex;
+  width: 100%;
+  height: 88rpx;
+  margin-top: 28rpx;
+  justify-content: center;
+  align-items: center;
+  border-radius: 12rpx;
+  color: #ffffff;
+  font-size: 29rpx;
+  font-weight: 700;
+  background: #4d8fe8;
+}
+
+.return-button.ghost {
+  margin-top: 70rpx;
+  color: #4d8fe8;
+  background: #ffffff;
+  box-shadow: none;
+  border: 1rpx solid #4d8fe8;
+}
+
+.return-button.secondary {
+  margin-top: 24rpx;
+  color: #4d8fe8;
+  background: #ffffff;
+  box-shadow: none;
+  border: 1rpx solid #4d8fe8;
+}
+
+.return-button.disabled {
+  opacity: 0.58;
+}
+</style>
diff --git a/mingdaojingjiang_guide_app/pages/onboarding/index.vue b/mingdaojingjiang_guide_app/pages/onboarding/index.vue
new file mode 100644
index 0000000..0b71d6b
--- /dev/null
+++ b/mingdaojingjiang_guide_app/pages/onboarding/index.vue
@@ -0,0 +1,687 @@
+<template>
+  <view class="page" :class="{ leaving: isLeaving }">
+    <guide-nav-bar title="导游进件" @back="goBack" />
+
+    <scroll-view class="form-scroll" scroll-y>
+      <view class="form-card subject-card">
+        <view class="merchant-type-row" @tap="chooseMerchantType">
+          <view class="type-copy">
+            <text class="field-label inline-label">进件类型</text>
+          </view>
+          <view class="type-control">
+            <text>{{ merchantType === 'enterprise' ? '企业' : '个人' }}</text>
+            <image class="down-icon" src="/static/icons/chevron-down.svg" mode="aspectFit" />
+          </view>
+        </view>
+      </view>
+
+      <view v-if="merchantType === 'enterprise'" class="form-card">
+        <text class="section-title">企业资料</text>
+
+        <view class="field-block">
+          <text class="field-label">统一社会信用代码</text>
+          <view class="input-shell" :class="{ 'field-invalid': invalidField === 'creditCode' }">
+            <input
+              class="text-input"
+              v-model="creditCode"
+              maxlength="18"
+              placeholder="请输入18位统一社会信用代码"
+              placeholder-class="input-placeholder"
+              @input="normalizeCreditCode"
+            />
+          </view>
+          <text v-if="invalidField === 'creditCode'" class="field-error">{{ errorMessage }}</text>
+        </view>
+
+        <view class="field-block">
+          <text class="field-label">营业执照有效期</text>
+          <view class="select-shell" :class="{ 'field-invalid': invalidField === 'businessValidity' }" @tap="chooseBusinessValidity">
+            <text>{{ businessValidity }}</text>
+            <image class="down-icon" src="/static/icons/chevron-down.svg" mode="aspectFit" />
+          </view>
+          <text v-if="invalidField === 'businessValidity'" class="field-error">{{ errorMessage }}</text>
+        </view>
+
+        <view class="field-block">
+          <text class="field-label">证照有效期起始日期</text>
+          <picker class="date-picker" mode="date" :value="businessStartDate" start="1990-01-01" end="2099-12-31" @change="onBusinessStartChange">
+            <view class="select-shell" :class="{ 'field-invalid': invalidField === 'businessStartDate' }">
+              <text :class="{ 'select-placeholder': !businessStartDate }">{{ businessStartDate || '请选择' }}</text>
+              <image class="down-icon" src="/static/icons/chevron-down.svg" mode="aspectFit" />
+            </view>
+          </picker>
+          <text v-if="invalidField === 'businessStartDate'" class="field-error">{{ errorMessage }}</text>
+        </view>
+
+        <view v-if="businessValidity === '固定期限'" class="field-block">
+          <text class="field-label">证照有效期结束日期</text>
+          <picker class="date-picker" mode="date" :value="businessEndDate" start="1990-01-01" end="2099-12-31" @change="onBusinessEndChange">
+            <view class="select-shell" :class="{ 'field-invalid': invalidField === 'businessEndDate' }">
+              <text :class="{ 'select-placeholder': !businessEndDate }">{{ businessEndDate || '请选择' }}</text>
+              <image class="down-icon" src="/static/icons/chevron-down.svg" mode="aspectFit" />
+            </view>
+          </picker>
+          <text v-if="invalidField === 'businessEndDate'" class="field-error">{{ errorMessage }}</text>
+        </view>
+
+        <view class="field-block">
+          <text class="field-label">注册地址(省、市、区)</text>
+          <picker class="region-picker" mode="region" @change="onRegisteredRegionChange">
+            <view class="select-shell" :class="{ 'field-invalid': invalidField === 'registeredRegion' }">
+              <text class="ellipsis-text" :class="{ 'select-placeholder': !registeredRegion }">{{ registeredRegion || '请选择' }}</text>
+              <image class="down-icon" src="/static/icons/chevron-down.svg" mode="aspectFit" />
+            </view>
+          </picker>
+          <text v-if="invalidField === 'registeredRegion'" class="field-error">{{ errorMessage }}</text>
+        </view>
+
+        <view class="field-block last-field">
+          <text class="field-label">详细注册地址</text>
+          <view class="input-shell" :class="{ 'field-invalid': invalidField === 'registeredAddress' }">
+            <input
+              class="text-input"
+              v-model="registeredAddress"
+              maxlength="80"
+              placeholder="请输入详细注册地址"
+              placeholder-class="input-placeholder"
+              @input="clearFieldError('registeredAddress')"
+            />
+          </view>
+          <text v-if="invalidField === 'registeredAddress'" class="field-error">{{ errorMessage }}</text>
+        </view>
+      </view>
+
+      <view class="form-card">
+        <text class="section-title">{{ merchantType === 'enterprise' ? '法人信息' : '个人信息' }}</text>
+
+        <view class="field-block">
+          <text class="field-label">{{ merchantType === 'enterprise' ? '法人姓名' : '个人姓名' }}</text>
+          <view class="input-shell" :class="{ 'field-invalid': invalidField === 'subjectName' }">
+            <input
+              class="text-input"
+              v-model="subjectName"
+              maxlength="30"
+              placeholder="请输入姓名"
+              placeholder-class="input-placeholder"
+              @input="clearFieldError('subjectName')"
+            />
+          </view>
+          <text v-if="invalidField === 'subjectName'" class="field-error">{{ errorMessage }}</text>
+        </view>
+
+        <view class="field-block">
+          <text class="field-label">{{ merchantType === 'enterprise' ? '法人证件类型' : '个人证件类型' }}</text>
+          <view class="select-shell readonly-shell">
+            <text>身份证</text>
+            <image class="down-icon" src="/static/icons/chevron-down.svg" mode="aspectFit" />
+          </view>
+        </view>
+
+        <view class="field-block">
+          <text class="field-label">{{ merchantType === 'enterprise' ? '法人证件号码' : '个人证件号码' }}</text>
+          <view class="input-shell" :class="{ 'field-invalid': invalidField === 'subjectIdNumber' }">
+            <input
+              class="text-input"
+              type="idcard"
+              v-model="subjectIdNumber"
+              maxlength="18"
+              placeholder="请输入身份证号码"
+              placeholder-class="input-placeholder"
+              @input="clearFieldError('subjectIdNumber')"
+            />
+          </view>
+          <text v-if="invalidField === 'subjectIdNumber'" class="field-error">{{ errorMessage }}</text>
+        </view>
+
+        <view class="field-block">
+          <text class="field-label">{{ merchantType === 'enterprise' ? '法人证件有效期类型' : '个人证件有效期类型' }}</text>
+          <view class="select-shell" :class="{ 'field-invalid': invalidField === 'subjectValidity' }" @tap="chooseSubjectValidity">
+            <text>{{ subjectValidity }}</text>
+            <image class="down-icon" src="/static/icons/chevron-down.svg" mode="aspectFit" />
+          </view>
+          <text v-if="invalidField === 'subjectValidity'" class="field-error">{{ errorMessage }}</text>
+        </view>
+
+        <view class="field-block">
+          <text class="field-label">{{ merchantType === 'enterprise' ? '法人证件有效期开始日期' : '个人证件有效期开始日期' }}</text>
+          <picker class="date-picker" mode="date" :value="subjectStartDate" start="1990-01-01" end="2099-12-31" @change="onSubjectStartChange">
+            <view class="select-shell" :class="{ 'field-invalid': invalidField === 'subjectStartDate' }">
+              <text :class="{ 'select-placeholder': !subjectStartDate }">{{ subjectStartDate || '请选择' }}</text>
+              <image class="down-icon" src="/static/icons/chevron-down.svg" mode="aspectFit" />
+            </view>
+          </picker>
+          <text v-if="invalidField === 'subjectStartDate'" class="field-error">{{ errorMessage }}</text>
+        </view>
+
+        <view v-if="subjectValidity === '固定期限'" class="field-block">
+          <text class="field-label">{{ merchantType === 'enterprise' ? '法人证件有效期截止日期' : '个人证件有效期截止日期' }}</text>
+          <picker class="date-picker" mode="date" :value="subjectEndDate" start="1990-01-01" end="2099-12-31" @change="onSubjectEndChange">
+            <view class="select-shell" :class="{ 'field-invalid': invalidField === 'subjectEndDate' }">
+              <text :class="{ 'select-placeholder': !subjectEndDate }">{{ subjectEndDate || '请选择' }}</text>
+              <image class="down-icon" src="/static/icons/chevron-down.svg" mode="aspectFit" />
+            </view>
+          </picker>
+          <text v-if="invalidField === 'subjectEndDate'" class="field-error">{{ errorMessage }}</text>
+        </view>
+
+        <view v-if="merchantType === 'individual'" class="field-block last-field">
+          <text class="field-label">手机号</text>
+          <view class="input-shell" :class="{ 'field-invalid': invalidField === 'personalPhone' }">
+            <input
+              class="text-input"
+              type="number"
+              v-model="personalPhone"
+              maxlength="11"
+              placeholder="请输入手机号"
+              placeholder-class="input-placeholder"
+              @input="clearFieldError('personalPhone')"
+            />
+          </view>
+          <text v-if="invalidField === 'personalPhone'" class="field-error">{{ errorMessage }}</text>
+        </view>
+      </view>
+
+      <view v-if="merchantType === 'enterprise'" class="form-card">
+        <text class="section-title">管理员信息</text>
+        <view class="field-block">
+          <text class="field-label">管理员姓名</text>
+          <view class="input-shell" :class="{ 'field-invalid': invalidField === 'administratorName' }">
+            <input
+              class="text-input"
+              v-model="administratorName"
+              maxlength="30"
+              placeholder="请输入管理员姓名"
+              placeholder-class="input-placeholder"
+              @input="clearFieldError('administratorName')"
+            />
+          </view>
+          <text v-if="invalidField === 'administratorName'" class="field-error">{{ errorMessage }}</text>
+        </view>
+        <view class="field-block last-field">
+          <text class="field-label">管理员手机号</text>
+          <view class="input-shell" :class="{ 'field-invalid': invalidField === 'administratorPhone' }">
+            <input
+              class="text-input"
+              type="number"
+              v-model="administratorPhone"
+              maxlength="11"
+              placeholder="请输入管理员手机号"
+              placeholder-class="input-placeholder"
+              @input="clearFieldError('administratorPhone')"
+            />
+          </view>
+          <text v-if="invalidField === 'administratorPhone'" class="field-error">{{ errorMessage }}</text>
+        </view>
+      </view>
+
+      <view class="submit-button" :class="{ disabled: submitting }" @tap="submitOnboarding">
+        <text>{{ submitting ? '提交中' : '提交审核' }}</text>
+      </view>
+      <view class="safe-space"></view>
+    </scroll-view>
+
+    <guide-dialog
+      :visible="confirmVisible"
+      title="确认提交审核"
+      content="提交后资料将进入审核,审核期间暂不能发起提现。"
+      confirm-text="确认提交"
+      @cancel="confirmVisible = false"
+      @confirm="confirmSubmit"
+    />
+  </view>
+</template>
+
+<script>
+import GuideNavBar from '../../components/guide-nav-bar/guide-nav-bar.vue'
+import GuideDialog from '../../components/guide-dialog/guide-dialog.vue'
+import { submitOnboarding } from '../../services/settlement'
+import { backOrHome, routeTo } from '../../utils/router'
+
+export default {
+  components: {
+    GuideNavBar,
+    GuideDialog
+  },
+  data() {
+    return {
+      submitting: false,
+      merchantType: 'enterprise',
+      creditCode: '',
+      businessValidity: '固定期限',
+      businessStartDate: '',
+      businessEndDate: '',
+      registeredRegion: '',
+      registeredProvinceCode: '',
+      registeredCityCode: '',
+      registeredDistrictCode: '',
+      registeredAddress: '',
+      subjectName: '',
+      subjectIdNumber: '',
+      subjectValidity: '固定期限',
+      subjectStartDate: '',
+      subjectEndDate: '',
+      administratorName: '',
+      administratorPhone: '',
+      personalPhone: '',
+      invalidField: '',
+      errorMessage: '',
+      confirmVisible: false,
+      isLeaving: false,
+      merchantTypeOptions: ['企业', '个人'],
+      validityOptions: ['固定期限', '长期']
+    }
+  },
+  methods: {
+    goBack() {
+      if (this.isLeaving) {
+        return
+      }
+      this.isLeaving = true
+      setTimeout(() => {
+        backOrHome('/pages/withdrawal/index')
+      }, 280)
+    },
+    setFieldError(field, message) {
+      this.invalidField = field
+      this.errorMessage = message
+    },
+    clearFieldError(field) {
+      if (this.invalidField === field) {
+        this.invalidField = ''
+        this.errorMessage = ''
+      }
+    },
+    normalizeCreditCode(event) {
+      const value = (event && event.detail && event.detail.value) || this.creditCode
+      this.creditCode = String(value || '').toUpperCase().replace(/[^0-9A-Z]/g, '')
+      this.clearFieldError('creditCode')
+    },
+    chooseMerchantType() {
+      uni.showActionSheet({
+        itemList: this.merchantTypeOptions,
+        success: (result) => {
+          this.merchantType = result.tapIndex === 0 ? 'enterprise' : 'individual'
+          this.invalidField = ''
+          this.errorMessage = ''
+        }
+      })
+    },
+    chooseBusinessValidity() {
+      uni.showActionSheet({
+        itemList: this.validityOptions,
+        success: (result) => {
+          this.businessValidity = this.validityOptions[result.tapIndex]
+          if (this.businessValidity === '长期') {
+            this.businessEndDate = ''
+          }
+          this.clearFieldError('businessValidity')
+        }
+      })
+    },
+    chooseSubjectValidity() {
+      uni.showActionSheet({
+        itemList: this.validityOptions,
+        success: (result) => {
+          this.subjectValidity = this.validityOptions[result.tapIndex]
+          if (this.subjectValidity === '长期') {
+            this.subjectEndDate = ''
+          }
+          this.clearFieldError('subjectValidity')
+        }
+      })
+    },
+    onRegisteredRegionChange(event) {
+      const values = (event.detail && event.detail.value) || []
+      const codes = (event.detail && event.detail.code) || []
+      if (values.length !== 3) {
+        return
+      }
+      this.registeredRegion = values[0] + ' ' + values[1] + ' ' + values[2]
+      this.registeredProvinceCode = codes[0] || ''
+      this.registeredCityCode = codes[1] || ''
+      this.registeredDistrictCode = codes[2] || ''
+      this.clearFieldError('registeredRegion')
+    },
+    onBusinessStartChange(event) {
+      this.businessStartDate = event.detail.value
+      this.clearFieldError('businessStartDate')
+    },
+    onBusinessEndChange(event) {
+      this.businessEndDate = event.detail.value
+      this.clearFieldError('businessEndDate')
+    },
+    onSubjectStartChange(event) {
+      this.subjectStartDate = event.detail.value
+      this.clearFieldError('subjectStartDate')
+    },
+    onSubjectEndChange(event) {
+      this.subjectEndDate = event.detail.value
+      this.clearFieldError('subjectEndDate')
+    },
+    isPhoneValid(phone) {
+      return /^1[3-9]\d{9}$/.test(phone)
+    },
+    validateCommonIdentity() {
+      if (!this.subjectName.trim()) {
+        this.setFieldError('subjectName', this.merchantType === 'enterprise' ? '请输入法人姓名' : '请输入个人姓名')
+        return false
+      }
+      if (!(/^\d{17}[\dXx]$/).test(this.subjectIdNumber.trim())) {
+        this.setFieldError('subjectIdNumber', '请输入正确的18位身份证号码')
+        return false
+      }
+      if (!this.subjectStartDate) {
+        this.setFieldError('subjectStartDate', '请选择证件有效期开始日期')
+        return false
+      }
+      if (this.subjectValidity === '固定期限') {
+        if (!this.subjectEndDate) {
+          this.setFieldError('subjectEndDate', '请选择证件有效期截止日期')
+          return false
+        }
+        if (this.subjectStartDate > this.subjectEndDate) {
+          this.setFieldError('subjectEndDate', '证件截止日期不能早于开始日期')
+          return false
+        }
+      }
+      return true
+    },
+    validateEnterprise() {
+      if (!(/^[0-9A-Z]{18}$/).test(this.creditCode.trim())) {
+        this.setFieldError('creditCode', '请输入正确的18位统一社会信用代码')
+        return false
+      }
+      if (!this.businessStartDate) {
+        this.setFieldError('businessStartDate', '请选择营业执照有效期开始日期')
+        return false
+      }
+      if (this.businessValidity === '固定期限') {
+        if (!this.businessEndDate) {
+          this.setFieldError('businessEndDate', '请选择营业执照有效期结束日期')
+          return false
+        }
+        if (this.businessStartDate > this.businessEndDate) {
+          this.setFieldError('businessEndDate', '证照结束日期不能早于开始日期')
+          return false
+        }
+      }
+      if (!this.registeredRegion) {
+        this.setFieldError('registeredRegion', '请选择注册地址')
+        return false
+      }
+      if (this.registeredAddress.trim().length < 5) {
+        this.setFieldError('registeredAddress', '请输入完整的详细注册地址')
+        return false
+      }
+      if (!this.validateCommonIdentity()) {
+        return false
+      }
+      if (!this.administratorName.trim()) {
+        this.setFieldError('administratorName', '请输入管理员姓名')
+        return false
+      }
+      if (!this.isPhoneValid(this.administratorPhone.trim())) {
+        this.setFieldError('administratorPhone', '请输入正确的管理员手机号')
+        return false
+      }
+      return true
+    },
+    validateIndividual() {
+      if (!this.validateCommonIdentity()) {
+        return false
+      }
+      if (!this.isPhoneValid(this.personalPhone.trim())) {
+        this.setFieldError('personalPhone', '请输入正确的手机号')
+        return false
+      }
+      return true
+    },
+    submitOnboarding() {
+      if (this.submitting) {
+        return
+      }
+      this.invalidField = ''
+      this.errorMessage = ''
+      const valid = this.merchantType === 'enterprise' ? this.validateEnterprise() : this.validateIndividual()
+      if (!valid) {
+        uni.showToast({ title: this.errorMessage, icon: 'none' })
+        return
+      }
+      this.confirmVisible = true
+    },
+    confirmSubmit() {
+      if (this.submitting) {
+        return
+      }
+      this.submitting = true
+      const contactName = this.merchantType === 'enterprise' ? this.administratorName.trim() : this.subjectName.trim()
+      const contactMobile = this.merchantType === 'enterprise' ? this.administratorPhone.trim() : this.personalPhone.trim()
+      submitOnboarding({
+        subjectType: this.merchantType,
+        subjectName: this.subjectName.trim(),
+        creditCode: this.creditCode.trim(),
+        subjectIdNumber: this.subjectIdNumber.trim(),
+        subjectCertificateValidityType: this.subjectValidity === '长期' ? 'long_term' : 'fixed',
+        subjectCertificateBeginDate: this.subjectStartDate,
+        subjectCertificateEndDate: this.subjectEndDate,
+        licenseValidityType: this.businessValidity === '长期' ? 'long_term' : 'fixed',
+        licenseBeginDate: this.businessStartDate,
+        licenseEndDate: this.businessEndDate,
+        registeredRegion: this.registeredRegion.trim(),
+        registeredProvinceCode: this.registeredProvinceCode,
+        registeredCityCode: this.registeredCityCode,
+        registeredDistrictCode: this.registeredDistrictCode,
+        registeredAddress: this.registeredAddress.trim(),
+        contactName,
+        contactMobile
+      }).then(() => {
+        this.submitting = false
+        routeTo('/pages/onboarding-pending/index', 'redirectTo')
+      }).catch((error) => {
+        this.submitting = false
+        uni.showToast({ title: (error && error.message) || '提交失败,请重试', icon: 'none' })
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.page {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  min-height: 0;
+  background: #f6f8fb;
+  padding: 0;
+  padding-bottom: env(safe-area-inset-bottom);
+  overflow: hidden;
+  animation: pageSlideIn 260ms cubic-bezier(0.22, 1, 0.36, 1) both;
+}
+
+.page.leaving {
+  animation: pageSlideOut 280ms cubic-bezier(0.4, 0, 1, 1) both;
+}
+
+@keyframes pageSlideIn {
+  from { opacity: 0.35; transform: translateX(72rpx); }
+  to { opacity: 1; transform: translateX(0); }
+}
+
+@keyframes pageSlideOut {
+  from { opacity: 1; transform: translateX(0); }
+  to { opacity: 0.2; transform: translateX(260rpx); }
+}
+
+.form-scroll {
+  flex: 1;
+  height: 0;
+  min-height: 0;
+  padding: 0 28rpx;
+  box-sizing: border-box;
+}
+
+.form-card {
+  margin-top: 26rpx;
+  padding: 30rpx;
+  border: 1rpx solid #edf0f4;
+  border-radius: 12rpx;
+  background: #ffffff;
+  box-sizing: border-box;
+}
+
+.subject-card {
+  margin-top: 24rpx;
+  padding-top: 20rpx;
+  padding-bottom: 20rpx;
+}
+
+.merchant-type-row {
+  display: flex;
+  min-height: 92rpx;
+  flex-direction: row;
+  align-items: center;
+}
+
+.type-copy {
+  display: flex;
+  flex: 1;
+  flex-direction: column;
+}
+
+.inline-label {
+  margin-bottom: 0;
+}
+
+.type-control {
+  display: flex;
+  width: 190rpx;
+  height: 72rpx;
+  padding: 0 18rpx 0 22rpx;
+  flex-direction: row;
+  align-items: center;
+  justify-content: space-between;
+  border: 1rpx solid #e1e6ed;
+  border-radius: 12rpx;
+  color: #1d2939;
+  font-size: 27rpx;
+  background: #f7f9fc;
+  box-sizing: border-box;
+}
+
+.section-title {
+  display: block;
+  margin-bottom: 28rpx;
+  color: #1f3b5b;
+  font-size: 31rpx;
+  font-weight: 700;
+}
+
+.field-block {
+  display: flex;
+  margin-bottom: 26rpx;
+  flex-direction: column;
+}
+
+.last-field {
+  margin-bottom: 0;
+}
+
+.field-label {
+  display: block;
+  margin-bottom: 13rpx;
+  color: #344054;
+  font-size: 27rpx;
+  font-weight: 600;
+}
+
+.input-shell,
+.select-shell {
+  display: flex;
+  width: 100%;
+  height: 82rpx;
+  padding: 0 22rpx;
+  flex-direction: row;
+  align-items: center;
+  border: 1rpx solid #cfd5dd;
+  border-radius: 12rpx;
+  background: #ffffff;
+  box-sizing: border-box;
+}
+
+.text-input {
+  flex: 1;
+  height: 80rpx;
+  color: #1d2939;
+  font-size: 27rpx;
+}
+
+.input-placeholder,
+.select-placeholder {
+  color: #a8b1be;
+  font-size: 26rpx;
+}
+
+.select-shell {
+  justify-content: space-between;
+  color: #1d2939;
+  font-size: 27rpx;
+}
+
+.readonly-shell {
+  color: #5a6472;
+  background: #f0f2f5;
+  border-color: #f0f2f5;
+}
+
+.date-picker,
+.region-picker {
+  width: 100%;
+}
+
+.down-icon {
+  width: 30rpx;
+  height: 30rpx;
+  margin-left: 14rpx;
+  flex: none;
+}
+
+.ellipsis-text {
+  flex: 1;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.field-invalid {
+  border-color: #f0746d;
+  background: #fff9f8;
+}
+
+.field-error {
+  margin-top: 9rpx;
+  color: #e95b55;
+  font-size: 23rpx;
+}
+
+.submit-button {
+  display: flex;
+  height: 88rpx;
+  margin-top: 34rpx;
+  justify-content: center;
+  align-items: center;
+  border-radius: 12rpx;
+  color: #ffffff;
+  font-size: 29rpx;
+  font-weight: 700;
+  background: #4d8fe8;
+}
+
+.submit-button.disabled {
+  opacity: 0.58;
+}
+
+.safe-space {
+  height: calc(70rpx + env(safe-area-inset-bottom));
+}
+</style>
diff --git a/mingdaojingjiang_guide_app/pages/withdrawal-detail/index.vue b/mingdaojingjiang_guide_app/pages/withdrawal-detail/index.vue
new file mode 100644
index 0000000..8667abe
--- /dev/null
+++ b/mingdaojingjiang_guide_app/pages/withdrawal-detail/index.vue
@@ -0,0 +1,342 @@
+<template>
+  <view class="detail-page" :class="{ leaving: isLeaving }">
+    <guide-nav-bar title="导游提现" @back="goBack" />
+
+    <scroll-view class="form-scroll" scroll-y>
+      <view class="form-card">
+        <view class="available-row">
+          <text class="available-label">当前可提</text>
+          <text class="available-value">¥{{ availableText }}</text>
+        </view>
+        <view class="field-block">
+          <view class="field-row">
+            <text class="field-label">提现金额</text>
+            <view class="amount-shell" :class="{ invalid: error }">
+              <text class="amount-prefix">¥</text>
+              <input
+                class="amount-input"
+                v-model="amount"
+                type="digit"
+                maxlength="10"
+                placeholder="请输入"
+                placeholder-class="input-placeholder"
+                @input="clearError"
+              />
+            </view>
+          </view>
+          <text v-if="error" class="field-error">{{ error }}</text>
+        </view>
+        <view class="field-divider"></view>
+        <view class="field-row bank-row">
+          <text class="field-label">提现银行卡</text>
+          <view class="bank-value">
+            <view v-if="selected" class="selected-bank">
+              <text class="bank-name">{{ selected.bankName }}</text>
+              <text class="card-number">{{ selected.cardMasked }}</text>
+            </view>
+            <text v-else class="bank-placeholder">请选择银行卡</text>
+          </view>
+          <view class="select-bank-button" @tap="chooseBank">
+            <text>{{ selected ? '更换' : '选择' }}</text>
+          </view>
+        </view>
+      </view>
+
+      <view class="submit-button" :class="{ disabled: submitting }" @tap="submit">
+        <text>{{ submitting ? '提交中' : '发起提现' }}</text>
+      </view>
+    </scroll-view>
+
+    <guide-dialog
+      :visible="confirmVisible"
+      title="确认提现"
+      :content="confirmContent"
+      confirm-text="确认提现"
+      @cancel="confirmVisible = false"
+      @confirm="confirmSubmit"
+    />
+  </view>
+</template>
+
+<script>
+import GuideNavBar from '../../components/guide-nav-bar/guide-nav-bar.vue'
+import GuideDialog from '../../components/guide-dialog/guide-dialog.vue'
+import { consumeSelectedBankId, getBankCards } from '../../services/settlement'
+import { createWithdrawal, getWithdrawalSummary } from '../../services/withdrawal'
+import { backOrHome, routeTo } from '../../utils/router'
+
+export default {
+  components: {
+    GuideNavBar,
+    GuideDialog
+  },
+  data() {
+    return {
+      amount: '',
+      availableAmount: 0,
+      cards: [],
+      selectedId: '',
+      error: '',
+      submitting: false,
+      confirmVisible: false,
+      isLeaving: false
+    }
+  },
+  computed: {
+    availableText() {
+      return Number(this.availableAmount || 0).toFixed(2)
+    },
+    selected() {
+      return this.cards.find((item) => item.id === this.selectedId) || null
+    },
+    confirmContent() {
+      const bankName = (this.selected && this.selected.bankName) || '已绑定银行卡'
+      return '确认提现 ¥' + Number(this.amount || 0).toFixed(2) + ' 至' + bankName + '吗?'
+    }
+  },
+  onShow() {
+    this.loadSummary()
+  },
+  methods: {
+    goBack() {
+      if (this.isLeaving) {
+        return
+      }
+      this.isLeaving = true
+      setTimeout(() => {
+        backOrHome('/pages/withdrawal/index')
+      }, 280)
+    },
+    clearError() {
+      this.error = ''
+    },
+    chooseBank() {
+      routeTo('/pages/bank-card-select/index')
+    },
+    loadSummary() {
+      getWithdrawalSummary().then((summary) => {
+        this.availableAmount = Number(summary && summary.availableAmount || 0)
+      }).catch((error) => {
+        this.error = error.message || '可提金额加载失败,请稍后重试'
+      })
+      getBankCards().then((items) => {
+        this.cards = items || []
+      }).catch((error) => {
+        this.error = error.message || '银行卡加载失败,请稍后重试'
+      })
+      const selectedId = consumeSelectedBankId()
+      if (selectedId) {
+        this.selectedId = selectedId
+      }
+    },
+    submit() {
+      if (this.submitting) {
+        return
+      }
+      const value = Number(this.amount)
+      const cents = Math.round(value * 100)
+      const availableCents = Math.round(Number(this.availableAmount || 0) * 100)
+      if (!Number.isFinite(value) || cents <= 0 || cents > availableCents) {
+        this.error = '请输入不超过当前可提金额的提现金额'
+        return
+      }
+      if (!this.selected) {
+        this.error = '请选择审核通过的银行卡'
+        return
+      }
+      this.error = ''
+      this.confirmVisible = true
+    },
+    confirmSubmit() {
+      if (this.submitting) {
+        return
+      }
+      this.submitting = true
+      createWithdrawal(Number(Number(this.amount).toFixed(2)), this.selected).then((created) => {
+        this.submitting = false
+        uni.redirectTo({
+          url: '/pages/withdrawal-success/index?withdrawalNo=' + encodeURIComponent((created && created.withdrawalNo) || '')
+        })
+      }).catch((error) => {
+        this.submitting = false
+        this.error = error.message || '提现提交失败,请重试'
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.detail-page {
+  display: flex;
+  flex-direction: column;
+  min-height: 100vh;
+  background: #f6f8fb;
+  padding-bottom: env(safe-area-inset-bottom);
+  animation: pageSlideIn 260ms cubic-bezier(0.22, 1, 0.36, 1) both;
+}
+
+.detail-page.leaving {
+  animation: pageSlideOut 280ms cubic-bezier(0.4, 0, 1, 1) both;
+}
+
+@keyframes pageSlideIn {
+  from { opacity: 0.35; transform: translateX(72rpx); }
+  to { opacity: 1; transform: translateX(0); }
+}
+
+@keyframes pageSlideOut {
+  from { opacity: 1; transform: translateX(0); }
+  to { opacity: 0.2; transform: translateX(260rpx); }
+}
+
+.form-scroll {
+  flex: 1;
+  min-height: 0;
+  padding: 28rpx 28rpx 40rpx;
+  box-sizing: border-box;
+}
+
+.form-card {
+  padding: 10rpx 28rpx 14rpx;
+  border: 1rpx solid #edf0f4;
+  border-radius: 12rpx;
+  background: #ffffff;
+}
+
+.available-row {
+  display: flex;
+  align-items: center;
+  height: 72rpx;
+}
+
+.available-label {
+  color: #667085;
+  font-size: 25rpx;
+}
+
+.available-value {
+  margin-left: 12rpx;
+  color: #1f3b5b;
+  font-size: 27rpx;
+  font-weight: 700;
+}
+
+.field-row {
+  display: flex;
+  min-height: 104rpx;
+  align-items: center;
+}
+
+.field-label {
+  width: 178rpx;
+  flex: none;
+  color: #344054;
+  font-size: 27rpx;
+}
+
+.amount-shell {
+  display: flex;
+  height: 70rpx;
+  padding: 0 18rpx;
+  flex: 1;
+  align-items: center;
+  border: 1rpx solid #cfd5dd;
+  border-radius: 12rpx;
+  box-sizing: border-box;
+}
+
+.amount-shell.invalid {
+  border-color: #f0746d;
+  background: #fff9f8;
+}
+
+.amount-prefix {
+  color: #8b94a2;
+  font-size: 27rpx;
+}
+
+.amount-input {
+  height: 68rpx;
+  margin-left: 8rpx;
+  flex: 1;
+  color: #1d2939;
+  font-size: 27rpx;
+}
+
+.input-placeholder {
+  color: #b5bdc9;
+  font-size: 25rpx;
+}
+
+.field-error {
+  margin: 0 0 14rpx 178rpx;
+  color: #e95b55;
+  font-size: 22rpx;
+}
+
+.field-divider {
+  height: 1rpx;
+  margin-left: 178rpx;
+  background: #edf0f4;
+}
+
+.bank-value {
+  min-width: 0;
+  flex: 1;
+}
+
+.selected-bank {
+  display: flex;
+  flex-direction: column;
+}
+
+.bank-name {
+  color: #1d2939;
+  font-size: 25rpx;
+}
+
+.card-number {
+  margin-top: 4rpx;
+  color: #8a93a1;
+  font-size: 22rpx;
+}
+
+.bank-placeholder {
+  color: #b5bdc9;
+  font-size: 25rpx;
+}
+
+.select-bank-button {
+  display: flex;
+  width: 84rpx;
+  height: 52rpx;
+  margin-left: 14rpx;
+  flex: none;
+  align-items: center;
+  justify-content: center;
+  border: 1rpx solid #58a5f8;
+  border-radius: 12rpx;
+  color: #318cf0;
+  background: #ffffff;
+  font-size: 23rpx;
+}
+
+.submit-button {
+  display: flex;
+  width: 100%;
+  height: 88rpx;
+  margin-top: 34rpx;
+  align-items: center;
+  justify-content: center;
+  border-radius: 12rpx;
+  color: #ffffff;
+  background: #4d8fe8;
+  font-size: 30rpx;
+  font-weight: 700;
+}
+
+.submit-button.disabled {
+  opacity: 0.58;
+}
+</style>
diff --git a/mingdaojingjiang_guide_app/pages/withdrawal-success/index.vue b/mingdaojingjiang_guide_app/pages/withdrawal-success/index.vue
new file mode 100644
index 0000000..4d27eae
--- /dev/null
+++ b/mingdaojingjiang_guide_app/pages/withdrawal-success/index.vue
@@ -0,0 +1,114 @@
+<template>
+  <view class="success-page">
+    <guide-nav-bar title="导游提现" @back="returnToWithdrawal" />
+
+    <view class="result-content">
+      <view class="success-icon-shell">
+        <image class="success-icon" src="/static/icons/shield-check.svg" mode="aspectFit" />
+      </view>
+      <text class="result-title">提交成功,等待审核</text>
+      <text v-if="withdrawalNo" class="withdrawal-no">提现单号:{{ withdrawalNo }}</text>
+      <text class="result-tip">审核通过后将发起到账处理,请留意提现记录状态。</text>
+      <view class="return-button" @tap="returnToWithdrawal">
+        <text>返回</text>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import GuideNavBar from '../../components/guide-nav-bar/guide-nav-bar.vue'
+import { backOrHome } from '../../utils/router'
+
+export default {
+  components: {
+    GuideNavBar
+  },
+  data() {
+    return {
+      withdrawalNo: ''
+    }
+  },
+  onLoad(options) {
+    this.withdrawalNo = options && options.withdrawalNo ? String(options.withdrawalNo) : ''
+  },
+  methods: {
+    returnToWithdrawal() {
+      uni.navigateBack({
+        fail() {
+          backOrHome('/pages/withdrawal/index')
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.success-page {
+  display: flex;
+  flex-direction: column;
+  min-height: 100vh;
+  background: #f6f8fb;
+  padding-bottom: env(safe-area-inset-bottom);
+}
+
+.result-content {
+  display: flex;
+  flex: 1;
+  padding: 96rpx 48rpx 0;
+  flex-direction: column;
+  align-items: center;
+  box-sizing: border-box;
+}
+
+.success-icon-shell {
+  display: flex;
+  width: 140rpx;
+  height: 140rpx;
+  align-items: center;
+  justify-content: center;
+  border-radius: 70rpx;
+  background: #eef6ff;
+}
+
+.success-icon {
+  width: 72rpx;
+  height: 72rpx;
+}
+
+.result-title {
+  margin-top: 42rpx;
+  color: #1f3b5b;
+  font-size: 30rpx;
+  font-weight: 700;
+}
+
+.withdrawal-no {
+  margin-top: 22rpx;
+  color: #667085;
+  font-size: 25rpx;
+}
+
+.result-tip {
+  margin-top: 14rpx;
+  color: #667085;
+  font-size: 23rpx;
+  text-align: center;
+  line-height: 34rpx;
+}
+
+.return-button {
+  display: flex;
+  width: 100%;
+  height: 88rpx;
+  margin-top: 86rpx;
+  align-items: center;
+  justify-content: center;
+  border-radius: 12rpx;
+  color: #ffffff;
+  background: #4d8fe8;
+  font-size: 29rpx;
+  font-weight: 700;
+}
+</style>
diff --git a/mingdaojingjiang_guide_app/pages/withdrawal/index.vue b/mingdaojingjiang_guide_app/pages/withdrawal/index.vue
new file mode 100644
index 0000000..39c405f
--- /dev/null
+++ b/mingdaojingjiang_guide_app/pages/withdrawal/index.vue
@@ -0,0 +1,267 @@
+<template>
+  <view class="withdrawal-page" :class="{ leaving: isLeaving }">
+    <guide-nav-bar title="导游提现" @back="goBack" />
+
+    <view class="balance-card">
+      <text class="balance-label">当前可提</text>
+      <text class="balance-amount">¥{{ availableText }}</text>
+      <view class="withdraw-button" @tap="startWithdrawal">提现</view>
+    </view>
+
+    <view class="heading">
+      <text>提现记录</text>
+      <text>共 {{ records.length }} 条</text>
+    </view>
+
+    <scroll-view class="record-list" scroll-y>
+      <view v-if="loading" class="state-text">正在加载</view>
+      <view v-else-if="!records.length" class="state-text">暂无提现记录</view>
+      <view v-for="item in records" :key="item.id" class="record">
+        <view class="record-top">
+          <text>提现</text>
+          <text class="record-money">¥{{ formatMoney(item.amount) }}</text>
+        </view>
+        <view class="record-bottom">
+          <text>{{ displayTime(item) }} {{ item.cardMasked || '' }}</text>
+          <text :class="'status ' + statusClass(item.status)">{{ statusText(item.status) }}</text>
+        </view>
+        <text v-if="item.reason" class="reason">{{ item.reason }}</text>
+      </view>
+    </scroll-view>
+  </view>
+</template>
+
+<script>
+import GuideNavBar from '../../components/guide-nav-bar/guide-nav-bar.vue'
+import { getOnboardingStatus } from '../../services/settlement'
+import { getWithdrawals, getWithdrawalSummary } from '../../services/withdrawal'
+import { backOrHome, routeTo } from '../../utils/router'
+
+export default {
+  components: {
+    GuideNavBar
+  },
+  data() {
+    return {
+      loading: false,
+      availableAmount: 0,
+      records: [],
+      isLeaving: false
+    }
+  },
+  computed: {
+    availableText() {
+      return this.formatMoney(this.availableAmount)
+    }
+  },
+  onShow() {
+    this.loadData()
+  },
+  methods: {
+    goBack() {
+      if (this.isLeaving) {
+        return
+      }
+      this.isLeaving = true
+      setTimeout(() => {
+        backOrHome('/pages/earnings/index')
+      }, 280)
+    },
+    startWithdrawal() {
+      getOnboardingStatus().then((item) => {
+        const status = (item && item.status) || 'unsubmitted'
+        if (status === 'approved') {
+          routeTo('/pages/withdrawal-detail/index')
+          return
+        }
+        if (status === 'pending' || status === 'rejected') {
+          routeTo('/pages/onboarding-pending/index')
+          return
+        }
+        routeTo('/pages/onboarding/index')
+      }).catch(() => {
+        routeTo('/pages/onboarding/index')
+      })
+    },
+    loadData() {
+      this.loading = true
+      Promise.all([
+        getWithdrawalSummary(),
+        getWithdrawals()
+      ]).then(([summary, page]) => {
+        this.availableAmount = Number(summary && summary.availableAmount || 0)
+        this.records = (page && page.records) || []
+      }).catch((error) => {
+        uni.showToast({ title: error.message || '提现记录加载失败', icon: 'none' })
+      }).finally(() => {
+        this.loading = false
+      })
+    },
+    formatMoney(amount = 0) {
+      return Number(amount || 0).toFixed(2)
+    },
+    formatTime(value) {
+      return value ? String(value).replace('T', ' ').slice(0, 19) : ''
+    },
+    displayTime(item) {
+      if (item.status === 'APPROVED' && item.processedAt) {
+        return '到账 ' + this.formatTime(item.processedAt)
+      }
+      return this.formatTime(item.appliedAt)
+    },
+    statusText(status) {
+      if (status === 'APPROVED') return '已到账'
+      if (status === 'REJECTED') return '已驳回'
+      return '待审核'
+    },
+    statusClass(status) {
+      if (status === 'APPROVED') return 'paid'
+      if (status === 'REJECTED') return 'rejected'
+      return 'pending'
+    }
+  }
+}
+</script>
+
+<style scoped>
+.withdrawal-page {
+  display: flex;
+  flex-direction: column;
+  min-height: 100vh;
+  background: #f6f8fb;
+  padding-bottom: env(safe-area-inset-bottom);
+  animation: pageSlideIn 260ms cubic-bezier(0.22, 1, 0.36, 1) both;
+}
+
+.withdrawal-page.leaving {
+  animation: pageSlideOut 280ms cubic-bezier(0.4, 0, 1, 1) both;
+}
+
+@keyframes pageSlideIn {
+  from { opacity: 0.35; transform: translateX(72rpx); }
+  to { opacity: 1; transform: translateX(0); }
+}
+
+@keyframes pageSlideOut {
+  from { opacity: 1; transform: translateX(0); }
+  to { opacity: 0.2; transform: translateX(260rpx); }
+}
+
+.balance-card {
+  margin: 28rpx 28rpx 0;
+  padding: 36rpx 32rpx 32rpx;
+  border: 1rpx solid #edf0f4;
+  border-radius: 12rpx;
+  background: #ffffff;
+  text-align: center;
+}
+
+.balance-label {
+  color: #667085;
+  font-size: 26rpx;
+}
+
+.balance-amount {
+  display: block;
+  margin: 16rpx 0 28rpx;
+  color: #1f3b5b;
+  font-size: 56rpx;
+  font-weight: 800;
+  line-height: 1;
+}
+
+.withdraw-button {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  height: 88rpx;
+  border-radius: 12rpx;
+  background: #4d8fe8;
+  color: #ffffff;
+  font-size: 30rpx;
+  font-weight: 700;
+}
+
+.heading {
+  display: flex;
+  justify-content: space-between;
+  padding: 36rpx 36rpx 18rpx;
+  color: #1f3b5b;
+  font-size: 30rpx;
+  font-weight: 700;
+}
+
+.heading text:last-child {
+  color: #a0a9b7;
+  font-size: 24rpx;
+  font-weight: 400;
+}
+
+.record-list {
+  flex: 1;
+  min-height: 0;
+  padding: 0 28rpx 24rpx;
+  box-sizing: border-box;
+}
+
+.record {
+  padding: 26rpx 28rpx;
+  margin-bottom: 16rpx;
+  border: 1rpx solid #edf0f4;
+  border-radius: 12rpx;
+  background: #ffffff;
+}
+
+.record-top,
+.record-bottom {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.record-top {
+  color: #1f2937;
+  font-size: 28rpx;
+  font-weight: 700;
+}
+
+.record-money {
+  font-weight: 800;
+}
+
+.record-bottom {
+  margin-top: 16rpx;
+  color: #667085;
+  font-size: 23rpx;
+}
+
+.status {
+  font-weight: 700;
+}
+
+.pending {
+  color: #e58b28;
+}
+
+.paid {
+  color: #249765;
+}
+
+.rejected,
+.reason {
+  color: #e95b5b;
+}
+
+.reason {
+  display: block;
+  margin-top: 8rpx;
+  font-size: 22rpx;
+}
+
+.state-text {
+  padding-top: 120rpx;
+  color: #8993a1;
+  text-align: center;
+  font-size: 28rpx;
+}
+</style>
diff --git a/mingdaojingjiang_guide_app/services/settlement.js b/mingdaojingjiang_guide_app/services/settlement.js
new file mode 100644
index 0000000..c07d6e0
--- /dev/null
+++ b/mingdaojingjiang_guide_app/services/settlement.js
@@ -0,0 +1,141 @@
+import {
+  GUIDE_BANK_CARDS_KEY,
+  GUIDE_ONBOARDING_KEY,
+  GUIDE_SELECTED_BANK_KEY,
+  GUIDE_SELECTED_BRANCH_KEY,
+  bankBranches,
+  initialBankCards,
+  initialOnboarding
+} from '../constants/mock'
+
+function formatNow() {
+  const now = new Date()
+  const pad = (value) => String(value).padStart(2, '0')
+  return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`
+}
+
+function maskCard(cardNumber) {
+  const digits = String(cardNumber || '').replace(/\D/g, '')
+  return `****${digits.slice(-4)}`
+}
+
+function readOnboarding() {
+  const stored = uni.getStorageSync(GUIDE_ONBOARDING_KEY)
+  if (stored && stored.status) {
+    return stored
+  }
+  uni.setStorageSync(GUIDE_ONBOARDING_KEY, { ...initialOnboarding })
+  return { ...initialOnboarding }
+}
+
+function writeOnboarding(data) {
+  uni.setStorageSync(GUIDE_ONBOARDING_KEY, data)
+}
+
+function readBankCards() {
+  const stored = uni.getStorageSync(GUIDE_BANK_CARDS_KEY)
+  if (Array.isArray(stored) && stored.length) {
+    return stored
+  }
+  uni.setStorageSync(GUIDE_BANK_CARDS_KEY, initialBankCards)
+  return initialBankCards.slice()
+}
+
+function writeBankCards(cards) {
+  uni.setStorageSync(GUIDE_BANK_CARDS_KEY, cards)
+}
+
+export function getOnboardingStatus() {
+  return Promise.resolve(readOnboarding())
+}
+
+export function submitOnboarding(payload) {
+  const next = {
+    status: 'pending',
+    reason: '',
+    submittedAt: formatNow(),
+    payload: payload || {}
+  }
+  writeOnboarding(next)
+  return Promise.resolve(next)
+}
+
+export function refreshOnboardingStatus() {
+  const current = readOnboarding()
+  if (current.status === 'pending') {
+    const next = {
+      ...current,
+      status: 'approved',
+      reason: '',
+      reviewedAt: formatNow()
+    }
+    writeOnboarding(next)
+    return Promise.resolve(next)
+  }
+  return Promise.resolve(current)
+}
+
+export function getBankCards() {
+  return Promise.resolve(readBankCards())
+}
+
+export function addBankCard(payload) {
+  const cards = readBankCards()
+  const created = {
+    id: `card_${Date.now()}`,
+    bankName: payload.bankName,
+    branchName: payload.branchName,
+    cardMasked: maskCard(payload.cardNumber),
+    status: 'pending',
+    reason: ''
+  }
+  writeBankCards([created, ...cards])
+  return Promise.resolve(created)
+}
+
+export function searchBankBranches(keyword = '') {
+  const query = String(keyword || '').trim()
+  const items = bankBranches.filter((item) => {
+    if (!query) {
+      return true
+    }
+    return item.bankName.indexOf(query) >= 0 || item.branchName.indexOf(query) >= 0
+  })
+  return Promise.resolve(items)
+}
+
+export function saveSelectedBankId(id) {
+  uni.setStorageSync(GUIDE_SELECTED_BANK_KEY, id)
+}
+
+export function consumeSelectedBankId() {
+  const value = uni.getStorageSync(GUIDE_SELECTED_BANK_KEY)
+  if (value) {
+    uni.removeStorageSync(GUIDE_SELECTED_BANK_KEY)
+  }
+  return value || ''
+}
+
+export function saveSelectedBranch(branch) {
+  uni.setStorageSync(GUIDE_SELECTED_BRANCH_KEY, JSON.stringify(branch))
+}
+
+export function consumeSelectedBranch() {
+  const stored = uni.getStorageSync(GUIDE_SELECTED_BRANCH_KEY)
+  if (!stored) {
+    return null
+  }
+  uni.removeStorageSync(GUIDE_SELECTED_BRANCH_KEY)
+  if (typeof stored === 'object') {
+    return stored
+  }
+  try {
+    return JSON.parse(stored)
+  } catch (error) {
+    return {
+      branchName: stored,
+      bankName: stored,
+      branchCode: ''
+    }
+  }
+}
diff --git a/mingdaojingjiang_guide_app/services/withdrawal.js b/mingdaojingjiang_guide_app/services/withdrawal.js
new file mode 100644
index 0000000..3907234
--- /dev/null
+++ b/mingdaojingjiang_guide_app/services/withdrawal.js
@@ -0,0 +1,49 @@
+import { GUIDE_WITHDRAWALS_KEY, initialWithdrawals, withdrawalSummary } from '../constants/mock'
+
+function readWithdrawals() {
+  const records = uni.getStorageSync(GUIDE_WITHDRAWALS_KEY)
+  if (Array.isArray(records) && records.length) {
+    return records
+  }
+  uni.setStorageSync(GUIDE_WITHDRAWALS_KEY, initialWithdrawals)
+  return initialWithdrawals.slice()
+}
+
+function writeWithdrawals(records) {
+  uni.setStorageSync(GUIDE_WITHDRAWALS_KEY, records)
+}
+
+function formatNow() {
+  const now = new Date()
+  const pad = (value) => String(value).padStart(2, '0')
+  return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`
+}
+
+export function getWithdrawalSummary() {
+  return Promise.resolve({ ...withdrawalSummary })
+}
+
+export function getWithdrawals() {
+  const records = readWithdrawals()
+  return Promise.resolve({
+    records,
+    total: records.length
+  })
+}
+
+export function createWithdrawal(amount, bank) {
+  const records = readWithdrawals()
+  const created = {
+    id: `WD${Date.now()}`,
+    withdrawalNo: `WD${Date.now()}`,
+    amount: Number(Number(amount).toFixed(2)),
+    status: 'PENDING',
+    reason: '',
+    bankName: (bank && bank.bankName) || '',
+    cardMasked: (bank && bank.cardMasked) || '',
+    appliedAt: formatNow(),
+    processedAt: ''
+  }
+  writeWithdrawals([created, ...records])
+  return Promise.resolve(created)
+}
diff --git a/mingdaojingjiang_guide_app/static/icons/chevron-left.svg b/mingdaojingjiang_guide_app/static/icons/chevron-left.svg
new file mode 100644
index 0000000..c7d9116
--- /dev/null
+++ b/mingdaojingjiang_guide_app/static/icons/chevron-left.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#222222" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M15 5L8 12L15 19"/></svg>
diff --git a/mingdaojingjiang_guide_app/static/icons/clock-3.svg b/mingdaojingjiang_guide_app/static/icons/clock-3.svg
new file mode 100644
index 0000000..b17569a
--- /dev/null
+++ b/mingdaojingjiang_guide_app/static/icons/clock-3.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#4d8fe8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 6v6h4"/></svg>
diff --git a/mingdaojingjiang_guide_app/static/icons/search.svg b/mingdaojingjiang_guide_app/static/icons/search.svg
new file mode 100644
index 0000000..c5d940b
--- /dev/null
+++ b/mingdaojingjiang_guide_app/static/icons/search.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#596579" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>

--
Gitblit v1.7.1