mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-20 01:34:24 +08:00
support batch replace category / account / tag in import transaction dialog
This commit is contained in:
+5
-1
@@ -1509,7 +1509,11 @@
|
|||||||
"No data to import": "No data to import",
|
"No data to import": "No data to import",
|
||||||
"Cannot import invalid transactions": "Cannot import invalid transactions",
|
"Cannot import invalid transactions": "Cannot import invalid transactions",
|
||||||
"Unable to parse import file": "Unable to parse import file",
|
"Unable to parse import file": "Unable to parse import file",
|
||||||
"Batch Replace": "Batch Replace",
|
"Batch Replace Selected Expense Categories": "Batch Replace Selected Expense Categories",
|
||||||
|
"Batch Replace Selected Income Categories": "Batch Replace Selected Income Categories",
|
||||||
|
"Batch Replace Selected Transfer Categories": "Batch Replace Selected Transfer Categories",
|
||||||
|
"Batch Replace Selected Accounts": "Batch Replace Selected Accounts",
|
||||||
|
"Batch Replace Selected Destination Accounts": "Batch Replace Selected Destination Accounts",
|
||||||
"Replace Invalid Expense Categories": "Replace Invalid Expense Categories",
|
"Replace Invalid Expense Categories": "Replace Invalid Expense Categories",
|
||||||
"Replace Invalid Income Categories": "Replace Invalid Income Categories",
|
"Replace Invalid Income Categories": "Replace Invalid Income Categories",
|
||||||
"Replace Invalid Transfer Categories": "Replace Invalid Transfer Categories",
|
"Replace Invalid Transfer Categories": "Replace Invalid Transfer Categories",
|
||||||
|
|||||||
@@ -1509,7 +1509,11 @@
|
|||||||
"No data to import": "没有可以导入的数据",
|
"No data to import": "没有可以导入的数据",
|
||||||
"Cannot import invalid transactions": "不能导入无效的交易",
|
"Cannot import invalid transactions": "不能导入无效的交易",
|
||||||
"Unable to parse import file": "无法解析导入的文件",
|
"Unable to parse import file": "无法解析导入的文件",
|
||||||
"Batch Replace": "批量替换",
|
"Batch Replace Selected Expense Categories": "批量替换选中的支出分类",
|
||||||
|
"Batch Replace Selected Income Categories": "批量替换选中的收入分类",
|
||||||
|
"Batch Replace Selected Transfer Categories": "批量替换选中的转账分类",
|
||||||
|
"Batch Replace Selected Accounts": "批量替换选中的账户",
|
||||||
|
"Batch Replace Selected Destination Accounts": "批量替换选中的目标账户",
|
||||||
"Replace Invalid Expense Categories": "替换无效的支出分类",
|
"Replace Invalid Expense Categories": "替换无效的支出分类",
|
||||||
"Replace Invalid Income Categories": "替换无效的收入分类",
|
"Replace Invalid Income Categories": "替换无效的收入分类",
|
||||||
"Replace Invalid Transfer Categories": "替换无效的转账分类",
|
"Replace Invalid Transfer Categories": "替换无效的转账分类",
|
||||||
|
|||||||
+34
-15
@@ -3,16 +3,21 @@
|
|||||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="d-flex align-center justify-center">
|
<div class="d-flex align-center justify-center">
|
||||||
<h4 class="text-h4" v-if="type === 'expenseCategory'">{{ $t('Replace Invalid Expense Categories') }}</h4>
|
<h4 class="text-h4" v-if="mode === 'batchReplace' && type === 'expenseCategory'">{{ $t('Batch Replace Selected Expense Categories') }}</h4>
|
||||||
<h4 class="text-h4" v-if="type === 'incomeCategory'">{{ $t('Replace Invalid Income Categories') }}</h4>
|
<h4 class="text-h4" v-if="mode === 'batchReplace' && type === 'incomeCategory'">{{ $t('Batch Replace Selected Income Categories') }}</h4>
|
||||||
<h4 class="text-h4" v-if="type === 'transferCategory'">{{ $t('Replace Invalid Transfer Categories') }}</h4>
|
<h4 class="text-h4" v-if="mode === 'batchReplace' && type === 'transferCategory'">{{ $t('Batch Replace Selected Transfer Categories') }}</h4>
|
||||||
<h4 class="text-h4" v-if="type === 'account'">{{ $t('Replace Invalid Accounts') }}</h4>
|
<h4 class="text-h4" v-if="mode === 'batchReplace' && type === 'account'">{{ $t('Batch Replace Selected Accounts') }}</h4>
|
||||||
<h4 class="text-h4" v-if="type === 'tag'">{{ $t('Replace Invalid Transaction Tags') }}</h4>
|
<h4 class="text-h4" v-if="mode === 'batchReplace' && type === 'destinationAccount'">{{ $t('Batch Replace Selected Destination Accounts') }}</h4>
|
||||||
|
<h4 class="text-h4" v-if="mode === 'replaceInvalidItems' && type === 'expenseCategory'">{{ $t('Replace Invalid Expense Categories') }}</h4>
|
||||||
|
<h4 class="text-h4" v-if="mode === 'replaceInvalidItems' && type === 'incomeCategory'">{{ $t('Replace Invalid Income Categories') }}</h4>
|
||||||
|
<h4 class="text-h4" v-if="mode === 'replaceInvalidItems' && type === 'transferCategory'">{{ $t('Replace Invalid Transfer Categories') }}</h4>
|
||||||
|
<h4 class="text-h4" v-if="mode === 'replaceInvalidItems' && type === 'account'">{{ $t('Replace Invalid Accounts') }}</h4>
|
||||||
|
<h4 class="text-h4" v-if="mode === 'replaceInvalidItems' && type === 'tag'">{{ $t('Replace Invalid Transaction Tags') }}</h4>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<v-card-text class="my-md-4 w-100 d-flex justify-center" v-if="type === 'expenseCategory' || type === 'incomeCategory' || type === 'transferCategory'">
|
<v-card-text class="my-md-4 w-100 d-flex justify-center" v-if="type === 'expenseCategory' || type === 'incomeCategory' || type === 'transferCategory'">
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12">
|
<v-col cols="12" v-if="mode === 'replaceInvalidItems'">
|
||||||
<v-autocomplete
|
<v-autocomplete
|
||||||
item-title="name"
|
item-title="name"
|
||||||
item-value="value"
|
item-value="value"
|
||||||
@@ -76,9 +81,9 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-card-text class="my-md-4 w-100 d-flex justify-center" v-if="type === 'account'">
|
<v-card-text class="my-md-4 w-100 d-flex justify-center" v-if="type === 'account' || type === 'destinationAccount'">
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12">
|
<v-col cols="12" v-if="mode === 'replaceInvalidItems'">
|
||||||
<v-autocomplete
|
<v-autocomplete
|
||||||
item-title="name"
|
item-title="name"
|
||||||
item-value="value"
|
item-value="value"
|
||||||
@@ -111,7 +116,7 @@
|
|||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-card-text class="my-md-4 w-100 d-flex justify-center" v-if="type === 'tag'">
|
<v-card-text class="my-md-4 w-100 d-flex justify-center" v-if="type === 'tag'">
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12">
|
<v-col cols="12" v-if="mode === 'replaceInvalidItems'">
|
||||||
<v-autocomplete
|
<v-autocomplete
|
||||||
item-title="name"
|
item-title="name"
|
||||||
item-value="value"
|
item-value="value"
|
||||||
@@ -157,7 +162,7 @@
|
|||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-card-text class="overflow-y-visible">
|
<v-card-text class="overflow-y-visible">
|
||||||
<div class="w-100 d-flex justify-center gap-4">
|
<div class="w-100 d-flex justify-center gap-4">
|
||||||
<v-btn :disabled="(!sourceItem && sourceItem !== '') || (!targetItem && targetItem !== '')" @click="confirm">{{ $t('OK') }}</v-btn>
|
<v-btn :disabled="(mode === 'replaceInvalidItems' && !sourceItem && sourceItem !== '') || (!targetItem && targetItem !== '')" @click="confirm">{{ $t('OK') }}</v-btn>
|
||||||
<v-btn color="secondary" variant="tonal" @click="cancel">{{ $t('Cancel') }}</v-btn>
|
<v-btn color="secondary" variant="tonal" @click="cancel">{{ $t('Cancel') }}</v-btn>
|
||||||
</div>
|
</div>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
@@ -198,6 +203,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showState: false,
|
showState: false,
|
||||||
|
mode: '',
|
||||||
type: '',
|
type: '',
|
||||||
invalidItems: [],
|
invalidItems: [],
|
||||||
sourceItem: null,
|
sourceItem: null,
|
||||||
@@ -264,9 +270,16 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
open(options) {
|
open(options) {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
self.mode = options.mode;
|
||||||
self.type = options.type;
|
self.type = options.type;
|
||||||
self.invalidItems = options.invalidItems;
|
|
||||||
self.sourceItem = null;
|
self.sourceItem = null;
|
||||||
|
|
||||||
|
if (self.mode === 'batchReplace') {
|
||||||
|
self.invalidItems = null;
|
||||||
|
} else if (self.mode === 'replaceInvalidItems') {
|
||||||
|
self.invalidItems = options.invalidItems;
|
||||||
|
}
|
||||||
|
|
||||||
self.targetItem = null;
|
self.targetItem = null;
|
||||||
self.showState = true;
|
self.showState = true;
|
||||||
|
|
||||||
@@ -277,10 +290,16 @@ export default {
|
|||||||
},
|
},
|
||||||
confirm() {
|
confirm() {
|
||||||
if (this.resolve) {
|
if (this.resolve) {
|
||||||
this.resolve({
|
if (this.mode === 'batchReplace') {
|
||||||
sourceItem: this.sourceItem,
|
this.resolve({
|
||||||
targetItem: this.targetItem
|
targetItem: this.targetItem
|
||||||
});
|
});
|
||||||
|
} else if (this.mode === 'replaceInvalidItems') {
|
||||||
|
this.resolve({
|
||||||
|
sourceItem: this.sourceItem,
|
||||||
|
targetItem: this.targetItem
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.showState = false;
|
this.showState = false;
|
||||||
@@ -12,6 +12,27 @@
|
|||||||
<v-icon :icon="icons.more" />
|
<v-icon :icon="icons.more" />
|
||||||
<v-menu activator="parent">
|
<v-menu activator="parent">
|
||||||
<v-list>
|
<v-list>
|
||||||
|
<v-list-item :prepend-icon="icons.replace"
|
||||||
|
:disabled="selectedExpenseTransactionCount < 1"
|
||||||
|
:title="$t('Batch Replace Selected Expense Categories')"
|
||||||
|
@click="showBatchReplaceDialog('expenseCategory')"></v-list-item>
|
||||||
|
<v-list-item :prepend-icon="icons.replace"
|
||||||
|
:disabled="selectedIncomeTransactionCount < 1"
|
||||||
|
:title="$t('Batch Replace Selected Income Categories')"
|
||||||
|
@click="showBatchReplaceDialog('incomeCategory')"></v-list-item>
|
||||||
|
<v-list-item :prepend-icon="icons.replace"
|
||||||
|
:disabled="selectedTransferTransactionCount < 1"
|
||||||
|
:title="$t('Batch Replace Selected Transfer Categories')"
|
||||||
|
@click="showBatchReplaceDialog('transferCategory')"></v-list-item>
|
||||||
|
<v-list-item :prepend-icon="icons.replace"
|
||||||
|
:disabled="selectedImportTransactionCount < 1"
|
||||||
|
:title="$t('Batch Replace Selected Accounts')"
|
||||||
|
@click="showBatchReplaceDialog('account')"></v-list-item>
|
||||||
|
<v-list-item :prepend-icon="icons.replace"
|
||||||
|
:disabled="selectedTransferTransactionCount < 1"
|
||||||
|
:title="$t('Batch Replace Selected Destination Accounts')"
|
||||||
|
@click="showBatchReplaceDialog('destinationAccount')"></v-list-item>
|
||||||
|
<v-divider class="my-2"/>
|
||||||
<v-list-item :prepend-icon="icons.replace"
|
<v-list-item :prepend-icon="icons.replace"
|
||||||
:disabled="allInvalidExpenseCategoryNames < 1"
|
:disabled="allInvalidExpenseCategoryNames < 1"
|
||||||
:title="$t('Replace Invalid Expense Categories')"
|
:title="$t('Replace Invalid Expense Categories')"
|
||||||
@@ -241,7 +262,7 @@
|
|||||||
<v-icon class="mr-1" :icon="icons.alert"/>
|
<v-icon class="mr-1" :icon="icons.alert"/>
|
||||||
<span>{{ item.originalSourceAccountName }}</span>
|
<span>{{ item.originalSourceAccountName }}</span>
|
||||||
</div>
|
</div>
|
||||||
<v-icon class="mx-1" size="13" :icon="icons.arrowRight" v-if="item.type === allTransactionTypes.Transfer && item.sourceAccountId !== item.destinationAccountId"></v-icon>
|
<v-icon class="mx-1" size="13" :icon="icons.arrowRight" v-if="item.type === allTransactionTypes.Transfer"></v-icon>
|
||||||
<span v-if="item.type === allTransactionTypes.Transfer && item.destinationAccountId && item.destinationAccountId !== '0' && allAccountsMap[item.destinationAccountId]">{{allAccountsMap[item.destinationAccountId].name }}</span>
|
<span v-if="item.type === allTransactionTypes.Transfer && item.destinationAccountId && item.destinationAccountId !== '0' && allAccountsMap[item.destinationAccountId]">{{allAccountsMap[item.destinationAccountId].name }}</span>
|
||||||
<div class="text-error font-italic" v-else-if="item.type === allTransactionTypes.Transfer && (!item.destinationAccountId || item.destinationAccountId === '0' || !allAccountsMap[item.destinationAccountId])">
|
<div class="text-error font-italic" v-else-if="item.type === allTransactionTypes.Transfer && (!item.destinationAccountId || item.destinationAccountId === '0' || !allAccountsMap[item.destinationAccountId])">
|
||||||
<v-icon class="mr-1" :icon="icons.alert"/>
|
<v-icon class="mr-1" :icon="icons.alert"/>
|
||||||
@@ -431,14 +452,14 @@
|
|||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
|
|
||||||
<replace-invalid-item-dialog ref="replaceInvalidItemDialog" />
|
<batch-replace-dialog ref="batchReplaceDialog" />
|
||||||
<confirm-dialog ref="confirmDialog"/>
|
<confirm-dialog ref="confirmDialog"/>
|
||||||
<snack-bar ref="snackbar" />
|
<snack-bar ref="snackbar" />
|
||||||
<input ref="fileInput" type="file" style="display: none" :accept="supportedImportFileExtensions" @change="setImportFile($event)" />
|
<input ref="fileInput" type="file" style="display: none" :accept="supportedImportFileExtensions" @change="setImportFile($event)" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ReplaceInvalidItemDialog from './ReplaceInvalidItemDialog.vue';
|
import BatchReplaceDialog from './BatchReplaceDialog.vue';
|
||||||
|
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useSettingsStore } from '@/stores/setting.js';
|
import { useSettingsStore } from '@/stores/setting.js';
|
||||||
@@ -484,7 +505,7 @@ import {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ReplaceInvalidItemDialog
|
BatchReplaceDialog
|
||||||
},
|
},
|
||||||
props: [
|
props: [
|
||||||
'persistent'
|
'persistent'
|
||||||
@@ -707,6 +728,39 @@ export default {
|
|||||||
|
|
||||||
return count;
|
return count;
|
||||||
},
|
},
|
||||||
|
selectedExpenseTransactionCount() {
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.importTransactions.length; i++) {
|
||||||
|
if (this.importTransactions[i].selected && this.importTransactions[i].type === this.allTransactionTypes.Expense) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
},
|
||||||
|
selectedIncomeTransactionCount() {
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.importTransactions.length; i++) {
|
||||||
|
if (this.importTransactions[i].selected && this.importTransactions[i].type === this.allTransactionTypes.Income) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
},
|
||||||
|
selectedTransferTransactionCount() {
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.importTransactions.length; i++) {
|
||||||
|
if (this.importTransactions[i].selected && this.importTransactions[i].type === this.allTransactionTypes.Transfer) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
},
|
||||||
selectedInvalidTransactionCount() {
|
selectedInvalidTransactionCount() {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
@@ -938,23 +992,17 @@ export default {
|
|||||||
},
|
},
|
||||||
selectAllInThisPage() {
|
selectAllInThisPage() {
|
||||||
for (let i = Math.max(0, (this.currentPage - 1) * this.countPerPage); i < Math.min(this.importTransactions.length, this.currentPage * this.countPerPage); i++) {
|
for (let i = Math.max(0, (this.currentPage - 1) * this.countPerPage); i < Math.min(this.importTransactions.length, this.currentPage * this.countPerPage); i++) {
|
||||||
if (this.importTransactions[i] && this.importTransactions[i].valid) {
|
this.importTransactions[i].selected = true;
|
||||||
this.importTransactions[i].selected = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
selectNoneInThisPage() {
|
selectNoneInThisPage() {
|
||||||
for (let i = Math.max(0, (this.currentPage - 1) * this.countPerPage); i < Math.min(this.importTransactions.length, this.currentPage * this.countPerPage); i++) {
|
for (let i = Math.max(0, (this.currentPage - 1) * this.countPerPage); i < Math.min(this.importTransactions.length, this.currentPage * this.countPerPage); i++) {
|
||||||
if (this.importTransactions[i]) {
|
this.importTransactions[i].selected = false;
|
||||||
this.importTransactions[i].selected = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
selectInvertInThisPage() {
|
selectInvertInThisPage() {
|
||||||
for (let i = Math.max(0, (this.currentPage - 1) * this.countPerPage); i < Math.min(this.importTransactions.length, this.currentPage * this.countPerPage); i++) {
|
for (let i = Math.max(0, (this.currentPage - 1) * this.countPerPage); i < Math.min(this.importTransactions.length, this.currentPage * this.countPerPage); i++) {
|
||||||
if (this.importTransactions[i] && (this.importTransactions[i].valid || this.importTransactions[i].selected)) {
|
this.importTransactions[i].selected = !this.importTransactions[i].selected;
|
||||||
this.importTransactions[i].selected = !this.importTransactions[i].selected;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
editTransaction(transaction) {
|
editTransaction(transaction) {
|
||||||
@@ -967,10 +1015,71 @@ export default {
|
|||||||
updateTransactionData(transaction) {
|
updateTransactionData(transaction) {
|
||||||
transaction.valid = this.isTransactionValid(transaction);
|
transaction.valid = this.isTransactionValid(transaction);
|
||||||
},
|
},
|
||||||
|
showBatchReplaceDialog(type) {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
self.$refs.batchReplaceDialog.open({
|
||||||
|
mode: 'batchReplace',
|
||||||
|
type: type
|
||||||
|
}).then(result => {
|
||||||
|
if (!result || !result.targetItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let updatedCount = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < self.importTransactions.length; i++) {
|
||||||
|
const transaction = self.importTransactions[i];
|
||||||
|
|
||||||
|
if (!transaction.selected) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let updated = false;
|
||||||
|
|
||||||
|
if (type === 'expenseCategory') {
|
||||||
|
if (transaction.type === self.allTransactionTypes.Expense) {
|
||||||
|
transaction.categoryId = result.targetItem;
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
} else if (type === 'incomeCategory') {
|
||||||
|
if (transaction.type === self.allTransactionTypes.Income) {
|
||||||
|
transaction.categoryId = result.targetItem;
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
} else if (type === 'transferCategory') {
|
||||||
|
if (transaction.type === self.allTransactionTypes.Transfer) {
|
||||||
|
transaction.categoryId = result.targetItem;
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
} else if (type === 'account') {
|
||||||
|
transaction.sourceAccountId = result.targetItem;
|
||||||
|
updated = true;
|
||||||
|
} else if (type === 'destinationAccount') {
|
||||||
|
if (transaction.type === self.allTransactionTypes.Transfer) {
|
||||||
|
transaction.destinationAccountId = result.targetItem;
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
|
updatedCount++;
|
||||||
|
self.updateTransactionData(transaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updatedCount > 0) {
|
||||||
|
self.$refs.snackbar.showMessage('format.misc.youHaveUpdatedTransactions', {
|
||||||
|
count: updatedCount
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
showReplaceInvalidItemDialog(type, invalidItems) {
|
showReplaceInvalidItemDialog(type, invalidItems) {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
self.$refs.replaceInvalidItemDialog.open({
|
self.$refs.batchReplaceDialog.open({
|
||||||
|
mode: 'replaceInvalidItems',
|
||||||
type: type,
|
type: type,
|
||||||
invalidItems: invalidItems
|
invalidItems: invalidItems
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
|
|||||||
Reference in New Issue
Block a user