|
@@ -161,37 +161,82 @@
|
|
|
<button type="button" class="btn btn-primary btn-sm" @click="openInvoicePicker">
|
|
<button type="button" class="btn btn-primary btn-sm" @click="openInvoicePicker">
|
|
|
从发票夹选择
|
|
从发票夹选择
|
|
|
</button>
|
|
</button>
|
|
|
- <div v-if="selectedInvoicePicksSorted.length" class="invoice-picked">
|
|
|
|
|
|
|
+ <div v-if="selectedInvoicePicksSorted.length" class="invoice-picked-row">
|
|
|
<div
|
|
<div
|
|
|
v-for="pick in selectedInvoicePicksSorted"
|
|
v-for="pick in selectedInvoicePicksSorted"
|
|
|
:key="pick.key"
|
|
: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>
|
|
|
</div>
|
|
</div>
|
|
|
<p class="form-tip">请先在「我的发票夹」上传发票;提交时按合计扣减余额。选择对象为附件文件。</p>
|
|
<p class="form-tip">请先在「我的发票夹」上传发票;提交时按合计扣减余额。选择对象为附件文件。</p>
|
|
|
</div>
|
|
</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">
|
|
|
<div class="invoice-picker__title">选择发票附件</div>
|
|
<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">
|
|
<div class="invoice-picker__footer">
|
|
|
<button type="button" class="btn btn-primary btn-block" @click="confirmInvoicePicker">
|
|
<button type="button" class="btn btn-primary btn-block" @click="confirmInvoicePicker">
|
|
|
确定(已选 {{ pickerDraftKeys.length }})
|
|
确定(已选 {{ pickerDraftKeys.length }})
|
|
@@ -569,7 +614,8 @@ function confirmInvoicePicker() {
|
|
|
const set = new Set(pickerDraftKeys.value)
|
|
const set = new Set(pickerDraftKeys.value)
|
|
|
selectedInvoicePicks.value = attachItems.value
|
|
selectedInvoicePicks.value = attachItems.value
|
|
|
.filter((i) => set.has(i.key))
|
|
.filter((i) => set.has(i.key))
|
|
|
- .map((i) => ({ ...i }))
|
|
|
|
|
|
|
+ .map((i) => ({ ...i, previewUrl: i.previewUrl }))
|
|
|
|
|
+ kickAttachPreviews(selectedInvoicePicks.value)
|
|
|
showInvoicePicker.value = false
|
|
showInvoicePicker.value = false
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -919,26 +965,69 @@ onMounted(async () => {
|
|
|
color: #dc2626;
|
|
color: #dc2626;
|
|
|
margin-left: 4px;
|
|
margin-left: 4px;
|
|
|
}
|
|
}
|
|
|
-.invoice-picked {
|
|
|
|
|
- margin-top: 10px;
|
|
|
|
|
|
|
+.invoice-picked-row {
|
|
|
|
|
+ margin-top: 12px;
|
|
|
display: flex;
|
|
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;
|
|
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;
|
|
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 {
|
|
.form-tip {
|
|
|
margin-top: 8px;
|
|
margin-top: 8px;
|
|
@@ -956,6 +1045,108 @@ onMounted(async () => {
|
|
|
font-weight: 600;
|
|
font-weight: 600;
|
|
|
text-align: center;
|
|
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 {
|
|
.invoice-picker__footer {
|
|
|
margin-top: auto;
|
|
margin-top: auto;
|
|
|
padding: 12px 16px;
|
|
padding: 12px 16px;
|