| | |
| | | <template> |
| | | <view class="page"> |
| | | <view class="navigation-bar"> |
| | | <view class="back-button" @tap="goBack"><image class="back-icon" src="/static/icons/lucide/chevron-right.svg" mode="aspectFit"></image></view> |
| | | <text class="page-title">商户进件</text> |
| | | </view> |
| | | <view class="navigation-bar"><view class="back-button" @tap="goBack"><image class="back-icon" src="/static/icons/lucide/back-left.svg" mode="aspectFit"></image></view><text class="page-title">商户进件</text></view> |
| | | <view class="step-bar"><view class="step-item" v-for="(title, index) in stepTitles" :key="index"><text :class="index + 1 == currentStep ? 'step-index step-index-active' : (index + 1 < currentStep ? 'step-index step-index-done' : 'step-index')">{{index + 1}}</text><text :class="index + 1 == currentStep ? 'step-label step-label-active' : (index + 1 < currentStep ? 'step-label step-label-done' : 'step-label')">{{stepTitle(index)}}</text></view></view> |
| | | <scroll-view class="form-scroll" scroll-y="true" :scroll-top="scrollTop" :show-scrollbar="false"><view class="form-body"> |
| | | <view v-if="draftNotice" class="draft-hint">已恢复上次未提交的草稿,可继续填写</view> |
| | | |
| | | <scroll-view class="form-scroll" scroll-y="true" :show-scrollbar="false" :enhanced="false"> |
| | | <view class="form-card subject-card"> |
| | | <view class="merchant-type-row" @tap="chooseMerchantType"> |
| | | <view class="type-copy"><text class="field-label inline-label">商户类型</text></view> |
| | | <view class="type-control"><text>{{merchantType == 'enterprise' ? '企业' : '个人'}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view> |
| | | <view v-if="currentStep == 1"> |
| | | <view class="field-block"><text class="field-label required">商户类型</text><picker class="date-picker" mode="selector" :range="merchantTypeOptions" :value="merchantTypeIndex" @change="onMerchantTypeChange"><view class="select-shell"><text>{{merchantType == 'enterprise' ? '企业' : '个人'}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker></view> |
| | | <view v-if="merchantType == 'enterprise'"> |
| | | <view class="field-block"><text class="field-label required">商户名称</text><view class="input-shell" :class="invalidField == 'merchantName' ? 'field-invalid' : ''"><input class="text-input" v-model="merchantName" maxlength="128" placeholder="请输入" placeholder-class="input-placeholder" @input="clearFieldError('merchantName')" /></view><text class="field-hint">必须与营业执照上的名称完全一致</text><text v-if="invalidField == 'merchantName'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label">商户简称</text><view class="input-shell"><input class="text-input" v-model="merchantShortName" maxlength="128" placeholder="请输入" placeholder-class="input-placeholder" /></view><text class="field-hint">会显示在用户的支付页面上,留空则用商户名称</text></view> |
| | | <view class="field-block"><text class="field-label required">公司类型</text><picker class="date-picker" mode="selector" :range="companyTypeOptions" :value="companyTypeIndex" @change="onCompanyTypeChange"><view class="select-shell" :class="invalidField == 'companyType' ? 'field-invalid' : ''"><text :class="companyType.length == 0 ? 'select-placeholder' : ''">{{companyType.length > 0 ? companyType : '请选择'}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker><text v-if="invalidField == 'companyType'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">营业执照</text><view class="upload-box" :class="invalidField == 'businessLicenseUrl' ? 'field-invalid' : ''" @tap="chooseDocument('businessLicense')"><image v-if="businessLicenseUrl.length > 0" class="document-image" :src="businessLicenseUrl" mode="aspectFill"></image><text v-else>{{uploadingField == 'businessLicense' ? '上传中...' : '添加图片'}}</text></view><text class="field-hint">JPG/PNG/BMP,单张不超过 2MB,证照不能过期</text><text v-if="invalidField == 'businessLicenseUrl'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">统一社会信用代码</text><view class="input-shell" :class="invalidField == 'creditCode' ? 'field-invalid' : ''"><input class="text-input" :value="creditCode" maxlength="18" placeholder="请输入" placeholder-class="input-placeholder" @input="normalizeCreditCode" /></view><text class="field-hint">18 位,字母会自动转大写</text><text v-if="invalidField == 'creditCode'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">营业执照有效期</text><picker class="date-picker" mode="selector" :range="validityOptions" :value="businessValidityIndex" @change="onBusinessValidityChange"><view class="select-shell"><text>{{businessValidity}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker></view> |
| | | <view class="field-block"><text class="field-label required">营业执照有效期起始日期</text><picker class="date-picker" mode="date" :value="businessStartDate" start="1900-01-01" end="2099-12-31" @change="onBusinessStartChange"><view class="select-shell" :class="invalidField == 'businessStartDate' ? 'field-invalid' : ''"><text :class="businessStartDate.length == 0 ? 'select-placeholder' : ''">{{businessStartDate.length > 0 ? businessStartDate : '请选择'}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker><text v-if="invalidField == 'businessStartDate'" class="field-error">{{errorMessage}}</text></view> |
| | | <view v-if="businessValidity == '固定期限'" class="field-block"><text class="field-label required">营业执照有效期结束日期</text><picker class="date-picker" mode="date" :value="businessEndDate" start="1900-01-01" end="2099-12-31" @change="onBusinessEndChange"><view class="select-shell" :class="invalidField == 'businessEndDate' ? 'field-invalid' : ''"><text :class="businessEndDate.length == 0 ? 'select-placeholder' : ''">{{businessEndDate.length > 0 ? businessEndDate : '请选择'}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker><text v-if="invalidField == 'businessEndDate'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">成立时间</text><picker class="date-picker" mode="date" :value="establishedDate" start="1900-01-01" end="2099-12-31" @change="onEstablishedDateChange"><view class="select-shell" :class="invalidField == 'establishedDate' ? 'field-invalid' : ''"><text :class="establishedDate.length == 0 ? 'select-placeholder' : ''">{{establishedDate.length > 0 ? establishedDate : '请选择'}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker><text v-if="invalidField == 'establishedDate'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">注册地址(省、市、区)</text><picker class="region-picker" mode="region" @change="onRegisteredRegionChange"><view class="select-shell" :class="invalidField == 'registeredRegion' ? 'field-invalid' : ''"><text class="ellipsis-text" :class="registeredRegion.length == 0 ? 'select-placeholder' : ''">{{registeredRegion.length > 0 ? registeredRegion : '请选择'}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker><text v-if="invalidField == 'registeredRegion'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">详细注册地址</text><view class="input-shell" :class="invalidField == 'registeredAddress' ? 'field-invalid' : ''"><input class="text-input" v-model="registeredAddress" maxlength="255" placeholder="请输入" placeholder-class="input-placeholder" @input="clearFieldError('registeredAddress')" /></view><text class="field-hint">与营业执照上的住所一致</text><text v-if="invalidField == 'registeredAddress'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text :class="capitalRequired() ? 'field-label required' : 'field-label'">注册资本</text><view class="input-shell" :class="invalidField == 'registeredCapital' ? 'field-invalid' : ''"><input class="text-input" type="digit" v-model="registeredCapital" maxlength="64" placeholder="请输入" placeholder-class="input-placeholder" @input="clearFieldError('registeredCapital')" /></view><text class="field-hint">单位:万元,可填小数</text><text v-if="invalidField == 'registeredCapital'" class="field-error">{{errorMessage}}</text></view> |
| | | </view> |
| | | <view v-if="merchantType == 'individual'"> |
| | | <view class="field-block"><text class="field-label required">行业类型</text><picker class="date-picker" mode="selector" :range="industryTypeOptions" :value="industryTypeIndex" @change="onIndustryTypeChange"><view class="select-shell" :class="invalidField == 'industryType' ? 'field-invalid' : ''"><text :class="industryType.length == 0 ? 'select-placeholder' : ''">{{industryType.length > 0 ? industryType : '请选择'}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker><text v-if="invalidField == 'industryType'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">经营地区(省、市、区)</text><picker class="region-picker" mode="region" @change="onRegisteredRegionChange"><view class="select-shell" :class="invalidField == 'registeredRegion' ? 'field-invalid' : ''"><text class="ellipsis-text" :class="registeredRegion.length == 0 ? 'select-placeholder' : ''">{{registeredRegion.length > 0 ? registeredRegion : '请选择'}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker><text v-if="invalidField == 'registeredRegion'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">经营详细地址</text><view class="input-shell" :class="invalidField == 'businessAddress' ? 'field-invalid' : ''"><input class="text-input" v-model="businessAddress" maxlength="255" placeholder="请输入" placeholder-class="input-placeholder" @input="clearFieldError('businessAddress')" /></view><text class="field-hint">与门头照上的地址一致(线下报备需要)</text><text v-if="invalidField == 'businessAddress'" class="field-error">{{errorMessage}}</text></view> |
| | | </view> |
| | | <!-- 门店材料是线下报备的一组三张:门头照(F22)、内景照(F24)、收银台(F105)。通道在 |
| | | 进件这一步就按 F-code 逐张核对,缺一张整笔驳回,所以企业与个人主体都要交。 --> |
| | | <view class="field-block"><text class="field-label required">店铺门头照</text><view class="upload-box" :class="invalidField == 'storeHeaderUrl' ? 'field-invalid' : ''" @tap="chooseDocument('storeHeader')"><image v-if="storeHeaderUrl.length > 0" class="document-image" :src="storeHeaderUrl" mode="aspectFill"></image><text v-else>{{uploadingField == 'storeHeader' ? '上传中...' : '添加图片'}}</text></view><text class="field-hint">实体门头照(招牌清晰可见);流动经营、上门服务传经营现场照</text><text v-if="invalidField == 'storeHeaderUrl'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">店内环境照</text><view class="upload-box" :class="invalidField == 'storeIndoorUrl' ? 'field-invalid' : ''" @tap="chooseDocument('storeIndoor')"><image v-if="storeIndoorUrl.length > 0" class="document-image" :src="storeIndoorUrl" mode="aspectFill"></image><text v-else>{{uploadingField == 'storeIndoor' ? '上传中...' : '添加图片'}}</text></view><text class="field-hint">店内环境或工作区域照片,不能与门头照相同</text><text v-if="invalidField == 'storeIndoorUrl'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">店铺收银台照片</text><view class="upload-box" :class="invalidField == 'storeCashierUrl' ? 'field-invalid' : ''" @tap="chooseDocument('storeCashier')"><image v-if="storeCashierUrl.length > 0" class="document-image" :src="storeCashierUrl" mode="aspectFill"></image><text v-else>{{uploadingField == 'storeCashier' ? '上传中...' : '添加图片'}}</text></view><text class="field-hint">收银台或吧台实拍,能看到收款设备/收款码</text><text v-if="invalidField == 'storeCashierUrl'" class="field-error">{{errorMessage}}</text></view> |
| | | </view> |
| | | |
| | | <view v-if="merchantType == 'enterprise'" class="form-card"> |
| | | <text class="section-title">企业资料</text> |
| | | |
| | | <view class="field-block"> |
| | | <text class="field-label">统一社会信用代码</text> |
| | | <view class="input-shell" :class="invalidField == 'creditCode' ? 'field-invalid' : ''"><input class="text-input" v-model="creditCode" maxlength="18" placeholder="请输入18位统一社会信用代码" placeholder-class="input-placeholder" @input="normalizeCreditCode" /></view> |
| | | <text v-if="invalidField == 'creditCode'" class="field-error">{{errorMessage}}</text> |
| | | </view> |
| | | |
| | | <view class="field-block"> |
| | | <text class="field-label">营业执照有效期</text> |
| | | <view class="select-shell" :class="invalidField == 'businessValidity' ? 'field-invalid' : ''" @tap="chooseBusinessValidity"><text>{{businessValidity}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view> |
| | | <text v-if="invalidField == 'businessValidity'" class="field-error">{{errorMessage}}</text> |
| | | </view> |
| | | |
| | | <view class="field-block"> |
| | | <text class="field-label">证照有效期起始日期</text> |
| | | <picker class="date-picker" mode="date" :value="businessStartDate" start="1990-01-01" end="2099-12-31" @change="onBusinessStartChange"><view class="select-shell" :class="invalidField == 'businessStartDate' ? 'field-invalid' : ''"><text :class="businessStartDate.length == 0 ? 'select-placeholder' : ''">{{businessStartDate.length > 0 ? businessStartDate : '请选择'}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker> |
| | | <text v-if="invalidField == 'businessStartDate'" class="field-error">{{errorMessage}}</text> |
| | | </view> |
| | | |
| | | <view v-if="businessValidity == '固定期限'" class="field-block"> |
| | | <text class="field-label">证照有效期结束日期</text> |
| | | <picker class="date-picker" mode="date" :value="businessEndDate" start="1990-01-01" end="2099-12-31" @change="onBusinessEndChange"><view class="select-shell" :class="invalidField == 'businessEndDate' ? 'field-invalid' : ''"><text :class="businessEndDate.length == 0 ? 'select-placeholder' : ''">{{businessEndDate.length > 0 ? businessEndDate : '请选择'}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker> |
| | | <text v-if="invalidField == 'businessEndDate'" class="field-error">{{errorMessage}}</text> |
| | | </view> |
| | | |
| | | <view class="field-block"> |
| | | <text class="field-label">注册地址(省、市、区)</text> |
| | | <picker class="region-picker" mode="region" @change="onRegisteredRegionChange"><view class="select-shell" :class="invalidField == 'registeredRegion' ? 'field-invalid' : ''"><text class="ellipsis-text" :class="registeredRegion.length == 0 ? 'select-placeholder' : ''">{{registeredRegion.length > 0 ? registeredRegion : '请选择'}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker> |
| | | <text v-if="invalidField == 'registeredRegion'" class="field-error">{{errorMessage}}</text> |
| | | </view> |
| | | |
| | | <view class="field-block last-field"> |
| | | <text class="field-label">详细注册地址</text> |
| | | <view class="input-shell" :class="invalidField == 'registeredAddress' ? 'field-invalid' : ''"><input class="text-input" v-model="registeredAddress" maxlength="80" placeholder="请输入详细注册地址" placeholder-class="input-placeholder" @input="clearFieldError('registeredAddress')" /></view> |
| | | <text v-if="invalidField == 'registeredAddress'" class="field-error">{{errorMessage}}</text> |
| | | </view> |
| | | <view v-if="currentStep == 2"> |
| | | <view class="field-block"><text class="field-label required">{{merchantType == 'enterprise' ? '法人姓名' : '个人姓名'}}</text><view class="input-shell" :class="invalidField == 'subjectName' ? 'field-invalid' : ''"><input class="text-input" v-model="subjectName" maxlength="64" placeholder="请输入" placeholder-class="input-placeholder" @input="clearFieldError('subjectName')" /></view><text class="field-hint">与身份证上的姓名一致</text><text v-if="invalidField == 'subjectName'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">{{merchantType == 'enterprise' ? '法人身份证件类型' : '本人证件类型'}}</text><view class="select-shell readonly-shell"><text>身份证</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></view> |
| | | <view class="field-block"><text class="field-label required">{{merchantType == 'enterprise' ? '法人身份证件人像面' : '本人身份证件人像面'}}</text><view class="upload-box" :class="invalidField == 'legalIdFrontUrl' ? 'field-invalid' : ''" @tap="chooseDocument('legalIdFront')"><image v-if="legalIdFrontUrl.length > 0" class="document-image" :src="legalIdFrontUrl" mode="aspectFill"></image><text v-else>{{uploadingField == 'legalIdFront' ? '上传中...' : '添加图片'}}</text></view><text class="field-hint">JPG/PNG/BMP,单张不超过 2MB,四角完整、不要反光</text><text v-if="invalidField == 'legalIdFrontUrl'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">{{merchantType == 'enterprise' ? '法人身份证件国徽面' : '本人身份证件国徽面'}}</text><view class="upload-box" :class="invalidField == 'legalIdBackUrl' ? 'field-invalid' : ''" @tap="chooseDocument('legalIdBack')"><image v-if="legalIdBackUrl.length > 0" class="document-image" :src="legalIdBackUrl" mode="aspectFill"></image><text v-else>{{uploadingField == 'legalIdBack' ? '上传中...' : '添加图片'}}</text></view><text class="field-hint">JPG/PNG/BMP,单张不超过 2MB,四角完整、不要反光</text><text v-if="invalidField == 'legalIdBackUrl'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">{{merchantType == 'enterprise' ? '法人证件号码' : '本人证件号码'}}</text><view class="input-shell" :class="invalidField == 'subjectIdNumber' ? 'field-invalid' : ''"><input class="text-input" :value="subjectIdNumber" maxlength="18" placeholder="请输入" placeholder-class="input-placeholder" @input="normalizeIdNumber" /></view><text v-if="invalidField == 'subjectIdNumber'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">{{merchantType == 'enterprise' ? '法人证件有效期类型' : '本人证件有效期类型'}}</text><picker class="date-picker" mode="selector" :range="subjectValidityOptions" :value="subjectValidityIndex" @change="onSubjectValidityChange"><view class="select-shell"><text>{{subjectValidity}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker></view> |
| | | <view class="field-block"><text class="field-label required">{{merchantType == 'enterprise' ? '法人证件有效期开始日期' : '本人证件有效期开始日期'}}</text><picker class="date-picker" mode="date" :value="subjectStartDate" start="1900-01-01" end="2099-12-31" @change="onSubjectStartChange"><view class="select-shell" :class="invalidField == 'subjectStartDate' ? 'field-invalid' : ''"><text :class="subjectStartDate.length == 0 ? 'select-placeholder' : ''">{{subjectStartDate.length > 0 ? subjectStartDate : '请选择'}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker><text v-if="invalidField == 'subjectStartDate'" class="field-error">{{errorMessage}}</text></view> |
| | | <view v-if="subjectValidity != '长期有效'" class="field-block"><text class="field-label required">{{merchantType == 'enterprise' ? '法人证件有效期截止日期' : '本人证件有效期截止日期'}}</text><picker class="date-picker" mode="date" :value="subjectEndDate" start="1900-01-01" end="2099-12-31" @change="onSubjectEndChange"><view class="select-shell" :class="invalidField == 'subjectEndDate' ? 'field-invalid' : ''"><text :class="subjectEndDate.length == 0 ? 'select-placeholder' : ''">{{subjectEndDate.length > 0 ? subjectEndDate : '请选择'}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker><text v-if="invalidField == 'subjectEndDate'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">{{merchantType == 'enterprise' ? '法人证件地址' : '本人证件地址'}}</text><view class="input-shell" :class="invalidField == 'legalCertificateAddress' ? 'field-invalid' : ''"><input class="text-input" v-model="legalCertificateAddress" maxlength="255" placeholder="请输入" placeholder-class="input-placeholder" @input="clearFieldError('legalCertificateAddress')" /></view><text class="field-hint">与身份证上的住址一致,至少 4 个字</text><text v-if="invalidField == 'legalCertificateAddress'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">{{merchantType == 'enterprise' ? '法人手机号' : '本人手机号'}}</text><view class="input-shell" :class="invalidField == 'legalMobile' ? 'field-invalid' : ''"><input class="text-input" type="number" v-model="legalMobile" maxlength="11" placeholder="请输入" placeholder-class="input-placeholder" @input="clearFieldError('legalMobile')" /></view><text class="field-hint">微信实名认证要用经营者/法人本人的手机号,不是联系人手机号</text><text v-if="invalidField == 'legalMobile'" class="field-error">{{errorMessage}}</text></view> |
| | | </view> |
| | | |
| | | <view class="form-card"> |
| | | <text class="section-title">{{merchantType == 'enterprise' ? '法人信息' : '个人信息'}}</text> |
| | | |
| | | <view class="field-block"> |
| | | <text class="field-label">{{merchantType == 'enterprise' ? '法人姓名' : '个人姓名'}}</text> |
| | | <view class="input-shell" :class="invalidField == 'subjectName' ? 'field-invalid' : ''"><input class="text-input" v-model="subjectName" maxlength="30" placeholder="请输入姓名" placeholder-class="input-placeholder" @input="clearFieldError('subjectName')" /></view> |
| | | <text v-if="invalidField == 'subjectName'" class="field-error">{{errorMessage}}</text> |
| | | </view> |
| | | |
| | | <view class="field-block"> |
| | | <text class="field-label">{{merchantType == 'enterprise' ? '法人证件类型' : '个人证件类型'}}</text> |
| | | <view class="select-shell readonly-shell"><text>身份证</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view> |
| | | </view> |
| | | |
| | | <view class="field-block"> |
| | | <text class="field-label">{{merchantType == 'enterprise' ? '法人证件号码' : '个人证件号码'}}</text> |
| | | <view class="input-shell" :class="invalidField == 'subjectIdNumber' ? 'field-invalid' : ''"><input class="text-input" type="idcard" v-model="subjectIdNumber" maxlength="18" placeholder="请输入身份证号码" placeholder-class="input-placeholder" @input="clearFieldError('subjectIdNumber')" /></view> |
| | | <text v-if="invalidField == 'subjectIdNumber'" class="field-error">{{errorMessage}}</text> |
| | | </view> |
| | | |
| | | <view class="field-block"> |
| | | <text class="field-label">{{merchantType == 'enterprise' ? '法人证件有效期类型' : '个人证件有效期类型'}}</text> |
| | | <view class="select-shell" :class="invalidField == 'subjectValidity' ? 'field-invalid' : ''" @tap="chooseSubjectValidity"><text>{{subjectValidity}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view> |
| | | <text v-if="invalidField == 'subjectValidity'" class="field-error">{{errorMessage}}</text> |
| | | </view> |
| | | |
| | | <view class="field-block"> |
| | | <text class="field-label">{{merchantType == 'enterprise' ? '法人证件有效期开始日期' : '个人证件有效期开始日期'}}</text> |
| | | <picker class="date-picker" mode="date" :value="subjectStartDate" start="1990-01-01" end="2099-12-31" @change="onSubjectStartChange"><view class="select-shell" :class="invalidField == 'subjectStartDate' ? 'field-invalid' : ''"><text :class="subjectStartDate.length == 0 ? 'select-placeholder' : ''">{{subjectStartDate.length > 0 ? subjectStartDate : '请选择'}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker> |
| | | <text v-if="invalidField == 'subjectStartDate'" class="field-error">{{errorMessage}}</text> |
| | | </view> |
| | | |
| | | <view v-if="subjectValidity == '固定期限'" class="field-block"> |
| | | <text class="field-label">{{merchantType == 'enterprise' ? '法人证件有效期截止日期' : '个人证件有效期截止日期'}}</text> |
| | | <picker class="date-picker" mode="date" :value="subjectEndDate" start="1990-01-01" end="2099-12-31" @change="onSubjectEndChange"><view class="select-shell" :class="invalidField == 'subjectEndDate' ? 'field-invalid' : ''"><text :class="subjectEndDate.length == 0 ? 'select-placeholder' : ''">{{subjectEndDate.length > 0 ? subjectEndDate : '请选择'}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker> |
| | | <text v-if="invalidField == 'subjectEndDate'" class="field-error">{{errorMessage}}</text> |
| | | </view> |
| | | |
| | | <view v-if="merchantType == 'individual'" class="field-block last-field"> |
| | | <text class="field-label">手机号</text> |
| | | <view class="input-shell" :class="invalidField == 'personalPhone' ? 'field-invalid' : ''"><input class="text-input" type="number" v-model="personalPhone" maxlength="11" placeholder="请输入手机号" placeholder-class="input-placeholder" @input="clearFieldError('personalPhone')" /></view> |
| | | <text v-if="invalidField == 'personalPhone'" class="field-error">{{errorMessage}}</text> |
| | | </view> |
| | | <view v-if="currentStep == 3"> |
| | | <view v-if="merchantType == 'enterprise'" class="field-block"><text class="field-label required">管理员姓名</text><view class="input-shell" :class="invalidField == 'contactName' ? 'field-invalid' : ''"><input class="text-input" v-model="contactName" maxlength="64" placeholder="请输入" placeholder-class="input-placeholder" @input="clearFieldError('contactName')" /></view><text v-if="invalidField == 'contactName'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">{{merchantType == 'enterprise' ? '管理员手机号' : '手机号'}}</text><view class="input-shell" :class="invalidField == 'contactMobile' ? 'field-invalid' : ''"><input class="text-input" type="number" :value="contactMobile" maxlength="11" placeholder="请输入" placeholder-class="input-placeholder" @input="normalizePhone" /></view><text class="field-hint">汇付审核如需联系会打这个号码</text><text v-if="invalidField == 'contactMobile'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">{{merchantType == 'enterprise' ? '管理员邮箱' : '邮箱'}}</text><view class="input-shell" :class="invalidField == 'administratorEmail' ? 'field-invalid' : ''"><input class="text-input" v-model="administratorEmail" maxlength="128" placeholder="请输入" placeholder-class="input-placeholder" @input="clearFieldError('administratorEmail')" /></view><text class="field-hint">用于接收进件结果通知,必须能收信</text><text v-if="invalidField == 'administratorEmail'" class="field-error">{{errorMessage}}</text></view> |
| | | </view> |
| | | |
| | | <view v-if="merchantType == 'enterprise'" class="form-card"> |
| | | <text class="section-title">管理员信息</text> |
| | | <view class="field-block"> |
| | | <text class="field-label">管理员姓名</text> |
| | | <view class="input-shell" :class="invalidField == 'administratorName' ? 'field-invalid' : ''"><input class="text-input" v-model="administratorName" maxlength="30" placeholder="请输入管理员姓名" placeholder-class="input-placeholder" @input="clearFieldError('administratorName')" /></view> |
| | | <text v-if="invalidField == 'administratorName'" class="field-error">{{errorMessage}}</text> |
| | | </view> |
| | | <view class="field-block last-field"> |
| | | <text class="field-label">管理员手机号</text> |
| | | <view class="input-shell" :class="invalidField == 'administratorPhone' ? 'field-invalid' : ''"><input class="text-input" type="number" v-model="administratorPhone" maxlength="11" placeholder="请输入管理员手机号" placeholder-class="input-placeholder" @input="clearFieldError('administratorPhone')" /></view> |
| | | <text v-if="invalidField == 'administratorPhone'" class="field-error">{{errorMessage}}</text> |
| | | </view> |
| | | <view v-if="currentStep == 4"> |
| | | <view class="settlement-title">结算账户信息</view> |
| | | <view class="field-block"><text class="field-label required">{{bankAccountLabel()}}</text><view class="input-shell" :class="invalidField == 'bankCardNumber' ? 'field-invalid' : ''"><input class="text-input" type="number" maxlength="32" :value="bankCardNumber" placeholder="请输入结算账号" placeholder-class="input-placeholder" @input="normalizeBankNumber" /></view><text v-if="bankAccountLabel() == '对公账号'" class="field-hint">账号名必须与营业执照上的名称一致</text><text v-if="invalidField == 'bankCardNumber'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">开户省份</text><picker class="region-picker" mode="selector" :range="areaProvinceNames" :value="bankProvinceIndex" @change="onBankProvinceChange"><view class="select-shell" :class="invalidField == 'bankProvince' ? 'field-invalid' : ''"><text :class="bankProvince.length == 0 ? 'select-placeholder' : ''">{{bankProvince.length > 0 ? bankProvince : '请选择'}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker></view> |
| | | <view class="field-block"><text class="field-label required">开户城市</text><picker class="region-picker" mode="selector" :range="bankCityNames" :value="bankCityIndex" :disabled="bankProvince.length == 0" @change="onBankCityChange"><view class="select-shell" :class="invalidField == 'bankCity' ? 'field-invalid' : ''"><text :class="bankCity.length == 0 ? 'select-placeholder' : ''">{{bankCity.length > 0 ? bankCity : '请选择'}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view></picker><text class="field-hint">先选省份和城市,再选开户行</text></view> |
| | | <view class="field-block"><text class="field-label required">开户行(支行)</text><view :class="invalidField == 'bankBranch' ? 'select-shell field-invalid' : (bankCityCode.length == 0 ? 'select-shell readonly-shell' : 'select-shell')" @tap="chooseBankBranch"><text class="ellipsis-text" :class="bankBranch.length == 0 ? 'select-placeholder' : ''">{{bankBranch.length > 0 ? bankBranch : (bankCityCode.length == 0 ? '请先选择开户城市' : '请选择')}}</text><image class="down-icon" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view><text v-if="invalidField == 'bankBranch'" class="field-error">{{errorMessage}}</text></view> |
| | | <view class="field-block"><text class="field-label required">{{settlementProofLabel()}}</text><view class="upload-box" :class="invalidField == 'settlementProofUrl' ? 'field-invalid' : ''" @tap="chooseDocument('settlementProof')"><image v-if="settlementProofUrl.length > 0" class="document-image" :src="settlementProofUrl" mode="aspectFill"></image><text v-else>{{uploadingField == 'settlementProof' ? '上传中...' : '添加图片'}}</text></view><text class="field-hint">{{settlementProofHint()}}</text><text v-if="invalidField == 'settlementProofUrl'" class="field-error">{{errorMessage}}</text></view> |
| | | </view> |
| | | |
| | | <view class="submit-button" @tap="submitOnboarding"><text>提交审核</text></view> |
| | | <view v-if="submitError.length > 0" class="banner-error">{{submitError}}</view> |
| | | <view class="step-actions"> |
| | | <view v-if="currentStep > 1" class="secondary-button" @tap="goPrevStep"><text>上一步</text></view> |
| | | <view v-if="currentStep < 4" class="action-primary" @tap="goNextStep"><text>下一步</text></view> |
| | | <view v-else class="action-primary" :class="submitting || uploadingField.length > 0 ? 'button-disabled' : ''" @tap="submitForm"><text>{{submitting ? '提交中...' : '提交进件'}}</text></view> |
| | | </view> |
| | | <view class="safe-space"></view> |
| | | </scroll-view> |
| | | </view></scroll-view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup lang="uts"> |
| | | import { submitOnboarding as submitOnboardingRemote } from '../../services/settlement.uts' |
| | | const submitting = ref(false) |
| | | const merchantType = ref('enterprise') |
| | | const creditCode = ref('') |
| | | const businessValidity = ref('固定期限') |
| | | const businessStartDate = ref('') |
| | | const businessEndDate = ref('') |
| | | const registeredRegion = ref('') |
| | | const registeredProvinceCode = ref('') |
| | | const registeredCityCode = ref('') |
| | | const registeredDistrictCode = ref('') |
| | | const registeredAddress = ref('') |
| | | const subjectName = ref('') |
| | | const subjectIdNumber = ref('') |
| | | const subjectValidity = ref('固定期限') |
| | | const subjectStartDate = ref('') |
| | | const subjectEndDate = ref('') |
| | | const administratorName = ref('') |
| | | const administratorPhone = ref('') |
| | | const personalPhone = ref('') |
| | | const invalidField = ref('') |
| | | const errorMessage = ref('') |
| | | import { loadOnboardingStatus, SettlementOnboarding, submitOnboarding as submitOnboardingRemote } from '../../services/settlement.uts' |
| | | import { uploadMerchantProductImage } from '../../services/merchant-management.uts' |
| | | import { getMerchantOnboardingDraftStorageKey } from '../../services/onboarding-draft.uts' |
| | | import { getSession } from '../../services/session.uts' |
| | | import { areaProvinceNames, areaProvinceCodes, areaCityNames, areaCityCodes } from '../../config/area-codes.uts' |
| | | |
| | | const stepTitles = ['主体信息', '法人信息', '联系人', '结算账户'] |
| | | /** 个人主体没有法人,第二步展示为「本人信息」。 */ |
| | | const stepTitle = (index: number): string => (index == 1 && merchantType.value == 'individual') ? '本人信息' : stepTitles[index] |
| | | const currentStep = ref(1) |
| | | const scrollTop = ref(0) |
| | | const draftNotice = ref(false) |
| | | const submitError = ref('') |
| | | |
| | | const submitting = ref(false); const uploadingField = ref(''); const merchantType = ref('enterprise'); const merchantName = ref(''); const merchantShortName = ref(''); const companyType = ref(''); const companyTypeIndex = ref(-1); const companyTypeOptions = ['政府机构', '国营企业', '私营企业', '外资企业', '个体工商户', '其它组织', '事业单位', '业主委员会']; const industryType = ref(''); const industryTypeIndex = ref(0); const industryTypeOptions = ['餐饮服务', '酒店服务', '景区服务', '加油服务', '停车服务', '其它']; const businessLicenseUrl = ref(''); const creditCode = ref(''); const businessValidity = ref('固定期限'); const businessStartDate = ref(''); const businessEndDate = ref(''); const establishedDate = ref(''); const registeredRegion = ref(''); const registeredProvinceCode = ref(''); const registeredCityCode = ref(''); const registeredDistrictCode = ref(''); const registeredAddress = ref(''); const businessAddress = ref(''); const registeredCapital = ref(''); const subjectName = ref(''); const legalIdFrontUrl = ref(''); const legalIdBackUrl = ref(''); const subjectIdNumber = ref(''); const subjectValidity = ref('10年'); const subjectStartDate = ref(''); const subjectEndDate = ref(''); const legalCertificateAddress = ref(''); const legalMobile = ref(''); const contactName = ref(''); const contactMobile = ref(''); const administratorEmail = ref(''); const bankCardNumber = ref(''); const bankProvince = ref(''); const bankProvinceCode = ref(''); const bankProvinceIndex = ref(0); const bankCity = ref(''); const bankCityCode = ref(''); const bankCityNames = ref([] as string[]); const bankCityCodes = ref([] as string[]); const bankCityIndex = ref(0); const bankBranch = ref(''); const bankBranchCode = ref(''); const bankName = ref(''); const settlementProofUrl = ref(''); const storeHeaderUrl = ref(''); const storeIndoorUrl = ref(''); const storeCashierUrl = ref(''); const invalidField = ref(''); const errorMessage = ref('') |
| | | const merchantTypeOptions = ['企业', '个人'] |
| | | const merchantTypeIndex = ref(0) |
| | | const validityOptions = ['固定期限', '长期'] |
| | | |
| | | const goBack = () => { |
| | | uni.navigateBack() |
| | | const businessValidityIndex = ref(0) |
| | | const subjectValidityOptions = ['10年', '20年', '长期有效'] |
| | | const subjectValidityIndex = ref(0) |
| | | // 草稿按当前登录的操作员分命名空间,换账号看不到上一个账号的草稿(见 services/onboarding-draft.uts)。 |
| | | // 必须在取用点现算:进入页面时机不确定,模块加载时算会把当时的会话固化住。 |
| | | const draftStorageKey = (): string => getMerchantOnboardingDraftStorageKey() |
| | | const currentOperatorId = (): string => { |
| | | const session = getSession() |
| | | if (session == null || session.operatorId == null) return '' |
| | | return (session.operatorId as string).trim() |
| | | } |
| | | let draftRestored = false |
| | | |
| | | const setFieldError = (field: string, message: string) => { |
| | | invalidField.value = field |
| | | errorMessage.value = message |
| | | } |
| | | /** 私营企业等类型汇付要求必须填注册资本,这里与服务端 normalizeRegisteredCapital 保持一致。 */ |
| | | const capitalRequired = (): boolean => merchantType.value == 'enterprise' && ['国营企业', '私营企业', '外资企业', '其它组织', '事业单位'].indexOf(companyType.value) >= 0 |
| | | const bankAccountLabel = (): string => (merchantType.value == 'enterprise' && companyType.value != '个体工商户') ? '对公账号' : '银行卡号' |
| | | const settlementProofLabel = (): string => (merchantType.value == 'enterprise' && companyType.value != '个体工商户') ? '开户许可证' : '银行卡卡号面' |
| | | const settlementProofHint = (): string => (merchantType.value == 'enterprise' && companyType.value != '个体工商户') ? '银行出具的开户许可证或基本存款账户信息,JPG/PNG/BMP,不超过 2MB' : '银行卡正面照片,JPG/PNG/BMP,不超过 2MB,卡号清晰' |
| | | |
| | | const clearFieldError = (field: string) => { |
| | | if (invalidField.value == field) { |
| | | invalidField.value = '' |
| | | errorMessage.value = '' |
| | | } |
| | | } |
| | | |
| | | const normalizeCreditCode = () => { |
| | | creditCode.value = creditCode.value.toUpperCase() |
| | | clearFieldError('creditCode') |
| | | } |
| | | |
| | | const chooseMerchantType = () => { |
| | | uni.showActionSheet({ |
| | | itemList: merchantTypeOptions, |
| | | success: (result: any) => { |
| | | merchantType.value = result.tapIndex == 0 ? 'enterprise' : 'individual' |
| | | invalidField.value = '' |
| | | errorMessage.value = '' |
| | | /** 返回本步骤第一处问题,格式「字段|提示」;没有问题时返回空串。纯函数,不改动错误状态。 */ |
| | | const stepProblem = (step: number): string => { |
| | | if (step == 1) { |
| | | if (merchantType.value == 'enterprise') { |
| | | if (merchantName.value.trim().length == 0) return 'merchantName|必须与营业执照一致,请输入商户名称' |
| | | if (companyType.value.length == 0) return 'companyType|请选择公司类型' |
| | | if (businessLicenseUrl.value.length == 0) return 'businessLicenseUrl|请上传营业执照' |
| | | if (!(/^[0-9A-Z]{18}$/).test(creditCode.value)) return 'creditCode|请输入正确的18位统一社会信用代码' |
| | | if (businessStartDate.value.length == 0) return 'businessStartDate|请选择营业执照有效期开始日期' |
| | | if (businessValidity.value == '固定期限' && businessEndDate.value.length == 0) return 'businessEndDate|请选择营业执照有效期结束日期' |
| | | if (businessStartDate.value.length > 0 && businessEndDate.value.length > 0 && businessStartDate.value > businessEndDate.value) return 'businessEndDate|结束日期不能早于开始日期' |
| | | if (establishedDate.value.length == 0) return 'establishedDate|请选择成立时间' |
| | | if (registeredRegion.value.length == 0) return 'registeredRegion|请选择注册地址' |
| | | if (registeredAddress.value.trim().length == 0) return 'registeredAddress|请输入详细注册地址' |
| | | if (capitalRequired() && registeredCapital.value.trim().length == 0) return 'registeredCapital|该类型必须填写注册资本(单位:万元)' |
| | | if (registeredCapital.value.trim().length > 0 && !(/^\d+(\.\d{1,2})?$/).test(registeredCapital.value.trim())) return 'registeredCapital|注册资本请输入数字,单位为万元' |
| | | } else { |
| | | if (industryType.value.length == 0) return 'industryType|请选择行业类型' |
| | | if (registeredRegion.value.length == 0) return 'registeredRegion|请选择经营地区' |
| | | // 经营详细地址:线下报备(entity 有实体经营场所)下它是通道必填项,是进件的 detail_addr。 |
| | | if (businessAddress.value.trim().length == 0) return 'businessAddress|请输入经营详细地址' |
| | | } |
| | | }) |
| | | // 门店材料是线下报备的一组三张(门头照 F22、内景照 F24、收银台 F105),**不分主体类型**: |
| | | // 通道在进件这一步就按 F-code 逐张核对,缺一张整笔驳回。在这里拦住,好过提交上去以后被驳回 |
| | | // ——那时商户得整份进件重走一遍。 |
| | | if (storeHeaderUrl.value.length == 0) return 'storeHeaderUrl|请上传店铺门头照' |
| | | if (storeIndoorUrl.value.length == 0) return 'storeIndoorUrl|请上传店内环境照' |
| | | if (storeCashierUrl.value.length == 0) return 'storeCashierUrl|请上传店铺收银台照片' |
| | | } |
| | | if (step == 2) { |
| | | if (subjectName.value.trim().length == 0) return 'subjectName|' + (merchantType.value == 'enterprise' ? '请输入法人姓名' : '请输入个人姓名') |
| | | if (legalIdFrontUrl.value.length == 0) return 'legalIdFrontUrl|请上传身份证人像面' |
| | | if (legalIdBackUrl.value.length == 0) return 'legalIdBackUrl|请上传身份证国徽面' |
| | | if (!(/^\d{17}[0-9X]$/).test(subjectIdNumber.value)) return 'subjectIdNumber|请输入正确的18位身份证号码' |
| | | if (subjectStartDate.value.length == 0) return 'subjectStartDate|请选择证件有效期开始日期' |
| | | if (subjectValidity.value != '长期有效' && subjectEndDate.value.length == 0) return 'subjectEndDate|请选择证件有效期截止日期' |
| | | const address = legalCertificateAddress.value.trim() |
| | | if (address.length == 0) return 'legalCertificateAddress|请输入证件地址(与身份证一致)' |
| | | if (address.length < 4) return 'legalCertificateAddress|证件地址至少 4 个字,请写完整' |
| | | const legalMobileValue = legalMobile.value.trim() |
| | | if (legalMobileValue.length == 0) return 'legalMobile|请输入手机号(微信实名认证要用)' |
| | | if (!(/^1\d{10}$/).test(legalMobileValue)) return 'legalMobile|请输入正确的 11 位手机号' |
| | | } |
| | | if (step == 3) { |
| | | if (merchantType.value == 'enterprise' && contactName.value.trim().length == 0) return 'contactName|请输入管理员姓名' |
| | | if (!(/^1\d{10}$/).test(contactMobile.value)) return 'contactMobile|请输入正确的11位手机号' |
| | | if (!(/^[^\s@]+@[^\s@]+\.[^\s@]+$/).test(administratorEmail.value.trim())) return 'administratorEmail|请输入正确的邮箱,用于接收进件结果' |
| | | } |
| | | if (step == 4) { |
| | | if (!(/^\d{16,32}$/).test(bankCardNumber.value)) return 'bankCardNumber|请输入正确的' + bankAccountLabel() |
| | | if (bankProvinceCode.value.length == 0) return 'bankProvince|请选择开户省份' |
| | | if (bankCityCode.value.length == 0) return 'bankCity|请选择开户城市' |
| | | if (bankBranchCode.value.length == 0) return 'bankBranch|请选择开户行(支行)' |
| | | if (settlementProofUrl.value.length == 0) return 'settlementProofUrl|请上传' + settlementProofLabel() |
| | | } |
| | | return '' |
| | | } |
| | | |
| | | const chooseBusinessValidity = () => { |
| | | uni.showActionSheet({ |
| | | itemList: validityOptions, |
| | | success: (result: any) => { |
| | | businessValidity.value = validityOptions[result.tapIndex] |
| | | if (businessValidity.value == '长期') businessEndDate.value = '' |
| | | clearFieldError('businessValidity') |
| | | } |
| | | }) |
| | | } |
| | | |
| | | const chooseSubjectValidity = () => { |
| | | uni.showActionSheet({ |
| | | itemList: validityOptions, |
| | | success: (result: any) => { |
| | | subjectValidity.value = validityOptions[result.tapIndex] |
| | | if (subjectValidity.value == '长期') subjectEndDate.value = '' |
| | | clearFieldError('subjectValidity') |
| | | } |
| | | }) |
| | | } |
| | | |
| | | |
| | | const onRegisteredRegionChange = (event: any) => { |
| | | const values = event.detail.value as Array<string> |
| | | const codes = event.detail.code as Array<string> |
| | | if (values.length != 3 || codes.length != 3) return |
| | | registeredRegion.value = values[0] + ' ' + values[1] + ' ' + values[2] |
| | | registeredProvinceCode.value = codes[0] |
| | | registeredCityCode.value = codes[1] |
| | | registeredDistrictCode.value = codes[2] |
| | | clearFieldError('registeredRegion') |
| | | } |
| | | |
| | | const onBusinessStartChange = (event: any) => { |
| | | businessStartDate.value = event.detail.value as string |
| | | clearFieldError('businessStartDate') |
| | | } |
| | | |
| | | const onBusinessEndChange = (event: any) => { |
| | | businessEndDate.value = event.detail.value as string |
| | | clearFieldError('businessEndDate') |
| | | } |
| | | |
| | | const onSubjectStartChange = (event: any) => { |
| | | subjectStartDate.value = event.detail.value as string |
| | | clearFieldError('subjectStartDate') |
| | | } |
| | | |
| | | const onSubjectEndChange = (event: any) => { |
| | | subjectEndDate.value = event.detail.value as string |
| | | clearFieldError('subjectEndDate') |
| | | } |
| | | |
| | | const isPhoneValid = (phone: string) => /^1[3-9]\d{9}$/.test(phone) |
| | | |
| | | const validateCommonIdentity = () => { |
| | | if (subjectName.value.trim().length == 0) { |
| | | setFieldError('subjectName', merchantType.value == 'enterprise' ? '请输入法人姓名' : '请输入个人姓名') |
| | | return false |
| | | } |
| | | if (!(/^\d{17}[\dXx]$/).test(subjectIdNumber.value.trim())) { |
| | | setFieldError('subjectIdNumber', '请输入正确的18位身份证号码') |
| | | return false |
| | | } |
| | | if (subjectStartDate.value.length == 0) { |
| | | setFieldError('subjectStartDate', '请选择证件有效期开始日期') |
| | | return false |
| | | } |
| | | if (subjectValidity.value == '固定期限') { |
| | | if (subjectEndDate.value.length == 0) { |
| | | setFieldError('subjectEndDate', '请选择证件有效期截止日期') |
| | | return false |
| | | } |
| | | if (subjectStartDate.value > subjectEndDate.value) { |
| | | setFieldError('subjectEndDate', '证件截止日期不能早于开始日期') |
| | | return false |
| | | } |
| | | } |
| | | return true |
| | | } |
| | | |
| | | const validateEnterprise = () => { |
| | | if (!(/^[0-9A-Z]{18}$/).test(creditCode.value.trim())) { |
| | | setFieldError('creditCode', '请输入正确的18位统一社会信用代码') |
| | | return false |
| | | } |
| | | if (businessStartDate.value.length == 0) { |
| | | setFieldError('businessStartDate', '请选择营业执照有效期开始日期') |
| | | return false |
| | | } |
| | | if (businessValidity.value == '固定期限') { |
| | | if (businessEndDate.value.length == 0) { |
| | | setFieldError('businessEndDate', '请选择营业执照有效期结束日期') |
| | | return false |
| | | } |
| | | if (businessStartDate.value > businessEndDate.value) { |
| | | setFieldError('businessEndDate', '证照结束日期不能早于开始日期') |
| | | return false |
| | | } |
| | | } |
| | | if (registeredRegion.value.length == 0) { |
| | | setFieldError('registeredRegion', '请选择注册地址') |
| | | return false |
| | | } |
| | | if (registeredAddress.value.trim().length < 5) { |
| | | setFieldError('registeredAddress', '请输入完整的详细注册地址') |
| | | return false |
| | | } |
| | | if (!validateCommonIdentity()) return false |
| | | if (administratorName.value.trim().length == 0) { |
| | | setFieldError('administratorName', '请输入管理员姓名') |
| | | return false |
| | | } |
| | | if (!isPhoneValid(administratorPhone.value.trim())) { |
| | | setFieldError('administratorPhone', '请输入正确的管理员手机号') |
| | | return false |
| | | } |
| | | return true |
| | | } |
| | | |
| | | const validateIndividual = () => { |
| | | if (!validateCommonIdentity()) return false |
| | | if (!isPhoneValid(personalPhone.value.trim())) { |
| | | setFieldError('personalPhone', '请输入正确的手机号') |
| | | return false |
| | | } |
| | | return true |
| | | } |
| | | |
| | | const submitOnboarding = () => { |
| | | if (submitting.value) return |
| | | /** 校验某一步:通过返回 true;否则把第一处问题写进 invalidField / errorMessage 并返回 false。 */ |
| | | const validateStep = (step: number): boolean => { |
| | | invalidField.value = '' |
| | | errorMessage.value = '' |
| | | const valid = merchantType.value == 'enterprise' ? validateEnterprise() : validateIndividual() |
| | | if (!valid) { |
| | | uni.showToast({ title: errorMessage.value, icon: 'none' }) |
| | | return |
| | | } |
| | | uni.showModal({ |
| | | title: '确认提交审核', |
| | | content: '提交后资料将进入审核,审核期间暂不能发起提现。', |
| | | confirmText: '确认提交', |
| | | confirmColor: '#FF6B2C', |
| | | success: (result: any) => { |
| | | if (!result.confirm) return |
| | | submitting.value = true |
| | | const contactName = merchantType.value == 'enterprise' ? administratorName.value.trim() : subjectName.value.trim() |
| | | const contactMobile = merchantType.value == 'enterprise' ? administratorPhone.value.trim() : personalPhone.value.trim() |
| | | submitOnboardingRemote({ subjectType: merchantType.value, subjectName: subjectName.value.trim(), creditCode: creditCode.value.trim(), subjectIdNumber: subjectIdNumber.value.trim(), subjectCertificateValidityType: subjectValidity.value == '长期' ? 'long_term' : 'fixed', subjectCertificateBeginDate: subjectStartDate.value, subjectCertificateEndDate: subjectEndDate.value, licenseValidityType: businessValidity.value == '长期' ? 'long_term' : 'fixed', licenseBeginDate: businessStartDate.value, licenseEndDate: businessEndDate.value, registeredRegion: registeredRegion.value.trim(), registeredProvinceCode: registeredProvinceCode.value, registeredCityCode: registeredCityCode.value, registeredDistrictCode: registeredDistrictCode.value, registeredAddress: registeredAddress.value.trim(), contactName: contactName, contactMobile: contactMobile }, 'onboarding-' + Date.now(), () => { |
| | | submitting.value = false |
| | | uni.redirectTo({ url: '/pages/merchant-onboarding-pending/merchant-onboarding-pending' }) |
| | | }, (error: any) => { |
| | | submitting.value = false |
| | | uni.showToast({ title: error.message != null ? error.message : '提交失败,请重试', icon: 'none' }) |
| | | }) |
| | | } |
| | | }) |
| | | const problem = stepProblem(step) |
| | | if (problem.length == 0) return true |
| | | const separator = problem.indexOf('|') |
| | | invalidField.value = problem.substring(0, separator) |
| | | errorMessage.value = problem.substring(separator + 1) |
| | | return false |
| | | } |
| | | |
| | | const resetScroll = () => { scrollTop.value = scrollTop.value == 0 ? 1 : 0 } |
| | | const goNextStep = () => { |
| | | if (!validateStep(currentStep.value)) { uni.showToast({ title: errorMessage.value, icon: 'none' }); return } |
| | | saveDraft() |
| | | if (currentStep.value < 4) currentStep.value = currentStep.value + 1 |
| | | resetScroll() |
| | | } |
| | | const goPrevStep = () => { saveDraft(); if (currentStep.value > 1) currentStep.value = currentStep.value - 1; resetScroll() } |
| | | |
| | | const draftPayload = (): any => ({ ownerOperatorId: currentOperatorId(), merchantType: merchantType.value, merchantName: merchantName.value, merchantShortName: merchantShortName.value, companyType: companyType.value, companyTypeIndex: companyTypeIndex.value, industryType: industryType.value, industryTypeIndex: industryTypeIndex.value, businessLicenseUrl: businessLicenseUrl.value, creditCode: creditCode.value, businessValidity: businessValidity.value, businessStartDate: businessStartDate.value, businessEndDate: businessEndDate.value, establishedDate: establishedDate.value, registeredRegion: registeredRegion.value, registeredProvinceCode: registeredProvinceCode.value, registeredCityCode: registeredCityCode.value, registeredDistrictCode: registeredDistrictCode.value, registeredAddress: registeredAddress.value, businessAddress: businessAddress.value, registeredCapital: registeredCapital.value, subjectName: subjectName.value, legalIdFrontUrl: legalIdFrontUrl.value, legalIdBackUrl: legalIdBackUrl.value, subjectIdNumber: subjectIdNumber.value, subjectValidity: subjectValidity.value, subjectStartDate: subjectStartDate.value, subjectEndDate: subjectEndDate.value, legalCertificateAddress: legalCertificateAddress.value, legalMobile: legalMobile.value, contactName: contactName.value, contactMobile: contactMobile.value, administratorEmail: administratorEmail.value, bankCardNumber: bankCardNumber.value, bankProvince: bankProvince.value, bankProvinceCode: bankProvinceCode.value, bankProvinceIndex: bankProvinceIndex.value, bankCity: bankCity.value, bankCityCode: bankCityCode.value, bankCityIndex: bankCityIndex.value, bankBranch: bankBranch.value, bankBranchCode: bankBranchCode.value, bankName: bankName.value, settlementProofUrl: settlementProofUrl.value, storeHeaderUrl: storeHeaderUrl.value, storeIndoorUrl: storeIndoorUrl.value, storeCashierUrl: storeCashierUrl.value, currentStep: currentStep.value }) |
| | | const saveDraft = () => { uni.setStorageSync(draftStorageKey(), JSON.stringify(draftPayload())) } |
| | | const clearDraft = () => { uni.removeStorageSync(draftStorageKey()) } |
| | | const restoreDraft = () => { |
| | | const stored = uni.getStorageSync(draftStorageKey()) |
| | | if (stored == null || stored == '') return |
| | | try { |
| | | const draft = JSON.parse(stored as string) as any |
| | | // 草稿必须属于当前登录的操作员。key 已经按操作员分命名空间,这里再校验一次归属: |
| | | // 更新前存在旧的全局 key 下的草稿,里面可能有别的账号的姓名、身份证号、银行卡号; |
| | | // 归属不符就直接丢弃,宁可让用户重填,也不能把别人的资料预填进来。 |
| | | const owner = draft.ownerOperatorId != null ? (draft.ownerOperatorId as string) : '' |
| | | if (owner != currentOperatorId()) { clearDraft(); return } |
| | | if (draft.merchantType != null) merchantType.value = draft.merchantType as string |
| | | if (draft.merchantName != null) merchantName.value = draft.merchantName as string |
| | | if (draft.merchantShortName != null) merchantShortName.value = draft.merchantShortName as string |
| | | if (draft.companyType != null) companyType.value = draft.companyType as string |
| | | if (draft.companyTypeIndex != null) companyTypeIndex.value = Number(draft.companyTypeIndex) |
| | | if (draft.industryType != null) industryType.value = draft.industryType as string |
| | | if (draft.industryTypeIndex != null) industryTypeIndex.value = Number(draft.industryTypeIndex) |
| | | if (draft.businessLicenseUrl != null) businessLicenseUrl.value = draft.businessLicenseUrl as string |
| | | if (draft.creditCode != null) creditCode.value = draft.creditCode as string |
| | | if (draft.businessValidity != null) businessValidity.value = draft.businessValidity as string |
| | | if (draft.businessStartDate != null) businessStartDate.value = draft.businessStartDate as string |
| | | if (draft.businessEndDate != null) businessEndDate.value = draft.businessEndDate as string |
| | | if (draft.establishedDate != null) establishedDate.value = draft.establishedDate as string |
| | | if (draft.registeredRegion != null) registeredRegion.value = draft.registeredRegion as string |
| | | if (draft.registeredProvinceCode != null) registeredProvinceCode.value = draft.registeredProvinceCode as string |
| | | if (draft.registeredCityCode != null) registeredCityCode.value = draft.registeredCityCode as string |
| | | if (draft.registeredDistrictCode != null) registeredDistrictCode.value = draft.registeredDistrictCode as string |
| | | if (draft.registeredAddress != null) registeredAddress.value = draft.registeredAddress as string |
| | | if (draft.businessAddress != null) businessAddress.value = draft.businessAddress as string |
| | | if (draft.registeredCapital != null) registeredCapital.value = draft.registeredCapital as string |
| | | if (draft.subjectName != null) subjectName.value = draft.subjectName as string |
| | | if (draft.legalIdFrontUrl != null) legalIdFrontUrl.value = draft.legalIdFrontUrl as string |
| | | if (draft.legalIdBackUrl != null) legalIdBackUrl.value = draft.legalIdBackUrl as string |
| | | if (draft.subjectIdNumber != null) subjectIdNumber.value = draft.subjectIdNumber as string |
| | | if (draft.subjectValidity != null) subjectValidity.value = draft.subjectValidity as string |
| | | if (draft.subjectStartDate != null) subjectStartDate.value = draft.subjectStartDate as string |
| | | if (draft.subjectEndDate != null) subjectEndDate.value = draft.subjectEndDate as string |
| | | if (draft.legalCertificateAddress != null) legalCertificateAddress.value = draft.legalCertificateAddress as string |
| | | if (draft.legalMobile != null) legalMobile.value = draft.legalMobile as string |
| | | if (draft.contactName != null) contactName.value = draft.contactName as string |
| | | if (draft.contactMobile != null) contactMobile.value = draft.contactMobile as string |
| | | if (draft.administratorEmail != null) administratorEmail.value = draft.administratorEmail as string |
| | | if (draft.bankCardNumber != null) bankCardNumber.value = draft.bankCardNumber as string |
| | | if (draft.bankProvince != null) bankProvince.value = draft.bankProvince as string |
| | | if (draft.bankProvinceCode != null) bankProvinceCode.value = draft.bankProvinceCode as string |
| | | if (draft.bankProvinceIndex != null) bankProvinceIndex.value = Number(draft.bankProvinceIndex) |
| | | if (draft.bankCity != null) bankCity.value = draft.bankCity as string |
| | | if (draft.bankCityCode != null) bankCityCode.value = draft.bankCityCode as string |
| | | if (draft.bankCityIndex != null) bankCityIndex.value = Number(draft.bankCityIndex) |
| | | if (draft.bankBranch != null) bankBranch.value = draft.bankBranch as string |
| | | if (draft.bankBranchCode != null) bankBranchCode.value = draft.bankBranchCode as string |
| | | if (draft.bankName != null) bankName.value = draft.bankName as string |
| | | if (draft.settlementProofUrl != null) settlementProofUrl.value = draft.settlementProofUrl as string |
| | | if (draft.storeHeaderUrl != null) storeHeaderUrl.value = draft.storeHeaderUrl as string |
| | | if (draft.storeIndoorUrl != null) storeIndoorUrl.value = draft.storeIndoorUrl as string |
| | | if (draft.storeCashierUrl != null) storeCashierUrl.value = draft.storeCashierUrl as string |
| | | if (bankProvince.value.length > 0 && bankProvinceIndex.value >= 0 && bankProvinceIndex.value < areaCityNames.length) { bankCityNames.value = areaCityNames[bankProvinceIndex.value]; bankCityCodes.value = areaCityCodes[bankProvinceIndex.value] } |
| | | merchantTypeIndex.value = merchantType.value == 'individual' ? 1 : 0 |
| | | const restoredBusinessValidity = validityOptions.indexOf(businessValidity.value); if (restoredBusinessValidity >= 0) businessValidityIndex.value = restoredBusinessValidity |
| | | const restoredSubjectValidity = subjectValidityOptions.indexOf(subjectValidity.value); if (restoredSubjectValidity >= 0) subjectValidityIndex.value = restoredSubjectValidity |
| | | if (draft.currentStep != null) { const restoredStep = Number(draft.currentStep); currentStep.value = (restoredStep >= 1 && restoredStep <= 4) ? restoredStep : 1 } |
| | | // 走到这里才说明草稿确实属于当前账号,这时才提示"已恢复草稿"。 |
| | | draftNotice.value = true |
| | | } catch (_error) { clearDraft() } |
| | | } |
| | | const goBack = () => uni.navigateBack() |
| | | const setError = (field: string, message: string) => { invalidField.value = field; errorMessage.value = message } |
| | | const clearFieldError = (field: string) => { if (invalidField.value == field) { invalidField.value = ''; errorMessage.value = '' }; if (submitError.value.length > 0) submitError.value = '' } |
| | | const normalizeCreditCode = (e: any) => { creditCode.value = (e.detail.value as string).toUpperCase().replace(/[^0-9A-Z]/g, ''); clearFieldError('creditCode') } |
| | | const normalizeIdNumber = (e: any) => { subjectIdNumber.value = (e.detail.value as string).toUpperCase().replace(/[^0-9X]/g, ''); clearFieldError('subjectIdNumber') } |
| | | const normalizePhone = (e: any) => { contactMobile.value = (e.detail.value as string).replace(/[^0-9]/g, ''); clearFieldError('contactMobile') } |
| | | const normalizeBankNumber = (e: any) => { bankCardNumber.value = (e.detail.value as string).replace(/[^0-9]/g, ''); clearFieldError('bankCardNumber') } |
| | | const onMerchantTypeChange = (event: any) => { const index = parseInt(event.detail.value as string); if (index < 0 || index >= merchantTypeOptions.length) return; merchantTypeIndex.value = index; merchantType.value = index == 0 ? 'enterprise' : 'individual'; invalidField.value = ''; errorMessage.value = '' } |
| | | const onBusinessValidityChange = (event: any) => { const index = parseInt(event.detail.value as string); if (index < 0 || index >= validityOptions.length) return; businessValidityIndex.value = index; businessValidity.value = validityOptions[index]; if (businessValidity.value == '长期') businessEndDate.value = '' } |
| | | const onSubjectValidityChange = (event: any) => { const index = parseInt(event.detail.value as string); if (index < 0 || index >= subjectValidityOptions.length) return; subjectValidityIndex.value = index; subjectValidity.value = subjectValidityOptions[index]; if (subjectValidity.value == '长期有效') subjectEndDate.value = '' } |
| | | const onCompanyTypeChange = (event: any) => { const index = parseInt(event.detail.value as string); if (index < 0 || index >= companyTypeOptions.length) return; companyTypeIndex.value = index; companyType.value = companyTypeOptions[index]; clearFieldError('companyType') } |
| | | const onIndustryTypeChange = (event: any) => { const index = parseInt(event.detail.value as string); if (index < 0 || index >= industryTypeOptions.length) return; industryTypeIndex.value = index; industryType.value = industryTypeOptions[index]; clearFieldError('industryType') } |
| | | const onBusinessStartChange = (e: any) => { businessStartDate.value = e.detail.value as string; clearFieldError('businessStartDate') }; const onBusinessEndChange = (e: any) => { businessEndDate.value = e.detail.value as string; clearFieldError('businessEndDate') }; const onEstablishedDateChange = (e: any) => { establishedDate.value = e.detail.value as string; clearFieldError('establishedDate') }; const onSubjectStartChange = (e: any) => { subjectStartDate.value = e.detail.value as string; clearFieldError('subjectStartDate') }; const onSubjectEndChange = (e: any) => { subjectEndDate.value = e.detail.value as string; clearFieldError('subjectEndDate') } |
| | | const onRegisteredRegionChange = (e: any) => { const values = e.detail.value as Array<string>; const codes = e.detail.code as Array<string>; if (values.length != 3 || codes.length != 3) return; registeredRegion.value = values[0] + ' ' + values[1] + ' ' + values[2]; registeredProvinceCode.value = codes[0]; registeredCityCode.value = codes[1]; registeredDistrictCode.value = codes[2]; clearFieldError('registeredRegion') } |
| | | const chooseDocument = (kind: string) => { if (uploadingField.value.length > 0) return; uni.chooseImage({ count: 1, sizeType: ['compressed'], sourceType: ['album', 'camera'], success: (r: any) => { const files = r.tempFiles as Array<any>; if (files.length == 0) return; if ((files[0].size as number) > 2097152) { uni.showToast({ title: '图片不能超过2MB', icon: 'none' }); return }; uploadingField.value = kind; uploadMerchantProductImage(files[0].path as string, (url: string) => { uploadingField.value = ''; if (kind == 'businessLicense') { businessLicenseUrl.value = url; clearFieldError('businessLicenseUrl') } else if (kind == 'legalIdFront') { legalIdFrontUrl.value = url; clearFieldError('legalIdFrontUrl') } else if (kind == 'legalIdBack') { legalIdBackUrl.value = url; clearFieldError('legalIdBackUrl') } else if (kind == 'storeHeader') { storeHeaderUrl.value = url; clearFieldError('storeHeaderUrl') } else if (kind == 'storeIndoor') { storeIndoorUrl.value = url; clearFieldError('storeIndoorUrl') } else if (kind == 'storeCashier') { storeCashierUrl.value = url; clearFieldError('storeCashierUrl') } else { settlementProofUrl.value = url; clearFieldError('settlementProofUrl') } }, (error: any) => { uploadingField.value = ''; uni.showToast({ title: error.message != null ? error.message : '图片上传失败,请重试', icon: 'none' }) }) } }) } |
| | | const onBankProvinceChange = (event: any) => { const index = parseInt(event.detail.value as string); if (index < 0 || index >= areaProvinceNames.length) return; bankProvinceIndex.value = index; bankProvince.value = areaProvinceNames[index]; bankProvinceCode.value = areaProvinceCodes[index]; bankCityNames.value = areaCityNames[index]; bankCityCodes.value = areaCityCodes[index]; bankCityIndex.value = 0; bankCity.value = ''; bankCityCode.value = ''; bankBranch.value = ''; bankBranchCode.value = ''; bankName.value = '' } |
| | | const onBankCityChange = (event: any) => { const index = parseInt(event.detail.value as string); if (index < 0 || index >= bankCityNames.value.length) return; bankCityIndex.value = index; bankCity.value = bankCityNames.value[index]; bankCityCode.value = bankCityCodes.value[index]; bankBranch.value = ''; bankBranchCode.value = ''; bankName.value = '' } |
| | | const chooseBankBranch = () => { if (bankCityCode.value.length == 0) { uni.showToast({ title: '请先选择开户城市', icon: 'none' }); return }; uni.navigateTo({ url: '/pages/bank-branch-select/bank-branch-select?city=' + encodeURIComponent(bankProvince.value + ' ' + bankCity.value) + '&cityCode=' + encodeURIComponent(bankCityCode.value) }) } |
| | | const submitForm = () => { |
| | | if (submitting.value || uploadingField.value.length > 0) return |
| | | saveDraft(); submitError.value = '' |
| | | for (let step = 1; step <= 4; step = step + 1) { |
| | | if (!validateStep(step)) { |
| | | currentStep.value = step |
| | | resetScroll() |
| | | uni.showToast({ title: errorMessage.value, icon: 'none' }) |
| | | return |
| | | } |
| | | } |
| | | submitting.value = true |
| | | submitOnboardingRemote({ subjectType: merchantType.value, subjectName: subjectName.value.trim(), creditCode: creditCode.value, subjectCertificateValidityType: subjectValidity.value == '长期有效' ? 'long_term' : 'fixed', subjectCertificateBeginDate: subjectStartDate.value, subjectCertificateEndDate: subjectEndDate.value, subjectIdNumber: subjectIdNumber.value, licenseValidityType: businessValidity.value == '长期' ? 'long_term' : 'fixed', licenseBeginDate: businessStartDate.value, licenseEndDate: businessEndDate.value, registeredRegion: registeredRegion.value, registeredProvinceCode: registeredProvinceCode.value, registeredCityCode: registeredCityCode.value, registeredDistrictCode: registeredDistrictCode.value, registeredAddress: merchantType.value == 'enterprise' ? registeredAddress.value.trim() : legalCertificateAddress.value.trim(), merchantName: merchantName.value.trim(), merchantShortName: merchantShortName.value.trim(), companyType: merchantType.value == 'enterprise' ? companyType.value : industryType.value, businessLicenseUrl: businessLicenseUrl.value, establishedDate: establishedDate.value, registeredCapital: registeredCapital.value.trim(), legalIdFrontUrl: legalIdFrontUrl.value, legalIdBackUrl: legalIdBackUrl.value, legalCertificateAddress: legalCertificateAddress.value.trim(), legalMobile: legalMobile.value.trim(), administratorEmail: administratorEmail.value.trim(), contactName: merchantType.value == 'enterprise' ? contactName.value.trim() : subjectName.value.trim(), contactMobile: contactMobile.value, bankCardNumber: bankCardNumber.value, bankProvinceCode: bankProvinceCode.value, bankCityCode: bankCityCode.value, bankBranchCode: bankBranchCode.value, settlementCardFrontUrl: merchantType.value == 'enterprise' && companyType.value != '个体工商户' ? '' : settlementProofUrl.value, registrationAccountUrl: merchantType.value == 'enterprise' && companyType.value != '个体工商户' ? settlementProofUrl.value : '', storeHeaderUrl: storeHeaderUrl.value, storeIndoorUrl: storeIndoorUrl.value, storeCashierUrl: storeCashierUrl.value, businessAddress: merchantType.value == 'individual' ? businessAddress.value.trim() : '' }, 'onboarding-' + Date.now(), (result: SettlementOnboarding) => { submitting.value = false; if (result.status == 'approved') clearDraft(); uni.redirectTo({ url: '/pages/merchant-onboarding-pending/merchant-onboarding-pending' }) }, (error: any) => { submitting.value = false; submitError.value = error.message != null ? error.message : '提交失败,请重试'; uni.showToast({ title: submitError.value, icon: 'none' }) }) |
| | | } |
| | | |
| | | onShow(() => { |
| | | if (!draftRestored) { draftRestored = true; restoreDraft() } |
| | | loadOnboardingStatus((item: SettlementOnboarding) => { |
| | | // retryable:通道侧配置已明确失败(例如微信支付配置被拒),这条进件不会自己再往前走, |
| | | // 停在页面上让商户重新提交,而不是跳去"审核中"把人挡在门外。 |
| | | if ((item.status == 'pending' || item.status == 'approved') && !item.retryable) { |
| | | uni.redirectTo({ url: '/pages/merchant-onboarding-pending/merchant-onboarding-pending' }) |
| | | } |
| | | }, (_error: any) => {}) |
| | | const stored = uni.getStorageSync('merchant-settlement-selected-branch') |
| | | if (stored == null || stored == '') return |
| | | try { |
| | | const branch = JSON.parse(stored as string) |
| | | bankBranch.value = branch.branchName as string |
| | | bankName.value = branch.bankName as string |
| | | bankBranchCode.value = branch.branchCode as string |
| | | } catch (_error) {} |
| | | uni.removeStorageSync('merchant-settlement-selected-branch') |
| | | }) |
| | | </script> |
| | | |
| | | <style> |
| | | page { background-color: #F4F6F9; } |
| | | .page { display: flex; flex: 1; width: 100%; height: 100vh; flex-direction: column; overflow: hidden; background-color: #F4F6F9; } |
| | | .navigation-bar { display: flex; height: 174rpx; padding: 68rpx 38rpx 0; flex: none; flex-direction: row; align-items: center; position: relative; background-color: #F4F6F9; box-sizing: border-box; } |
| | | .back-button { display: flex; width: 64rpx; height: 64rpx; justify-content: center; align-items: center; position: absolute; left: 38rpx; top: 89rpx; z-index: 2; } |
| | | .back-icon { width: 38rpx; height: 38rpx; transform: rotate(180deg); } |
| | | .back-button { display: flex; width: 80rpx; height: 80rpx; justify-content: center; align-items: center; position: absolute; left: 18rpx; top: 79rpx; z-index: 2; } |
| | | .back-icon { width: 44rpx; height: 44rpx; } |
| | | .page-title { width: 100%; color: #202B3E; font-size: 34rpx; line-height: 48rpx; text-align: center; font-weight: 700; } |
| | | .form-scroll { flex: 1; height: 0; padding: 0 34rpx; box-sizing: border-box; } |
| | | .form-card { margin-top: 26rpx; padding: 30rpx; border-radius: 30rpx; background-color: #FFFFFF; box-shadow: 0 14rpx 34rpx rgba(49, 55, 66, 0.05); box-sizing: border-box; } |
| | | .subject-card { margin-top: 0; padding-top: 20rpx; padding-bottom: 20rpx; } |
| | | .merchant-type-row { display: flex; min-height: 92rpx; flex-direction: row; align-items: center; } |
| | | .type-copy { display: flex; flex: 1; flex-direction: column; } |
| | | .inline-label { margin-bottom: 0; } |
| | | .type-control { display: flex; width: 190rpx; height: 72rpx; padding: 0 18rpx 0 22rpx; flex-direction: row; align-items: center; justify-content: space-between; border-radius: 18rpx; color: #303B4C; font-size: 27rpx; line-height: 38rpx; background-color: #F2F3F5; box-sizing: border-box; } |
| | | .section-title { display: block; margin-bottom: 28rpx; color: #263244; font-size: 31rpx; line-height: 44rpx; font-weight: 700; } |
| | | .field-block { display: flex; margin-bottom: 26rpx; flex-direction: column; } |
| | | .last-field { margin-bottom: 0; } |
| | | .field-label { display: block; margin-bottom: 13rpx; color: #374253; font-size: 27rpx; line-height: 38rpx; font-weight: 600; } |
| | | .input-shell, .select-shell { display: flex; width: 100%; height: 82rpx; padding: 0 22rpx; flex-direction: row; align-items: center; border-width: 1rpx; border-style: solid; border-color: #DDE3EA; border-radius: 18rpx; background-color: #FFFEFD; box-sizing: border-box; } |
| | | .text-input { flex: 1; height: 80rpx; color: #303B4C; font-size: 27rpx; line-height: 80rpx; } |
| | | .input-placeholder, .select-placeholder { color: #A8B1BE; font-size: 26rpx; } |
| | | .select-shell { justify-content: space-between; color: #303B4C; font-size: 27rpx; line-height: 38rpx; } |
| | | .readonly-shell { color: #5A6472; background-color: #F0F2F5; border-color: #F0F2F5; } |
| | | .date-picker { width: 100%; } |
| | | .down-icon { width: 30rpx; height: 30rpx; margin-left: 14rpx; flex: none; } |
| | | .step-bar { display: flex; height: 92rpx; padding: 0 38rpx; flex: none; flex-direction: row; align-items: flex-start; background-color: #F4F6F9; box-sizing: border-box; } |
| | | .step-item { display: flex; flex: 1; flex-direction: column; align-items: center; } |
| | | .step-index { width: 40rpx; height: 40rpx; border-radius: 20rpx; color: #9AA4B3; font-size: 22rpx; line-height: 40rpx; text-align: center; background-color: #E4E8EF; } |
| | | .step-index-active { color: #FFFFFF; background-color: #FF7625; } |
| | | .step-index-done { color: #FF7625; background-color: #FFF0E4; } |
| | | .step-label { margin-top: 8rpx; color: #9AA4B3; font-size: 22rpx; line-height: 30rpx; } |
| | | .step-label-active { color: #FF7625; font-weight: 700; } |
| | | .step-label-done { color: #C9A08A; } |
| | | .form-scroll { flex: 1; height: 0; background-color: #F4F6F9; } |
| | | .form-body { width: 100%; padding: 8rpx 38rpx 0; background-color: #F4F6F9; box-sizing: border-box; } |
| | | .draft-hint { margin-bottom: 24rpx; padding: 18rpx 24rpx; color: #B4520F; font-size: 24rpx; line-height: 34rpx; background-color: #FFF5EA; border-radius: 20rpx; } |
| | | .banner-error { margin-top: 8rpx; padding: 18rpx 24rpx; color: #F06A66; font-size: 24rpx; line-height: 34rpx; background-color: #FEF3F2; border-radius: 20rpx; } |
| | | |
| | | .field-block { display: flex; margin-bottom: 30rpx; flex-direction: column; } |
| | | .settlement-title { margin: 6rpx 0 28rpx; color: #202B3E; font-size: 30rpx; line-height: 42rpx; font-weight: 700; } |
| | | .field-label { display: block; margin-bottom: 14rpx; color: #313744; font-size: 28rpx; line-height: 40rpx; font-weight: 700; } |
| | | .field-hint { margin-top: 10rpx; color: #9AA4B3; font-size: 22rpx; line-height: 32rpx; } |
| | | .input-shell, .select-shell { display: flex; width: 100%; height: 104rpx; padding: 0 30rpx; align-items: center; flex-direction: row; border: 1rpx solid #E0E5EC; border-radius: 30rpx; color: #313744; font-size: 28rpx; line-height: 38rpx; background-color: #FFFEFD; box-sizing: border-box; } |
| | | .text-input { flex: 1; height: 100rpx; border-radius: 30rpx; color: #313744; font-size: 28rpx; line-height: 100rpx; } |
| | | .input-placeholder, .select-placeholder { color: #B8C0CC; font-size: 28rpx; } |
| | | .select-shell { justify-content: space-between; } |
| | | .readonly-shell { background-color: #EDF0F4; } |
| | | .date-picker, .region-picker { width: 100%; } |
| | | .down-icon { width: 28rpx; height: 28rpx; flex: none; opacity: 0.45; } |
| | | .ellipsis-text { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } |
| | | .upload-box { display: flex; width: 100%; height: 220rpx; justify-content: center; align-items: center; overflow: hidden; border: 1rpx solid #E0E5EC; border-radius: 30rpx; color: #9AA4B3; font-size: 26rpx; background-color: #FFFEFD; box-sizing: border-box; } |
| | | .document-image { width: 100%; height: 100%; border-radius: 30rpx; } |
| | | .field-invalid { border-color: #F0746D; background-color: #FFF9F8; } |
| | | .field-error { margin-top: 9rpx; color: #E95B55; font-size: 23rpx; line-height: 33rpx; } |
| | | .submit-button { display: flex; height: 88rpx; margin-top: 34rpx; justify-content: center; align-items: center; border-radius: 44rpx; color: #FFFFFF; font-size: 29rpx; line-height: 41rpx; font-weight: 700; background-image: linear-gradient(90deg, #FF7625 0%, #FF9630 100%); box-shadow: 0 12rpx 24rpx rgba(255, 107, 36, 0.20); } |
| | | .safe-space { height: 70rpx; } |
| | | </style> |
| | | .field-error { margin-top: 10rpx; color: #F06A66; font-size: 24rpx; line-height: 34rpx; } |
| | | .step-actions { display: flex; margin-top: 24rpx; flex-direction: row; align-items: center; } |
| | | .secondary-button { display: flex; width: 200rpx; height: 100rpx; margin-right: 24rpx; justify-content: center; align-items: center; border-radius: 50rpx; color: #313744; font-size: 28rpx; line-height: 38rpx; font-weight: 700; background-color: #EDF0F4; } |
| | | .action-primary { display: flex; flex: 1; height: 100rpx; justify-content: center; align-items: center; border-radius: 50rpx; color: #FFFFFF; font-size: 30rpx; line-height: 42rpx; font-weight: 700; background-image: linear-gradient(90deg, #FF8A24 0%, #FF4B36 100%); box-shadow: 0 14rpx 26rpx rgba(255, 101, 48, 0.22); } |
| | | .button-disabled { opacity: 0.58; } |
| | | .safe-space { height: 120rpx; } |
| | | </style> |