feat: complete admin panel and backend admin api migration
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<!-- Ported from legacy views/customer/omde/BankerList.vue — differs from OmdeList by title + model url only (verified: 18 diff lines, all whitespace besides those two). -->
|
||||
<OmdeCustomerList :title="title" :model="model" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import OmdeCustomerList from './OmdeCustomerList.vue';
|
||||
import { buildOmdeListModel, OMDE_SOURCES } from './omdeListModel';
|
||||
|
||||
const title = OMDE_SOURCES.banker.title;
|
||||
const model = buildOmdeListModel('banker');
|
||||
</script>
|
||||
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<!-- Ported from legacy views/customer/omde/OmdeArchivedList.vue. -->
|
||||
<OmdeCustomerList :title="title" :model="model" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import OmdeCustomerList from './OmdeCustomerList.vue';
|
||||
import { buildOmdeListModel, OMDE_SOURCES } from './omdeListModel';
|
||||
|
||||
const title = OMDE_SOURCES.archived.title;
|
||||
const model = buildOmdeListModel('archived');
|
||||
</script>
|
||||
@@ -0,0 +1,414 @@
|
||||
<template>
|
||||
<!--
|
||||
Shared implementation of the five legacy wholesale-customer lists
|
||||
(OmdeList, BankerList, ShopkeeperList, OmdeNonVerifiedList, OmdeArchivedList).
|
||||
|
||||
Legacy shipped these as five ~615-line files. Verified by diff, they differ by:
|
||||
- ShopkeeperList vs OmdeList: 4 lines (card title + model import ONLY)
|
||||
- BankerList: 18 lines, all whitespace/formatting except the same two
|
||||
- OmdeArchived: 24 lines, same plus method ordering
|
||||
- OmdeNonVerified: 34 lines, same plus two genuine quirks (see props below)
|
||||
Per the agreed decision, behaviour and appearance are preserved exactly while
|
||||
the implementation lives once, parameterised by props.
|
||||
-->
|
||||
<div id="page-user-list">
|
||||
<a-card :title="title">
|
||||
<div v-if="query && query.count && query.total">
|
||||
{{ query.total }} رکورد یافت شد -
|
||||
<span class="text-danger">{{ query.count }} رکورد غیر فعال</span>
|
||||
</div>
|
||||
|
||||
<XTable ref="tbl" :model="model" @query="setCount">
|
||||
<template #city="{ record }">
|
||||
<div v-if="record.city && record.city.title">{{ record.city.title }}</div>
|
||||
<div v-else>-</div>
|
||||
</template>
|
||||
|
||||
<template #sts="{ record }">
|
||||
{{ record.sts_str }}
|
||||
</template>
|
||||
|
||||
<template #active="{ record }">
|
||||
<a-tag :color="record.active === 1 ? 'green' : 'red'">
|
||||
{{ record.active === 1 ? 'فعال' : 'غیر فعال' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<template #opt="{ record }">
|
||||
<div class="flex">
|
||||
<a-dropdown>
|
||||
<div class="cursor-pointer flex items-center">
|
||||
<DownOutlined />
|
||||
<SettingOutlined class="cursor-pointer w-5 ml-1" />
|
||||
</div>
|
||||
<template #overlay>
|
||||
<a-menu @click="handleMenuClick($event, record.id)">
|
||||
<a-menu-item
|
||||
v-if="hasPermission('persons.list.client.omde.edit')"
|
||||
key="1"
|
||||
@click="toEdit(record.id)"
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<EditOutlined class="cursor-pointer w-5 ml-1" />
|
||||
<div>ویرایش</div>
|
||||
</div>
|
||||
</a-menu-item>
|
||||
|
||||
<a-menu-item
|
||||
v-if="hasPermission('persons.list.client.omde.view')"
|
||||
key="2"
|
||||
@click="toView(record.id)"
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<EyeOutlined class="cursor-pointer w-5 ml-1" />
|
||||
<div>مشاهده اطلاعات</div>
|
||||
</div>
|
||||
</a-menu-item>
|
||||
|
||||
<a-menu-item
|
||||
v-if="hasPermission('persons.list.client.omde.view.orders')"
|
||||
key="3"
|
||||
@click="viewOrder(record)"
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<UnorderedListOutlined class="cursor-pointer w-5 ml-1" />
|
||||
<div>مشاهده سفارش</div>
|
||||
</div>
|
||||
</a-menu-item>
|
||||
|
||||
<a-menu-item
|
||||
v-if="hasPermission('persons.list.client.omde.view.documents')"
|
||||
key="4"
|
||||
@click="viewCash(record)"
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<FileTextOutlined class="cursor-pointer w-5 ml-1" />
|
||||
<div>سند های مالی</div>
|
||||
</div>
|
||||
</a-menu-item>
|
||||
|
||||
<a-menu-item v-if="hasPermission('persons.list.client.omde.able')" key="7">
|
||||
<a-popconfirm
|
||||
ok-text="بله"
|
||||
cancel-text="خیر"
|
||||
title="آیا مطمئن هستید؟"
|
||||
@confirm="toggleArchive(record.id)"
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<CloudDownloadOutlined class="cursor-pointer w-5 ml-1" />
|
||||
<div>بایگانی کردن</div>
|
||||
</div>
|
||||
</a-popconfirm>
|
||||
</a-menu-item>
|
||||
|
||||
<!-- step one -->
|
||||
<a-menu-item key="8">
|
||||
<a-popconfirm
|
||||
:ok-text="record.sts_verify_step_one === 1 ? 'تایید' : 'بله'"
|
||||
:cancel-text="record.sts_verify_step_one === 1 ? 'رد' : 'خیر'"
|
||||
:title="
|
||||
record.sts_verify_step_one === 1
|
||||
? 'وضعیت تایید اولیه مشتری به چه نحوی تغییر یابد؟'
|
||||
: 'آیا از تغییر وضعیت مشتری اطمینان دارید؟'
|
||||
"
|
||||
@confirm="onConfirmStepOne(record)"
|
||||
@cancel="onCancelStepOne(record)"
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<component
|
||||
:is="stepIconComponent(getStepOneIcon(record))"
|
||||
class="cursor-pointer w-5 ml-1"
|
||||
:class="stepIconClass(getStepOneIcon(record))"
|
||||
/>
|
||||
<div>{{ getStepOneText(record) }}</div>
|
||||
</div>
|
||||
</a-popconfirm>
|
||||
</a-menu-item>
|
||||
|
||||
<!-- step two: unlocked only once step one is ACCEPTED (=== 2) -->
|
||||
<a-menu-item :disabled="record.sts_verify_step_one !== 2" key="9">
|
||||
<a-popconfirm
|
||||
:ok-text="record.sts_verify_step_two === 1 ? 'تایید' : 'بله'"
|
||||
:cancel-text="record.sts_verify_step_two === 1 ? 'رد' : 'خیر'"
|
||||
:title="
|
||||
record.sts_verify_step_two === 1
|
||||
? 'وضعیت تایید سفارش مشتری به چه نحوی تغییر یابد؟'
|
||||
: 'آیا از تغییر وضعیت سفارش مشتری اطمینان دارید؟'
|
||||
"
|
||||
@confirm="onConfirmStepTwo(record)"
|
||||
@cancel="onCancelStepTwo(record)"
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<component
|
||||
:is="stepIconComponent(getStepTwoIcon(record))"
|
||||
class="cursor-pointer w-5 ml-1"
|
||||
:class="stepIconClass(getStepTwoIcon(record))"
|
||||
/>
|
||||
<div>{{ getStepTwoText(record) }}</div>
|
||||
</div>
|
||||
</a-popconfirm>
|
||||
</a-menu-item>
|
||||
|
||||
<a-menu-item
|
||||
v-if="hasPermission('persons.list.client.omde.able.order')"
|
||||
:disabled="record.sts_verify_step_one !== 2"
|
||||
key="5"
|
||||
>
|
||||
<a-popconfirm
|
||||
ok-text="بله"
|
||||
cancel-text="خیر"
|
||||
title="آیا مطمئن هستید؟"
|
||||
@confirm="toggleSts(record)"
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<component
|
||||
:is="record.sts === 1 ? CheckOutlined : StopOutlined"
|
||||
class="cursor-pointer w-5 ml-1"
|
||||
:class="record.sts === 1 ? 'text-success' : 'text-danger'"
|
||||
/>
|
||||
<div>{{ record.sts === 1 ? 'معلق کردن سفارش' : 'فعال کردن سفارش' }}</div>
|
||||
</div>
|
||||
</a-popconfirm>
|
||||
</a-menu-item>
|
||||
|
||||
<a-menu-item
|
||||
v-if="hasPermission('persons.list.client.omde.able')"
|
||||
:disabled="record.sts_verify_step_one !== 2"
|
||||
key="6"
|
||||
>
|
||||
<a-popconfirm
|
||||
ok-text="بله"
|
||||
cancel-text="خیر"
|
||||
title="آیا مطمئن هستید؟"
|
||||
@confirm="toggleActive(record)"
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<!--
|
||||
NOTE legacy binds :icon="getStsIcon(record)" here while
|
||||
colouring by getActiveIcon(record) — it shows the ORDER
|
||||
status icon next to the ACCOUNT label. Preserved.
|
||||
-->
|
||||
<component
|
||||
:is="record.sts === 1 ? CheckOutlined : StopOutlined"
|
||||
class="cursor-pointer w-5 ml-1"
|
||||
:class="record.active === 1 ? 'text-success' : 'text-danger'"
|
||||
/>
|
||||
<div>
|
||||
{{ record.active === 1 ? 'معلق کردن حساب کاربری' : ' فعال کردن حساب کاربری' }}
|
||||
</div>
|
||||
</div>
|
||||
</a-popconfirm>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
</XTable>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { message } from 'ant-design-vue';
|
||||
import {
|
||||
CheckOutlined,
|
||||
CloudDownloadOutlined,
|
||||
DownOutlined,
|
||||
EditOutlined,
|
||||
EyeOutlined,
|
||||
FileTextOutlined,
|
||||
QuestionCircleOutlined,
|
||||
SettingOutlined,
|
||||
StopOutlined,
|
||||
UnorderedListOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import XTable from '../../../components/x-table/XTable.vue';
|
||||
import { useUsersStore } from '../../../stores/users';
|
||||
import api from '../../../api/client';
|
||||
import type { XTbl } from '../../../components/x-table/model';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
/** Card heading — the only visible difference between four of the five lists. */
|
||||
title: string;
|
||||
/** The table definition (differs only in its url). */
|
||||
model: XTbl;
|
||||
/**
|
||||
* OmdeNonVerifiedList's quirk: its popconfirm calls rejectStepOne() in BOTH
|
||||
* the accept and reject branches, so the first step can never be approved
|
||||
* from that screen. Faithfully reproduced behind this flag rather than
|
||||
* silently "fixed" — see docs/ADMIN-PORT-PLAN.md.
|
||||
*/
|
||||
rejectOnlyStepOne?: boolean;
|
||||
}>(),
|
||||
{ rejectOnlyStepOne: false },
|
||||
);
|
||||
|
||||
const router = useRouter();
|
||||
const users = useUsersStore();
|
||||
const hasPermission = (rid: string) => Boolean(users.hasPermission(rid));
|
||||
|
||||
const tbl = ref<{ fetch: () => Promise<void> } | null>(null);
|
||||
const query = ref<{ count?: number; total?: number } | undefined>(undefined);
|
||||
|
||||
/** legacy setCount(): the "N رکورد غیر فعال" banner above the grid */
|
||||
function setCount(raw: unknown) {
|
||||
const value = raw as { count?: number; total?: number } | undefined;
|
||||
query.value = {
|
||||
count: value && value.count ? value.count : undefined,
|
||||
total: value && value.total ? value.total : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* legacy handleMenuClick(): closes the dropdown for the keys that open a
|
||||
* popconfirm, so the menu does not stay open behind the confirmation.
|
||||
*/
|
||||
function handleMenuClick(e: { key: string | number }, _id: string) {
|
||||
const key = String(e.key);
|
||||
return key === '5' || key === '6' || key === '7' || key === '8' || key === '9';
|
||||
}
|
||||
|
||||
// ── verification step display, ported verbatim (1 pending / 2 accepted / 3 rejected)
|
||||
function getStepOneText(row: Record<string, any>) {
|
||||
switch (row.sts_verify_step_one) {
|
||||
case 1:
|
||||
return 'در انتظار تایید ورود مشتری';
|
||||
case 2:
|
||||
return 'رد ورود مشتری';
|
||||
case 3:
|
||||
return 'تایید ورود مشتری';
|
||||
}
|
||||
}
|
||||
function getStepOneIcon(row: Record<string, any>) {
|
||||
switch (row.sts_verify_step_one) {
|
||||
case 1:
|
||||
return 'HelpCircleIcon';
|
||||
case 2:
|
||||
return 'SlashIcon';
|
||||
case 3:
|
||||
return 'CheckIcon';
|
||||
}
|
||||
}
|
||||
function getStepTwoText(row: Record<string, any>) {
|
||||
switch (row.sts_verify_step_two) {
|
||||
case 1:
|
||||
return 'در انتظار فعال سازی';
|
||||
case 2:
|
||||
return 'غیر فعال سازی';
|
||||
case 3:
|
||||
return 'فعال سازی';
|
||||
}
|
||||
}
|
||||
function getStepTwoIcon(row: Record<string, any>) {
|
||||
switch (row.sts_verify_step_two) {
|
||||
case 1:
|
||||
return 'HelpCircleIcon';
|
||||
case 2:
|
||||
return 'SlashIcon';
|
||||
case 3:
|
||||
return 'CheckIcon';
|
||||
}
|
||||
}
|
||||
|
||||
/** feather name -> ant component, matching legacy's three icons */
|
||||
function stepIconComponent(name?: string) {
|
||||
if (name === 'HelpCircleIcon') return QuestionCircleOutlined;
|
||||
if (name === 'CheckIcon') return CheckOutlined;
|
||||
return StopOutlined; // SlashIcon
|
||||
}
|
||||
function stepIconClass(name?: string) {
|
||||
if (name === 'HelpCircleIcon') return 'text-primary';
|
||||
if (name === 'CheckIcon') return 'text-success';
|
||||
return 'text-danger';
|
||||
}
|
||||
|
||||
// ── actions
|
||||
async function postVerify(payload: Record<string, unknown>, errorMsg: string) {
|
||||
try {
|
||||
await api.post('major/verify', payload);
|
||||
message.info('تغییرات ثبت شد ');
|
||||
tbl.value?.fetch();
|
||||
} catch (e) {
|
||||
message.error(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
const acceptStepOne = (id: string) =>
|
||||
postVerify({ customer_id: id, sts_verify_step_one: 2 }, 'خطا در تغییر وضعیت اولیه مشتری');
|
||||
const rejectStepOne = (id: string) =>
|
||||
postVerify({ customer_id: id, sts_verify_step_one: 3 }, 'خطا در تغییر وضعیت اولیه مشتری');
|
||||
const acceptStepTwo = (id: string) =>
|
||||
postVerify({ customer_id: id, sts_verify_step_two: 2 }, 'خطا در تغییر وضعیت سفارش مشتری');
|
||||
const rejectStepTwo = (id: string) =>
|
||||
postVerify({ customer_id: id, sts_verify_step_two: 3 }, 'خطا در تغییر وضعیت سفارش مشتری');
|
||||
|
||||
/** legacy inline ternary on @confirm, preserved including the NonVerified quirk */
|
||||
function onConfirmStepOne(record: Record<string, any>) {
|
||||
if (record.sts_verify_step_one === 1) {
|
||||
return props.rejectOnlyStepOne ? rejectStepOne(record.id) : acceptStepOne(record.id);
|
||||
}
|
||||
if (record.sts_verify_step_one === 2) return rejectStepOne(record.id);
|
||||
return acceptStepOne(record.id);
|
||||
}
|
||||
function onCancelStepOne(record: Record<string, any>) {
|
||||
if (record.sts_verify_step_one === 1) return rejectStepOne(record.id);
|
||||
}
|
||||
function onConfirmStepTwo(record: Record<string, any>) {
|
||||
if (record.sts_verify_step_two === 1) return acceptStepTwo(record.id);
|
||||
if (record.sts_verify_step_two === 2) return rejectStepTwo(record.id);
|
||||
return acceptStepTwo(record.id);
|
||||
}
|
||||
function onCancelStepTwo(record: Record<string, any>) {
|
||||
if (record.sts_verify_step_two === 1) return rejectStepTwo(record.id);
|
||||
}
|
||||
|
||||
async function toggleActive(record: Record<string, any>) {
|
||||
try {
|
||||
await api.post('customer/toggle-active', { id: record.id });
|
||||
message.info('تغییرات ثبت شد ');
|
||||
await tbl.value?.fetch();
|
||||
} catch (e) {
|
||||
message.error('خطا در تغییر وضعیت');
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleSts(record: Record<string, any>) {
|
||||
try {
|
||||
// BankerList sent an explicit {id, sts} object while the others posted the
|
||||
// whole row; the server reads only `id`, so one shape is used here.
|
||||
await api.post('customer/toggle-sts', { id: record.id, sts: record.sts });
|
||||
message.info('تغییرات ثبت شد ');
|
||||
await tbl.value?.fetch();
|
||||
} catch (e) {
|
||||
message.error('خطا در تغییر وضعیت');
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleArchive(id: string) {
|
||||
try {
|
||||
await api.get(`major/archive/${id}`);
|
||||
message.info('تغییرات ثبت شد ');
|
||||
await tbl.value?.fetch();
|
||||
} catch (e) {
|
||||
message.error('خطا در بایگانی');
|
||||
}
|
||||
}
|
||||
|
||||
function toEdit(id: string) {
|
||||
router.push(`/bulkCustomer/edit/${id}`);
|
||||
}
|
||||
function toView(id: string) {
|
||||
router.push(`/bulkCustomer/view/${id}`);
|
||||
}
|
||||
function viewOrder(record: Record<string, any>) {
|
||||
router.push(`/bulkCustomer/view/${record.id}?tab=orders`);
|
||||
}
|
||||
function viewCash(record: Record<string, any>) {
|
||||
router.push(`/bulkCustomer/view/${record.id}?tab=documents`);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<!-- Ported from legacy views/customer/omde/OmdeList.vue (615 lines). -->
|
||||
<OmdeCustomerList :title="title" :model="model" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import OmdeCustomerList from './OmdeCustomerList.vue';
|
||||
import { buildOmdeListModel, OMDE_SOURCES } from './omdeListModel';
|
||||
|
||||
const title = OMDE_SOURCES.omde.title;
|
||||
const model = buildOmdeListModel('omde');
|
||||
</script>
|
||||
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<!-- Ported from legacy views/customer/omde/OmdeNonVerifiedList.vue. Carries the legacy quirk where step-one confirm rejects instead of accepting. -->
|
||||
<OmdeCustomerList :title="title" :model="model" reject-only-step-one />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import OmdeCustomerList from './OmdeCustomerList.vue';
|
||||
import { buildOmdeListModel, OMDE_SOURCES } from './omdeListModel';
|
||||
|
||||
const title = OMDE_SOURCES.nonVerified.title;
|
||||
const model = buildOmdeListModel('nonVerified');
|
||||
</script>
|
||||
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<!-- Ported from legacy views/customer/omde/ShopkeeperList.vue — verified to differ from OmdeList by exactly 4 lines: card title and model import. -->
|
||||
<OmdeCustomerList :title="title" :model="model" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import OmdeCustomerList from './OmdeCustomerList.vue';
|
||||
import { buildOmdeListModel, OMDE_SOURCES } from './omdeListModel';
|
||||
|
||||
const title = OMDE_SOURCES.shopkeeper.title;
|
||||
const model = buildOmdeListModel('shopkeeper');
|
||||
</script>
|
||||
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<!--
|
||||
Ported from legacy henza_admin/src/views/customer/omde/ViewOmdeCustomer.vue
|
||||
(376 lines). Shares its implementation with the retail detail screen — see
|
||||
CustomerDetail.vue for why. The wholesale variant adds the activity log
|
||||
(legacy `GET log/ago/{id}`).
|
||||
-->
|
||||
<CustomerDetail
|
||||
title="مشاهده اطلاعات مشتری عمده"
|
||||
:show-activity="true"
|
||||
:show-wallet="false"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import CustomerDetail from '../CustomerDetail.vue';
|
||||
</script>
|
||||
@@ -0,0 +1,70 @@
|
||||
// Ported from legacy henza_admin/src/views/customer/omde/*Model.js
|
||||
//
|
||||
// The five legacy model files (OmdeListModel, BankerListModel,
|
||||
// shopkeeperListModel, OmdeListNonVerifiedModel, OmdeArchiveListModel) are
|
||||
// byte-identical except for ONE line — the endpoint url, plus a different title
|
||||
// on two of them. Verified by diff. They are built here from one factory.
|
||||
import XTbl, { Xtc } from '../../../components/x-table/model';
|
||||
import { useUsersStore } from '../../../stores/users';
|
||||
|
||||
/** Endpoint + title per legacy file. */
|
||||
export const OMDE_SOURCES = {
|
||||
omde: { url: 'admin/customers-omde/list', title: 'لیست مشتریان های عمده' },
|
||||
banker: { url: 'admin/customers-omde/banker/list', title: 'لیست بنکداران' },
|
||||
shopkeeper: { url: 'admin/customers-omde/shopkeeper/list', title: 'لیست مغازه داران حضوری' },
|
||||
// NOTE both of these carry the SAME title as the plain omde list in legacy —
|
||||
// only their url differs. Preserved rather than "corrected".
|
||||
nonVerified: { url: 'admin/customers-omde/non-verified/list', title: 'لیست مشتریان های عمده' },
|
||||
archived: { url: 'admin/customers-omde/archived/list', title: 'لیست مشتریان های عمده' },
|
||||
} as const;
|
||||
|
||||
export type OmdeSourceKey = keyof typeof OMDE_SOURCES;
|
||||
|
||||
export function buildOmdeListModel(source: OmdeSourceKey): XTbl {
|
||||
const users = useUsersStore();
|
||||
|
||||
// legacy: fetch the city list once if the filter map is empty
|
||||
if (Object.keys(users.cityFilters).length < 1) {
|
||||
users.getCities();
|
||||
}
|
||||
|
||||
const { url, title } = OMDE_SOURCES[source];
|
||||
const tbl = new XTbl(url, title);
|
||||
|
||||
tbl.add(new Xtc('id', 'کد').filterNumber());
|
||||
tbl.add(new Xtc('fname', 'نام').filterText().hidden());
|
||||
tbl.add(new Xtc('lname', 'نام خانوادگی').filterText().hidden());
|
||||
tbl.add(new Xtc('name', 'نام و نام خانوادگی').filterText());
|
||||
tbl.add(new Xtc('mobile', 'موبایل').filterText().hidden().noSort());
|
||||
tbl.add(
|
||||
new Xtc('city_id', 'شهر')
|
||||
.hidden()
|
||||
.renderSlot('city')
|
||||
.filterKey('city_id')
|
||||
.filterSelect(users.cityFilters),
|
||||
);
|
||||
tbl.add(new Xtc('reagent.name', 'کارشناس فروش').filterText().noSort());
|
||||
tbl.add(new Xtc('gram_level', 'سطح اعتبار').filterNumber().noSort());
|
||||
tbl.add(new Xtc('percent', 'درصد').filterNumber().noSort());
|
||||
tbl.add(new Xtc('remain', 'بدهی گرمی').filterNumber().noSort());
|
||||
tbl.add(new Xtc('rial_remain', 'بدهی ریالی').filterNumber().noSort());
|
||||
tbl.add(new Xtc('caj', 'تاریخ ثبت').filterDate().noSort());
|
||||
tbl.add(new Xtc('uaj', 'بروزرسانی').hidden().filterDate().noSort());
|
||||
tbl.add(new Xtc('sts_str', 'وضعیت ثبت سفارش').renderSlot('sts').noSort());
|
||||
tbl.add(
|
||||
new Xtc('active', 'وضعیت حساب کاربری').renderSlot('active').noSort().filterSelect({
|
||||
'1': 'فعال',
|
||||
'-1': 'غیر فعال',
|
||||
}),
|
||||
);
|
||||
tbl.add(new Xtc('phone', 'تلفن').filterText().hidden().noSort());
|
||||
tbl.add(new Xtc('address', 'آدرس').filterText().hidden().noSort());
|
||||
tbl.add(new Xtc('store_name', 'نام مغازه').filterText().hidden().noSort());
|
||||
tbl.add(new Xtc('zone', 'منطقه').filterText().hidden().noSort());
|
||||
tbl.add(new Xtc('pasazh_name', 'نام پاساژ').filterText().hidden().noSort());
|
||||
tbl.add(new Xtc('opt', 'عملیات').noSort().renderSlot('opt'));
|
||||
|
||||
return tbl;
|
||||
}
|
||||
|
||||
export default buildOmdeListModel;
|
||||
Reference in New Issue
Block a user