dct 3 mesiacov pred
rodič
commit
b8bf6ca0a3

+ 1 - 0
package.json

@@ -32,6 +32,7 @@
     "lodash-es": "^4.17.21",
     "moment": "^2.30.1",
     "print-js": "^1.6.0",
+    "qrcode": "^1.5.4",
     "vue": "^3.2.38",
     "vue-demi": "^0.14.6",
     "vue-router": "^4.0.0-0",

+ 52 - 0
pnpm-lock.yaml

@@ -71,6 +71,9 @@ importers:
       print-js:
         specifier: ^1.6.0
         version: 1.6.0
+      qrcode:
+        specifier: ^1.5.4
+        version: 1.5.4
       vue:
         specifier: ^3.2.38
         version: 3.5.23
@@ -2303,6 +2306,9 @@ packages:
   diffie-hellman@5.0.3:
     resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==}
 
+  dijkstrajs@1.0.3:
+    resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
+
   dir-glob@2.2.2:
     resolution: {integrity: sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==}
     engines: {node: '>=4'}
@@ -4056,6 +4062,10 @@ packages:
     resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
     engines: {node: '>=8'}
 
+  pngjs@5.0.0:
+    resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
+    engines: {node: '>=10.13.0'}
+
   pnp-webpack-plugin@1.7.0:
     resolution: {integrity: sha512-2Rb3vm+EXble/sMXNSu6eoBx8e79gKqhNq9F5ZWW6ERNCTE/Q0wQNne5541tE5vKjfM8hpNCYL+LGc1YTfI0dg==}
     engines: {node: '>=6'}
@@ -4301,6 +4311,11 @@ packages:
 
       (For a CapTP with native promises, see @endo/eventual-send and @endo/captp)
 
+  qrcode@1.5.4:
+    resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==}
+    engines: {node: '>=10.13.0'}
+    hasBin: true
+
   qs@6.13.0:
     resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
     engines: {node: '>=0.6'}
@@ -5388,6 +5403,10 @@ packages:
   yargs-parser@13.1.2:
     resolution: {integrity: sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==}
 
+  yargs-parser@18.1.3:
+    resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
+    engines: {node: '>=6'}
+
   yargs-parser@20.2.9:
     resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
     engines: {node: '>=10'}
@@ -5395,6 +5414,10 @@ packages:
   yargs@13.3.2:
     resolution: {integrity: sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==}
 
+  yargs@15.4.1:
+    resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
+    engines: {node: '>=8'}
+
   yargs@16.2.0:
     resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
     engines: {node: '>=10'}
@@ -8193,6 +8216,8 @@ snapshots:
       miller-rabin: 4.0.1
       randombytes: 2.1.0
 
+  dijkstrajs@1.0.3: {}
+
   dir-glob@2.2.2:
     dependencies:
       path-type: 3.0.0
@@ -10112,6 +10137,8 @@ snapshots:
     dependencies:
       find-up: 4.1.0
 
+  pngjs@5.0.0: {}
+
   pnp-webpack-plugin@1.7.0:
     dependencies:
       ts-pnp: 1.2.0
@@ -10421,6 +10448,12 @@ snapshots:
 
   q@1.5.1: {}
 
+  qrcode@1.5.4:
+    dependencies:
+      dijkstrajs: 1.0.3
+      pngjs: 5.0.0
+      yargs: 15.4.1
+
   qs@6.13.0:
     dependencies:
       side-channel: 1.1.0
@@ -11785,6 +11818,11 @@ snapshots:
       camelcase: 5.3.1
       decamelize: 1.2.0
 
+  yargs-parser@18.1.3:
+    dependencies:
+      camelcase: 5.3.1
+      decamelize: 1.2.0
+
   yargs-parser@20.2.9: {}
 
   yargs@13.3.2:
@@ -11800,6 +11838,20 @@ snapshots:
       y18n: 4.0.3
       yargs-parser: 13.1.2
 
+  yargs@15.4.1:
+    dependencies:
+      cliui: 6.0.0
+      decamelize: 1.2.0
+      find-up: 4.1.0
+      get-caller-file: 2.0.5
+      require-directory: 2.1.1
+      require-main-filename: 2.0.0
+      set-blocking: 2.0.0
+      string-width: 4.2.3
+      which-module: 2.0.1
+      y18n: 4.0.3
+      yargs-parser: 18.1.3
+
   yargs@16.2.0:
     dependencies:
       cliui: 7.0.4

