Просмотр исходного кода

feat: 下单时,需要上传[发票]和[身份证/营业执照证件],并且在订单详情中展示

laiqi 1 год назад
Родитель
Сommit
04b62c9fde

+ 3 - 1
.eslintrc-auto-import.json

@@ -96,6 +96,8 @@
     "onWatcherCleanup": true,
     "useId": true,
     "useModel": true,
-    "useTemplateRef": true
+    "useTemplateRef": true,
+    "uploadFile": true,
+    "uploadFileRequest": true
   }
 }

+ 15 - 3
src/App.vue

@@ -3,9 +3,11 @@ 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')
+  // console.log('App =====> onLaunch')
   // 检查是否已登录
   const appStore = useAppStore()
 
@@ -16,14 +18,24 @@ onLaunch(async () => {
     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)
+    }
   }
 })
 
 onShow(() => {
-  console.log('App =====> onShow')
+  // console.log('App =====> onShow')
 })
 onHide(() => {
-  console.log('App =====> onHide')
+  // console.log('App =====> onHide')
 })
 </script>
 

+ 26 - 16
src/hooks/useUpload.ts

@@ -1,5 +1,6 @@
 // TODO: 别忘加更改环境变量的 VITE_UPLOAD_BASEURL 地址。
 import { getEnvBaseUploadUrl } from '@/utils'
+import { useAppStore } from '@/store/app'
 
 const VITE_UPLOAD_BASEURL = `${getEnvBaseUploadUrl()}`
 
@@ -49,21 +50,30 @@ export default function useUpload<T = string>(formData: Record<string, any> = {}
   return { loading, error, data, run }
 }
 
