feat(bx-mobile): show attachment grid picker on reimburse apply

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-08-09 19:46:31 +08:00
1 parent 9f92012e43
commit 0e8cefe89f
1 file changed
+226 -35
@@ -161,37 +161,82 @@
<button type="button" class="btn btn-primary btn-sm" @click="openInvoicePicker">
从发票夹选择
</button>
<div v-if="selectedInvoicePicksSorted.length" class="invoice-picked">
<div v-if="selectedInvoicePicksSorted.length" class="invoice-picked-row">
<div
v-for="pick in selectedInvoicePicksSorted"
:key="pick.key"
class="invoice-picked__item"
class="invoice-picked-card"
>
<span>¥{{ money(pick.amount) }} · {{ pick.fileName }}</span>
<button type="button" class="del" @click="removeInvoicePick(pick.key)">×</button>
<button
type="button"
class="invoice-picked-card__remove"
aria-label="移除"
@click="removeInvoicePick(pick.key)"
>
×
</button>
<div class="invoice-picked-card__preview">
<img
v-if="isImageFileType(pick.fileType) && pick.previewUrl"
:src="pick.previewUrl"
:alt="pick.fileName"
/>
<div v-else class="invoice-picked-card__icon">
{{ isImageFileType(pick.fileType) ? '图片' : 'PDF' }}
</div>
</div>
<div class="invoice-picked-card__meta">
<span class="invoice-picked-card__name">{{ pick.fileName }}</span>
<div class="invoice-picked-card__amount">¥{{ money(pick.amount) }}</div>
</div>
</div>
</div>
<p class="form-tip">请先在「我的发票夹」上传发票;提交时按合计扣减余额。选择对象为附件文件。</p>
</div>
<van-popup v-model:show="showInvoicePicker" position="bottom" round :style="{ height: '70%' }">
<van-popup v-model:show="showInvoicePicker" position="bottom" round :style="{ height: '72%' }">
<div class="invoice-picker">
<div class="invoice-picker__title">选择发票附件</div>
<van-cell-group>
<van-cell
v-for="item in attachItems"
:key="item.key"
clickable
@click="togglePickerKey(item.key)"
<div class="invoice-picker__body">
<p v-if="!pickerMonthGroups.length" class="invoice-picker__empty">暂无发票附件</p>
<section
v-for="group in pickerMonthGroups"
:key="group.monthKey"
class="invoice-month"
>
<template #title>
¥{{ money(item.amount) }} · {{ item.fileName }}
</template>
<template #right-icon>
<span v-if="pickerDraftKeys.includes(item.key)">✓</span>
</template>
</van-cell>
</van-cell-group>
<h4 class="invoice-month__title">{{ group.monthLabel }}</h4>
<div class="invoice-attach-grid">
<button
v-for="item in group.items"
:key="item.key"
type="button"
class="invoice-attach-card"
:class="{ 'is-selected': pickerDraftKeys.includes(item.key) }"
@click="togglePickerKey(item.key)"
>
<span class="invoice-attach-card__index">{{ item.serial }}</span>
<span
v-if="pickerDraftKeys.includes(item.key)"
class="invoice-attach-card__check"
>✓</span>
<div class="invoice-attach-card__preview">
<img
v-if="isImageFileType(item.fileType) && item.previewUrl"
:src="item.previewUrl"
:alt="item.fileName"
/>
<div v-else class="invoice-attach-card__doc">
{{ isImageFileType(item.fileType) ? '图片' : 'PDF' }}
</div>
</div>
<div class="invoice-attach-card__meta">
<span class="invoice-attach-card__name">{{ item.fileName }}</span>
<div class="invoice-attach-card__amount">¥{{ money(item.amount) }}</div>
</div>
</button>
</div>
</section>
</div>
<div class="invoice-picker__footer">
<button type="button" class="btn btn-primary btn-block" @click="confirmInvoicePicker">
确定(已选 {{ pickerDraftKeys.length }})
@@ -569,7 +614,8 @@ function confirmInvoicePicker() {
const set = new Set(pickerDraftKeys.value)
selectedInvoicePicks.value = attachItems.value
.filter((i) => set.has(i.key))
.map((i) => ({ ...i }))
.map((i) => ({ ...i, previewUrl: i.previewUrl }))
kickAttachPreviews(selectedInvoicePicks.value)
showInvoicePicker.value = false
}
@@ -919,26 +965,69 @@ onMounted(async () => {
color: #dc2626;
margin-left: 4px;
}
.invoice-picked {
margin-top: 10px;
.invoice-picked-row {
margin-top: 12px;
display: flex;
flex-direction: column;
gap: 8px;
gap: 10px;
overflow-x: auto;
padding-bottom: 4px;
-webkit-overflow-scrolling: touch;
}
.invoice-picked__item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 10px;
background: #f8fafc;
.invoice-picked-card {
position: relative;
flex: 0 0 120px;
width: 120px;
border: 1px solid var(--border);
border-radius: 8px;
font-size: 13px;
background: #fff;
overflow: hidden;
}
.invoice-picked__item .del {
.invoice-picked-card__remove {
position: absolute;
top: 4px;
right: 4px;
z-index: 2;
width: 22px;
height: 22px;
border: none;
background: transparent;
font-size: 18px;
color: #94a3b8;
border-radius: 50%;
background: rgba(15, 23, 42, 0.55);
color: #fff;
font-size: 14px;
line-height: 1;
}
.invoice-picked-card__preview {
height: 80px;
background: #f1f5f9;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.invoice-picked-card__preview img {
width: 100%;
height: 100%;
object-fit: cover;
}
.invoice-picked-card__icon {
font-size: 12px;
color: var(--text-muted);
}
.invoice-picked-card__meta {
padding: 8px;
}
.invoice-picked-card__name {
display: block;
font-size: 11px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.invoice-picked-card__amount {
margin-top: 4px;
font-size: 12px;
font-weight: 600;
color: var(--primary-dark);
}
.form-tip {
margin-top: 8px;
@@ -956,6 +1045,108 @@ onMounted(async () => {
font-weight: 600;
text-align: center;
}
.invoice-picker__body {
flex: 1;
overflow-y: auto;
padding: 12px;
-webkit-overflow-scrolling: touch;
}
.invoice-picker__empty {
text-align: center;
color: var(--text-muted);
padding: 40px 0;
font-size: 13px;
}
.invoice-month {
margin-bottom: 18px;
}
.invoice-month__title {
margin: 0 0 10px 4px;
font-size: 13px;
font-weight: 600;
}
.invoice-attach-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
.invoice-attach-card {
position: relative;
padding: 0;
border: 1.5px solid var(--border);
border-radius: 8px;
background: #fff;
text-align: left;
overflow: hidden;
}
.invoice-attach-card.is-selected {
border-color: var(--primary);
box-shadow: 0 0 0 1px var(--primary);
}
.invoice-attach-card__index {
position: absolute;
top: 4px;
left: 4px;
z-index: 1;
min-width: 18px;
height: 18px;
padding: 0 4px;
border-radius: 9px;
background: rgba(15, 23, 42, 0.5);
color: #fff;
font-size: 10px;
display: flex;
align-items: center;
justify-content: center;
}
.invoice-attach-card__check {
position: absolute;
top: 4px;
right: 4px;
z-index: 1;
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--primary);
color: #fff;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
}
.invoice-attach-card__preview {
height: 72px;
background: #f1f5f9;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.invoice-attach-card__preview img {
width: 100%;
height: 100%;
object-fit: cover;
}
.invoice-attach-card__doc {
font-size: 11px;
color: var(--text-muted);
}
.invoice-attach-card__meta {
padding: 6px 8px 8px;
}
.invoice-attach-card__name {
display: block;
font-size: 11px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.invoice-attach-card__amount {
margin-top: 2px;
font-size: 12px;
font-weight: 600;
color: var(--primary-dark);
}
.invoice-picker__footer {
margin-top: auto;
padding: 12px 16px;