1131 lines
36 KiB
Vue
1131 lines
36 KiB
Vue
![]() |
<template>
|
|||
|
<div class="table-container" :class="{ 'has-search': hasSearch }">
|
|||
|
<!-- #region header -->
|
|||
|
<div class="table-header d-flex align-items-center">
|
|||
|
<div v-if="hasSearch">
|
|||
|
<!-- <slot name="headerSearch"></slot> -->
|
|||
|
|
|||
|
<form class="table-search-form" role="search" @submit.prevent="search">
|
|||
|
<div class="form-group">
|
|||
|
<div class="input-group">
|
|||
|
<div class="input-group-prepend">
|
|||
|
<span class="input-group-text">
|
|||
|
<svg
|
|||
|
class=""
|
|||
|
aria-hidden="true"
|
|||
|
focusable="false"
|
|||
|
data-prefix="fas"
|
|||
|
data-icon="search"
|
|||
|
role="img"
|
|||
|
xmlns="http://www.w3.org/2000/svg"
|
|||
|
viewBox="0 0 512 512"
|
|||
|
data-fa-i2svg=""
|
|||
|
>
|
|||
|
<path
|
|||
|
fill="currentColor"
|
|||
|
d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"
|
|||
|
></path>
|
|||
|
</svg>
|
|||
|
</span>
|
|||
|
</div>
|
|||
|
|
|||
|
<input
|
|||
|
v-model="searchForm.query"
|
|||
|
type="search"
|
|||
|
required
|
|||
|
class="form-control"
|
|||
|
id="search-query"
|
|||
|
placeholder="جستجو..."
|
|||
|
name="search-query"
|
|||
|
aria-label="جستجو در اسناد، عناوین و واژگان"
|
|||
|
aria-describedby="basic-addon1"
|
|||
|
size="50"
|
|||
|
@keyup="search()"
|
|||
|
@keydown="onKeyDown()"
|
|||
|
/>
|
|||
|
|
|||
|
<div class="input-group-append">
|
|||
|
<button
|
|||
|
@click="resetForm()"
|
|||
|
:class="{ 'show-reset-btn': searchForm.query.length > 0 }"
|
|||
|
type="button"
|
|||
|
class="input-group-text"
|
|||
|
id="basic-addon1"
|
|||
|
>
|
|||
|
<svg class="icon icon-Component-294--1">
|
|||
|
<use xlink:href="#icon-Component-294--1"></use>
|
|||
|
</svg>
|
|||
|
</button>
|
|||
|
|
|||
|
<!-- <div class="dropdown">
|
|||
|
<button
|
|||
|
class="btn dropdown-toggle"
|
|||
|
type="button"
|
|||
|
data-bs-toggle="dropdown"
|
|||
|
aria-expanded="false"
|
|||
|
>
|
|||
|
<svg class="icon icon-filter" style="width: 1.3em">
|
|||
|
<use xlink:href="#icon-filter"></use>
|
|||
|
</svg>
|
|||
|
عنوان
|
|||
|
</button>
|
|||
|
<div class="dropdown-menu">
|
|||
|
<button class="dropdown-item" type="button" value="current">
|
|||
|
عنوان
|
|||
|
</button>
|
|||
|
<button class="dropdown-item" type="button" value="all">
|
|||
|
شماره
|
|||
|
</button>
|
|||
|
<button class="dropdown-item" type="button" value="all">
|
|||
|
کد
|
|||
|
</button>
|
|||
|
</div>
|
|||
|
</div> -->
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</form>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="me-auto">
|
|||
|
<slot name="tableHeaderActions"></slot>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<!-- #endregion header -->
|
|||
|
<!-- #region main -->
|
|||
|
<div
|
|||
|
class="table-responsive firefox-scrollbar"
|
|||
|
:style="{
|
|||
|
height: $attrs.height,
|
|||
|
maxHeight: $attrs.maxHeight,
|
|||
|
minHeight: $attrs.minHeight,
|
|||
|
}"
|
|||
|
>
|
|||
|
<table
|
|||
|
class="table table-hover table-striped"
|
|||
|
:class="{ 'column-highlight-active': isColumnHighlightActive }"
|
|||
|
id="table1"
|
|||
|
>
|
|||
|
<thead class="thead">
|
|||
|
<tr>
|
|||
|
<th scope="col">#</th>
|
|||
|
<th
|
|||
|
:class="{ 'cursor-title': activeTableColumnTitle }"
|
|||
|
v-for="(tableColumn, index) in tableColumns"
|
|||
|
:colspan="tableColumn.width"
|
|||
|
@click="clickManagementTableColumns(tableColumn)"
|
|||
|
:key="index"
|
|||
|
scope="col"
|
|||
|
v-tooltip="tableColumn?.titleTooltip"
|
|||
|
>
|
|||
|
{{ tableColumn.title }}
|
|||
|
<button-component
|
|||
|
:class="tableColumn.sortorder"
|
|||
|
classes="p-0 sort-indicator"
|
|||
|
buttonText=""
|
|||
|
v-if="showHeaderSortButton"
|
|||
|
>
|
|||
|
<span class="tavasi tavasi-chevron-sort"></span>
|
|||
|
<span class="tavasi tavasi-chevron-sort-asce"
|
|||
|
><span class="path1"></span><span class="path2"></span
|
|||
|
></span>
|
|||
|
<span class="tavasi tavasi-chevron-sort-desc"
|
|||
|
><span class="path1"></span><span class="path2"></span
|
|||
|
></span>
|
|||
|
|
|||
|
<!-- <span class="tavasi tavasi-sort"></span> -->
|
|||
|
<!-- <span class="tavasi tavasi-desc"></span> -->
|
|||
|
<!-- <span class="tavasi tavasi-asce"></span> -->
|
|||
|
|
|||
|
<!-- <span class="tavasi tavasi-Component-132--1"></span> -->
|
|||
|
</button-component>
|
|||
|
</th>
|
|||
|
<th v-if="showActions" scope="col">{{ $t("Operation") }}</th>
|
|||
|
</tr>
|
|||
|
</thead>
|
|||
|
|
|||
|
<template v-if="fetchingData">
|
|||
|
<tbody>
|
|||
|
<tr>
|
|||
|
<td colspan="12">
|
|||
|
<the-content-loading
|
|||
|
:loadingTitle="'در حال دریافت اطلاعات'"
|
|||
|
class="table-loading"
|
|||
|
></the-content-loading>
|
|||
|
</td>
|
|||
|
</tr>
|
|||
|
</tbody>
|
|||
|
</template>
|
|||
|
|
|||
|
<template v-else>
|
|||
|
<template v-if="cloneItems.length">
|
|||
|
<draggable
|
|||
|
handle=".my-handle"
|
|||
|
:sort="isSortable"
|
|||
|
:disabled="!isDraggable"
|
|||
|
:list="cloneItems"
|
|||
|
tag="tbody"
|
|||
|
group="people"
|
|||
|
:removeCloneOnHide="false"
|
|||
|
@start="handleDragStart"
|
|||
|
@sort="$emit('onSort', $event)"
|
|||
|
>
|
|||
|
<tr
|
|||
|
v-for="(rowItem, index) in cloneItems"
|
|||
|
:class="[
|
|||
|
{ active: rowItem?.active ?? false },
|
|||
|
{ unactive: getUnactiveClass(rowItem) },
|
|||
|
]"
|
|||
|
:key="rowItem.id_store ?? rowItem.id"
|
|||
|
:id="rowItem.id_store ?? rowItem.id"
|
|||
|
>
|
|||
|
<th v-if="hasCheckbox">
|
|||
|
<input
|
|||
|
type="checkbox"
|
|||
|
class="form-control"
|
|||
|
:value="rowItem[tableColumn.key]"
|
|||
|
:true-value="1"
|
|||
|
:false-value="0"
|
|||
|
@change="
|
|||
|
$emit('checkbox-selected', {
|
|||
|
rowItem,
|
|||
|
newOrder: $event.target.value,
|
|||
|
})
|
|||
|
"
|
|||
|
/>
|
|||
|
</th>
|
|||
|
|
|||
|
<th scope="row" :class="{ 'my-handle': isDraggable }">
|
|||
|
{{ paginationInfo.offset + index + 1 }}
|
|||
|
{{ isDraggable ? "::" : "" }}
|
|||
|
</th>
|
|||
|
|
|||
|
<!-- :class="{ 'my-handle': key < 1 }" -->
|
|||
|
<td
|
|||
|
:colspan="tableColumn.width"
|
|||
|
v-for="(tableColumn, key) in tableColumns"
|
|||
|
:key="key"
|
|||
|
:class="[
|
|||
|
tableColumn.textAlign
|
|||
|
? `text-${tableColumn.textAlign}`
|
|||
|
: 'text-center',
|
|||
|
]"
|
|||
|
>
|
|||
|
<input
|
|||
|
class="form-control"
|
|||
|
style="width: 4em"
|
|||
|
@input="
|
|||
|
$emit('update-order', {
|
|||
|
rowItem,
|
|||
|
newOrder: $event.target.value,
|
|||
|
})
|
|||
|
"
|
|||
|
type="number"
|
|||
|
min="1"
|
|||
|
v-if="tableColumn.key == 'subject_order'"
|
|||
|
:value="rowItem[tableColumn.key]"
|
|||
|
/>
|
|||
|
|
|||
|
<a
|
|||
|
class="is-link show-text-decoration 1231231231313"
|
|||
|
@click.prevent="
|
|||
|
methodName('on-linked-title-click', {
|
|||
|
rowItem,
|
|||
|
tableColumn,
|
|||
|
index,
|
|||
|
})
|
|||
|
"
|
|||
|
@auxclick.prevent.stop="
|
|||
|
methodName('on-linked-title-click', {
|
|||
|
rowItem,
|
|||
|
tableColumn,
|
|||
|
index,
|
|||
|
})
|
|||
|
"
|
|||
|
v-else-if="tableColumn.isLink"
|
|||
|
:href="rowItem[tableColumn.key]"
|
|||
|
:title="rowItem[tableColumn.key]"
|
|||
|
v-html="getCellValue(rowItem, tableColumn)"
|
|||
|
>
|
|||
|
</a>
|
|||
|
<template v-else
|
|||
|
><span v-html="getCellValue(rowItem, tableColumn)"></span
|
|||
|
></template>
|
|||
|
</td>
|
|||
|
|
|||
|
<td v-if="showActions">
|
|||
|
<span class="action-column">
|
|||
|
<template v-for="(rowAction, j) in tableActions">
|
|||
|
<template v-if="rowAction.toggle_icons">
|
|||
|
<button
|
|||
|
v-if="rowAction.key != 'tbookmark' || isRealUser"
|
|||
|
:key="j"
|
|||
|
@click="
|
|||
|
methodName(rowAction.action, {
|
|||
|
item: rowItem,
|
|||
|
index: index,
|
|||
|
rowAction: rowAction,
|
|||
|
})
|
|||
|
"
|
|||
|
:title="rowAction.title"
|
|||
|
class="btn"
|
|||
|
:type="rowAction.type"
|
|||
|
>
|
|||
|
<svg
|
|||
|
v-if="rowAction.key == 'tbookmark'"
|
|||
|
class=""
|
|||
|
:class="
|
|||
|
rowItem._source[rowAction.key] == 1
|
|||
|
? 'icon icon-' + rowAction.toggle_icons.icon1
|
|||
|
: 'icon icon-' + rowAction.toggle_icons.icon2
|
|||
|
"
|
|||
|
>
|
|||
|
<use
|
|||
|
:xlink:href="
|
|||
|
rowItem._source[rowAction.key] == 1
|
|||
|
? '#icon-' + rowAction.toggle_icons.icon1
|
|||
|
: '#icon-' + rowAction.toggle_icons.icon2
|
|||
|
"
|
|||
|
></use>
|
|||
|
</svg>
|
|||
|
<svg v-else :class="'icon icon-' + rowAction.icon">
|
|||
|
<use :xlink:href="'#icon-' + rowAction.icon"></use>
|
|||
|
</svg>
|
|||
|
</button>
|
|||
|
</template>
|
|||
|
|
|||
|
<template v-else>
|
|||
|
<template v-if="rowAction.can">
|
|||
|
<button-component
|
|||
|
:key="j"
|
|||
|
v-can="rowAction.can"
|
|||
|
@click="
|
|||
|
methodName(rowAction.action, {
|
|||
|
item: rowItem,
|
|||
|
index: index,
|
|||
|
rowAction: rowAction,
|
|||
|
})
|
|||
|
"
|
|||
|
buttonText=""
|
|||
|
:classes="rowAction.class"
|
|||
|
:title="rowAction.title"
|
|||
|
>
|
|||
|
<svg :class="`icon icon-${rowAction.icon}`">
|
|||
|
<use
|
|||
|
:xlink:href="`#icon-${rowAction.icon}`"
|
|||
|
></use>
|
|||
|
</svg>
|
|||
|
</button-component>
|
|||
|
</template>
|
|||
|
<template v-else>
|
|||
|
<button-component
|
|||
|
:key="j"
|
|||
|
@click="
|
|||
|
methodName(rowAction.action, {
|
|||
|
item: rowItem,
|
|||
|
index: index,
|
|||
|
rowAction: rowAction,
|
|||
|
})
|
|||
|
"
|
|||
|
buttonText=""
|
|||
|
:classes="rowAction.class"
|
|||
|
>
|
|||
|
<svg :class="`icon icon-${rowAction.icon}`">
|
|||
|
<use
|
|||
|
:xlink:href="`#icon-${rowAction.icon}`"
|
|||
|
></use>
|
|||
|
</svg>
|
|||
|
|
|||
|
<!-- <span v-else :class="rowAction.icon"></span> -->
|
|||
|
</button-component>
|
|||
|
</template>
|
|||
|
</template>
|
|||
|
</template>
|
|||
|
<!-- <context-menu
|
|||
|
style="position: static; display: inline-flex"
|
|||
|
v-if="tableActions.length > 2"
|
|||
|
:id="rowItem.id"
|
|||
|
:contextMenu="tableActions"
|
|||
|
@topicing="showSubjectForm(index)"
|
|||
|
@other="methodName($event, index)"
|
|||
|
>
|
|||
|
</context-menu> -->
|
|||
|
</span>
|
|||
|
</td>
|
|||
|
</tr>
|
|||
|
</draggable>
|
|||
|
|
|||
|
<tfoot v-if="hasFooter">
|
|||
|
<tr>
|
|||
|
<td colspan="12">
|
|||
|
<slot name="footer"></slot>
|
|||
|
</td>
|
|||
|
</tr>
|
|||
|
</tfoot>
|
|||
|
</template>
|
|||
|
|
|||
|
<tbody v-else-if="cloneItems.length == 0">
|
|||
|
<table-no-data />
|
|||
|
</tbody>
|
|||
|
</template>
|
|||
|
</table>
|
|||
|
</div>
|
|||
|
<!-- #endregion main -->
|
|||
|
<!-- #region pagination -->
|
|||
|
<jahat-pagination
|
|||
|
v-if="hasPagination"
|
|||
|
:paginationInfo="paginationInfo"
|
|||
|
@page-changed="clickCallback"
|
|||
|
@page-limit-changed="limitChanged"
|
|||
|
>
|
|||
|
</jahat-pagination>
|
|||
|
<!-- #endregion pagination -->
|
|||
|
</div>
|
|||
|
</template>
|
|||
|
|
|||
|
<script>
|
|||
|
import { VueDraggableNext } from "vue-draggable-next";
|
|||
|
|
|||
|
// import draggable from "vuedraggable";
|
|||
|
// import VueDragDrop from "vue-drag-drop";
|
|||
|
import { mapActions, mapState } from "pinia";
|
|||
|
|
|||
|
// const { moment } = useNuxtApp();
|
|||
|
// import moment from "jalaliMoment";
|
|||
|
// import tableAndListMixin from "~/mixins/tableAndListMixin";
|
|||
|
|
|||
|
/**
|
|||
|
* @vue-prop {String} [canAddSubject=""] - مجوز افزودن موضوع
|
|||
|
* @vue-prop {String} [canSeeSummary=""] - مجوز مشاهده خلاصه
|
|||
|
* @vue-prop {String} [canSeeDetails=""] - مجوز مشاهده جزئیات
|
|||
|
* @vue-prop {String} [canDelete=""] - مجوز حذف
|
|||
|
* @vue-prop {String} [canEdit=""] - مجوز ویرایش
|
|||
|
* @vue-prop {Boolean} [hasFooter=false] - مشخص کنید آیا فوتر وجود دارد یا خیر
|
|||
|
* @vue-prop {Boolean} [hasSubject=false] - مشخص کنید آیا موضوع وجود دارد یا خیر
|
|||
|
* @vue-prop {Boolean} [hasEdit=true] - مشخص کنید آیا قابلیت ویرایش وجود دارد یا خیر
|
|||
|
* @vue-prop {Boolean} [hasDelete=true] - مشخص کنید آیا قابلیت حذف وجود دارد یا خیر
|
|||
|
* @vue-prop {Boolean} [hasSummary=true] - مشخص کنید آیا خلاصه وجود دارد یا خیر
|
|||
|
* @vue-prop {Boolean} [hasDetails=false] - مشخص کنید آیا جزئیات وجود دارد یا خیر
|
|||
|
* @vue-prop {Boolean} [hasSearch=false] - مشخص کنید آیا قابلیت جستجو وجود دارد یا خیر
|
|||
|
* @vue-prop {Boolean} [showActions=true] - مشخص کنید آیا اقدامات نمایش داده شود یا خیر
|
|||
|
* @vue-prop {Boolean} [isDraggable=false] - مشخص کنید آیا قابلیت کشیدن و رها کردن وجود دارد یا خیر
|
|||
|
* @vue-prop {Boolean} [isSortable=false] - مشخص کنید آیا قابلیت مرتب سازی وجود دارد یا خیر
|
|||
|
* @vue-prop {Boolean} [showHeaderSortButton=false] - مشخص کنید آیا قابلیت مرتب سازی در هدر وجود دارد یا خیر
|
|||
|
* @vue-prop {Array} [items=[]] - آیتمها
|
|||
|
* @vue-prop {Array} [tableColumns=[]] - ستونهای جدول
|
|||
|
* @vue-prop {Boolean} [fetchingData=false] - مشخص کنید آیا دادهها در حال دریافت هستند یا خیر
|
|||
|
* @vue-prop {Object} [sortingInfo] - اطلاعات مربوط به مرتب سازی
|
|||
|
* @vue-prop {Number} [sortingInfo.sortby="id"] - فیلد براساس آن مرتب سازی انجام شود
|
|||
|
* @vue-prop {String} [sortingInfo.sortorder="desc"] - ترتیب مرتب سازی
|
|||
|
* @vue-prop {Object} [paginationInfo] - اطلاعات مربوط به صفحه بندی
|
|||
|
* @vue-prop {Number} [paginationInfo.pages=0] - تعداد کل صفحات
|
|||
|
* @vue-prop {Number} [paginationInfo.total=0] - تعداد کل آیتمها
|
|||
|
* @vue-prop {Number} [paginationInfo.page=1] - صفحه فعلی
|
|||
|
* @vue-prop {Number} [paginationInfo.offset=0] - آفست (صفحه * تعداد در هر صفحه)
|
|||
|
* @vue-prop {Number} [paginationInfo.limit=10] - تعداد در هر صفحه
|
|||
|
* @vue-prop {Array} [tableActions] - اقدامات مربوط به جدول
|
|||
|
* @vue-prop {Boolean} [tableActions.showOutside=true] - نمایش خارج از جدول
|
|||
|
* @vue-prop {Boolean} [tableActions.show=true] - نمایش
|
|||
|
* @vue-prop {String} [tableActions.icon="default"] - آیکون
|
|||
|
* @vue-prop {String} [tableActions.title=""] - عنوان
|
|||
|
* @vue-prop {Object} [tableActions.to] - مقصد لینک
|
|||
|
* @vue-prop {Boolean} [tableActions.selected=false] - انتخاب شده یا نه
|
|||
|
* @vue-prop {Boolean} [tableActions.disabled=false] - غیرفعال
|
|||
|
* @vue-prop {String} [tableActions.howToOpen=""] - نحوه باز شدن لینک
|
|||
|
* @vue-prop {String} [tableActions.href=""] - لینک
|
|||
|
* @vue-prop {String} [tableActions.class=""] - کلاس
|
|||
|
* @vue-prop {String} [tableActions.action=""] - اقدام
|
|||
|
* @vue-prop {String} [tableActions.can=""] - مجوز
|
|||
|
*
|
|||
|
* @vue-data {String} [prevColumn=undefined] - ستونی که قبل از حالت فعلی مرتب سازی شده بود
|
|||
|
* @vue-data {Number} [sortOrderIndicator=0] - نشانگر ترتیب مرتب سازی
|
|||
|
* @vue-data {Object} [sortOrder] - ترتیب مرتب سازی ممکن (asc, desc, undefined)
|
|||
|
* @vue-data {Boolean} [showDetailsPanel=false] - نشان دادن پنل جزئیات
|
|||
|
* @vue-data {Boolean} [dragging=false] - در حال کشیدن
|
|||
|
* @vue-data {Number} [page=1] - شماره صفحه فعلی
|
|||
|
* @vue-data {Number} [typingTimer=0] - تایمر تایپ
|
|||
|
* @vue-data {Number} [doneTypingInterval=1000] - فاصله زمانی برای اتمام تایپ
|
|||
|
* @vue-data {Object} [selectedItem] - آیتم انتخاب شده
|
|||
|
* @vue-data {Object} [selectedItemClone] - کپی از آیتم انتخاب شده
|
|||
|
* @vue-data {Array} [cloneItems=[]] - آیتمهای کپی
|
|||
|
* @vue-data {Number} [prevSelectedItemIndex=undefined] - اندیس آیتم انتخاب شده قبلی
|
|||
|
* @vue-data {Object} [searchForm] - فرم جستجو
|
|||
|
* @vue-data {String} [searchForm.query=""] - متن جستجو
|
|||
|
*
|
|||
|
* @vue-computed {Function} [mapState.selectedProjectGetter] - مپ کردن گترهای مورد نیاز از استور ماژول "list" به کامپوتد فعلی
|
|||
|
* @vue-computed {Object} [tHead] - سرتیتر جدول
|
|||
|
* @vue-computed {Array} [tBody] - بدنه جدول
|
|||
|
*
|
|||
|
* @vue-event {Function} mapActions.SET_LIST_ENTITY - تنظیم لیست انتیتی
|
|||
|
* @vue-event {Function} mapActions.SET_ITEM_ENTITY - تنظیم ایتم انتیتی
|
|||
|
* @vue-event {Function} mapActions.SET_SELECTED_ITEM -
|
|||
|
*
|
|||
|
*/
|
|||
|
|
|||
|
export default {
|
|||
|
props: {
|
|||
|
canAddSubject: {
|
|||
|
default: "",
|
|||
|
type: String,
|
|||
|
},
|
|||
|
canSeeSummary: {
|
|||
|
default: "",
|
|||
|
type: String,
|
|||
|
},
|
|||
|
canSeeDetails: {
|
|||
|
default: "",
|
|||
|
type: String,
|
|||
|
},
|
|||
|
canDelete: {
|
|||
|
default: "",
|
|||
|
type: String,
|
|||
|
},
|
|||
|
canEdit: {
|
|||
|
default: "",
|
|||
|
type: String,
|
|||
|
},
|
|||
|
|
|||
|
hasCheckbox: {
|
|||
|
default: false,
|
|||
|
type: Boolean,
|
|||
|
},
|
|||
|
hasFooter: {
|
|||
|
default: false,
|
|||
|
type: Boolean,
|
|||
|
},
|
|||
|
hasSubject: {
|
|||
|
default: false,
|
|||
|
type: Boolean,
|
|||
|
},
|
|||
|
hasEdit: {
|
|||
|
default: true,
|
|||
|
type: Boolean,
|
|||
|
},
|
|||
|
hasDelete: {
|
|||
|
default: true,
|
|||
|
type: Boolean,
|
|||
|
},
|
|||
|
hasSummary: {
|
|||
|
default: true,
|
|||
|
type: Boolean,
|
|||
|
},
|
|||
|
hasDetails: {
|
|||
|
default: false,
|
|||
|
type: Boolean,
|
|||
|
},
|
|||
|
hasSearch: {
|
|||
|
default: false,
|
|||
|
type: Boolean,
|
|||
|
},
|
|||
|
|
|||
|
showActions: {
|
|||
|
default: true,
|
|||
|
type: Boolean,
|
|||
|
},
|
|||
|
isDraggable: {
|
|||
|
default: false,
|
|||
|
type: Boolean,
|
|||
|
},
|
|||
|
isSortable: {
|
|||
|
default: false,
|
|||
|
type: Boolean,
|
|||
|
},
|
|||
|
showHeaderSortButton: {
|
|||
|
default: true,
|
|||
|
type: Boolean,
|
|||
|
},
|
|||
|
isColumnHighlightActive: {
|
|||
|
default: false,
|
|||
|
type: Boolean,
|
|||
|
},
|
|||
|
hasPagination: {
|
|||
|
default: true,
|
|||
|
type: Boolean,
|
|||
|
},
|
|||
|
items: {
|
|||
|
default() {
|
|||
|
return [];
|
|||
|
},
|
|||
|
},
|
|||
|
tableColumns: {
|
|||
|
default() {
|
|||
|
return [];
|
|||
|
},
|
|||
|
},
|
|||
|
fetchingData: {
|
|||
|
default: false,
|
|||
|
type: Boolean,
|
|||
|
},
|
|||
|
activeTableColumnTitle: {
|
|||
|
default: false,
|
|||
|
type: Boolean,
|
|||
|
},
|
|||
|
sortingInfo: {
|
|||
|
default() {
|
|||
|
return {
|
|||
|
sortby: "id",
|
|||
|
sortorder: "desc",
|
|||
|
};
|
|||
|
},
|
|||
|
},
|
|||
|
paginationInfo: {
|
|||
|
default() {
|
|||
|
return {
|
|||
|
pages: 0,
|
|||
|
total: 0,
|
|||
|
page: 1,
|
|||
|
offset: 0, // page * per_page
|
|||
|
limit: 10, //per_page
|
|||
|
};
|
|||
|
},
|
|||
|
},
|
|||
|
tableActions: {
|
|||
|
default() {
|
|||
|
return [
|
|||
|
{
|
|||
|
showOutside: true,
|
|||
|
show: true,
|
|||
|
icon: "tavasi tavasi-Component-71--1",
|
|||
|
title: "جزییات",
|
|||
|
to: {
|
|||
|
name: "undefined",
|
|||
|
},
|
|||
|
selected: false,
|
|||
|
disabled: false,
|
|||
|
howToOpen: "",
|
|||
|
href: "",
|
|||
|
class: "btn show-detail-btn -rotate-180",
|
|||
|
action: "showDetails",
|
|||
|
can: "",
|
|||
|
},
|
|||
|
{
|
|||
|
showOutside: false,
|
|||
|
|
|||
|
show: true,
|
|||
|
icon: "default",
|
|||
|
title: "ویرایش",
|
|||
|
to: {
|
|||
|
name: "undefined",
|
|||
|
},
|
|||
|
selected: false,
|
|||
|
disabled: false,
|
|||
|
howToOpen: "",
|
|||
|
href: "",
|
|||
|
class: "edit-btn",
|
|||
|
action: "edit-table-item",
|
|||
|
can: "item-info_edit",
|
|||
|
},
|
|||
|
{
|
|||
|
showOutside: false,
|
|||
|
|
|||
|
show: true,
|
|||
|
icon: "default",
|
|||
|
title: "حذف",
|
|||
|
to: {
|
|||
|
name: "undefined",
|
|||
|
},
|
|||
|
selected: false,
|
|||
|
disabled: false,
|
|||
|
howToOpen: "",
|
|||
|
href: "",
|
|||
|
class: "delete-btn",
|
|||
|
action: "delete-table-item",
|
|||
|
can: "item-list_delete",
|
|||
|
},
|
|||
|
];
|
|||
|
},
|
|||
|
},
|
|||
|
},
|
|||
|
emits: [
|
|||
|
"open-form",
|
|||
|
"delete-table-item",
|
|||
|
"reset-form",
|
|||
|
"search",
|
|||
|
"show-summary",
|
|||
|
"edit-table-item",
|
|||
|
"page-changed",
|
|||
|
"show-details",
|
|||
|
],
|
|||
|
mounted() {
|
|||
|
// this.cloneItems = structuredClone(this.items);
|
|||
|
this.cloneItems = this.items;
|
|||
|
if (this.cloneItems)
|
|||
|
this.cloneItems?.forEach((item, index) => {
|
|||
|
if ("_source" in item)
|
|||
|
this.cloneItems[index] = { ...item, ...item._source };
|
|||
|
});
|
|||
|
},
|
|||
|
watch: {
|
|||
|
items: {
|
|||
|
handler(newVal) {
|
|||
|
// this.cloneItems = structuredClone(newVal);
|
|||
|
this.cloneItems = newVal;
|
|||
|
|
|||
|
if (!this.cloneItems) return;
|
|||
|
|
|||
|
this.cloneItems?.forEach((item, index) => {
|
|||
|
if ("_source" in item)
|
|||
|
this.cloneItems[index] = { ...item, ...item._source };
|
|||
|
});
|
|||
|
},
|
|||
|
nested: true,
|
|||
|
immediate: true,
|
|||
|
},
|
|||
|
},
|
|||
|
data() {
|
|||
|
return {
|
|||
|
// sort
|
|||
|
prevSelectedItemIndex: undefined,
|
|||
|
prevColumn: undefined,
|
|||
|
|
|||
|
showDetailsPanel: false,
|
|||
|
dragging: false,
|
|||
|
|
|||
|
sortOrderIndicator: 0,
|
|||
|
page: 1,
|
|||
|
typingTimer: 0,
|
|||
|
doneTypingInterval: 1000,
|
|||
|
|
|||
|
sortOrder: {
|
|||
|
0: "asc",
|
|||
|
1: "desc",
|
|||
|
2: undefined,
|
|||
|
},
|
|||
|
selectedItem: {},
|
|||
|
selectedItemClone: {},
|
|||
|
searchForm: {
|
|||
|
query: "",
|
|||
|
},
|
|||
|
|
|||
|
myTableActions: this.tableActions,
|
|||
|
|
|||
|
cloneItems: [],
|
|||
|
};
|
|||
|
},
|
|||
|
computed: {
|
|||
|
...mapState("list", ["selectedProjectGetter"]),
|
|||
|
tHead() {
|
|||
|
return Object.values(this.tableColumns);
|
|||
|
},
|
|||
|
tBody() {
|
|||
|
return Object.keys(this.tableColumns);
|
|||
|
},
|
|||
|
},
|
|||
|
methods: {
|
|||
|
/**
|
|||
|
* محاسبه مقدار در جدول براساس کلید مشخص شده و در صورت نیاز کوتاه کردن آن.
|
|||
|
* @param {Object} rowItem - آیتم های ردیف جدول.
|
|||
|
* @param {String} key - کلید مورد نظر برای بازیابی مقدار.
|
|||
|
* @param {Number} [trancate_word=0] - تعداد کلمات حداکثر برای کوتاه کردن مقدار.
|
|||
|
* @returns {String} - مقدار محاسبه شده .
|
|||
|
*/
|
|||
|
getCellValue(rowItem, column) {
|
|||
|
let key = column.key;
|
|||
|
let trancate_word = column?.trancate_word;
|
|||
|
let value = "";
|
|||
|
|
|||
|
// ///////////////////////////////
|
|||
|
if ("highlight" in rowItem && key in rowItem.highlight) {
|
|||
|
if (Array.isArray(rowItem.highlight[key]))
|
|||
|
value = rowItem.highlight[key][0];
|
|||
|
else value = rowItem.highlight[key];
|
|||
|
return value;
|
|||
|
}
|
|||
|
|
|||
|
////////////////////////////////
|
|||
|
// برای مقادیر فیلد خاص از یک آبجکت مثل :
|
|||
|
// other_info__full_path == other_info -- > full_path
|
|||
|
key = key.replace("__", ".");
|
|||
|
if (key.includes(".")) {
|
|||
|
let keys = key.split(".");
|
|||
|
for (let i = 0; i < keys.length - 1; i++)
|
|||
|
if (rowItem[keys[i]]) rowItem = rowItem[keys[i]];
|
|||
|
|
|||
|
key = keys[keys.length - 1];
|
|||
|
}
|
|||
|
/////////////////////////////////
|
|||
|
|
|||
|
if (rowItem[key]) value = rowItem[key];
|
|||
|
|
|||
|
if (trancate_word && trancate_word > 0) {
|
|||
|
let words = value.split(" ");
|
|||
|
if (words.length > trancate_word) {
|
|||
|
value = words.slice(0, trancate_word).join(" ");
|
|||
|
value += "...";
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// برای وقتی که با یک محاسبه خاص مقدار برگردانده شود ، مثلا تعداد یک آرایه یا طول یک متن
|
|||
|
if (column?.process) {
|
|||
|
if (column.process == "len") return value.length;
|
|||
|
else if (column.process == "convert_date") {
|
|||
|
let date = new Date(value);
|
|||
|
if (value.toString().length == 10) {
|
|||
|
date = new Date(value * 1000);
|
|||
|
}
|
|||
|
|
|||
|
return date.toLocaleDateString("fa-IR");
|
|||
|
// return moment(value).format("jYYYY/jMM/jDD");
|
|||
|
} else if (column.process == "convert_gdate") {
|
|||
|
let date = new Date(value);
|
|||
|
return date.toLocaleDateString("fa-IR");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (column?.colors) {
|
|||
|
if (value in column?.colors) {
|
|||
|
value =
|
|||
|
`<span style="color:${column?.colors[value]}">` + value + "</span>";
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return value;
|
|||
|
},
|
|||
|
|
|||
|
...mapActions("list", ["SET_SELECTED_ITEM", "SET_LIST"]),
|
|||
|
...mapActions("entity", ["SET_ITEM_ENTITY", "SET_LIST_ENTITY"]),
|
|||
|
handlerActions(item, rowAction, index, items) {
|
|||
|
this.$emit("actions", {
|
|||
|
item: item,
|
|||
|
index: index,
|
|||
|
items: items,
|
|||
|
rowAction: rowAction,
|
|||
|
});
|
|||
|
// if (rowAction.key == "tbookmark")
|
|||
|
// this.updateListAnswer(index, "tbookmark", 0);
|
|||
|
},
|
|||
|
updateListAnswer(index, key, value) {
|
|||
|
// if (index in this.cloneItems) {
|
|||
|
// if (key in this.cloneItems[index]["_source"])
|
|||
|
// this.cloneItems[index]["_source"][key] = value;
|
|||
|
// }
|
|||
|
},
|
|||
|
/**
|
|||
|
* بررسی غیرفعال بودن آیتم.
|
|||
|
* @param {Object} item - آیتم مورد نظر برای بررسی کلاس غیرفعال بودن
|
|||
|
* @returns {boolean} - نتیجه بررسی کلاس غیرفعال بودن آیتم
|
|||
|
*/
|
|||
|
getUnactiveClass(item) {
|
|||
|
let key = "is_active";
|
|||
|
|
|||
|
if ("islock" in item) key = "islock";
|
|||
|
|
|||
|
if (item[key] == undefined) return false;
|
|||
|
else if (item[key] == 1) return true;
|
|||
|
else if (item[key] == 0) return false;
|
|||
|
},
|
|||
|
/**
|
|||
|
* مرتبسازی بر اساس ستون انتخاب شده.
|
|||
|
* @param {Object} col - ستون برای مرتبسازی
|
|||
|
*/
|
|||
|
sortBy(col) {
|
|||
|
// تنظیم مجدد به حالت صعودی اگر شماره نشانگر بیشتر از یک باشد
|
|||
|
if (this.sortOrderIndicator > 1) this.sortOrderIndicator = 0;
|
|||
|
|
|||
|
// اگر ستون جدید انتخاب شدهاست، سفارش مرتبسازی ستون قبلی را پاک کنید و نشانگر مرتبسازی را دوباره تنظیم کنید
|
|||
|
if (this.prevColumn && col.key != this.prevColumn.key) {
|
|||
|
this.prevColumn.sortorder = undefined;
|
|||
|
this.sortOrderIndicator = 0;
|
|||
|
}
|
|||
|
|
|||
|
// به روز رسانی ستون جدول قبلی
|
|||
|
this.prevColumn = col;
|
|||
|
|
|||
|
// به روز رسانی سفارش مرتبسازی فعلی ستون
|
|||
|
col["sortorder"] = this.sortOrder[this.sortOrderIndicator];
|
|||
|
|
|||
|
// ارسال اطلاعات مرتبسازی به کامپوننت والد
|
|||
|
this.$emit("sort-changed", {
|
|||
|
sortby: col.key,
|
|||
|
sortorder: this.sortOrder[this.sortOrderIndicator],
|
|||
|
});
|
|||
|
|
|||
|
// افزایش یک واحد به شمارنده نشانگر مرتبسازی
|
|||
|
this.sortOrderIndicator += 1;
|
|||
|
},
|
|||
|
|
|||
|
/**
|
|||
|
* انجام عملیات مربوط به دکمههای خارج از context menu (showOutside == true).
|
|||
|
* @param {string} action - عملیات مورد نظر
|
|||
|
* @param {number} index - اندیس مورد نظر
|
|||
|
*/
|
|||
|
methodName(action, data) {
|
|||
|
// event.preventDefault();
|
|||
|
if (action && action != "actions") {
|
|||
|
try {
|
|||
|
this[action](data);
|
|||
|
} catch (err) {
|
|||
|
if (action == "on-linked-title-click") this.$emit(action, data);
|
|||
|
// "edit-table-item" , "delete-table-item"
|
|||
|
else this.$emit(action, data.index);
|
|||
|
}
|
|||
|
} else {
|
|||
|
//این حالت از بک اند می آید معمولا
|
|||
|
|
|||
|
data["items"] = this.cloneItems;
|
|||
|
this.$emit("actions", data);
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
/**
|
|||
|
* نمایش صفحه متنی.
|
|||
|
* @param {number} index - اندیس مورد نظر
|
|||
|
*/
|
|||
|
showText(index) {
|
|||
|
if (!this.cloneItems) return;
|
|||
|
|
|||
|
const clickedItem = this.markActive(index);
|
|||
|
|
|||
|
this.cloneItems?.forEach((element) => {
|
|||
|
element["_id"] = element["id_store"];
|
|||
|
});
|
|||
|
|
|||
|
this.SET_ITEM_ENTITY(this.cloneItems[index]);
|
|||
|
this.SET_LIST_ENTITY(this.cloneItems);
|
|||
|
localStorage.setItem("myList", JSON.stringify(this.cloneItems));
|
|||
|
localStorage.setItem("myItem", JSON.stringify(this.cloneItems[index]));
|
|||
|
|
|||
|
let pageTitle = clickedItem.title ?? "بدون عنوان";
|
|||
|
pageTitle = pageTitle.replaceAll(" ", "-");
|
|||
|
|
|||
|
const routeData = this.$router.resolve({
|
|||
|
name: "navigation",
|
|||
|
params: {
|
|||
|
id: clickedItem.id_store,
|
|||
|
key: this.selectedProjectGetter.index_name,
|
|||
|
},
|
|||
|
query: {
|
|||
|
searchtext: this.pageTitle ?? undefined,
|
|||
|
},
|
|||
|
});
|
|||
|
window.open(routeData.href, "_blank");
|
|||
|
},
|
|||
|
|
|||
|
/**
|
|||
|
* نمایش جزئیات آیتم انتخاب شده.
|
|||
|
* @param {number} index - اندیس مورد نظر
|
|||
|
*/
|
|||
|
showDetails(index) {
|
|||
|
this.SET_SELECTED_ITEM(this.markActive(index));
|
|||
|
this.showDetailsPanel = !this.showDetailsPanel;
|
|||
|
|
|||
|
this.$emit("show-details", {
|
|||
|
show: this.showDetailsPanel,
|
|||
|
rowItem: this.markActive(index),
|
|||
|
index,
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
/**
|
|||
|
* نمایش فرم موضوع در صفحه لیست.
|
|||
|
* @param {number} index - اندیس مورد نظر
|
|||
|
*/
|
|||
|
showSubjectForm(index) {
|
|||
|
this.$emit("show-subject-form", { rowItem: this.markActive(index) });
|
|||
|
},
|
|||
|
|
|||
|
/**
|
|||
|
* حذف آیتم با شناسه مشخص شده.
|
|||
|
* @param {string} itemId - شناسه آیتم برای حذف
|
|||
|
*/
|
|||
|
deleteItem(itemId) {
|
|||
|
this.$emit("delete-item", itemId);
|
|||
|
},
|
|||
|
|
|||
|
/**
|
|||
|
* تغییر وضعیت پنل.
|
|||
|
* @param {number} index - اندیس مورد نظر
|
|||
|
*/
|
|||
|
togglePanel(index = undefined) {
|
|||
|
this.$emit("open-form", this.markActive(index));
|
|||
|
},
|
|||
|
|
|||
|
/**
|
|||
|
* تنظیم مجدد فرم.
|
|||
|
*/
|
|||
|
resetForm() {
|
|||
|
this.searchForm.query = "";
|
|||
|
this.$emit("reset-form");
|
|||
|
},
|
|||
|
|
|||
|
/**
|
|||
|
* جستجو با استفاده از فرم.
|
|||
|
*/
|
|||
|
search() {
|
|||
|
clearTimeout(this.typingTimer);
|
|||
|
|
|||
|
this.typingTimer = setTimeout(() => {
|
|||
|
this.$emit("search", this.searchForm.query);
|
|||
|
}, this.doneTypingInterval);
|
|||
|
},
|
|||
|
|
|||
|
/**
|
|||
|
* اعمال عملیاتی هنگامی که دکمه کیبورد فشرده شدهاست.
|
|||
|
*/
|
|||
|
onKeyDown() {
|
|||
|
clearTimeout(this.typingTimer);
|
|||
|
},
|
|||
|
|
|||
|
/**
|
|||
|
* تغییر در محدوده صفحهبندی.
|
|||
|
* @param {Object} paging - اطلاعات مربوط به صفحهبندی
|
|||
|
*/
|
|||
|
limitChanged(paging) {
|
|||
|
this.$emit("page-limit-changed", paging);
|
|||
|
},
|
|||
|
|
|||
|
/**
|
|||
|
* فراخوانی بازخوردی پس از کلیک بر روی صفحهبندی.
|
|||
|
* @param {Object} paging - اطلاعات مربوط به صفحهبندی
|
|||
|
*/
|
|||
|
clickCallback(paging) {
|
|||
|
this.$emit("page-changed", paging);
|
|||
|
},
|
|||
|
|
|||
|
/**
|
|||
|
* علامت گذاری آیتم فعال.
|
|||
|
* @param {number} index - اندیس مورد نظر
|
|||
|
* @returns {Object} - کپی آیتم فعال
|
|||
|
*/
|
|||
|
markActive(index) {
|
|||
|
if (this.prevSelectedItemIndex !== undefined) {
|
|||
|
this.cloneItems[this.prevSelectedItemIndex]["active"] = false;
|
|||
|
}
|
|||
|
|
|||
|
this.prevSelectedItemIndex = index;
|
|||
|
this.cloneItems[index]["active"] = true;
|
|||
|
|
|||
|
// this.selectedItemClone = structuredClone(this.cloneItems[index]);
|
|||
|
this.selectedItemClone = this.cloneItems[index];
|
|||
|
return this.selectedItemClone;
|
|||
|
},
|
|||
|
/**
|
|||
|
* شروع هندل کردن draggable
|
|||
|
* @param {Event} e - رویداد مربوطه
|
|||
|
*/
|
|||
|
handleDragStart(e) {
|
|||
|
e.originalEvent.dataTransfer.setData(
|
|||
|
"text",
|
|||
|
e.clone.children[1].innerHTML
|
|||
|
);
|
|||
|
e.originalEvent.dataTransfer.setDragImage(new Image(), 0, 0);
|
|||
|
},
|
|||
|
clickManagementTableColumns(tableColumn) {
|
|||
|
if (this.showHeaderSortButton) {
|
|||
|
this.sortBy(tableColumn);
|
|||
|
} else {
|
|||
|
if (this.activeTableColumnTitle) {
|
|||
|
this.$emit("click-table-column-title", tableColumn);
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
},
|
|||
|
components: {
|
|||
|
draggable: VueDraggableNext,
|
|||
|
},
|
|||
|
};
|
|||
|
</script>
|
|||
|
|
|||
|
<style lang="scss">
|
|||
|
.my-handle {
|
|||
|
cursor: move;
|
|||
|
}
|
|||
|
.sort-indicator {
|
|||
|
.tavasi-sort {
|
|||
|
opacity: 0.3;
|
|||
|
}
|
|||
|
&:hover .tavasi-chevron-sort {
|
|||
|
opacity: 0.5;
|
|||
|
}
|
|||
|
|
|||
|
.tavasi-desc,
|
|||
|
.tavasi-chevron-sort-desc {
|
|||
|
display: none;
|
|||
|
}
|
|||
|
.tavasi-asce,
|
|||
|
.tavasi-chevron-sort-asce {
|
|||
|
display: none;
|
|||
|
}
|
|||
|
|
|||
|
&:hover .tavasi-sort {
|
|||
|
opacity: 0.5;
|
|||
|
}
|
|||
|
|
|||
|
&:hover .tavasi-chevron-sort {
|
|||
|
opacity: 1;
|
|||
|
}
|
|||
|
|
|||
|
&.desc {
|
|||
|
.tavasi-sort,
|
|||
|
.tavasi-chevron-sort {
|
|||
|
display: none;
|
|||
|
}
|
|||
|
.tavasi-desc,
|
|||
|
.tavasi-chevron-sort-desc {
|
|||
|
display: inline;
|
|||
|
}
|
|||
|
.tavasi-asce,
|
|||
|
.tavasi-chevron-sort-asce {
|
|||
|
display: none;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
&.asc {
|
|||
|
.tavasi-sort,
|
|||
|
.tavasi-chevron-sort {
|
|||
|
display: none;
|
|||
|
}
|
|||
|
.tavasi-desc,
|
|||
|
.tavasi-chevron-sort-desc {
|
|||
|
display: none;
|
|||
|
}
|
|||
|
.tavasi-asce,
|
|||
|
.tavasi-chevron-sort-asce {
|
|||
|
display: inline;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
.show-text-decoration {
|
|||
|
&:hover {
|
|||
|
text-decoration: underline;
|
|||
|
}
|
|||
|
}
|
|||
|
.thead {
|
|||
|
z-index: 1;
|
|||
|
}
|
|||
|
|
|||
|
// .sortable-ghost {
|
|||
|
// visibility: hidden;
|
|||
|
// opacity: 0;
|
|||
|
// display: none;
|
|||
|
// }
|
|||
|
// .sortable-choosen {
|
|||
|
// visibility: hidden;
|
|||
|
// opacity: 0;
|
|||
|
// display: none;
|
|||
|
// }
|
|||
|
|
|||
|
.action-column {
|
|||
|
svg {
|
|||
|
font-size: 0.8rem !important;
|
|||
|
}
|
|||
|
span {
|
|||
|
font-size: 1.3rem !important;
|
|||
|
}
|
|||
|
&:hover {
|
|||
|
color: var(--primary-color) !important;
|
|||
|
}
|
|||
|
}
|
|||
|
.cursor-title {
|
|||
|
cursor: pointer;
|
|||
|
}
|
|||
|
.column-highlight-active {
|
|||
|
th,
|
|||
|
td {
|
|||
|
border: 1px solid #ccc;
|
|||
|
text-align: center;
|
|||
|
padding: 8px;
|
|||
|
}
|
|||
|
}
|
|||
|
</style>
|