ソースを参照

feat: 1.领取优惠劵新流程开发;2.下订单新流程开发;3.上传发票流程开发;4.新增优惠劵页面;5.新增我的商家页面;

laiqi 1 年間 前
コミット
1d21fa6fc8

+ 14 - 13
env/.env

@@ -1,26 +1,27 @@
-VITE_APP_TITLE = '达州农机优惠劵'
-VITE_APP_PORT = 9000
+VITE_APP_TITLE='达州农机优惠劵'
+VITE_APP_PORT=9000
+VITE_APP_VERSION=1.1.0
 
-VITE_UNI_APPID = 'H57F2ACE4'
-VITE_WX_APPID = 'wxd3ca52d7f454048d'
+VITE_UNI_APPID='H57F2ACE4'
+VITE_WX_APPID='wxd3ca52d7f454048d'
 
 # h5部署网站的base,配置到 manifest.config.ts 里的 h5.router.base
 VITE_APP_PUBLIC_BASE=/unibest/
 
 # VITE_SERVER_BASEURL = 'http://211.149.199.65:5012'
-VITE_SERVER_BASEURL = 'https://dzapi.kdboss.cn'
-VITE_UPLOAD_BASEURL = 'https://dzapi.kdboss.cn'
+VITE_SERVER_BASEURL='https://dzapi.kdboss.cn'
+VITE_UPLOAD_BASEURL='https://dzapi.kdboss.cn'
 
 # 有些同学可能需要在微信小程序里面根据 develop、trial、release 分别设置上传地址,参考代码如下。
 # 下面的变量如果没有设置,会默认使用 VITE_SERVER_BASEURL or VITE_UPLOAD_BASEURL
-VITE_SERVER_BASEURL__WEIXIN_DEVELOP = 'https://dzapi.kdboss.cn'
-VITE_SERVER_BASEURL__WEIXIN_TRIAL = 'https://dzapi.kdboss.cn'
-VITE_SERVER_BASEURL__WEIXIN_RELEASE = 'https://dzapi.kdboss.cn'
+VITE_SERVER_BASEURL__WEIXIN_DEVELOP='https://dzapi.kdboss.cn'
+VITE_SERVER_BASEURL__WEIXIN_TRIAL='https://dzapi.kdboss.cn'
+VITE_SERVER_BASEURL__WEIXIN_RELEASE='https://dzapi.kdboss.cn'
 
-VITE_UPLOAD_BASEURL__WEIXIN_DEVELOP = 'https://dzapi.kdboss.cn'
-VITE_UPLOAD_BASEURL__WEIXIN_TRIAL = 'https://dzapi.kdboss.cn'
-VITE_UPLOAD_BASEURL__WEIXIN_RELEASE = 'https://dzapi.kdboss.cn'
+VITE_UPLOAD_BASEURL__WEIXIN_DEVELOP='https://dzapi.kdboss.cn'
+VITE_UPLOAD_BASEURL__WEIXIN_TRIAL='https://dzapi.kdboss.cn'
+VITE_UPLOAD_BASEURL__WEIXIN_RELEASE='https://dzapi.kdboss.cn'
 
 # h5是否需要配置代理
 VITE_APP_PROXY=false
-VITE_APP_PROXY_PREFIX = '/api'
+VITE_APP_PROXY_PREFIX='/api'

+ 3 - 3
env/.env.development

@@ -1,6 +1,6 @@
 # 变量必须以 VITE_ 为前缀才能暴露给外部读取
-NODE_ENV = 'development'
+NODE_ENV='development'
 # 是否去除console 和 debugger
-VITE_DELETE_CONSOLE = false
+VITE_DELETE_CONSOLE=false
 # 是否开启sourcemap
-VITE_SHOW_SOURCEMAP = true
+VITE_SHOW_SOURCEMAP=true

+ 3 - 3
env/.env.production

@@ -1,6 +1,6 @@
 # 变量必须以 VITE_ 为前缀才能暴露给外部读取
-NODE_ENV = 'development'
+NODE_ENV='production'
 # 是否去除console 和 debugger
-VITE_DELETE_CONSOLE = true
+VITE_DELETE_CONSOLE=true
 # 是否开启sourcemap
-VITE_SHOW_SOURCEMAP = false
+VITE_SHOW_SOURCEMAP=false

+ 2 - 2
env/.env.test

@@ -1,4 +1,4 @@
 # 变量必须以 VITE_ 为前缀才能暴露给外部读取
-NODE_ENV = 'development'
+NODE_ENV='test'
 # 是否去除console 和 debugger
-VITE_DELETE_CONSOLE = false
+VITE_DELETE_CONSOLE=false

+ 8 - 21
src/App.vue

@@ -1,33 +1,20 @@
 <script setup lang="ts">
 import { onLaunch, onShow, onHide } from '@dcloudio/uni-app'
 import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'
-import { useAppStore } from '@/store/app'
-import { useUserStore } from '@/store/user'
 import { useDictStore } from '@/store/dict'
 import { useFileStore } from '@/store/file'
 
 onLaunch(async () => {
   // console.log('App =====> onLaunch')
-  // 检查是否已登录
-  const appStore = useAppStore()
 
-  if (!appStore.isLogined) {
-    uni.navigateTo({ url: '/pages-sub/auth/index' })
-  } else {
-    // 获取用户信息,用于:1.展示   2.判断是否需要完善信息
-    const userStore = useUserStore()
-    const res = await userStore.getUserInfo()
-    userStore.setUserDetail(res.Data)
-
-    // 获取文件前缀地址
-    const dictStore = useDictStore()
-    const fileStore = useFileStore()
-    const configList = await dictStore.getConfigList('fileheadurl')
-    if (configList.length > 0) {
-      // 将字符串分割后转换为数字类型
-      const fileHeadUrl = configList[0].substance
-      fileStore.setFileHeadUrl(fileHeadUrl)
-    }
+  // 获取文件前缀地址
+  const dictStore = useDictStore()
+  const fileStore = useFileStore()
+  const configList = await dictStore.getConfigList('fileheadurl')
+  if (configList.length > 0) {
+    // 将字符串分割后转换为数字类型
+    const fileHeadUrl = configList[0].substance
+    fileStore.setFileHeadUrl(fileHeadUrl)
   }
 })
 

+ 195 - 0
src/components/UploadComponent.vue

@@ -0,0 +1,195 @@
+<template>
+  <div class="upload-component">
+    <wd-upload
+      v-model:file-list="fileList"
+      image-mode="aspectFill"
+      :upload-method="
+        createCustomUpload({
+          attmodel,
+          attpath,
+          modelStats,
+        })
+      "
+      :rules="[{ required, message }]"
+      :limit="limit"
+      :before-upload="beforeUpload"
+      @remove="handleRemove"
+    ></wd-upload>
+    <div class="tips mt-2" v-if="tips">
+      <wd-text type="warning" :text="tips"></wd-text>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, watch } from 'vue'
+import { useUserStore } from '@/store/user'
+import { useMessage } from 'wot-design-uni'
+import { uploadFile } from '@/hooks/useUpload'
+
+// 定义类型
+interface FileItem {
+  url: string
+  id: string
+}
+
+const props = defineProps({
+  // 外部传入的文件列表
+  modelValue: {
+    type: Array,
+    default: () => [],
+  },
+  // 存放文件ID的数组
+  fileIds: {
+    type: Array as () => FileItem[],
+    default: () => [],
+  },
+  // attlsh 关联的ID
+  attlsh: {
+    type: String,
+    default: '',
+  },
+  // 上传配置
+  attmodel: {
+    type: String,
+    required: true,
+  },
+  // 上传路径
+  attpath: {
+    type: String,
+    required: true,
+  },
+  // 存放文件ID的数组
+  modelStats: {
+    type: String,
+    required: true,
+  },
+  // 验证配置
+  required: {
+    type: Boolean,
+    default: true,
+  },
+  // 验证提示
+  message: {
+    type: String,
+    default: '请上传文件',
+  },
+  // 上传数量限制
+  limit: {
+    type: Number,
+    default: 1,
+  },
+  // 提示信息
+  tips: {
+    type: String,
+    default: '',
+  },
+})
+
+const emit = defineEmits(['update:modelValue', 'update:fileIds'])
+
+const userStore = useUserStore()
+const messageService = useMessage() // 重命名变量以避免冲突
+const fileList = ref([])
+
+// 监听外部传入的文件列表变化
+watch(
+  () => props.modelValue,
+  (newVal) => {
+    fileList.value = newVal
+  },
+  { deep: true },
+)
+
+// 监听组件内部文件列表变化
+watch(
+  () => fileList.value,
+  (newVal) => {
+    emit('update:modelValue', newVal)
+  },
+  { deep: true },
+)
+
+// 上传前验证
+function beforeUpload({ files, resolve }) {
+  // 判断是否完善了个人信息
+  // if (!userStore.isAuthComplete) {
+  //   handleAuthComplete()
+  //   return
+  // }
+  resolve(true)
+}
+
+// 创建自定义上传方法
+function createCustomUpload({ attmodel, attpath, modelStats }) {
+  return async function (file, formData, options) {
+    const requestFromData: {
+      attmodel: string
+      attpath: string
+      attlsh?: string
+    } = {
+      attmodel,
+      attpath,
+    }
+
+    if (props.attlsh) {
+      requestFromData.attlsh = props.attlsh
+    }
+
+    const loading = ref(false)
+    const error = ref(false)
+    const data = ref<any>()
+
+    await uploadFile({
+      tempFilePath: file.url,
+      formData: requestFromData,
+      data,
+      error,
+      loading,
+    })
+
+    if (!error.value) {
+      options.onSuccess('', file, formData)
+      // {"Status":0,"Data":"2025041016044040627399","Message":"执行成功","OtherData":"https://dzapi.kdboss.cn/upfile/orders_invoice/20250410/2025041016044040731543.png"}
+      const dataObject = JSON.parse(data.value)
+      const newFileIds = [...props.fileIds]
+      newFileIds.push({
+        url: file.url,
+        id: dataObject.Data,
+      })
+      emit('update:fileIds', newFileIds)
+    }
+  }
+}
+
+// 删除文件时,删除ids数组数据
+function handleRemove({ file }) {
+  const newFileIds = props.fileIds.filter((item: FileItem) => item.url !== file.url)
+  emit('update:fileIds', newFileIds)
+}
+
+// // 完善资料提示
+// function handleAuthComplete() {
+//   messageService
+//     .confirm({
+//       msg: '您还未完善个人信息, 请先完善信息',
+//       title: '提示',
+//       confirmButtonText: '去完善',
+//       cancelButtonText: '继续浏览',
+//     })
+//     .then(() => {
+//       uni.navigateTo({
+//         url: '/pages-sub/user/index',
+//       })
+//     })
+//     .catch(() => {
+//       // ...
+//     })
+// }
+</script>
+
+<style lang="scss" scoped>
+.upload-component {
+  width: 100%;
+}
+</style>

