first commit

This commit is contained in:
2026-02-26 09:32:03 +08:00
commit 36a8e4c51b
845 changed files with 116474 additions and 0 deletions
@@ -0,0 +1,604 @@
<script lang="ts" setup>
import {z} from "zod";
import * as R from 'ramda'
import {debounce} from "throttle-debounce";
import Config from "@/config";
import ChooseImage from "@/components/choose-image/choose-image.vue";
import {
appMerchantCategoryListGet,
appMerchantGetFirstShopSettledGet,
appShopSettlementApplyPost,
appShopSettlementDetailGet
} from "@/service";
import useEventEmit from "@/hooks/useEventEmit";
import {EventEnum} from "@/constant/enums";
import {useUserStore} from "@/store";
const userStore = useUserStore()
const {t} = useI18n();
// 审核状态 null 未提交 1审核中 2审核通过 3审核驳回
const auditStatus = ref(null);
const type = ref('')
const rejectReason = ref('')
const id = ref('')
const shopInfo = ref({})
onLoad((options) => {
appMerchantGetFirstShopSettledGet({}).then(res=> {
console.log(res)
if(res.data) {
shopInfo.value = res.data
auditStatus.value = Number(res.data.auditStatus)
rejectReason.value = res.data.rejectReason
}
})
})
// 点击了重新编辑
function clickInitEdit() {
auditStatus.value = null
// appShopSettlementDetailGet({
// params: {
// id: id.value
// }
// }).then(res => {
//
// })
formData.value = shopInfo.value
formData.value.idCardImage = shopInfo.value.idCardImage || ''
formData.value.passportImage = shopInfo.value.passportImage || ''
formData.value.foodOperationCertificateImage = shopInfo.value.foodOperationCertificateImage || ''
handleShowTypePicker(false)
}
// 表单数据
const formData = ref({
shopName: "",
shopTypeId: "", // 所属类型(关联店铺类型表)
category: "",
street: "", // 街道
city: "", // 市
state: "", // 州
detailAddress: "",
description: "",
idCardImage: "",
passportImage: "",
foodOperationCertificateImage: "",
});
// 表单验证schema
const FormSchema = z.object({
shopName: z.string().min(1, {message: t("pages-user.store-settle-in.schema.storeName")}),
category: z.string().min(1, {message: t("pages-user.store-settle-in.schema.category")}),
detailAddress: z.string().min(1, {message: t("pages-user.store-settle-in.schema.detailAddress")}),
description: z.string().min(1, {message: t("pages-user.store-settle-in.schema.description")}),
// idCardImage: z.string().min(1, {message: t("pages-user.store-settle-in.schema.idCardFront")}),
// passportImage: z.string().min(1, {message: t("pages-user.store-settle-in.schema.idCardBack")}),
// foodOperationCertificateImage: z.string().min(1, {message: t("pages-user.store-settle-in.schema.businessLicense")}),
});
const currentImageType = ref("");
const chooseImageRef = ref();
// 状态
const isSubmitting = ref(false);
// 表单验证
function validateForm(): boolean {
const validateResult = FormSchema.safeParse(formData.value);
if (!validateResult.success) {
const fieldErrors = validateResult.error.flatten().fieldErrors;
const firstError = R.values(fieldErrors)[0]?.[0];
if (firstError) {
uni.showToast({
title: firstError,
icon: "none",
});
}
return false;
}
return true;
}
// 提交表单
async function submitForm() {
if (!validateForm()) return;
isSubmitting.value = true;
try {
console.log("提交数据:", formData.value);
appShopSettlementApplyPost({
body: {
...formData.value,
}
}).then(res => {
uni.showToast({
title: t("toast.submitSuccess"),
icon: "none",
});
auditStatus.value = 1
})
} catch (error) {
console.error("提交失败:", error);
uni.showToast({
title: "提交失败,请重试",
icon: "none",
});
} finally {
isSubmitting.value = false;
}
}
// 防抖提交
const handleSubmit = debounce(Config.debounceLongTime, submitForm, {
atBegin: true,
});
// 选择图片 - 添加安全检查
function chooseImage(type: string) {
currentImageType.value = type;
if (chooseImageRef.value?.init) {
chooseImageRef.value.init();
}
}
// 图片选择回调 - 修复类型问题
function onImageChange(images: string[]) {
if (images.length > 0 && currentImageType.value) {
// 使用类型断言确保类型安全
(formData.value as any)[currentImageType.value] = images[0];
}
}
const show = ref(false);
function handleClose() {
show.value = false;
}
const columnsType = ref([])
const typePickerValue = ref('')
function handleShowTypePicker(type: boolean) {
appMerchantCategoryListGet({
pageNum: 1,
pageSize: 100,
}).then(res => {
console.log(res)
columnsType.value = res.data
show.value = type;
typePickerValue.value = columnsType.value[0].id
if (!type) {
const data = res.data.find(item => item.id === formData.value.shopTypeId)
formData.value.category = data.name
}
})
}
function handleSubmitPickType() {
console.log(typePickerValue.value)
const data = columnsType.value.find(item => item.id === typePickerValue.value)
if (data) {
formData.value.shopTypeId = data.id
formData.value.category = data.name
}
handleClose()
}
function handleAddressPicker() {
uni.navigateTo({
url: '/pages-user/pages/search-address/index',
})
}
useEventEmit(EventEnum.CHOOSE_ADDRESS, (data) => {
console.log('接收到的地址信息', data)
// 解析地址信息并填充表单
if (data && data.addressComponents) {
// 重置地址相关字段
formData.value.street = ""
formData.value.city = ""
formData.value.state = ""
// 遍历地址组件,根据类型填充对应字段
data.addressComponents.forEach((component: any) => {
const types = component.types || []
// 街道号码 (street_number)
if (types.includes('street_number')) {
formData.value.street = component.longText + ' ' + formData.value.street
}
// 街道名称 (route)
if (types.includes('route')) {
formData.value.street = (formData.value.street + ' ' + component.longText).trim()
}
// 子区域 (sublocality_level_1) - 可作为街道的一部分
if (types.includes('sublocality_level_1') && !formData.value.street) {
formData.value.street = component.longText
}
// 城市 (locality)
if (types.includes('locality')) {
formData.value.city = component.longText
}
// 如果没有locality,使用administrative_area_level_3作为城市
if (types.includes('administrative_area_level_3') && !formData.value.city) {
formData.value.city = component.longText
}
// 州/省 (administrative_area_level_1)
if (types.includes('administrative_area_level_1')) {
formData.value.state = component.shortText || component.longText
}
})
// 如果没有解析到街道信息,使用格式化地址的第一部分
if (!formData.value.street && data.formattedAddress) {
const addressParts = data.formattedAddress.split(', ')
if (addressParts.length > 0) {
formData.value.street = addressParts[0]
}
}
// 设置详细地址为完整的格式化地址
if (data.formattedAddress) {
formData.value.detailAddress = data.formattedAddress
}
console.log('解析后的地址信息:', {
street: formData.value.street,
city: formData.value.city,
state: formData.value.state,
detailAddress: formData.value.detailAddress
})
}
})
// 删除图片
function removeImage(type: string) {
if (type && formData.value.hasOwnProperty(type)) {
(formData.value as any)[type] = "";
}
}
</script>
<template>
<view class="">
<navbar customClass="!bg-transparent"/>
<view class="px-30rpx mt-18rpx mb-44rpx">
<view class="text-56rpx lh-56rpx text-#333 font-bold">{{ t("pages-user.store-settle-in.title") }}</view>
<view class="text-28rpx text-#666 lh-28rpx mt-20rpx pr-240rpx"
>{{ t("pages-user.store-settle-in.desc") }}
</view
>
</view>
<view class="px-30rpx relative mb-30rpx z-1">
<image
class="absolute top--200rpx right-20rpx z-9 w-256rpx h-213rpx"
src="@img/chef/109.png"
></image>
<view class="bg-white rounded-16rpx px-30rpx pb-46rpx relative z-10">
<template v-if="auditStatus === null">
<view class="border-bottom py-32rpx flex-center-sb">
<view class="text-28rpx text-#333 w-140rpx">{{ t("pages-user.store-settle-in.storeName") }}:</view>
<wd-input
v-model.trim="formData.shopName"
:cursorSpacing="20"
:focus-when-clear="false"
:maxlength="20"
:placeholder="t('common.enter')"
clearable
custom-class="flex-1"
no-border
placeholderStyle="font-size: 28rpx;color: #999;"
>
</wd-input>
</view>
<view class="border-bottom py-32rpx flex-center-sb" @click="handleShowTypePicker(true)">
<view class="flex text-28rpx">
<view class="text-#333 w-140rpx">{{ t("pages-user.store-settle-in.category") }}:</view>
<template v-if="formData.category">
<text class="text-#333">
{{ formData.category }}
</text>
</template>
<template v-else>
<text class="text-#999">
{{ t("common.select") }}
</text>
</template>
</view>
<image class="w-22rpx h-30rpx" src="@img/chef/100202.png"></image>
</view>
<view class="border-bottom py-32rpx flex-center-sb" @click="handleAddressPicker">
<view class="flex text-28rpx">
<view class="text-#333 w-140rpx">{{ t("pages-user.store-settle-in.address") }}:</view>
<template v-if="formData.street || formData.city || formData.state">
<text class="text-#333 flex-1">
{{ [formData.street, formData.city, formData.state].filter(Boolean).join(', ') }}
</text>
</template>
<template v-else>
<text class="text-#999">{{ t("common.select") }}</text>
</template>
</view>
<image class="w-22rpx h-30rpx" src="@img/chef/100202.png"></image>
</view>
<view class="border-bottom py-32rpx flex-center-sb">
<view class="text-28rpx text-#333 w-140rpx">{{ t("pages-user.store-settle-in.detailedAddress") }}:</view>
<wd-input
v-model.trim="formData.detailAddress"
:cursorSpacing="20"
:focus-when-clear="false"
:maxlength="20"
:placeholder="t('common.enter')"
clearable
custom-class="flex-1"
no-border
placeholderStyle="font-size: 28rpx;color: #999;"
>
</wd-input>
</view>
<view class="border-bottom py-32rpx">
<view class="text-28rpx text-#333 mb-24rpx">{{ t("pages-user.store-settle-in.introduction") }}:</view>
<view
class="min-h-226rpx box-border bg-#F6F6F6 rounded-36rpx overflow-hidden px-18rpx py-10rpx"
>
<wd-textarea
v-model="formData.description"
:maxlength="500"
:placeholder="t('components.placeholder')"
auto-height
custom-class="!bg-#F6F6F6"
custom-textarea-container-class="!bg-#F6F6F6"
no-border
/>
</view>
</view>
<view class="border-bottom py-30rpx">
<view class="text-28rpx text-#333 mb-24rpx">{{ t("pages-user.store-settle-in.idCardFront") }}:</view>
<view class="flex-center-sb">
<view class="relative" @click="chooseImage('idCardImage')">
<image
v-if="formData.idCardImage"
class="absolute top--10rpx right--10rpx z-10 w-36rpx h-36rpx"
src="@img/chef/113.png"
@click.stop="removeImage('idCardImage')"
></image>
<image
v-if="!formData.idCardImage"
class="w-276rpx h-188rpx"
src="@img/chef/110.png"
></image>
<image
v-else
:src="formData.idCardImage"
class="w-276rpx h-188rpx rounded-16rpx"
mode="aspectFill"
></image>
</view>
<view class="relative" @click="chooseImage('passportImage')">
<image
v-if="formData.passportImage"
class="absolute top--10rpx right--10rpx z-10 w-36rpx h-36rpx"
src="@img/chef/113.png"
@click.stop="removeImage('passportImage')"
></image>
<image
v-if="!formData.passportImage"
class="w-276rpx h-188rpx"
src="@img/chef/111.png"
></image>
<image
v-else
:src="formData.passportImage"
class="w-276rpx h-188rpx rounded-16rpx"
mode="aspectFill"
></image>
</view>
</view>
</view>
<view>
<view class="text-28rpx text-#333 mt-32rpx mb-24rpx">{{
t("pages-user.store-settle-in.businessLicense")
}}:
</view>
<view class="relative w-160rpx h-160rpx" @click="chooseImage('foodOperationCertificateImage')">
<image
v-if="formData.foodOperationCertificateImage"
class="absolute top--10rpx right--10rpx z-10 w-36rpx h-36rpx"
src="@img/chef/113.png"
@click.stop="removeImage('foodOperationCertificateImage')"
></image>
<image
v-if="!formData.foodOperationCertificateImage"
class="w-160rpx h-160rpx"
src="@img/chef/112.png"
></image>
<image
v-else
:src="formData.foodOperationCertificateImage"
class="w-160rpx h-160rpx rounded-16rpx"
mode="aspectFill"
></image>
</view>
</view>
</template>
<view v-else class="center flex-col h-798rpx">
<template v-if="auditStatus === 1">
<image
class="w-98rpx h-98rpx"
src="@img/chef/1109.png"
></image>
<view class="text-42rpx lh-42rpx text-#333 font-500 mt-26rpx mb-18rpx">
{{ t("pages-user.store-settle-in.audit.submitted") }}
</view>
<view class="text-28rpx lh-28rpx text-#868686 text-center">
{{ t("pages-user.store-settle-in.audit.submittedDesc") }}
</view>
</template>
<template v-else-if="auditStatus === 2">
<image
class="w-98rpx h-98rpx"
src="@img/chef/1108.png"
></image>
<view class="text-42rpx lh-42rpx text-#333 font-500 mt-26rpx mb-18rpx">
{{ t("pages-user.store-settle-in.audit.pass") }}
</view>
<view class="text-28rpx lh-28rpx text-#868686 text-center">
{{ t("pages-user.store-settle-in.audit.passDesc") }}
<br>
<text class="mt-22rpx block">{{ t("pages-user.store-settle-in.audit.passDesc1") }}~</text>
</view>
</template>
<template v-else-if="auditStatus === 3">
<image
class="w-98rpx h-98rpx"
src="@img/chef/1110.png"
></image>
<view class="text-42rpx lh-42rpx text-#333 font-500 mt-26rpx mb-18rpx">
{{ t("pages-user.store-settle-in.audit.reject") }}
</view>
<view class="text-28rpx lh-28rpx text-#868686">{{
t("pages-user.store-settle-in.audit.rejectDesc")
}}{{ rejectReason }}
</view>
</template>
</view>
</view>
</view>
<template v-if="auditStatus === null">
<fixed-bottom-large-btn
:text="`${t('common.submit')}`"
class="z-100"
fixed
@click="handleSubmit"
/>
</template>
<template v-if="auditStatus === 3">
<!--如果是走新增进来的且Id不存在-->
<fixed-bottom-large-btn
:text="`${t('common.reEdit')}`"
class="z-100"
fixed
@click="clickInitEdit"
/>
</template>
<ChooseImage ref="chooseImageRef" @change="onImageChange"/>
<wd-popup
v-model="show"
position="bottom"
@close="handleClose"
>
<view>
<view class="flex-center-sb h-102rpx bg-#F7F7F7 px-30rpx">
<view @click="handleClose" class="text-30rpx text-#999">{{ t('common.cancel') }}</view>
<view class="text-34rpx text-#333">{{ t('common.placeholder.pleaseSelect') }}</view>
<view class="text-30rpx text-#FF6106" @click="handleSubmitPickType">{{ t('common.confirm') }}</view>
</view>
<view class="bg-#fff px-54rpx py-56rpx">
<wd-picker-view v-model="typePickerValue" :columns="columnsType" label-key="name" value-key="id"/>
</view>
</view>
</wd-popup>
</view>
</template>
<style lang="scss" scoped>
.form-item {
@apply flex items-start py-30rpx border-b border-#f0f0f0;
&:last-child {
@apply border-b-0;
}
}
.form-label {
@apply text-28rpx text-primary font-medium w-160rpx flex-shrink-0;
}
.form-input-wrapper {
@apply flex-1 flex items-center justify-between;
}
.form-input {
@apply flex-1 text-28rpx text-primary;
}
.form-textarea-wrapper {
@apply flex-1;
}
.form-textarea {
@apply w-full min-h-200rpx text-28rpx text-primary leading-40rpx p-20rpx bg-#f8f8f8 rounded-20rpx;
}
.form-upload-wrapper {
@apply flex-1 flex gap-20rpx;
}
.upload-item {
@apply w-160rpx h-120rpx border-2rpx border-dashed border-#ddd rounded-20rpx flex items-center justify-center bg-#f8f8f8;
}
.upload-image {
@apply w-full h-full rounded-20rpx;
}
.upload-placeholder {
@apply flex items-center justify-center w-full h-full;
}
.submit-btn {
@apply w-full h-88rpx bg-#333 text-white text-32rpx font-medium rounded-44rpx;
&:disabled {
@apply bg-#ccc;
}
}
:deep(.uni-picker-view-wrapper) {
& > uni-picker-view-column:first-of-type .uni-picker-view-group {
.uni-picker-view-indicator {
border-radius: 20rpx 0 0 20rpx !important;
&:after {
border: none;
}
&:before {
border: none;
}
}
}
}
:deep(.wd-picker-view-column__item) {
line-height: 94rpx !important;
}
:deep(.uni-picker-view-indicator) {
height: 94rpx !important;
}
</style>