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",
|
||||
"Cannot import invalid transactions": "Cannot import invalid transactions",
|
||||
"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 Income Categories": "Replace Invalid Income Categories",
|
||||
"Replace Invalid Transfer Categories": "Replace Invalid Transfer Categories",
|
||||
|
||||
@@ -1509,7 +1509,11 @@
|
||||
"No data to import": "没有可以导入的数据",
|
||||
"Cannot import invalid transactions": "不能导入无效的交易",
|
||||
"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 Income Categories": "替换无效的收入分类",
|
||||
"Replace Invalid Transfer Categories": "替换无效的转账分类",
|
||||
|
||||
+34
-15
@@ -3,16 +3,21 @@
|
||||
<v-card class="pa-2 pa-sm-4 pa-md-4">
|
||||
<template #title>
|
||||
<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="type === 'incomeCategory'">{{ $t('Replace Invalid Income Categories') }}</h4>
|
||||
<h4 class="text-h4" v-if="type === 'transferCategory'">{{ $t('Replace Invalid Transfer Categories') }}</h4>
|
||||
<h4 class="text-h4" v-if="type === 'account'">{{ $t('Replace Invalid 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 === 'expenseCategory'">{{ $t('Batch Replace Selected Expense 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="mode === 'batchReplace' && type === 'transferCategory'">{{ $t('Batch Replace Selected Transfer Categories') }}</h4>
|
||||
<h4 class="text-h4" v-if="mode === 'batchReplace' && type === 'account'">{{ $t('Batch Replace Selected Accounts') }}</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>
|
||||
</template>
|
||||
<v-card-text class="my-md-4 w-100 d-flex justify-center" v-if="type === 'expenseCategory' || type === 'incomeCategory' || type === 'transferCategory'">
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-col cols="12" v-if="mode === 'replaceInvalidItems'">
|
||||
<v-autocomplete
|
||||
item-title="name"
|
||||
item-value="value"
|
||||
@@ -76,9 +81,9 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</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-col cols="12">
|
||||
<v-col cols="12" v-if="mode === 'replaceInvalidItems'">
|
||||
<v-autocomplete
|
||||
item-title="name"
|
||||
item-value="value"
|
||||
@@ -111,7 +116,7 @@
|
||||
</v-card-text>
|
||||
<v-card-text class="my-md-4 w-100 d-flex justify-center" v-if="type === 'tag'">
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-col cols="12" v-if="mode === 'replaceInvalidItems'">
|
||||
<v-autocomplete
|
||||
item-title="name"
|
||||
item-value="value"
|
||||
@@ -157,7 +162,7 @@
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<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>
|
||||
</div>
|
||||
</v-card-text>
|
||||
@@ -198,6 +203,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
showState: false,
|
||||
mode: '',
|
||||
type: '',
|
||||
invalidItems: [],
|
||||
sourceItem: null,
|
||||
@@ -264,9 +270,16 @@ export default {
|
||||
methods: {
|
||||
open(options) {
|
||||
const self = this;
|
||||
self.mode = options.mode;
|
||||
self.type = options.type;
|
||||
self.invalidItems = options.invalidItems;
|
||||
self.sourceItem = null;
|
||||
|
||||
if (self.mode === 'batchReplace') {
|
||||
self.invalidItems = null;
|
||||
} else if (self.mode === 'replaceInvalidItems') {
|
||||
self.invalidItems = options.invalidItems;
|
||||
}
|
||||
|
||||
self.targetItem = null;
|
||||
self.showState = true;
|
||||
|
||||
@@ -277,10 +290,16 @@ export default {
|
||||
},
|
||||
confirm() {
|
||||
if (this.resolve) {
|
||||
this.resolve({
|
||||
sourceItem: this.sourceItem,
|
||||
targetItem: this.targetItem
|
||||
});
|
||||
if (this.mode === 'batchReplace') {
|
||||
this.resolve({
|
||||
targetItem: this.targetItem
|
||||
});
|
||||
} else if (this.mode === 'replaceInvalidItems') {
|
||||
this.resolve({
|
||||
sourceItem: this.sourceItem,
|
||||
targetItem: this.targetItem
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.showState = false;
|
||||
@@ -12,6 +12,27 @@
|
||||
<v-icon :icon="icons.more" />
|
||||
<v-menu activator="parent">
|
||||
<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"
|
||||
:disabled="allInvalidExpenseCategoryNames < 1"
|
||||
:title="$t('Replace Invalid Expense Categories')"
|
||||
@@ -241,7 +262,7 @@
|
||||
<v-icon class="mr-1" :icon="icons.alert"/>
|
||||
<span>{{ item.originalSourceAccountName }}</span>
|
||||
</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>
|
||||
<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"/>
|
||||
@@ -431,14 +452,14 @@
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<replace-invalid-item-dialog ref="replaceInvalidItemDialog" />
|
||||
<batch-replace-dialog ref="batchReplaceDialog" />
|
||||
<confirm-dialog ref="confirmDialog"/>
|
||||
<snack-bar ref="snackbar" />
|
||||
<input ref="fileInput" type="file" style="display: none" :accept="supportedImportFileExtensions" @change="setImportFile($event)" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ReplaceInvalidItemDialog from './ReplaceInvalidItemDialog.vue';
|
||||
import BatchReplaceDialog from './BatchReplaceDialog.vue';
|
||||
|
||||
import { mapStores } from 'pinia';
|
||||
import { useSettingsStore } from '@/stores/setting.js';
|
||||
@@ -484,7 +505,7 @@ import {
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ReplaceInvalidItemDialog
|
||||
BatchReplaceDialog
|
||||
},
|
||||
props: [
|
||||
'persistent'
|
||||
@@ -707,6 +728,39 @@ export default {
|
||||
|
||||
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() {
|
||||
let count = 0;
|
||||
|
||||
@@ -938,23 +992,17 @@ export default {
|
||||
},
|
||||
selectAllInThisPage() {
|
||||
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() {
|
||||
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() {
|
||||
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) {
|
||||
@@ -967,10 +1015,71 @@ export default {
|
||||
updateTransactionData(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) {
|
||||
const self = this;
|
||||
|
||||
self.$refs.replaceInvalidItemDialog.open({
|
||||
self.$refs.batchReplaceDialog.open({
|
||||
mode: 'replaceInvalidItems',
|
||||
type: type,
|
||||
invalidItems: invalidItems
|
||||
}).then(result => {
|
||||
|
||||
Reference in New Issue
Block a user