-function uploadFile<T>({ tempFilePath, formData, data, error, loading }) {
-  uni.uploadFile({
-    url: VITE_UPLOAD_BASEURL,
-    filePath: tempFilePath,
-    name: 'file',
-    formData,
-    success: (uploadFileRes) => {
-      data.value = uploadFileRes.data as T
-    },
-    fail: (err) => {
-      console.error('uni.uploadFile err->', err)
-      error.value = true
-    },
-    complete: () => {
-      loading.value = false
-    },
+export function uploadFile<T>({ tempFilePath, formData, data, error, loading }) {
+  return new Promise((resolve, reject) => {
+    const appStore = useAppStore()
+    uni.uploadFile({
+      url: VITE_UPLOAD_BASEURL + '/api/attachment/addimg',
+      filePath: tempFilePath,
+      name: 'file',
+      formData,
+      header: {
+        token: `${appStore.appInfo.token}`, // 请求token
+      },
+      success: (uploadFileRes) => {
+        // console.log('uploadFileRes->', uploadFileRes)
+        data.value = uploadFileRes.data as T
+        resolve(data.value)
+      },
+      fail: (err) => {
+        console.error('uni.uploadFile err->', err)
+        error.value = true
+        reject(err)
+      },
+      complete: () => {
+        loading.value = false
+      },
+    })
   })
 }

+ 185 - 29
src/pages/form/formStep3.vue

@@ -53,14 +53,14 @@
       </div>
     </view>
 
-    <!-- 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-form ref="form" :model="model">
+    <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码"
@@ -72,9 +72,64 @@
               :rules="[{ required: true, message: '请填写SN码' }]"
             />
           </wd-cell-group>
-        </wd-form>
-      </div>
-    </view>
+        </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">
@@ -139,6 +194,7 @@ 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'
 
 const appStore = useAppStore()
 const userStore = useUserStore()
@@ -157,32 +213,108 @@ const couponsShow = ref<any[]>([]) // 展示的优惠券列表
 const couponChoose = ref('') // 选择的优惠券ID
 const submitLoading = ref(false) // 提交订单loading
 const model = reactive<{
-  ordersproductsn: string
+  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 handleSubmit() {
+function beforeUpload({ files, resolve }) {
   // 判断是否完善了个人信息
   if (!userStore.isAuthComplete) {
-    message
-      .confirm({
-        msg: '您还未完善个人信息, 请先完善信息',
-        title: '提示',
-        confirmButtonText: '去完善',
-        cancelButtonText: '继续浏览',
-      })
-      .then(() => {
-        uni.navigateTo({
-          url: '/pages-sub/user/index',
-        })
-      })
-      .catch(() => {
-        // ...
+    handleAuthComplete()
+    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)
+
+    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)
+      model[modelStats].push({
+        url: file.url,
+        id: dataObject.Data,
       })
+
+      // console.log('model[modelStats]:', model)
+    }
   }
+}
+
+// 删除文件时,删除ids数组数据
+function handleRemoveInvoice({ file }) {
+  model.ordersinvoiceImgIds = model.ordersinvoiceImgIds.filter((item) => item.url !== file.url)
+}
+
+// 删除文件时,删除ids数组数据
+function handleRemoveIdentity({ file }) {
+  model.ordersidentityImgIds = model.ordersidentityImgIds.filter((item) => item.url !== file.url)
+}
+
+// 完善资料提示
+function handleAuthComplete() {
+  message
+    .confirm({
+      msg: '您还未完善个人信息, 请先完善信息',
+      title: '提示',
+      confirmButtonText: '去完善',
+      cancelButtonText: '继续浏览',
+    })
+    .then(() => {
+      uni.navigateTo({
+        url: '/pages-sub/user/index',
+      })
+    })
+    .catch(() => {
+      // ...
+    })
+}
+
+// 提交表单
+function handleSubmit() {
+  // 判断是否完善了个人信息
+  if (!userStore.isAuthComplete) handleAuthComplete()
 
   form.value.validate().then(async (res) => {
     if (res.valid) {
@@ -195,15 +327,36 @@ function handleSubmit() {
         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(','),
         })
-        console.log('submitOrder res:', res)
 
         message
           .alert({
@@ -303,6 +456,9 @@ onLoad(async (query) => {
   productsid.value = query.productsid
   getProductDetail() // 获取产品详情
   getCouponsShow() // 获取可展示的优惠券列表
+
+  // 获取用户类型
+  userstype.value = await userStore.getUserType() // 个人/企业
 })
 </script>
 

+ 1 - 1
src/pages/index/index.vue

@@ -258,7 +258,7 @@ uni.$on('authComplete', function (data) {
 
 // 入口完善信息提示
 const showCompleteDialog = () => {
-  console.log(appStore.isLogined, userStore.isAuthComplete, appStore.needAlertCompleteInDays)
+  // console.log(appStore.isLogined, userStore.isAuthComplete, appStore.needAlertCompleteInDays)
 
   // 如果已登录且已完善信息,则不提示
   if (appStore.isLogined && userStore.isAuthComplete) return

+ 1 - 1
src/pages/mine/index.vue

@@ -108,7 +108,7 @@ const isLogin = computed(() => appStore.isLogined)
 
 onMounted(() => {
   userDetail.value = userStore.userDetail
-  console.log(userDetail.value, 'userDetail')
+  // console.log(userDetail.value, 'userDetail')
 })
 
 // 页面跳转

+ 41 - 0
src/pages/order/detail.vue

@@ -18,6 +18,20 @@
       <wd-cell title="实付金额" :value="`¥${orderDetail.ordersdiscountprice}`" />
       <wd-cell title="订单状态" :value="getOrderStatus(orderDetail.ordersorderstatus)" />
       <wd-cell title="下单时间" :value="orderDetail.ordersorderdate" />
+      <wd-cell title="发票图片">
+        <view class="flex flex-wrap">
+          <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>
+      </wd-cell>
+      <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" />
+          </view>
+        </view>
+      </wd-cell>
     </wd-cell-group>
 
     <!-- 回到订单列表 -->
@@ -29,17 +43,21 @@
 
 <script lang="ts" setup>
 import { useOrderStore } from '@/store/order'
+import { useFileStore } from '@/store/file'
 import type { OrderType } from '@/types/order'
 
 const orderStore = useOrderStore()
+const fileStore = useFileStore()
 const currentOrdersId = ref('')
 const orderDetail = ref<OrderType>({} as OrderType)
 
+// 获取订单详情
 const getOrderDetail = async () => {
   const res = await orderStore.getOrderDetail(currentOrdersId.value)
   orderDetail.value = res
 }
 
+// 获取订单状态
 const getOrderStatus = (status: number) => {
   const statusMap = {
     0: '待审核',
@@ -49,15 +67,38 @@ const getOrderStatus = (status: number) => {
   return statusMap[status] || '未知状态'
 }
 
+// 回到订单列表
 function handleBack() {
   uni.navigateTo({
     url: '/pages/order/index',
   })
 }
 
+const invoiceImg = ref([]) // 发票图片
+const identityImg = ref([]) // 证件信息图片
+// 获取发票图片附件
+const getInvoiceImg = async () => {
+  const res = await fileStore.getAttachmentList({
+    attlsh: currentOrdersId.value,
+    attmodel: 'orders_invoice',
+  })
+  invoiceImg.value = res
+}
+
+// 获取证件信息图片附件
+const getIdentityImg = async () => {
+  const res = await fileStore.getAttachmentList({
+    attlsh: currentOrdersId.value,
+    attmodel: 'orders_identity',
+  })
+  identityImg.value = res
+}
+
 onLoad((query) => {
   currentOrdersId.value = query.ordersid
   getOrderDetail()
+  getInvoiceImg()
+  getIdentityImg()
 })
 </script>
 

+ 52 - 0
src/store/file.ts

@@ -0,0 +1,52 @@
+import { defineStore } from 'pinia'
+import { getAttachmentListApi } from '@/service/file'
+import { until } from '@vueuse/core'
+
+export const useFileStore = defineStore(
+  'file',
+  () => {
+    // 文件前缀地址
+    const fileHeadUrl = ref('')
+
+    // 查询附件列表
+    const getAttachmentList = async ({
+      attlsh,
+      attmodel,
+    }: {
+      attlsh: string
+      attmodel: string
+    }) => {
+      const { data: configList, loading } = useRequest(
+        () => getAttachmentListApi({ attlsh, attmodel }, { pageindex: 1, rows: 999 }),
+        {
+          immediate: true,
+        },
+      )
+
+      await until(loading).toBe(false)
+      // https://dzapi.kdboss.cn/upfile/orders_invoice/20250410/2025041018283477178626.png
+
+      const fileList = configList.value.Data.map((item) => {
+        return {
+          ...item,
+          url: `${fileHeadUrl.value}${item.attpath}${item.attname}.${item.atttype}`,
+        }
+      })
+
+      return fileList
+    }
+
+    const setFileHeadUrl = (url: string) => {
+      fileHeadUrl.value = url
+    }
+
+    return {
+      getAttachmentList,
+      setFileHeadUrl,
+      fileHeadUrl,
+    }
+  },
+  {
+    persist: true,
+  },
+)

+ 1 - 0
src/store/order.ts

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

+ 6 - 0
src/store/user.ts

@@ -31,6 +31,11 @@ export const useUserStore = defineStore(
       }
     })
 
+    // 获取用户类型
+    const getUserType = () => {
+      return userDetail.value.userstype
+    }
+
     const setUserInfo = (val: IUserInfo) => {
       userInfo.value = val
     }
@@ -87,6 +92,7 @@ export const useUserStore = defineStore(
       isAuthComplete,
       updateUser,
       getSellUserList,
+      getUserType,
     }
   },
   {

+ 3 - 0
src/types/auto-import.d.ts

@@ -74,6 +74,8 @@ declare global {
   const toValue: typeof import('vue')['toValue']
   const triggerRef: typeof import('vue')['triggerRef']
   const unref: typeof import('vue')['unref']
+  const uploadFile: typeof import('../hooks/useUpload')['uploadFile']
+  const uploadFileRequest: typeof import('../hooks/useUpload')['uploadFileRequest']
   const useAttrs: typeof import('vue')['useAttrs']
   const useCssModule: typeof import('vue')['useCssModule']
   const useCssVars: typeof import('vue')['useCssVars']
@@ -170,6 +172,7 @@ declare module 'vue' {
     readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
     readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
     readonly unref: UnwrapRef<typeof import('vue')['unref']>
+    readonly uploadFile: UnwrapRef<typeof import('../hooks/useUpload')['uploadFile']>
     readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
     readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
     readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>