修改
This commit is contained in:
4
env/.env
vendored
4
env/.env
vendored
@@ -10,8 +10,8 @@ VITE_WX_APPID = 'wxa023ce905b13a4de'
|
||||
VITE_APP_PUBLIC_BASE=/
|
||||
|
||||
# 后台请求地址localhost
|
||||
VITE_SERVER_BASEURL = 'http://1.14.158.154:48080/admin-api'
|
||||
VITE_UPLOAD_BASEURL = 'http://1.14.158.154:48080/upload'
|
||||
VITE_SERVER_BASEURL = 'https://api.tuanbanlv.com/admin-api'
|
||||
VITE_UPLOAD_BASEURL = 'https://api.tuanbanlv.com/upload'
|
||||
# 备注:如果后台带统一前缀,则也要加到后面,eg: https://ukw0y1.laf.run/api
|
||||
|
||||
# 注意,如果是微信小程序,还有一套请求地址的配置,根据 develop、trial、release 分别设置上传地址,见 `src/utils/index.ts`。
|
||||
|
||||
27
private.wxa023ce905b13a4de.key
Normal file
27
private.wxa023ce905b13a4de.key
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAqu4smP/F2ib/J2+pJp1Y379U8N18eEdNEIi0+Bw/TZSn90sN
|
||||
mZvTAu/fB1kkSzjHLhvK8+TMTx/WDvD9u/9WpVlBK9rE6bx8+OcFCk5gePqDZdY0
|
||||
ybd99D1oicudmO2yU+YMyBD4zM5qvdVyU5EDj6OnV1zB6D5n3pw2thICplYobN5K
|
||||
osTEBr/c33RX8/3ZbRo0AdyZPcY5I3Uq6vDMCKdmJn4H0klWG3jXYtsen8HVzO3F
|
||||
UWFFeiZeyBSq5iVBtm6s8w2mQhRbjc6rS0ERalrGL7mV0RjVQauL6TJrIxoI/8v4
|
||||
aIOgo7gApFlqTf3nfyZw4OTEPQWrTKM2s7/qIwIDAQABAoIBAG0EoW8n2sHrk1tM
|
||||
rV7ShmeWeY9yRDvWhgFgn8OLCJjrkkF4HgF10ByUbvQZ17seSHNRCJ2LtP9WN8mp
|
||||
zLtF/LZS+e0FiAfnzvFVLvLG0GL4rCucdmidXnkTXYRdWHO8TruSA17q7DR8Brpy
|
||||
04sW92V6pHVk1MvSWZ8ylPaFACmjyPjc/gvUdeZG1v0tUg6069r+RZegLotzhcDi
|
||||
0T2N9W/0nWFIkNNCQrG5ze24NFD+FmhcNQnx/TQMUeU02JxAfA6sM6jdFcRm1j8Z
|
||||
M2H/VBe1fOZjQceBHgfeexEBNWNqHB9QJIRdk51+EFC7I+CL1wIvJegrUYAQvwNm
|
||||
njCzhnkCgYEA2SX45T5Zyicp8FGrYH1ufVRnipO/mwAkvlY8tbFd1C4fPB8Hkbx7
|
||||
ZrM5G3293HJ2TBQO+kCIU1WyT9weVeJ7P9bruDyjNOSu1q7d4xjq49LSLXvo7C4+
|
||||
T8V7z1fUZt0A0jOdaqfmw+R7ICzQAna8PI/J8elW3Vh7uNk0TRcIXZ0CgYEAyYNI
|
||||
yrrpabP/sxGYfUpb+Ko6iAcISPTT7rZcN+dVGqqFb8tkvHUSPhhy/H5WYcdFFhzI
|
||||
S5nMXXmRsgDDRdltukUg4zoA1um5gdI8SeOLzEdcFF0p/Pfc60G+LoY7K/aUlvy2
|
||||
DeSpSHJgblZVMKw+2/lfvc2M/100BOjObWkWur8CgYEAgNthqVeonKdE4dD065tD
|
||||
N6ggkUE/0FDzfOdbu033KfP8oQagzUCV0cnEt6WURv69aEP251XoD9uopm8uqTRu
|
||||
guGcm4WQK9EQV2EJVrvwlyUBh/AhthVy8I91+wJZjnjTBemPHj1oWRJ6ZgtxnCSt
|
||||
axrAcYdP/qWFNZneyWhDlJkCgYB+7jwuvteB5oidAetcmDcghhGCV3OniNfqGGI0
|
||||
MHoR5vFQPvzAHLoV9Q6Q7v94ba2dxRmBTWpGQuo8BnD6EYAlgZ+6oXGf7e8U0Bl7
|
||||
rWIElbpxdVGab4JviaTC53hkM9ja1mnSjIL5CFqnhaf5lbWumADvrIcw30OCCCbn
|
||||
EffoPwKBgQDU/Q3/lZahWTr4w6NtNmyhi0EnoLv+a9afhYdii1+v5g5PfG0mDvx0
|
||||
0lmX2+JNqhF5ZMlVACLY1wR0trz4Iz2LstOst1XE3ipy2zbQTF/g6ujudOxrzOuw
|
||||
jr+1JcbjZfywRQO+2wgegm8Qut7cZe5AraR6cqzIZ6DUO8QtbmsJnw==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -81,3 +81,13 @@ export function generateContract(id: number) {
|
||||
export function generateContractHtml(id: number) {
|
||||
return http.get<string>(`/car/renewal-order/generate-contract-html?id=${id}`)
|
||||
}
|
||||
|
||||
/** 创建线上签名令牌(用于分享签名) */
|
||||
export function createSignToken(id: number) {
|
||||
return http.post<{ uuid: string; signUrl: string }>('/car/renewal-order/create-sign-token', undefined, { id })
|
||||
}
|
||||
|
||||
/** 清空订单合同与客户签名(重新生成合同时先清空再扫码签名) */
|
||||
export function clearContractAndSignature(id: number) {
|
||||
return http.post<boolean>('/car/renewal-order/clear-contract-sign', undefined, { id })
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ export interface RenewalProductVO {
|
||||
productName?: string // 产品名称
|
||||
productContent?: string // 产品内容
|
||||
productType?: string // 产品类别
|
||||
effectiveYear?: string // 生效年限(仅产品类别为无忧时有值)
|
||||
remark?: string // 备注
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
<wd-cell title="产品类别">
|
||||
<dict-tag :type="DICT_TYPE.CAR_RENEWAL_PRODUCT_TYPE" :value="productData?.productType" />
|
||||
</wd-cell>
|
||||
<wd-cell v-if="showEffectiveYear" title="生效年限">
|
||||
<dict-tag :type="DICT_TYPE.CAR_RENEWAL_YEAR" :value="productData?.effectiveYear" />
|
||||
</wd-cell>
|
||||
<wd-cell title="产品内容" :value="productData?.productContent || '-'" />
|
||||
<wd-cell title="备注" :value="productData?.remark || '-'" />
|
||||
<wd-cell title="创建时间" :value="formatDateTime(productData?.createTime) || '-'" />
|
||||
@@ -54,6 +57,7 @@ import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useToast } from 'wot-design-uni'
|
||||
import { deleteRenewalProduct, getRenewalProduct } from '@/api/car/renewalproduct'
|
||||
import { getDictLabel } from '@/hooks/useDict'
|
||||
import { useAccess } from '@/hooks/useAccess'
|
||||
import { navigateBackPlus } from '@/utils'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
@@ -73,6 +77,12 @@ const deleting = ref(false)
|
||||
const productId = ref<number>()
|
||||
const productData = ref<RenewalProductVO | null>(null)
|
||||
|
||||
/** 仅当产品类别为「无忧」或「无忧延保」时显示生效年限 */
|
||||
const showEffectiveYear = computed(() => {
|
||||
const label = getDictLabel(DICT_TYPE.CAR_RENEWAL_PRODUCT_TYPE, productData.value?.productType) || ''
|
||||
return label === '无忧' || label === '无忧延保'
|
||||
})
|
||||
|
||||
/** 返回上一页 */
|
||||
function handleBack() {
|
||||
navigateBackPlus()
|
||||
@@ -101,6 +111,8 @@ function handleDelete() {
|
||||
try {
|
||||
await deleteRenewalProduct(Number(productId.value))
|
||||
toast.success('删除成功')
|
||||
// 标记列表页需要刷新,返回后列表会重新拉取数据
|
||||
uni.setStorageSync('renewalproduct_list_need_refresh', true)
|
||||
setTimeout(() => {
|
||||
handleBack()
|
||||
}, 500)
|
||||
|
||||
@@ -36,6 +36,17 @@
|
||||
label-key="label"
|
||||
value-key="value"
|
||||
/>
|
||||
<wd-picker
|
||||
v-if="showEffectiveYear"
|
||||
v-model="formData.effectiveYear"
|
||||
:columns="effectiveYearOptions"
|
||||
label="生效年限"
|
||||
label-width="180rpx"
|
||||
prop="effectiveYear"
|
||||
placeholder="请选择生效年限"
|
||||
label-key="label"
|
||||
value-key="value"
|
||||
/>
|
||||
<wd-textarea
|
||||
v-model="formData.productContent"
|
||||
label="产品内容"
|
||||
@@ -77,10 +88,10 @@
|
||||
import type { FormInstance } from 'wot-design-uni/components/wd-form/types'
|
||||
import type { RenewalProductVO } from '@/api/car/renewalproduct'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { useToast } from 'wot-design-uni'
|
||||
import { createRenewalProduct, getRenewalProduct, updateRenewalProduct } from '@/api/car/renewalproduct'
|
||||
import { getStrDictOptions } from '@/hooks/useDict'
|
||||
import { getDictLabel, getStrDictOptions } from '@/hooks/useDict'
|
||||
import { navigateBackPlus } from '@/utils'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
|
||||
@@ -107,10 +118,41 @@ const productTypeOptions = computed(() => {
|
||||
}))
|
||||
})
|
||||
|
||||
// 生效年限选项(仅产品类别为「无忧」时展示)
|
||||
const effectiveYearOptions = computed(() => {
|
||||
return getStrDictOptions(DICT_TYPE.CAR_RENEWAL_YEAR).map(item => ({
|
||||
label: item.label,
|
||||
value: item.value,
|
||||
}))
|
||||
})
|
||||
|
||||
/** 仅当产品类别为「无忧」或「无忧延保」时显示生效年限 */
|
||||
const showEffectiveYear = computed(() => {
|
||||
const label = getDictLabel(DICT_TYPE.CAR_RENEWAL_PRODUCT_TYPE, formData.value.productType) || ''
|
||||
return label === '无忧' || label === '无忧延保'
|
||||
})
|
||||
|
||||
// 产品类别切换为「无忧」或「无忧延保」时设置生效年限默认值,切换为其他时清空
|
||||
watch(
|
||||
() => formData.value.productType,
|
||||
(type) => {
|
||||
const label = getDictLabel(DICT_TYPE.CAR_RENEWAL_PRODUCT_TYPE, type) || ''
|
||||
if (label === '无忧' || label === '无忧延保') {
|
||||
const opts = getStrDictOptions(DICT_TYPE.CAR_RENEWAL_YEAR)
|
||||
if (opts.length > 0 && !formData.value.effectiveYear) {
|
||||
formData.value.effectiveYear = opts[0].value
|
||||
}
|
||||
} else {
|
||||
formData.value.effectiveYear = undefined
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const formData = ref<RenewalProductVO>({
|
||||
productName: undefined,
|
||||
productContent: undefined,
|
||||
productType: undefined,
|
||||
effectiveYear: undefined,
|
||||
remark: undefined,
|
||||
})
|
||||
|
||||
@@ -161,6 +203,8 @@ async function handleSubmit() {
|
||||
await createRenewalProduct(formData.value)
|
||||
toast.success('新增成功')
|
||||
}
|
||||
// 标记列表页需要刷新,返回后列表会重新拉取数据
|
||||
uni.setStorageSync('renewalproduct_list_need_refresh', true)
|
||||
setTimeout(() => {
|
||||
navigateBackPlus()
|
||||
}, 500)
|
||||
|
||||
@@ -170,16 +170,24 @@ onReachBottom(() => {
|
||||
loadMore()
|
||||
})
|
||||
|
||||
/** 页面显示时刷新(从编辑页面返回时) */
|
||||
/** 是否首次显示(首次不依赖 onShow 刷新,由 onMounted 拉取;从子页返回时 onShow 会再次触发并刷新) */
|
||||
const isFirstShow = ref(true)
|
||||
|
||||
/** 页面显示时刷新(与订单列表一致:从新增/编辑/详情页返回后刷新列表) */
|
||||
onShow(() => {
|
||||
// 检查页面栈,如果上一个页面是编辑页面,则刷新
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 1) {
|
||||
const prevPage = pages[pages.length - 2]
|
||||
if (prevPage?.route?.includes('/renewalproduct/form/index')) {
|
||||
// 检查存储中的刷新标志(新增、编辑、删除成功后子页会设置)
|
||||
const needRefresh = uni.getStorageSync('renewalproduct_list_need_refresh')
|
||||
if (needRefresh) {
|
||||
uni.removeStorageSync('renewalproduct_list_need_refresh')
|
||||
getList(true)
|
||||
isFirstShow.value = false
|
||||
return
|
||||
}
|
||||
// 非首次显示时也刷新一次(从表单页或详情页返回,确保列表是最新)
|
||||
if (!isFirstShow.value) {
|
||||
getList(true)
|
||||
}
|
||||
}
|
||||
isFirstShow.value = false
|
||||
})
|
||||
|
||||
/** 初始化 */
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
<wd-input v-model="formData.invoiceDate" prop="invoiceDate" v-show="false" />
|
||||
<wd-input
|
||||
v-model="formData.invoiceAmount"
|
||||
label="发票金额"
|
||||
@@ -100,7 +101,7 @@
|
||||
clearable
|
||||
placeholder="请输入购买时公里数"
|
||||
/>
|
||||
<wd-cell title="行驶证" title-width="180rpx" />
|
||||
<wd-cell title="行驶证" title-width="180rpx" required />
|
||||
<view class="px-24rpx py-16rpx">
|
||||
<wd-upload
|
||||
v-model:file-list="drivingLicenseFileList"
|
||||
@@ -140,7 +141,7 @@
|
||||
:source-type="['album', 'camera']"
|
||||
/>
|
||||
</view>
|
||||
<wd-cell title="购车发票" title-width="180rpx" />
|
||||
<wd-cell title="购车发票" title-width="180rpx" required />
|
||||
<view class="px-24rpx py-16rpx">
|
||||
<wd-upload
|
||||
v-model:file-list="carInvoiceFileList"
|
||||
@@ -150,7 +151,7 @@
|
||||
:source-type="['album', 'camera']"
|
||||
/>
|
||||
</view>
|
||||
<wd-cell title="购置税发票" title-width="180rpx" />
|
||||
<wd-cell title="购置税凭证" title-width="180rpx" required />
|
||||
<view class="px-24rpx py-16rpx">
|
||||
<wd-upload
|
||||
v-model:file-list="purchaseTaxInvoiceFileList"
|
||||
@@ -180,6 +181,7 @@
|
||||
v-model="formData.serviceBuyer"
|
||||
label="服务购买方"
|
||||
label-width="180rpx"
|
||||
prop="serviceBuyer"
|
||||
clearable
|
||||
placeholder="请输入服务购买方"
|
||||
/>
|
||||
@@ -187,6 +189,7 @@
|
||||
v-model="formData.carBuyer"
|
||||
label="车辆购买方"
|
||||
label-width="180rpx"
|
||||
prop="carBuyer"
|
||||
clearable
|
||||
placeholder="请输入车辆购买方"
|
||||
/>
|
||||
@@ -195,6 +198,7 @@
|
||||
:columns="certTypeOptions"
|
||||
label="证件类型"
|
||||
label-width="180rpx"
|
||||
prop="certType"
|
||||
placeholder="请选择证件类型"
|
||||
value-key="value"
|
||||
label-key="label"
|
||||
@@ -203,6 +207,7 @@
|
||||
v-model="formData.certNo"
|
||||
label="证件号码"
|
||||
label-width="180rpx"
|
||||
prop="certNo"
|
||||
clearable
|
||||
placeholder="请输入证件号码"
|
||||
/>
|
||||
@@ -210,6 +215,7 @@
|
||||
v-model="formData.mobile"
|
||||
label="联系电话"
|
||||
label-width="180rpx"
|
||||
prop="mobile"
|
||||
type="number"
|
||||
clearable
|
||||
placeholder="请输入联系电话"
|
||||
@@ -234,12 +240,12 @@
|
||||
:columns="storeOptions"
|
||||
label="门店"
|
||||
label-width="180rpx"
|
||||
prop="storeId"
|
||||
placeholder="请选择门店"
|
||||
value-key="id"
|
||||
label-key="storeName"
|
||||
disabled
|
||||
/>
|
||||
<wd-cell title="身份证正面" title-width="180rpx" />
|
||||
<wd-cell title="身份证正面" title-width="180rpx" required />
|
||||
<view class="px-24rpx py-16rpx">
|
||||
<wd-upload
|
||||
v-model:file-list="idCardFrontFileList"
|
||||
@@ -249,7 +255,7 @@
|
||||
:source-type="['album', 'camera']"
|
||||
/>
|
||||
</view>
|
||||
<wd-cell title="身份证反面" title-width="180rpx" />
|
||||
<wd-cell title="身份证反面" title-width="180rpx" required />
|
||||
<view class="px-24rpx py-16rpx">
|
||||
<wd-upload
|
||||
v-model:file-list="idCardBackFileList"
|
||||
@@ -280,6 +286,7 @@
|
||||
v-model="formData.serviceProduct"
|
||||
label="服务产品"
|
||||
label-width="180rpx"
|
||||
prop="serviceProduct"
|
||||
readonly
|
||||
placeholder="选择产品后自动填充"
|
||||
/>
|
||||
@@ -287,6 +294,7 @@
|
||||
v-model="formData.productValidity"
|
||||
label="产品时效"
|
||||
label-width="180rpx"
|
||||
prop="productValidity"
|
||||
readonly
|
||||
placeholder="选择产品后自动填充"
|
||||
/>
|
||||
@@ -294,6 +302,7 @@
|
||||
:model-value="productTypeLabel"
|
||||
label="产品类别"
|
||||
label-width="180rpx"
|
||||
prop="productType"
|
||||
readonly
|
||||
placeholder="选择产品后自动填充"
|
||||
/>
|
||||
@@ -301,16 +310,18 @@
|
||||
v-model="formData.productFee"
|
||||
label="产品费用"
|
||||
label-width="180rpx"
|
||||
prop="productFee"
|
||||
type="digit"
|
||||
clearable
|
||||
placeholder="请输入产品费用"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="formData.originalWarrantyYears"
|
||||
label="原厂质保时长"
|
||||
label="产品年限"
|
||||
label-width="180rpx"
|
||||
required
|
||||
clearable
|
||||
placeholder="请输入原厂质保时长"
|
||||
placeholder="请输入产品年限(如:3)"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="formData.originalWarrantyMileage"
|
||||
@@ -325,27 +336,31 @@
|
||||
<!-- 选项卡4: 其他信息 -->
|
||||
<view v-show="tabIndex === 3">
|
||||
<wd-cell-group border title="其他信息">
|
||||
<wd-input
|
||||
<wd-picker
|
||||
v-model="formData.settlementMethod"
|
||||
:columns="settlementMethodOptions"
|
||||
label="结算方式"
|
||||
label-width="180rpx"
|
||||
clearable
|
||||
placeholder="请输入结算方式"
|
||||
prop="settlementMethod"
|
||||
placeholder="请选择结算方式"
|
||||
value-key="value"
|
||||
label-key="label"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="formData.inputUser"
|
||||
label="录单人"
|
||||
label-width="180rpx"
|
||||
prop="inputUser"
|
||||
clearable
|
||||
placeholder="请输入录单人"
|
||||
/>
|
||||
<wd-cell v-if="formData.productType === '00'" title="合同路径" title-width="180rpx">
|
||||
<wd-cell v-if="showContractComponents" title="合同路径" title-width="180rpx">
|
||||
<template #value>
|
||||
<text class="text-24rpx text-[#999]">保存订单后可生成在线合同</text>
|
||||
</template>
|
||||
</wd-cell>
|
||||
<wd-textarea
|
||||
v-if="formData.productType === '00'"
|
||||
v-if="showContractComponents"
|
||||
v-model="formData.contractRemark"
|
||||
label="合同备注"
|
||||
label-width="180rpx"
|
||||
@@ -408,7 +423,7 @@ import type { StoreVO } from '@/api/tire/store'
|
||||
import dayjs from 'dayjs'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useMessage, useToast } from 'wot-design-uni'
|
||||
import { createRenewalOrder } from '@/api/car/renewalorder'
|
||||
import { createRenewalOrder, createSignToken } from '@/api/car/renewalorder'
|
||||
import { findByProductType, getRenewalProduct } from '@/api/car/renewalproduct'
|
||||
import { findByProductType as findStoreByProductType } from '@/api/tire/store'
|
||||
import { getDictLabel, getStrDictOptions } from '@/hooks/useDict'
|
||||
@@ -428,7 +443,7 @@ const toast = useToast()
|
||||
const message = useMessage()
|
||||
const tokenStore = useTokenStore()
|
||||
const userStore = useUserStore()
|
||||
const getTitle = computed(() => '新增续保订单')
|
||||
const getTitle = computed(() => '新增订单')
|
||||
const formLoading = ref(false)
|
||||
const tabIndex = ref(0) // 当前选项卡索引
|
||||
const invoiceFileList = ref<UploadFile[]>([]) // 发票图片文件列表
|
||||
@@ -447,6 +462,11 @@ const productList = ref<RenewalProductVO[]>([]) // 续保产品列表
|
||||
const storeOptions = computed(() => storeList.value.map(item => ({ id: item.id, storeName: item.storeName })))
|
||||
const productOptions = computed(() => productList.value.map(item => ({ id: item.id, productName: item.productName })))
|
||||
const certTypeOptions = computed(() => getStrDictOptions('car_renewal_identity_type'))
|
||||
const settlementMethodOptions = computed(() => getStrDictOptions(DICT_TYPE.CAR_RENEWAL_PAY_METHOD))
|
||||
// 判断是否显示合同相关组件(仅当产品类别为00或02时显示)
|
||||
const showContractComponents = computed(() => {
|
||||
return formData.value.productType === '00' || formData.value.productType === '02'
|
||||
})
|
||||
|
||||
const formData = ref<RenewalOrderVO>({
|
||||
id: undefined,
|
||||
@@ -472,10 +492,10 @@ const formData = ref<RenewalOrderVO>({
|
||||
serviceProduct: undefined,
|
||||
productType: undefined,
|
||||
productValidity: undefined,
|
||||
originalWarrantyYears: undefined,
|
||||
originalWarrantyYears: '3',
|
||||
originalWarrantyMileage: undefined,
|
||||
productFee: undefined,
|
||||
settlementMethod: undefined,
|
||||
settlementMethod: '00',
|
||||
remark: undefined,
|
||||
inputUser: undefined,
|
||||
contractUrl: undefined,
|
||||
@@ -497,9 +517,23 @@ const formRules = {
|
||||
carModel: [{ required: true, message: '车型不能为空' }],
|
||||
vin: [{ required: true, message: '车架号不能为空' }],
|
||||
engineNo: [{ required: true, message: '发动机号不能为空' }],
|
||||
invoiceDate: [{ required: true, message: '发票日期不能为空' }],
|
||||
invoiceAmount: [{ required: true, message: '发票金额不能为空' }],
|
||||
purchaseMileage: [{ required: true, message: '购买时公里数不能为空' }],
|
||||
serviceBuyer: [{ required: true, message: '服务购买方不能为空' }],
|
||||
carBuyer: [{ required: true, message: '车辆购买方不能为空' }],
|
||||
certType: [{ required: true, message: '证件类型不能为空' }],
|
||||
certNo: [{ required: true, message: '证件号码不能为空' }],
|
||||
mobile: [{ required: true, message: '联系电话不能为空' }],
|
||||
storeId: [{ required: true, message: '门店不能为空' }],
|
||||
productId: [{ required: true, message: '续保产品不能为空' }],
|
||||
serviceProduct: [{ required: true, message: '服务产品不能为空' }],
|
||||
productType: [{ required: true, message: '产品类别不能为空' }],
|
||||
productValidity: [{ required: true, message: '产品时效不能为空' }],
|
||||
productFee: [{ required: true, message: '产品费用不能为空' }],
|
||||
settlementMethod: [{ required: true, message: '结算方式不能为空' }],
|
||||
inputUser: [{ required: true, message: '录单人不能为空' }],
|
||||
originalWarrantyYears: [{ required: true, message: '产品年限不能为空' }],
|
||||
}
|
||||
|
||||
// 根据 productType 值获取字典 label
|
||||
@@ -713,7 +747,7 @@ function createUploadMethod(fieldName: keyof RenewalOrderVO): UploadMethod {
|
||||
}
|
||||
}
|
||||
|
||||
/** 多张图片上传(购车发票、购置税发票、商业险保单) */
|
||||
/** 多张图片上传(购车发票、购置税凭证、商业险保单) */
|
||||
function createMultiUploadMethod(fieldName: 'carInvoiceUrls' | 'purchaseTaxInvoiceUrls' | 'businessInsurancePolicyUrls'): UploadMethod {
|
||||
return (file, uploadFormData, options) => {
|
||||
const uploadTask = uni.uploadFile({
|
||||
@@ -756,14 +790,36 @@ function createMultiUploadMethod(fieldName: 'carInvoiceUrls' | 'purchaseTaxInvoi
|
||||
|
||||
// 订单录入页面不需要加载详情,只允许新增
|
||||
|
||||
// ========== 新增完成后合同预览逻辑(产品类别 00) ==========
|
||||
// 流程:提交 → 创建订单 → 若 productType===00 则跳转合同预览页 → 客户签名 → 确认生成合同 → 再跳转订单详情
|
||||
// ========== 新增完成后合同预览逻辑(产品类别 00 或 02) ==========
|
||||
// 流程:提交 → 创建订单 → 若 productType===00 或 02 则跳转合同预览页 → 客户签名 → 确认生成合同 → 再跳转订单详情
|
||||
// 否则:创建成功 → 提示 → 跳转订单详情
|
||||
|
||||
async function handleSubmit() {
|
||||
const { valid } = await formRef.value!.validate()
|
||||
if (!valid) return
|
||||
|
||||
// 必传文件校验:行驶证、购车发票、购置税凭证、身份证正反面
|
||||
if (!formData.value.drivingLicenseUrl) {
|
||||
toast.error('请上传行驶证')
|
||||
return
|
||||
}
|
||||
if (!formData.value.carInvoiceUrls?.length) {
|
||||
toast.error('请上传购车发票')
|
||||
return
|
||||
}
|
||||
if (!formData.value.purchaseTaxInvoiceUrls?.length) {
|
||||
toast.error('请上传购置税凭证')
|
||||
return
|
||||
}
|
||||
if (!formData.value.idCardFrontUrl) {
|
||||
toast.error('请上传身份证正面')
|
||||
return
|
||||
}
|
||||
if (!formData.value.idCardBackUrl) {
|
||||
toast.error('请上传身份证反面')
|
||||
return
|
||||
}
|
||||
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = { ...formData.value }
|
||||
@@ -777,17 +833,49 @@ async function handleSubmit() {
|
||||
data.invoiceDate = dayjs(data.invoiceDate).format('YYYY-MM-DD')
|
||||
}
|
||||
}
|
||||
// 确保 productType 被正确传递
|
||||
if (!data.productType && formData.value.productType) {
|
||||
data.productType = formData.value.productType
|
||||
}
|
||||
const raw = await createRenewalOrder(data) as unknown
|
||||
const id = (raw as { data?: number })?.data ?? (raw as number)
|
||||
const isProduct00 = formData.value.productType === '00'
|
||||
// 检查产品类别是否为 00 或 02
|
||||
const productType = data.productType || formData.value.productType
|
||||
const needContract = (productType === '00' || productType === '02') && id
|
||||
|
||||
if (isProduct00 && id) {
|
||||
// 新增完成 → 跳转合同预览 → 客户签名 → 确认生成合同 → 合同预览页内再跳转详情
|
||||
console.log('新增订单提交 - productType:', productType, 'needContract:', needContract, 'id:', id, 'data:', data)
|
||||
|
||||
if (needContract) {
|
||||
// 新增完成 → 弹出选项:直接签名 / 分享签名
|
||||
uni.setStorageSync('renewalorder_list_need_refresh', true)
|
||||
toast.show('订单已创建,请选择签名方式')
|
||||
uni.showActionSheet({
|
||||
itemList: ['直接签名', '分享签名'],
|
||||
success: async (res) => {
|
||||
if (res.tapIndex === 0) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/car/renewalorder/contract-preview/index?id=${id}&from=add`,
|
||||
})
|
||||
toast.show('订单已创建,请完成客户签名后生成合同')
|
||||
toast.show('请完成客户签名后生成合同')
|
||||
} else if (res.tapIndex === 1) {
|
||||
try {
|
||||
const tokenRes = await createSignToken(id) as { data?: { signUrl?: string }; signUrl?: string }
|
||||
const signUrl = tokenRes?.data?.signUrl ?? tokenRes?.signUrl
|
||||
if (!signUrl) {
|
||||
toast.show('生成签名链接失败')
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/car/renewalorder/sign-share/index?signUrl=${encodeURIComponent(signUrl)}`,
|
||||
})
|
||||
toast.show('请分享链接给客户签名,签名成功后合同将自动生成')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
toast.error('操作失败')
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
} else {
|
||||
// 非 00 产品:直接提示成功并跳转详情
|
||||
toast.success('新增成功')
|
||||
@@ -806,14 +894,24 @@ async function handleSubmit() {
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
await Promise.all([getStoreList(), getProductList()])
|
||||
// 新增:默认选择门店第一项
|
||||
if (!formData.value.storeId && storeList.value.length > 0) {
|
||||
formData.value.storeId = storeList.value[0].id
|
||||
// 新增:默认选择当前用户所在门店;车辆购买方默认为当前用户门店名称,均可修改
|
||||
if (userStore.storeId != null && storeList.value.some(s => s.id === userStore.storeId)) {
|
||||
formData.value.storeId = userStore.storeId
|
||||
formData.value.serviceBuyer = userStore.storeName || storeList.value.find(s => s.id === userStore.storeId)?.storeName || ''
|
||||
} else if (storeList.value.length > 0) {
|
||||
if (!formData.value.storeId) formData.value.storeId = storeList.value[0].id
|
||||
if (!formData.value.serviceBuyer) formData.value.serviceBuyer = userStore.storeName || storeList.value[0].storeName || ''
|
||||
}
|
||||
// 新增:默认选择证件类型字典第一项
|
||||
if (!formData.value.certType && certTypeOptions.value.length > 0) {
|
||||
formData.value.certType = certTypeOptions.value[0].value
|
||||
}
|
||||
// 新增:默认选择结算方式为 '00'
|
||||
if (!formData.value.settlementMethod) {
|
||||
formData.value.settlementMethod = '00'
|
||||
}
|
||||
// 新增:录单人默认当前登录用户名称,可修改
|
||||
formData.value.inputUser = userStore.userInfo?.nickname || userStore.userInfo?.username || ''
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -115,6 +115,12 @@ async function load() {
|
||||
previewLoading.value = true
|
||||
try {
|
||||
await Promise.all([fetchOrder(), fetchHtml()])
|
||||
// 如果是新增订单后跳转过来的,提示用户可以签名
|
||||
if (fromAdd.value === 'add' || fromAdd.value === 'form_create') {
|
||||
setTimeout(() => {
|
||||
toast.show('请预览合同内容,确认无误后点击"点击签名"按钮进行签名')
|
||||
}, 500)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('加载合同预览失败', e)
|
||||
toast.error('加载失败')
|
||||
@@ -172,9 +178,13 @@ onLoad((options) => {
|
||||
})
|
||||
|
||||
/** 从签名页返回时刷新订单,以更新“已签名”状态 */
|
||||
onShow(() => {
|
||||
onShow(async () => {
|
||||
if (orderId.value && !previewLoading.value) {
|
||||
fetchOrder()
|
||||
await fetchOrder()
|
||||
// 如果是新增订单后跳转过来的(add 或 form_create),签名完成后自动提示可以生成合同
|
||||
if ((fromAdd.value === 'add' || fromAdd.value === 'form_create') && order.value?.customerSignatureUrl) {
|
||||
toast.show('签名已完成,请点击"确认生成合同"按钮生成合同')
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
/>
|
||||
</view>
|
||||
</wd-cell>
|
||||
<wd-cell title="购置税发票" title-width="180rpx">
|
||||
<wd-cell title="购置税凭证" title-width="180rpx">
|
||||
<view class="flex items-center justify-end">
|
||||
<wd-upload
|
||||
v-model:file-list="purchaseTaxInvoiceFileList"
|
||||
@@ -326,11 +326,10 @@
|
||||
/>
|
||||
<wd-input
|
||||
v-model="formData.originalWarrantyYears"
|
||||
label="原厂质保时长"
|
||||
label="产品年限"
|
||||
label-width="180rpx"
|
||||
clearable
|
||||
placeholder="请输入原厂质保时长"
|
||||
readonly
|
||||
placeholder="-"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="formData.originalWarrantyMileage"
|
||||
@@ -362,7 +361,7 @@
|
||||
placeholder="请输入录单人"
|
||||
readonly
|
||||
/>
|
||||
<wd-cell v-if="formData.productType === '00'" title="合同路径" title-width="180rpx">
|
||||
<wd-cell v-if="formData.productType === '00' || formData.productType === '02'" title="合同路径" title-width="180rpx">
|
||||
<view class="contract-area">
|
||||
<view class="contract-row contract-row-top">
|
||||
<view
|
||||
@@ -388,7 +387,7 @@
|
||||
</view>
|
||||
</wd-cell>
|
||||
<wd-textarea
|
||||
v-if="formData.productType === '00'"
|
||||
v-if="formData.productType === '00' || formData.productType === '02'"
|
||||
v-model="formData.contractRemark"
|
||||
label="合同备注"
|
||||
label-width="180rpx"
|
||||
@@ -424,7 +423,7 @@ import dayjs from 'dayjs'
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useToast } from 'wot-design-uni'
|
||||
import { getRenewalOrder } from '@/api/car/renewalorder'
|
||||
import { clearContractAndSignature, createSignToken, getRenewalOrder } from '@/api/car/renewalorder'
|
||||
import { findByProductType, getRenewalProduct } from '@/api/car/renewalproduct'
|
||||
import { findByProductType as findStoreByProductType } from '@/api/tire/store'
|
||||
import { getEnvBaseUrl, navigateBackPlus } from '@/utils'
|
||||
@@ -454,7 +453,7 @@ const certificateOfConformityFileList = ref<UploadFile[]>([]) // 合格证文件
|
||||
const odometerPhotoFileList = ref<UploadFile[]>([]) // 里程表照片文件列表
|
||||
const nameplatePhotoFileList = ref<UploadFile[]>([]) // 车名牌照片文件列表
|
||||
const carInvoiceFileList = ref<UploadFile[]>([]) // 购车发票文件列表
|
||||
const purchaseTaxInvoiceFileList = ref<UploadFile[]>([]) // 购置税发票文件列表
|
||||
const purchaseTaxInvoiceFileList = ref<UploadFile[]>([]) // 购置税凭证文件列表
|
||||
const businessInsurancePolicyFileList = ref<UploadFile[]>([]) // 商业险保单文件列表
|
||||
|
||||
const storeOptions = computed(() => storeList.value.map(item => ({ id: item.id, storeName: item.storeName })))
|
||||
@@ -488,7 +487,7 @@ const formData = ref<RenewalOrderVO>({
|
||||
productId: undefined,
|
||||
serviceProduct: undefined,
|
||||
productValidity: undefined,
|
||||
originalWarrantyYears: undefined,
|
||||
originalWarrantyYears: '3',
|
||||
originalWarrantyMileage: undefined,
|
||||
productFee: undefined,
|
||||
settlementMethod: undefined,
|
||||
@@ -718,19 +717,55 @@ async function getDetail() {
|
||||
}
|
||||
}
|
||||
|
||||
/** 预览并生成合同:跳转合同预览页,签名后可确认生成 */
|
||||
/** 生成/重新生成合同:弹出选项(直接签名 / 分享签名) */
|
||||
function handlePreviewContract() {
|
||||
if (!orderId.value) {
|
||||
toast.show('订单编号为空')
|
||||
return
|
||||
}
|
||||
if (formData.value.productType !== '00') {
|
||||
toast.show('当前产品类别不支持生成合同')
|
||||
if (formData.value.productType !== '00' && formData.value.productType !== '02') {
|
||||
toast.show('当前产品类别不支持生成合同,仅产品类别为 00 或 02 时可生成合同')
|
||||
return
|
||||
}
|
||||
uni.showActionSheet({
|
||||
itemList: ['直接签名', '分享签名'],
|
||||
success: (res) => {
|
||||
if (res.tapIndex === 0) {
|
||||
// 直接签名:跳转合同预览页
|
||||
uni.navigateTo({
|
||||
url: `/pages/car/renewalorder/contract-preview/index?id=${orderId.value}`,
|
||||
})
|
||||
} else if (res.tapIndex === 1) {
|
||||
// 分享签名:创建令牌后跳转分享页
|
||||
doOpenShareSign(Number(orderId.value))
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/** 分享签名:若有合同则先清空,再创建令牌并跳转分享页 */
|
||||
async function doOpenShareSign(id: number) {
|
||||
try {
|
||||
const hadContract = !!formData.value.contractUrl
|
||||
if (hadContract) {
|
||||
await clearContractAndSignature(id)
|
||||
formData.value.contractUrl = undefined
|
||||
formData.value.customerSignatureUrl = undefined
|
||||
toast.show('已清空合同与签名,请分享链接给客户签名')
|
||||
}
|
||||
const res = await createSignToken(id) as { data?: { signUrl?: string }; signUrl?: string }
|
||||
const signUrl = res?.data?.signUrl ?? res?.signUrl
|
||||
if (!signUrl) {
|
||||
toast.show('生成签名链接失败')
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/car/renewalorder/sign-share/index?signUrl=${encodeURIComponent(signUrl)}`,
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
toast.error('操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
/** 页面加载时获取路由参数 */
|
||||
|
||||
@@ -69,13 +69,20 @@
|
||||
clearable
|
||||
placeholder="请输入发动机号"
|
||||
/>
|
||||
<wd-datetime-picker
|
||||
v-model="formData.invoiceDate"
|
||||
label="发票日期"
|
||||
label-width="180rpx"
|
||||
type="date"
|
||||
placeholder="请选择发票日期"
|
||||
/>
|
||||
<wd-cell title="发票日期" title-width="180rpx">
|
||||
<template #value>
|
||||
<picker
|
||||
mode="date"
|
||||
:value="formData.invoiceDate || getDefaultDate()"
|
||||
@change="onInvoiceDateChange"
|
||||
>
|
||||
<view class="invoice-date-picker-value">
|
||||
{{ formData.invoiceDate || getDefaultDate() || '请选择发票日期' }}
|
||||
</view>
|
||||
</picker>
|
||||
</template>
|
||||
</wd-cell>
|
||||
<wd-input v-model="formData.invoiceDate" prop="invoiceDate" v-show="false" />
|
||||
<wd-input
|
||||
v-model="formData.invoiceAmount"
|
||||
label="发票金额"
|
||||
@@ -94,7 +101,7 @@
|
||||
clearable
|
||||
placeholder="请输入购买时公里数"
|
||||
/>
|
||||
<wd-cell title="行驶证" title-width="180rpx">
|
||||
<wd-cell title="行驶证" title-width="180rpx" required>
|
||||
<template #value>
|
||||
<view class="flex items-center justify-end">
|
||||
<wd-upload
|
||||
@@ -146,7 +153,7 @@
|
||||
</view>
|
||||
</template>
|
||||
</wd-cell>
|
||||
<wd-cell title="购车发票" title-width="180rpx">
|
||||
<wd-cell title="购车发票" title-width="180rpx" required>
|
||||
<template #value>
|
||||
<view class="flex items-center justify-end">
|
||||
<wd-upload
|
||||
@@ -159,7 +166,7 @@
|
||||
</view>
|
||||
</template>
|
||||
</wd-cell>
|
||||
<wd-cell title="购置税发票" title-width="180rpx">
|
||||
<wd-cell title="购置税凭证" title-width="180rpx" required>
|
||||
<template #value>
|
||||
<view class="flex items-center justify-end">
|
||||
<wd-upload
|
||||
@@ -195,6 +202,7 @@
|
||||
v-model="formData.serviceBuyer"
|
||||
label="服务购买方"
|
||||
label-width="180rpx"
|
||||
prop="serviceBuyer"
|
||||
clearable
|
||||
placeholder="请输入服务购买方"
|
||||
/>
|
||||
@@ -202,6 +210,7 @@
|
||||
v-model="formData.carBuyer"
|
||||
label="车辆购买方"
|
||||
label-width="180rpx"
|
||||
prop="carBuyer"
|
||||
clearable
|
||||
placeholder="请输入车辆购买方"
|
||||
/>
|
||||
@@ -210,6 +219,7 @@
|
||||
:columns="certTypeOptions"
|
||||
label="证件类型"
|
||||
label-width="180rpx"
|
||||
prop="certType"
|
||||
placeholder="请选择证件类型"
|
||||
value-key="value"
|
||||
label-key="label"
|
||||
@@ -218,6 +228,7 @@
|
||||
v-model="formData.certNo"
|
||||
label="证件号码"
|
||||
label-width="180rpx"
|
||||
prop="certNo"
|
||||
clearable
|
||||
placeholder="请输入证件号码"
|
||||
/>
|
||||
@@ -225,6 +236,7 @@
|
||||
v-model="formData.mobile"
|
||||
label="联系电话"
|
||||
label-width="180rpx"
|
||||
prop="mobile"
|
||||
type="number"
|
||||
clearable
|
||||
placeholder="请输入联系电话"
|
||||
@@ -249,6 +261,7 @@
|
||||
:columns="storeOptions"
|
||||
label="门店"
|
||||
label-width="180rpx"
|
||||
prop="storeId"
|
||||
placeholder="请选择门店"
|
||||
value-key="id"
|
||||
label-key="storeName"
|
||||
@@ -266,7 +279,7 @@
|
||||
</view>
|
||||
</template>
|
||||
</wd-cell>
|
||||
<wd-cell title="身份证正面" title-width="180rpx">
|
||||
<wd-cell title="身份证正面" title-width="180rpx" required>
|
||||
<template #value>
|
||||
<view class="flex items-center justify-end">
|
||||
<wd-upload
|
||||
@@ -279,7 +292,7 @@
|
||||
</view>
|
||||
</template>
|
||||
</wd-cell>
|
||||
<wd-cell title="身份证反面" title-width="180rpx">
|
||||
<wd-cell title="身份证反面" title-width="180rpx" required>
|
||||
<template #value>
|
||||
<view class="flex items-center justify-end">
|
||||
<wd-upload
|
||||
@@ -313,6 +326,7 @@
|
||||
v-model="formData.serviceProduct"
|
||||
label="服务产品"
|
||||
label-width="180rpx"
|
||||
prop="serviceProduct"
|
||||
readonly
|
||||
placeholder="选择产品后自动填充"
|
||||
/>
|
||||
@@ -320,6 +334,7 @@
|
||||
v-model="formData.productValidity"
|
||||
label="产品时效"
|
||||
label-width="180rpx"
|
||||
prop="productValidity"
|
||||
readonly
|
||||
placeholder="选择产品后自动填充"
|
||||
/>
|
||||
@@ -327,6 +342,7 @@
|
||||
:model-value="productTypeLabel"
|
||||
label="产品类别"
|
||||
label-width="180rpx"
|
||||
prop="productType"
|
||||
readonly
|
||||
placeholder="选择产品后自动填充"
|
||||
/>
|
||||
@@ -334,16 +350,18 @@
|
||||
v-model="formData.productFee"
|
||||
label="产品费用"
|
||||
label-width="180rpx"
|
||||
prop="productFee"
|
||||
type="digit"
|
||||
clearable
|
||||
placeholder="请输入产品费用"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="formData.originalWarrantyYears"
|
||||
label="原厂质保时长"
|
||||
label="产品年限"
|
||||
label-width="180rpx"
|
||||
required
|
||||
clearable
|
||||
placeholder="请输入原厂质保时长"
|
||||
placeholder="请输入产品年限(如:3)"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="formData.originalWarrantyMileage"
|
||||
@@ -358,21 +376,25 @@
|
||||
<!-- 选项卡4: 其他信息 -->
|
||||
<view v-show="tabIndex === 3">
|
||||
<wd-cell-group border title="其他信息">
|
||||
<wd-input
|
||||
<wd-picker
|
||||
v-model="formData.settlementMethod"
|
||||
:columns="settlementMethodOptions"
|
||||
label="结算方式"
|
||||
label-width="180rpx"
|
||||
clearable
|
||||
placeholder="请输入结算方式"
|
||||
prop="settlementMethod"
|
||||
placeholder="请选择结算方式"
|
||||
value-key="value"
|
||||
label-key="label"
|
||||
/>
|
||||
<wd-input
|
||||
v-model="formData.inputUser"
|
||||
label="录单人"
|
||||
label-width="180rpx"
|
||||
prop="inputUser"
|
||||
clearable
|
||||
placeholder="请输入录单人"
|
||||
/>
|
||||
<wd-cell v-if="formData.productType === '00'" title="合同路径" title-width="180rpx">
|
||||
<wd-cell v-if="showContractComponents" title="合同路径" title-width="180rpx">
|
||||
<template #value>
|
||||
<view class="contract-area">
|
||||
<view class="contract-row contract-row-top">
|
||||
@@ -397,7 +419,7 @@
|
||||
</template>
|
||||
</wd-cell>
|
||||
<wd-textarea
|
||||
v-if="formData.productType === '00'"
|
||||
v-if="showContractComponents"
|
||||
v-model="formData.contractRemark"
|
||||
label="合同备注"
|
||||
label-width="180rpx"
|
||||
@@ -457,11 +479,18 @@ import type { RenewalProductVO } from '@/api/car/renewalproduct'
|
||||
import type { StoreVO } from '@/api/tire/store'
|
||||
import type { UploadFile, UploadMethod } from 'wot-design-uni/components/wd-upload/types'
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, onMounted, ref, watch, nextTick } from 'vue'
|
||||
import { useToast, useMessage } from 'wot-design-uni'
|
||||
import { createRenewalOrder, getRenewalOrder, updateRenewalOrder } from '@/api/car/renewalorder'
|
||||
import {
|
||||
clearContractAndSignature,
|
||||
createRenewalOrder,
|
||||
createSignToken,
|
||||
getRenewalOrder,
|
||||
updateRenewalOrder,
|
||||
} from '@/api/car/renewalorder'
|
||||
import { findByProductType, getRenewalProduct } from '@/api/car/renewalproduct'
|
||||
import { findByProductType as findStoreByProductType } from '@/api/tire/store'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { getEnvBaseUrl, navigateBackPlus } from '@/utils'
|
||||
import { getDictLabel, getStrDictOptions } from '@/hooks/useDict'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
@@ -482,7 +511,8 @@ definePage({
|
||||
|
||||
const toast = useToast()
|
||||
const message = useMessage()
|
||||
const getTitle = computed(() => props.id ? '编辑续保订单' : '新增续保订单')
|
||||
const userStore = useUserStore()
|
||||
const getTitle = computed(() => props.id ? '编辑订单' : '新增订单')
|
||||
const formLoading = ref(false)
|
||||
const tabIndex = ref(0) // 当前选项卡索引
|
||||
const storeList = ref<StoreVO[]>([]) // 门店列表
|
||||
@@ -496,17 +526,23 @@ const certificateOfConformityFileList = ref<UploadFile[]>([]) // 合格证文件
|
||||
const odometerPhotoFileList = ref<UploadFile[]>([]) // 里程表照片文件列表
|
||||
const nameplatePhotoFileList = ref<UploadFile[]>([]) // 车名牌照片文件列表
|
||||
const carInvoiceFileList = ref<UploadFile[]>([]) // 购车发票文件列表
|
||||
const purchaseTaxInvoiceFileList = ref<UploadFile[]>([]) // 购置税发票文件列表
|
||||
const purchaseTaxInvoiceFileList = ref<UploadFile[]>([]) // 购置税凭证文件列表
|
||||
const businessInsurancePolicyFileList = ref<UploadFile[]>([]) // 商业险保单文件列表
|
||||
|
||||
const storeOptions = computed(() => storeList.value.map(item => ({ id: item.id, storeName: item.storeName })))
|
||||
const productOptions = computed(() => productList.value.map(item => ({ id: item.id, productName: item.productName })))
|
||||
const certTypeOptions = computed(() => getStrDictOptions('car_renewal_identity_type'))
|
||||
const settlementMethodOptions = computed(() => getStrDictOptions(DICT_TYPE.CAR_RENEWAL_PAY_METHOD))
|
||||
// 根据 productType 值获取字典 label
|
||||
const productTypeLabel = computed(() => {
|
||||
if (!formData.value.productType) return ''
|
||||
return getDictLabel(DICT_TYPE.CAR_RENEWAL_PRODUCT_TYPE, formData.value.productType) || formData.value.productType
|
||||
})
|
||||
// 判断是否显示合同相关组件(仅当产品类别为00或02时显示)
|
||||
const showContractComponents = computed(() => {
|
||||
return formData.value.productType === '00' || formData.value.productType === '02'
|
||||
})
|
||||
|
||||
|
||||
const formData = ref<RenewalOrderVO>({
|
||||
id: undefined,
|
||||
@@ -518,7 +554,14 @@ const formData = ref<RenewalOrderVO>({
|
||||
purchaseMileage: undefined,
|
||||
engineNo: undefined,
|
||||
vin: undefined,
|
||||
invoiceDate: undefined,
|
||||
invoiceDate: (() => {
|
||||
// 设置默认值为当前日期,避免 wd-datetime-picker 收到 undefined 报错
|
||||
const today = new Date()
|
||||
const year = today.getFullYear()
|
||||
const month = String(today.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(today.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
})(),
|
||||
invoiceUrl: undefined,
|
||||
serviceBuyer: undefined,
|
||||
carBuyer: undefined,
|
||||
@@ -532,10 +575,10 @@ const formData = ref<RenewalOrderVO>({
|
||||
serviceProduct: undefined,
|
||||
productType: undefined,
|
||||
productValidity: undefined,
|
||||
originalWarrantyYears: undefined,
|
||||
originalWarrantyYears: '3',
|
||||
originalWarrantyMileage: undefined,
|
||||
productFee: undefined,
|
||||
settlementMethod: undefined,
|
||||
settlementMethod: '00',
|
||||
remark: undefined,
|
||||
inputUser: undefined,
|
||||
contractRemark: undefined,
|
||||
@@ -558,9 +601,23 @@ const formRules = {
|
||||
carModel: [{ required: true, message: '车型不能为空' }],
|
||||
vin: [{ required: true, message: '车架号不能为空' }],
|
||||
engineNo: [{ required: true, message: '发动机号不能为空' }],
|
||||
invoiceDate: [{ required: true, message: '发票日期不能为空' }],
|
||||
invoiceAmount: [{ required: true, message: '发票金额不能为空' }],
|
||||
purchaseMileage: [{ required: true, message: '购买时公里数不能为空' }],
|
||||
serviceBuyer: [{ required: true, message: '服务购买方不能为空' }],
|
||||
carBuyer: [{ required: true, message: '车辆购买方不能为空' }],
|
||||
certType: [{ required: true, message: '证件类型不能为空' }],
|
||||
certNo: [{ required: true, message: '证件号码不能为空' }],
|
||||
mobile: [{ required: true, message: '联系电话不能为空' }],
|
||||
storeId: [{ required: true, message: '门店不能为空' }],
|
||||
productId: [{ required: true, message: '续保产品不能为空' }],
|
||||
serviceProduct: [{ required: true, message: '服务产品不能为空' }],
|
||||
productType: [{ required: true, message: '产品类别不能为空' }],
|
||||
productValidity: [{ required: true, message: '产品时效不能为空' }],
|
||||
productFee: [{ required: true, message: '产品费用不能为空' }],
|
||||
settlementMethod: [{ required: true, message: '结算方式不能为空' }],
|
||||
inputUser: [{ required: true, message: '录单人不能为空' }],
|
||||
originalWarrantyYears: [{ required: true, message: '产品年限不能为空' }],
|
||||
}
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
@@ -595,13 +652,16 @@ async function handleNextTab() {
|
||||
let validFields: string[] = []
|
||||
if (tabIndex.value === 0) {
|
||||
// 验证车辆信息
|
||||
validFields = ['licensePlate', 'carBrand', 'carModel', 'vin', 'engineNo', 'invoiceAmount', 'purchaseMileage']
|
||||
validFields = ['licensePlate', 'carBrand', 'carModel', 'vin', 'engineNo', 'invoiceDate', 'invoiceAmount', 'purchaseMileage']
|
||||
} else if (tabIndex.value === 1) {
|
||||
// 购买方信息无必填项
|
||||
validFields = []
|
||||
// 验证购买方信息
|
||||
validFields = ['serviceBuyer', 'carBuyer', 'certType', 'certNo', 'mobile', 'storeId']
|
||||
} else if (tabIndex.value === 2) {
|
||||
// 验证产品信息
|
||||
validFields = ['productId']
|
||||
validFields = ['productId', 'serviceProduct', 'productType', 'productValidity', 'productFee', 'originalWarrantyYears']
|
||||
} else if (tabIndex.value === 3) {
|
||||
// 验证其他信息
|
||||
validFields = ['settlementMethod', 'inputUser']
|
||||
}
|
||||
|
||||
if (validFields.length > 0) {
|
||||
@@ -636,6 +696,25 @@ async function getProductList() {
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取默认日期 */
|
||||
function getDefaultDate() {
|
||||
const today = new Date()
|
||||
const year = today.getFullYear()
|
||||
const month = String(today.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(today.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
|
||||
/** 发票日期选择(原生 picker,避免 wd-datetime-picker Invalid array length 错误) */
|
||||
function onInvoiceDateChange(e: { detail: { value: string } }) {
|
||||
const v = e.detail?.value
|
||||
if (v && /^\d{4}-\d{2}-\d{2}$/.test(v)) {
|
||||
formData.value.invoiceDate = v
|
||||
} else {
|
||||
formData.value.invoiceDate = getDefaultDate()
|
||||
}
|
||||
}
|
||||
|
||||
/** 处理产品选择变化 */
|
||||
async function handleProductChange({ value }: { value: number }) {
|
||||
if (!value) {
|
||||
@@ -751,20 +830,54 @@ const contractFileName = computed(() => {
|
||||
return formData.value.contractUrl ? getFileNameFromUrl(formData.value.contractUrl) : '合同.pdf'
|
||||
})
|
||||
|
||||
/** 预览并生成合同:跳转合同预览页,签名后可确认生成 */
|
||||
/** 生成/重新生成合同:弹出选项(直接签名 / 分享签名) */
|
||||
function handlePreviewContract() {
|
||||
const id = props.id || formData.value.id
|
||||
if (!id) {
|
||||
toast.show('请先保存订单后再生成合同')
|
||||
return
|
||||
}
|
||||
if (formData.value.productType !== '00') {
|
||||
toast.show('当前产品类别不支持生成合同')
|
||||
if (formData.value.productType !== '00' && formData.value.productType !== '02') {
|
||||
toast.show('当前产品类别不支持生成合同,仅产品类别为 00 或 02 时可生成合同')
|
||||
return
|
||||
}
|
||||
uni.showActionSheet({
|
||||
itemList: ['直接签名', '分享签名'],
|
||||
success: (res) => {
|
||||
if (res.tapIndex === 0) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/car/renewalorder/contract-preview/index?id=${id}`,
|
||||
})
|
||||
} else if (res.tapIndex === 1) {
|
||||
doOpenShareSign(Number(id))
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/** 分享签名:若有合同则先清空,再创建令牌并跳转分享页 */
|
||||
async function doOpenShareSign(id: number) {
|
||||
try {
|
||||
const hadContract = !!formData.value.contractUrl
|
||||
if (hadContract) {
|
||||
await clearContractAndSignature(id)
|
||||
formData.value.contractUrl = undefined
|
||||
formData.value.customerSignatureUrl = undefined
|
||||
toast.show('已清空合同与签名,请分享链接给客户签名')
|
||||
}
|
||||
const res = await createSignToken(id) as { data?: { signUrl?: string }; signUrl?: string }
|
||||
const signUrl = res?.data?.signUrl ?? res?.signUrl
|
||||
if (!signUrl) {
|
||||
toast.show('生成签名链接失败')
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/car/renewalorder/sign-share/index?signUrl=${encodeURIComponent(signUrl)}`,
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
toast.error('操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
/** 处理合同下载并打开 */
|
||||
@@ -817,15 +930,37 @@ async function getDetail() {
|
||||
try {
|
||||
const orderId = copyId.value || Number(props.id)
|
||||
const data = await getRenewalOrder(orderId)
|
||||
// 处理发票日期
|
||||
// 处理发票日期 - 先设置一个默认值,避免组件初始化时收到 undefined
|
||||
const getDefaultDate = () => {
|
||||
const today = new Date()
|
||||
const year = today.getFullYear()
|
||||
const month = String(today.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(today.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
|
||||
// 先设置默认值
|
||||
formData.value.invoiceDate = getDefaultDate()
|
||||
|
||||
// 然后处理实际日期
|
||||
if (data.invoiceDate) {
|
||||
let processedDate: string | null = null
|
||||
if (typeof data.invoiceDate === 'string') {
|
||||
formData.value.invoiceDate = data.invoiceDate.split(' ')[0] // 只取日期部分
|
||||
const dateStr = data.invoiceDate.split(' ')[0] // 只取日期部分
|
||||
// 验证日期格式,确保符合 YYYY-MM-DD
|
||||
if (/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
|
||||
processedDate = dateStr
|
||||
}
|
||||
} else if (data.invoiceDate instanceof Date) {
|
||||
const year = data.invoiceDate.getFullYear()
|
||||
const month = String(data.invoiceDate.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(data.invoiceDate.getDate()).padStart(2, '0')
|
||||
formData.value.invoiceDate = `${year}-${month}-${day}`
|
||||
processedDate = `${year}-${month}-${day}`
|
||||
}
|
||||
|
||||
// 如果处理后的日期有效,使用它;否则保持默认值
|
||||
if (processedDate && /^\d{4}-\d{2}-\d{2}$/.test(processedDate)) {
|
||||
formData.value.invoiceDate = processedDate
|
||||
}
|
||||
}
|
||||
// 处理多张图片的 JSON 数组字段:如果后端返回的是字符串,需要解析为数组
|
||||
@@ -850,18 +985,103 @@ async function getDetail() {
|
||||
data.businessInsurancePolicyUrls = []
|
||||
}
|
||||
}
|
||||
// 如果是复制创建,清除主键和合同路径等与原订单绑定的信息
|
||||
// 如果是复制创建,清除主键、合同路径和所有图片相关字段
|
||||
if (copyId.value) {
|
||||
// 确保发票日期格式正确,如果为空或无效则设置为当前日期
|
||||
let invoiceDate = data.invoiceDate
|
||||
if (!invoiceDate || invoiceDate === 'Invalid Date' || invoiceDate === 'null' || invoiceDate === 'undefined') {
|
||||
const today = new Date()
|
||||
const year = today.getFullYear()
|
||||
const month = String(today.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(today.getDate()).padStart(2, '0')
|
||||
invoiceDate = `${year}-${month}-${day}`
|
||||
} else if (typeof invoiceDate === 'string') {
|
||||
// 确保日期格式为 YYYY-MM-DD
|
||||
invoiceDate = invoiceDate.split(' ')[0]
|
||||
// 验证日期格式
|
||||
if (!/^\d{4}-\d{2}-\d{2}$/.test(invoiceDate)) {
|
||||
const today = new Date()
|
||||
const year = today.getFullYear()
|
||||
const month = String(today.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(today.getDate()).padStart(2, '0')
|
||||
invoiceDate = `${year}-${month}-${day}`
|
||||
}
|
||||
}
|
||||
|
||||
const copyData: any = {
|
||||
...data,
|
||||
id: undefined,
|
||||
contractUrl: '',
|
||||
invoiceDate: invoiceDate || (() => {
|
||||
// 如果发票日期仍然无效,设置为当前日期
|
||||
const today = new Date()
|
||||
const year = today.getFullYear()
|
||||
const month = String(today.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(today.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
})(),
|
||||
// 清空所有图片相关字段
|
||||
customerSignatureUrl: undefined,
|
||||
idCardFrontUrl: undefined,
|
||||
idCardBackUrl: undefined,
|
||||
drivingLicenseUrl: undefined,
|
||||
certificateOfConformityUrl: undefined,
|
||||
odometerPhotoUrl: undefined,
|
||||
nameplatePhotoUrl: undefined,
|
||||
carInvoiceUrls: [],
|
||||
purchaseTaxInvoiceUrls: [],
|
||||
businessInsurancePolicyUrls: [],
|
||||
}
|
||||
// 确保 invoiceDate 格式正确
|
||||
if (!copyData.invoiceDate || !/^\d{4}-\d{2}-\d{2}$/.test(copyData.invoiceDate)) {
|
||||
const today = new Date()
|
||||
const year = today.getFullYear()
|
||||
const month = String(today.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(today.getDate()).padStart(2, '0')
|
||||
copyData.invoiceDate = `${year}-${month}-${day}`
|
||||
}
|
||||
// 确保 invoiceDate 是有效的字符串
|
||||
if (!copyData.invoiceDate || typeof copyData.invoiceDate !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(copyData.invoiceDate)) {
|
||||
const today = new Date()
|
||||
const year = today.getFullYear()
|
||||
const month = String(today.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(today.getDate()).padStart(2, '0')
|
||||
copyData.invoiceDate = `${year}-${month}-${day}`
|
||||
}
|
||||
|
||||
formData.value = copyData
|
||||
} else {
|
||||
formData.value = data
|
||||
// 使用 nextTick 确保组件正确更新,然后再次验证日期值
|
||||
await nextTick()
|
||||
// 强制确保日期值有效
|
||||
if (!formData.value.invoiceDate || typeof formData.value.invoiceDate !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(formData.value.invoiceDate)) {
|
||||
const today = new Date()
|
||||
const year = today.getFullYear()
|
||||
const month = String(today.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(today.getDate()).padStart(2, '0')
|
||||
formData.value.invoiceDate = `${year}-${month}-${day}`
|
||||
}
|
||||
// 初始化文件列表
|
||||
// 复制创建模式下,清空所有文件列表
|
||||
customerSignatureFileList.value = []
|
||||
idCardFrontFileList.value = []
|
||||
idCardBackFileList.value = []
|
||||
drivingLicenseFileList.value = []
|
||||
certificateOfConformityFileList.value = []
|
||||
odometerPhotoFileList.value = []
|
||||
nameplatePhotoFileList.value = []
|
||||
carInvoiceFileList.value = []
|
||||
purchaseTaxInvoiceFileList.value = []
|
||||
businessInsurancePolicyFileList.value = []
|
||||
} else {
|
||||
// 编辑模式下,确保发票日期有效
|
||||
if (!data.invoiceDate || !/^\d{4}-\d{2}-\d{2}$/.test(data.invoiceDate)) {
|
||||
const today = new Date()
|
||||
const year = today.getFullYear()
|
||||
const month = String(today.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(today.getDate()).padStart(2, '0')
|
||||
data.invoiceDate = `${year}-${month}-${day}`
|
||||
}
|
||||
formData.value = data
|
||||
// 编辑模式下,初始化文件列表
|
||||
customerSignatureFileList.value = urlToFileList(data.customerSignatureUrl)
|
||||
idCardFrontFileList.value = urlToFileList(data.idCardFrontUrl)
|
||||
idCardBackFileList.value = urlToFileList(data.idCardBackUrl)
|
||||
@@ -872,6 +1092,7 @@ async function getDetail() {
|
||||
carInvoiceFileList.value = urlToFileList(data.carInvoiceUrls)
|
||||
purchaseTaxInvoiceFileList.value = urlToFileList(data.purchaseTaxInvoiceUrls)
|
||||
businessInsurancePolicyFileList.value = urlToFileList(data.businessInsurancePolicyUrls)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取订单详情失败:', error)
|
||||
toast.show('获取订单详情失败')
|
||||
@@ -889,9 +1110,36 @@ async function handleSubmit() {
|
||||
return
|
||||
}
|
||||
|
||||
// 必传文件校验:行驶证、购车发票、购置税凭证、身份证正反面
|
||||
if (!formData.value.drivingLicenseUrl) {
|
||||
toast.show('请上传行驶证')
|
||||
return
|
||||
}
|
||||
if (!formData.value.carInvoiceUrls?.length) {
|
||||
toast.show('请上传购车发票')
|
||||
return
|
||||
}
|
||||
if (!formData.value.purchaseTaxInvoiceUrls?.length) {
|
||||
toast.show('请上传购置税凭证')
|
||||
return
|
||||
}
|
||||
if (!formData.value.idCardFrontUrl) {
|
||||
toast.show('请上传身份证正面')
|
||||
return
|
||||
}
|
||||
if (!formData.value.idCardBackUrl) {
|
||||
toast.show('请上传身份证反面')
|
||||
return
|
||||
}
|
||||
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = { ...formData.value }
|
||||
// 确保 productType 被正确传递
|
||||
if (!data.productType && formData.value.productType) {
|
||||
data.productType = formData.value.productType
|
||||
}
|
||||
|
||||
// 复制创建和新增都走新增接口
|
||||
if (props.id && !copyId.value) {
|
||||
await updateRenewalOrder(data)
|
||||
@@ -911,13 +1159,43 @@ async function handleSubmit() {
|
||||
} else {
|
||||
const res = (await createRenewalOrder(data)) as unknown
|
||||
const newId = (res as { data?: number })?.data ?? (res as number)
|
||||
const needContract = formData.value.productType === '00' && newId
|
||||
// 检查产品类别是否为 00 或 02
|
||||
const productType = data.productType || formData.value.productType
|
||||
const needContract = (productType === '00' || productType === '02') && newId
|
||||
|
||||
console.log('表单提交 - productType:', productType, 'needContract:', needContract, 'id:', newId, 'data:', data)
|
||||
|
||||
if (needContract) {
|
||||
// 新增完成 → 弹出选项:直接签名 / 分享签名
|
||||
uni.setStorageSync('renewalorder_list_need_refresh', true)
|
||||
toast.show('订单已创建,请选择签名方式')
|
||||
uni.showActionSheet({
|
||||
itemList: ['直接签名', '分享签名'],
|
||||
success: async (res) => {
|
||||
if (res.tapIndex === 0) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/car/renewalorder/contract-preview/index?id=${newId}&from=form_create`,
|
||||
})
|
||||
toast.show('订单已创建,请完成客户签名后生成合同')
|
||||
toast.show('请完成客户签名后生成合同')
|
||||
} else if (res.tapIndex === 1) {
|
||||
try {
|
||||
const tokenRes = await createSignToken(newId) as { data?: { signUrl?: string }; signUrl?: string }
|
||||
const signUrl = tokenRes?.data?.signUrl ?? tokenRes?.signUrl
|
||||
if (!signUrl) {
|
||||
toast.show('生成签名链接失败')
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/car/renewalorder/sign-share/index?signUrl=${encodeURIComponent(signUrl)}`,
|
||||
})
|
||||
toast.show('请分享链接给客户签名,签名成功后合同将自动生成')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
toast.error('操作失败')
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
} else {
|
||||
toast.success(copyId.value ? '复制创建成功' : '新增成功')
|
||||
uni.setStorageSync('renewalorder_list_need_refresh', true)
|
||||
@@ -959,17 +1237,32 @@ onShow(() => {
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
await Promise.all([getStoreList(), getProductList()])
|
||||
// 新增:默认选择门店第一项(编辑不覆盖)
|
||||
if ((!props.id && !copyId.value) && !formData.value.storeId && storeList.value.length > 0) {
|
||||
formData.value.storeId = storeList.value[0].id
|
||||
// 新增:默认选择当前用户所在门店;车辆购买方默认为当前用户门店名称,均可修改
|
||||
if (!props.id && !copyId.value && storeList.value.length > 0) {
|
||||
if (userStore.storeId != null && storeList.value.some((s: StoreVO) => s.id === userStore.storeId)) {
|
||||
formData.value.storeId = userStore.storeId
|
||||
formData.value.serviceBuyer = userStore.storeName || storeList.value.find((s: StoreVO) => s.id === userStore.storeId)?.storeName || ''
|
||||
} else {
|
||||
if (!formData.value.storeId) formData.value.storeId = storeList.value[0].id
|
||||
if (!formData.value.serviceBuyer) formData.value.serviceBuyer = userStore.storeName || storeList.value[0].storeName || ''
|
||||
}
|
||||
}
|
||||
// 新增:默认选择证件类型字典第一项(编辑不覆盖)
|
||||
if ((!props.id && !copyId.value) && !formData.value.certType && certTypeOptions.value.length > 0) {
|
||||
formData.value.certType = certTypeOptions.value[0].value
|
||||
}
|
||||
// 新增:默认选择结算方式为 '00'(编辑不覆盖)
|
||||
if ((!props.id && !copyId.value) && !formData.value.settlementMethod) {
|
||||
formData.value.settlementMethod = '00'
|
||||
}
|
||||
// 新增:录单人默认当前登录用户名称,可修改
|
||||
if (!props.id && !copyId.value) {
|
||||
formData.value.inputUser = userStore.userInfo?.nickname || userStore.userInfo?.username || ''
|
||||
}
|
||||
// 如果是编辑模式或复制创建模式,加载详情
|
||||
if (props.id || copyId.value) {
|
||||
await getDetail()
|
||||
@@ -978,6 +1271,10 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.invoice-date-picker-value {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
.contract-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -20,20 +20,24 @@
|
||||
<view class="relative p-24rpx" @click="handleDetail(item)">
|
||||
<view class="flex items-center justify-between mb-16rpx">
|
||||
<view class="text-32rpx text-[#333] font-semibold">
|
||||
{{ item.licensePlate || '-' }}
|
||||
{{ item.serviceBuyer || '-' }}
|
||||
</view>
|
||||
<view class="text-24rpx text-[#999]">
|
||||
{{ item.createTime ? formatDate(item.createTime) : '-' }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="space-y-12rpx text-26rpx text-[#666]">
|
||||
<view class="flex items-center justify-between">
|
||||
<text>车牌号:</text>
|
||||
<text>{{ item.licensePlate || '-' }}</text>
|
||||
</view>
|
||||
<view class="flex items-center justify-between">
|
||||
<text>品牌车型:</text>
|
||||
<text>{{ item.carBrand }} {{ item.carModel }}</text>
|
||||
</view>
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="flex items-center justify-between" @click.stop @click="handleEdit(item)">
|
||||
<text>服务购买方:</text>
|
||||
<text>{{ item.serviceBuyer || '-' }}</text>
|
||||
<text class="text-[#409eff] underline">{{ item.serviceBuyer || '-' }}</text>
|
||||
</view>
|
||||
<view class="flex items-center justify-between">
|
||||
<text>联系电话:</text>
|
||||
@@ -43,10 +47,18 @@
|
||||
<text>门店:</text>
|
||||
<text>{{ item.storeName || '-' }}</text>
|
||||
</view>
|
||||
<view class="flex items-center justify-between">
|
||||
<text>产品年限:</text>
|
||||
<text>{{ item.originalWarrantyYears || '-' }}</text>
|
||||
</view>
|
||||
<view class="flex items-center justify-between">
|
||||
<text>产品费用:</text>
|
||||
<text class="text-[#1890ff]">¥{{ item.productFee || 0 }}</text>
|
||||
</view>
|
||||
<view class="flex items-center justify-between">
|
||||
<text>结算方式:</text>
|
||||
<text>{{ getSettlementMethodLabel(item.settlementMethod) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 底部操作按钮 -->
|
||||
@@ -100,6 +112,8 @@ import { useToast } from 'wot-design-uni'
|
||||
import { navigateBackPlus } from '@/utils'
|
||||
import { formatDate } from '@/utils/date'
|
||||
import SearchForm from './components/search-form.vue'
|
||||
import { getDictLabel } from '@/hooks/useDict'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
@@ -177,6 +191,19 @@ function handleDetail(item: RenewalOrderVO) {
|
||||
})
|
||||
}
|
||||
|
||||
/** 编辑订单 */
|
||||
function handleEdit(item: RenewalOrderVO) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/car/renewalorder/form/index?id=${item.id}`,
|
||||
})
|
||||
}
|
||||
|
||||
/** 获取结算方式字典标签 */
|
||||
function getSettlementMethodLabel(value?: string) {
|
||||
if (!value) return '-'
|
||||
return getDictLabel(DICT_TYPE.CAR_RENEWAL_PAY_METHOD, value) || value
|
||||
}
|
||||
|
||||
/** 复制创建订单:基于原订单预填表单,进入新增模式 */
|
||||
function handleCopy(item: RenewalOrderVO) {
|
||||
uni.navigateTo({
|
||||
|
||||
133
src/pages/car/renewalorder/sign-share/index.vue
Normal file
133
src/pages/car/renewalorder/sign-share/index.vue
Normal file
@@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<view class="yd-page-container sign-share-page">
|
||||
<view v-if="fullUrl" class="sign-share-content">
|
||||
<view class="tip-box">
|
||||
<text class="tip-text">请将下方链接或二维码分享给客户,客户打开链接完成签名后合同将自动生成。</text>
|
||||
</view>
|
||||
<view class="link-box">
|
||||
<text class="link-label">签名链接</text>
|
||||
<text class="link-value" selectable>{{ fullUrl }}</text>
|
||||
</view>
|
||||
<view class="tip-small">链接有效期 12 小时</view>
|
||||
<wd-button type="primary" block class="copy-btn" @click="handleCopy">
|
||||
复制链接
|
||||
</wd-button>
|
||||
</view>
|
||||
<view v-else class="loading-wrap">
|
||||
<view class="loading-dot" />
|
||||
<text class="text-28rpx text-[#999] mt-24rpx">加载中...</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { ref } from 'vue'
|
||||
import { useToast } from 'wot-design-uni'
|
||||
import { getEnvBaseUrlRoot } from '@/utils'
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '分享签名',
|
||||
navigationStyle: 'default',
|
||||
},
|
||||
})
|
||||
|
||||
const toast = useToast()
|
||||
const fullUrl = ref('')
|
||||
|
||||
function handleCopy() {
|
||||
if (!fullUrl.value) return
|
||||
uni.setClipboardData({
|
||||
data: fullUrl.value,
|
||||
success: () => {
|
||||
toast.success('链接已复制')
|
||||
setTimeout(() => {
|
||||
// 关闭分享页 + 上一页(新增/编辑/详情),直接回到列表
|
||||
uni.navigateBack({ delta: 2 })
|
||||
}, 500)
|
||||
},
|
||||
fail: () => {
|
||||
toast.error('复制失败')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
let signUrl = options?.signUrl || ''
|
||||
if (signUrl) {
|
||||
try {
|
||||
signUrl = decodeURIComponent(signUrl)
|
||||
} catch {
|
||||
// 已为明文路径则忽略
|
||||
}
|
||||
const base = getEnvBaseUrlRoot()
|
||||
fullUrl.value = base + (signUrl.startsWith('/') ? signUrl : `/${signUrl}`)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.sign-share-page {
|
||||
min-height: 100vh;
|
||||
}
|
||||
.sign-share-content {
|
||||
padding: 32rpx;
|
||||
}
|
||||
.tip-box {
|
||||
margin-bottom: 32rpx;
|
||||
padding: 24rpx;
|
||||
background: #f0f9ff;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
.tip-text {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.link-box {
|
||||
margin-bottom: 16rpx;
|
||||
padding: 24rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
.link-label {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
.link-value {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
word-break: break-all;
|
||||
}
|
||||
.tip-small {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
.copy-btn {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
.loading-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 80rpx 0;
|
||||
}
|
||||
.loading-dot {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border: 4rpx solid #e5e5e5;
|
||||
border-top-color: #3b82f6;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -55,6 +55,8 @@ const BPM_DICT = {
|
||||
/** ========== CAR - 车辆模块 ========== */
|
||||
const CAR_DICT = {
|
||||
CAR_RENEWAL_PRODUCT_TYPE: 'car_renewal_product_type', // 续保产品类别
|
||||
CAR_RENEWAL_PAY_METHOD: 'car_renewal_pay_method', // 续保结算方式
|
||||
CAR_RENEWAL_YEAR: 'car_renewal_year', // 续保生效年限
|
||||
} as const
|
||||
|
||||
/** 字典类型枚举 - 统一导出 */
|
||||
|
||||
@@ -123,9 +123,9 @@ export function getEnvBaseUrl() {
|
||||
|
||||
// # 有些同学可能需要在微信小程序里面根据 develop、trial、release 分别设置上传地址,参考代码如下。
|
||||
// TODO @芋艿:这个后续也要调整。
|
||||
const VITE_SERVER_BASEURL__WEIXIN_DEVELOP = 'http://1.14.158.154:48080/admin-api'
|
||||
const VITE_SERVER_BASEURL__WEIXIN_TRIAL = 'http://1.14.158.154:48080/admin-api'
|
||||
const VITE_SERVER_BASEURL__WEIXIN_RELEASE = 'http://1.14.158.154:48080/admin-api'
|
||||
const VITE_SERVER_BASEURL__WEIXIN_DEVELOP = 'https://api.tuanbanlv.com/admin-api'
|
||||
const VITE_SERVER_BASEURL__WEIXIN_TRIAL = 'https://api.tuanbanlv.com/admin-api'
|
||||
const VITE_SERVER_BASEURL__WEIXIN_RELEASE = 'https://api.tuanbanlv.com/admin-api'
|
||||
|
||||
// 微信小程序端环境区分
|
||||
if (isMpWeixin) {
|
||||
@@ -150,17 +150,18 @@ export function getEnvBaseUrl() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据环境变量,获取基础路径的根路径,比如 http://1.14.158.154:48080
|
||||
* 根据环境变量,获取基础路径的根路径,比如 https://api.tuanbanlv.com
|
||||
*
|
||||
* add by 芋艿:用户类似 websocket 这种需要根路径的场景
|
||||
* 注意:小程序环境无 URL 构造函数,使用正则解析
|
||||
*
|
||||
* @return 根路径
|
||||
*/
|
||||
export function getEnvBaseUrlRoot() {
|
||||
const baseUrl = getEnvBaseUrl()
|
||||
// 提取根路径
|
||||
const urlObj = new URL(baseUrl)
|
||||
return urlObj.origin
|
||||
// 小程序环境 URL 可能不可用,用正则提取 origin
|
||||
const match = baseUrl.match(/^(https?:\/\/[^/]+)/)
|
||||
return match ? match[1] : baseUrl
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user