feat: add multiple selection option in item master

This commit is contained in:
Gadha2311
2025-10-30 17:34:36 +05:30
parent d72d2568c1
commit 488686b93a
3 changed files with 145 additions and 7 deletions
+1 -1
View File
@@ -8,7 +8,7 @@
{{ df.label }}
</div>
<div
style="width: 14px; height: 14px; overflow: hidden"
style="width: 14px; height: 14px"
:class="isReadOnly ? 'cursor-default' : 'cursor-pointer'"
>
<svg
+62 -5
View File
@@ -7,7 +7,24 @@
paddingRight: dataSlice.length > 13 ? 'var(--w-scrollbar)' : '',
}"
>
<p class="w-8 text-end me-4 text-gray-700 dark:text-gray-400">#</p>
<div
v-if="!isSelectionMode"
class="w-8 text-end me-2 text-gray-700 dark:text-gray-400"
>
#
</div>
<div v-else class="w-8 flex justify-end me-2">
<Check
:df="{
fieldtype: 'Check',
fieldname: 'selectAll',
label: '',
}"
:show-label="false"
:value="isAllSelected"
@change="toggleSelectAll"
/>
</div>
<Row
class="flex-1 text-gray-700 dark:text-gray-400 h-row-mid"
:column-count="columns.length"
@@ -47,9 +64,25 @@
<div v-for="(row, i) in dataSlice" :key="(row.name as string)">
<!-- Row Content -->
<div class="flex hover:bg-gray-50 dark:hover:bg-gray-850 items-center">
<p class="w-8 text-end me-4 text-gray-900 dark:text-gray-25">
<div
v-if="!isSelectionMode"
class="w-8 text-end me-2 text-gray-700 dark:text-gray-400"
>
{{ i + pageStart + 1 }}
</p>
</div>
<div v-else class="w-8 flex justify-end me-2">
<Check
:df="{
fieldtype: 'Check',
fieldname: 'selectItem',
label: '',
}"
:show-label="false"
:value="selectedItems.includes(row.name as string)"
@change="toggleItemSelection(row.name as string)"
/>
</div>
<Row
gap="1rem"
class="
@@ -60,7 +93,7 @@
h-row-mid
"
:column-count="columns.length"
@click="$emit('openDoc', row.name)"
@click="isSelectionMode ? null : $emit('openDoc', row.name)"
>
<ListCell
v-for="(column, c) in columns"
@@ -111,6 +144,7 @@
import { ListViewSettings, RenderData } from 'fyo/model/types';
import { cloneDeep } from 'lodash';
import Button from 'src/components/Button.vue';
import Check from 'src/components/Controls/Check.vue';
import Paginator from 'src/components/Paginator.vue';
import Row from 'src/components/Row.vue';
import { fyo } from 'src/initFyo';
@@ -125,6 +159,7 @@ export default defineComponent({
Row,
ListCell,
Button,
Check,
Paginator,
},
props: {
@@ -138,14 +173,16 @@ export default defineComponent({
},
schemaName: { type: String, required: true },
canCreate: Boolean,
isSelectionMode: Boolean,
},
emits: ['openDoc', 'makeNewDoc', 'updatedData'],
emits: ['openDoc', 'makeNewDoc', 'updatedData', 'selected-items-changed'],
data() {
return {
data: [] as RenderData[],
pageStart: 0,
pageEnd: 0,
statusMap: {} as Record<string, string>,
selectedItems: [] as string[],
};
},
computed: {
@@ -155,6 +192,11 @@ export default defineComponent({
count() {
return this.pageEnd - this.pageStart + 1;
},
isAllSelected(): boolean {
return (
this.data.length > 0 && this.selectedItems.length === this.data.length
);
},
columns() {
let columns = this.listConfig?.columns ?? [];
@@ -261,6 +303,21 @@ export default defineComponent({
})) as RenderData[];
this.$emit('updatedData', filters);
},
toggleItemSelection(itemName: string) {
const index = this.selectedItems.indexOf(itemName);
if (index > -1) {
this.selectedItems.splice(index, 1);
} else {
this.selectedItems.push(itemName);
}
this.$emit('selected-items-changed', this.selectedItems);
},
toggleSelectAll(checked: boolean) {
this.selectedItems = checked
? this.data.map((row) => row.name as string)
: [];
this.$emit('selected-items-changed', this.selectedItems);
},
},
});
</script>
+82 -1
View File
@@ -1,6 +1,32 @@
<template>
<div class="flex flex-col">
<PageHeader :title="title">
<Button
v-if="
schemaName === 'Item' &&
(!isSelectionMode || (isSelectionMode && selectedItems.length === 0))
"
ref="selectButton"
:icon="false"
@click="toggleSelectionMode"
>
{{ t`Select` }}
</Button>
<Select
v-if="
isSelectionMode && schemaName === 'Item' && selectedItems.length > 0
"
ref="actionSelect"
:df="{
fieldtype: 'Select',
fieldname: 'Create ',
label: 'Create Invoice',
options: actionOptions,
}"
:value="selectedAction"
class="w-40"
@change="onActionChange"
/>
<Button ref="exportButton" :icon="false" @click="openExportModal = true">
{{ t`Export` }}
</Button>
@@ -16,7 +42,7 @@
type="primary"
:padding="false"
class="px-3"
@click="makeNewDoc"
@click="handleMakeNewDoc"
>
<feather-icon name="plus" class="w-4 h-4" />
</Button>
@@ -27,10 +53,12 @@
:list-config="listConfig"
:filters="filters"
:can-create="canCreate"
:is-selection-mode="isSelectionMode"
class="flex-1 flex h-full"
@open-doc="openDoc"
@updated-data="updatedData"
@make-new-doc="makeNewDoc"
@selected-items-changed="updateSelectedItems"
/>
<Modal :open-modal="openExportModal" @closemodal="openExportModal = false">
<ExportWizard
@@ -49,6 +77,7 @@ import ExportWizard from 'src/components/ExportWizard.vue';
import FilterDropdown from 'src/components/FilterDropdown.vue';
import Modal from 'src/components/Modal.vue';
import PageHeader from 'src/components/PageHeader.vue';
import Select from 'src/components/Controls/Select.vue';
import { fyo } from 'src/initFyo';
import { shortcutsKey } from 'src/utils/injectionKeys';
import {
@@ -60,6 +89,7 @@ import { getFormRoute, routeTo } from 'src/utils/ui';
import { QueryFilter } from 'utils/db/types';
import { defineComponent, inject, ref } from 'vue';
import List from './List.vue';
import { Money } from 'pesa';
export default defineComponent({
name: 'ListView',
@@ -70,6 +100,7 @@ export default defineComponent({
FilterDropdown,
Modal,
ExportWizard,
Select,
},
props: {
schemaName: { type: String, required: true },
@@ -82,6 +113,7 @@ export default defineComponent({
list: ref<InstanceType<typeof List> | null>(null),
makeNewDocButton: ref<InstanceType<typeof Button> | null>(null),
exportButton: ref<InstanceType<typeof Button> | null>(null),
selectButton: ref<InstanceType<typeof Button> | null>(null),
filterDropdown: ref<InstanceType<typeof FilterDropdown> | null>(null),
};
},
@@ -90,10 +122,16 @@ export default defineComponent({
listConfig: undefined,
openExportModal: false,
listFilters: {},
isSelectionMode: false,
selectedAction: '',
selectedItems: [] as string[],
} as {
listConfig: undefined | ReturnType<typeof getListConfig>;
openExportModal: boolean;
listFilters: QueryFilter;
isSelectionMode: boolean;
selectedAction: string;
selectedItems: string[];
};
},
computed: {
@@ -113,6 +151,12 @@ export default defineComponent({
canCreate(): boolean {
return fyo.schemaMap[this.schemaName]?.create !== false;
},
actionOptions(): { value: string; label: string }[] {
return [
{ value: 'SalesInvoice', label: 'Sales Invoice' },
{ value: 'PurchaseInvoice', label: 'Purchase Invoice' },
];
},
},
activated() {
this.listConfig = getListConfig(this.schemaName);
@@ -160,9 +204,46 @@ export default defineComponent({
const route = getFormRoute(this.schemaName, doc.name!);
await routeTo(route);
},
async handleMakeNewDoc() {
await this.makeNewDoc();
},
applyFilter(filters: QueryFilter) {
this.list?.updateData(filters);
},
toggleSelectionMode() {
this.isSelectionMode = !this.isSelectionMode;
if (!this.isSelectionMode) {
this.selectedAction = '';
this.selectedItems = [];
}
},
async onActionChange(value: string) {
this.selectedAction = value;
if (value === 'SalesInvoice' || value === 'PurchaseInvoice') {
const doc = fyo.doc.getNewDoc(value);
for (const itemName of this.selectedItems) {
const itemDoc = await fyo.doc.getDoc('Item', itemName);
const itemRow = {
item: itemName,
rate: (itemDoc.rate as Money) || fyo.pesa(0),
quantity: 1,
};
await doc.append('items', itemRow);
}
const route = getFormRoute(value, doc.name!);
await routeTo(route);
this.selectedItems = [];
this.isSelectionMode = false;
}
},
updateSelectedItems(selected: string[]) {
this.selectedItems = selected;
},
},
});