mirror of
https://github.com/frappe/books.git
synced 2026-08-24 02:24:17 -05:00
Merge pull request #1384 from Gadha2311/multi-item-selection
feat : add multiple selection option in item master
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,6 +1,46 @@
|
||||
<template>
|
||||
<div class="flex flex-col">
|
||||
<PageHeader :title="title">
|
||||
<Button
|
||||
v-if="
|
||||
schemaName === 'Item' &&
|
||||
(!isSelectionMode || (isSelectionMode && selectedItems.length === 0))
|
||||
"
|
||||
@click="toggleSelectionMode"
|
||||
>
|
||||
{{ t`Select` }}
|
||||
</Button>
|
||||
<div
|
||||
v-if="
|
||||
isSelectionMode && schemaName === 'Item' && selectedItems.length > 0
|
||||
"
|
||||
class="relative"
|
||||
>
|
||||
<Button class="w-40" @click="toggleDropdown"> Create Invoice </Button>
|
||||
<div
|
||||
v-if="showDropdown"
|
||||
class="
|
||||
absolute
|
||||
top-full
|
||||
mt-1
|
||||
bg-white
|
||||
border border-gray-300
|
||||
rounded
|
||||
shadow-lg
|
||||
z-10
|
||||
w-40
|
||||
"
|
||||
>
|
||||
<div
|
||||
v-for="option in actionOptions"
|
||||
:key="option.value"
|
||||
class="px-4 py-2 hover:bg-gray-100 cursor-pointer text-sm"
|
||||
@click="createInvoice(option.value)"
|
||||
>
|
||||
{{ option.label }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button ref="exportButton" :icon="false" @click="openExportModal = true">
|
||||
{{ t`Export` }}
|
||||
</Button>
|
||||
@@ -16,7 +56,7 @@
|
||||
type="primary"
|
||||
:padding="false"
|
||||
class="px-3"
|
||||
@click="makeNewDoc"
|
||||
@click="handleMakeNewDoc"
|
||||
>
|
||||
<feather-icon name="plus" class="w-4 h-4" />
|
||||
</Button>
|
||||
@@ -27,10 +67,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 +91,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 { fyo } from 'src/initFyo';
|
||||
import { shortcutsKey } from 'src/utils/injectionKeys';
|
||||
import {
|
||||
@@ -60,6 +103,8 @@ 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';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ListView',
|
||||
@@ -90,10 +135,16 @@ export default defineComponent({
|
||||
listConfig: undefined,
|
||||
openExportModal: false,
|
||||
listFilters: {},
|
||||
isSelectionMode: false,
|
||||
showDropdown: false,
|
||||
selectedItems: [] as string[],
|
||||
} as {
|
||||
listConfig: undefined | ReturnType<typeof getListConfig>;
|
||||
openExportModal: boolean;
|
||||
listFilters: QueryFilter;
|
||||
isSelectionMode: boolean;
|
||||
showDropdown: boolean;
|
||||
selectedItems: string[];
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -113,6 +164,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 +217,52 @@ 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.showDropdown = false;
|
||||
this.selectedItems = [];
|
||||
}
|
||||
},
|
||||
toggleDropdown() {
|
||||
this.showDropdown = !this.showDropdown;
|
||||
},
|
||||
async createInvoice(value: string) {
|
||||
if (
|
||||
value === ModelNameEnum.SalesInvoice ||
|
||||
value === ModelNameEnum.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;
|
||||
this.showDropdown = false;
|
||||
}
|
||||
},
|
||||
|
||||
updateSelectedItems(selected: string[]) {
|
||||
this.selectedItems = selected;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user