feat: 实现协议全文与同意记录功能
新增协议全文后台管理、版本化发布及本人同意记录功能,支持可撤销授权;完善资料与授权相关边界提示,调整评估提交时的资料与授权校验逻辑,同步更新相关页面文案与路由。
This commit is contained in:
@@ -50,9 +50,11 @@
|
||||
</view>
|
||||
<view v-else class="upload-area">
|
||||
<text class="upload-title">资料选择 <text class="optional">(选填)</text></text>
|
||||
<text class="upload-desc">支持照片,仅在本机预览,尚未上传。</text>
|
||||
<view class="upload-grid"><view class="upload-card" v-for="(item, index) in uploadNames" :key="item" :class="index == 2 ? 'upload-last' : ''" @click="documentAction(item)"><image v-if="documentPath(item).length > 0" class="document-thumb" :src="documentPath(item)" mode="aspectFill" @error="documentExpired(item)" /><AppIcon v-else name="upload" :size="52" /><text class="upload-name">{{ item }}</text><text :class="documentPath(item).length > 0 ? 'upload-state selected-state' : 'upload-state'">{{ documentPath(item).length > 0 ? '已选择 · 管理' : '点击选择' }}</text></view></view>
|
||||
<view class="consent"><view class="check-hit" @click="documentConsent = !documentConsent"><view :class="documentConsent ? 'checkbox checked' : 'checkbox'"><text v-if="documentConsent" class="check-mark">✓</text></view></view><text class="consent-copy">我已阅读并同意<text class="consent-link" @click="showAgreement('资料使用协议')">《资料使用协议》</text></text></view>
|
||||
<text class="upload-desc">JPG / PNG,单张最多5MB。仅本人可查看和删除;照片未核验,暂不参与评估。</text>
|
||||
<view class="upload-grid"><view class="upload-card" v-for="(item, index) in uploadNames" :key="item" :class="index == uploadNames.length - 1 ? 'upload-last' : ''" @click="documentAction(item)"><image v-if="documentPath(item).length > 0" class="document-thumb" :src="documentPath(item)" mode="aspectFill" @error="documentExpired(item, '')" /><AppIcon v-else name="upload" :size="52" /><text class="upload-name">{{ item }}</text><text class="upload-state">{{ documentState(item) }}</text></view></view>
|
||||
<text v-if="documentMessage.length > 0" class="upload-desc">{{ documentMessage }}</text>
|
||||
<text v-if="documentRecoveryFailed" class="consent-link" @click="restoreDocuments">照片读取失败 · 点击重试</text>
|
||||
<view class="consent"><view class="check-hit" @click="toggleDocumentConsent"><view :class="documentConsent ? 'checkbox checked' : 'checkbox'"><text v-if="documentConsent" class="check-mark">✓</text></view></view><text class="consent-copy">我已阅读并同意<text class="consent-link" @click="showAgreement('资料使用协议')">《资料使用协议》</text></text></view>
|
||||
<view class="data-section"><text class="upload-title">数据授权 <text class="optional">(可选)</text></text><text class="upload-desc">自主选择授权项目,可随时取消选择。</text><view class="grant-list"><view v-for="grant in authorizations" :key="grant.key" :class="grant.selected ? 'grant-card grant-selected' : 'grant-card'" @click="grant.selected = !grant.selected"><text :class="grant.selected ? 'grant-name grant-name-active' : 'grant-name'">{{ grant.name }}</text><view :class="grant.selected ? 'checkbox checked' : 'checkbox'"><text v-if="grant.selected" class="check-mark">✓</text></view></view></view><view class="consent"><view class="check-hit" @click="dataConsent = !dataConsent"><view :class="dataConsent ? 'checkbox checked' : 'checkbox'"><text v-if="dataConsent" class="check-mark">✓</text></view></view><text class="consent-copy">我已阅读并同意<text class="consent-link" @click="showAgreement('数据授权协议')">《数据授权协议》</text></text></view><text class="local-note">当前仅保存选择意向,不查询外部数据。</text></view>
|
||||
</view>
|
||||
<view class="scroll-spacer" :style="bottomStyle(24)"></view>
|
||||
@@ -74,22 +76,29 @@
|
||||
import AssessmentSelect from '@/packages/finance/components/AssessmentSelect.uvue'
|
||||
import { AssessmentForm, SelectedDocument, DataAuthorization, emptyAssessmentForm, activeAssessmentType, getAssessmentInput, formFromInput, documentsFromInput, setAssessmentInput, showAgreementNotice, assetValidationMessage, contributionValidationMessage, clearAssessmentFlow } from '@/services/assessment.uts'
|
||||
import { contributionOptions as contributionChoices, contributionBaseVisible as baseVisible } from '@/services/contribution.uts'
|
||||
import { uploadDocument, downloadDocument, deleteDocument, releaseDocumentTemp, documentCategory, fetchOwnedDocuments } from '@/services/documents.uts'
|
||||
import { Agreement, emptyAgreement, fetchAgreement, grantConsent, consentRequestId } from '@/services/agreements.uts'
|
||||
const { bottomStyle, contentStyle, pageStyle } = usePageLayout()
|
||||
const contributionOptions = (current: string): string[] => contributionChoices(current)
|
||||
const contributionBaseVisible = (duration: string): boolean => baseVisible(duration)
|
||||
const showAgreement = (title: string) => { showAgreementNotice(title) }
|
||||
const editId = ref(''); const editError = ref(''); const editLoading = ref(false)
|
||||
let baseRevisionId = ''; let editGeneration = 0
|
||||
const step = ref(0)
|
||||
const assessmentType = ref('personal')
|
||||
const form = ref<AssessmentForm>(emptyAssessmentForm())
|
||||
let ownerToken = ''; let goingToConfirm = false; let visible = false; let accountTimer = 0
|
||||
let ownerToken = ''; let goingToConfirm = false; let goingToAgreement = false; let visible = false; let accountTimer = 0
|
||||
const sameOwner = (): boolean => { const session = getSession(); return session != null && session['token'] == ownerToken }
|
||||
const customCity = ref('')
|
||||
const cityChanged = (city: string) => { form.value.city = city; if (city != '其他城市') customCity.value = '' }
|
||||
const documentConsent = ref(false)
|
||||
const dataConsent = ref(false)
|
||||
const documents = ref<SelectedDocument[]>([])
|
||||
const documentBusy = ref(''); const documentMessage = ref('')
|
||||
const documentRecoveryFailed = ref(false); let thumbnailIds: string[] = []
|
||||
let documentGeneration = 0; let documentConsentId = ''; let consentKey = ''; let pendingAgreement: Agreement = emptyAgreement()
|
||||
let choosingImage = false; let previewingImage = false
|
||||
const clearDocumentTemps = () => { documents.value.forEach((file) => { if (file.documentId.length > 0) releaseDocumentTemp(file.path); file.path = '' }) }
|
||||
const invalidateDocuments = () => { documentGeneration++; documentBusy.value = ''; thumbnailIds = []; documentRecoveryFailed.value = false; clearDocumentTemps() }
|
||||
const authorizations = ref<DataAuthorization[]>([{ key: 'credit', name: '征信查询', selected: false }, { key: 'business', name: '经营数据', selected: false }, { key: 'fund', name: '公积金数据', selected: false }])
|
||||
const titles = ['基本信息', '经营 / 收入信息', '资产 / 负债信息', '资料上传 / 授权']
|
||||
const uploadNames = computed((): string[] => assessmentType.value == 'enterprise' ? ['营业执照', '银行流水', '经营证明'] : ['身份证正面', '身份证反面', '银行卡', '收入证明'])
|
||||
@@ -100,7 +109,7 @@
|
||||
if (sameOwner()) return true
|
||||
const session = getSession()
|
||||
ownerToken = session?.getString('token') ?? ''
|
||||
editGeneration++; initialized = false; stopAccountTimer(); clearAssessmentFlow()
|
||||
editGeneration++; invalidateDocuments(); documentConsentId = ''; consentKey = ''; pendingAgreement = emptyAgreement(); documentMessage.value = ''; initialized = false; stopAccountTimer(); clearAssessmentFlow()
|
||||
form.value = emptyAssessmentForm(); documents.value = []; customCity.value = ''; step.value = 0
|
||||
documentConsent.value = false; dataConsent.value = false; authorizations.value.forEach((item) => { item.selected = false })
|
||||
baseRevisionId = ''; editLoading.value = false; editError.value = '账户已变化,请返回重新填写。'
|
||||
@@ -121,6 +130,7 @@
|
||||
if (currentForm.city == '其他城市' && customCity.value.trim().length > 0) currentForm.city = customCity.value.trim()
|
||||
setAssessmentInput(assessmentType.value, step.value, currentForm, documents.value, authorizations.value, documentConsent.value, dataConsent.value, editId.value, baseRevisionId)
|
||||
}
|
||||
const showAgreement = (title: string) => { if (!ensureOwner()) return; rememberInput(); goingToAgreement = true; showAgreementNotice(title) }
|
||||
const restore = (type: string) => {
|
||||
assessmentType.value = type; step.value = 0; form.value = emptyAssessmentForm(); customCity.value = ''; documents.value = []; documentConsent.value = false; dataConsent.value = false
|
||||
authorizations.value.forEach((item) => { item.selected = false })
|
||||
@@ -136,7 +146,39 @@
|
||||
initialized = true
|
||||
rememberInput()
|
||||
}
|
||||
const switchType = (type: string) => { if (!ensureOwner() || editId.value.length > 0 || assessmentType.value == type) return; rememberInput(); restore(type); step.value = 0; rememberInput() }
|
||||
const hydrateDocumentCards = () => {
|
||||
if (!visible || !sameOwner() || step.value != 3) return
|
||||
const ticket = documentGeneration
|
||||
uploadNames.value.forEach((name) => {
|
||||
const item = documents.value.find((file) => file.name == name)
|
||||
if (item == null || item.documentId.length == 0 || item.deletePending || item.path.length > 0 || thumbnailIds.includes(item.documentId)) return
|
||||
const id = item.documentId; thumbnailIds.push(id)
|
||||
downloadDocument(id).then((path) => {
|
||||
const current = documents.value.find((file) => file.documentId == id && !file.deletePending)
|
||||
if (ticket != documentGeneration || !visible || !sameOwner() || current == null) { releaseDocumentTemp(path); return }
|
||||
if (current.path.length > 0) { releaseDocumentTemp(path); return }
|
||||
current.path = path
|
||||
}).catch(() => {
|
||||
if (ticket == documentGeneration && visible && sameOwner()) documentMessage.value = '缩略图读取失败,点击对应卡片可重试查看'
|
||||
}).finally(() => { if (ticket == documentGeneration) thumbnailIds = thumbnailIds.filter((value) => value != id) })
|
||||
})
|
||||
}
|
||||
const restoreDocuments = async () => {
|
||||
if (!ensureOwner() || !initialized || documentBusy.value.length > 0) return
|
||||
const ticket = documentGeneration; documentBusy.value = 'restore'
|
||||
try {
|
||||
const saved = await fetchOwnedDocuments()
|
||||
if (ticket != documentGeneration || !ensureOwner()) return
|
||||
const pending = documents.value.filter((file) => file.documentId.length == 0)
|
||||
documents.value.forEach((file) => { if (file.documentId.length > 0) releaseDocumentTemp(file.path) })
|
||||
// 卡片先处理待上传项;同类已保存照片通过卡片逐份管理。
|
||||
documents.value = pending.concat(saved.map((file): SelectedDocument => ({ name: file.name, path: '', documentId: file.documentId, consentId: file.consentId, deletePending: file.deletePending })))
|
||||
documentRecoveryFailed.value = false; hydrateDocumentCards()
|
||||
rememberInput()
|
||||
} catch (error) { if (ticket == documentGeneration && ensureOwner()) { documentRecoveryFailed.value = true; documentMessage.value = error instanceof Error ? error.message : '已保存照片读取失败,请重新读取' } }
|
||||
finally { if (ticket == documentGeneration) documentBusy.value = '' }
|
||||
}
|
||||
const switchType = (type: string) => { if (!ensureOwner() || editId.value.length > 0 || assessmentType.value == type) return; rememberInput(); invalidateDocuments(); restore(type); step.value = 0; rememberInput(); restoreDocuments() }
|
||||
const loadEdit = async (type: string) => {
|
||||
const ticket = ++editGeneration; editLoading.value = true
|
||||
try {
|
||||
@@ -144,6 +186,7 @@
|
||||
if (ticket != editGeneration || !ensureOwner()) return
|
||||
baseRevisionId = saved.getString('baseRevisionId') ?? ''
|
||||
writeAssessmentFlow(ownerToken, saved); restore(saved.getString('type') ?? type)
|
||||
restoreDocuments()
|
||||
} catch (err) { if (ticket == editGeneration && ensureOwner()) editError.value = err instanceof Error ? err.message : '资料读取失败,请返回重试。' }
|
||||
finally { if (ticket == editGeneration) editLoading.value = false }
|
||||
}
|
||||
@@ -161,16 +204,20 @@
|
||||
})
|
||||
onShow(() => {
|
||||
visible = true; stopAccountTimer()
|
||||
goingToConfirm = false
|
||||
const returningFromImage = choosingImage || previewingImage
|
||||
goingToConfirm = false; goingToAgreement = false; previewingImage = false
|
||||
if (initialized && !sameOwner()) ensureOwner()
|
||||
const input = getAssessmentInput(assessmentType.value)
|
||||
if (initialized && input != null && typeof input['step'] == 'number') step.value = Math.max(0, Math.min(3, input['step'] as number))
|
||||
startAccountTimer()
|
||||
if (!returningFromImage) restoreDocuments()
|
||||
})
|
||||
// 离开填写流程即丢弃未提交内容;进入确认页时只在内存传递。
|
||||
onHide(() => {
|
||||
visible = false; stopAccountTimer()
|
||||
if (!goingToConfirm) {
|
||||
if (choosingImage || previewingImage) return
|
||||
invalidateDocuments()
|
||||
if (!goingToConfirm && !goingToAgreement) {
|
||||
editGeneration++; if (editId.value.length > 0) editError.value = '已离开修改流程,请返回档案重新进入。'
|
||||
// 离页清空资料但不切换步骤,避免卸载时又挂载另一组原生输入组件。
|
||||
clearAssessmentFlow(); form.value = emptyAssessmentForm(); documents.value = []
|
||||
@@ -178,37 +225,130 @@
|
||||
authorizations.value.forEach((item) => { item.selected = false })
|
||||
}
|
||||
})
|
||||
onUnload(() => { visible = false; stopAccountTimer(); editGeneration++; initialized = false; clearAssessmentFlow() })
|
||||
onUnload(() => { visible = false; stopAccountTimer(); editGeneration++; invalidateDocuments(); initialized = false; clearAssessmentFlow() })
|
||||
// 页内前后步骤只维护当前内存状态,返回入口后不保留填写内容。
|
||||
onBackPress((): boolean => { rememberInput(); if (step.value > 0) { step.value--; rememberInput(); return true }; return false })
|
||||
const previous = () => { rememberInput(); if (step.value > 0) step.value--; rememberInput() }
|
||||
const back = () => { if (step.value > 0) { previous(); return }; rememberInput(); if (getCurrentPages().length > 1) uni.navigateBack(); else uni.redirectTo({ url: '/packages/finance/entry/entry?type=' + assessmentType.value }) }
|
||||
const documentPath = (name: string): string => { const item = documents.value.find((file) => file.name == name); return item == null ? '' : item.path }
|
||||
const documentExpired = (name: string) => {
|
||||
documents.value = documents.value.filter((file) => file.name != name); rememberInput()
|
||||
uni.showToast({ title: name + '已失效,请重新选择', icon: 'none' })
|
||||
const cardDocument = (name: string): SelectedDocument | null => documents.value.find((file) => file.name == name && file.documentId.length == 0) ?? documents.value.find((file) => file.name == name) ?? null
|
||||
const documentPath = (name: string): string => { const item = cardDocument(name); return item == null ? '' : item.path }
|
||||
const documentState = (name: string): string => {
|
||||
if (documentBusy.value == name) return '处理中…'
|
||||
const item = cardDocument(name)
|
||||
if (item == null) return '点击上传'
|
||||
const count = documents.value.filter((file) => file.name == name && file.documentId.length > 0).length
|
||||
if (item.documentId.length > 0 && count > 1) return count.toString() + ' 张照片 · 管理'
|
||||
if (item.deletePending) return '删除未完成 · 重试'
|
||||
return item.documentId.length > 0 ? '已上传 · 管理' : '未上传 · 重试'
|
||||
}
|
||||
const documentExpired = (name: string, id: string) => {
|
||||
const item = id.length == 0 ? cardDocument(name) : documents.value.find((file) => file.name == name && file.documentId == id)
|
||||
if (item != null) { if (item.documentId.length > 0) releaseDocumentTemp(item.path); item.path = '' }
|
||||
rememberInput(); documentMessage.value = '预览已失效,已上传资料仍可重新查看或删除'
|
||||
}
|
||||
const toggleDocumentConsent = async () => {
|
||||
if (!ensureOwner() || documentBusy.value.length > 0) return
|
||||
if (documentConsent.value) { documentConsent.value = false; rememberInput(); return }
|
||||
const ticket = documentGeneration; documentBusy.value = 'consent'; documentMessage.value = ''
|
||||
try {
|
||||
if (pendingAgreement.version.length == 0) {
|
||||
const agreement = await fetchAgreement('document-use')
|
||||
if (ticket != documentGeneration || !ensureOwner()) return
|
||||
pendingAgreement = agreement; consentKey = consentRequestId()
|
||||
}
|
||||
const record = await grantConsent(pendingAgreement, consentKey)
|
||||
if (ticket != documentGeneration || !ensureOwner()) return
|
||||
if (record.revokedAt.length > 0) { pendingAgreement = emptyAgreement(); consentKey = ''; throw new Error('该同意已撤销,请重新阅读并确认') }
|
||||
documentConsentId = record.consentId; documentConsent.value = true; pendingAgreement = emptyAgreement(); consentKey = ''; rememberInput()
|
||||
} catch (error) { if (ticket == documentGeneration && ensureOwner()) documentMessage.value = error instanceof Error ? error.message : '同意记录未确认,请再次点击重试' }
|
||||
finally { if (ticket == documentGeneration) documentBusy.value = '' }
|
||||
}
|
||||
const uploadSelectedDocument = async (name: string) => {
|
||||
if (!ensureOwner() || documentBusy.value.length > 0) return
|
||||
const item = documents.value.find((file) => file.name == name && file.documentId.length == 0)
|
||||
if (item == null || item.path.length == 0 || item.documentId.length > 0) return
|
||||
if (!documentConsent.value || documentConsentId.length == 0) { documentMessage.value = '请阅读并确认资料使用协议后上传'; return }
|
||||
const ticket = documentGeneration; documentBusy.value = name; documentMessage.value = ''
|
||||
try {
|
||||
const id = await uploadDocument(item.path, documentCategory(name), documentConsentId)
|
||||
if (ticket != documentGeneration || !ensureOwner()) return
|
||||
item.documentId = id; item.consentId = documentConsentId; rememberInput()
|
||||
documentMessage.value = '照片已单独保存,未核验、暂不参与评估'
|
||||
} catch (error) { if (ticket == documentGeneration && ensureOwner()) documentMessage.value = error instanceof Error ? error.message : '保存结果未确认,请手动重试;可能另存一份照片' }
|
||||
finally { if (ticket == documentGeneration) documentBusy.value = '' }
|
||||
}
|
||||
const chooseDocument = (name: string) => {
|
||||
if (!ensureOwner()) return
|
||||
const ticket = editGeneration
|
||||
if (!ensureOwner() || documentBusy.value.length > 0) return
|
||||
if (!documentConsent.value || documentConsentId.length == 0) { documentMessage.value = '请阅读并确认资料使用协议后上传'; return }
|
||||
const ticket = documentGeneration; choosingImage = true
|
||||
uni.chooseImage({ count: 1, sizeType: ['compressed'], sourceType: ['album', 'camera'], success: (result) => {
|
||||
if (ticket != editGeneration || !ensureOwner() || result.tempFilePaths.length == 0) return
|
||||
documents.value = documents.value.filter((file) => file.name != name)
|
||||
const document: SelectedDocument = { name, path: result.tempFilePaths[0] }
|
||||
documents.value.push(document); rememberInput()
|
||||
}, fail: (error) => { if (ticket == editGeneration && ensureOwner() && !error.errMsg.includes('cancel')) uni.showToast({ title: '选择失败,请重试或检查相册权限', icon: 'none' }) } })
|
||||
choosingImage = false
|
||||
if (ticket != documentGeneration || !sameOwner() || result.tempFilePaths.length == 0) return
|
||||
documents.value = documents.value.filter((file) => file.name != name || file.documentId.length > 0)
|
||||
const document: SelectedDocument = { name, path: result.tempFilePaths[0], documentId: '', consentId: '', deletePending: false }
|
||||
documents.value.unshift(document); rememberInput(); uploadSelectedDocument(name)
|
||||
}, fail: (error) => { choosingImage = false; if (ticket == documentGeneration && sameOwner() && !error.errMsg.includes('cancel')) documentMessage.value = '选择失败,请重试或检查相册权限' } })
|
||||
}
|
||||
const previewDocument = async (name: string, id: string) => {
|
||||
if (!ensureOwner() || documentBusy.value.length > 0) return
|
||||
const item = documents.value.find((file) => file.name == name && (id.length == 0 || file.documentId == id))
|
||||
if (item == null || item.deletePending || item.documentId.length == 0) return
|
||||
const ticket = documentGeneration; documentBusy.value = name
|
||||
try {
|
||||
const path = await downloadDocument(item.documentId)
|
||||
if (ticket != documentGeneration || !ensureOwner()) { releaseDocumentTemp(path); return }
|
||||
releaseDocumentTemp(item.path); item.path = path; previewingImage = true
|
||||
uni.previewImage({ urls: [path], fail: () => { previewingImage = false; documentExpired(name, item.documentId) } })
|
||||
} catch (error) { if (ticket == documentGeneration && ensureOwner()) documentMessage.value = error instanceof Error ? error.message : '资料读取失败,请重试' }
|
||||
finally { if (ticket == documentGeneration) documentBusy.value = '' }
|
||||
}
|
||||
const removeDocument = async (name: string, id: string) => {
|
||||
if (!ensureOwner() || documentBusy.value.length > 0) return
|
||||
const item = documents.value.find((file) => file.name == name && (id.length == 0 || file.documentId == id))
|
||||
if (item == null) return
|
||||
if (item.documentId.length == 0) { documents.value = documents.value.filter((file) => file.name != name || file.documentId.length > 0); rememberInput(); return }
|
||||
const ticket = documentGeneration; documentBusy.value = name; item.deletePending = true; releaseDocumentTemp(item.path); item.path = ''; rememberInput()
|
||||
try {
|
||||
await deleteDocument(item.documentId)
|
||||
if (ticket != documentGeneration || !ensureOwner()) return
|
||||
documents.value = documents.value.filter((file) => file.documentId != item.documentId); rememberInput(); documentMessage.value = '照片已删除'
|
||||
hydrateDocumentCards()
|
||||
} catch (error) { if (ticket == documentGeneration && ensureOwner()) documentMessage.value = error instanceof Error ? error.message : '删除未完成,请对同一资料重试' }
|
||||
finally { if (ticket == documentGeneration) documentBusy.value = '' }
|
||||
}
|
||||
const documentItemAction = (name: string, id: string) => {
|
||||
if (!ensureOwner() || documentBusy.value.length > 0) return
|
||||
const ticket = documentGeneration; const item = documents.value.find((file) => file.name == name && file.documentId == id)
|
||||
if (item == null) { chooseDocument(name); return }
|
||||
const list = item.deletePending ? ['重试删除'] : (item.documentId.length > 0 ? ['查看照片', '删除照片'] : ['重试上传', '重新选择', '移除未上传照片'])
|
||||
uni.showActionSheet({ itemList: list, success: (result) => {
|
||||
if (ticket != documentGeneration || !ensureOwner()) return
|
||||
if (item.deletePending) removeDocument(name, item.documentId)
|
||||
else if (item.documentId.length > 0) { if (result.tapIndex == 0) previewDocument(name, item.documentId); else removeDocument(name, item.documentId) }
|
||||
else { if (result.tapIndex == 0) uploadSelectedDocument(name); else if (result.tapIndex == 1) chooseDocument(name); else removeDocument(name, item.documentId) }
|
||||
} })
|
||||
}
|
||||
function documentPhotoAction(name: string, offset: number): void {
|
||||
const saved = documents.value.filter((file) => file.name == name && file.documentId.length > 0)
|
||||
const group = saved.slice(offset, offset + 5); const ticket = documentGeneration
|
||||
const list = group.map((file, index): string => '照片 ' + (offset + index + 1).toString() + (file.deletePending ? '(删除未完成)' : ''))
|
||||
const more = offset + group.length < saved.length
|
||||
// 微信操作菜单最多六项,每次保留五张照片和一个翻页入口。
|
||||
if (more || offset > 0) list.push(more ? '更多照片' : '返回第一组')
|
||||
uni.showActionSheet({ itemList: list, success: (result) => {
|
||||
if (ticket != documentGeneration || !ensureOwner() || result.tapIndex < 0 || result.tapIndex >= list.length) return
|
||||
if (result.tapIndex == group.length) { documentPhotoAction(name, more ? offset + group.length : 0); return }
|
||||
documentItemAction(name, group[result.tapIndex].documentId)
|
||||
} })
|
||||
}
|
||||
const documentAction = (name: string) => {
|
||||
if (!ensureOwner()) return
|
||||
const ticket = editGeneration
|
||||
const path = documentPath(name)
|
||||
if (path.length == 0) { chooseDocument(name); return }
|
||||
uni.showActionSheet({ itemList: ['预览图片', '重新选择', '移除资料'], success: (result) => {
|
||||
if (ticket != editGeneration || !ensureOwner()) return
|
||||
if (result.tapIndex == 0) uni.previewImage({ urls: [path], fail: () => { uni.showToast({ title: '本地图片已失效,请重新选择', icon: 'none' }) } })
|
||||
else if (result.tapIndex == 1) chooseDocument(name)
|
||||
else if (result.tapIndex == 2) { documents.value = documents.value.filter((file) => file.name != name); rememberInput() }
|
||||
} })
|
||||
if (!ensureOwner() || documentBusy.value.length > 0) return
|
||||
const item = cardDocument(name)
|
||||
if (item == null) { chooseDocument(name); return }
|
||||
if (item.documentId.length == 0) { documentItemAction(name, ''); return }
|
||||
const saved = documents.value.filter((file) => file.name == name && file.documentId.length > 0)
|
||||
if (saved.length == 1) { documentItemAction(name, item.documentId); return }
|
||||
documentPhotoAction(name, 0)
|
||||
}
|
||||
const validateStep = (): boolean => {
|
||||
let message = ''
|
||||
@@ -226,12 +366,12 @@
|
||||
}
|
||||
if (step.value == 1 && message.length == 0 && assessmentType.value == 'personal') message = contributionValidationMessage(form.value)
|
||||
if (step.value == 2) message = assetValidationMessage(form.value)
|
||||
if (step.value == 3 && documents.value.length > 0 && !documentConsent.value) message = '请阅读并同意资料使用协议,或移除资料'
|
||||
if (step.value == 3 && (documentBusy.value.length > 0 || documents.value.some((item) => item.documentId.length == 0 || item.deletePending))) message = '请先完成上传、删除重试,或移除未上传照片'
|
||||
if (step.value == 3 && authorizations.value.some((item) => item.selected) && !dataConsent.value) message = '请阅读并同意数据授权协议,或取消授权选择'
|
||||
if (message.length == 0) return true
|
||||
uni.showToast({ title: message, icon: 'none' }); return false
|
||||
}
|
||||
const next = () => { if (!ensureOwner() || editLoading.value || editError.value.length > 0) return; if (!validateStep()) return; if (step.value < 3) { step.value++; rememberInput() } else { rememberInput(); goingToConfirm = true; uni.navigateTo({ url: '/packages/finance/confirm/confirm', fail: () => { goingToConfirm = false } }) } }
|
||||
const next = () => { if (!ensureOwner() || editLoading.value || editError.value.length > 0) return; if (!validateStep()) return; if (step.value < 3) { step.value++; rememberInput(); hydrateDocumentCards() } else { rememberInput(); goingToConfirm = true; uni.navigateTo({ url: '/packages/finance/confirm/confirm', fail: () => { goingToConfirm = false } }) } }
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -259,7 +399,7 @@
|
||||
.upload-grid { flex-direction: row; margin-top: 22rpx; }
|
||||
.upload-card { flex: 1; min-width: 0; height: 200rpx; margin-right: 14rpx; border-radius: 16rpx; background-color: #f1f6fd; align-items: center; justify-content: center; }
|
||||
.upload-last { margin-right: 0; }
|
||||
.document-thumb { width: 74rpx; height: 62rpx; border-radius: 8rpx; }
|
||||
.document-thumb { width: 100%; height: 100rpx; border-radius: 10rpx; }
|
||||
.upload-name { font-size: 25rpx; color: #304260; margin-top: 16rpx; }
|
||||
.upload-state { color: #7a889e; font-size: 22rpx; margin-top: 9rpx; }
|
||||
.selected-state { color: #0868f7; }
|
||||
|
||||
Reference in New Issue
Block a user