+ 22 - 1
src/api/pay.js

@@ -7,4 +7,25 @@ export function getPayParams (orderID, payType) {
     method: 'post',
     data: { paymentMethod: payType || 'Online' },
   });
-}
+}
+
+export function getAuthUrl (redirectUrl, state) {
+  return request({
+    url: `/WxPortal/GetAuthUrl?redirectUrl=${redirectUrl}&state=${state}&snsapi=0`,
+    method: 'get',
+  });
+}
+
+export function getOpenId (code) {
+  return request({
+    url: `/WxPortal/Callback?code=${code}`,
+    method: 'get',
+  });
+}
+
+export function getWxJspayApi (openId, orderNo) {
+  return request({
+    url: `/WxPortal/WxJspayApi?openId=${openId}&orderNo=${orderNo}`,
+    method: 'get',
+  });
+}

+ 2 - 2
src/layout/components/menuTree.vue

@@ -54,8 +54,8 @@
       </el-menu-item> -->
       <!-- <el-menu-item index="/sys/userReset">
         <span>用户重置</span>
-      </el-menu-item>
-      <el-menu-item index="/sys/dictionary">
+      </el-menu-item> -->
+      <!-- <el-menu-item index="/sys/dictionary">
         <span>字典管理</span>
       </el-menu-item> -->
     </el-sub-menu>

+ 11 - 0
src/layout/main.vue

@@ -0,0 +1,11 @@
+<template>
+  <el-main>
+    <router-view />
+  </el-main>
+</template>
+
+<script>
+export default {
+  name: "Main"
+};
+</script>

+ 25 - 0
src/router/index.js

@@ -1,6 +1,7 @@
 import { createRouter, createWebHistory } from 'vue-router';
 
 import Layout from '@/layout/';
+import LayoutMain from '@/layout/main';
 
 const routes = [
   {
@@ -79,6 +80,30 @@ const routes = [
       },
     ],
   },