+ 3 - 3
src/interceptors/route.ts

@@ -10,9 +10,9 @@ import { needLoginPages as _needLoginPages, getNeedLoginPages } from '@/utils'
 // TODO Check
 const loginRoute = '/pages/login/index'
 
-const isLogined = () => {
+const isLoginState = () => {
   const appStore = useAppStore()
-  return appStore.isLogined
+  return appStore.isLoginState
 }
 
 const isDev = import.meta.env.DEV
@@ -34,7 +34,7 @@ const navigateToInterceptor = {
     if (!isNeedLogin) {
       return true
     }
-    const hasLogin = isLogined()
+    const hasLogin = isLoginState()
     if (hasLogin) {
       return true
     }

+ 36 - 2
src/pages-sub/auth/index.vue

@@ -29,6 +29,22 @@ import { useUserStore } from '@/store/user'
 const userStore = useUserStore()
 const appStore = useAppStore()
 const loading = ref(false)
+const redirectUrl = ref('') // 存储回跳页面路径
+
+// 接收回跳页面参数
+onLoad((options) => {
+  if (options.redirect) {
+    // 检查redirect是否有正确的路径格式
+    const redirectPath = decodeURIComponent(options.redirect)
+    // 确保路径以/开头
+    if (!redirectPath.startsWith('/')) {
+      redirectUrl.value = '/' + redirectPath
+    } else {
+      redirectUrl.value = redirectPath
+    }
+    // console.log('回跳路径:', redirectUrl.value)
+  }
+})
 
 // 微信登录
 const handleLogin = async () => {
@@ -66,8 +82,26 @@ const handleLogin = async () => {
               appStore.setAppInfo({ token, userid, openid })
               userStore.getUserInfo()
 
-              uni.reLaunch({ url: '/pages/index/index' })
-              uni.$emit('authComplete', { result: true })
+              // 根据是否有回跳地址决定跳转到哪里
+              if (redirectUrl.value) {
+                try {
+                  // 处理可能的路径错误
+                  // console.log('准备跳转到:', redirectUrl.value)
+                  // 检查路径格式,确保是有效的页面路径
+                  const validPath = redirectUrl.value
+                    .replace(/^pages-sub\/auth\//, '/')
+                    .replace(/^pages\//, '/')
+                  uni.reLaunch({ url: validPath })
+                } catch (navErr) {
+                  console.error('导航错误:', navErr)
+                  // 出错时跳转到首页
+                  uni.reLaunch({ url: '/pages/index/index' })
+                }
+              } else {
+                uni.reLaunch({ url: '/pages/index/index' })
+              }
+
+              uni.$emit('authComplete', { result: true, redirectUrl: redirectUrl.value })
             } else {
               uni.showToast({
                 title: '授权失败,请重试',

+ 17 - 1
src/pages.json

@@ -55,6 +55,14 @@
       }
     },
     {
+      "path": "pages/coupon/index",
+      "type": "page",
+      "layout": "default",
+      "style": {
+        "navigationBarTitleText": "我的优惠券"
+      }
+    },
+    {
       "path": "pages/form/formStep1",
       "type": "page",
       "style": {
@@ -79,7 +87,15 @@
       "path": "pages/form/formStep4",
       "type": "page",
       "style": {
-        "navigationBarTitleText": "填写sn号"
+        "navigationBarTitleText": "确认下单"
+      }
+    },
+    {
+      "path": "pages/merchant/index",
+      "type": "page",
+      "layout": "default",
+      "style": {
+        "navigationBarTitleText": "我的商家"
       }
     },
     {

+ 4 - 2
src/pages/about/index.vue

@@ -17,7 +17,7 @@
       <view class="text-center">
         <view class="text-2xl font-bold mb-3 text-gray-800">达州农机优惠劵</view>
         <view class="text-gray-600 mb-2">达州市达川区农机化技术推广服务站</view>
-        <view class="text-gray-500 text-sm">当前版本:V1.0.5</view>
+        <view class="text-gray-500 text-sm">当前版本:V{{ version }}</view>
       </view>
 
       <view class="mt-12 w-full">
@@ -44,7 +44,9 @@
 </template>
 
 <script lang="ts" setup>
-//
+// 使用固定版本号,从源代码维护
+// 因为环境变量可能会因为构建配置问题无法正确加载
+const version = ref(import.meta.env.VITE_APP_VERSION)
 </script>
 
 <style lang="scss" scoped>

+ 138 - 0
src/pages/coupon/index.vue

@@ -0,0 +1,138 @@
+<route lang="json5" type="page">
+{
+  layout: 'default',
+  style: {
+    navigationBarTitleText: '我的优惠券',
+  },
+}
+</route>
+
+<template>
+  <view class="bg-white min-h-screen">
+    <view class="space-y-4">
+      <!-- 优惠券列表 -->
+      <view class="sticky-box-content w-100vw" v-if="couponList.length">
+        <view class="coupon-list">
+          <view class="coupon-item" v-for="item in couponList" :key="item.coupon2id">
+            <view class="coupon-header">
+              <text class="coupon-code">券码: {{ item.coupon2code }}</text>
+              <wd-tag :type="item.coupon2isused ? 'primary' : 'success'" custom-class="status-tag">
+                {{ item.coupon2isused ? '已使用' : '未使用' }}
+              </wd-tag>
+            </view>
+            <view class="coupon-content">
+              <view class="coupon-info">
+                <text>卷名: {{ item.coupon2mc || '暂无卷名' }}</text>
+              </view>
+              <view class="coupon-info">
+                <text>产品: {{ item.productsname || '暂无产品信息' }}</text>
+              </view>
+              <view class="coupon-info">
+                <text>添加时间: {{ formatDate(item.coupon2adddatetime) }}</text>
+              </view>
+            </view>
+          </view>
+        </view>
+      </view>
+      <view class="sticky-box-content w-100vw" v-else>
+        <wd-status-tip image="content" tip="暂无优惠券" />
+      </view>
+
+      <!-- loading -->
+      <wd-overlay :show="overlayShow">
+        <view class="wrapper w-full h-full flex items-center justify-center">
+          <wd-loading />
+        </view>
+      </wd-overlay>
+
+      <!-- 回到首页 -->
+      <view class="sticky-box-content w-full box-border px-2 pb-2">
+        <wd-button type="primary" block @click="handleBack">回到首页</wd-button>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue'
+import { useCouponStore } from '@/store/coupon'
+import { useAppStore } from '@/store/app'
+
+const appStore = useAppStore()
+const couponStore = useCouponStore()
+const couponList = ref([])
+const overlayShow = ref(false)
+
+// 格式化日期
+function formatDate(dateStr) {
+  if (!dateStr) return '--'
+  const date = new Date(dateStr)
+  return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
+}
+
+function handleBack() {
+  uni.switchTab({
+    url: '/pages/index/index',
+  })
+}
+
+onLoad(() => {
+  overlayShow.value = true
+
+  // 调用获取优惠券列表接口
+  couponStore
+    .getCoupon2List({ coupon2userid: appStore.appInfo.userid })
+    .then((res) => {
+      couponList.value = res.Data
+      overlayShow.value = false
+    })
+    .finally(() => {
+      overlayShow.value = false
+    })
+})
+</script>
+
+<style lang="scss" scoped>
+.coupon-list {
+  padding: 0 12px;
+}
+
+.coupon-item {
+  position: relative;
+  padding: 12px;
+  margin-bottom: 12px;
+  background-color: #fff;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.coupon-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding-bottom: 8px;
+  border-bottom: 1px solid #f5f5f5;
+}
+
+.coupon-code {
+  flex: 1;
+  font-size: 16px;
+  font-weight: 500;
+  color: #333;
+}
+
+.status-tag {
+  flex-shrink: 0;
+}
+
+.coupon-content {
+  padding: 8px 0;
+}
+
+.coupon-info {
+  margin-bottom: 4px;
+  font-size: 14px;
+  line-height: 1.5;
+  color: #666;
+}
+</style>

+ 165 - 320
src/pages/form/formStep3.vue

@@ -53,131 +53,77 @@
       </div>
     </view>
 
-    <wd-form ref="form" :model="model">
-      <!-- SN码 -->
-      <view class="bg-white px-4 py-4 mb-3 shadow-sm">
-        <view class="flex items-center mb-4">
-          <view class="text-lg font-bold text-gray-800">农机SN码</view>
-          <view class="ml-2 flex-1 h-px bg-gray-100"></view>
-        </view>
-        <div class="detail-box rounded-lg">
-          <wd-cell-group border>
-            <wd-input
-              label="SN码"
-              label-width="100px"
-              prop="ordersproductsn"
-              clearable
-              v-model="model.ordersproductsn"
-              placeholder="请输入SN码"
-              :rules="[{ required: true, message: '请填写SN码' }]"
-            />
-          </wd-cell-group>
-        </div>
-      </view>
-      <!-- 购机发票 -->
-      <view class="bg-white px-4 py-4 mb-3 shadow-sm">
-        <view class="flex items-center mb-4">
-          <view class="text-lg font-bold text-gray-800">购机发票</view>
-          <view class="ml-2 flex-1 h-px bg-gray-100"></view>
-        </view>
-        <div class="detail-box rounded-lg">
-          <wd-cell-group border>
-            <wd-upload
-              v-model:file-list="model.ordersinvoiceImgList"
-              image-mode="aspectFill"
-              :upload-method="
-                createCustomUpload({
-                  attmodel: 'orders_invoice',
-                  attpath: '/orders_invoice/',
-                  modelStats: 'ordersinvoiceImgIds',
-                })
-              "
-              :rules="[{ required: true, message: '请上传购机发票' }]"
-              :limit="1"
-              :before-upload="beforeUpload"
-              @remove="handleRemoveInvoice"
-            ></wd-upload>
-          </wd-cell-group>
-        </div>
-      </view>
-      <!-- 证件信息 -->
-      <view class="bg-white px-4 py-4 mb-3 shadow-sm">
-        <view class="flex items-center mb-4">
-          <view class="text-lg font-bold text-gray-800">证件信息</view>
-          <view class="ml-2 flex-1 h-px bg-gray-100"></view>
-        </view>
-        <div class="detail-box rounded-lg">
-          <wd-cell-group border>
-            <wd-upload
-              v-model:file-list="model.ordersidentityImgList"
-              image-mode="aspectFill"
-              :upload-method="
-                createCustomUpload({
-                  attmodel: 'orders_identity',
-                  attpath: '/orders_identity/',
-                  modelStats: 'ordersidentityImgIds',
-                })
-              "
-              :rules="[{ required: true, message: '请上传证件信息' }]"
-              :limit="2"
-              :before-upload="beforeUpload"
-              @remove="handleRemoveIdentity"
-            ></wd-upload>
-          </wd-cell-group>
-          <div class="tips mt-2">
-            <wd-text type="warning" :text="tipsText" v-if="userStore.isAuthComplete"></wd-text>
-          </div>
-        </div>
-      </view>
-    </wd-form>
-
     <!-- 优惠券卡片 -->
     <view class="bg-white px-4 py-4 shadow-sm">
       <view class="flex items-center mb-4">
         <view class="text-lg font-bold text-gray-800">可用优惠券</view>
         <view class="ml-2 flex-1 h-px bg-gray-100"></view>
       </view>
+      <!-- 优惠劵列表 -->
       <view class="coupon-box">
         <!-- 这里添加优惠券列表组件 -->
         <view v-if="!coupons?.length" class="text-center py-8 text-gray-400">暂无可用优惠券</view>
         <view v-else>
           <view class="coupon-list">
-            <wd-radio-group v-model="couponChoose" shape="dot" @change="couponRadioChange">
-              <wd-radio
-                :value="coupon.couponid"
-                v-for="coupon in couponsShow"
-                :key="coupon.couponid"
-                :disabled="coupon.canReceive"
-              >
+            <wd-radio-group v-model="couponChoose" shape="dot">
+              <wd-radio :value="coupon.couponid" v-for="coupon in coupons" :key="coupon.couponid">
                 <view class="flex items-center justify-between">
                   <view class="text-gray-800">{{ coupon.couponmc }}</view>
-                  <view class="flex items-center justify-end" @click.stop>
-                    <wd-button
-                      size="small"
-                      type="success"
-                      @click="handleReceive(coupon)"
-                      plain
-                      hairline
-                      :disabled="!coupon.canReceive"
-                    >
-                      {{ !coupon.canReceive ? '已领取' : '领取并使用' }}
-                    </wd-button>
-                  </view>
                 </view>
               </wd-radio>
             </wd-radio-group>
           </view>
         </view>
       </view>
+      <!-- 领取优惠劵 -->
+      <view class="mt-4 px-2">
+        <wd-button type="primary" plain block @tap="showPhoneDialog">领取优惠劵</wd-button>
+      </view>
     </view>
 
-    <!-- 提交按钮 -->
-    <view class="mt-2 pb-4 px-2">
-      <wd-button type="success" block @tap="handleSubmit" :loading="submitLoading">
-        提交订单
-      </wd-button>
+    <!-- 下单 -->
+    <!-- 立即下单按钮 -->
+    <view class="fixed-bottom bg-white p-4 border-t border-gray-100">
+      <wd-button type="primary" block size="large" @tap="handleOrder">立即下单</wd-button>
     </view>
 
+    <!-- 手机号输入弹窗 -->
+    <wd-popup
+      v-model="phoneDialogVisible"
+      round
+      :closable="true"
+      title="请输入手机号"
+      custom-class="w-[80%]"
+    >
+      <view class="phone-dialog p-6">
+        <view class="text-center mb-5">
+          <view class="text-lg font-semibold text-gray-800 mb-2">请输入您的手机号码</view>
+          <view class="text-sm text-gray-500">领取优惠券需要验证您的手机号</view>
+        </view>
+        <wd-input
+          v-model="phone"
+          placeholder="请输入您的手机号码"
+          clearable
+          :error="phoneError"
+          :error-message="phoneErrorMsg"
+          @blur="validatePhone"
+          required
+          class="mb-4"
+        />
+        <view class="mt-6">
+          <wd-button
+            type="success"
+            block
+            @click="handleSubmit"
+            :loading="submitLoading"
+            :disabled="submitLoading"
+          >
+            确认领取
+          </wd-button>
+        </view>
+      </view>
+    </wd-popup>
+
     <!-- loading -->
     <wd-overlay :show="overlayShow">
       <view class="wrapper w-full h-full flex items-center justify-center">
@@ -190,195 +136,112 @@
 <script lang="ts" setup>
 import { useProductStore } from '@/store/product'
 import { useCouponStore } from '@/store/coupon'
-import { useOrderStore } from '@/store/order'
-import { useAppStore } from '@/store/app'
-import { useUserStore } from '@/store/user'
 import { useMessage } from 'wot-design-uni'
-import { uploadFile } from '@/hooks/useUpload'
+import { useAppStore } from '@/store/app'
 
-const appStore = useAppStore()
-const userStore = useUserStore()
 const couponStore = useCouponStore()
 const productStore = useProductStore()
-const orderStore = useOrderStore()
+const appStore = useAppStore()
 const message = useMessage()
 const usersid = ref('') // 销售网点用户id(渠道id)
 const productsid = ref('') // 产品id
 const productInfo = ref<any>(null) // 产品详情
 const overlayShow = ref(false) // 加载状态
 const coupons = ref<any[]>([]) // 可用优惠券列表
-const currentCoupon2id = ref('') // 当前选中的优惠劵
-const coupon2idList = ref<any[]>([]) // 已领取的优惠券ID列表
-const couponsShow = ref<any[]>([]) // 展示的优惠券列表
 const couponChoose = ref('') // 选择的优惠券ID
 const submitLoading = ref(false) // 提交订单loading
-const model = reactive<{
-  ordersproductsn: string // SN码
-  ordersinvoiceImgIds: any[] // 购机发票ids
-  ordersinvoiceImgList: any[] // 购机发票列表
-  ordersidentityImgIds: any[] // 证件信息ids
-  ordersidentityImgList: any[] // 证件信息列表
-}>({
-  ordersproductsn: '',
-  ordersinvoiceImgIds: [],
-  ordersinvoiceImgList: [],
-  ordersidentityImgIds: [],
-  ordersidentityImgList: [],
-})
-const form = ref()
-const userstype = ref('') // 用户类型
-const tipsText = computed(() => {
-  if (userstype.value === '个人') {
-    return '请上传身份证正面和反面'
-  }
-  return '请上传营业执照'
-})
-
-function beforeUpload({ files, resolve }) {
-  // 判断是否完善了个人信息
-  if (!userStore.isAuthComplete) {
-    handleAuthComplete()
+const phoneDialogVisible = ref(false) // 手机号输入弹窗显示状态
+
+// 手机号相关
+const phone = ref('')
+const phoneError = ref(false)
+const phoneErrorMsg = ref('')
+
+// 显示手机号输入弹窗
+function showPhoneDialog() {
+  // 检查是否选择了优惠券
+  if (!couponChoose.value && coupons.value && coupons.value.length > 0) {
+    message.alert({
+      msg: '请选择优惠券',
+      title: '提示',
+    })
     return
   }
-  // console.log('files:', files)
-  resolve(true)
-}
-
-// 创建自定义上传方法
-function createCustomUpload({ attmodel, attpath, modelStats }) {
-  return async function (file, formData, options) {
-    const requestFromData = {
-      attmodel,
-      attpath,
-    }
 
-    // console.log('file:', file)
-    // console.log('formData:', formData)
-    // console.log('options:', options)
+  phoneDialogVisible.value = true
+}
 
-    const loading = ref(false)
-    const error = ref(false)
-    const data = ref<any>()
+// 验证手机号
+function validatePhone() {
+  if (!phone.value) {
+    phoneError.value = true
+    phoneErrorMsg.value = '请输入手机号码'
+    return false
+  }
 
-    await uploadFile({
-      tempFilePath: file.url,
-      formData: requestFromData,
-      data,
-      error,
-      loading,
-    })
+  const phoneReg = /^1[3-9]\d{9}$/
+  if (!phoneReg.test(phone.value)) {
+    phoneError.value = true
+    phoneErrorMsg.value = '请输入正确的手机号码'
+    return false
+  }
 
-    if (!error.value) {
-      options.onSuccess('', file, formData)
-      // {"Status":0,"Data":"2025041016044040627399","Message":"执行成功","OtherData":"https://dzapi.kdboss.cn/upfile/orders_invoice/20250410/2025041016044040731543.png"}
-      const dataObject = JSON.parse(data.value)
-      model[modelStats].push({
-        url: file.url,
-        id: dataObject.Data,
-      })
+  phoneError.value = false
+  phoneErrorMsg.value = ''
+  return true
+}
 
-      // console.log('model[modelStats]:', model)
-    }
+// 提交表单
+async function handleSubmit() {
+  // 验证手机号
+  if (!validatePhone()) {
+    return
   }
-}
 
-// 删除文件时,删除ids数组数据
-function handleRemoveInvoice({ file }) {
-  model.ordersinvoiceImgIds = model.ordersinvoiceImgIds.filter((item) => item.url !== file.url)
-}
+  submitLoading.value = true
 
-// 删除文件时,删除ids数组数据
-function handleRemoveIdentity({ file }) {
-  model.ordersidentityImgIds = model.ordersidentityImgIds.filter((item) => item.url !== file.url)
-}
+  // 获取选中的优惠券
+  const selectedCoupon = coupons.value.find((coupon) => coupon.couponid === couponChoose.value)
 
-// 完善资料提示
-function handleAuthComplete() {
-  message
-    .confirm({
-      msg: '您还未完善个人信息, 请先完善信息',
+  if (!selectedCoupon) {
+    message.alert({
+      msg: '请选择优惠券',
       title: '提示',
-      confirmButtonText: '去完善',
-      cancelButtonText: '继续浏览',
-    })
-    .then(() => {
-      uni.navigateTo({
-        url: '/pages-sub/user/index',
-      })
     })
-    .catch(() => {
-      // ...
-    })
-}
+    submitLoading.value = false
+    return
+  }
 
-// 提交表单
-function handleSubmit() {
-  // 判断是否完善了个人信息
-  if (!userStore.isAuthComplete) handleAuthComplete()
-
-  form.value.validate().then(async (res) => {
-    if (res.valid) {
-      // 判断是否选择了优惠券
-      if (couponsShow.value.length && !currentCoupon2id.value) {
-        message.alert({
-          msg: '当前有可用优惠券,请选择优惠券',
-          title: '提示',
-        })
-        return
-      }
-
-      // 是否上传了购机发票
-      if (!model.ordersinvoiceImgIds.length) {
-        message.alert({
-          msg: '请上传购机发票',
-          title: '提示',
-        })
-        return
-      }
-
-      // 是否上传了证件信息
-      if (!model.ordersidentityImgIds.length) {
-        message.alert({
-          msg: '请上传证件信息',
-          title: '提示',
-        })
-        return
-      }
-
-      try {
-        submitLoading.value = true
-        const invoiceFileIds = model.ordersinvoiceImgIds.map((item) => item.id)
-        const identityFileIds = model.ordersidentityImgIds.map((item) => item.id)
-
-        const res = await orderStore.submitOrder({
-          ordersproductid: productsid.value,
-          orderscouponid: currentCoupon2id.value,
-          ordersproductsn: model.ordersproductsn,
-          ordersuserid1: usersid.value, // 渠道id  魏哥:微信下单增加参数ordersuserid1对应渠道id,后端渠道看自己的订单数据相比管理员只需要另外新增菜单权限配置,额外固定配置参数ordersuserid1.value=session:workeruserid,界面都可以先套用,然后给渠道归类一个角色。
-          filesid: [...invoiceFileIds, ...identityFileIds].join(','),
-        })
+  try {
+    // 调用领取优惠券API
+    const result = await couponStore.receiveCoupon({
+      couponid: selectedCoupon.couponid,
+      coupon2phone: phone.value,
+      coupon2productids: productsid.value,
+    })
 
-        message
-          .alert({
-            msg: '提交成功',
-            title: '提示',
-          })
-          .then(() => {
-            uni.navigateTo({ url: '/pages/order/index' })
-          })
-          .finally(() => {
-            submitLoading.value = false
-          })
-      } catch (error) {
-        message.alert({
-          msg: '提交失败',
-          title: '提示',
-        })
-      } finally {
-        submitLoading.value = false
-      }
+    if (result && result.Success) {
+      message.alert({
+        msg: '优惠券领取成功,请注意查收短信',
+        title: '提示',
+      })
+      phoneDialogVisible.value = false // 关闭弹窗
+    } else {
+      message.alert({
+        msg: result?.Message || '领取失败,请稍后再试',
+        title: '提示',
+      })
     }
-  })
+
+    submitLoading.value = false
+  } catch (error) {
+    console.error('领取优惠券出错:', error)
+    message.alert({
+      msg: '操作失败',
+      title: '提示',
+    })
+    submitLoading.value = false
+  }
 }
 
 // 获取产品详情
@@ -392,73 +255,55 @@ async function getProductDetail() {
   overlayShow.value = false
 }
 
-// 获取可用优惠券列表
+// 获取优惠券列表
 async function getCouponList() {
   if (!productsid.value) return
   coupons.value = await couponStore.getCouponList(productsid.value)
+  // 如果有可用优惠券,默认选中第一个
+  if (coupons.value && coupons.value.length > 0) {
+    couponChoose.value = coupons.value[0].couponid
+  }
 }
 
-// 领取优惠券
-async function handleReceive(coupon: any) {
-  overlayShow.value = true
-  await couponStore.receiveCoupon(coupon.couponid)
-  overlayShow.value = false
-
-  message.alert({
-    msg: '领取成功',
-    title: '提示',
-  })
-
-  // 重新获取优惠券列表状态
-  await getCouponsShow()
-}
-
-// 获取该用户下,已领取的优惠劵id,用于判断是否可以领取
-async function getCoupon2id() {
-  const { Data } = await couponStore.getCoupon2id({
-    coupon2userid: appStore.appInfo.userid,
-    coupon2isused: 0,
-    coupon2productids: productsid.value,
-  })
-
-  coupon2idList.value = Data
-}
-
-// 优惠券选择
-async function couponRadioChange(e: any) {
-  currentCoupon2id.value = couponsShow.value.find((item) => item.couponid === e.value)?.coupon2code
-}
-
-// 获取可展示的优惠券列表
-function getCouponsShow() {
-  Promise.all([
-    getCouponList(), // 获取可用优惠券列表
-    getCoupon2id(), // 获取该用户下,已领取的优惠劵id,用于判断是否可以领取
-  ]).then(() => {
-    couponsShow.value = coupons.value.map((coupon) => {
-      // 找到匹配的已领取优惠券记录
-      const receivedCoupon = coupon2idList.value.find(
-        (item) => item.coupon2coupon1id === coupon.couponid,
-      )
-
-      return {
-        ...coupon,
-        canReceive: !receivedCoupon, // 是否可以领取
-        coupon2sid: receivedCoupon?.coupon2sid, // 优惠券id
-        coupon2code: receivedCoupon?.coupon2code, // 优惠券码
-      }
+// 立即下单
+function handleOrder() {
+  // 检查登录状态
+  if (!appStore.isLoginState) {
+    // 未登录状态,显示确认框
+    message
+      .confirm({
+        title: '提示',
+        msg: '请先登录后再进行下单操作',
+        confirmButtonText: '去登录',
+        cancelButtonText: '取消',
+      })
+      .then(() => {
+        // 用户点击确认,跳转到登录页面
+        // 保存当前页面路径用于登录后回跳
+        const currentPage = getCurrentPages()[getCurrentPages().length - 1].route
+        const queryParams = `?usersid=${usersid.value}&productsid=${productsid.value}`
+        const redirectPath = `/${currentPage}${queryParams}`
+
+        uni.navigateTo({
+          url: `/pages-sub/auth/index?redirect=${encodeURIComponent(redirectPath)}`,
+        })
+      })
+      .catch(() => {
+        // 取消操作,不做任何处理
+      })
+  } else {
+    // 已登录状态,跳转到订单确认页面
+    uni.navigateTo({
+      url: `/pages/form/formStep4?usersid=${usersid.value}&productsid=${productsid.value}`,
     })
-  })
+  }
 }
 
 onLoad(async (query) => {
   usersid.value = query.usersid
   productsid.value = query.productsid
   getProductDetail() // 获取产品详情
-  getCouponsShow() // 获取可展示的优惠券列表
-
-  // 获取用户类型
-  userstype.value = await userStore.getUserType() // 个人/企业
+  getCouponList() // 获取优惠券列表
 })
 </script>
 
@@ -468,7 +313,7 @@ onLoad(async (query) => {
 }
 
 .detail-box {
-  :deep(.wd-cell) {
+  :deep(.wd-cell__wrapper) {
     // @apply py-2;
   }
 
@@ -477,7 +322,7 @@ onLoad(async (query) => {
   }
 
   :deep(.wd-cell__value) {
-    @apply text-gray-800 text-sm;
+    @apply text-gray-800 text-sm whitespace-nowrap;
   }
 }
 

+ 419 - 34
src/pages/form/formStep4.vue

@@ -1,71 +1,456 @@
 <route lang="json5">
 {
   style: {
-    navigationBarTitleText: '填写sn号',
+    navigationBarTitleText: '确认下单',
   },
 }
 </route>
 
 <template>
   <!-- 选网点,选择机器型号,输入SN号,下订单 -->
-  <view class="bg-white min-h-screen px-4 py-4">
-    <view class="space-y-4">
-      <!-- 输入SN号 -->
-      <view class="form-item">
-        <text class="form-label">SN号</text>
-        <wd-input v-model="formData.sn" placeholder="请输入SN号" />
+  <view class="bg-gray-100 min-h-screen pb-6">
+    <!-- 步骤1:优惠码输入 -->
+    <view class="bg-white px-4 py-4 mb-3 shadow-sm">
+      <view class="flex items-center mb-4">
+        <view class="text-lg font-bold text-gray-800">输入优惠码</view>
+        <view class="ml-2 flex-1 h-px bg-gray-100"></view>
       </view>
 
-      <!-- 提交按钮 -->
-      <view class="mt-8 pb-8">
-        <wd-button type="success" block @tap="handleSubmit">提交申请</wd-button>
+      <wd-cell-group>
+        <!-- 优惠码 -->
+        <wd-input
+          label="优惠码"
+          v-model="formData.couponCode"
+          placeholder="请输入优惠码"
+          clearable
+          required
+          :error="couponError"
+          :error-message="couponErrorMsg"
+          @blur="validateCoupon"
+          right-icon="warning"
+          @click-right-icon="handleCouponInfo"
+        />
+      </wd-cell-group>
+
+      <view class="mt-4">
+        <wd-button type="primary" block @tap="verifyCouponCode" :loading="verifyLoading">
+          验证优惠码
+        </wd-button>
       </view>
     </view>
+
+    <!-- 步骤2:显示产品信息和其他输入字段(仅在优惠码验证成功后显示) -->
+    <template v-if="isVerified && productInfo">
+      <!-- 产品信息 -->
+      <view v-if="productInfo" class="bg-white px-4 py-4 mb-3 shadow-sm">
+        <view class="flex items-center mb-4">
+          <view class="text-lg font-bold text-gray-800">产品信息</view>
+          <view class="ml-2 flex-1 h-px bg-gray-100"></view>
+        </view>
+
+        <view class="product-detail">
+          <view class="product-title text-xl font-bold text-gray-800 mb-3">
+            {{ productInfo.productsname }}
+          </view>
+          <view class="text-gray-600 mb-1">生产企业: {{ productInfo.scqyinfomc }}</view>
+          <view class="text-gray-600 mb-1">机具类型: {{ productInfo.productsjjlx }}</view>
+          <view class="text-gray-600 mb-1">产品型号: {{ productInfo.productsmodel }}</view>
+        </view>
+      </view>
+
+      <!-- 表单信息 -->
+      <view class="bg-white px-4 py-4 mb-3 shadow-sm">
+        <view class="flex items-center mb-4">
+          <view class="text-lg font-bold text-gray-800">订单信息</view>
+          <view class="ml-2 flex-1 h-px bg-gray-100"></view>
+        </view>
+
+        <wd-cell-group>
+          <!-- SN号 -->
+          <wd-input
+            label="SN码"
+            v-model="formData.sn"
+            placeholder="请输入SN码"
+            clearable
+            required
+            :error="snError"
+            :error-message="snErrorMsg"
+            @blur="validateSn"
+          />
+
+          <!-- 手机号 -->
+          <wd-input
+            label="手机号"
+            v-model="formData.phone"
+            placeholder="请输入手机号"
+            clearable
+            required
+            :error="phoneError"
+            :error-message="phoneErrorMsg"
+            @blur="validatePhone"
+          />
+        </wd-cell-group>
+      </view>
+
+      <!-- 证件上传 -->
+      <view class="bg-white px-4 py-4 mb-3 shadow-sm">
+        <view class="flex items-center mb-4">
+          <view class="text-lg font-bold text-gray-800">证件上传</view>
+          <view class="ml-2 flex-1 h-px bg-gray-100"></view>
+        </view>
+
+        <view class="mb-4">
+          <view class="text-gray-700 mb-2 text-base flex items-center">
+            <text>证件类型</text>
+            <text class="text-red-500 ml-1">*</text>
+          </view>
+          <wd-radio-group v-model="formData.idType" shape="button">
+            <wd-radio value="personal">个人身份证</wd-radio>
+            <wd-radio value="business">企业营业执照</wd-radio>
+          </wd-radio-group>
+        </view>
+
+        <!-- 个人身份证上传 -->
+        <view v-if="formData.idType === 'personal'" class="mb-4">
+          <view class="text-gray-700 mb-2 text-base flex items-center">
+            <text>身份证照片</text>
+            <text class="text-red-500 ml-1">*</text>
+          </view>
+          <UploadComponent
+            v-model="formData.idCardFront"
+            v-model:fileIds="formData.idCardFrontIds"
+            :limit="2"
+            attmodel="orders_identity"
+            attpath="/orders_identity/"
+            modelStats="idCardFrontIds"
+            message="请上传身份证照片"
+            tips="请上传身份证正面和反面"
+          />
+        </view>
+
+        <!-- 营业执照上传 -->
+        <view v-if="formData.idType === 'business'" class="mb-4">
+          <view class="text-gray-700 mb-2 text-base flex items-center">
+            <text>营业执照照片</text>
+            <text class="text-red-500 ml-1">*</text>
+          </view>
+          <UploadComponent
+            v-model="formData.businessLicense"
+            v-model:fileIds="formData.businessLicenseIds"
+            :limit="1"
+            attmodel="orders_identity"
+            attpath="/orders_identity/"
+            modelStats="businessLicenseIds"
+            message="请上传营业执照照片"
+          />
+        </view>
+
+        <!-- 购买人照片上传 (可选) -->
+        <view class="mb-4">
+          <view class="text-gray-700 mb-2 text-base flex items-center">
+            <text>购买人照片</text>
+            <text class="text-gray-400 ml-1">(可选)</text>
+          </view>
+          <UploadComponent
+            v-model="formData.buyerPhoto"
+            v-model:fileIds="formData.buyerPhotoIds"
+            :limit="1"
+            :required="false"
+            attmodel="orders_identity"
+            attpath="/orders_identity/"
+            modelStats="buyerPhotoIds"
+            message="请上传购买人照片"
+          />
+        </view>
+      </view>
+
+      <!-- 提交按钮 -->
+      <view class="px-4 pt-4 pb-12">
+        <wd-button type="primary" block size="large" @tap="handleSubmit" :loading="submitLoading">
+          确认下单
+        </wd-button>
+      </view>
+    </template>
+
+    <!-- loading -->
+    <wd-overlay :show="overlayShow">
+      <view class="wrapper w-full h-full flex items-center justify-center">
+        <wd-loading />
+      </view>
+    </wd-overlay>
   </view>
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue'
+import { ref, reactive, computed } from 'vue'
 import { useUserStore } from '@/store/user'
 import { useMessage } from 'wot-design-uni'
+import { useProductStore } from '@/store/product'
+import { useOrderStore } from '@/store/order'
+import { useCouponStore } from '@/store/coupon'
+import UploadComponent from '@/components/UploadComponent.vue'
 
 const message = useMessage()
 const userStore = useUserStore()
-const formData = ref({
-  sn: '',
+const productStore = useProductStore()
+const orderStore = useOrderStore()
+const couponStore = useCouponStore()
+
+// 页面参数
+const usersid = ref('') // 销售网点用户id(渠道id)
+const productsid = ref('') // 产品id
+const productInfo = ref<any>(null) // 产品详情
+const overlayShow = ref(false) // 加载状态
+const submitLoading = ref(false) // 提交订单loading
+const verifyLoading = ref(false) // 验证优惠码loading
+const isVerified = ref(false) // 优惠码是否已验证
+
+// 表单数据
+const formData = reactive({
+  sn: '', // SN码
+  phone: '', // 手机号
+  couponCode: '', // 优惠码
+  idType: 'personal', // 证件类型:personal - 个人身份证,business - 营业执照
+
+  // 身份证正反面
+  idCardFront: [], // 身份证照片列表
+  idCardFrontIds: [], // 身份证照片ID列表
+
+  // 营业执照
+  businessLicense: [], // 营业执照照片列表
+  businessLicenseIds: [], // 营业执照照片ID列表
+
+  // 购买人照片(可选)
+  buyerPhoto: [], // 购买人照片列表
+  buyerPhotoIds: [], // 购买人照片ID列表
 })
 
-// 提交表单
-function handleSubmit() {
-  // ...
+// 表单验证状态
+const snError = ref(false)
+const snErrorMsg = ref('')
+const phoneError = ref(false)
+const phoneErrorMsg = ref('')
+const couponError = ref(false)
+const couponErrorMsg = ref('')
+
+// 验证SN码
+function validateSn() {
+  if (!formData.sn) {
+    snError.value = true
+    snErrorMsg.value = '请输入SN码'
+    return false
+  }
+
+  snError.value = false
+  snErrorMsg.value = ''
+  return true
+}
+
+// 验证手机号
+function validatePhone() {
+  if (!formData.phone) {
+    phoneError.value = true
+    phoneErrorMsg.value = '请输入手机号码'
+    return false
+  }
+
+  const phoneReg = /^1[3-9]\d{9}$/
+  if (!phoneReg.test(formData.phone)) {
+    phoneError.value = true
+    phoneErrorMsg.value = '请输入正确的手机号码'
+    return false
+  }
+
+  phoneError.value = false
+  phoneErrorMsg.value = ''
+  return true
 }
 
-onShow(() => {
-  if (!userStore.isAuthComplete) {
-    message
-      .confirm({
-        msg: '您还未完善个人信息, 无法享受优惠劵活动, 点击按钮完善信息',
+// 验证优惠码
+function validateCoupon() {
+  if (!formData.couponCode) {
+    couponError.value = true
+    couponErrorMsg.value = '请输入优惠码'
+    return false
+  }
+
+  couponError.value = false
+  couponErrorMsg.value = ''
+  return true
+}
+
+// 验证优惠码并获取产品信息
+async function verifyCouponCode() {
+  if (!validateCoupon()) return
+
+  try {
+    verifyLoading.value = true
+    overlayShow.value = true
+
+    const result = await couponStore.getCoupon2ViewmList({
+      coupon2code: formData.couponCode,
+    })
+
+    if (result && result.Data && result.Data.length > 0) {
+      // 获取优惠券对应的产品信息
+      const couponProduct = result.Data[0]
+
+      // 设置产品ID
+      productsid.value = couponProduct.coupon2productids || ''
+
+      // 获取产品详细信息
+      await getProductDetail()
+
+      // 填充手机号码(优先使用优惠券中的手机号,如果没有则使用用户当前手机号)
+      if (couponProduct.coupon2phone) {
+        formData.phone = couponProduct.coupon2phone
+      } else if (userStore.userDetail?.usersphone) {
+        formData.phone = userStore.userDetail.usersphone
+      }
+
+      isVerified.value = true
+      message.alert({
+        title: '成功',
+        msg: '优惠码验证成功',
+      })
+    } else {
+      message.alert({
         title: '提示',
-        confirmButtonText: '去完善',
-        cancelButtonText: '我还在考虑',
+        msg: '无效的优惠码,请检查后重试',
       })
-      .then(() => {
-        uni.navigateTo({
-          url: '/pages-sub/user/index',
-        })
+    }
+  } catch (error) {
+    console.error('验证优惠码出错:', error)
+    message.alert({
+      title: '提示',
+      msg: '验证优惠码失败,请稍后再试',
+    })
+  } finally {
+    verifyLoading.value = false
+    overlayShow.value = false
+  }
+}
+
+// 处理优惠码信息提示
+function handleCouponInfo() {
+  message.alert({
+    title: '优惠码说明',
+    msg: '请输入您收到的优惠码,验证成功后可以查看产品信息并下单',
+  })
+}
+
+// 验证表单
+function validateForm() {
+  if (!validateSn()) return false
+  if (!validatePhone()) return false
+
+  // 验证证件上传
+  if (formData.idType === 'personal' && !formData.idCardFrontIds.length) {
+    message.alert({
+      title: '提示',
+      msg: '请上传身份证照片',
+    })
+    return false
+  }
+
+  if (formData.idType === 'business' && !formData.businessLicenseIds.length) {
+    message.alert({
+      title: '提示',
+      msg: '请上传营业执照照片',
+    })
+    return false
+  }
+
+  return true
+}
+
+// 提交表单
+async function handleSubmit() {
+  // 表单验证
+  if (!validateForm()) return
+
+  try {
+    submitLoading.value = true
+
+    // 收集所有上传文件的ID
+    const allFileIds = [
+      ...(formData.idType === 'personal' ? formData.idCardFrontIds : formData.businessLicenseIds),
+      ...formData.buyerPhotoIds,
+    ]
+
+    // 提取ID字段
+    const fileIdsString = allFileIds.map((item) => item.id).join(',')
+
+    // 准备提交数据
+    const orderData = {
+      ordersproductid: productsid.value, // 产品id
+      orderscouponid: formData.couponCode, // 优惠码
+      ordersproductsn: formData.sn, // SN号
+      ordersuserid1: usersid.value, // 销售网点id
+      ordersphone: formData.phone, // 手机号
+      filesid: fileIdsString, // 文件ID,已获取
+    }
+
+    console.log('提交订单数据:', orderData)
+
+    // 调用下单接口
+    const result = await orderStore.submitOrder(orderData)
+
+    if (result && result.Status === 0) {
+      message.alert({
+        title: '提示',
+        msg: '下单成功',
       })
-      .catch(() => {
-        uni.navigateBack()
+
+      // 下单成功后跳转到订单列表页
+      setTimeout(() => {
+        uni.reLaunch({
+          url: '/pages/order/index',
+        })
+      }, 1500)
+    } else {
+      message.alert({
+        title: '提示',
+        msg: result?.Message || '下单失败,请稍后再试',
       })
+    }
+  } catch (error) {
+    console.error('下单出错:', error)
+    message.alert({
+      title: '提示',
+      msg: '下单失败,请稍后再试',
+    })
+  } finally {
+    submitLoading.value = false
   }
+}
+
+// 获取产品详情
+async function getProductDetail() {
+  if (!productsid.value) return
+  overlayShow.value = true
+  productInfo.value = await productStore.getProductDetail({
+    productsid: productsid.value,
+    merchantid: usersid.value,
+  })
+  overlayShow.value = false
+}
+
+// 页面加载
+onLoad(async (query) => {
+  usersid.value = query.usersid || ''
 })
 </script>
 
 <style lang="scss" scoped>
-.form-item {
-  @apply mb-6;
-  .form-label {
-    @apply block text-gray-700 mb-2 text-base;
-  }
+.product-title {
+  @apply leading-relaxed break-all;
+}
+
+.wrapper {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  height: 100%;
 }
 </style>

+ 0 - 42
src/pages/index/index.vue

@@ -152,16 +152,11 @@
 import banner1 from '@/static/banner1.png'
 import { ref } from 'vue'
 import { useUserStore } from '@/store/user'
-import { useAppStore } from '@/store/app'
 import { useDictStore } from '@/store/dict'
-import { useMessage } from 'wot-design-uni'
-import dayjs from 'dayjs'
 import StatisticsCard from '@/components/StatisticsCard.vue'
 
 const userStore = useUserStore()
-const appStore = useAppStore()
 const dictStore = useDictStore()
-const message = useMessage()
 
 const statisticsDataShow = ref(false)
 const current = ref<number>(0)
@@ -250,42 +245,6 @@ function handleApply() {
   })
 }
 
-uni.$on('authComplete', function (data) {
-  if (data.result) {
-    showCompleteDialog()
-  }
-})
-
-// 入口完善信息提示
-const showCompleteDialog = () => {
-  // console.log(appStore.isLogined, userStore.isAuthComplete, appStore.needAlertCompleteInDays)
-
-  // 如果已登录且已完善信息,则不提示
-  if (appStore.isLogined && userStore.isAuthComplete) return
-
-  // 如果需要提示完善信息,并且N天内没有提示过,则提示
-  if (appStore.needAlertCompleteInDays) {
-    message
-      .confirm({
-        msg: '您还未完善个人信息, 无法享受优惠劵活动, 点击按钮完善信息',
-        title: '提示',
-        confirmButtonText: '去完善',
-        cancelButtonText: '继续浏览',
-      })
-      .then(() => {
-        uni.navigateTo({
-          url: '/pages-sub/user/index',
-        })
-      })
-      .catch(() => {
-        appStore.setAlertCompleteInfo({
-          hasAlert: true,
-          time: dayjs().format('YYYY-MM-DD HH:mm:ss'),
-        })
-      })
-  }
-}
-
 // 获取统计数据
 const getStatisticsData = async () => {
   const configList = await dictStore.getConfigList('mini_program_index_sumary')
@@ -306,7 +265,6 @@ const getStatisticsDataShow = async () => {
 
 onShow(() => {
   getUserList()
-  showCompleteDialog()
   getStatisticsData()
   getStatisticsDataShow()
 })

+ 210 - 0
src/pages/merchant/index.vue

@@ -0,0 +1,210 @@
+<route lang="json5" type="page">
+{
+  layout: 'default',
+  style: {
+    navigationBarTitleText: '我的商家',
+  },
+}
+</route>
+
+<template>
+  <view class="bg-white min-h-screen p-4">
+    <!-- 页面标题 -->
+    <view class="text-xl font-bold text-center py-3">农机销售网点</view>
+
+    <!-- 搜索框 -->
+    <view class="mb-4">
+      <wd-search
+        placeholder="搜索网点名称"
+        cancel-txt="搜索"
+        @search="handleSearch"
+        @clear="handleClear"
+        @change="handleSearchChange"
+      />
+    </view>
+
+    <!-- 网点列表 -->
+    <view v-if="filteredLocationList.length > 0">
+      <view v-for="(item, index) in filteredLocationList" :key="index" class="merchant-card">
+        <view class="merchant-header">
+          <text class="merchant-name">{{ item.name }}</text>
+          <wd-tag type="success" custom-class="merchant-tag">推荐</wd-tag>
+        </view>
+
+        <view class="merchant-content">
+          <view class="merchant-info-item">
+            <wd-icon name="location" class="text-green-500" />
+            <text class="merchant-info-text">{{ item.address }}</text>
+          </view>
+
+          <view class="merchant-info-item mt-2">
+            <wd-icon name="phone" class="text-green-500" />
+            <text class="merchant-info-text">{{ item.phone }}</text>
+          </view>
+        </view>
+
+        <view class="merchant-footer">
+          <wd-button
+            type="success"
+            size="small"
+            @tap="handleCall(item.phone)"
+            custom-class="merchant-btn"
+          >
+            拨打电话
+          </wd-button>
+
+          <wd-button
+            type="primary"
+            size="small"
+            @tap="handleGoods(item)"
+            custom-class="merchant-btn"
+          >
+            立即申请
+          </wd-button>
+        </view>
+      </view>
+    </view>
+
+    <!-- 暂无数据 -->
+    <view v-else>
+      <wd-status-tip image="comment" tip="暂无网点数据" />
+    </view>
+
+    <!-- loading -->
+    <wd-overlay :show="loading">
+      <view class="wrapper w-full h-full flex items-center justify-center">
+        <wd-loading />
+      </view>
+    </wd-overlay>
+  </view>
+</template>
+
+<script lang="ts" setup>
+import { ref, computed } from 'vue'
+import { useUserStore } from '@/store/user'
+import { useThrottleFn } from '@vueuse/core'
+
+const userStore = useUserStore()
+const loading = ref(false)
+const locationList = ref([])
+const searchValue = ref('')
+
+// 筛选后的网点列表
+const filteredLocationList = computed(() => {
+  if (!searchValue.value) return locationList.value
+
+  return locationList.value.filter(
+    (item) => item.name.includes(searchValue.value) || item.address.includes(searchValue.value),
+  )
+})
+
+// 获取销售点列表
+const getUserList = async () => {
+  loading.value = true
+  try {
+    const userList = await userStore.getSellUserList()
+
+    locationList.value = userList.map((item: any) => ({
+      name: item.usersname,
+      address: item.usersaddress,
+      phone: item.usersphone,
+      usersid: item.usersid,
+    }))
+  } catch (error) {
+    console.error('获取网点列表失败', error)
+    uni.showToast({
+      title: '获取网点列表失败',
+      icon: 'none',
+    })
+  } finally {
+    loading.value = false
+  }
+}
+
+// 搜索相关
+function handleSearch(e: any) {
+  searchValue.value = e.value
+}
+
+function handleClear() {
+  searchValue.value = ''
+}
+
+const handleSearchChange = useThrottleFn((e: any) => {
+  searchValue.value = e.value
+}, 300)
+
+// 处理拨打电话
+function handleCall(phone: string) {
+  uni.makePhoneCall({
+    phoneNumber: phone,
+  })
+}
+
+// 跳转到申请页面
+function handleGoods(location: any) {
+  uni.navigateTo({
+    url: `/pages/form/formStep2?usersid=${location.usersid}`,
+  })
+}
+
+onLoad(() => {
+  getUserList()
+})
+</script>
+
+<style lang="scss" scoped>
+.merchant-card {
+  padding: 16px;
+  margin-bottom: 16px;
+  background-color: #fff;
+  border-radius: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+}
+
+.merchant-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding-bottom: 12px;
+  border-bottom: 1px solid #f5f5f5;
+}
+
+.merchant-name {
+  font-size: 18px;
+  font-weight: 600;
+  color: #333;
+}
+
+.merchant-tag {
+  flex-shrink: 0;
+}
+
+.merchant-content {
+  padding: 12px 0;
+}
+
+.merchant-info-item {
+  display: flex;
+  align-items: flex-start;
+  margin-bottom: 8px;
+}
+
+.merchant-info-text {
+  margin-left: 8px;
+  font-size: 14px;
+  line-height: 1.5;
+  color: #666;
+}
+
+.merchant-footer {
+  display: flex;
+  gap: 12px;
+  justify-content: flex-end;
+  margin-top: 8px;
+}
+
+.merchant-btn {
+  min-width: 90px;
+}
+</style>

+ 41 - 7
src/pages/mine/index.vue

@@ -25,8 +25,16 @@
           <wd-icon name="user-avatar" size="30" class="text-gray-100" />
         </view> -->
         <view class="ml-4 text-white">
-          <view class="text-xl font-bold">
-            {{ isLogin ? userDetail.usersname || '微信用户' : '未登录' }}
+          <view class="text-xl font-bold flex">
+            <text>{{ isLogin ? userDetail.usersname || '微信用户' : '未登录' }}</text>
+            <view v-if="!isLogin" class="ml-2">
+              <button
+                class="bg-white text-blue-400 text-sm px-3 py-1 rounded-full"
+                @click="handleLogin"
+              >
+                立即登录
+              </button>
+            </view>
           </view>
           <view class="text-sm mt-1 opacity-90" v-if="!isLogin">当前游客身份</view>
         </view>
@@ -36,7 +44,7 @@
     <!-- 菜单列表 -->
     <view class="px-4 py-2">
       <!-- 我的资料 -->
-      <view class="menu-item" @click="handleNavigate('/pages-sub/user/index')">
+      <view class="menu-item" @click="handleToUserInfo">
         <view class="flex items-center">
           <wd-icon name="user" class="text-gray-500" />
           <text class="ml-3">我的资料</text>
@@ -45,7 +53,7 @@
       </view>
 
       <!-- 优惠劵 -->
-      <view class="menu-item" @click="handleNavigate('/pages/settings/index')">
+      <view class="menu-item" @click="handleToAuthPage('/pages/coupon/index')">
         <view class="flex items-center">
           <wd-icon name="creditcard" class="text-gray-500" />
           <text class="ml-3">优惠劵</text>
@@ -54,7 +62,7 @@
       </view>
 
       <!-- 我的商家 -->
-      <view class="menu-item" @click="handleNavigate('/pages/settings/index')">
+      <view class="menu-item" @click="handleToAuthPage('/pages/merchant/index')">
         <view class="flex items-center">
           <wd-icon name="usergroup" class="text-gray-500" />
           <text class="ml-3">我的商家</text>
@@ -63,7 +71,7 @@
       </view>
 
       <!-- 我的订单 -->
-      <view class="menu-item" @click="handleNavigate('/pages/order/index')">
+      <view class="menu-item" @click="handleToAuthPage('/pages/order/index')">
         <view class="flex items-center">
           <wd-icon name="a-rootlist" class="text-gray-500" />
           <text class="ml-3">我的订单</text>
@@ -104,7 +112,7 @@ const appStore = useAppStore()
 // 用户信息
 const userDetail = ref<Partial<UserType>>({})
 
-const isLogin = computed(() => appStore.isLogined)
+const isLogin = computed(() => appStore.isLoginState)
 
 onMounted(() => {
   userDetail.value = userStore.userDetail
@@ -115,6 +123,32 @@ onMounted(() => {
 function handleNavigate(url: string) {
   uni.navigateTo({ url })
 }
+
+// 跳转至用户信息页面
+function handleToUserInfo() {
+  if (appStore.isLoginState) {
+    uni.navigateTo({ url: '/pages-sub/user/index' })
+  } else {
+    appStore.alertLogin()
+  }
+}
+
+// 立即登录
+function handleLogin() {
+  const currentPage = getCurrentPages()[getCurrentPages().length - 1].route
+  // 确保路径正确
+  const redirectPath = currentPage.startsWith('/') ? currentPage : `/${currentPage}`
+  uni.navigateTo({ url: `/pages-sub/auth/index?redirect=${encodeURIComponent(redirectPath)}` })
+}
+
+// 跳转至授权页面
+function handleToAuthPage(url: string) {
+  if (appStore.isLoginState) {
+    uni.navigateTo({ url })
+  } else {
+    appStore.alertLogin()
+  }
+}
 </script>
 
 <style lang="scss" scoped>

+ 97 - 5
src/pages/order/detail.vue

@@ -16,16 +16,29 @@
       <wd-cell title="订单总金额" :value="`¥${orderDetail.orderstotalprice}`" />
       <wd-cell title="优惠金额" :value="`¥${orderDetail.ordersyhprice}`" />
       <wd-cell title="实付金额" :value="`¥${orderDetail.ordersdiscountprice}`" />
-      <wd-cell title="订单状态" :value="getOrderStatus(orderDetail.ordersorderstatus)" />
+      <wd-cell title="订单状态">
+        <template #default>
+          <wd-tag :type="getOrderStatusType(orderDetail.ordersorderstatus)">
+            {{ getOrderStatus(orderDetail.ordersorderstatus) }}
+          </wd-tag>
+        </template>
+      </wd-cell>
       <wd-cell title="下单时间" :value="orderDetail.ordersorderdate" />
-      <wd-cell title="发票图片">
-        <view class="flex flex-wrap">
+      <wd-cell title="发票信息">
+        <!-- 发票信息展示 -->
+        <view class="flex flex-wrap" v-if="invoiceImg.length">
           <view class="flex-1" v-for="item in invoiceImg" :key="item.id">
             <wd-img :width="100" :height="100" :src="item.url" :enable-preview="true" />
           </view>
         </view>
+        <!-- 发票信息上传按钮 -->
+        <view class="flex flex-wrap justify-end" v-else>
+          <wd-button type="primary" size="small" @click="showUploadDialog = true">
+            上传发票
+          </wd-button>
+        </view>
       </wd-cell>
-      <wd-cell title="证件信息图片">
+      <wd-cell title="证件信息">
         <view class="flex flex-wrap">
           <view class="flex-1" v-for="item in identityImg" :key="item.id">
             <wd-img :width="100" :height="100" :src="item.url" :enable-preview="true" />
@@ -38,18 +51,63 @@
     <view class="sticky-box-content w-full box-border px-2 pb-2 mt-4">
       <wd-button type="primary" block @click="handleBack">回到订单列表</wd-button>
     </view>
+
+    <!-- 上传发票弹窗 -->
+    <wd-popup
+      v-model="showUploadDialog"
+      round
+      closable
+      custom-class="upload-popup w-[80%]"
+      class="w-[80%]"
+    >
+      <view class="p-5">
+        <view class="text-lg font-medium text-center mb-5">上传发票</view>
+        <view class="upload-container mb-6">
+          <UploadComponent
+            v-model="invoiceUpload.fileList"
+            v-model:fileIds="invoiceUpload.fileIds"
+            :limit="1"
+            :attlsh="currentOrdersId"
+            attmodel="orders_invoice"
+            attpath="/orders_invoice/"
+            modelStats="invoiceFileIds"
+            message="请上传发票照片"
+          />
+          <view class="text-xs text-gray-500 mt-2 text-center">
+            上传清晰的发票照片,以便于核对信息
+          </view>
+          <!-- 如果上传成功,展示上传成功的文字 -->
+          <view v-if="invoiceUpload.fileIds.length" class="text-center text-green-500 mt-2">
+            上传成功
+          </view>
+        </view>
+        <view class="px-2">
+          <wd-button type="primary" block @click="handleConfirmUpload">确认</wd-button>
+        </view>
+      </view>
+    </wd-popup>
   </view>
 </template>
 
 <script lang="ts" setup>
 import { useOrderStore } from '@/store/order'
 import { useFileStore } from '@/store/file'
+import { useMessage } from 'wot-design-uni'
+import UploadComponent from '@/components/UploadComponent.vue'
 import type { OrderType } from '@/types/order'
 
 const orderStore = useOrderStore()
 const fileStore = useFileStore()
+const message = useMessage()
 const currentOrdersId = ref('')
 const orderDetail = ref<OrderType>({} as OrderType)
+const showUploadDialog = ref(false)
+
+// 上传发票文件相关
+const invoiceUpload = reactive({
+  fileList: [],
+  fileIds: [],
+})
 
 // 获取订单详情
 const getOrderDetail = async () => {
@@ -67,6 +125,16 @@ const getOrderStatus = (status: number) => {
   return statusMap[status] || '未知状态'
 }
 
+// 获取订单状态对应的标签类型
+const getOrderStatusType = (status: number) => {
+  const typeMap = {
+    0: 'warning', // 待审核 - 黄色警告色
+    1: 'success', // 已支付 - 绿色成功色
+    2: 'primary', // 已完成 - 蓝色主要色
+  }
+  return typeMap[status] || 'danger' // 未知状态使用红色危险色
+}
+
 // 回到订单列表
 function handleBack() {
   uni.navigateTo({
@@ -76,6 +144,7 @@ function handleBack() {
 
 const invoiceImg = ref([]) // 发票图片
 const identityImg = ref([]) // 证件信息图片
+
 // 获取发票图片附件
 const getInvoiceImg = async () => {
   const res = await fileStore.getAttachmentList({
@@ -94,6 +163,17 @@ const getIdentityImg = async () => {
   identityImg.value = res
 }
 
+// 确认上传并关闭弹窗
+function handleConfirmUpload() {
+  showUploadDialog.value = false
+  // 重新请求订单详情和发票图片
+  getOrderDetail()
+  getInvoiceImg()
+  // 清空上传数据
+  invoiceUpload.fileList = []
+  invoiceUpload.fileIds = []
+}
+
 onLoad((query) => {
   currentOrdersId.value = query.ordersid
   getOrderDetail()
@@ -103,5 +183,17 @@ onLoad((query) => {
 </script>
 
 <style lang="scss" scoped>
-//
+.upload-popup {
+  border-radius: 16px;
+}
+
+.upload-container {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+:deep(.wd-upload) {
+  width: 100%;
+}
 </style>

+ 106 - 17
src/pages/order/index.vue

@@ -26,22 +26,32 @@
 
       <!-- 订单列表 -->
       <view class="sticky-box-content w-100vw" v-if="orderListShow.length">
-        <wd-cell-group>
-          <wd-cell
+        <view class="order-list">
+          <view
+            class="order-item"
             v-for="item in orderListShow"
             :key="item.ordersid"
-            :title="`${item.ordersproductname}`"
-            is-link
             @click="handleClick(item)"
-            custom-class="product-cell"
           >
-            <template #label>
-              <p class="text-gray-500">订单号:{{ item.ordersnumber }}</p>
-              <p class="text-gray-500">下单时间:{{ item.ordersorderdate }}</p>
-              <p class="text-gray-500">订单总金额:{{ Number(item.orderstotalprice).toFixed(2) }}</p>
-            </template>
-          </wd-cell>
-        </wd-cell-group>
+            <view class="order-header">
+              <text class="product-name">{{ item.ordersproductname }}</text>
+              <wd-tag :type="getOrderStatusType(item.ordersorderstatus)" custom-class="status-tag">
+                {{ getOrderStatus(item.ordersorderstatus) }}
+              </wd-tag>
+            </view>
+            <view class="order-content">
+              <text class="order-info">订单号: {{ item.ordersnumber }}</text>
+              <text class="order-info">下单时间: {{ item.ordersorderdate }}</text>
+              <view class="price-row">
+                <text class="order-info">订单总金额:</text>
+                <text class="price-text">¥{{ Number(item.orderstotalprice).toFixed(2) }}</text>
+              </view>
+            </view>
+            <view class="order-footer">
+              <wd-icon name="arrow-right" />
+            </view>
+          </view>
+        </view>
       </view>
       <view class="sticky-box-content w-100vw" v-else>
         <wd-status-tip image="content" tip="暂无内容" />
@@ -96,6 +106,30 @@ function change(e: any) {
   handleChange(e)
 }
 
+// 获取订单状态
+const getOrderStatus = (status: number) => {
+  const statusMap = {
+    0: '待审核',
+    1: '已支付',
+    2: '已完成',
+  }
+  return statusMap[status] || '未知状态'
+}
+
+// 获取订单状态对应的标签类型
+// 待审核 (0): 黄色警告色 (warning)
+// 已支付 (1): 绿色成功色 (success)
+// 已完成 (2): 蓝色主要色 (primary)
+// 未知状态: 红色危险色 (danger)
+const getOrderStatusType = (status: number) => {
+  const typeMap = {
+    0: 'warning', // 待审核 - 黄色警告色
+    1: 'success', // 已支付 - 绿色成功色
+    2: 'primary', // 已完成 - 蓝色主要色
+  }
+  return typeMap[status] || 'danger' // 未知状态使用红色危险色
+}
+
 function handleClick(item: any) {
   uni.navigateTo({
     url: `/pages/order/detail?ordersid=${item.ordersid}`,
@@ -124,10 +158,65 @@ onLoad((query) => {
 </script>
 
 <style lang="scss" scoped>
-::v-deep(.order-cell) {
-  .wd-cell__right {
-    flex: inherit !important;
-    width: 50px !important;
-  }
+.order-list {
+  padding: 0 12px;
+}
+
+.order-item {
+  position: relative;
+  padding: 12px;
+  margin-bottom: 12px;
+  background-color: #fff;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+}
+
+.order-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding-bottom: 8px;
+  border-bottom: 1px solid #f5f5f5;
+}
+
+.product-name {
+  flex: 1;
+  font-size: 16px;
+  font-weight: 500;
+  color: #333;
+}
+
+.status-tag {
+  flex-shrink: 0;
+}
+
+.order-content {
+  padding: 8px 0;
+}
+
+.order-info {
+  display: block;
+  font-size: 14px;
+  line-height: 1.6;
+  color: #666;
+}
+
+.price-row {
+  display: flex;
+  align-items: center;
+  margin-top: 2px;
+}
+
+.price-text {
+  font-size: 14px;
+  font-weight: 500;
+  color: #f56c6c;
+}
+
+.order-footer {
+  display: flex;
+  justify-content: flex-end;
+  margin-top: 4px;
+  color: #999;
 }
 </style>

+ 17 - 3
src/service/coupon1/index.ts

@@ -14,9 +14,15 @@ export const getCouponListApi = (params: Partial<Coupon1Type>, page: Partial<Lis
 }
 
 /** 领取优惠券 */
-export const receiveCouponApi = (couponid: string) => {
-  return http.get<any>('/api/nlua/call?pagevalue=43', {
-    couponid,
+export const receiveCouponApi = (params: {
+  couponid: string
+  coupon2phone: string
+  coupon2productids: string
+}) => {
+  return http.get<any>('/api/nlua/call?pagevalue=98', {
+    couponid: params.couponid, // 优惠券id
+    coupon2phone: params.coupon2phone, // 手机号
+    coupon2productids: params.coupon2productids, // 选择的商品id
   })
 }
 
@@ -34,3 +40,11 @@ export const getCoupon2idApi = (
     'coupon2productids.like': params.coupon2productids, // 产品id
   })
 }
+
+/** 获取已关联到当前用户的优惠券列表 */
+export const getCoupon2ListApi = (params: Partial<Coupon2Type>, page: Partial<ListType>) => {
+  return http.get<any>('/api/query/list?pagevalue=100', {
+    ...page,
+    'coupon2userid.value': params.coupon2userid, // 用户id
+  })
+}

+ 12 - 0
src/service/coupon2/index.ts

@@ -0,0 +1,12 @@
+import { http } from '@/utils/http'
+import type { Coupon2Type } from '@/types/coupon2'
+import type { ListType, ListResponseType } from '@/types/list'
+import { parseQueryValues } from '@/utils'
+
+/** 获取商品可用优惠券列表 */
+export const getCoupon2ViewmListApi = (params: Partial<Coupon2Type>, page: Partial<ListType>) => {
+  return http.get<ListResponseType<Coupon2Type>>('/api/query/list?pagevalue=100', {
+    ...page,
+    ...parseQueryValues(params),
+  })
+}

+ 12 - 2
src/store/app.ts

@@ -30,7 +30,7 @@ export const useAppStore = defineStore(
       appInfo.value = { ...initState }
     }
 
-    const isLogined = computed(() => !!appInfo.value.token)
+    const isLoginState = computed(() => !!appInfo.value.token)
 
     const needAlertCompleteInDays = computed(() => {
       // 如果已经提示过,并且距离上一次提示的时间在3天以内,则不需要提示
@@ -44,11 +44,21 @@ export const useAppStore = defineStore(
       alertCompleteInfo.value = val
     }
 
+    /**
+     * 判断用户是否登陆:没有登录->提示登录,已经登录,返回true
+     */
+    const alertLogin = () => {
+      if (!isLoginState.value) {
+        uni.showToast({ title: '请先登录', icon: 'none' })
+      }
+    }
+
     return {
       appInfo,
       setAppInfo,
       clearAppInfo,
-      isLogined,
+      isLoginState,
+      alertLogin,
       reset,
       alertCompleteInfo,
       setAlertCompleteInfo,

+ 42 - 5
src/store/coupon.ts

@@ -1,5 +1,11 @@
 import { defineStore } from 'pinia'
-import { getCouponListApi, receiveCouponApi, getCoupon2idApi } from '@/service/coupon1'
+import {
+  getCouponListApi,
+  receiveCouponApi,
+  getCoupon2idApi,
+  getCoupon2ListApi,
+} from '@/service/coupon1'
+import { getCoupon2ViewmListApi } from '@/service/coupon2'
 import { until } from '@vueuse/core'
 
 export const useCouponStore = defineStore(
@@ -19,10 +25,13 @@ export const useCouponStore = defineStore(
     }
 
     // 领取优惠券
-    const receiveCoupon = async (couponid: string) => {
-      const { data: couponList, loading } = useRequest(() => receiveCouponApi(couponid), {
-        immediate: true,
-      })
+    const receiveCoupon = async ({ couponid, coupon2phone, coupon2productids }) => {
+      const { data: couponList, loading } = useRequest(
+        () => receiveCouponApi({ couponid, coupon2phone, coupon2productids }),
+        {
+          immediate: true,
+        },
+      )
 
       await until(loading).toBe(false)
       return couponList.value
@@ -38,10 +47,38 @@ export const useCouponStore = defineStore(
       return couponList.value
     }
 
+    // 通过优惠券id获取产品信息(视图)
+    const getCoupon2ViewmList = async (params: { coupon2code: string }) => {
+      const { data: couponList, loading } = useRequest(
+        () => getCoupon2ViewmListApi(params, { pageindex: 1, rows: 999 }),
+        {
+          immediate: true,
+        },
+      )
+
+      await until(loading).toBe(false)
+      return couponList.value
+    }
+
+    // 获取已关联到当前用户的优惠券列表
+    const getCoupon2List = async (params: { coupon2userid: string }) => {
+      const { data: couponList, loading } = useRequest(
+        () => getCoupon2ListApi(params, { pageindex: 1, rows: 999 }),
+        {
+          immediate: true,
+        },
+      )
+
+      await until(loading).toBe(false)
+      return couponList.value
+    }
+
     return {
       getCouponList,
       receiveCoupon,
       getCoupon2id,
+      getCoupon2ViewmList,
+      getCoupon2List,
     }
   },
   {

+ 1 - 0
src/store/order.ts

@@ -34,6 +34,7 @@ export const useOrderStore = defineStore(
       orderscouponid: string
       ordersproductsn: string
       ordersuserid1: string
+      ordersphone?: string
       filesid: string
     }) => {
       const { data: order, loading } = useRequest(() => submitOrderApi(params), {

+ 8 - 1
src/types/coupon2.ts

@@ -1,6 +1,7 @@
 // CREATE TABLE `coupon2` (
 //   `coupon2sid` varchar(50) NOT NULL COMMENT '优惠券id',
 //   `coupon2code` varchar(50) DEFAULT NULL COMMENT '优惠券代码',
+//   `coupon2mc` varchar(50) DEFAULT NULL COMMENT '优惠券名称',
 //   `coupon2adddatetime` datetime DEFAULT NULL COMMENT '优惠券申请时间',
 //   `coupon2reviewdatetime` datetime DEFAULT NULL COMMENT '优惠券审核时间',
 //   `coupon2userid` varchar(50) DEFAULT NULL COMMENT '关联申请人id',
@@ -8,14 +9,18 @@
 //   `coupon2isused` tinyint(2) DEFAULT NULL COMMENT '是否已使用(0:未使用,1:已使用)',
 //   `coupon2productids` varchar(5000) DEFAULT NULL COMMENT '可用产品id',
 //   `coupon2coupon1id` varchar(50) DEFAULT NULL COMMENT '关联主券id',
+//   `coupon2phone` varchar(50) DEFAULT NULL COMMENT '领取手机号',
 //   PRIMARY KEY (`coupon2sid`) USING BTREE,
-//   KEY `couponsid` (`coupon2sid`)
+//   KEY `couponsid` (`coupon2sid`),
+//   KEY `coupon2code` (`coupon2code`)
 // ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='我的优惠券信息';
 
 export interface Coupon2Type {
   coupon2sid: string
   /** 优惠券代码 */
   coupon2code: string
+  /** 优惠券名称 */
+  coupon2mc: string
   /** 优惠券申请时间 */
   coupon2adddatetime: string
   /** 优惠券审核时间 */
@@ -30,4 +35,6 @@ export interface Coupon2Type {
   coupon2productids: string
   /** 关联主券id */
   coupon2coupon1id: string
+  /** 领取手机号 */
+  coupon2phone: string
 }

+ 2 - 0
src/types/uni-pages.d.ts

@@ -6,10 +6,12 @@
 interface NavigateToOptions {
   url: "/pages/index/index" |
        "/pages/about/index" |
+       "/pages/coupon/index" |
        "/pages/form/formStep1" |
        "/pages/form/formStep2" |
        "/pages/form/formStep3" |
        "/pages/form/formStep4" |
+       "/pages/merchant/index" |
        "/pages/mine/index" |
        "/pages/order/detail" |
        "/pages/order/index" |

+ 15 - 7
src/utils/http.ts

@@ -7,16 +7,22 @@ export const http = <T>(options: CustomRequestOptions & { formatData?: boolean }
   const userStore = useUserStore()
   const appStore = useAppStore()
 
+  const header = Object.assign(
+    {
+      token: `${appStore.appInfo.token}`,
+    },
+    options.header,
+  )
+
+  if (!appStore.appInfo.token) {
+    delete header.token
+  }
+
   // 1. 返回 Promise 对象
   return new Promise<IResData<T>>((resolve, reject) => {
     uni.request({
       ...options,
-      header: Object.assign(
-        {
-          token: `${appStore.appInfo.token}`,
-        },
-        options.header,
-      ),
+      header,
       dataType: 'json',
       // #ifndef MP-WEIXIN
       responseType: 'json',
@@ -51,10 +57,11 @@ export const http = <T>(options: CustomRequestOptions & { formatData?: boolean }
                 title: Message || '请求错误',
                 duration: 3000,
               })
+              reject(res)
               break
             case -2: // token过期
               // 判断是否已登录
-              if (appStore.isLogined) {
+              if (appStore.isLoginState) {
                 uni.showToast({
                   icon: 'none',
                   title: Message || '请求错误',
@@ -66,6 +73,7 @@ export const http = <T>(options: CustomRequestOptions & { formatData?: boolean }
                   },
                 })
               }
+              reject(res)
               break
             default:
               break