feat(bx-mobile): add optional apply-attachment uploader on reimburse apply
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
6adb3cee9d
commit
8d14909165
1 file changed
+113
@@ -262,6 +262,68 @@
|
||||
</div>
|
||||
</van-popup>
|
||||
|
||||
<!-- 申请附件(可选) -->
|
||||
<div class="form-card">
|
||||
<div class="form-header">
|
||||
<h3>申请附件(可选)</h3>
|
||||
</div>
|
||||
<p class="form-tip">补充行程单、说明截图、合同等;与发票夹无关。</p>
|
||||
<div class="uploader-slot-btn advance-attachment-upload">
|
||||
<van-uploader
|
||||
:after-read="onAfterReadApplyAttachment"
|
||||
:before-read="beforeReadApplyAttachment"
|
||||
:disabled="applyAttachment.uploading.value"
|
||||
multiple
|
||||
result-type="file"
|
||||
:accept="attachmentAccept"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
:class="{ 'btn-loading': applyAttachment.uploading.value }"
|
||||
:disabled="applyAttachment.uploading.value"
|
||||
>
|
||||
{{ applyAttachment.uploading.value ? '上传中…' : '上传附件' }}
|
||||
</button>
|
||||
</van-uploader>
|
||||
</div>
|
||||
<div v-if="applyAttachment.files.value.length" class="att-grid advance-att-grid">
|
||||
<div
|
||||
v-for="(file, idx) in applyAttachment.files.value"
|
||||
:key="file.id"
|
||||
class="att-img advance-att-tile"
|
||||
:class="{ 'is-uploading': file._pending }"
|
||||
:title="file.fileName"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="applyAttachment.open(file)"
|
||||
@keydown.enter.prevent="applyAttachment.open(file)"
|
||||
>
|
||||
<img
|
||||
v-if="applyAttachment.isImage(file.fileType) && file.previewUrl"
|
||||
:src="file.previewUrl"
|
||||
:alt="file.fileName"
|
||||
/>
|
||||
<div v-else class="att-file-icon att-doc-thumb">
|
||||
<span class="att-doc-thumb__ext">{{ applyAttachment.fileExt(file.fileName) }}</span>
|
||||
<span class="att-doc-thumb__name">{{ file.fileName }}</span>
|
||||
</div>
|
||||
<div v-if="file._pending" class="att-upload-mask" aria-label="上传中">
|
||||
<van-loading size="22px" color="#fff" />
|
||||
<span class="att-upload-mask__text">上传中</span>
|
||||
</div>
|
||||
<button
|
||||
v-if="!file._pending"
|
||||
type="button"
|
||||
class="del"
|
||||
@click.stop="applyAttachment.remove(idx)"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 报销说明 -->
|
||||
<div class="form-card">
|
||||
<div class="form-header">
|
||||
@@ -364,10 +426,39 @@ import {
|
||||
type ExpenseItem,
|
||||
} from '@/hooks/useExpenseItemTree'
|
||||
import { useApplicantOptions } from '@/hooks/useApplicantOptions'
|
||||
import { useAttachment } from '@/hooks/useAttachment'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { SUBMIT_APPROVE_BUTTON_COLOR } from '@/constants/dialog'
|
||||
import BottomActionBar from '@/components/mobile/BottomActionBar.vue'
|
||||
|
||||
const MAX_ATTACHMENTS = 10
|
||||
|
||||
/** 与 Web BxAttachmentUpload 对齐:图片/视频/文档/压缩包 */
|
||||
const attachmentAccept = [
|
||||
'image/*',
|
||||
'video/*',
|
||||
'application/pdf',
|
||||
'application/msword',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'application/vnd.ms-excel',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'application/vnd.ms-powerpoint',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'text/plain',
|
||||
'text/csv',
|
||||
'application/rtf',
|
||||
'application/zip',
|
||||
'application/x-zip-compressed',
|
||||
'application/x-rar-compressed',
|
||||
'application/x-7z-compressed',
|
||||
'.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
|
||||
'.txt', '.csv', '.rtf', '.wps', '.et', '.dps',
|
||||
'.zip', '.rar', '.7z',
|
||||
'.mp4', '.webm', '.mov', '.avi',
|
||||
].join(',')
|
||||
|
||||
const applyAttachment = useAttachment()
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const auth = useAuthStore()
|
||||
@@ -660,6 +751,28 @@ function removeInvoicePick(key: string) {
|
||||
selectedInvoicePicks.value = selectedInvoicePicks.value.filter((p) => p.key !== key)
|
||||
}
|
||||
|
||||
function beforeReadApplyAttachment(file: File | File[]) {
|
||||
const batch = Array.isArray(file) ? file : [file]
|
||||
const remain = MAX_ATTACHMENTS - applyAttachment.files.value.length
|
||||
if (remain <= 0) {
|
||||
showToast(`最多上传 ${MAX_ATTACHMENTS} 个附件`)
|
||||
return false
|
||||
}
|
||||
if (batch.length > remain) {
|
||||
showToast(`还可上传 ${remain} 个附件,超出部分已忽略`)
|
||||
return Promise.resolve(batch.slice(0, remain))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async function onAfterReadApplyAttachment(uploadFile: any) {
|
||||
const files = Array.isArray(uploadFile) ? uploadFile : [uploadFile]
|
||||
for (const f of files) {
|
||||
if (!f?.file) continue
|
||||
await applyAttachment.upload(f.file)
|
||||
}
|
||||
}
|
||||
|
||||
function lineExpenseDisplay(id: number | string): string {
|
||||
if (id === '' || id == null) return ''
|
||||
return formatExpenseName(id, flatList.value)
|
||||
|
||||
Reference in new issue
Block a user