+  {
+    path: '/payment',
+    component: LayoutMain,
+    children: [
+      {
+        path: 'detail/:id',
+        name: 'payment',
+        meta: {
+          title: '订单支付',
+          requireAuth: false,
+        },
+        component: () => import('@/views/payment/index'),
+      },
+      {
+        path: 'success',
+        name: 'success',
+        meta: {
+          title: '订单支付成功',
+          requireAuth: false,
+        },
+        component: () => import('@/views/payment/success'),
+      },
+    ],
+  },
   {
     path: '/sys/',
     component: Layout,

+ 133 - 27
src/views/order/index.vue

@@ -41,8 +41,11 @@
           <el-select v-model="forms.paymentStatus" :empty-values="[null, undefined]" :value-on-clear="null" clearable
             style="width: 100px">
             <el-option label="全部" value=""></el-option>
-            <el-option label="待支付" value="Pending"></el-option>
+            <el-option label="未支付" value="Unpaid"></el-option>
+            <el-option label="部分支付" value="Partial"></el-option>
             <el-option label="已支付" value="Paid"></el-option>
+            <el-option label="退款中" value="Refunding"></el-option>
+            <el-option label="已退款" value="Refunded"></el-option>
           </el-select>
         </el-form-item>
         <el-form-item label="" label-width="20px">
@@ -105,18 +108,18 @@
           </el-tag>
         </template>
       </el-table-column>
-      <el-table-column prop="progress" label="进度" width="180">
+      <!-- <el-table-column prop="progress" label="进度" width="180">
         <template #default="scope">
           <el-progress :percentage="scope.row.progress"></el-progress>
         </template>
-      </el-table-column>
+      </el-table-column> -->
 
       <el-table-column fixed="right" label="操作" width="320">
         <template #default="scope">
           <!-- <el-button type="primary" link size="small" @click="edit(scope.row)">编辑</el-button> -->
           <!-- <el-button type="success" link size="small" @click="audit(scope.row)">审核</el-button> -->
           <el-button type="info" link size="small" @click="attachment(scope.row)">附件</el-button>
-          <el-button type="warning" v-if="userAccountId === 'testuser'" link size="small"
+          <el-button type="warning" v-if="userAccountId === 'testuser' && orderCanPay(scope.row)" link size="small"
             @click="payment(scope.row)">收款</el-button>
           <el-button type="info" link size="small" @click="editSchedule(scope.row)">行程</el-button>
           <el-button type="primary" link size="small" @click="editTourists(scope.row)">游客</el-button>
@@ -232,9 +235,10 @@
                     </template>
                   </el-table-column>
 
-                  <el-table-column label="数量" width="80" align="right">
+                  <el-table-column label="游客数量" width="180" align="right">
                     <template #default="scope">
-                      <div>{{ Number(scope.row.quantity) || 0 }}</div>
+                      <el-input-number v-model.number="scope.row.quantity" :min="1"
+                        style="width:160px"></el-input-number>
                     </template>
                   </el-table-column>
 
@@ -259,17 +263,15 @@
 
                 <el-row style="margin-top:10px;">
                   <el-col :span="12">
-                    <el-form-item label="产品小计" label-width="120px">
-                      <div style="line-height:36px;">¥{{((formData.details || []).reduce((s, p) => s +
-                        (Number(p.unitPrice) || 0) * (Number(p.quantity) || 0), 0)).toFixed(2)}}</div>
+                    <el-form-item label="当天产品小计" label-width="120px">
+                      <div style="line-height:36px;">¥{{ currentDayTotal }}</div>
                     </el-form-item>
                   </el-col>
-                  <!-- <el-col :span="12">
+                  <el-col :span="12">
                     <el-form-item label="订单总金额" label-width="120px">
-                      <el-input-number v-model.number="formData.totalAmount" :precision="2" :min="0"
-                        style="width:160px;"></el-input-number>
+                      <div style="line-height:36px;">¥{{ totalPrice }}</div>
                     </el-form-item>
-                  </el-col> -->
+                  </el-col>
                 </el-row>
               </div>
               <div v-else style="text-align: center">
@@ -443,7 +445,7 @@
             <el-collapse-item title="附件" name="attachments">
               <el-row :gutter="10" style="margin-bottom: 10px">
                 <el-col :span="24">
-                  <el-button type="primary" size="small" @click="downloadTemplate">下载模板</el-button>
+                  <el-button type="primary" size="small" @click="downloadTemplate">下载游客信息模板</el-button>
                 </el-col>
               </el-row>
               <el-row :gutter="10">
@@ -452,7 +454,7 @@
                     :data="uploadParams" :headers="headers" accept=".xls,.xlsx" :limit="10" :on-success="uploadSuccess"
                     :file-list="fileList" :on-error="uploadError" :on-remove="removeFile" :before-upload="beforeUpload"
                     @on-preview="downloadFile">
-                    <el-button type="primary" size="small">上传<i class="el-icon-plus"></i></el-button>
+                    <el-button type="primary" size="small">上传游客信息模板<i class="el-icon-plus"></i></el-button>
                   </el-upload>
 
                   <div class="file-list">
@@ -813,13 +815,13 @@
 
     <el-dialog title="上传附件" v-model="attachDialogVisible" width="700px" append-to-body @close="currentOrderId = ''">
       <div style="margin-bottom:12px;">
-        <el-button type="primary" size="small" @click="downloadTemplate">下载模板</el-button>
+        <el-button type="primary" size="small" @click="downloadTemplate">下载游客信息模板</el-button>
       </div>
 
       <el-upload ref="orderUploadRef" action="/api/BaseUploader/Basic" :show-file-list="false" :data="uploadParams"
         :headers="headers" accept=".xls,.xlsx" :limit="10" :on-success="uploadSuccess" :file-list="fileList"
         :on-error="uploadError" :on-remove="removeFile" :before-upload="beforeUpload" @on-preview="downloadFile">
-        <el-button type="primary" size="small">上传<i class="el-icon-plus"></i></el-button>
+        <el-button type="primary" size="small">上传游客信息模板<i class="el-icon-plus"></i></el-button>
       </el-upload>
 
       <div class="file-list" style="margin: 10px 0">
@@ -871,6 +873,32 @@
       </template>
     </el-dialog>
 
+    <el-dialog title="收款二维码" v-model="onlinePaymentDialogVisible" width="420px" append-to-body
+      @close="cancelOnlinePayment">
+      <div style="text-align:center; padding: 10px 0;">
+        <div style="margin-bottom:8px; font-size:16px;">
+          收款金额:<strong style="color:#f56c6c">¥{{ (paymentForm.amount || 0).toFixed(2) }}</strong>
+        </div>
+
+        <div style="margin-bottom:10px; color:#666;">请使用支付客户端扫码支付</div>
+        <div style="display:flex; justify-content:center; align-items:center;">
+          <canvas id="payment-qr-code" style="width: 280px; height: 280px"></canvas>
+        </div>
+        <div style="margin-top:8px; color:#999; font-size:12px;">
+          订单号:{{ paymentForm.orderNo || '—' }}
+          <el-button type="text" size="mini"
+            @click="() => { navigator.clipboard?.writeText(paymentForm.orderNo || '') }">复制</el-button>
+        </div>
+      </div>
+
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="onlinePaymentDialogVisible = false">取 消</el-button>
+          <el-button type="primary" @click="submitOnlinePayment">支付完成</el-button>
+        </span>
+      </template>
+    </el-dialog>
+
   </el-card>
 </template>
 <script>
@@ -900,6 +928,22 @@ import { getTravelAgencies } from '@/api/travelAgency';
 import moment from 'moment/moment';
 import { ProductTypes, FileTypeMap } from '@/constant';
 import utils from '@/utils/util';
+import QRCode from 'qrcode';
+import { getAuthUrl } from '@/api/pay';
+
+const QRCodeOpts = {
+  errorCorrectionLevel: 'H', //容错级别
+  type: 'image/png', //生成的二维码类型
+  quality: 0.3, //二维码质量
+  margin: 0, //二维码留白边距
+  width: 280, //宽
+  height: 280, //高
+  text: 'http://www.xxx.com', //二维码内容
+  color: {
+    dark: '#333333', //前景色
+    light: '#fff', //背景色
+  },
+};
 
 export default {
   name: "OrderManage",
@@ -928,7 +972,7 @@ export default {
           { label: "游玩天数", prop: "travelDays" },
           { label: "人数", prop: "touristCount" },
           { label: "金额", prop: "totalAmount" },
-          { label: "金额", prop: "paidAmount" },
+          { label: "已付金额", prop: "paidAmount" },
           { label: "订单状态", prop: "status" },
           { label: "审核状态", prop: "auditStatus" },
           { label: "支付状态", prop: "paymentStatus" },
@@ -1081,6 +1125,7 @@ export default {
         method: 'Online',
         orderNo: '',
       },
+      onlinePaymentDialogVisible: false,
     };
   },
   methods: {
@@ -1268,6 +1313,52 @@ export default {
         orderNo: '',
       };
     },
+    submitPayment() {
+      if (this.paymentForm.method === 'Online') {
+        // 在线支付,生成二维码
+        this.onlinePaymentDialogVisible = true;
+        this.$nextTick(() => {
+          this.generateWeChatQRCode();
+        });
+      } else {
+        // TODO
+        this.$message.success('收款成功');
+        this.paymentDialogVisible = false;
+        this.getOrderList();
+      }
+    },
+    async generateWeChatQRCode() {
+      const { orderNo, amount } = this.paymentForm;
+      const qrCodeContainer = document.getElementById('payment-qr-code');
+      qrCodeContainer.innerHTML = '';
+      const state = { amount };
+      const redirectUrl = encodeURIComponent(`${window.location.origin}/payment/detail/${orderNo}`); // 回调地址,获取到 code 之后重定向的 URL
+      // const res = await getAuthUrl(redirectUrl, JSON.stringify(state));
+      // const authUrl = res?.data?.authUrl?.replaceAll('ljyx.', 'ljyxweb.');
+      // const authUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx55b78e406222f02c&redirect_uri=https://ljyxweb.hhtrip.com.cn/payment/detail/${orderNo}&response_type=code&scope=snsapi_base&state=${JSON.stringify(state)}&connect_redirect=1#wechat_redirect`
+      const authUrl = `${window.location.origin}/WxPortal/GetAuthUrl?redirectUrl=${redirectUrl}&state=${encodeURIComponent(JSON.stringify(state))}&snsapi=0`;
+      console.log('---auth url---', decodeURIComponent(authUrl));
+      // if (!authUrl) {
+      //   this.$message.error('二维码加载失败');
+      //   return;
+      // };
+      // 将获取到的数据(val)画到msg(canvas)上
+      QRCode.toCanvas(qrCodeContainer, authUrl, QRCodeOpts, (error) => {
+        if (error) {
+          console.log('二维码加载失败', error);
+          this.$message.error('二维码加载失败');
+        }
+      });
+    },
+    cancelOnlinePayment() {
+      this.onlinePaymentDialogVisible = false;
+      document.getElementById('payment-qr-code').innerHTML = '';
+    },
+    submitOnlinePayment() {
+      this.$message.success('支付成功');
+      this.onlinePaymentDialogVisible = false;
+      this.getOrderList();
+    },
     del(row) {
       this.$confirm("此操作将删除选择数据, 是否继续?", "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", })
         .then(() => {
@@ -1351,7 +1442,7 @@ export default {
     handelPtSelect(selectedRows, row) {
       this.productList.forEach((product) => {
         if (selectedRows.find((sr) => sr.id === product.id)) {
-          this.tempSelectedProducts.push(({ id: product.id, productName: product.productName }))
+          this.tempSelectedProducts.push(({ id: product.id, productName: product.productName, useDate: this.currUseDate }))
         } else {
           this.tempSelectedProducts = this.tempSelectedProducts.filter((item) => item.id !== product.id);
         }
@@ -1361,7 +1452,7 @@ export default {
     handlePtSelectAll(selectedRows) {
       this.productList.forEach((product) => {
         if (selectedRows.find((sr) => sr.id === product.id)) {
-          this.tempSelectedProducts.push(({ id: product.id, productName: product.productName }))
+          this.tempSelectedProducts.push(({ id: product.id, productName: product.productName, useDate: this.currUseDate }))
         } else {
           this.tempSelectedProducts = this.tempSelectedProducts.filter((item) => item.id !== product.id);
         }
@@ -1382,13 +1473,15 @@ export default {
       }
 
       this.tempSelectedProducts.forEach((item) => {
-        if (!this.formData.details.find((d) => d.productID === item.id)) {
+        const product = this.productList.find(p => p.id === item.id);
+
+        if (!this.formData.details.find((d) => d.productID === item.id && d.useDate === item.useDate) && product) {
           this.formData.details.push({
-            productID: item.id,
-            productName: item.productName,
-            productCode: item.productCode,
-            productType: ProductTypes[item.type] || item.type,
-            unitPrice: item.price,
+            productID: product.id,
+            productName: product.productName,
+            productCode: product.productCode,
+            productType: ProductTypes[product.type] || product.type,
+            unitPrice: product.price,
             useDate: this.currUseDate,
             quantity: 1,
             remark: ''
@@ -1491,6 +1584,9 @@ export default {
       }
       return texts[status] || status
     },
+    orderCanPay(row) {
+      return row.paymentStatus !== 'Paid';
+    },
     handleAgencyChange(agencyId) {
       const agency = this.agencyOptions.find(item => item.id === agencyId)
       if (agency) {
@@ -1509,7 +1605,7 @@ export default {
     getScheduleList(orderId) {
       // 调用API获取行程列表
       getScheduleByOrderId(orderId).then(res => {
-        console.log(res, 111);
+        console.log(res, 'scheduleList');
         this.scheduleList = res.data;
       });
     },
@@ -1644,6 +1740,16 @@ export default {
     },
   },
   computed: {
+    currentDayTotal() {
+      const { formData, currUseDate } = this;
+      return ((formData.details || []).filter((d) => d.useDate === currUseDate).reduce((s, p) => s +
+        (Number(p.unitPrice) || 0) * (Number(p.quantity) || 0), 0)).toFixed(2)
+    },
+    totalPrice() {
+      const { formData } = this;
+      return ((formData.details || []).reduce((s, p) => s +
+        (Number(p.unitPrice) || 0) * (Number(p.quantity) || 0), 0)).toFixed(2)
+    },
     travelInfo() {
       return {
         travelStartDate: this.formData.travelStartDate,

+ 3 - 3
src/views/order/review.vue

@@ -40,7 +40,7 @@
         <template #default="scope">
           <div>开始日期: {{ scope.row.travelStartDate }}</div>
           <div>结束日期: {{ scope.row.travelEndDate }}</div>
-          <div>共: <el-tag size="small">{{ scope.row.travelDays }}天</el-tag></div>
+          <div>共: <el-tag size="small">{{ scope.row.travelDays + 1 }}天</el-tag></div>
         </template>
       </el-table-column>
       <el-table-column prop="touristCount" label="人数" width="80" align="center"></el-table-column>
@@ -73,11 +73,11 @@
           </el-tag>
         </template>
       </el-table-column>
-      <el-table-column prop="progress" label="进度" width="180">
+      <!-- <el-table-column prop="progress" label="进度" width="180">
         <template #default="scope">
           <el-progress :percentage="scope.row.progress"></el-progress>
         </template>
-      </el-table-column>
+      </el-table-column> -->
 
       <el-table-column fixed="right" label="操作" width="320">
         <template #default="scope">

+ 69 - 7
src/views/payment/index.vue

@@ -18,6 +18,13 @@
           <span class="name">微信支付</span>
         </div>
       </div>
+      <!-- <div>href: {{ href }}</div>
+      <div>code: {{ code }}</div>
+      <div>orderNo: {{ orderNo }}</div>
+      <div>openIdRes: {{ openIdRes }}</div>
+      <div>openId: {{ openId }}</div>
+      <div>state: {{ state }}</div>
+      <div>jsApi: {{ jsApi }}</div> -->
 
       <button @click="confirmPayment" :disabled="loading" class="confirm-btn">
         {{ loading ? '处理中...' : '确认支付' }}
@@ -27,31 +34,86 @@
 </template>
 
 <script>
+import { getOpenId, getWxJspayApi } from '@/api/pay';
+import wx from 'weixin-js-sdk';
+
 export default {
   name: 'Payment',
   data() {
+    const { query, params } = this.$route;
+    const { code, state, openId } = query;
+    let amount = 0;
+    try {
+      const st = JSON.parse(state)
+      amount = st.amount || 0;
+    } catch (error) {
+      console.log(error);
+    }
     return {
-      orderNo: '20240115001',
-      amount: '199.99',
+      href: window.location.href,
       selectedMethod: 'wechat',
       loading: false,
       methods: [
         { id: 'wechat', name: '微信支付', icon: '💬' },
         { id: 'alipay', name: '支付宝', icon: '🔵' },
         { id: 'card', name: '银行卡', icon: '💳' }
-      ]
+      ],
+      orderNo: params.id || '',
+      code: code || '',
+      openId: openId || '',
+      state: this.$route.query.state || '',
+      amount,
+      jsApi: '',
+      openIdRes: '',
     };
   },
+  async mounted() {
+    const openidRes = await getOpenId(this.code);
+    console.log('---get openid---', openidRes);
+    if (openidRes.code === 200) {
+      this.openIdRes = openidRes;
+      this.openId = openidRes.data.openid;
+      const wxJspayApiRes = await getWxJspayApi(this.openId, this.orderNo);
+      console.log('---wx jspay api---', wxJspayApiRes);
+      if (wxJspayApiRes.code === 200) {
+        this.jsApi = wxJspayApiRes.data;
+        this.confirmPayment();
+      } else {
+        this.$message.error(wxJspayApiRes.message || '获取支付参数失败');
+      }
+    } else {
+      this.$message.error(openidRes.message || '获取用户信息失败');
+      return;
+    }
+  },
   methods: {
     goBack() {
       this.$router.go(-1);
     },
     confirmPayment() {
       this.loading = true;
-      setTimeout(() => {
-        this.$router.push('/order/success');
-        this.loading = false;
-      }, 2000);
+      wx.config({
+        debug: false,
+        ...this.jsApi,
+        jsApiList: ['checkJsApi', 'chooseWXPay']
+      });
+      wx.ready(() => {
+        wx.chooseWXPay({
+          ...this.jsApi,
+          signType: 'RSA',
+          package: this.jsApi.prepayIdPackage,
+          paySign: this.jsApi.signature,
+          success: (res) => {
+            this.$router.push(`/payment/success?orderNo=${this.orderNo}&code=${this.code}&state=${this.state}`);
+          },
+          fail: (err) => {
+            this.$message.error('支付失败,请重试');
+          },
+          complete: () => {
+            this.loading = false;
+          }
+        });
+      });
     }
   }
 };