575 lines
21 KiB
Plaintext
575 lines
21 KiB
Plaintext
<%@ Page Title="出納核對匯款人" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" EnableEventValidation="false" CodeFile="verify.aspx.cs" Inherits="admin_transfer_verify" %>
|
||
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" runat="Server">
|
||
</asp:Content>
|
||
<asp:Content ID="Content3" ContentPlaceHolderID="page_nav" runat="Server">
|
||
<h5 class="mb-0">出納 - 核對匯款人</h5>
|
||
</asp:Content>
|
||
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
|
||
<div id="verify-app">
|
||
<v-app>
|
||
<v-container v-if="step === 1">
|
||
<v-card>
|
||
<v-card-title class="bg-success white--text text-center">
|
||
<h5 class="mb-0">出納核對匯款人(程序1)</h5>
|
||
<v-spacer></v-spacer>
|
||
<v-btn color="primary" @click="saveAndGoStep2">確認</v-btn>
|
||
</v-card-title>
|
||
<v-card-text>
|
||
<v-data-table
|
||
:headers="headers"
|
||
:items="items"
|
||
:loading="loading"
|
||
loading-text="載入中..."
|
||
class="elevation-1"
|
||
item-key="id"
|
||
>
|
||
<template v-slot:item.info="{ item }">
|
||
<div>
|
||
<div><span class="text-muted">姓名:</span>{{ item.name }}</div>
|
||
<div><span class="text-muted">電話:</span>{{ item.phone }}</div>
|
||
<div><span class="text-muted">法會:</span>{{ getActivityName(item.activity_num) }}</div>
|
||
</div>
|
||
</template>
|
||
<template v-slot:item.f_num="{ item }">
|
||
<span v-if="item.f_num && item.follower">
|
||
<a
|
||
:href="'/admin/follower/reg.aspx?num=' + item.follower.num"
|
||
class="text-success"
|
||
target="_blank"
|
||
>
|
||
{{ item.follower.u_name }}(F{{ item.f_num }})
|
||
</a>
|
||
<div class="small text-muted">
|
||
電話:{{ item.follower.phone }}<br>
|
||
手機:{{ item.follower.cellphone }}
|
||
</div>
|
||
</span>
|
||
<span v-else class="text-danger">未自動比對</span>
|
||
</template>
|
||
|
||
<template v-slot:item.actions="{ item }">
|
||
<v-btn small outlined color="primary" @click="openFollowerDialog(item)">選擇信眾</v-btn>
|
||
</template>
|
||
<template v-slot:item.status="{ item }">
|
||
<div class="d-flex align-center">
|
||
<v-select
|
||
:items="statusOptions"
|
||
v-model="item.status"
|
||
item-text="text"
|
||
item-value="value"
|
||
dense
|
||
outlined
|
||
hide-details
|
||
class="mr-2"
|
||
style="width: 110px;max-width: 110px;"
|
||
:menu-props="{ contentClass: 'mini-dropdown', maxHeight: 200 }"
|
||
></v-select>
|
||
<v-text-field
|
||
v-model="item.verify_note"
|
||
dense
|
||
outlined
|
||
hide-details
|
||
style="width: 200px"
|
||
placeholder="核對記錄"
|
||
></v-text-field>
|
||
</div>
|
||
</template>
|
||
</v-data-table>
|
||
</v-card-text>
|
||
</v-card>
|
||
</v-container>
|
||
|
||
<!-- 階段2區塊,預設隱藏 -->
|
||
<v-container v-if="step === 2">
|
||
<v-card>
|
||
<v-card-title class="bg-info white--text text-center">
|
||
<h5 class="mb-0">出納核對金額(程序2)</h5>
|
||
<v-spacer></v-spacer>
|
||
<v-btn color="primary" @click="submitStep2">送出</v-btn>
|
||
</v-card-title>
|
||
<v-card-text>
|
||
<v-data-table
|
||
:headers="step2Headers"
|
||
:items="items"
|
||
class="elevation-1 step2-table"
|
||
item-key="id"
|
||
>
|
||
<template v-slot:item.info="{ item }">
|
||
<div>
|
||
<div><span class="text-muted">法會:</span>{{ getActivityName(item.activity_num) }}</div>
|
||
<div><span class="text-muted">姓名:</span>{{ item.follower ? item.follower.u_name : '' }}</div>
|
||
<div><span class="text-muted">電話:</span>{{ item.follower ? (item.follower.phone || item.follower.cellphone) : '' }}</div>
|
||
</div>
|
||
</template>
|
||
<template v-slot:item.note="{ item }">
|
||
<div>
|
||
{{ item.note }}
|
||
<div v-if="item.proof_img">
|
||
<a :href="'../../upload/transfer_proof/' + item.proof_img" target="_blank">查看相片</a>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<template v-slot:item.acc_num="{ item }">
|
||
<div>
|
||
<div class="mb-2">
|
||
<span class="badge bg-primary me-1" title="支付方式">{{ payTypeText[item.pay_type] || item.pay_type }}</span>
|
||
<span class="badge bg-secondary" title="型態:個人/共同">{{ item.pay_mode }}</span>
|
||
<span class="fw-bold text-primary" title="帳號後5碼">{{ item.account_last5 }}</span>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<v-select
|
||
:items="bankOptions"
|
||
v-model="item.acc_num"
|
||
dense
|
||
outlined
|
||
hide-details
|
||
style="max-width: 200px"
|
||
></v-select>
|
||
</div>
|
||
</template>
|
||
<template v-slot:item.check_date="{ item }">
|
||
<div class="mb-2">
|
||
<span class="text-muted small">登記日期:</span>
|
||
<span
|
||
class="fw-bold text-danger"
|
||
style="cursor:pointer"
|
||
@click="$set(item, 'check_date', item.create_time ? item.create_time.split('T')[0] : '')"
|
||
title="點擊帶入日期"
|
||
>
|
||
{{ item.create_time | date }}
|
||
</span>
|
||
</div>
|
||
<div class="mb-2">
|
||
<v-text-field
|
||
v-model="item.check_date"
|
||
type="date"
|
||
dense
|
||
outlined
|
||
hide-details
|
||
style="max-width: 140px"
|
||
></v-text-field>
|
||
</div>
|
||
</template>
|
||
<template v-slot:item.check_amount="{ item }">
|
||
<div class="mb-2">
|
||
<span
|
||
class="text-muted small"
|
||
>金額:</span>
|
||
<span
|
||
class="fw-bold text-danger"
|
||
style="cursor:pointer"
|
||
@click="$set(item, 'check_amount', item.amount)"
|
||
title="點擊帶入金額"
|
||
>
|
||
{{ item.amount | currency }}
|
||
</span>
|
||
</div>
|
||
<div>
|
||
<v-text-field
|
||
v-model="item.check_amount"
|
||
type="number"
|
||
dense
|
||
outlined
|
||
hide-details
|
||
style="max-width: 100px"
|
||
></v-text-field>
|
||
</div>
|
||
</template>
|
||
<template v-slot:item.check_memo="{ item }">
|
||
<div class="d-flex align-center my-2" style="min-width: 300px">
|
||
<v-text-field
|
||
v-model="item.check_memo"
|
||
dense
|
||
outlined
|
||
hide-details
|
||
class="mr-2"
|
||
style="width: 180px"
|
||
placeholder="帳簿備註"
|
||
></v-text-field>
|
||
<v-select
|
||
:items="checkStatusOptions"
|
||
v-model="item.check_status"
|
||
item-text="text"
|
||
item-value="value"
|
||
dense
|
||
outlined
|
||
hide-details
|
||
style="width: 110px"
|
||
:menu-props="{ contentClass: 'mini-dropdown', maxHeight: 200 }"
|
||
></v-select>
|
||
</div>
|
||
<div class="my-2">
|
||
<v-text-field
|
||
v-model="item.verify_note"
|
||
dense
|
||
outlined
|
||
hide-details
|
||
style="max-width: 310px"
|
||
placeholder="核對記錄"
|
||
></v-text-field>
|
||
</div>
|
||
|
||
</template>
|
||
</v-data-table>
|
||
</v-card-text>
|
||
</v-card>
|
||
</v-container>
|
||
|
||
<!-- 信眾選擇對話框 -->
|
||
<v-dialog v-model="follower_dialog.show" max-width="800px">
|
||
<v-card>
|
||
<v-card-title class="grey lighten-2">
|
||
選擇信眾
|
||
<v-spacer></v-spacer>
|
||
<v-btn icon @click="follower_dialog.show = false">
|
||
<v-icon>mdi-close</v-icon>
|
||
</v-btn>
|
||
</v-card-title>
|
||
|
||
<v-card-text>
|
||
<v-row>
|
||
<v-col cols="12">
|
||
<v-text-field
|
||
v-model="follower_dialog.search"
|
||
label="搜尋信眾"
|
||
prepend-icon="mdi-magnify"
|
||
@keyup.enter="searchFollowers"
|
||
clearable
|
||
></v-text-field>
|
||
</v-col>
|
||
</v-row>
|
||
|
||
<v-data-table
|
||
:headers="follower_dialog.headers"
|
||
:items="follower_dialog.items"
|
||
:loading="follower_dialog.loading"
|
||
:search="follower_dialog.search"
|
||
item-key="num"
|
||
class="elevation-1"
|
||
@click:row="selectFollower"
|
||
>
|
||
<template v-slot:item.actions="{ item }">
|
||
<v-btn small color="primary" @click="selectFollower(item)">
|
||
選擇
|
||
</v-btn>
|
||
</template>
|
||
</v-data-table>
|
||
</v-card-text>
|
||
</v-card>
|
||
</v-dialog>
|
||
</v-app>
|
||
</div>
|
||
|
||
|
||
</asp:Content>
|
||
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" runat="Server">
|
||
<script>
|
||
function openFollowerModal(rowIdx) {
|
||
// 這裡可記錄目前要選擇哪一列
|
||
window.currentRowIdx = rowIdx;
|
||
var modal = new bootstrap.Modal(document.getElementById('followerModal'));
|
||
modal.show();
|
||
}
|
||
function selectFollower(fNum, name, rowIdx) {
|
||
// 這裡可將選擇結果寫回對應列(需配合實際資料結構)
|
||
var table = document.querySelector('table.table-bordered tbody');
|
||
var row = table.children[rowIdx-1];
|
||
var cell = row.children[1];
|
||
cell.innerHTML = '<a href="#" class="link-success">' + name + '(' + fNum + ')</a>' +
|
||
'<button type="button" class="btn btn-sm btn-outline-secondary ms-2" onclick="openFollowerModal(' + rowIdx + ')">選擇信眾</button>';
|
||
var modal = bootstrap.Modal.getInstance(document.getElementById('followerModal'));
|
||
modal.hide();
|
||
}
|
||
function showStep2() {
|
||
document.getElementById('verify-step1').style.display = 'none';
|
||
document.getElementById('verify-step2').style.display = '';
|
||
}
|
||
</script>
|
||
<script>
|
||
new Vue({
|
||
el: '#verify-app',
|
||
vuetify: new Vuetify(),
|
||
data() {
|
||
return {
|
||
step: 1,
|
||
headers: [
|
||
{ text: '匯款人資訊', value: 'info' },
|
||
{ text: '對應信眾', value: 'f_num' },
|
||
{ text: '選擇信眾', value: 'actions', sortable: false },
|
||
{ text: '狀態 | 核對記錄', value: 'status' },0
|
||
],
|
||
items: [],
|
||
activities: [],
|
||
loading: false,
|
||
statusOptions: [
|
||
{ text: '', value: '' },
|
||
{ text: '待確認', value: '1' },
|
||
{ text: '確認', value: '2' },
|
||
{ text: '作廢', value: '3' }
|
||
],
|
||
checkStatusOptions: [
|
||
{ text: '', value: '' },
|
||
{ text: '未核對', value: '1' },
|
||
{ text: '核對', value: '2' },
|
||
{ text: '金額不符', value: '3' },
|
||
{ text: '其他問題', value: '4' },
|
||
{ text: '作廢', value: '5' }
|
||
],
|
||
follower_dialog: {
|
||
show: false,
|
||
loading: false,
|
||
search: '',
|
||
headers: [
|
||
{ text: '編號', value: 'num' },
|
||
{ text: '姓名', value: 'u_name' },
|
||
{ text: '地址', value: 'address' }
|
||
],
|
||
items: [],
|
||
selected: null,
|
||
current_item: null
|
||
},
|
||
step2Headers: [
|
||
{ text: '匯款人資訊', value: 'info' },
|
||
{ text: '匯款備註/相片', value: 'note' },
|
||
{ text: '入帳銀行/帳戶 | 支付資訊/帳號後5碼', value: 'acc_num' },
|
||
{ text: '入帳日期', value: 'check_date' },
|
||
{ text: '入帳金額', value: 'check_amount' },
|
||
{ text: '備註/狀態 | 核對記錄', value: 'check_memo' }
|
||
],
|
||
bankOptions: [],
|
||
payTypeText: {
|
||
1: '現金',
|
||
2: '匯款',
|
||
3: '支票'
|
||
},
|
||
};
|
||
},
|
||
filters: {
|
||
currency(val) {
|
||
if (!val) return '';
|
||
return Number(val).toLocaleString();
|
||
},
|
||
date(val) {
|
||
if (!val) return '';
|
||
return val.split('T')[0];
|
||
}
|
||
},
|
||
methods: {
|
||
getActivityName(num) {
|
||
const act = this.activities.find(a => a.num === num);
|
||
return act ? act.subject : '';
|
||
},
|
||
openFollowerDialog(item) {
|
||
this.follower_dialog.current_item = item;
|
||
this.follower_dialog.show = true;
|
||
this.searchFollowers();
|
||
},
|
||
async searchFollowers() {
|
||
this.follower_dialog.loading = true;
|
||
try {
|
||
const response = await axios.post(HTTP_HOST + 'api/follower/GetList', {
|
||
f_number: this.follower_dialog.search,
|
||
u_name: this.follower_dialog.search
|
||
}, {
|
||
params: {
|
||
page: 1,
|
||
pageSize: 10
|
||
}
|
||
});
|
||
this.follower_dialog.items = response.data.list;
|
||
} catch (error) {
|
||
console.error('Error fetching followers:', error);
|
||
this.snackbar.text = "查詢信眾失敗";
|
||
this.snackbar.show = true;
|
||
} finally {
|
||
this.follower_dialog.loading = false;
|
||
}
|
||
},
|
||
selectFollower(follower) {
|
||
if (this.follower_dialog.current_item) {
|
||
// 更新當前項目的 f_num
|
||
this.follower_dialog.current_item.f_num = follower.num;
|
||
// 更新 follower 物件
|
||
this.follower_dialog.current_item.follower = {
|
||
num: follower.num,
|
||
u_name: follower.u_name,
|
||
address: follower.address
|
||
};
|
||
}
|
||
this.follower_dialog.show = false;
|
||
},
|
||
async loadData() {
|
||
this.loading = true;
|
||
// 取得活動清單
|
||
const actRes = await axios.get('../../api/activity');
|
||
this.activities = actRes.data;
|
||
// 依 step 取得不同資料
|
||
let res;
|
||
if (this.step === 1) {
|
||
res = await axios.get('../../api/transfer_register/pending');
|
||
} else if (this.step === 2) {
|
||
res = await axios.get('../../api/transfer_register/step2_list');
|
||
}
|
||
this.items = res.data.map(item => ({
|
||
...item,
|
||
status: item.status ? String(item.status) : '',
|
||
check_status: item.check_status ? String(item.check_status) : ''
|
||
}));
|
||
// 取得銀行帳戶清單
|
||
const bankRes = await axios.post('../../api/accounting/GetAccountKindList', {}, { params: { page: 1, pageSize: 1000 } });
|
||
this.bankOptions = bankRes.data.list.map(x => ({
|
||
text: x.kind + (x.bank_name ? ' - ' + x.bank_name : '') + (x.bank_id ? ' (' + x.bank_id + ')' : ''),
|
||
value: x.num
|
||
}));
|
||
this.loading = false;
|
||
},
|
||
async saveAndGoStep2() {
|
||
// 組出要更新的資料
|
||
const updateList = this.items.map(item => ({
|
||
id: item.id,
|
||
f_num: item.f_num,
|
||
status: item.status ? String(item.status) : '',
|
||
check_status: item.check_status ? String(item.check_status) : '',
|
||
verify_note: item.verify_note,
|
||
acc_num: item.acc_num,
|
||
check_date: item.check_date,
|
||
check_amount: item.check_amount,
|
||
check_memo: item.check_memo
|
||
}));
|
||
try {
|
||
const res = await axios.post('../../api/transfer_register/batch_update', updateList);
|
||
// 檢查回傳格式
|
||
if (res.data && res.data.success) {
|
||
this.step = 2;
|
||
await this.loadData();
|
||
} else {
|
||
alert('儲存失敗,請重試!!');
|
||
}
|
||
} catch (e) {
|
||
alert('儲存失敗,請重試:' + e.message);
|
||
}
|
||
},
|
||
async submitStep2() {
|
||
// 組出要更新的資料
|
||
const updateList = this.items.map(item => ({
|
||
id: item.id,
|
||
f_num: item.f_num,
|
||
status: item.status ? String(item.status) : '',
|
||
check_status: item.check_status ? String(item.check_status) : '',
|
||
verify_note: item.verify_note,
|
||
acc_num: item.acc_num,
|
||
check_date: item.check_date,
|
||
check_amount: item.check_amount,
|
||
check_memo: item.check_memo
|
||
}));
|
||
try {
|
||
const res = await axios.post('../../api/transfer_register/batch_update', updateList);
|
||
if (res.data && res.data.success) {
|
||
alert('送出成功!');
|
||
} else {
|
||
alert('送出失敗,請重試 :' + res.data.message);
|
||
}
|
||
} catch (e) {
|
||
alert('送出失敗,請再試一次!');
|
||
}
|
||
}
|
||
},
|
||
created() {
|
||
this.loadData();
|
||
}
|
||
});
|
||
/*
|
||
fix:
|
||
step 2 submit: f_num don't clear
|
||
*/
|
||
</script>
|
||
<style>
|
||
.mini-dropdown {
|
||
min-width: 110px !important;
|
||
font-size: 0.95rem !important;
|
||
padding-top: 0 !important;
|
||
padding-bottom: 0 !important;
|
||
}
|
||
.mini-dropdown .v-list-item {
|
||
min-height: 32px !important;
|
||
padding-top: 0 !important;
|
||
padding-bottom: 0 !important;
|
||
}
|
||
/* 統一所有 dense 輸入元件高度 */
|
||
.v-input.v-input--dense {
|
||
min-height: 32px !important;
|
||
height: 32px !important;
|
||
font-size: 0.95rem !important;
|
||
}
|
||
.v-input.v-input--dense .v-input__slot,
|
||
.v-input.v-input--dense .v-select__slot,
|
||
.v-input.v-input--dense .v-select__selections,
|
||
.v-input.v-input--dense .v-text-field__slot {
|
||
min-height: 32px !important;
|
||
height: 32px !important;
|
||
line-height: 32px !important;
|
||
font-size: 0.95rem !important;
|
||
padding-top: 0 !important;
|
||
padding-bottom: 0 !important;
|
||
}
|
||
|
||
/* 強制覆蓋所有 Vuetify 權重,讓下拉箭頭垂直置中 */
|
||
.v-input.v-input--dense .v-input__append-inner,
|
||
.v-text-field.v-input--dense .v-input__append-inner,
|
||
.v-input__append-inner[style] {
|
||
margin-top: 0 !important;
|
||
margin-bottom: 0 !important;
|
||
align-items: center !important;
|
||
display: flex !important;
|
||
height: 32px !important;
|
||
}
|
||
.v-input.v-input--dense .v-icon {
|
||
line-height: 32px !important;
|
||
font-size: 20px !important;
|
||
}
|
||
|
||
/* 讓 v-text-field 內的 input 也置中 */
|
||
.v-input.v-input--dense input {
|
||
height: 32px !important;
|
||
line-height: 32px !important;
|
||
font-size: 0.95rem !important;
|
||
padding-top: 0 !important;
|
||
padding-bottom: 0 !important;
|
||
}
|
||
|
||
/* ===== 新增:step2第一欄寬度加大 ===== */
|
||
.v-data-table td:first-child, .v-data-table th:first-child {
|
||
min-width: 220px !important;
|
||
width: 220px !important;
|
||
max-width: 300px !important;
|
||
white-space: normal !important;
|
||
}
|
||
|
||
/* ===== 新增:隱藏 number 欄位上下箭頭 ===== */
|
||
.v-input input[type=number]::-webkit-inner-spin-button,
|
||
.v-input input[type=number]::-webkit-outer-spin-button {
|
||
-webkit-appearance: none;
|
||
margin: 0;
|
||
}
|
||
.v-input input[type=number] {
|
||
-moz-appearance: textfield;
|
||
}
|
||
.step2-table tbody td{
|
||
height:90px !important;
|
||
min-height:90px !important;
|
||
}
|
||
|
||
/* 針對所有 v-select 內容垂直置中 */
|
||
.v-select__selection {
|
||
align-items: center !important;
|
||
display: flex !important;
|
||
height: 32px !important; /* 跟 input 高度一致 */
|
||
line-height: 32px !important;
|
||
padding-top: 0 !important;
|
||
padding-bottom: 0 !important;
|
||
margin-top: 0 !important;
|
||
}
|
||
</style>
|
||
</asp:Content>
|