change responsive]
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<MobileCardList
|
||||
v-model:search="search"
|
||||
:loading="loading"
|
||||
:has-more="hasMore"
|
||||
:show-empty="!loading && items.length === 0"
|
||||
empty-text="رکوردی یافت نشد"
|
||||
@search="reload"
|
||||
@load-more="loadMore"
|
||||
>
|
||||
<AccountingMobileListItem
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
@open="openSheet"
|
||||
/>
|
||||
</MobileCardList>
|
||||
|
||||
<AccountingMobileDetailSheet
|
||||
v-model:visible="sheetVisible"
|
||||
:item="selectedItem"
|
||||
@info="$emit('info', $event)"
|
||||
@orders="$emit('orders', $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getTotalAccountingTable } from '@/api/accounting'
|
||||
import type { TotalAccounting } from '@/model/accounting'
|
||||
import MobileCardList from '@/components/mobile/MobileCardList.vue'
|
||||
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
||||
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
||||
import AccountingMobileDetailSheet from './AccountingMobileDetailSheet.vue'
|
||||
import AccountingMobileListItem from './AccountingMobileListItem.vue'
|
||||
|
||||
defineEmits<{
|
||||
(e: 'info', item: TotalAccounting): void
|
||||
(e: 'orders', item: TotalAccounting): void
|
||||
}>()
|
||||
|
||||
const { sheetVisible, selectedItem, openSheet, syncSelectedItem } = useMobileDetailSheet<TotalAccounting>()
|
||||
|
||||
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<TotalAccounting>({
|
||||
fetchApi: getTotalAccountingTable,
|
||||
onFetched: (rows) => syncSelectedItem(rows)
|
||||
})
|
||||
|
||||
onMounted(() => reload())
|
||||
|
||||
defineExpose({ fetch, openSheet })
|
||||
</script>
|
||||
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<MobileDetailSheet v-model:visible="visibleProxy" :item="item" :title="sheetTitle">
|
||||
<template #default="{ item: row }">
|
||||
<MobileSheetHeader :title="sheetTitle" :show-title="isMobile">
|
||||
<template #actions>
|
||||
<MobileIconButton plain icon="vuesax-linear:stickynote" color="#10913A" :size="22" @click="$emit('info', row)" />
|
||||
<MobileIconButton plain icon="vuesax-linear:receipt-item" color="#10913A" :size="22" @click="$emit('orders', row)" />
|
||||
</template>
|
||||
</MobileSheetHeader>
|
||||
|
||||
<div class="space-y-2">
|
||||
<MobileStatCard bordered variant="success" label="موجودی ریالی" :value="usePrice(row.balance_cash ?? 0, true, '')">
|
||||
<template #prefix>
|
||||
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" color="#15803d" />
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
<MobileStatCard bordered variant="success" label="موجودی طلایی" :value="`${row.balance_gold ?? 0} گرم`">
|
||||
<template #prefix>
|
||||
<BaseIcon icon="vuesax-linear:coin" size="18" color="#15803d" />
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
</div>
|
||||
|
||||
<MobileInfoGrid>
|
||||
<MobileDetailRow label="نام مشتری" :value="row.customer_to?.name || '-'" />
|
||||
<MobileDetailRow label="نام مشتری دعوت شده" :value="row.customer?.name || '-'" />
|
||||
<MobileDetailRow label="زمان ثبت" :value="row.created_at ? toJalali(row.created_at) : '-'" />
|
||||
<MobileDetailRow label="نقد (ریال)" :value="usePrice(row.cash ?? 0, true, '')" />
|
||||
<MobileDetailRow label="طلا (گرم)" :value="`${row.gold ?? 0}`" />
|
||||
<MobileDetailRow label="توضیحات" full-width :value="row.note || '-'" />
|
||||
</MobileInfoGrid>
|
||||
</template>
|
||||
</MobileDetailSheet>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { TotalAccounting } from '@/model/accounting'
|
||||
import MobileDetailRow from '@/components/mobile/MobileDetailRow.vue'
|
||||
import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
||||
import MobileDetailSheet from '@/components/mobile/MobileDetailSheet.vue'
|
||||
import MobileIconButton from '@/components/mobile/MobileIconButton.vue'
|
||||
import MobileInfoGrid from '@/components/mobile/MobileInfoGrid.vue'
|
||||
import MobileSheetHeader from '@/components/mobile/MobileSheetHeader.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import { toJalali } from '@core/utils/useDataTime'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
item: TotalAccounting | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
(e: 'info', item: TotalAccounting): void
|
||||
(e: 'orders', item: TotalAccounting): void
|
||||
}>()
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
set: (value: boolean) => emit('update:visible', value)
|
||||
})
|
||||
|
||||
const sheetTitle = computed(() => props.item?.customer_to?.name || props.item?.customer?.name || 'حسابداری')
|
||||
</script>
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<MobileCard clickable @click="$emit('open', item)">
|
||||
<div class="space-y-1">
|
||||
<div>
|
||||
<div class="flex justify-between items-center gap-x-3 gap-y-1 text-xs">
|
||||
<div class="truncate text-sm font-700 text-gray-900">{{ customerToName }}</div>
|
||||
<div class="text-xs text-gray-400">{{ createdAtLabel }}</div>
|
||||
</div>
|
||||
<span class="text-xs text-gray-600">نقد: <span class="font-600 text-gray-900">{{ cashLabel }}</span></span>
|
||||
<span class="text-xs text-gray-600">طلا: <span class="font-600 text-gray-900">{{ goldLabel }}</span></span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</MobileCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { TotalAccounting } from '@/model/accounting'
|
||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||
import { toJalali } from '@core/utils/useDataTime'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
|
||||
const props = defineProps<{
|
||||
item: TotalAccounting
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'open', item: TotalAccounting): void
|
||||
}>()
|
||||
|
||||
const customerToName = computed(() => props.item.customer_to?.name || '-')
|
||||
const createdAtLabel = computed(() => (props.item.created_at ? toJalali(props.item.created_at) : '-'))
|
||||
const cashLabel = computed(() => usePrice(props.item.cash ?? 0, true, ''))
|
||||
const goldLabel = computed(() => `${props.item.gold ?? 0} گرم`)
|
||||
</script>
|
||||
@@ -1,141 +1,67 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="min-w-0 w-full max-w-full overflow-x-hidden">
|
||||
<BasicShowContext title="حسابداری">
|
||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #extraHeader>
|
||||
<!-- <BaseButton v-if="!customerId" type="primary" size="small" class="flex" @click="emits('add')">-->
|
||||
<!-- <div>افزودن سفارش</div>-->
|
||||
<!-- <BaseIcon icon="vuesax-linear:receipt-add" Size="16px" />-->
|
||||
<!-- </BaseButton>-->
|
||||
</template>
|
||||
<template #action="{ column, record }">
|
||||
<div class="gap-2 flex justify-center">
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('', record)">
|
||||
<template #title>
|
||||
<span>برداشت و واریز</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="handleOrderModal(record)">
|
||||
<template #title>
|
||||
<span>سفارشات</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:receipt-item" color="#10913A" />
|
||||
</a-tooltip>
|
||||
<div v-if="!isMobile" class="min-w-0 w-full max-w-full overflow-x-auto">
|
||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #action="{ record }">
|
||||
<div class="flex justify-center gap-2">
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('info', record)">
|
||||
<template #title><span>برداشت و واریز</span></template>
|
||||
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('orders', record)">
|
||||
<template #title><span>سفارشات</span></template>
|
||||
<Icon icon="vuesax-linear:receipt-item" color="#10913A" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</BaseTable>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</BaseTable>
|
||||
<AccountingMobileCardList
|
||||
v-else
|
||||
ref="mobileListRef"
|
||||
@info="emits('info', $event)"
|
||||
@orders="emits('orders', $event)"
|
||||
/>
|
||||
</BasicShowContext>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="tsx" setup>
|
||||
import type { TableInput } from '@core/type/baseTable'
|
||||
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||
import { changeCustomerActivation, getCustomerTable } from '@/api/customer'
|
||||
import { cols } from '../hooks/accountingColumns'
|
||||
import { useUserStore } from '@/store/userStore'
|
||||
import { usePrefix } from '@core/composable/usePrefix'
|
||||
import { message } from 'ant-design-vue'
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { getTotalAccountingTable } from '@/api/accounting'
|
||||
import {ref} from "vue";
|
||||
import {cloneDeep} from "lodash";
|
||||
import { cols } from '@/pages/menuPages/accounting/hooks/accountingColumns'
|
||||
import AccountingMobileCardList from '@/pages/menuPages/accounting/components/AccountingMobileCardList.vue'
|
||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
||||
import type { TableInput } from '@core/type/baseTable'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
const showOrderModal = ref<boolean>(false)
|
||||
function handleOrderModal(record) {
|
||||
|
||||
showOrderModal.value = true
|
||||
}
|
||||
defineProps<{
|
||||
customerId?: number
|
||||
}>()
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: 'info', record: unknown): void
|
||||
(e: 'orders', record: unknown): void
|
||||
}>()
|
||||
|
||||
const [submit, reload] = useBaseTable()
|
||||
/*const select = (val) => {
|
||||
val.sts === 1 ? return ['1'] : ['2']
|
||||
}*/
|
||||
//emits
|
||||
interface Prop {
|
||||
customerId: number
|
||||
}
|
||||
const emits = defineEmits(['delete', 'edit', 'add', 'info'])
|
||||
const props = defineProps<Prop>()
|
||||
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||
|
||||
const { prefixCls } = usePrefix('pophoverstatus')
|
||||
|
||||
//permission
|
||||
function checkPermission(temp: string) {
|
||||
const store = useUserStore()
|
||||
return store.canAccess(temp)
|
||||
}
|
||||
|
||||
//define table
|
||||
const table = ref(null)
|
||||
|
||||
const tblProps = ref<any>({
|
||||
const tblProps = ref({
|
||||
columns: cols,
|
||||
api: (params: TableInput) => {
|
||||
if (props.customerId) {
|
||||
// params.filters = []
|
||||
// params.filters?.push({
|
||||
// field: "customer_id",
|
||||
// type: "text",
|
||||
// value: props.customerId,
|
||||
// cr: "and",
|
||||
// op: "eq",
|
||||
// hidden: false
|
||||
// })
|
||||
}
|
||||
return getTotalAccountingTable(params)
|
||||
}
|
||||
//bordered: true
|
||||
api: (params: TableInput) => getTotalAccountingTable(params)
|
||||
})
|
||||
const loading = ref<boolean>(false)
|
||||
|
||||
async function changeCustomerActivationFunction(id: number) {
|
||||
try {
|
||||
loading.value = true
|
||||
await changeCustomerActivation(id)
|
||||
reload()
|
||||
message.success('تغییر وضعیت با موفقیت انجام شد')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
async function fetch() {
|
||||
if (isMobile.value) {
|
||||
await mobileListRef.value?.fetch?.()
|
||||
return
|
||||
}
|
||||
await reload()
|
||||
}
|
||||
|
||||
const fetch = () => {
|
||||
// console.log('sss')
|
||||
reload()
|
||||
}
|
||||
// const spinTable = (val: boolean) => {
|
||||
// // console.log('sss')
|
||||
// table?.value?.loading.value = val
|
||||
// }
|
||||
defineExpose({
|
||||
fetch
|
||||
// spinTable
|
||||
})
|
||||
defineExpose({ fetch })
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@prefix: ~'@{prefixCls}-pophoverstatus';
|
||||
|
||||
.@{prefix} {
|
||||
.ant-menu-rtl.ant-menu-inline,
|
||||
.ant-menu-rtl.ant-menu-vertical {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.ant-menu-item-only-child {
|
||||
border-radius: @border-radius-base;
|
||||
}
|
||||
|
||||
//bor
|
||||
/* .ant-input-group-addon:first-child {
|
||||
!* border: #d9d9d9 1px solid;
|
||||
border-radius: 0 6px 6px 0;*!
|
||||
}*/
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -79,7 +79,6 @@ export const cols: BaseTableColumn[] = [
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
slot: 'action',
|
||||
fixed: 'right',
|
||||
filterable: false
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<BasicMainCard>
|
||||
<div class="flex w-full h-full">
|
||||
<tbl
|
||||
ref="table"
|
||||
class="w-full h-full"
|
||||
@info="info"
|
||||
@edit="edit"
|
||||
@delete="deleteItem"
|
||||
@add="add"
|
||||
/>
|
||||
<div class="flex h-full w-full min-w-0 max-w-full overflow-x-hidden">
|
||||
<tbl ref="table" class="h-full w-full min-w-0" @info="onAccountingWithdraw" @orders="onAccountingOrders" />
|
||||
|
||||
<!-- <BasicMenu :menu="menu" width="13rem" @onChange="tabSelected" />
|
||||
<component :is="selectedComponent" class="mr-5" />-->
|
||||
@@ -143,6 +136,7 @@ import { cloneDeep } from 'lodash'
|
||||
import type { Item } from '@/model/Item'
|
||||
import type { Customer } from '@/model/customer'
|
||||
import { createCustomer, deleteCustomer, getCustomersList, updateCustomer } from '@/api/customer'
|
||||
import type { TotalAccounting } from '@/model/accounting'
|
||||
import type { Order } from '@/model/order'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
import { getLastPrice } from '@/api/price'
|
||||
@@ -234,6 +228,14 @@ function stopGetPrice() {
|
||||
//info
|
||||
let infoParams = reactive<Info>({})
|
||||
|
||||
function onAccountingWithdraw(_record: TotalAccounting) {
|
||||
message.info('برداشت و واریز بهزودی در دسترس است')
|
||||
}
|
||||
|
||||
function onAccountingOrders(_record: TotalAccounting) {
|
||||
message.info('لیست سفارشات بهزودی در دسترس است')
|
||||
}
|
||||
|
||||
function info(val: Order) {
|
||||
infoParams.name = val.customer.name
|
||||
infoParams.type = val.customer.mobile
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<MobileCardList
|
||||
v-model:search="search"
|
||||
:loading="loading"
|
||||
:has-more="hasMore"
|
||||
:show-empty="!loading && items.length === 0"
|
||||
empty-text="حسابی یافت نشد"
|
||||
search-placeholder="جستجو حساب"
|
||||
show-refresh
|
||||
@search="reload"
|
||||
@refresh="reload"
|
||||
@load-more="loadMore"
|
||||
>
|
||||
<AccountMobileListItem
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
@open="openSheet"
|
||||
@toggle-sts="confirmToggleSts"
|
||||
/>
|
||||
</MobileCardList>
|
||||
|
||||
<AccountMobileDetailSheet v-model:visible="sheetVisible" :item="selectedItem" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Modal } from 'ant-design-vue'
|
||||
import { getAccountTable } from '@/api/card'
|
||||
import type { Card } from '@/model/card'
|
||||
import MobileCardList from '@/components/mobile/MobileCardList.vue'
|
||||
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
||||
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
||||
import AccountMobileDetailSheet from './AccountMobileDetailSheet.vue'
|
||||
import AccountMobileListItem from './AccountMobileListItem.vue'
|
||||
|
||||
type AccountRow = Card & { sts_str?: string; created_at?: string; customer_id?: number }
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'toggleSts', item: AccountRow): void
|
||||
}>()
|
||||
|
||||
const { sheetVisible, selectedItem, openSheet, syncSelectedItem } = useMobileDetailSheet<AccountRow>()
|
||||
|
||||
function confirmToggleSts(item: AccountRow) {
|
||||
const isApproved = item.sts === 2
|
||||
Modal.confirm({
|
||||
title: isApproved ? 'غیر فعال سازی حساب' : 'فعال سازی حساب',
|
||||
content: isApproved
|
||||
? 'آیا از غیرفعالسازی این حساب اطمینان دارید؟'
|
||||
: 'آیا از فعالسازی این حساب اطمینان دارید؟',
|
||||
okText: 'تایید',
|
||||
cancelText: 'انصراف',
|
||||
centered: true,
|
||||
onOk: () => emit('toggleSts', item)
|
||||
})
|
||||
}
|
||||
|
||||
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<AccountRow>({
|
||||
fetchApi: getAccountTable,
|
||||
onFetched: (rows) => syncSelectedItem(rows)
|
||||
})
|
||||
|
||||
onMounted(() => reload())
|
||||
|
||||
defineExpose({ fetch, openSheet })
|
||||
</script>
|
||||
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<MobileDetailSheet v-model:visible="visibleProxy" :item="item" :title="sheetTitle">
|
||||
<template #default="{ item: row }">
|
||||
<MobileSheetHeader :title="sheetTitle" :subtitle="dateLabel(row)" :show-title="isMobile" />
|
||||
|
||||
<span class="inline-flex rounded-lg border px-2 py-0.5 text-xs font-600" :class="statusClass(row)">
|
||||
{{ row.sts_str || '-' }}
|
||||
</span>
|
||||
|
||||
<div class="space-y-2">
|
||||
<MobileStatCard bordered label="نام بانک" :value="row.bank_name || '-'">
|
||||
<template #prefix>
|
||||
<BaseIcon icon="vuesax-linear:bank" size="18" color="#2563eb" />
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
</div>
|
||||
|
||||
<MobileInfoGrid>
|
||||
<MobileDetailRow label="کد حساب" :value="row.id ?? '-'" />
|
||||
<MobileDetailRow label="کد کاربر" :value="row.customer_id ?? '-'" />
|
||||
<MobileDetailRow label="نام مشتری" full-width :value="row.customer?.name || '-'" />
|
||||
<MobileDetailRow label="شماره حساب" full-width :value="row.account_number || '-'" />
|
||||
<MobileDetailRow label="شماره کارت" full-width :value="row.cart_number || '-'" />
|
||||
<MobileDetailRow label="شماره شبا" full-width :value="row.shaba_number || '-'" />
|
||||
<MobileDetailRow label="تاریخ ثبت" :value="dateLabel(row)" />
|
||||
</MobileInfoGrid>
|
||||
</template>
|
||||
</MobileDetailSheet>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Card } from '@/model/card'
|
||||
import MobileDetailRow from '@/components/mobile/MobileDetailRow.vue'
|
||||
import MobileDetailSheet from '@/components/mobile/MobileDetailSheet.vue'
|
||||
import MobileInfoGrid from '@/components/mobile/MobileInfoGrid.vue'
|
||||
import MobileSheetHeader from '@/components/mobile/MobileSheetHeader.vue'
|
||||
import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import { toJalaliNoTime } from '@core/utils/useDataTime'
|
||||
|
||||
type AccountRow = Card & { sts_str?: string; created_at?: string; customer_id?: number }
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
item: AccountRow | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
}>()
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
set: (value: boolean) => emit('update:visible', value)
|
||||
})
|
||||
|
||||
const sheetTitle = computed(() => props.item?.customer?.name || `حساب #${props.item?.id ?? '-'}`)
|
||||
|
||||
function statusClass(row: AccountRow) {
|
||||
switch (row.sts) {
|
||||
case 1:
|
||||
return 'border-amber-300 bg-amber-50 text-amber-700'
|
||||
case 2:
|
||||
return 'border-green-300 bg-green-50 text-green-700'
|
||||
default:
|
||||
return 'border-red-300 bg-red-50 text-red-600'
|
||||
}
|
||||
}
|
||||
|
||||
function dateLabel(row: AccountRow) {
|
||||
return row.created_at ? toJalaliNoTime(row.created_at) : '-'
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<MobileCard clickable @click="$emit('open', item)">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="min-w-0 flex-1 space-y-1">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="truncate text-sm font-700 text-gray-900">{{ customerName }}</span>
|
||||
<span class="shrink-0 rounded-md border px-1.5 py-0.5 text-xs font-600" :class="statusClass">
|
||||
{{ statusLabel }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-2 text-xs text-gray-600">
|
||||
<span class="truncate">{{ item.bank_name || '-' }}</span>
|
||||
<span class="shrink-0 text-gray-400">#{{ item.id ?? '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<MobileIconButton
|
||||
:icon="item.sts === 2 ? 'vuesax-linear:card-tick' : 'vuesax-linear:card-remove'"
|
||||
:color="item.sts === 2 ? '#16A34A' : '#DC2626'"
|
||||
:size="20"
|
||||
@click="$emit('toggleSts', item)"
|
||||
/>
|
||||
</div>
|
||||
</MobileCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Card } from '@/model/card'
|
||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||
import MobileIconButton from '@/components/mobile/MobileIconButton.vue'
|
||||
|
||||
type AccountRow = Card & { sts_str?: string; created_at?: string; customer_id?: number }
|
||||
|
||||
const props = defineProps<{
|
||||
item: AccountRow
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'open', item: AccountRow): void
|
||||
(e: 'toggleSts', item: AccountRow): void
|
||||
}>()
|
||||
|
||||
const customerName = computed(() => props.item.customer?.name || '-')
|
||||
const statusLabel = computed(() => props.item.sts_str || '-')
|
||||
|
||||
const statusClass = computed(() => {
|
||||
switch (props.item.sts) {
|
||||
case 1:
|
||||
return 'border-amber-300 bg-amber-50 text-amber-700'
|
||||
case 2:
|
||||
return 'border-green-300 bg-green-50 text-green-700'
|
||||
default:
|
||||
return 'border-red-300 bg-red-50 text-red-600'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,7 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||
<BasicShowContext title="لیست حساب ها">
|
||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<div class="flex h-full min-h-0 flex-col">
|
||||
<BaseTable v-if="!isMobile" class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #extraHeader>
|
||||
<!-- <BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
||||
<div>افزودن مشتری عمده</div>
|
||||
@@ -27,6 +28,14 @@
|
||||
</div>
|
||||
</template>
|
||||
</BaseTable>
|
||||
|
||||
<AccountMobileCardList
|
||||
v-else
|
||||
ref="mobileListRef"
|
||||
class="min-h-0 flex-1"
|
||||
@toggle-sts="changeAccountStsFunction"
|
||||
/>
|
||||
</div>
|
||||
</BasicShowContext>
|
||||
</div>
|
||||
</template>
|
||||
@@ -44,7 +53,11 @@ import { getOrderTable } from '@/api/order'
|
||||
import { getWithdrawTable } from '@/api/wallet'
|
||||
import { changeAccountSts, getAccountTable } from '@/api/card'
|
||||
import type { Card } from '@/model/card'
|
||||
import AccountMobileCardList from './AccountMobileCardList.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
const [submit, reload] = useBaseTable()
|
||||
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||
/*const select = (val) => {
|
||||
val.sts === 1 ? return ['1'] : ['2']
|
||||
}*/
|
||||
@@ -64,7 +77,7 @@ async function changeAccountStsFunction(val: Card) {
|
||||
loading.value = true
|
||||
await changeAccountSts({ id: val.id, sts: val.sts !== 2 ? 2 : 3 })
|
||||
message.success(val.sts !== 2 ? 'با موفقیت تایید شد' : 'با موفقیت رد شد')
|
||||
reload()
|
||||
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
} finally {
|
||||
@@ -83,9 +96,8 @@ const tblProps = ref<any>({
|
||||
})
|
||||
const loading = ref<boolean>(false)
|
||||
|
||||
const fetch = () => {
|
||||
// console.log('sss')
|
||||
reload()
|
||||
async function fetch() {
|
||||
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||
}
|
||||
// const spinTable = (val: boolean) => {
|
||||
// // console.log('sss')
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<MobileCardList
|
||||
v-model:search="search"
|
||||
:loading="loading"
|
||||
:has-more="hasMore"
|
||||
:show-empty="!loading && items.length === 0"
|
||||
empty-text="مشتری بایگانیشدهای یافت نشد"
|
||||
@search="reload"
|
||||
@load-more="loadMore"
|
||||
>
|
||||
<CustomerArchiveMobileListItem
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
@open="openSheet"
|
||||
@info="$emit('info', $event)"
|
||||
@unarchive="$emit('unarchive', $event)"
|
||||
/>
|
||||
</MobileCardList>
|
||||
|
||||
<CustomerArchiveMobileDetailSheet v-model:visible="sheetVisible" :item="selectedItem" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getCustomerArchiveTable } from '@/api/customer'
|
||||
import type { Customer } from '@/model/customer'
|
||||
import MobileCardList from '@/components/mobile/MobileCardList.vue'
|
||||
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
||||
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
||||
import CustomerArchiveMobileDetailSheet from './CustomerArchiveMobileDetailSheet.vue'
|
||||
import CustomerArchiveMobileListItem from './CustomerArchiveMobileListItem.vue'
|
||||
|
||||
defineEmits<{
|
||||
(e: 'info', item: Customer): void
|
||||
(e: 'unarchive', item: Customer): void
|
||||
}>()
|
||||
|
||||
const { sheetVisible, selectedItem, openSheet, syncSelectedItem } = useMobileDetailSheet<Customer>()
|
||||
|
||||
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<Customer>({
|
||||
fetchApi: getCustomerArchiveTable,
|
||||
onFetched: (rows) => syncSelectedItem(rows)
|
||||
})
|
||||
|
||||
onMounted(() => reload())
|
||||
|
||||
defineExpose({ fetch })
|
||||
</script>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<MobileDetailSheet v-model:visible="visibleProxy" :item="item" :title="item?.name || ''">
|
||||
<template #default="{ item: row }">
|
||||
<MobileSheetHeader :title="row.name || '-'" :show-title="isMobile" />
|
||||
|
||||
<MobileInfoGrid>
|
||||
<MobileDetailRow label="کد کاربری" :value="row.id ?? '-'" />
|
||||
<MobileDetailRow label="شماره موبایل" :value="row.mobile || '-'" />
|
||||
<MobileDetailRow label="وضعیت کاربر" full-width :value-class="row.active ? 'text-green-700' : 'text-red-600'">
|
||||
{{ row.active ? 'فعال' : 'غیرفعال' }}
|
||||
</MobileDetailRow>
|
||||
<MobileDetailRow
|
||||
label="وضعیت سفارش دهی"
|
||||
full-width
|
||||
:value-class="row.trade_active ? 'text-green-700' : 'text-red-600'"
|
||||
>
|
||||
{{ row.trade_active ? 'مجاز' : 'غیر مجاز' }}
|
||||
</MobileDetailRow>
|
||||
</MobileInfoGrid>
|
||||
</template>
|
||||
</MobileDetailSheet>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Customer } from '@/model/customer'
|
||||
import MobileDetailRow from '@/components/mobile/MobileDetailRow.vue'
|
||||
import MobileDetailSheet from '@/components/mobile/MobileDetailSheet.vue'
|
||||
import MobileInfoGrid from '@/components/mobile/MobileInfoGrid.vue'
|
||||
import MobileSheetHeader from '@/components/mobile/MobileSheetHeader.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
item: Customer | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
}>()
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
set: (value: boolean) => emit('update:visible', value)
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<MobileCard clickable @click="$emit('open', item)">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex min-w-0 flex-1 items-center gap-2">
|
||||
<BaseIcon icon="vuesax-linear:user" size="20" color="#6b7280" />
|
||||
<button
|
||||
type="button"
|
||||
class="truncate border-none bg-transparent p-0 text-right text-sm font-700 text-gray-900 cursor-pointer"
|
||||
@click.stop="$emit('info', item)"
|
||||
>
|
||||
{{ item.name || '-' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="shrink-0 text-sm text-gray-600">{{ item.mobile || '-' }}</div>
|
||||
|
||||
<div class="flex shrink-0 items-center gap-2">
|
||||
<span class="whitespace-nowrap rounded-lg border px-2 py-0.5 text-xs font-600" :class="typeTagClass">
|
||||
{{ item.type_str || 'کاربر' }}
|
||||
</span>
|
||||
<MobileIconButton
|
||||
icon="vuesax-linear:archive-slash"
|
||||
color="#e3bf00"
|
||||
:size="20"
|
||||
@click="$emit('unarchive', item)"
|
||||
/>
|
||||
<span class="inline-block h-2.5 w-2.5 rounded-full" :class="item.active ? 'bg-green-500' : 'bg-red-500'" />
|
||||
</div>
|
||||
</div>
|
||||
</MobileCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Customer } from '@/model/customer'
|
||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||
import MobileIconButton from '@/components/mobile/MobileIconButton.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
item: Customer
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'open', item: Customer): void
|
||||
(e: 'info', item: Customer): void
|
||||
(e: 'unarchive', item: Customer): void
|
||||
}>()
|
||||
|
||||
const typeTagClass = computed(() => {
|
||||
const type = props.item.type_str || ''
|
||||
if (type.includes('نماینده')) return 'border-blue-400 bg-blue-50 text-blue-600'
|
||||
return 'border-gray-300 bg-white text-gray-600'
|
||||
})
|
||||
</script>
|
||||
@@ -1,128 +1,87 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||
<BasicShowContext title="لیست مشتریان بایگانی شده">
|
||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<!-- <template #extraHeader>
|
||||
<BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
||||
<div>افزودن مشتری عمده</div>
|
||||
<BaseIcon icon="vuesax-linear:user-add" Size="16px" />
|
||||
</BaseButton>
|
||||
</template>-->
|
||||
<template #action="{ column, record }">
|
||||
<div class="gap-2 flex justify-center">
|
||||
<a-tooltip :key="record.is_archive" placement="bottom" class="cursor-pointer">
|
||||
<template #title>
|
||||
<span>{{ record.is_archive ? 'بیرون بردن از بایگانی' : 'بایگانی کردن' }}</span>
|
||||
</template>
|
||||
<a-popconfirm
|
||||
title="آیا مشتری را از بایگانی خارج می کنید؟"
|
||||
ok-text="تایید"
|
||||
cancel-text="رد"
|
||||
placement="topLeft"
|
||||
@confirm="archiveFunction(record.id)"
|
||||
@cancel=""
|
||||
>
|
||||
<!-- <template #icon><question-circle-outlined style="color: red" /></template>-->
|
||||
<Icon class="cursor-pointer" icon="vuesax-linear:archive-slash" color="#e3bf00" />
|
||||
</a-popconfirm>
|
||||
</a-tooltip>
|
||||
<div class="flex h-full min-h-0 flex-col">
|
||||
<BaseTable v-if="!isMobile" class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #action="{ record }">
|
||||
<div class="flex justify-center gap-2">
|
||||
<a-tooltip :key="record.is_archive" placement="bottom" class="cursor-pointer">
|
||||
<template #title>
|
||||
<span>{{ record.is_archive ? 'بیرون بردن از بایگانی' : 'بایگانی کردن' }}</span>
|
||||
</template>
|
||||
<a-popconfirm
|
||||
title="آیا مشتری را از بایگانی خارج می کنید؟"
|
||||
ok-text="تایید"
|
||||
cancel-text="رد"
|
||||
placement="topLeft"
|
||||
@confirm="archiveFunction(record.id)"
|
||||
>
|
||||
<Icon class="cursor-pointer" icon="vuesax-linear:archive-slash" color="#e3bf00" />
|
||||
</a-popconfirm>
|
||||
</a-tooltip>
|
||||
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('info', record)">
|
||||
<template #title>
|
||||
<span>مشخصات</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
||||
</a-tooltip>
|
||||
<!-- <a-tooltip
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="emits('delete', record.id)"
|
||||
>
|
||||
<template #title>
|
||||
<span>حذف</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:trash" color="#EB3535" />
|
||||
</a-tooltip>-->
|
||||
</div>
|
||||
</template>
|
||||
</BaseTable>
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('info', record)">
|
||||
<template #title><span>مشخصات</span></template>
|
||||
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</BaseTable>
|
||||
|
||||
<CustomerArchiveMobileCardList
|
||||
v-else
|
||||
ref="mobileListRef"
|
||||
class="min-h-0 flex-1"
|
||||
@info="emits('info', $event)"
|
||||
@unarchive="handleUnarchive"
|
||||
/>
|
||||
</div>
|
||||
</BasicShowContext>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="tsx" setup>
|
||||
import type { TableInput } from '@core/type/baseTable'
|
||||
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
||||
<script setup lang="ts">
|
||||
import { message, Modal } from 'ant-design-vue'
|
||||
import { ref } from 'vue'
|
||||
import { changeCustomerArchive, getCustomerArchiveTable } from '@/api/customer'
|
||||
import type { Customer } from '@/model/customer'
|
||||
import CustomerArchiveMobileCardList from '@/pages/menuPages/customers/archive/components/CustomerArchiveMobileCardList.vue'
|
||||
import { cols } from '@/pages/menuPages/customers/archive/hooks/archiveColumns'
|
||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||
import {
|
||||
changeCustomerActivation,
|
||||
changeCustomerArchive,
|
||||
changeCustomerOrderActivation,
|
||||
getCustomerArchiveTable,
|
||||
getCustomerTable
|
||||
} from '@/api/customer'
|
||||
import { cols } from '../hooks/archiveColumns'
|
||||
import { useUserStore } from '@/store/userStore'
|
||||
import { usePrefix } from '@core/composable/usePrefix'
|
||||
import { message } from 'ant-design-vue'
|
||||
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
||||
import type { TableInput } from '@core/type/baseTable'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
const [submit, reload] = useBaseTable()
|
||||
/*const select = (val) => {
|
||||
val.sts === 1 ? return ['1'] : ['2']
|
||||
}*/
|
||||
//emits
|
||||
const emits = defineEmits(['delete', 'edit', 'add', 'info', 'charge'])
|
||||
|
||||
const { prefixCls } = usePrefix('pophoverstatus')
|
||||
const loading = ref(false)
|
||||
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||
|
||||
//permission
|
||||
function checkPermission(temp: string) {
|
||||
const store = useUserStore()
|
||||
return store.canAccess(temp)
|
||||
}
|
||||
|
||||
//define table
|
||||
const table = ref(null)
|
||||
|
||||
const tblProps = ref<any>({
|
||||
const tblProps = ref({
|
||||
columns: cols,
|
||||
api: (params: TableInput) => {
|
||||
return getCustomerArchiveTable(params)
|
||||
}
|
||||
//bordered: true
|
||||
api: (params: TableInput) => getCustomerArchiveTable(params)
|
||||
})
|
||||
const loading = ref<boolean>(false)
|
||||
|
||||
async function changeCustomerActivationFunction(id: number) {
|
||||
try {
|
||||
loading.value = true
|
||||
await changeCustomerActivation(id)
|
||||
reload()
|
||||
message.success('تغییر وضعیت با موفقیت انجام شد')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
async function fetch() {
|
||||
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||
}
|
||||
|
||||
async function changeCustomerOrderActivationFunction(id: number) {
|
||||
try {
|
||||
loading.value = true
|
||||
await changeCustomerOrderActivation(id)
|
||||
reload()
|
||||
message.success('تغییر وضعیت سفارش دهی با موفقیت انجام شد')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
function handleUnarchive(customer: Customer) {
|
||||
Modal.confirm({
|
||||
title: 'آیا مشتری را از بایگانی خارج می کنید؟',
|
||||
okText: 'تایید',
|
||||
cancelText: 'رد',
|
||||
centered: true,
|
||||
onOk: () => archiveFunction(customer.id)
|
||||
})
|
||||
}
|
||||
|
||||
async function archiveFunction(id: number) {
|
||||
try {
|
||||
loading.value = true
|
||||
await changeCustomerArchive(id)
|
||||
reload()
|
||||
await fetch()
|
||||
message.success('مشتری با موفقیت از بایگانی خارج شد.')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
@@ -130,37 +89,6 @@ async function archiveFunction(id: number) {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
const fetch = () => {
|
||||
// console.log('sss')
|
||||
reload()
|
||||
}
|
||||
// const spinTable = (val: boolean) => {
|
||||
// // console.log('sss')
|
||||
// table?.value?.loading.value = val
|
||||
// }
|
||||
defineExpose({
|
||||
fetch
|
||||
// spinTable
|
||||
})
|
||||
|
||||
defineExpose({ fetch })
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@prefix: ~'@{prefixCls}-pophoverstatus';
|
||||
|
||||
.@{prefix} {
|
||||
.ant-menu-rtl.ant-menu-inline,
|
||||
.ant-menu-rtl.ant-menu-vertical {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.ant-menu-item-only-child {
|
||||
border-radius: @border-radius-base;
|
||||
}
|
||||
|
||||
//bor
|
||||
/* .ant-input-group-addon:first-child {
|
||||
!* border: #d9d9d9 1px solid;
|
||||
border-radius: 0 6px 6px 0;*!
|
||||
}*/
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -157,7 +157,6 @@ export const cols: BaseTableColumn[] = [
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
slot: 'action',
|
||||
fixed: 'right',
|
||||
filterable: false
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full min-h-0">
|
||||
<!-- <BasicMainCard>-->
|
||||
<div class="flex w-full h-full">
|
||||
<tbl ref="table" class="w-full h-full" @info="info" @edit="edit" @charge="walletCharge" />
|
||||
<div class="flex h-full min-h-0 w-full min-w-0 flex-col overflow-hidden">
|
||||
<tbl ref="table" class="h-full min-h-0 w-full" @info="info" @edit="edit" @charge="walletCharge" />
|
||||
|
||||
<!-- <BasicMenu :menu="menu" width="13rem" @onChange="tabSelected" />
|
||||
<component :is="selectedComponent" class="mr-5" />-->
|
||||
@@ -55,15 +55,17 @@
|
||||
</div>
|
||||
</a-form>
|
||||
</BasicModal>
|
||||
<BasicModal
|
||||
<BasicActionSheet
|
||||
v-model:visible="userInfoVisible"
|
||||
width="40vw"
|
||||
title="اطلاعات کاربر"
|
||||
custom-footer
|
||||
modal-width="100vw"
|
||||
:loading="loading"
|
||||
:showButton="false"
|
||||
@close="userInfoVisible = false"
|
||||
>
|
||||
<div class="grid grid-cols-4 gap-2">
|
||||
<div class="col-span-4 bg-gray-100 w-full rounded-lg flex p-3 justify-between">
|
||||
<div v-if="isMobile" class="mb-3 text-base font-700">اطلاعات کاربر</div>
|
||||
<div class="space-y-2">
|
||||
<div class="rounded-lg bg-gray-100 w-full flex flex-col gap-3 p-3 md:flex-row md:justify-between">
|
||||
<div>
|
||||
<div class="font-bold">{{ userInfo.name }}</div>
|
||||
<div class="mt-1">{{ userInfo?.mobile }}</div>
|
||||
@@ -82,35 +84,38 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<BasicInfoItem title="کد کاربری" :value="userInfo?.id" />
|
||||
<BasicInfoItem title="نوع کاربر" :value="userInfo?.type_str" />
|
||||
<BasicInfoItem
|
||||
title="تاریخ تولد"
|
||||
:value="userInfo?.birth_date ? toJalaliNoTime(userInfo?.birth_date) : '_'"
|
||||
/>
|
||||
<BasicInfoItem title="شماره ملی" :value="userInfo?.n_code ?? '_'" />
|
||||
<BasicInfoItem title="نام استان" :value="userInfo?.province ?? '_'" />
|
||||
<BasicInfoItem title="نام شهر" :value="userInfo?.city ?? '_'" />
|
||||
<BasicInfoItem title="سقف اعتبار" :value="userInfo?.gram ? `${userInfo?.gram} گرم` : '_'" />
|
||||
<BasicInfoItem title="کد معرف" :value="userInfo?.referral_id" />
|
||||
<BasicInfoItem
|
||||
title="کد معرف کاربر"
|
||||
:value="userInfo?.referral ? userInfo?.referral?.id : '_'"
|
||||
/>
|
||||
<BasicInfoItem
|
||||
title="نام معرف کاربر"
|
||||
:value="userInfo?.referral ? userInfo?.referral?.name : '_'"
|
||||
/>
|
||||
<BasicInfoItem title="وضعیت سفارش دهی">
|
||||
<CustomTag
|
||||
:textColor="userInfo?.trade_active === 1 ? '#25855A' : '#C53030'"
|
||||
:bgColor="userInfo?.trade_active === 1 ? '#E3F4E9' : '#FECACA'"
|
||||
class="font-bold px-6"
|
||||
>
|
||||
{{ userInfo?.trade_active === 1 ? 'مجاز' : 'غیر مجاز' }}
|
||||
</CustomTag>
|
||||
</BasicInfoItem>
|
||||
<div class="col-span-4 grid grid-cols-2 gap-3">
|
||||
<div class="grid grid-cols-2 gap-2 md:grid-cols-4">
|
||||
<BasicInfoItem title="کد کاربری" :value="userInfo?.id" />
|
||||
<BasicInfoItem title="نوع کاربر" :value="userInfo?.type_str" />
|
||||
<BasicInfoItem
|
||||
title="تاریخ تولد"
|
||||
:value="userInfo?.birth_date ? toJalaliNoTime(userInfo?.birth_date) : '_'"
|
||||
/>
|
||||
<BasicInfoItem title="شماره ملی" :value="userInfo?.n_code ?? '_'" />
|
||||
<BasicInfoItem title="نام استان" :value="userInfo?.province ?? '_'" />
|
||||
<BasicInfoItem title="نام شهر" :value="userInfo?.city ?? '_'" />
|
||||
<BasicInfoItem title="سقف اعتبار" :value="userInfo?.gram ? `${userInfo?.gram} گرم` : '_'" />
|
||||
<BasicInfoItem title="کد معرف" :value="userInfo?.referral_id" />
|
||||
<BasicInfoItem
|
||||
title="کد معرف کاربر"
|
||||
:value="userInfo?.referral ? userInfo?.referral?.id : '_'"
|
||||
/>
|
||||
<BasicInfoItem
|
||||
title="نام معرف کاربر"
|
||||
:value="userInfo?.referral ? userInfo?.referral?.name : '_'"
|
||||
/>
|
||||
<BasicInfoItem title="وضعیت سفارش دهی" class="col-span-2 md:col-span-1">
|
||||
<CustomTag
|
||||
:textColor="userInfo?.trade_active === 1 ? '#25855A' : '#C53030'"
|
||||
:bgColor="userInfo?.trade_active === 1 ? '#E3F4E9' : '#FECACA'"
|
||||
class="font-bold px-6"
|
||||
>
|
||||
{{ userInfo?.trade_active === 1 ? 'مجاز' : 'غیر مجاز' }}
|
||||
</CustomTag>
|
||||
</BasicInfoItem>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-3 md:grid-cols-2">
|
||||
<div
|
||||
v-for="(item, index) in userInfo.wallet"
|
||||
:key="index"
|
||||
@@ -124,7 +129,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</BasicActionSheet>
|
||||
|
||||
<BasicModal
|
||||
v-model:visible="showWalletCharge"
|
||||
@@ -150,7 +155,9 @@ import { useStaticApiListsStore } from '@/store/staticApisStore'
|
||||
import { ref } from 'vue'
|
||||
import tbl from './components/table.vue'
|
||||
import type { FormInstance } from 'ant-design-vue'
|
||||
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
||||
import BasicInfoModal from '@/components/BasicInfoModal/index.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import { message } from 'ant-design-vue/es'
|
||||
import type { Info } from '@/components/BasicInfoModal/model/Info'
|
||||
import { toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<MobileCardList
|
||||
v-model:search="search"
|
||||
:loading="loading"
|
||||
:has-more="hasMore"
|
||||
:show-empty="!loading && items.length === 0"
|
||||
empty-text="مشتریای یافت نشد"
|
||||
@search="reload"
|
||||
@load-more="loadMore"
|
||||
>
|
||||
<CustomerMobileListItem
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
@open="openSheet"
|
||||
@info="$emit('info', $event)"
|
||||
@edit="$emit('edit', $event)"
|
||||
/>
|
||||
</MobileCardList>
|
||||
|
||||
<CustomerMobileDetailSheet
|
||||
v-model:visible="sheetVisible"
|
||||
:item="selectedItem"
|
||||
@info="$emit('info', $event)"
|
||||
@archive="$emit('archive', $event)"
|
||||
@toggleActive="$emit('toggleActive', $event)"
|
||||
@chargeMoney="$emit('chargeMoney', $event)"
|
||||
@chargeGold="$emit('chargeGold', $event)"
|
||||
@toggleTrade="$emit('toggleTrade', $event)"
|
||||
@confirmUser="$emit('confirmUser', $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getCustomerTable } from '@/api/customer'
|
||||
import type { Customer } from '@/model/customer'
|
||||
import MobileCardList from '@/components/mobile/MobileCardList.vue'
|
||||
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
||||
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
||||
import CustomerMobileListItem from './CustomerMobileListItem.vue'
|
||||
import CustomerMobileDetailSheet from './CustomerMobileDetailSheet.vue'
|
||||
|
||||
defineEmits<{
|
||||
(e: 'info', item: Customer): void
|
||||
(e: 'edit', item: Customer): void
|
||||
(e: 'archive', item: Customer): void
|
||||
(e: 'toggleActive', item: Customer): void
|
||||
(e: 'chargeMoney', item: Customer): void
|
||||
(e: 'chargeGold', item: Customer): void
|
||||
(e: 'toggleTrade', item: Customer): void
|
||||
(e: 'confirmUser', item: Customer): void
|
||||
}>()
|
||||
|
||||
const { sheetVisible, selectedItem, openSheet, syncSelectedItem } = useMobileDetailSheet<Customer>()
|
||||
|
||||
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<Customer>({
|
||||
fetchApi: getCustomerTable,
|
||||
onFetched: (rows) => syncSelectedItem(rows)
|
||||
})
|
||||
|
||||
onMounted(() => reload())
|
||||
|
||||
defineExpose({ fetch })
|
||||
</script>
|
||||
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<MobileDetailSheet v-model:visible="visibleProxy" :item="item" :title="item?.name || ''">
|
||||
<template #default="{ item: row }">
|
||||
<MobileSheetHeader :title="row.name || '-'" :show-title="isMobile">
|
||||
<template #actions>
|
||||
<MobileIconButton plain icon="vuesax-linear:archive-tick" color="#e3bf00" :size="22" @click="$emit('archive', row)" />
|
||||
<MobileIconButton
|
||||
plain
|
||||
:icon="row.active ? 'vuesax-linear:user-tick' : 'vuesax-linear:user-remove'"
|
||||
:color="row.active ? '#54AD06' : '#ad0627'"
|
||||
:size="22"
|
||||
@click="$emit('toggleActive', row)"
|
||||
/>
|
||||
<MobileIconButton plain icon="vuesax-linear:wallet-add" color="#9333ea" :size="22" @click="$emit('chargeMoney', row)" />
|
||||
<MobileIconButton plain icon="vuesax-linear:wallet-money" color="#ea580c" :size="22" @click="$emit('chargeGold', row)" />
|
||||
<MobileIconButton
|
||||
plain
|
||||
:icon="row.trade_active ? 'vuesax-linear:box-tick' : 'vuesax-linear:box-remove'"
|
||||
:color="row.trade_active ? '#54AD06' : '#ad0627'"
|
||||
:size="22"
|
||||
@click="$emit('toggleTrade', row)"
|
||||
/>
|
||||
<MobileIconButton
|
||||
v-if="!(row as any).is_legal"
|
||||
plain
|
||||
icon="vuesax-linear:user-search"
|
||||
color="#0d9488"
|
||||
:size="22"
|
||||
@click="$emit('confirmUser', row)"
|
||||
/>
|
||||
</template>
|
||||
</MobileSheetHeader>
|
||||
|
||||
<div class="space-y-2">
|
||||
<MobileStatCard bordered variant="success" label="موجودی طلایی" :value="`${goldBalance(row)} گرم`">
|
||||
<template #prefix>
|
||||
<BaseIcon icon="vuesax-linear:coin" size="18" color="#15803d" />
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
<MobileStatCard
|
||||
bordered
|
||||
variant="success"
|
||||
label="موجودی ریالی"
|
||||
:value="usePrice(cashBalance(row), true, '')"
|
||||
>
|
||||
<template #prefix>
|
||||
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" color="#15803d" />
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
</div>
|
||||
|
||||
<MobileInfoGrid>
|
||||
<MobileDetailRow label="وضعیت کاربر" full-width :value-class="row.active ? 'text-green-700' : 'text-red-600'">
|
||||
{{ row.active ? 'فعال' : 'غیرفعال' }}
|
||||
</MobileDetailRow>
|
||||
<MobileDetailRow
|
||||
label="وضعیت سفارش دهی"
|
||||
full-width
|
||||
:value-class="row.trade_active ? 'text-green-700' : 'text-red-600'"
|
||||
>
|
||||
{{ row.trade_active ? 'مجاز' : 'غیر مجاز' }}
|
||||
</MobileDetailRow>
|
||||
<MobileDetailRow label="کد کاربری" :value="row.id ?? '-'" />
|
||||
<MobileDetailRow label="نوع کاربر" :value="row.type_str || '-'" />
|
||||
<MobileDetailRow label="کد معرف" :value="row.referral_id ?? '-'" />
|
||||
<MobileDetailRow label="نام معرف کاربر" :value="row.referral?.name ?? '-'" />
|
||||
<MobileDetailRow label="گنج (گرم)" :value="row.gold ?? '-'" />
|
||||
<MobileDetailRow label="آخرین بازدید" :value="lastSeen(row)" value-class="text-xs" />
|
||||
</MobileInfoGrid>
|
||||
</template>
|
||||
</MobileDetailSheet>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Customer } from '@/model/customer'
|
||||
import MobileDetailRow from '@/components/mobile/MobileDetailRow.vue'
|
||||
import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
||||
import MobileDetailSheet from '@/components/mobile/MobileDetailSheet.vue'
|
||||
import MobileIconButton from '@/components/mobile/MobileIconButton.vue'
|
||||
import MobileInfoGrid from '@/components/mobile/MobileInfoGrid.vue'
|
||||
import MobileSheetHeader from '@/components/mobile/MobileSheetHeader.vue'
|
||||
import { getCashBalance, getGoldBalance } from '@/components/mobile/utils/wallet'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import { toJalali } from '@core/utils/useDataTime'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
item: Customer | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
(e: 'info', item: Customer): void
|
||||
(e: 'archive', item: Customer): void
|
||||
(e: 'toggleActive', item: Customer): void
|
||||
(e: 'chargeMoney', item: Customer): void
|
||||
(e: 'chargeGold', item: Customer): void
|
||||
(e: 'toggleTrade', item: Customer): void
|
||||
(e: 'confirmUser', item: Customer): void
|
||||
}>()
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
set: (value: boolean) => emit('update:visible', value)
|
||||
})
|
||||
|
||||
function cashBalance(row: Customer) {
|
||||
return getCashBalance(row.wallet)
|
||||
}
|
||||
|
||||
function goldBalance(row: Customer) {
|
||||
return getGoldBalance(row.wallet)
|
||||
}
|
||||
|
||||
function lastSeen(row: Customer) {
|
||||
return row.last_seen_at ? toJalali(row.last_seen_at) : '-'
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<MobileCard clickable @click="$emit('open', item)">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex min-w-0 flex-1 items-center gap-2">
|
||||
<BaseIcon icon="vuesax-linear:user" size="20" color="#6b7280" />
|
||||
<button
|
||||
type="button"
|
||||
class="truncate border-none bg-transparent p-0 text-right text-sm font-700 text-gray-900 cursor-pointer"
|
||||
@click.stop="$emit('info', item)"
|
||||
>
|
||||
{{ item.name || '-' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="shrink-0 text-sm text-gray-600">{{ item.mobile || '-' }}</div>
|
||||
|
||||
<div class="flex shrink-0 items-center gap-2">
|
||||
<span class="whitespace-nowrap rounded-lg border px-2 py-0.5 text-xs font-600" :class="typeTagClass">
|
||||
{{ item.type_str || 'کاربر' }}
|
||||
</span>
|
||||
<MobileIconButton icon="vuesax-linear:edit" color="#4572E9" :size="20" @click="$emit('edit', item)" />
|
||||
<span class="inline-block h-2.5 w-2.5 rounded-full" :class="item.active ? 'bg-green-500' : 'bg-red-500'" />
|
||||
</div>
|
||||
</div>
|
||||
</MobileCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Customer } from '@/model/customer'
|
||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||
import MobileIconButton from '@/components/mobile/MobileIconButton.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
item: Customer
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'open', item: Customer): void
|
||||
(e: 'info', item: Customer): void
|
||||
(e: 'edit', item: Customer): void
|
||||
}>()
|
||||
|
||||
const typeTagClass = computed(() => {
|
||||
const type = props.item.type_str || ''
|
||||
if (type.includes('نماینده')) return 'border-blue-400 bg-blue-50 text-blue-600'
|
||||
return 'border-gray-300 bg-white text-gray-600'
|
||||
})
|
||||
</script>
|
||||
@@ -1,138 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
<BasicShowContext title=" لیست مشتریان">
|
||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||
<BasicShowContext title="لیست مشتریان">
|
||||
<template #extra>
|
||||
<BaseButton type="primary" size="small" class="flex shrink-0 md:hidden" @click="emits('add')">
|
||||
<div>افزودن</div>
|
||||
<BaseIcon icon="vuesax-linear:user-add" Size="16px" />
|
||||
</BaseButton>
|
||||
</template>
|
||||
<div class="flex h-full min-h-0 flex-col">
|
||||
<BaseTable v-if="!isMobile" class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #extraHeader>
|
||||
<BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
||||
<div>افزودن مشتری</div>
|
||||
<BaseIcon icon="vuesax-linear:user-add" Size="16px" />
|
||||
</BaseButton>
|
||||
</template>
|
||||
<template #action="{ column, record }">
|
||||
<div class="md:hidden">
|
||||
<a-popover title="" placement="right" @cancel="">
|
||||
<template #content>
|
||||
<div class="flex flex-col gap-3">
|
||||
<a-tooltip :key="record.is_archive" placement="bottom" class="cursor-pointer">
|
||||
<template #title>
|
||||
<span>{{
|
||||
record.is_archive ? 'بیرون بردن از بایگانی' : 'بایگانی کردن'
|
||||
}}</span>
|
||||
</template>
|
||||
<a-popconfirm
|
||||
title="آیا مشتری را بایگانی می کنید؟"
|
||||
ok-text="تایید"
|
||||
cancel-text="رد"
|
||||
placement="topLeft"
|
||||
@confirm="archiveFunction(record.id)"
|
||||
@cancel=""
|
||||
>
|
||||
<!-- <template #icon><question-circle-outlined style="color: red" /></template>-->
|
||||
<Icon
|
||||
class="cursor-pointer"
|
||||
icon="vuesax-linear:archive-tick"
|
||||
color="#e3bf00"
|
||||
/>
|
||||
</a-popconfirm>
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
:key="record.active"
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="changeCustomerActivationFunction(record.id)"
|
||||
>
|
||||
<template #title>
|
||||
<span>
|
||||
{{ record.active ? 'غیرفعال کردن حساب کاربری' : 'فعال کردن حساب کاربری' }}
|
||||
</span>
|
||||
</template>
|
||||
<Icon
|
||||
:icon="
|
||||
record.active ? 'vuesax-linear:user-tick' : 'vuesax-linear:user-remove'
|
||||
"
|
||||
:color="record.active ? '#54AD06' : '#ad0627'"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="emits('chargeMoney', record)"
|
||||
>
|
||||
<template #title>
|
||||
<span>شارژ کیف پول ریالی</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:wallet-add" color="#9333ea" />
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="emits('chargeGold', record)"
|
||||
>
|
||||
<template #title>
|
||||
<span>شارژ کیف پول طلایی</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:wallet-money" color="#ea580c" />
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="emits('info', record)"
|
||||
>
|
||||
<template #title>
|
||||
<span>مشخصات</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="emits('edit', record)"
|
||||
>
|
||||
<template #title>
|
||||
<span>ویرایش</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:edit" color="#4572E9" />
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
:key="record.trade_active"
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="changeCustomerOrderActivationFunction(record.id)"
|
||||
>
|
||||
<template #title>
|
||||
<span>
|
||||
{{
|
||||
record.trade_active ? 'غیر مجاز کردن سفارش دهی' : 'مجاز کردن سفارش دهی'
|
||||
}}
|
||||
</span>
|
||||
</template>
|
||||
<Icon
|
||||
:icon="
|
||||
record.trade_active === 1
|
||||
? 'vuesax-linear:box-tick'
|
||||
: 'vuesax-linear:box-remove'
|
||||
"
|
||||
:color="record.trade_active === 1 ? '#54AD06' : '#ad0627'"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
v-if="!record.is_legal"
|
||||
:key="record.trade_active"
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="openConfirmUser(record)"
|
||||
>
|
||||
<template #title>
|
||||
<span>تایید کاربر</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:user-search" color="#0d9488" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<!-- <template #icon><question-circle-outlined style="color: red" /></template>-->
|
||||
<Icon class="cursor-pointer" icon="vuesax-linear:setting" color="#000" />
|
||||
</a-popover>
|
||||
</div>
|
||||
<template #action="{ record }">
|
||||
<div class="gap-2 hidden md:flex justify-center">
|
||||
<a-tooltip :key="record.is_archive" placement="bottom" class="cursor-pointer">
|
||||
<template #title>
|
||||
@@ -144,9 +27,7 @@
|
||||
cancel-text="رد"
|
||||
placement="topLeft"
|
||||
@confirm="archiveFunction(record.id)"
|
||||
@cancel=""
|
||||
>
|
||||
<!-- <template #icon><question-circle-outlined style="color: red" /></template>-->
|
||||
<Icon class="cursor-pointer" icon="vuesax-linear:archive-tick" color="#e3bf00" />
|
||||
</a-popconfirm>
|
||||
</a-tooltip>
|
||||
@@ -157,45 +38,27 @@
|
||||
@click="changeCustomerActivationFunction(record.id)"
|
||||
>
|
||||
<template #title>
|
||||
<span>{{
|
||||
record.active ? 'غیرفعال کردن حساب کاربری' : 'فعال کردن حساب کاربری'
|
||||
}}</span>
|
||||
<span>{{ record.active ? 'غیرفعال کردن حساب کاربری' : 'فعال کردن حساب کاربری' }}</span>
|
||||
</template>
|
||||
<Icon
|
||||
:icon="record.active ? 'vuesax-linear:user-tick' : 'vuesax-linear:user-remove'"
|
||||
:color="record.active ? '#54AD06' : '#ad0627'"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="emits('chargeMoney', record)"
|
||||
>
|
||||
<template #title>
|
||||
<span>شارژ کیف پول ریالی</span>
|
||||
</template>
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('chargeMoney', record)">
|
||||
<template #title><span>شارژ کیف پول ریالی</span></template>
|
||||
<Icon icon="vuesax-linear:wallet-add" color="#9333ea" />
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="emits('chargeGold', record)"
|
||||
>
|
||||
<template #title>
|
||||
<span>شارژ کیف پول طلایی</span>
|
||||
</template>
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('chargeGold', record)">
|
||||
<template #title><span>شارژ کیف پول طلایی</span></template>
|
||||
<Icon icon="vuesax-linear:wallet-money" color="#ea580c" />
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('info', record)">
|
||||
<template #title>
|
||||
<span>مشخصات</span>
|
||||
</template>
|
||||
<template #title><span>مشخصات</span></template>
|
||||
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('edit', record)">
|
||||
<template #title>
|
||||
<span>ویرایش</span>
|
||||
</template>
|
||||
<template #title><span>ویرایش</span></template>
|
||||
<Icon icon="vuesax-linear:edit" color="#4572E9" />
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
@@ -205,42 +68,41 @@
|
||||
@click="changeCustomerOrderActivationFunction(record.id)"
|
||||
>
|
||||
<template #title>
|
||||
<span>{{
|
||||
record.trade_active ? 'غیر مجاز کردن سفارش دهی' : 'مجاز کردن سفارش دهی'
|
||||
}}</span>
|
||||
<span>{{ record.trade_active ? 'غیر مجاز کردن سفارش دهی' : 'مجاز کردن سفارش دهی' }}</span>
|
||||
</template>
|
||||
<Icon
|
||||
:icon="
|
||||
record.trade_active === 1 ? 'vuesax-linear:box-tick' : 'vuesax-linear:box-remove'
|
||||
"
|
||||
:icon="record.trade_active === 1 ? 'vuesax-linear:box-tick' : 'vuesax-linear:box-remove'"
|
||||
:color="record.trade_active === 1 ? '#54AD06' : '#ad0627'"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
v-if="!record.is_legal"
|
||||
:key="record.trade_active"
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="openConfirmUser(record)"
|
||||
>
|
||||
<template #title>
|
||||
<span>تایید کاربر</span>
|
||||
</template>
|
||||
<template #title><span>تایید کاربر</span></template>
|
||||
<Icon icon="vuesax-linear:user-search" color="#0d9488" />
|
||||
</a-tooltip>
|
||||
<!-- <a-tooltip
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="emits('delete', record.id)"
|
||||
>
|
||||
<template #title>
|
||||
<span>حذف</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:trash" color="#EB3535" />
|
||||
</a-tooltip>-->
|
||||
</div>
|
||||
</template>
|
||||
</BaseTable>
|
||||
|
||||
<CustomerMobileCardList
|
||||
v-else
|
||||
ref="mobileListRef"
|
||||
class="min-h-0 flex-1"
|
||||
@info="emits('info', $event)"
|
||||
@edit="emits('edit', $event)"
|
||||
@archive="handleArchive"
|
||||
@toggleActive="(item) => changeCustomerActivationFunction(item.id)"
|
||||
@chargeMoney="emits('chargeMoney', $event)"
|
||||
@chargeGold="emits('chargeGold', $event)"
|
||||
@toggleTrade="(item) => changeCustomerOrderActivationFunction(item.id)"
|
||||
@confirmUser="openConfirmUser"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<BasicModal
|
||||
v-model:visible="confirmVisible"
|
||||
width="50vw"
|
||||
@@ -268,10 +130,9 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="tsx" setup>
|
||||
import type { TableInput } from '@core/type/baseTable'
|
||||
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||
<script setup lang="ts">
|
||||
import { message, Modal } from 'ant-design-vue'
|
||||
import { ref } from 'vue'
|
||||
import {
|
||||
changeCustomerActivation,
|
||||
changeCustomerArchive,
|
||||
@@ -280,59 +141,38 @@ import {
|
||||
getCustomerTable
|
||||
} from '@/api/customer'
|
||||
import { cols } from '@/pages/menuPages/customers/base/hooks/customerColumns'
|
||||
import { useUserStore } from '@/store/userStore'
|
||||
import { usePrefix } from '@core/composable/usePrefix'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { inputRules } from '@/utils/inputRules'
|
||||
import type { User } from '@/model/user'
|
||||
import CustomerMobileCardList from '@/pages/menuPages/customers/base/components/CustomerMobileCardList.vue'
|
||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
||||
import type { TableInput } from '@core/type/baseTable'
|
||||
import { toJalaliNoTime } from '@core/utils/useDataTime'
|
||||
import type { Customer } from '@/model/customer'
|
||||
import { onMounted } from 'vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
const [submit, reload] = useBaseTable()
|
||||
/*const select = (val) => {
|
||||
val.sts === 1 ? return ['1'] : ['2']
|
||||
}*/
|
||||
//emits
|
||||
const emits = defineEmits(['delete', 'edit', 'add', 'info', 'chargeMoney', 'chargeGold'])
|
||||
|
||||
const { prefixCls } = usePrefix('pophoverstatus')
|
||||
|
||||
//permission
|
||||
function checkPermission(temp: string) {
|
||||
const store = useUserStore()
|
||||
return store.canAccess(temp)
|
||||
}
|
||||
|
||||
//define table
|
||||
const table = ref(null)
|
||||
|
||||
const confirmVisible = ref<boolean>(false)
|
||||
const loading = ref(false)
|
||||
const confirmVisible = ref(false)
|
||||
const confirmInfo = ref<Customer>()
|
||||
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||
|
||||
const tblProps = ref<any>({
|
||||
columns: cols,
|
||||
api: (params: TableInput) => {
|
||||
return getCustomerTable(params)
|
||||
}
|
||||
//bordered: true
|
||||
api: (params: TableInput) => getCustomerTable(params)
|
||||
})
|
||||
const refPop = ref()
|
||||
|
||||
// function edit(recordId){
|
||||
// emits('edit', recordId);
|
||||
// console.log( recordId);
|
||||
// console.log( visibleAction.value[parseInt(recordId)]);
|
||||
// visibleAction.value[recordId]=false
|
||||
// console.log('ddddd')
|
||||
// }
|
||||
const loading = ref<boolean>(false)
|
||||
const fetch = async () => {
|
||||
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||
}
|
||||
|
||||
defineExpose({ fetch })
|
||||
|
||||
async function changeCustomerActivationFunction(id: number) {
|
||||
try {
|
||||
loading.value = true
|
||||
await changeCustomerActivation(id)
|
||||
reload()
|
||||
await fetch()
|
||||
message.success('تغییر وضعیت با موفقیت انجام شد')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
@@ -346,11 +186,21 @@ function openConfirmUser(user: Customer) {
|
||||
confirmVisible.value = true
|
||||
}
|
||||
|
||||
function handleArchive(customer: Customer) {
|
||||
Modal.confirm({
|
||||
title: 'آیا مشتری را بایگانی می کنید؟',
|
||||
okText: 'تایید',
|
||||
cancelText: 'رد',
|
||||
centered: true,
|
||||
onOk: () => archiveFunction(customer.id)
|
||||
})
|
||||
}
|
||||
|
||||
async function archiveFunction(id: number) {
|
||||
try {
|
||||
loading.value = true
|
||||
await changeCustomerArchive(id)
|
||||
reload()
|
||||
await fetch()
|
||||
message.success('مشتری با موفقیت بایگانی شد.')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
@@ -363,7 +213,7 @@ async function confirmUserFunction() {
|
||||
try {
|
||||
loading.value = true
|
||||
await confirmUser(confirmInfo.value?.id)
|
||||
reload()
|
||||
await fetch()
|
||||
confirmVisible.value = false
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
@@ -376,7 +226,7 @@ async function changeCustomerOrderActivationFunction(id: number) {
|
||||
try {
|
||||
loading.value = true
|
||||
await changeCustomerOrderActivation(id)
|
||||
reload()
|
||||
await fetch()
|
||||
message.success('تغییر وضعیت سفارش دهی با موفقیت انجام شد')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
@@ -384,42 +234,11 @@ async function changeCustomerOrderActivationFunction(id: number) {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const fetch = () => {
|
||||
// console.log('sss')
|
||||
reload()
|
||||
}
|
||||
// const spinTable = (val: boolean) => {
|
||||
// // console.log('sss')
|
||||
// table?.value?.loading.value = val
|
||||
// }
|
||||
defineExpose({
|
||||
fetch
|
||||
// spinTable
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@prefix: ~'@{prefixCls}-pophoverstatus';
|
||||
.ant-popover,
|
||||
.ant-tooltip {
|
||||
z-index: 1001 !important;
|
||||
}
|
||||
|
||||
.@{prefix} {
|
||||
.ant-menu-rtl.ant-menu-inline,
|
||||
.ant-menu-rtl.ant-menu-vertical {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.ant-menu-item-only-child {
|
||||
border-radius: @border-radius-base;
|
||||
}
|
||||
|
||||
//bor
|
||||
/* .ant-input-group-addon:first-child {
|
||||
!* border: #d9d9d9 1px solid;
|
||||
border-radius: 0 6px 6px 0;*!
|
||||
}*/
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -208,7 +208,6 @@ export const cols: BaseTableColumn[] = [
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
slot: 'action',
|
||||
fixed: 'right',
|
||||
filterable: false
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- <BasicMainCard>-->
|
||||
<div class="flex w-full h-full">
|
||||
<tbl
|
||||
ref="table"
|
||||
class="w-full h-full"
|
||||
<div class="h-full min-h-0 min-w-0 overflow-hidden">
|
||||
<tbl
|
||||
ref="table"
|
||||
class="h-full min-h-0 w-full"
|
||||
@info="info"
|
||||
@edit="edit"
|
||||
@add="add"
|
||||
@@ -15,16 +13,21 @@
|
||||
<!-- <BasicMenu :menu="menu" width="13rem" @onChange="tabSelected" />
|
||||
<component :is="selectedComponent" class="mr-5" />-->
|
||||
<!-- <BasicShowContext title="پروژه ها"> <Search /> </BasicShowContext>-->
|
||||
</div>
|
||||
<!-- </BasicMainCard>-->
|
||||
<BasicModal
|
||||
<BasicActionSheet
|
||||
v-model:visible="addUserVisible"
|
||||
width="40rem"
|
||||
:title="`${editId ? 'ویرایش' : 'افزودن'} کاربر`"
|
||||
@ok="ok"
|
||||
modal-width="40rem"
|
||||
:loading="loading"
|
||||
:showButton="true"
|
||||
:showCancel="true"
|
||||
:showOk="true"
|
||||
ok-text="ثبت"
|
||||
cancel-text="انصراف"
|
||||
@ok="ok"
|
||||
@close="addUserVisible = false"
|
||||
>
|
||||
<div v-if="isMobile" class="mb-3 text-base font-700">{{ `${editId ? 'ویرایش' : 'افزودن'} کاربر` }}</div>
|
||||
<a-form :model="form" ref="formRef" autocomplete="off">
|
||||
<div class="grid md:grid-cols-2 gap-4">
|
||||
<a-form-item label="نام" name="fname" :rules="inputRules()">
|
||||
@@ -88,19 +91,20 @@
|
||||
</a-form-item>-->
|
||||
</div>
|
||||
</a-form>
|
||||
</BasicModal>
|
||||
<BasicModal
|
||||
</BasicActionSheet>
|
||||
<BasicActionSheet
|
||||
v-model:visible="userInfoVisible"
|
||||
width="100vw"
|
||||
title="اطلاعات کاربر"
|
||||
modal-width="100vw"
|
||||
:loading="loading"
|
||||
custom-footer
|
||||
:showButton="false"
|
||||
@close="userInfoVisible = false"
|
||||
>
|
||||
<a-tabs v-model:activeKey="activeKey">
|
||||
<div v-if="isMobile" class="mb-3 text-base font-700">اطلاعات کاربر</div>
|
||||
<a-tabs v-if="userInfo" v-model:activeKey="activeKey">
|
||||
<a-tab-pane key="اطلاعات جامع" tab="اطلاعات جامع">
|
||||
<div class="grid grid-cols-4 gap-2">
|
||||
<div class="col-span-4 bg-gray-100 w-full rounded-lg flex p-3 justify-between">
|
||||
<div class="space-y-2">
|
||||
<div class="rounded-lg bg-gray-100 w-full flex flex-col gap-3 p-3 md:flex-row md:justify-between">
|
||||
<div>
|
||||
<div class="font-bold">{{ userInfo.name }}</div>
|
||||
<div class="mt-1">{{ userInfo?.mobile }}</div>
|
||||
@@ -135,47 +139,51 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BasicInfoItem title="کد کاربری" :value="userInfo?.id" />
|
||||
<BasicInfoItem title="نوع کاربر" :value="userInfo?.type_str" />
|
||||
<BasicInfoItem
|
||||
title="تاریخ تولد"
|
||||
:value="userInfo?.birth_date ? toJalaliNoTime(userInfo?.birth_date) : '_'"
|
||||
/>
|
||||
<BasicInfoItem title="شماره ملی" :value="userInfo?.n_code ?? '_'" />
|
||||
<BasicInfoItem title="نام استان" :value="userInfo?.province ?? '_'" />
|
||||
<BasicInfoItem title="نام شهر" :value="userInfo?.city ?? '_'" />
|
||||
<BasicInfoItem
|
||||
title="سقف اعتبار"
|
||||
:value="userInfo?.gram ? `${userInfo?.gram} گرم` : '_'"
|
||||
/>
|
||||
<BasicInfoItem title="کد معرف" :value="userInfo?.referral_id" />
|
||||
<BasicInfoItem
|
||||
title="کد معرف کاربر"
|
||||
:value="userInfo?.referral ? userInfo?.referral?.id : '_'"
|
||||
/>
|
||||
<BasicInfoItem
|
||||
title="نام معرف کاربر"
|
||||
:value="userInfo?.referral ? userInfo?.referral?.name : '_'"
|
||||
/>
|
||||
<BasicInfoItem
|
||||
title="میزان سود معرفی"
|
||||
:value="userInfo?.profit_amount ? `${userInfo?.profit_amount} گرم` : '_'"
|
||||
/>
|
||||
<BasicInfoItem
|
||||
title="حداکثر معرفی"
|
||||
:value="userInfo?.add_user_limit ? `${userInfo?.add_user_limit} نفر` : '_'"
|
||||
/>
|
||||
<BasicInfoItem title="آخرین بازدید" :value="toJalali(userInfo?.last_seen_at)" />
|
||||
<BasicInfoItem title="وضعیت سفارش دهی">
|
||||
<CustomTag
|
||||
:textColor="userInfo?.trade_active === 1 ? '#25855A' : '#C53030'"
|
||||
:bgColor="userInfo?.trade_active === 1 ? '#E3F4E9' : '#FECACA'"
|
||||
class="font-bold px-6"
|
||||
>
|
||||
{{ userInfo?.trade_active === 1 ? 'مجاز' : 'غیر مجاز' }}
|
||||
</CustomTag>
|
||||
</BasicInfoItem>
|
||||
<div class="col-span-4 grid grid-cols-2 gap-3">
|
||||
|
||||
<div class="grid grid-cols-2 gap-2 md:grid-cols-4">
|
||||
<BasicInfoItem title="کد کاربری" :value="userInfo?.id" />
|
||||
<BasicInfoItem title="نوع کاربر" :value="userInfo?.type_str" />
|
||||
<BasicInfoItem
|
||||
title="تاریخ تولد"
|
||||
:value="userInfo?.birth_date ? toJalaliNoTime(userInfo?.birth_date) : '_'"
|
||||
/>
|
||||
<BasicInfoItem title="شماره ملی" :value="userInfo?.n_code ?? '_'" />
|
||||
<BasicInfoItem title="نام استان" :value="userInfo?.province ?? '_'" />
|
||||
<BasicInfoItem title="نام شهر" :value="userInfo?.city ?? '_'" />
|
||||
<BasicInfoItem
|
||||
title="سقف اعتبار"
|
||||
:value="userInfo?.gram ? `${userInfo?.gram} گرم` : '_'"
|
||||
/>
|
||||
<BasicInfoItem title="کد معرف" :value="userInfo?.referral_id" />
|
||||
<BasicInfoItem
|
||||
title="کد معرف کاربر"
|
||||
:value="userInfo?.referral ? userInfo?.referral?.id : '_'"
|
||||
/>
|
||||
<BasicInfoItem
|
||||
title="نام معرف کاربر"
|
||||
:value="userInfo?.referral ? userInfo?.referral?.name : '_'"
|
||||
/>
|
||||
<BasicInfoItem
|
||||
title="میزان سود معرفی"
|
||||
:value="userInfo?.profit_amount ? `${userInfo?.profit_amount} گرم` : '_'"
|
||||
/>
|
||||
<BasicInfoItem
|
||||
title="حداکثر معرفی"
|
||||
:value="userInfo?.add_user_limit ? `${userInfo?.add_user_limit} نفر` : '_'"
|
||||
/>
|
||||
<BasicInfoItem title="آخرین بازدید" :value="toJalali(userInfo?.last_seen_at)" />
|
||||
<BasicInfoItem title="وضعیت سفارش دهی" class="col-span-2 md:col-span-1">
|
||||
<CustomTag
|
||||
:textColor="userInfo?.trade_active === 1 ? '#25855A' : '#C53030'"
|
||||
:bgColor="userInfo?.trade_active === 1 ? '#E3F4E9' : '#FECACA'"
|
||||
class="font-bold px-6"
|
||||
>
|
||||
{{ userInfo?.trade_active === 1 ? 'مجاز' : 'غیر مجاز' }}
|
||||
</CustomTag>
|
||||
</BasicInfoItem>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-3 md:grid-cols-2">
|
||||
<div
|
||||
v-for="(item, index) in userInfo.wallet"
|
||||
:key="index"
|
||||
@@ -206,7 +214,7 @@
|
||||
<FundsTable ref="fundTable" :customerId="userInfo?.id" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</BasicModal>
|
||||
</BasicActionSheet>
|
||||
<BasicModal
|
||||
v-model:visible="showMoneyWalletCharge"
|
||||
width="20rem"
|
||||
@@ -275,6 +283,7 @@ import { ref, watch } from 'vue'
|
||||
import tbl from './components/table.vue'
|
||||
import type { FormInstance } from 'ant-design-vue'
|
||||
import BasicInfoModal from '@/components/BasicInfoModal/index.vue'
|
||||
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
||||
import { message } from 'ant-design-vue/es'
|
||||
import type { Info } from '@/components/BasicInfoModal/model/Info'
|
||||
import { toJalali, toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
||||
@@ -295,6 +304,7 @@ import FundsTable from '@/pages/menuPages/customers/base/components/fundsTable.v
|
||||
import ReferralsTable from '@/pages/menuPages/customers/base/components/referralsTable.vue'
|
||||
import type { Order } from '@/model/order'
|
||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
//tabs
|
||||
const activeKey = ref('اطلاعات جامع')
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full min-h-0">
|
||||
<BasicMainCard>
|
||||
<div class="flex flex-col md:flex-row md:h-full">
|
||||
<div class="flex h-full min-h-0 min-w-0 flex-col md:flex-row">
|
||||
<!-- {{ menu }}-->
|
||||
<BasicMenu width="11rem" :menu="menu" mode="horizontal" class="md:hidden overflow-auto" />
|
||||
<BasicMenu width="11rem" :menu="menu" mode="horizontal" class="shrink-0 overflow-auto md:hidden" />
|
||||
<BasicMenu
|
||||
width="13rem"
|
||||
:menu="menu"
|
||||
class="hidden md:inline overflow-auto min-w-[13.5rem]"
|
||||
class="hidden min-w-[13.5rem] shrink-0 overflow-auto md:inline"
|
||||
/>
|
||||
<!-- <component :is="selectedComponent" class="mr-5" />-->
|
||||
<RouterView class="md:mr-5 md:mt-0 mt-5 w-full"></RouterView>
|
||||
<RouterView class="mt-5 min-h-0 min-w-0 w-full flex-1 overflow-hidden md:mr-5 md:mt-0" />
|
||||
</div>
|
||||
</BasicMainCard>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<MobileDetailSheet v-model:visible="visibleProxy" :item="item" :title="sheetTitle">
|
||||
<template #default="{ item: row }">
|
||||
<MobileSheetHeader :title="sheetTitle" :show-title="isMobile" />
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="rounded-lg border px-2 py-0.5 text-xs font-600" :class="unitTagClass(row)">
|
||||
{{ unitLabel(row) }}
|
||||
</span>
|
||||
<span class="rounded-lg border px-2 py-0.5 text-xs font-600" :class="statusClass(row)">
|
||||
{{ statusLabel(row) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<MobileStatCard bordered label="مقدار" :value="amountLabel(row)">
|
||||
<template #prefix>
|
||||
<BaseIcon
|
||||
:icon="row.unit === 2 ? 'vuesax-linear:coin' : 'vuesax-linear:empty-wallet'"
|
||||
size="18"
|
||||
color="#2563eb"
|
||||
/>
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
|
||||
<MobileInfoGrid>
|
||||
<MobileDetailRow label="کد" full-width :value="row.code || '-'" />
|
||||
<MobileDetailRow label="تاریخ انقضا" full-width :value="dateLabel(row.expiry_date)" />
|
||||
<MobileDetailRow label="تاریخ استفاده" full-width :value="dateLabel(row.used_at)" />
|
||||
<MobileDetailRow label="نام کاربر" full-width :value="row.assigned_to?.name || '-'" />
|
||||
<MobileDetailRow label="موبایل کاربر" full-width :value="row.assigned_to?.mobile || '-'" />
|
||||
</MobileInfoGrid>
|
||||
</template>
|
||||
</MobileDetailSheet>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Giftcardcode } from '@/model/giftCard'
|
||||
import MobileDetailRow from '@/components/mobile/MobileDetailRow.vue'
|
||||
import MobileDetailSheet from '@/components/mobile/MobileDetailSheet.vue'
|
||||
import MobileInfoGrid from '@/components/mobile/MobileInfoGrid.vue'
|
||||
import MobileSheetHeader from '@/components/mobile/MobileSheetHeader.vue'
|
||||
import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import { toJalali } from '@core/utils/useDataTime'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
item: Giftcardcode | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
}>()
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
set: (value: boolean) => emit('update:visible', value)
|
||||
})
|
||||
|
||||
const sheetTitle = computed(() => props.item?.code || 'کد کارت هدیه')
|
||||
|
||||
function statusLabel(row: Giftcardcode) {
|
||||
switch (row.sts) {
|
||||
case 1:
|
||||
return 'فعال'
|
||||
case 2:
|
||||
return 'استفاده شده'
|
||||
default:
|
||||
return 'غیرفعال'
|
||||
}
|
||||
}
|
||||
|
||||
function statusClass(row: Giftcardcode) {
|
||||
switch (row.sts) {
|
||||
case 1:
|
||||
return 'border-amber-300 bg-amber-50 text-amber-700'
|
||||
case 2:
|
||||
return 'border-green-300 bg-green-50 text-green-700'
|
||||
default:
|
||||
return 'border-red-300 bg-red-50 text-red-600'
|
||||
}
|
||||
}
|
||||
|
||||
function unitLabel(row: Giftcardcode) {
|
||||
return row.unit === 2 ? 'طلایی' : 'ریالی'
|
||||
}
|
||||
|
||||
function unitTagClass(row: Giftcardcode) {
|
||||
return row.unit === 2
|
||||
? 'border-amber-300 bg-amber-50 text-amber-700'
|
||||
: 'border-green-300 bg-green-50 text-green-700'
|
||||
}
|
||||
|
||||
function amountLabel(row: Giftcardcode) {
|
||||
return `${row.amount ?? 0} ${row.unit === 2 ? 'گرم' : 'ریال'}`
|
||||
}
|
||||
|
||||
function dateLabel(value?: string) {
|
||||
return value ? toJalali(value) : '-'
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<MobileCard clickable @click="$emit('open', item)">
|
||||
<div class="space-y-1">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="truncate font-mono text-sm font-700 text-gray-900">{{ item.code || '-' }}</span>
|
||||
<span class="shrink-0 rounded-md border px-1.5 py-0.5 text-xs font-600" :class="statusClass">
|
||||
{{ statusLabel }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between gap-2 text-xs text-gray-600">
|
||||
<span class="rounded-md border px-1.5 py-0.5 font-600" :class="unitTagClass">{{ unitLabel }}</span>
|
||||
<span class="font-600 text-gray-900">{{ amountLabel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</MobileCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Giftcardcode } from '@/model/giftCard'
|
||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
item: Giftcardcode
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'open', item: Giftcardcode): void
|
||||
}>()
|
||||
|
||||
const statusLabel = computed(() => {
|
||||
switch (props.item.sts) {
|
||||
case 1:
|
||||
return 'فعال'
|
||||
case 2:
|
||||
return 'استفاده شده'
|
||||
default:
|
||||
return 'غیرفعال'
|
||||
}
|
||||
})
|
||||
|
||||
const statusClass = computed(() => {
|
||||
switch (props.item.sts) {
|
||||
case 1:
|
||||
return 'border-amber-300 bg-amber-50 text-amber-700'
|
||||
case 2:
|
||||
return 'border-green-300 bg-green-50 text-green-700'
|
||||
default:
|
||||
return 'border-red-300 bg-red-50 text-red-600'
|
||||
}
|
||||
})
|
||||
|
||||
const unitLabel = computed(() => (props.item.unit === 2 ? 'طلایی' : 'ریالی'))
|
||||
const unitTagClass = computed(() =>
|
||||
props.item.unit === 2
|
||||
? 'border-amber-300 bg-amber-50 text-amber-700'
|
||||
: 'border-green-300 bg-green-50 text-green-700'
|
||||
)
|
||||
|
||||
const amountLabel = computed(() => `${props.item.amount ?? 0} ${props.item.unit === 2 ? 'گرم' : 'ریال'}`)
|
||||
</script>
|
||||
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<BasicActionSheet
|
||||
v-model:visible="visibleProxy"
|
||||
title="لیست کدهای کارت هدیه"
|
||||
modal-width="80%"
|
||||
drawer-height="90vh"
|
||||
:showButton="true"
|
||||
:showCancel="true"
|
||||
:showOk="false"
|
||||
cancel-text="بستن"
|
||||
@cancel="close"
|
||||
@close="close"
|
||||
>
|
||||
<div v-if="isMobile" class="mb-3 text-base font-700">لیست کدهای کارت هدیه</div>
|
||||
|
||||
<a-table
|
||||
class="hidden md:block"
|
||||
:columns="columns"
|
||||
:data-source="codes"
|
||||
:pagination="{ pageSize: 10 }"
|
||||
row-key="id"
|
||||
>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
||||
<template v-else-if="column.key === 'sts'">
|
||||
<CustomTag v-if="record.sts === 1" textColor="#ca8a04" bgColor="#fef3c7">فعال</CustomTag>
|
||||
<CustomTag v-else-if="record.sts === 2" textColor="#25855A" bgColor="#E3F4E9">استفاده شده</CustomTag>
|
||||
<CustomTag v-else textColor="#C53030" bgColor="#FECACA">غیرفعال</CustomTag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'unit'">
|
||||
<CustomTag v-if="record.unit === 2" textColor="#C53030" bgColor="#FECACA">طلایی</CustomTag>
|
||||
<CustomTag v-else textColor="#25855A" bgColor="#E3F4E9">ریالی</CustomTag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'amount'">
|
||||
{{ record.amount }} {{ record.unit === 2 ? 'گرم' : 'ریال' }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'expiry_date'">
|
||||
{{ record.expiry_date ? toJalali(record.expiry_date) : '-' }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'used_at'">
|
||||
{{ record.used_at ? toJalali(record.used_at) : '-' }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'assigned_to_name'">
|
||||
{{ record.assigned_to?.name ?? '-' }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'assigned_to_mobile'">
|
||||
{{ record.assigned_to?.mobile ?? '-' }}
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<div class="space-y-2 md:hidden">
|
||||
<GiftCardCodeMobileListItem
|
||||
v-for="item in codes"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
@open="openSheet"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<GiftCardCodeMobileDetailSheet v-model:visible="sheetVisible" :item="selectedItem" />
|
||||
</BasicActionSheet>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Giftcardcode } from '@/model/giftCard'
|
||||
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
||||
import CustomTag from '@/components/CustomTag.vue'
|
||||
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import { toJalali } from '@core/utils/useDataTime'
|
||||
import GiftCardCodeMobileDetailSheet from './GiftCardCodeMobileDetailSheet.vue'
|
||||
import GiftCardCodeMobileListItem from './GiftCardCodeMobileListItem.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
codes: Giftcardcode[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
}>()
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
set: (value: boolean) => emit('update:visible', value)
|
||||
})
|
||||
|
||||
const { sheetVisible, selectedItem, openSheet } = useMobileDetailSheet<Giftcardcode>()
|
||||
|
||||
const columns = [
|
||||
{ title: 'ردیف', key: 'index', dataIndex: 'index' },
|
||||
{ title: 'کد', dataIndex: 'code', key: 'code' },
|
||||
{ title: 'نوع', dataIndex: 'unit', key: 'unit' },
|
||||
{ title: 'مقدار', dataIndex: 'amount', key: 'amount' },
|
||||
{ title: 'وضعیت', dataIndex: 'sts', key: 'sts' },
|
||||
{ title: 'تاریخ انقضا', dataIndex: 'expiry_date', key: 'expiry_date' },
|
||||
{ title: 'تاریخ استفاده', dataIndex: 'used_at', key: 'used_at' },
|
||||
{ title: 'نام کاربر اختصاص داده شده', dataIndex: 'assigned_to.name', key: 'assigned_to_name' },
|
||||
{ title: 'شماره موبایل کاربر اختصاص داده شده', dataIndex: 'assigned_to.mobile', key: 'assigned_to_mobile' }
|
||||
]
|
||||
|
||||
function close() {
|
||||
visibleProxy.value = false
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<a-form :model="form" ref="formRef" autocomplete="off">
|
||||
<div class="space-y-3">
|
||||
<a-form-item label="شماره موبایل مشتری" name="mobile" :rules="inputLenRules(11)">
|
||||
<BasicInput v-model:value="form.mobile" type="tel" placeholder="09123456789" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="نام مشتری" name="name" :rules="inputRules()">
|
||||
<BasicInput v-model:value="form.name" placeholder="نام و نام خانوادگی مشتری" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="نوع کارت هدیه" name="unit" :rules="inputRules()">
|
||||
<a-radio-group v-model:value="form.unit" button-style="solid">
|
||||
<a-radio-button value="1">ریالی</a-radio-button>
|
||||
<a-radio-button value="2">طلایی</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="تعداد کدها" name="count" :rules="inputRulesNumber()">
|
||||
<BasicInput v-model:value="form.count" type="tel" placeholder="تعداد کدهای کارت هدیه" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
:label="`مقدار ${form.unit === '2' ? '(گرم)' : '(ریال)'}`"
|
||||
name="amount"
|
||||
:rules="inputRulesNumber()"
|
||||
>
|
||||
<BasicInput
|
||||
v-model:value="form.amount"
|
||||
:addon-before="form.unit === '2' ? 'گرم' : 'ریال'"
|
||||
type="tel"
|
||||
placeholder="مقدار کارت هدیه"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="تاریخ انقضا" name="expiry_date" :rules="inputRules()">
|
||||
<BaseDatePicker v-model:value="form.expiry_date" format="YYYY-MM-DD" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="توضیحات" name="description" class="mb-0">
|
||||
<BasicTextArea v-model:value="form.description" place-holder="توضیحات اختیاری" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormInstance } from 'ant-design-vue'
|
||||
import type { GiftcardInput } from '@/model/giftCard'
|
||||
import { inputLenRules, inputRules, inputRulesNumber } from '@/utils/inputRules'
|
||||
|
||||
defineProps<{
|
||||
form: GiftcardInput
|
||||
}>()
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
defineExpose({
|
||||
validateFields: () => formRef.value?.validateFields(),
|
||||
resetFields: () => formRef.value?.resetFields()
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<MobileCardList
|
||||
v-model:search="search"
|
||||
:loading="loading"
|
||||
:has-more="hasMore"
|
||||
:show-empty="!loading && items.length === 0"
|
||||
empty-text="کارت هدیهای یافت نشد"
|
||||
search-placeholder="جستجو کارت هدیه"
|
||||
show-refresh
|
||||
@search="reload"
|
||||
@refresh="reload"
|
||||
@load-more="loadMore"
|
||||
>
|
||||
<GiftCardMobileListItem
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
:hide-customer="!!customerId"
|
||||
@open="openSheet"
|
||||
@gift-card-codes="$emit('giftCardCodes', $event)"
|
||||
/>
|
||||
</MobileCardList>
|
||||
|
||||
<GiftCardMobileDetailSheet v-model:visible="sheetVisible" :item="selectedItem" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getGiftCardTable } from '@/api/giftCard'
|
||||
import type { Giftcard } from '@/model/giftCard'
|
||||
import MobileCardList from '@/components/mobile/MobileCardList.vue'
|
||||
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
||||
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
||||
import type { TableInput } from '@core/type/baseTable'
|
||||
import GiftCardMobileDetailSheet from './GiftCardMobileDetailSheet.vue'
|
||||
import GiftCardMobileListItem from './GiftCardMobileListItem.vue'
|
||||
|
||||
type GiftCardRow = Giftcard & { customer?: { name?: string; mobile?: string } }
|
||||
|
||||
const props = defineProps<{
|
||||
customerId?: number
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'giftCardCodes', item: GiftCardRow): void
|
||||
}>()
|
||||
|
||||
const { sheetVisible, selectedItem, openSheet, syncSelectedItem } = useMobileDetailSheet<GiftCardRow>()
|
||||
|
||||
function fetchGiftCards(params: TableInput) {
|
||||
if (props.customerId) {
|
||||
params.filters = params.filters ?? []
|
||||
params.filters.push({
|
||||
field: 'customer_id',
|
||||
type: 'text',
|
||||
value: props.customerId,
|
||||
cr: 'and',
|
||||
op: 'eq',
|
||||
hidden: false
|
||||
})
|
||||
}
|
||||
return getGiftCardTable(params)
|
||||
}
|
||||
|
||||
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<GiftCardRow>({
|
||||
fetchApi: fetchGiftCards,
|
||||
onFetched: (rows) => syncSelectedItem(rows)
|
||||
})
|
||||
|
||||
onMounted(() => reload())
|
||||
|
||||
defineExpose({ fetch, openSheet })
|
||||
</script>
|
||||
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<MobileDetailSheet v-model:visible="visibleProxy" :item="item" :title="sheetTitle">
|
||||
<template #default="{ item: row }">
|
||||
<MobileSheetHeader :title="sheetTitle" :subtitle="createdAtLabel(row)" :show-title="isMobile" />
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="rounded-lg border px-2 py-0.5 text-xs font-600" :class="unitTagClass(row)">
|
||||
{{ unitLabel(row) }}
|
||||
</span>
|
||||
<span
|
||||
v-if="row.sts_str"
|
||||
class="rounded-lg border px-2 py-0.5 text-xs font-600"
|
||||
:class="statusClass(row)"
|
||||
>
|
||||
{{ row.sts_str }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<MobileStatCard bordered variant="success" :label="amountLabel(row)" :value="amountValue(row)">
|
||||
<template #prefix>
|
||||
<BaseIcon
|
||||
:icon="row.unit === 2 ? 'vuesax-linear:coin' : 'vuesax-linear:empty-wallet'"
|
||||
size="18"
|
||||
color="#15803d"
|
||||
/>
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
<MobileStatCard
|
||||
v-if="row.unit !== 2"
|
||||
bordered
|
||||
label="مبلغ کل (ریال)"
|
||||
:value="usePrice(row.total ?? 0, true, '')"
|
||||
>
|
||||
<template #prefix>
|
||||
<BaseIcon icon="vuesax-linear:money-send" size="18" color="#2563eb" />
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
</div>
|
||||
|
||||
<MobileInfoGrid>
|
||||
<MobileDetailRow label="کد" :value="row.id ?? '-'" />
|
||||
<MobileDetailRow label="تعداد کدها" :value="row.count ?? '-'" />
|
||||
<MobileDetailRow label="تعداد استفاده شده" full-width :value="row.used_count ?? '-'" />
|
||||
<MobileDetailRow label="کد یکتا" full-width :value="row.unique_code || '-'" />
|
||||
<MobileDetailRow label="نام مشتری" full-width :value="row.customer?.name || '-'" />
|
||||
<MobileDetailRow label="موبایل مشتری" full-width :value="row.customer?.mobile ?? '-'" />
|
||||
<MobileDetailRow label="تاریخ ثبت" full-width :value="createdAtLabel(row)" />
|
||||
<MobileDetailRow label="تاریخ انقضا" full-width :value="expiryLabel(row)" />
|
||||
<MobileDetailRow label="توضیحات" full-width :value="row.description || '-'" />
|
||||
</MobileInfoGrid>
|
||||
</template>
|
||||
</MobileDetailSheet>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Giftcard } from '@/model/giftCard'
|
||||
import MobileDetailRow from '@/components/mobile/MobileDetailRow.vue'
|
||||
import MobileDetailSheet from '@/components/mobile/MobileDetailSheet.vue'
|
||||
import MobileInfoGrid from '@/components/mobile/MobileInfoGrid.vue'
|
||||
import MobileSheetHeader from '@/components/mobile/MobileSheetHeader.vue'
|
||||
import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import { toJalali, toJalaliNoTime } from '@core/utils/useDataTime'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
|
||||
type GiftCardRow = Giftcard & { customer?: { name?: string; mobile?: string } }
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
item: GiftCardRow | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
}>()
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
set: (value: boolean) => emit('update:visible', value)
|
||||
})
|
||||
|
||||
const sheetTitle = computed(() => props.item?.unique_code || `کارت هدیه #${props.item?.id ?? '-'}`)
|
||||
|
||||
function unitLabel(row: GiftCardRow) {
|
||||
return row.unit === 2 ? 'طلایی' : 'ریالی'
|
||||
}
|
||||
|
||||
function unitTagClass(row: GiftCardRow) {
|
||||
return row.unit === 2
|
||||
? 'border-amber-300 bg-amber-50 text-amber-700'
|
||||
: 'border-green-300 bg-green-50 text-green-700'
|
||||
}
|
||||
|
||||
function statusClass(row: GiftCardRow) {
|
||||
switch (row.sts) {
|
||||
case 1:
|
||||
return 'border-amber-300 bg-amber-50 text-amber-700'
|
||||
case 2:
|
||||
return 'border-green-300 bg-green-50 text-green-700'
|
||||
default:
|
||||
return 'border-red-300 bg-red-50 text-red-600'
|
||||
}
|
||||
}
|
||||
|
||||
function amountLabel(row: GiftCardRow) {
|
||||
return row.unit === 2 ? 'مقدار (گرم)' : 'مقدار (ریال)'
|
||||
}
|
||||
|
||||
function amountValue(row: GiftCardRow) {
|
||||
if (row.unit === 2) return `${row.amount ?? 0} گرم`
|
||||
return usePrice(row.amount ?? 0, true, '')
|
||||
}
|
||||
|
||||
function createdAtLabel(row: GiftCardRow) {
|
||||
return row.created_at ? toJalali(row.created_at) : '-'
|
||||
}
|
||||
|
||||
function expiryLabel(row: GiftCardRow) {
|
||||
return row.expiry_date ? toJalali(row.expiry_date) : '-'
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<MobileCard clickable @click="$emit('open', item)">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="min-w-0 flex-1 space-y-1">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="flex min-w-0 items-center gap-2">
|
||||
<span class="shrink-0 rounded-md border px-1.5 py-0.5 text-xs font-600" :class="unitTagClass">
|
||||
{{ unitLabel }}
|
||||
</span>
|
||||
<span class="truncate text-sm font-700 text-gray-900">{{ uniqueCodeLabel }}</span>
|
||||
</div>
|
||||
<span class="shrink-0 rounded-md border px-1.5 py-0.5 text-xs font-600" :class="statusClass">
|
||||
{{ statusLabel }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div v-if="!hideCustomer" class="truncate text-xs text-gray-600">{{ customerName }}</div>
|
||||
</div>
|
||||
|
||||
<MobileIconButton
|
||||
icon="vuesax-linear:document-text"
|
||||
color="#1890ff"
|
||||
:size="20"
|
||||
@click="$emit('giftCardCodes', item)"
|
||||
/>
|
||||
</div>
|
||||
</MobileCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Giftcard } from '@/model/giftCard'
|
||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||
import MobileIconButton from '@/components/mobile/MobileIconButton.vue'
|
||||
|
||||
type GiftCardRow = Giftcard & { customer?: { name?: string; mobile?: string } }
|
||||
|
||||
const props = defineProps<{
|
||||
item: GiftCardRow
|
||||
hideCustomer?: boolean
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'open', item: GiftCardRow): void
|
||||
(e: 'giftCardCodes', item: GiftCardRow): void
|
||||
}>()
|
||||
|
||||
const unitLabel = computed(() => (props.item.unit === 2 ? 'طلایی' : 'ریالی'))
|
||||
const unitTagClass = computed(() =>
|
||||
props.item.unit === 2
|
||||
? 'border-amber-300 bg-amber-50 text-amber-700'
|
||||
: 'border-green-300 bg-green-50 text-green-700'
|
||||
)
|
||||
|
||||
const uniqueCodeLabel = computed(() => props.item.unique_code || `#${props.item.id ?? '-'}`)
|
||||
const customerName = computed(() => props.item.customer?.name || '-')
|
||||
const statusLabel = computed(() => props.item.sts_str || '-')
|
||||
|
||||
const statusClass = computed(() => {
|
||||
switch (props.item.sts) {
|
||||
case 1:
|
||||
return 'border-amber-300 bg-amber-50 text-amber-700'
|
||||
case 2:
|
||||
return 'border-green-300 bg-green-50 text-green-700'
|
||||
default:
|
||||
return 'border-red-300 bg-red-50 text-red-600'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,7 +1,15 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||
<BasicShowContext title="لیست کارت های هدیه">
|
||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template v-if="!customerId" #extra>
|
||||
<BaseButton type="primary" size="small" class="flex shrink-0 md:hidden" @click="emits('add')">
|
||||
<div>افزودن</div>
|
||||
<BaseIcon icon="vuesax-linear:receipt-add" Size="16px" />
|
||||
</BaseButton>
|
||||
</template>
|
||||
|
||||
<div class="flex h-full min-h-0 flex-col">
|
||||
<BaseTable v-if="!isMobile" class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #extraHeader>
|
||||
<BaseButton v-if="!customerId" type="primary" size="small" class="flex" @click="emits('add')">
|
||||
<div>افزودن کارت هدیه</div>
|
||||
@@ -25,6 +33,15 @@
|
||||
</div>
|
||||
</template>
|
||||
</BaseTable>
|
||||
|
||||
<GiftCardMobileCardList
|
||||
v-else
|
||||
ref="mobileListRef"
|
||||
class="min-h-0 flex-1"
|
||||
:customer-id="customerId"
|
||||
@gift-card-codes="emits('giftcardcodes', $event)"
|
||||
/>
|
||||
</div>
|
||||
</BasicShowContext>
|
||||
</div>
|
||||
</template>
|
||||
@@ -33,81 +50,47 @@
|
||||
import type { TableInput } from '@core/type/baseTable'
|
||||
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||
import { changeCustomerActivation, getCustomerTable } from '@/api/customer'
|
||||
import { cols } from '../hooks/orderColumns'
|
||||
import { useUserStore } from '@/store/userStore'
|
||||
import { usePrefix } from '@core/composable/usePrefix'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { getOrderTable } from '@/api/order'
|
||||
import {getGiftCardTable} from "@/api/giftCard";
|
||||
import { getGiftCardTable } from '@/api/giftCard'
|
||||
import GiftCardMobileCardList from './GiftCardMobileCardList.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
interface Prop {
|
||||
customerId?: number
|
||||
}
|
||||
|
||||
const props = defineProps<Prop>()
|
||||
const emits = defineEmits(['delete', 'edit', 'add', 'info', 'giftcardcodes'])
|
||||
|
||||
const [submit, reload] = useBaseTable()
|
||||
/*const select = (val) => {
|
||||
val.sts === 1 ? return ['1'] : ['2']
|
||||
}*/
|
||||
//emits
|
||||
interface Prop {
|
||||
customerId: number
|
||||
}
|
||||
const emits = defineEmits(['delete', 'edit', 'add', 'info', 'giftcardcodes'])
|
||||
const props = defineProps<Prop>()
|
||||
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||
|
||||
const { prefixCls } = usePrefix('pophoverstatus')
|
||||
|
||||
//permission
|
||||
function checkPermission(temp: string) {
|
||||
const store = useUserStore()
|
||||
return store.canAccess(temp)
|
||||
}
|
||||
|
||||
//define table
|
||||
const table = ref(null)
|
||||
|
||||
const tblProps = ref<any>({
|
||||
columns: cols,
|
||||
api: (params: TableInput) => {
|
||||
if (props.customerId) {
|
||||
params.filters = []
|
||||
params.filters?.push({
|
||||
field: "customer_id",
|
||||
type: "text",
|
||||
field: 'customer_id',
|
||||
type: 'text',
|
||||
value: props.customerId,
|
||||
cr: "and",
|
||||
op: "eq",
|
||||
cr: 'and',
|
||||
op: 'eq',
|
||||
hidden: false
|
||||
})
|
||||
}
|
||||
return getGiftCardTable(params)
|
||||
}
|
||||
//bordered: true
|
||||
})
|
||||
const loading = ref<boolean>(false)
|
||||
|
||||
async function changeCustomerActivationFunction(id: number) {
|
||||
try {
|
||||
loading.value = true
|
||||
await changeCustomerActivation(id)
|
||||
reload()
|
||||
message.success('تغییر وضعیت با موفقیت انجام شد')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
async function fetch() {
|
||||
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||
}
|
||||
|
||||
const fetch = () => {
|
||||
// console.log('sss')
|
||||
reload()
|
||||
}
|
||||
// const spinTable = (val: boolean) => {
|
||||
// // console.log('sss')
|
||||
// table?.value?.loading.value = val
|
||||
// }
|
||||
defineExpose({
|
||||
fetch
|
||||
// spinTable
|
||||
})
|
||||
defineExpose({ fetch })
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@@ -122,11 +105,5 @@ defineExpose({
|
||||
.ant-menu-item-only-child {
|
||||
border-radius: @border-radius-base;
|
||||
}
|
||||
|
||||
//bor
|
||||
/* .ant-input-group-addon:first-child {
|
||||
!* border: #d9d9d9 1px solid;
|
||||
border-radius: 0 6px 6px 0;*!
|
||||
}*/
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -17,69 +17,24 @@
|
||||
<!-- <BasicShowContext title="پروژه ها"> <Search /> </BasicShowContext>-->
|
||||
</div>
|
||||
</BasicMainCard>
|
||||
<BasicModal
|
||||
<BasicActionSheet
|
||||
v-model:visible="addGiftCardVisible"
|
||||
width="30rem"
|
||||
:title="`${editId ? 'ویرایش' : 'افزودن'} کارت هدیه`"
|
||||
@ok="ok"
|
||||
:title="giftCardSheetTitle"
|
||||
modal-width="30rem"
|
||||
drawer-height="90vh"
|
||||
:loading="loading"
|
||||
@close="addGiftCardVisible = false"
|
||||
:showButton="true"
|
||||
:showCancel="true"
|
||||
:showOk="true"
|
||||
ok-text="ثبت"
|
||||
cancel-text="انصراف"
|
||||
@ok="ok"
|
||||
@close="closeGiftCardSheet"
|
||||
@cancel="closeGiftCardSheet"
|
||||
>
|
||||
<a-form :model="form" ref="formRef" autocomplete="off">
|
||||
<div class="grid md:grid-cols-1 gap-4">
|
||||
<a-form-item label="شماره موبایل مشتری" name="mobile" :rules="inputLenRules(11)">
|
||||
<BasicInput
|
||||
v-model:value="form.mobile"
|
||||
type="tel"
|
||||
placeholder="09123456789"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="نام مشتری" name="name" :rules="inputRules()">
|
||||
<BasicInput
|
||||
v-model:value="form.name"
|
||||
placeholder="نام و نام خانوادگی مشتری"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="نوع کارت هدیه" name="unit" :rules="inputRules()">
|
||||
<a-radio-group
|
||||
v-model:value="form.unit"
|
||||
button-style="solid"
|
||||
>
|
||||
<a-radio-button value="1">ریالی</a-radio-button>
|
||||
<a-radio-button value="2">طلایی</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="تعداد کدها" name="count" :rules="inputRulesNumber()">
|
||||
<BasicInput
|
||||
v-model:value="form.count"
|
||||
type="tel"
|
||||
placeholder="تعداد کدهای کارت هدیه"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
:label="`مقدار ${form.unit === '2' ? '(گرم)' : '(ریال)'}`"
|
||||
name="amount"
|
||||
:rules="inputRulesNumber()"
|
||||
>
|
||||
<BasicInput
|
||||
v-model:value="form.amount"
|
||||
:addon-before="form.unit === '2' ? 'گرم' : 'ریال'"
|
||||
type="tel"
|
||||
placeholder="مقدار کارت هدیه"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="تاریخ انقضا" name="expiry_date" :rules="inputRules()">
|
||||
<BaseDatePicker v-model:value="form.expiry_date" format="YYYY-MM-DD" />
|
||||
</a-form-item>
|
||||
<a-form-item label="توضیحات" name="description">
|
||||
<BasicTextArea
|
||||
v-model:value="form.description"
|
||||
place-holder="توضیحات اختیاری"
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-form>
|
||||
</BasicModal>
|
||||
<div v-if="isMobile" class="mb-3 text-base font-700">{{ giftCardSheetTitle }}</div>
|
||||
<GiftCardEditorFormBody ref="giftCardEditorRef" :form="form" />
|
||||
</BasicActionSheet>
|
||||
<BasicInfoModal
|
||||
v-model:visible="userInfoVisible"
|
||||
@close="userInfoVisible = false"
|
||||
@@ -90,94 +45,20 @@
|
||||
:name="infoParams.name"
|
||||
:items="infoParams.items"
|
||||
/>
|
||||
<BasicModal
|
||||
v-model:visible="giftCardCodesVisible"
|
||||
width="80%"
|
||||
title="لیست کدهای کارت هدیه"
|
||||
custom-footer
|
||||
@close="giftCardCodesVisible = false"
|
||||
>
|
||||
<template #footer>
|
||||
<a-button @click="giftCardCodesVisible = false">بستن</a-button>
|
||||
</template>
|
||||
<a-table
|
||||
:columns="giftCardCodesColumns"
|
||||
:data-source="selectedGiftCardCodes"
|
||||
:pagination="{ pageSize: 10 }"
|
||||
row-key="id"
|
||||
>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.key === 'index'">
|
||||
{{ index + 1 }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'sts'">
|
||||
<CustomTag
|
||||
v-if="record.sts === 1"
|
||||
textColor="#ca8a04"
|
||||
bgColor="#fef3c7"
|
||||
>
|
||||
فعال
|
||||
</CustomTag>
|
||||
<CustomTag
|
||||
v-else-if="record.sts === 2"
|
||||
textColor="#25855A"
|
||||
bgColor="#E3F4E9"
|
||||
>
|
||||
استفاده شده
|
||||
</CustomTag>
|
||||
<CustomTag
|
||||
v-else
|
||||
textColor="#C53030"
|
||||
bgColor="#FECACA"
|
||||
>
|
||||
غیرفعال
|
||||
</CustomTag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'unit'">
|
||||
<CustomTag
|
||||
v-if="record.unit === 2"
|
||||
textColor="#C53030"
|
||||
bgColor="#FECACA"
|
||||
>
|
||||
طلایی
|
||||
</CustomTag>
|
||||
<CustomTag
|
||||
v-else
|
||||
textColor="#25855A"
|
||||
bgColor="#E3F4E9"
|
||||
>
|
||||
ریالی
|
||||
</CustomTag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'amount'">
|
||||
{{ record.amount }} {{ record.unit === 2 ? 'گرم' : 'ریال' }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'expiry_date'">
|
||||
{{ record.expiry_date ? toJalali(record.expiry_date) : '-' }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'used_at'">
|
||||
{{ record.used_at ? toJalali(record.used_at) : '-' }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'assigned_to_name'">
|
||||
{{ record.assigned_to?.name ?? '-' }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'assigned_to_mobile'">
|
||||
{{ record.assigned_to?.mobile ?? '-' }}
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</BasicModal>
|
||||
<GiftCardCodesSheet v-model:visible="giftCardCodesVisible" :codes="selectedGiftCardCodes" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// import BasicInfoModal from '@/components/BasicInfoModal'
|
||||
import { inputLenRules, inputRules, inputRulesNumber } from '@/utils/inputRules'
|
||||
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
||||
import { ref } from 'vue'
|
||||
import tbl from './components/table.vue'
|
||||
import type { FormInstance } from 'ant-design-vue'
|
||||
import BasicInfoModal from '@/components/BasicInfoModal/index.vue'
|
||||
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
||||
import GiftCardEditorFormBody from './components/GiftCardEditorFormBody.vue'
|
||||
import GiftCardCodesSheet from './components/GiftCardCodesSheet.vue'
|
||||
import { message } from 'ant-design-vue/es'
|
||||
import type { Info } from '@/components/BasicInfoModal/model/Info'
|
||||
import { toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
||||
@@ -188,10 +69,9 @@ import type { Customer } from '@/model/customer'
|
||||
import { createCustomer, deleteCustomer, getCustomersList, updateCustomer } from '@/api/customer'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
import type { Giftcard, Giftcardcode, GiftcardInput } from '@/model/giftCard'
|
||||
import CustomTag from '@/components/CustomTag.vue'
|
||||
import { toJalali } from '@core/utils/useDataTime'
|
||||
import { createGiftCard } from '@/api/giftCard'
|
||||
import dayjs from 'dayjs'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
//loading
|
||||
const loading = ref<boolean>(false)
|
||||
@@ -203,6 +83,17 @@ const store = useStaticApiListsStore()
|
||||
const table = ref(null)
|
||||
const refreshTable = () => table?.value?.fetch()
|
||||
|
||||
const giftCardEditorRef = ref<{
|
||||
validateFields?: () => ReturnType<NonNullable<FormInstance['validateFields']>>
|
||||
resetFields?: () => void
|
||||
}>()
|
||||
|
||||
const giftCardSheetTitle = computed(() => `${editId ? 'ویرایش' : 'افزودن'} کارت هدیه`)
|
||||
|
||||
function closeGiftCardSheet() {
|
||||
addGiftCardVisible.value = false
|
||||
}
|
||||
|
||||
//form
|
||||
const form = ref<GiftcardInput>({
|
||||
mobile: '',
|
||||
@@ -213,61 +104,12 @@ const form = ref<GiftcardInput>({
|
||||
expiry_date: '',
|
||||
description: ''
|
||||
})
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
//info
|
||||
let infoParams = reactive<Info>({})
|
||||
const giftCardCodesVisible = ref<boolean>(false)
|
||||
const selectedGiftCardCodes = ref<Giftcardcode[]>([])
|
||||
|
||||
const giftCardCodesColumns = [
|
||||
{
|
||||
title: 'ردیف',
|
||||
key: 'index',
|
||||
dataIndex: 'index'
|
||||
},
|
||||
{
|
||||
title: 'کد',
|
||||
dataIndex: 'code',
|
||||
key: 'code'
|
||||
},
|
||||
{
|
||||
title: 'نوع',
|
||||
dataIndex: 'unit',
|
||||
key: 'unit'
|
||||
},
|
||||
{
|
||||
title: 'مقدار',
|
||||
dataIndex: 'amount',
|
||||
key: 'amount'
|
||||
},
|
||||
{
|
||||
title: 'وضعیت',
|
||||
dataIndex: 'sts',
|
||||
key: 'sts'
|
||||
},
|
||||
{
|
||||
title: 'تاریخ انقضا',
|
||||
dataIndex: 'expiry_date',
|
||||
key: 'expiry_date'
|
||||
},
|
||||
{
|
||||
title: 'تاریخ استفاده',
|
||||
dataIndex: 'used_at',
|
||||
key: 'used_at'
|
||||
},
|
||||
{
|
||||
title: 'نام کاربر اختصاص داده شده',
|
||||
dataIndex: 'assigned_to.name',
|
||||
key: 'assigned_to_name'
|
||||
},
|
||||
{
|
||||
title: 'شماره موبایل کاربر اختصاص داده شده',
|
||||
dataIndex: 'assigned_to.mobile',
|
||||
key: 'assigned_to_mobile'
|
||||
}
|
||||
]
|
||||
|
||||
function info(val: Giftcard) {
|
||||
// نمایش اطلاعات اصلی در BasicInfoModal
|
||||
infoParams.title = 'اطلاعات کارت هدیه'
|
||||
@@ -361,7 +203,7 @@ async function add() {
|
||||
|
||||
//reset data
|
||||
const resetData = () => {
|
||||
formRef.value?.resetFields()
|
||||
giftCardEditorRef.value?.resetFields?.()
|
||||
form.value = {
|
||||
mobile: '',
|
||||
name: '',
|
||||
@@ -382,7 +224,7 @@ const userInfoVisible = ref<boolean>(false)
|
||||
|
||||
//submit gift card
|
||||
async function ok() {
|
||||
formRef.value?.validateFields().then(async () => {
|
||||
await giftCardEditorRef.value?.validateFields?.().then(async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
// تنظیم تاریخ انقضا به آخر همان روز (23:59:59) و تبدیل به میلادی
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<a-form :model="form" ref="formRef" autocomplete="off">
|
||||
<div class="space-y-3">
|
||||
<div class="flex flex-col items-center gap-1 rounded-xl bg-gray-50 px-4 py-3 sm:flex-row sm:justify-center sm:gap-4">
|
||||
<span class="text-sm text-gray-600">قیمت {{ form.direction === 1 ? 'خرید' : 'فروش' }} طلا:</span>
|
||||
<span class="text-sm font-700 text-gray-900">{{ marketPriceLabel }}</span>
|
||||
</div>
|
||||
|
||||
<a-form-item label="نوع تراکنش" name="direction" :rules="inputRules()">
|
||||
<a-radio-group v-model:value="form.direction" button-style="solid" @change="$emit('changePrice')">
|
||||
<a-radio-button :value="1">خرید</a-radio-button>
|
||||
<a-radio-button :value="2">فروش</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
:label="`قیمت ${form.direction === 1 ? 'خرید' : 'فروش'} هر گرم`"
|
||||
name="price"
|
||||
:rules="inputRules()"
|
||||
>
|
||||
<BasicInput
|
||||
v-model:value="form.price"
|
||||
addon-before="ریال"
|
||||
type="price"
|
||||
@change="$emit('changePriceValue')"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="تاریخ سفارش" name="pay_at" :rules="inputRules()">
|
||||
<BaseDatePicker v-model:value="form.pay_at" format="YYYY-MM-DD HH:mm:ss" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="سود" name="difference" :rules="inputRules()">
|
||||
<BasicInput v-model:value="form.difference" addon-before="ریال" type="price" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="کل قیمت لیمان" name="total_price_liman">
|
||||
<BasicInput v-model:value="form.total_price_liman" addon-before="ریال" type="price" />
|
||||
</a-form-item>
|
||||
|
||||
<div class="space-y-2 rounded-xl bg-orange-50 p-3">
|
||||
<a-form-item label="انتخاب کاربر" name="customer_id" :rules="inputRules()" class="mb-0">
|
||||
<BasicSelect
|
||||
v-model:value="form.customer_id"
|
||||
:items="userItems"
|
||||
valueName="name"
|
||||
keyName="id"
|
||||
@change="$emit('changeUser', $event)"
|
||||
/>
|
||||
</a-form-item>
|
||||
<BasicInfoItem is-line title="کیف پول طلا" :value="selectedCustomer?.wallet?.[0]?.gold ?? '_'" />
|
||||
<BasicInfoItem is-line title="کیف پول ریالی" :value="selectedCustomer?.wallet?.[0]?.cash ?? '_'" />
|
||||
</div>
|
||||
|
||||
<div class="space-y-2 rounded-xl bg-blue-50 p-3">
|
||||
<div class="text-sm font-600 text-gray-700">مقدار {{ form.direction === 1 ? 'خرید' : 'فروش' }}:</div>
|
||||
<a-form-item name="goldCash" :rules="inputRulesNumber()" class="mb-0">
|
||||
<BasicInput
|
||||
v-model:value="form.goldCash"
|
||||
addon-before="ریال"
|
||||
addon-bg-color="#0A1B37"
|
||||
addon-text-color="white"
|
||||
place-holder="ارزش کل"
|
||||
type="price"
|
||||
@onchange="$emit('calculateGold', $event)"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item name="goldValue" :rules="inputRulesNumber()" class="mb-0">
|
||||
<BasicInput
|
||||
v-model:value="form.goldValue"
|
||||
addon-before="گرم"
|
||||
addon-bg-color="#0A1B37"
|
||||
addon-text-color="white"
|
||||
place-holder="مقدار طلا"
|
||||
type="tel"
|
||||
@onchange="$emit('calculateCash', $event)"
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormInstance } from 'ant-design-vue'
|
||||
import type { Customer } from '@/model/customer'
|
||||
import type { Order } from '@/model/order'
|
||||
import type { last } from '@/model/price'
|
||||
import { inputRules, inputRulesNumber } from '@/utils/inputRules'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
|
||||
const props = defineProps<{
|
||||
form: Order
|
||||
dataLast?: last
|
||||
userItems?: Customer[]
|
||||
selectedCustomer?: Customer
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'changePrice'): void
|
||||
(e: 'changePriceValue'): void
|
||||
(e: 'changeUser', value: Customer): void
|
||||
(e: 'calculateGold', value: number): void
|
||||
(e: 'calculateCash', value: number): void
|
||||
}>()
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
const marketPriceLabel = computed(() => {
|
||||
const price = props.form.direction === 1 ? props.dataLast?.buy ?? 0 : props.dataLast?.sell ?? 0
|
||||
return usePrice(price)
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
validateFields: () => formRef.value?.validateFields(),
|
||||
resetFields: () => formRef.value?.resetFields()
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<MobileCardList
|
||||
v-model:search="search"
|
||||
:loading="loading"
|
||||
:has-more="hasMore"
|
||||
:show-empty="!loading && items.length === 0"
|
||||
empty-text="سفارشی یافت نشد"
|
||||
search-placeholder="جستجو سفارش"
|
||||
show-refresh
|
||||
@search="reload"
|
||||
@refresh="reload"
|
||||
@load-more="loadMore"
|
||||
>
|
||||
<OrderMobileListItem
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
@open="openSheet"
|
||||
/>
|
||||
</MobileCardList>
|
||||
|
||||
<OrderMobileDetailSheet
|
||||
v-model:visible="sheetVisible"
|
||||
:item="selectedItem"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getOrderTable } from '@/api/order'
|
||||
import type { Order } from '@/model/order'
|
||||
import MobileCardList from '@/components/mobile/MobileCardList.vue'
|
||||
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
||||
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
||||
import type { TableInput } from '@core/type/baseTable'
|
||||
import OrderMobileDetailSheet from './OrderMobileDetailSheet.vue'
|
||||
import OrderMobileListItem from './OrderMobileListItem.vue'
|
||||
|
||||
type OrderRow = Order & { sts_str?: string; service_charge?: number }
|
||||
|
||||
const props = defineProps<{
|
||||
customerId?: number
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'info', item: OrderRow): void
|
||||
}>()
|
||||
|
||||
const { sheetVisible, selectedItem, openSheet, syncSelectedItem } = useMobileDetailSheet<OrderRow>()
|
||||
|
||||
function fetchOrders(params: TableInput) {
|
||||
if (props.customerId) {
|
||||
params.filters = params.filters ?? []
|
||||
params.filters.push({
|
||||
field: 'customer_id',
|
||||
type: 'text',
|
||||
value: props.customerId,
|
||||
cr: 'and',
|
||||
op: 'eq',
|
||||
hidden: false
|
||||
})
|
||||
}
|
||||
return getOrderTable(params)
|
||||
}
|
||||
|
||||
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<OrderRow>({
|
||||
fetchApi: fetchOrders,
|
||||
onFetched: (rows) => syncSelectedItem(rows)
|
||||
})
|
||||
|
||||
onMounted(() => reload())
|
||||
|
||||
defineExpose({ fetch, openSheet })
|
||||
</script>
|
||||
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<MobileDetailSheet v-model:visible="visibleProxy" :item="item" :title="sheetTitle">
|
||||
<template #default="{ item: row }">
|
||||
<MobileSheetHeader :subtitle="createdAtLabel(row)" :show-title="isMobile">
|
||||
<template #title>
|
||||
<span class="min-w-0 truncate">{{ sheetTitle }}</span>
|
||||
</template>
|
||||
</MobileSheetHeader>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="rounded-lg border px-2 py-0.5 text-xs font-600" :class="directionClass(row)">
|
||||
{{ directionLabel(row) }}
|
||||
</span>
|
||||
<span
|
||||
v-if="row.sts_str"
|
||||
class="rounded-lg border px-2 py-0.5 text-xs font-600"
|
||||
:class="statusClass(row)"
|
||||
>
|
||||
{{ row.sts_str }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<MobileStatCard bordered variant="success" label="قیمت کل برای مشتری (ریال)" :value="usePrice(row.total_price ?? 0, true, '')">
|
||||
<template #prefix>
|
||||
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" color="#15803d" />
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
<MobileStatCard bordered variant="warning" label="مقدار (گرم)" :value="`${row.amount ?? 0} گرم`">
|
||||
<template #prefix>
|
||||
<BaseIcon icon="vuesax-linear:coin" size="18" color="#b45309" />
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<MobileStatCard bordered label="قیمت هر گرم طلا برای مشتری (ریال)" :value="usePrice(row.price ?? 0, true, '')" />
|
||||
<MobileStatCard bordered label="قیمت هر گرم طلا بدون سود (ریال)" :value="usePrice(row.price_original ?? 0, true, '')" />
|
||||
<MobileStatCard bordered label="قیمت کل لیمان (ریال)" :value="usePrice(row.total_price_liman ?? 0, true, '')" />
|
||||
<MobileStatCard bordered label="قیمت کل بدون سود (ریال)" :value="usePrice(row.total_price_original ?? 0, true, '')" />
|
||||
<MobileStatCard bordered label="سود هر گرم (ریال)" :value="usePrice(row.fee ?? 0, true, '')" />
|
||||
<MobileStatCard bordered label="کارمزد طلایی" :value="serviceChargeLabel(row)" />
|
||||
</div>
|
||||
|
||||
<MobileInfoGrid>
|
||||
<MobileDetailRow label="کد سفارش" :value="row.id ?? '-'" />
|
||||
<MobileDetailRow label="شماره موبایل" :value="row.customer?.mobile || '-'" />
|
||||
<MobileDetailRow label="سود کل (ریال)" :value="usePrice(row.total_fee ?? 0, true, '')" />
|
||||
<MobileDetailRow label="کد کاربری" :value="row.customer?.id ?? '-'" />
|
||||
<MobileDetailRow label="شماره ملی" :value="row.customer?.n_code || '-'" />
|
||||
<MobileDetailRow label="شماره تلفن" :value="row.customer?.phone_number || '-'" />
|
||||
<MobileDetailRow label="نام استان" :value="row.customer?.province || '-'" />
|
||||
<MobileDetailRow label="نام شهر" :value="row.customer?.city || '-'" />
|
||||
</MobileInfoGrid>
|
||||
</template>
|
||||
</MobileDetailSheet>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Order } from '@/model/order'
|
||||
import MobileDetailRow from '@/components/mobile/MobileDetailRow.vue'
|
||||
import MobileDetailSheet from '@/components/mobile/MobileDetailSheet.vue'
|
||||
import MobileInfoGrid from '@/components/mobile/MobileInfoGrid.vue'
|
||||
import MobileSheetHeader from '@/components/mobile/MobileSheetHeader.vue'
|
||||
import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import { toJalali } from '@core/utils/useDataTime'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
|
||||
type OrderRow = Order & { sts_str?: string; service_charge?: number | string }
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
item: OrderRow | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
}>()
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
set: (value: boolean) => emit('update:visible', value)
|
||||
})
|
||||
|
||||
const sheetTitle = computed(() => {
|
||||
if (!props.item) return 'سفارش'
|
||||
return props.item.customer?.name ? `سفارش ${props.item.customer.name}` : `سفارش #${props.item.id ?? '-'}`
|
||||
})
|
||||
|
||||
function directionLabel(row: OrderRow) {
|
||||
return row.direction === 2 ? 'فروش' : 'خرید'
|
||||
}
|
||||
|
||||
function directionClass(row: OrderRow) {
|
||||
return row.direction === 2
|
||||
? 'border-red-300 bg-red-50 text-red-600'
|
||||
: 'border-green-300 bg-green-50 text-green-700'
|
||||
}
|
||||
|
||||
function statusClass(row: OrderRow) {
|
||||
switch (row.sts) {
|
||||
case 1:
|
||||
return 'border-amber-300 bg-amber-50 text-amber-700'
|
||||
case 2:
|
||||
return 'border-green-300 bg-green-50 text-green-700'
|
||||
default:
|
||||
return 'border-red-300 bg-red-50 text-red-600'
|
||||
}
|
||||
}
|
||||
|
||||
function createdAtLabel(row: OrderRow) {
|
||||
return row.created_at ? toJalali(row.created_at) : '-'
|
||||
}
|
||||
|
||||
function serviceChargeLabel(row: OrderRow) {
|
||||
if (row.service_charge == null || row.service_charge === '') return '-'
|
||||
const value = Number(row.service_charge)
|
||||
if (Number.isNaN(value)) return `${row.service_charge} گرم`
|
||||
return `${parseFloat(value.toFixed(5))} گرم`
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<MobileCard clickable @click="$emit('open', item)">
|
||||
<div class="space-y-1">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="flex min-w-0 items-center gap-2">
|
||||
<span class="shrink-0 rounded-md border px-1.5 py-0.5 text-xs font-600" :class="directionClass">
|
||||
{{ directionLabel }}
|
||||
</span>
|
||||
<span class="truncate text-sm font-700 text-gray-900">{{ customerName }}</span>
|
||||
</div>
|
||||
<span class="shrink-0 text-xs font-600" :class="statusValueClass">{{ statusLabel }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between gap-2 text-xs text-gray-600">
|
||||
<span>
|
||||
<span class="text-gray-500">طلا:</span>
|
||||
<span class="font-600 text-gray-900">{{ amountLabel }}</span>
|
||||
</span>
|
||||
<span class="font-700 text-gray-900">{{ totalPriceLabel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</MobileCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Order } from '@/model/order'
|
||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
|
||||
const props = defineProps<{
|
||||
item: Order & { sts_str?: string; service_charge?: number }
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'open', item: Order): void
|
||||
}>()
|
||||
|
||||
const directionLabel = computed(() => (props.item.direction === 2 ? 'فروش' : 'خرید'))
|
||||
const directionClass = computed(() =>
|
||||
props.item.direction === 2
|
||||
? 'border-red-300 bg-red-50 text-red-600'
|
||||
: 'border-green-300 bg-green-50 text-green-700'
|
||||
)
|
||||
|
||||
const customerName = computed(() => props.item.customer?.name || '-')
|
||||
const amountLabel = computed(() => {
|
||||
const amount = props.item.amount
|
||||
if (amount == null) return '-'
|
||||
return `${Number(amount.toFixed(3))} گرم`
|
||||
})
|
||||
const totalPriceLabel = computed(() => usePrice(props.item.total_price ?? 0, true, ''))
|
||||
const statusLabel = computed(() => props.item.sts_str || '-')
|
||||
|
||||
const statusValueClass = computed(() => {
|
||||
switch (props.item.sts) {
|
||||
case 1:
|
||||
return 'text-amber-700'
|
||||
case 2:
|
||||
return 'text-green-700'
|
||||
default:
|
||||
return 'text-red-600'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,24 +1,40 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||
<BasicShowContext title="لیست سفارشات">
|
||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #extraHeader>
|
||||
<BaseButton v-if="!customerId" type="primary" size="small" class="flex" @click="emits('add')">
|
||||
<div>افزودن سفارش</div>
|
||||
<BaseIcon icon="vuesax-linear:receipt-add" Size="16px" />
|
||||
</BaseButton>
|
||||
</template>
|
||||
<template #action="{ column, record }">
|
||||
<div class="gap-2 flex justify-center">
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('info', record)">
|
||||
<template #title>
|
||||
<span>مشخصات</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</BaseTable>
|
||||
<template v-if="!customerId" #extra>
|
||||
<BaseButton type="primary" size="small" class="flex shrink-0 md:hidden" @click="emits('add')">
|
||||
<div>افزودن</div>
|
||||
<BaseIcon icon="vuesax-linear:receipt-add" Size="16px" />
|
||||
</BaseButton>
|
||||
</template>
|
||||
<div class="flex h-full min-h-0 flex-col">
|
||||
<BaseTable v-if="!isMobile" class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #extraHeader>
|
||||
<BaseButton v-if="!customerId" type="primary" size="small" class="flex" @click="emits('add')">
|
||||
<div>افزودن سفارش</div>
|
||||
<BaseIcon icon="vuesax-linear:receipt-add" Size="16px" />
|
||||
</BaseButton>
|
||||
</template>
|
||||
<template #action="{ column, record }">
|
||||
<div class="gap-2 flex justify-center">
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('info', record)">
|
||||
<template #title>
|
||||
<span>مشخصات</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</BaseTable>
|
||||
|
||||
<OrderMobileCardList
|
||||
v-else
|
||||
ref="mobileListRef"
|
||||
class="min-h-0 flex-1"
|
||||
:customer-id="customerId"
|
||||
@info="emits('info', $event)"
|
||||
/>
|
||||
</div>
|
||||
</BasicShowContext>
|
||||
</div>
|
||||
</template>
|
||||
@@ -33,14 +49,17 @@ import { useUserStore } from '@/store/userStore'
|
||||
import { usePrefix } from '@core/composable/usePrefix'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { getOrderTable } from '@/api/order'
|
||||
import OrderMobileCardList from './OrderMobileCardList.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
const [submit, reload] = useBaseTable()
|
||||
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||
/*const select = (val) => {
|
||||
val.sts === 1 ? return ['1'] : ['2']
|
||||
}*/
|
||||
//emits
|
||||
interface Prop {
|
||||
customerId: number
|
||||
customerId?: number
|
||||
}
|
||||
const emits = defineEmits(['delete', 'edit', 'add', 'info'])
|
||||
const props = defineProps<Prop>()
|
||||
@@ -89,9 +108,8 @@ async function changeCustomerActivationFunction(id: number) {
|
||||
}
|
||||
}
|
||||
|
||||
const fetch = () => {
|
||||
// console.log('sss')
|
||||
reload()
|
||||
async function fetch() {
|
||||
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||
}
|
||||
// const spinTable = (val: boolean) => {
|
||||
// // console.log('sss')
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full min-h-0">
|
||||
<BasicMainCard>
|
||||
<div class="flex w-full h-full">
|
||||
<div class="flex h-full min-h-0 w-full min-w-0 flex-col overflow-hidden">
|
||||
<tbl
|
||||
ref="table"
|
||||
class="w-full h-full"
|
||||
class="h-full min-h-0 w-full"
|
||||
@info="info"
|
||||
@edit="edit"
|
||||
@delete="deleteItem"
|
||||
@@ -16,107 +16,35 @@
|
||||
<!-- <BasicShowContext title="پروژه ها"> <Search /> </BasicShowContext>-->
|
||||
</div>
|
||||
</BasicMainCard>
|
||||
<BasicModal
|
||||
<BasicActionSheet
|
||||
v-model:visible="addOrderVisible"
|
||||
width="25rem"
|
||||
:title="`${editId ? 'ویرایش' : 'افزودن'} سفارش`"
|
||||
@ok="ok"
|
||||
:title="orderSheetTitle"
|
||||
modal-width="25rem"
|
||||
drawer-height="90vh"
|
||||
:loading="loading"
|
||||
@close="stopGetPrice"
|
||||
:showButton="true"
|
||||
:showCancel="true"
|
||||
:showOk="true"
|
||||
ok-text="ثبت"
|
||||
cancel-text="انصراف"
|
||||
@ok="ok"
|
||||
@close="closeAddOrder"
|
||||
@cancel="closeAddOrder"
|
||||
>
|
||||
<a-form :model="form" ref="formRef" autocomplete="off">
|
||||
<div class="grid md:grid-cols-1 gap-4">
|
||||
<div class="bg-gray-50 px-10 py-3 flex justify-center gap-4">
|
||||
<div>قیمت {{ form.direction === 1 ? 'خرید' : 'فروش' }} طلا:</div>
|
||||
<div class="font-bold">
|
||||
{{ usePrice(form.direction === 1 ? dataLast?.buy ?? 0 : dataLast?.sell ?? 0) }}
|
||||
</div>
|
||||
</div>
|
||||
<a-form-item label="نوع تراکنش" name="direction" :rules="inputRules()">
|
||||
<a-radio-group
|
||||
v-model:value="form.direction"
|
||||
button-style="solid"
|
||||
@change="changePrice"
|
||||
>
|
||||
<a-radio-button :value="1">خرید</a-radio-button>
|
||||
<a-radio-button :value="2">فروش</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
:label="`قیمت ${form.direction === 1 ? 'خرید' : 'فروش'} هر گرم`"
|
||||
name="price"
|
||||
:rules="inputRules()"
|
||||
>
|
||||
<BasicInput
|
||||
v-model:value="form.price"
|
||||
addon-before="ریال"
|
||||
type="price"
|
||||
@change="changePriceValue"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="تاریخ سفارش" name="pay_at" :rules="inputRules()">
|
||||
<BaseDatePicker v-model:value="form.pay_at" format="YYYY-MM-DD HH:mm:ss" />
|
||||
</a-form-item>
|
||||
<a-form-item label="سود" name="difference" :rules="inputRules()">
|
||||
<BasicInput v-model:value="form.difference" addon-before="ریال" type="price" />
|
||||
</a-form-item>
|
||||
<a-form-item label="کل قیمت لیمان" name="total_price_liman">
|
||||
<BasicInput v-model:value="form.total_price_liman" addon-before="ریال" type="price" />
|
||||
</a-form-item>
|
||||
<div class="p-2 bg-orange-50 rounded-lg">
|
||||
<a-form-item label="انتخاب کاربر" name="customer_id" :rules="inputRules()">
|
||||
<BasicSelect
|
||||
v-model:value="form.customer_id"
|
||||
:items="userItems"
|
||||
valueName="name"
|
||||
keyName="id"
|
||||
@change="changeUser"
|
||||
/>
|
||||
</a-form-item>
|
||||
<BasicInfoItem
|
||||
is-line
|
||||
title="کیف پول طلا"
|
||||
class="mt-2"
|
||||
:value="selectedCustomer?.wallet[0]?.gold ?? '_'"
|
||||
/>
|
||||
<BasicInfoItem
|
||||
is-line
|
||||
title="کیف پول ریالی"
|
||||
class="mt-1"
|
||||
:value="selectedCustomer?.wallet[0]?.cash ?? '_'"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 bg-blue-50 p-2 rounded-lg">
|
||||
<div>مقدار {{ form.direction === 1 ? 'خرید' : 'فروش' }}:</div>
|
||||
<a-form-item name="goldCash" :rules="inputRulesNumber()">
|
||||
<BasicInput
|
||||
v-model:value="form.goldCash"
|
||||
addon-before="ریال"
|
||||
addon-bg-color="#0A1B37"
|
||||
addon-text-color="white"
|
||||
place-holder="ارزش کل"
|
||||
type="price"
|
||||
@onchange="calculateGold"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item name="goldValue" :rules="inputRulesNumber()">
|
||||
<BasicInput
|
||||
v-model:value="form.goldValue"
|
||||
addon-before="گرم"
|
||||
addon-bg-color="#0A1B37"
|
||||
addon-text-color="white"
|
||||
place-holder="مقدار طلا"
|
||||
type="tel"
|
||||
@onchange="calculateCash"
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<!-- <a-form-item label="نام چاپی" name="print_name" :rules="inputRules()">
|
||||
<BasicInput v-model:value="form.print_name" />
|
||||
</a-form-item>-->
|
||||
</div>
|
||||
</a-form>
|
||||
</BasicModal>
|
||||
<div v-if="isMobile" class="mb-3 text-base font-700">{{ orderSheetTitle }}</div>
|
||||
<OrderEditorFormBody
|
||||
ref="orderEditorRef"
|
||||
:form="form"
|
||||
:data-last="dataLast"
|
||||
:user-items="userItems"
|
||||
:selected-customer="selectedCustomer"
|
||||
@change-price="changePrice"
|
||||
@change-price-value="changePriceValue"
|
||||
@change-user="changeUser"
|
||||
@calculate-gold="calculateGold"
|
||||
@calculate-cash="calculateCash"
|
||||
/>
|
||||
</BasicActionSheet>
|
||||
<BasicInfoModal
|
||||
v-model:visible="userInfoVisible"
|
||||
@close="userInfoVisible = false"
|
||||
@@ -132,25 +60,26 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
// import BasicInfoModal from '@/components/BasicInfoModal'
|
||||
import { inputLenRules, inputRules, inputRulesNumber } from '@/utils/inputRules'
|
||||
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
||||
import { ref } from 'vue'
|
||||
import tbl from './components/table.vue'
|
||||
import type { FormInstance } from 'ant-design-vue'
|
||||
import BasicInfoModal from '@/components/BasicInfoModal/index.vue'
|
||||
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
||||
import OrderEditorFormBody from './components/OrderEditorFormBody.vue'
|
||||
import { message } from 'ant-design-vue/es'
|
||||
import type { Info } from '@/components/BasicInfoModal/model/Info'
|
||||
import { toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
||||
import { confirmDeleteItem } from '@/utils/confirmModal'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import type { Item } from '@/model/Item'
|
||||
import type { Customer } from '@/model/customer'
|
||||
import { createCustomer, deleteCustomer, getCustomersList, updateCustomer } from '@/api/customer'
|
||||
import { deleteCustomer, getCustomersList } from '@/api/customer'
|
||||
import type { Order } from '@/model/order'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
import { getLastPrice } from '@/api/price'
|
||||
import type { last } from '@/model/price'
|
||||
import { createOrder } from '@/api/order'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
//loading
|
||||
const loading = ref<boolean>(false)
|
||||
@@ -191,9 +120,18 @@ const form = ref<Order>({
|
||||
goldCash: undefined,
|
||||
goldValue: undefined
|
||||
})
|
||||
const formRef = ref<FormInstance>()
|
||||
const orderEditorRef = ref<{
|
||||
validateFields?: () => ReturnType<NonNullable<FormInstance['validateFields']>>
|
||||
resetFields?: () => void
|
||||
}>()
|
||||
const dataLast = ref<last>()
|
||||
let interval: any
|
||||
let interval: ReturnType<typeof setInterval> | undefined
|
||||
|
||||
const orderSheetTitle = computed(() => `${editId ? 'ویرایش' : 'افزودن'} سفارش`)
|
||||
|
||||
function closeAddOrder() {
|
||||
stopGetPrice()
|
||||
}
|
||||
|
||||
async function setIntervalGetPrice() {
|
||||
try {
|
||||
@@ -226,12 +164,11 @@ async function getPrices() {
|
||||
|
||||
async function changeUser(val: Customer) {
|
||||
selectedCustomer.value = cloneDeep(val)
|
||||
console.log(selectedCustomer.value.wallet[0])
|
||||
}
|
||||
|
||||
function stopGetPrice() {
|
||||
clearInterval(interval)
|
||||
console.log('stop')
|
||||
if (interval) clearInterval(interval)
|
||||
interval = undefined
|
||||
}
|
||||
|
||||
//info
|
||||
@@ -384,7 +321,7 @@ async function add() {
|
||||
|
||||
//reset data
|
||||
const resetData = () => {
|
||||
formRef.value?.resetFields()
|
||||
orderEditorRef.value?.resetFields?.()
|
||||
form.value = {
|
||||
direction: 1,
|
||||
price: 0,
|
||||
@@ -415,9 +352,7 @@ const userInfoVisible = ref<boolean>(false)
|
||||
|
||||
//submit user
|
||||
async function ok() {
|
||||
formRef.value?.validateFields().then(async () => {
|
||||
// console.log('salam', form.value)
|
||||
// return
|
||||
await orderEditorRef.value?.validateFields?.().then(async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
form.value.amount = form.value.goldValue
|
||||
@@ -427,14 +362,11 @@ async function ok() {
|
||||
await createOrder(form.value)
|
||||
message.success('سفارش با موفقیت ثبت شد')
|
||||
addOrderVisible.value = false
|
||||
closeAddOrder()
|
||||
resetData()
|
||||
refreshTable()
|
||||
// await getResponsible()
|
||||
// await getQC()
|
||||
// await getManager()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
// message.error('خطا در بارگذاری اطلاعات')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<MobileCardList
|
||||
v-model:search="search"
|
||||
:loading="loading"
|
||||
:has-more="hasMore"
|
||||
:show-empty="!loading && items.length === 0"
|
||||
empty-text="قیمتی یافت نشد"
|
||||
search-placeholder="جستجو تاریخچه قیمت"
|
||||
show-refresh
|
||||
@search="reload"
|
||||
@refresh="reload"
|
||||
@load-more="loadMore"
|
||||
>
|
||||
<PriceMobileListItem v-for="item in items" :key="item.id" :item="item" @open="openSheet" />
|
||||
</MobileCardList>
|
||||
|
||||
<PriceMobileDetailSheet v-model:visible="sheetVisible" :item="selectedItem" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getPriceTable } from '@/api/price'
|
||||
import type { Price } from '@/model/price'
|
||||
import MobileCardList from '@/components/mobile/MobileCardList.vue'
|
||||
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
||||
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
||||
import PriceMobileDetailSheet from './PriceMobileDetailSheet.vue'
|
||||
import PriceMobileListItem from './PriceMobileListItem.vue'
|
||||
|
||||
const { sheetVisible, selectedItem, openSheet, syncSelectedItem } = useMobileDetailSheet<Price>()
|
||||
|
||||
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<Price>({
|
||||
fetchApi: getPriceTable,
|
||||
onFetched: (rows) => syncSelectedItem(rows)
|
||||
})
|
||||
|
||||
onMounted(() => reload())
|
||||
|
||||
defineExpose({ fetch, openSheet })
|
||||
</script>
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<MobileDetailSheet v-model:visible="visibleProxy" :item="item" :title="sheetTitle">
|
||||
<template #default="{ item: row }">
|
||||
<MobileSheetHeader :title="sheetTitle" :subtitle="dateTimeLabel(row)" :show-title="isMobile" />
|
||||
|
||||
<div class="space-y-2">
|
||||
<MobileStatCard bordered variant="success" label="قیمت هر گرم طلا برای مشتری (ریال)" :value="usePrice(row.price ?? 0, true, '')">
|
||||
<template #prefix>
|
||||
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" color="#15803d" />
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
</div>
|
||||
|
||||
<MobileInfoGrid>
|
||||
<MobileDetailRow label="کد قیمت" :value="row.id ?? '-'" />
|
||||
<MobileDetailRow label="کد کاربری" :value="row.user_id ?? '-'" />
|
||||
<MobileDetailRow label="نام و نام خانوادگی" full-width :value="row.user?.name || '-'" />
|
||||
<MobileDetailRow label="تاریخ ثبت" :value="dateLabel(row)" />
|
||||
<MobileDetailRow label="زمان ثبت" :value="row.time_at || '-'" />
|
||||
</MobileInfoGrid>
|
||||
</template>
|
||||
</MobileDetailSheet>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Price } from '@/model/price'
|
||||
import MobileDetailRow from '@/components/mobile/MobileDetailRow.vue'
|
||||
import MobileDetailSheet from '@/components/mobile/MobileDetailSheet.vue'
|
||||
import MobileInfoGrid from '@/components/mobile/MobileInfoGrid.vue'
|
||||
import MobileSheetHeader from '@/components/mobile/MobileSheetHeader.vue'
|
||||
import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import { toJalaliNoTime } from '@core/utils/useDataTime'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
item: Price | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
}>()
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
set: (value: boolean) => emit('update:visible', value)
|
||||
})
|
||||
|
||||
const sheetTitle = computed(() => props.item?.user?.name || `قیمت #${props.item?.id ?? '-'}`)
|
||||
|
||||
function dateLabel(row: Price) {
|
||||
return row.created_at ? toJalaliNoTime(row.created_at) : '-'
|
||||
}
|
||||
|
||||
function dateTimeLabel(row: Price) {
|
||||
const date = dateLabel(row)
|
||||
const time = row.time_at
|
||||
if (date === '-' && !time) return '-'
|
||||
if (!time) return date
|
||||
return `${date} · ${time}`
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<MobileCard clickable @click="$emit('open', item)">
|
||||
<div class="space-y-1">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="truncate text-sm font-700 text-gray-900">{{ userName }}</span>
|
||||
<span class="shrink-0 text-xs text-gray-400">{{ dateLabel }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between gap-2 text-xs">
|
||||
<span class="truncate text-gray-600">#{{ item.id ?? '-' }}</span>
|
||||
<span class="shrink-0 font-700 text-green-700">{{ priceLabel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</MobileCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Price } from '@/model/price'
|
||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||
import { toJalaliNoTime } from '@core/utils/useDataTime'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
|
||||
const props = defineProps<{
|
||||
item: Price
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'open', item: Price): void
|
||||
}>()
|
||||
|
||||
const userName = computed(() => props.item.user?.name || '-')
|
||||
const dateLabel = computed(() => (props.item.created_at ? toJalaliNoTime(props.item.created_at) : '-'))
|
||||
const priceLabel = computed(() => usePrice(props.item.price ?? 0, true, ''))
|
||||
</script>
|
||||
@@ -1,7 +1,15 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||
<BasicShowContext title="تاریخچه قیمت">
|
||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #extra>
|
||||
<BaseButton type="primary" size="small" class="flex shrink-0 md:hidden" @click="emits('add')">
|
||||
<div>قیمت گذاری</div>
|
||||
<BaseIcon icon="vuesax-linear:dollar-circle" Size="16px" />
|
||||
</BaseButton>
|
||||
</template>
|
||||
|
||||
<div class="flex h-full min-h-0 flex-col">
|
||||
<BaseTable v-if="!isMobile" class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #extraHeader>
|
||||
<BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
||||
<div>قیمت گذاری</div>
|
||||
@@ -19,6 +27,9 @@
|
||||
</div>
|
||||
</template>
|
||||
</BaseTable>
|
||||
|
||||
<PriceMobileCardList v-else ref="mobileListRef" class="min-h-0 flex-1" />
|
||||
</div>
|
||||
</BasicShowContext>
|
||||
</div>
|
||||
</template>
|
||||
@@ -27,63 +38,32 @@
|
||||
import type { TableInput } from '@core/type/baseTable'
|
||||
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||
import { changeCustomerActivation, getCustomerTable } from '@/api/customer'
|
||||
import { cols } from '../hooks/priceColumns'
|
||||
import { useUserStore } from '@/store/userStore'
|
||||
import { usePrefix } from '@core/composable/usePrefix'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { getOrderTable } from '@/api/order'
|
||||
import { getPriceTable } from '@/api/price'
|
||||
import PriceMobileCardList from './PriceMobileCardList.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
const [submit, reload] = useBaseTable()
|
||||
/*const select = (val) => {
|
||||
val.sts === 1 ? return ['1'] : ['2']
|
||||
}*/
|
||||
//emits
|
||||
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||
|
||||
const emits = defineEmits(['delete', 'edit', 'add', 'info'])
|
||||
|
||||
const { prefixCls } = usePrefix('pophoverstatus')
|
||||
|
||||
//permission
|
||||
function checkPermission(temp: string) {
|
||||
const store = useUserStore()
|
||||
return store.canAccess(temp)
|
||||
}
|
||||
|
||||
//define table
|
||||
const table = ref(null)
|
||||
|
||||
const tblProps = ref<any>({
|
||||
columns: cols,
|
||||
api: (params: TableInput) => {
|
||||
return getPriceTable(params)
|
||||
}
|
||||
//bordered: true
|
||||
})
|
||||
const loading = ref<boolean>(false)
|
||||
async function changeCustomerActivationFunction(id: number) {
|
||||
try {
|
||||
loading.value = true
|
||||
await changeCustomerActivation(id)
|
||||
reload()
|
||||
message.success('تغییر وضعیت با موفقیت انجام شد')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
async function fetch() {
|
||||
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||
}
|
||||
|
||||
const fetch = () => {
|
||||
// console.log('sss')
|
||||
reload()
|
||||
}
|
||||
// const spinTable = (val: boolean) => {
|
||||
// // console.log('sss')
|
||||
// table?.value?.loading.value = val
|
||||
// }
|
||||
defineExpose({
|
||||
fetch
|
||||
// spinTable
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -98,10 +78,5 @@ defineExpose({
|
||||
.ant-menu-item-only-child {
|
||||
border-radius: @border-radius-base;
|
||||
}
|
||||
//bor
|
||||
/* .ant-input-group-addon:first-child {
|
||||
!* border: #d9d9d9 1px solid;
|
||||
border-radius: 0 6px 6px 0;*!
|
||||
}*/
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<MobileCardList
|
||||
v-model:search="search"
|
||||
:loading="loading"
|
||||
:has-more="hasMore"
|
||||
:show-empty="!loading && items.length === 0"
|
||||
empty-text="محصولی یافت نشد"
|
||||
search-placeholder="جستجو محصول"
|
||||
show-refresh
|
||||
@search="reload"
|
||||
@refresh="reload"
|
||||
@load-more="loadMore"
|
||||
>
|
||||
<ArchiveMobileListItem
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
@info="$emit('info', $event)"
|
||||
@unarchive="$emit('unarchive', $event)"
|
||||
/>
|
||||
</MobileCardList>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getProductArchiveTable } from '@/api/product'
|
||||
import type { Product } from '@/model/product'
|
||||
import MobileCardList from '@/components/mobile/MobileCardList.vue'
|
||||
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
||||
import ArchiveMobileListItem from './ArchiveMobileListItem.vue'
|
||||
|
||||
defineEmits<{
|
||||
(e: 'info', item: Product): void
|
||||
(e: 'unarchive', item: Product): void
|
||||
}>()
|
||||
|
||||
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<Product>({
|
||||
fetchApi: getProductArchiveTable
|
||||
})
|
||||
|
||||
onMounted(() => reload())
|
||||
|
||||
defineExpose({ fetch })
|
||||
</script>
|
||||
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<MobileCard>
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="flex min-w-0 items-center gap-2">
|
||||
<span class="inline-block h-2.5 w-2.5 rounded-full" :class="item.active ? 'bg-green-500' : 'bg-red-400'" />
|
||||
<span class="truncate text-sm font-700 text-gray-900">{{ item.title || '-' }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<MobileIconButton icon="vuesax-linear:archive-slash" color="#e3bf00" @click="$emit('unarchive', item)" />
|
||||
<MobileIconButton icon="vuesax-linear:stickynote" color="#10913A" @click="$emit('info', item)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 flex items-center justify-between gap-2">
|
||||
<span class="truncate text-xs text-gray-500">قیمت: {{ usePrice(item.price || 0) }}</span>
|
||||
<span class="text-xs font-700" :class="item.open_buy ? 'text-green-700' : 'text-red-600'">
|
||||
{{ item.open_buy ? 'خرید: باز' : 'خرید: بسته' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="mt-2 flex items-center justify-between">
|
||||
<span class="text-xs text-gray-500">کد محصول: {{ item.id ?? '-' }}</span>
|
||||
<span class="text-xs font-700" :class="item.open_sell ? 'text-green-700' : 'text-red-600'">
|
||||
{{ item.open_sell ? 'فروش: باز' : 'فروش: بسته' }}
|
||||
</span>
|
||||
</div>
|
||||
</MobileCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Product } from '@/model/product'
|
||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||
import MobileIconButton from '@/components/mobile/MobileIconButton.vue'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
|
||||
defineProps<{
|
||||
item: Product
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'info', item: Product): void
|
||||
(e: 'unarchive', item: Product): void
|
||||
}>()
|
||||
</script>
|
||||
@@ -1,7 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||
<BasicShowContext title="لیست محصولات بایگانی شده">
|
||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<div class="flex h-full min-h-0 flex-col">
|
||||
<BaseTable v-if="!isMobile" class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<!-- <template #extraHeader>
|
||||
<BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
||||
<div>افزودن مشتری عمده</div>
|
||||
@@ -46,6 +47,14 @@
|
||||
</div>
|
||||
</template>
|
||||
</BaseTable>
|
||||
<ArchiveMobileCardList
|
||||
v-else
|
||||
ref="mobileListRef"
|
||||
class="min-h-0 flex-1"
|
||||
@info="emits('info', $event)"
|
||||
@unarchive="archiveFunction($event.id)"
|
||||
/>
|
||||
</div>
|
||||
</BasicShowContext>
|
||||
</div>
|
||||
</template>
|
||||
@@ -65,6 +74,9 @@ import { cols } from '../hooks/archiveColumns'
|
||||
import { useUserStore } from '@/store/userStore'
|
||||
import { usePrefix } from '@core/composable/usePrefix'
|
||||
import { message } from 'ant-design-vue'
|
||||
import ArchiveMobileCardList from './ArchiveMobileCardList.vue'
|
||||
import { getProductArchiveTable } from '@/api/product'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
const [submit, reload] = useBaseTable()
|
||||
/*const select = (val) => {
|
||||
@@ -87,7 +99,7 @@ const table = ref(null)
|
||||
const tblProps = ref<any>({
|
||||
columns: cols,
|
||||
api: (params: TableInput) => {
|
||||
return getCustomerArchiveTable(params)
|
||||
return getProductArchiveTable(params)
|
||||
}
|
||||
//bordered: true
|
||||
})
|
||||
@@ -122,7 +134,7 @@ async function archiveFunction(id: number) {
|
||||
try {
|
||||
loading.value = true
|
||||
await changeCustomerArchive(id)
|
||||
reload()
|
||||
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||
message.success('مشتری با موفقیت از بایگانی خارج شد.')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
@@ -130,9 +142,10 @@ async function archiveFunction(id: number) {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
const fetch = () => {
|
||||
// console.log('sss')
|
||||
reload()
|
||||
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||
|
||||
const fetch = async () => {
|
||||
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||
}
|
||||
// const spinTable = (val: boolean) => {
|
||||
// // console.log('sss')
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
<template>
|
||||
<CollapsePanel :key="activeKey">
|
||||
<template #header>
|
||||
<div class="w-full">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<span>{{ props.title }}</span>
|
||||
<BaseIcon :class="[{ rotate: props.actives.includes(props.activeKey) }]" icon="vuesax-linear:arrow-left-1" size="17" />
|
||||
</div>
|
||||
<div :class="[{ open: props.actives.includes(props.activeKey) }]"></div>
|
||||
</div>
|
||||
</template>
|
||||
<slot name="content"></slot>
|
||||
</CollapsePanel>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {CollapsePanel} from "ant-design-vue";
|
||||
|
||||
interface Props {
|
||||
title: string,
|
||||
activeKey: string | number,
|
||||
actives: string[] | number[]
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
||||
.open {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: #E5E7EB;
|
||||
|
||||
}
|
||||
|
||||
.rotate {
|
||||
transform: rotate(-90deg);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.ant-collapse > .ant-collapse-item > .ant-collapse-header {
|
||||
direction: rtl !important;
|
||||
justify-content: space-between !important;
|
||||
background-color: #fff !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.ant-collapse{
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.ant-collapse > .ant-collapse-item {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.ant-collapse-content {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.ant-collapse > .ant-collapse-item > .ant-collapse-header .ant-collapse-arrow {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.ant-collapse > .ant-collapse-item > .ant-collapse-header {
|
||||
padding: 12px 16px 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<Collapse class="grid grid-cols-1 gap-2" :activeKey="activeKey" @update:activeKey="$emit('update:activeKey', $event)">
|
||||
<BaseCollapse title="اطلاعات محصول" active-key="0" :actives="activeKey">
|
||||
<template #content>
|
||||
<div
|
||||
v-if="form.group_type === '' || form.group_type !== 'آبشده'"
|
||||
class="md:flex justify-between border-1 border-solid rounded-lg border-gray-200 p-2"
|
||||
>
|
||||
<div class="items-center">
|
||||
<span class="ml-4">دریافت قیمت از :</span>
|
||||
<a-radio-group :value="methodOption" name="radioGroup" @update:value="$emit('update:methodOption', $event)">
|
||||
<a-radio class="items-center" value="1">دستی</a-radio>
|
||||
<a-radio value="2">اتوماتیک</a-radio>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
<div>
|
||||
<div v-if="methodOption === '1'" class="md:flex items-center justify-between md:w-[25rem]">
|
||||
<div>قیمت وسط :</div>
|
||||
<BasicInput class="mt-1 md:mt-unset" addon-before="ریال" type="price" v-model:value="form.price" />
|
||||
<AButton class="border-green text-green mt-1 md:mt-unset rounded-full aspect-square" size="small" shape="circle" @click="$emit('addPrice')">
|
||||
<template #icon><Icon icon="vuesax-linear:add" size="20" /></template>
|
||||
</AButton>
|
||||
<AButton class="border-red text-red mr-1 md:mr-unset rounded-full" size="small" shape="circle" @click="$emit('minusPrice')">
|
||||
<template #icon><Icon icon="vuesax-linear:minus" size="20" /></template>
|
||||
</AButton>
|
||||
</div>
|
||||
<div v-if="methodOption === '2'" class="md:flex justify-between items-center">
|
||||
<div class="ml-4">قیمت :</div>
|
||||
<BasicSelect class="w-11rem method_style" allowClear :items="pricesList" valueName="name" keyName="id" v-model:value="form.reading_method" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="form.group_type === '' || form.group_type !== 'آبشده'"
|
||||
class="md:flex justify-between p-2 border-1 border-solid rounded-lg border-gray-200 mt-2"
|
||||
>
|
||||
<div>
|
||||
<span class="ml-4">وزن :</span>
|
||||
<a-radio-group :value="weightOption" name="radioGroup" @update:value="$emit('update:weightOption', $event)">
|
||||
<a-radio value="1">دارد</a-radio>
|
||||
<a-radio value="2">ندارد</a-radio>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
<div v-if="weightOption === '1'" class="flex items-center">
|
||||
<span class="ml-4 ">وزن به گرم :</span>
|
||||
<BasicInput v-model:value="form.amount" type="number" />
|
||||
<div class="mt-1 mr-4" v-if="form.amount && form.amount < 1 && 0 < form.amount">معادل {{ form.amount * 1000 }} سوت</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-form :model="form" ref="formRef" autocomplete="off">
|
||||
<div class="grid md:grid-cols-2 gap-4">
|
||||
<a-form-item label="عنوان" name="title" :rules="inputRules()"><BasicInput v-model:value="form.title" /></a-form-item>
|
||||
<a-form-item v-if="getConfigApp('name') === 'garoosi'" label="دسته بندی" name="group_type" :rules="inputRules()">
|
||||
<BasicSelect v-model:value="form.group_type" :items="garoosiCategoryItems" valueName="value" keyName="value" />
|
||||
</a-form-item>
|
||||
<a-form-item v-else label="دسته بندی" name="group_type" :rules="inputRules()">
|
||||
<BasicSelect v-model:value="form.group_type" :items="categoryItems" valueName="value" keyName="value" />
|
||||
</a-form-item>
|
||||
<a-form-item label="جنس فلز" name="metal_id" :rules="inputRules()">
|
||||
<BasicSelect v-model:value="form.metal_id" :items="metalItems" valueName="value" keyName="key" />
|
||||
</a-form-item>
|
||||
<a-form-item label="نوع قیمت" name="direct_price" :rules="inputRules()">
|
||||
<BasicSelect v-model:value="form.direct_price" :items="priceItems" valueName="value" keyName="key" />
|
||||
</a-form-item>
|
||||
<a-form-item label="واحد" name="unit" :rules="inputRules()">
|
||||
<BasicSelect v-model:value="form.unit" :items="unitItems" valueName="value" keyName="value" />
|
||||
</a-form-item>
|
||||
<a-form-item v-if="form.group_type === '' || form.group_type !== 'آبشده'" label="هزینه نقدی موقع خرید" name="cash_fee_buy" :rules="inputRules()">
|
||||
<BasicInput v-model:value="form.cash_fee_buy" type="number" />
|
||||
</a-form-item>
|
||||
<a-form-item v-if="form.group_type === '' || form.group_type !== 'آبشده'" label="هزینه نقدی موقع فروش" name="cash_fee_sell" :rules="inputRules()">
|
||||
<BasicInput v-model:value="form.cash_fee_sell" type="number" />
|
||||
</a-form-item>
|
||||
<a-form-item label="زمان شروع فعال سازی" name="auto_open_at">
|
||||
<TimePicker v-model:value="form.auto_open_at" :locale="customLocale" placeholder="انتخاب زمان" :defaultValue="dayjs('00:00:00', 'HH:mm:ss')" />
|
||||
</a-form-item>
|
||||
<a-form-item label="زمان پایان فعال سازی" name="auto_close_at">
|
||||
<TimePicker v-model:value="form.auto_close_at" :locale="customLocale" placeholder="انتخاب زمان" :defaultValue="dayjs('00:00:00', 'HH:mm:ss')" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-form>
|
||||
</template>
|
||||
</BaseCollapse>
|
||||
|
||||
<BaseCollapse title="تصاویر محصول" active-key="1" :actives="activeKey">
|
||||
<template #content>
|
||||
<IndexImageUploader
|
||||
:key="editId ?? 'new-product'"
|
||||
:file-list="productIndexImage"
|
||||
@update:file-list="$emit('update:productIndexImage', $event)"
|
||||
@ok="(id, res) => $emit('uploadedIndexFile', id, res)"
|
||||
@delete="$emit('deleteIndexFile')"
|
||||
/>
|
||||
</template>
|
||||
</BaseCollapse>
|
||||
</Collapse>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Collapse, TimePicker } from 'ant-design-vue'
|
||||
import { inputRules } from '@/utils/inputRules'
|
||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||
import BaseCollapse from '@/components/BaseCollapse.vue'
|
||||
import IndexImageUploader from '@/components/ProductUploader/IndexImageUploader.vue'
|
||||
import { getConfigApp } from '@/utils/config'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
defineProps<{
|
||||
form: any
|
||||
formRef: any
|
||||
methodOption: string
|
||||
weightOption: string
|
||||
activeKey: string[]
|
||||
pricesList: Array<{ id: string; name: string }>
|
||||
garoosiCategoryItems: any[]
|
||||
categoryItems: any[]
|
||||
metalItems: any[]
|
||||
priceItems: any[]
|
||||
unitItems: any[]
|
||||
customLocale: any
|
||||
productIndexImage: any[]
|
||||
editId?: number
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'addPrice'): void
|
||||
(e: 'minusPrice'): void
|
||||
(e: 'update:methodOption', value: string): void
|
||||
(e: 'update:weightOption', value: string): void
|
||||
(e: 'update:activeKey', value: string[]): void
|
||||
(e: 'update:productIndexImage', value: any[]): void
|
||||
(e: 'uploadedIndexFile', id: number, response?: Record<string, any>): void
|
||||
(e: 'deleteIndexFile'): void
|
||||
}>()
|
||||
</script>
|
||||
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<MobileCardList
|
||||
v-model:search="search"
|
||||
:loading="loading"
|
||||
:has-more="hasMore"
|
||||
search-placeholder="جستجو محصول"
|
||||
show-refresh
|
||||
@search="reload"
|
||||
@refresh="reload"
|
||||
@load-more="loadMore"
|
||||
>
|
||||
<ProductMobileListItem
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
@open="openSheet"
|
||||
@edit="$emit('edit', $event)"
|
||||
/>
|
||||
</MobileCardList>
|
||||
|
||||
<ProductMobileDetailSheet
|
||||
v-model:visible="sheetVisible"
|
||||
:item="selectedItem"
|
||||
@toggleBuy="$emit('toggleBuy', $event)"
|
||||
@toggleSell="$emit('toggleSell', $event)"
|
||||
@toggleActive="$emit('toggleActive', $event)"
|
||||
@edit="$emit('edit', $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getCustomerTable } from '@/api/product'
|
||||
import type { Product } from '@/model/product'
|
||||
import MobileCardList from '@/components/mobile/MobileCardList.vue'
|
||||
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
||||
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
||||
import ProductMobileListItem from './ProductMobileListItem.vue'
|
||||
import ProductMobileDetailSheet from './ProductMobileDetailSheet.vue'
|
||||
|
||||
defineEmits<{
|
||||
(e: 'edit', item: Product): void
|
||||
(e: 'toggleBuy', item: Product): void
|
||||
(e: 'toggleSell', item: Product): void
|
||||
(e: 'toggleActive', item: Product): void
|
||||
}>()
|
||||
|
||||
const { sheetVisible, selectedItem, openSheet, syncSelectedItem } = useMobileDetailSheet<Product>()
|
||||
|
||||
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<Product>({
|
||||
fetchApi: getCustomerTable,
|
||||
onFetched: (rows) => syncSelectedItem(rows)
|
||||
})
|
||||
|
||||
onMounted(() => reload())
|
||||
|
||||
defineExpose({ fetch })
|
||||
</script>
|
||||
@@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<MobileDetailSheet v-model:visible="visibleProxy" :item="item" :title="item?.title || ''">
|
||||
<template #default="{ item: row }">
|
||||
<MobileSheetHeader :subtitle="updatedAtLabel(row)" :show-title="isMobile">
|
||||
<template #title>
|
||||
<div class="flex w-full items-center justify-between gap-3">
|
||||
<span class="min-w-0 truncate">{{ row.title || '-' }}</span>
|
||||
<div class="flex shrink-0 items-center gap-2">
|
||||
<MobileIconButton
|
||||
plain
|
||||
:key="`buy-${row.id}-${row.open_buy}`"
|
||||
icon="vuesax-linear:money-receive"
|
||||
:color="row.open_buy ? '#16A34A' : '#DC2626'"
|
||||
:size="22"
|
||||
@click="$emit('toggleBuy', row)"
|
||||
/>
|
||||
<MobileIconButton
|
||||
plain
|
||||
:key="`sell-${row.id}-${row.open_sell}`"
|
||||
icon="vuesax-linear:money-send"
|
||||
:color="row.open_sell ? '#16A34A' : '#DC2626'"
|
||||
:size="22"
|
||||
@click="$emit('toggleSell', row)"
|
||||
/>
|
||||
<MobileIconButton
|
||||
plain
|
||||
:key="`active-${row.id}-${row.active}`"
|
||||
:icon="row.active ? 'vuesax-linear:tick-circle' : 'vuesax-linear:close-circle'"
|
||||
:color="row.active ? '#16A34A' : '#DC2626'"
|
||||
:size="22"
|
||||
@click="$emit('toggleActive', row)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</MobileSheetHeader>
|
||||
|
||||
<MobileStatCard variant="success" bordered label="قیمت محصول" :value="usePrice(row.price || 0)">
|
||||
<template #prefix>
|
||||
<BaseIcon icon="vuesax-linear:money-send" size="18" color="#15803d" />
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
|
||||
<MobileStatCard variant="danger" bordered label="کارمزد نقدی" :value="cashFeeLabel(row)">
|
||||
<template #prefix>
|
||||
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" color="#dc2626" />
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
|
||||
<div class="flex items-center justify-between rounded-xl border border-gray-200 bg-white px-4 py-3">
|
||||
<div class="flex items-center">
|
||||
<BaseIcon icon="vuesax-linear:calendar" size="18" color="#2563eb" />
|
||||
<span class="mr-2 font-700 text-primary">درصد کارمزد</span>
|
||||
</div>
|
||||
<div class="text-2xl font-800">
|
||||
<span class="text-gray-900">{{ row.percent_fee_buy || 0 }}</span>
|
||||
<span class="mx-1 text-green-600">=</span>
|
||||
<span class="text-green-600">{{ row.percent_fee_sell || 0 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<MobileInfoGrid>
|
||||
<MobileDetailRow label="کد محصول" :value="row.id ?? '-'" />
|
||||
<MobileDetailRow label="دسته بندی محصول" :value="row.group_type || '-'" />
|
||||
<MobileDetailRow label="واحد" :value="row.unit || '-'" />
|
||||
<MobileDetailRow label="وزن" :value="row.amount ?? '-'" />
|
||||
</MobileInfoGrid>
|
||||
</template>
|
||||
</MobileDetailSheet>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Product } from '@/model/product'
|
||||
import MobileDetailRow from '@/components/mobile/MobileDetailRow.vue'
|
||||
import MobileDetailSheet from '@/components/mobile/MobileDetailSheet.vue'
|
||||
import MobileIconButton from '@/components/mobile/MobileIconButton.vue'
|
||||
import MobileInfoGrid from '@/components/mobile/MobileInfoGrid.vue'
|
||||
import MobileSheetHeader from '@/components/mobile/MobileSheetHeader.vue'
|
||||
import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import { toJalali } from '@core/utils/useDataTime'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
item: Product | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
(e: 'toggleBuy', item: Product): void
|
||||
(e: 'toggleSell', item: Product): void
|
||||
(e: 'toggleActive', item: Product): void
|
||||
(e: 'edit', item: Product): void
|
||||
}>()
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
set: (value: boolean) => emit('update:visible', value)
|
||||
})
|
||||
|
||||
function updatedAtLabel(row: Product) {
|
||||
return row.created_at ? `آخرین بروزرسانی: ${toJalali(row.created_at)}` : '-'
|
||||
}
|
||||
|
||||
function cashFeeLabel(row: Product) {
|
||||
return usePrice((row.cash_fee_buy || 0) + (row.cash_fee_sell || 0))
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<MobileCard>
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="flex min-w-0 items-center gap-2">
|
||||
<span class="inline-block h-2.5 w-2.5 shrink-0 rounded-full" :class="statusDotClass" />
|
||||
<button
|
||||
type="button"
|
||||
class="truncate border-none bg-transparent p-0 text-right text-sm font-700 text-gray-900 cursor-pointer"
|
||||
@click="$emit('open', item)"
|
||||
>
|
||||
{{ item.title || '-' }}
|
||||
</button>
|
||||
</div>
|
||||
<MobileIconButton icon="vuesax-linear:edit" color="#2563eb" :size="20" @click="$emit('edit', item)" />
|
||||
</div>
|
||||
|
||||
<div class="mt-3 flex items-center justify-between gap-2">
|
||||
<span class="text-xs text-gray-500">کد محصول: {{ item.id ?? '-' }}</span>
|
||||
<span class="text-sm font-600 text-gray-700">{{ priceLabel }}</span>
|
||||
</div>
|
||||
</MobileCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Product } from '@/model/product'
|
||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||
import MobileIconButton from '@/components/mobile/MobileIconButton.vue'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
|
||||
const props = defineProps<{
|
||||
item: Product
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'open', item: Product): void
|
||||
(e: 'edit', item: Product): void
|
||||
}>()
|
||||
|
||||
const statusDotClass = computed(() => (props.item.active ? 'bg-green-500' : 'bg-red-400'))
|
||||
const priceLabel = computed(() => (props.item.price ? usePrice(props.item.price) : '-'))
|
||||
</script>
|
||||
@@ -1,103 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<BasicShowContext title="لیست قلک ها">
|
||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
||||
</BaseTable>
|
||||
</BasicShowContext>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="tsx" setup>
|
||||
import type {TableInput} from '@core/type/baseTable'
|
||||
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||
import {
|
||||
changeCustomerActivation,
|
||||
changeCustomerArchive,
|
||||
changeCustomerOrderActivation,
|
||||
confirmUser,
|
||||
getCustomerTable
|
||||
} from '@/api/customer'
|
||||
import {cols} from '@/pages/menuPages/customers/base/hooks/fundsColumns'
|
||||
import {useUserStore} from '@/store/userStore'
|
||||
import {usePrefix} from '@core/composable/usePrefix'
|
||||
import {message} from 'ant-design-vue'
|
||||
import {inputRules} from '@/utils/inputRules'
|
||||
import type {User} from '@/model/user'
|
||||
import {toJalaliNoTime} from '@core/utils/useDataTime'
|
||||
import type {Customer} from '@/model/customer'
|
||||
import {onMounted} from 'vue'
|
||||
import {getFundsTable} from "@/api/user";
|
||||
|
||||
const [submit, reload] = useBaseTable()
|
||||
|
||||
interface Prop {
|
||||
customerId?: number
|
||||
}
|
||||
|
||||
const emits = defineEmits(['delete', 'edit', 'add', 'info', 'chargeMoney', 'chargeGold'])
|
||||
const props = defineProps<Prop>()
|
||||
|
||||
const {prefixCls} = usePrefix('pophoverstatus')
|
||||
|
||||
//permission
|
||||
function checkPermission(temp: string) {
|
||||
const store = useUserStore()
|
||||
return store.canAccess(temp)
|
||||
}
|
||||
|
||||
//define table
|
||||
const table = ref(null)
|
||||
|
||||
const confirmVisible = ref<boolean>(false)
|
||||
const confirmInfo = ref<Customer>()
|
||||
|
||||
const tblProps = ref<any>({
|
||||
columns: cols,
|
||||
api: (params: TableInput) => {
|
||||
return getFundsTable(params, props.customerId)
|
||||
}
|
||||
//bordered: true
|
||||
})
|
||||
const refPop = ref()
|
||||
|
||||
const loading = ref<boolean>(false)
|
||||
|
||||
const fetch = () => {
|
||||
// console.log('sss')
|
||||
reload()
|
||||
}
|
||||
// const spinTable = (val: boolean) => {
|
||||
// // console.log('sss')
|
||||
// table?.value?.loading.value = val
|
||||
// }
|
||||
defineExpose({
|
||||
fetch
|
||||
// spinTable
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@prefix: ~'@{prefixCls}-pophoverstatus';
|
||||
.ant-popover,
|
||||
.ant-tooltip {
|
||||
z-index: 1001 !important;
|
||||
}
|
||||
|
||||
.@{prefix} {
|
||||
.ant-menu-rtl.ant-menu-inline,
|
||||
.ant-menu-rtl.ant-menu-vertical {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.ant-menu-item-only-child {
|
||||
border-radius: @border-radius-base;
|
||||
}
|
||||
|
||||
//bor
|
||||
/* .ant-input-group-addon:first-child {
|
||||
!* border: #d9d9d9 1px solid;
|
||||
border-radius: 0 6px 6px 0;*!
|
||||
}*/
|
||||
}
|
||||
</style>
|
||||
@@ -1,104 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<BasicShowContext title="لیست معرفی کرده ها">
|
||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
||||
</BaseTable>
|
||||
</BasicShowContext>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="tsx" setup>
|
||||
import type {TableInput} from '@core/type/baseTable'
|
||||
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||
import {
|
||||
changeCustomerActivation,
|
||||
changeCustomerArchive,
|
||||
changeCustomerOrderActivation,
|
||||
confirmUser,
|
||||
getCustomerTable
|
||||
} from '@/api/customer'
|
||||
import {cols} from '@/pages/menuPages/customers/base/hooks/referralColumns'
|
||||
import {useUserStore} from '@/store/userStore'
|
||||
import {usePrefix} from '@core/composable/usePrefix'
|
||||
import {message} from 'ant-design-vue'
|
||||
import {inputRules} from '@/utils/inputRules'
|
||||
import type {User} from '@/model/user'
|
||||
import {toJalaliNoTime} from '@core/utils/useDataTime'
|
||||
import type {Customer} from '@/model/customer'
|
||||
import {onMounted} from 'vue'
|
||||
import {getReferralsTable} from "@/api/user";
|
||||
|
||||
const [submit, reload] = useBaseTable()
|
||||
|
||||
interface Prop {
|
||||
customerId?: number
|
||||
}
|
||||
|
||||
const emits = defineEmits(['delete', 'edit', 'add', 'info', 'chargeMoney', 'chargeGold'])
|
||||
const props = defineProps<Prop>()
|
||||
|
||||
const {prefixCls} = usePrefix('pophoverstatus')
|
||||
|
||||
//permission
|
||||
function checkPermission(temp: string) {
|
||||
const store = useUserStore()
|
||||
return store.canAccess(temp)
|
||||
}
|
||||
|
||||
//define table
|
||||
const table = ref(null)
|
||||
|
||||
const confirmVisible = ref<boolean>(false)
|
||||
const confirmInfo = ref<Customer>()
|
||||
|
||||
const tblProps = ref<any>({
|
||||
columns: cols,
|
||||
api: (params: TableInput) => {
|
||||
|
||||
return getReferralsTable(params, props.customerId)
|
||||
}
|
||||
//bordered: true
|
||||
})
|
||||
const refPop = ref()
|
||||
|
||||
const loading = ref<boolean>(false)
|
||||
|
||||
const fetch = () => {
|
||||
// console.log('sss')
|
||||
reload()
|
||||
}
|
||||
// const spinTable = (val: boolean) => {
|
||||
// // console.log('sss')
|
||||
// table?.value?.loading.value = val
|
||||
// }
|
||||
defineExpose({
|
||||
fetch
|
||||
// spinTable
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@prefix: ~'@{prefixCls}-pophoverstatus';
|
||||
.ant-popover,
|
||||
.ant-tooltip {
|
||||
z-index: 1001 !important;
|
||||
}
|
||||
|
||||
.@{prefix} {
|
||||
.ant-menu-rtl.ant-menu-inline,
|
||||
.ant-menu-rtl.ant-menu-vertical {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.ant-menu-item-only-child {
|
||||
border-radius: @border-radius-base;
|
||||
}
|
||||
|
||||
//bor
|
||||
/* .ant-input-group-addon:first-child {
|
||||
!* border: #d9d9d9 1px solid;
|
||||
border-radius: 0 6px 6px 0;*!
|
||||
}*/
|
||||
}
|
||||
</style>
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="md:flex pr-3">
|
||||
<div class="flex h-full min-h-0 min-w-0 max-w-full flex-col overflow-hidden">
|
||||
<div class="shrink-0 pr-3 md:flex">
|
||||
<div class="content-center">فعال سازی محصولات:</div>
|
||||
<div class="ml-2 content-center mt-1 md:mt-unset md:mr-4">زمان شروع </div>
|
||||
<div class="ml-2 content-center mt-1 md:mt-unset md:mr-4">زمان شروع</div>
|
||||
<div>
|
||||
<TimePicker
|
||||
v-model:value="startTime"
|
||||
@@ -11,7 +11,7 @@
|
||||
:defaultValue="dayjs('00:00:00', 'HH:mm:ss')"
|
||||
/>
|
||||
</div>
|
||||
<div class="md:mr-4 ml-2 mt-2 md:mt-unset content-center">زمان پایان </div>
|
||||
<div class="md:mr-4 ml-2 mt-2 md:mt-unset content-center">زمان پایان</div>
|
||||
<div>
|
||||
<TimePicker
|
||||
v-model:value="endTime"
|
||||
@@ -20,592 +20,179 @@
|
||||
:defaultValue="dayjs('00:00:00', 'HH:mm:ss')"
|
||||
/>
|
||||
</div>
|
||||
<BaseButton @click="setTimeActive(startTime , endTime)" class="mt-2 md:mt-unset md:mr-2">ثبت</BaseButton>
|
||||
<BaseButton class="mt-2 md:mt-unset md:mr-2" @click="setTimeActive(startTime, endTime)">ثبت</BaseButton>
|
||||
</div>
|
||||
<BasicShowContext title=" لیست محصولات">
|
||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #extraHeader>
|
||||
<BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
||||
<div>افزودن محصول</div>
|
||||
<BaseIcon icon="vuesax-linear:box" Size="16px" />
|
||||
</BaseButton>
|
||||
</template>
|
||||
<template #action="{ column, record }">
|
||||
<div class="gap-2 flex justify-center">
|
||||
<a-tooltip class="cursor-pointer" :key="record.open_buy">
|
||||
<template v-if="!record.open_buy" #title><span>فعال کردن خرید</span></template>
|
||||
<template v-else #title><span>غیر فعال کردن خرید</span></template>
|
||||
<BaseIcon
|
||||
|
||||
<BasicShowContext title="لیست محصولات" class="min-h-0 flex-1">
|
||||
<template #extra>
|
||||
<BaseButton type="primary" size="small" class="flex shrink-0 md:hidden" @click="emits('add')">
|
||||
<div>افزودن</div>
|
||||
<BaseIcon icon="vuesax-linear:box" Size="16px" />
|
||||
</BaseButton>
|
||||
</template>
|
||||
<div class="flex h-full min-h-0 flex-col">
|
||||
<BaseTable v-if="!isMobile" class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #extraHeader>
|
||||
<BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
||||
<div>افزودن محصول</div>
|
||||
<BaseIcon icon="vuesax-linear:box" Size="16px" />
|
||||
</BaseButton>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<div class="gap-2 flex justify-center">
|
||||
<a-tooltip class="cursor-pointer" :key="`buy-${record.id}-${record.open_buy}`">
|
||||
<template v-if="!record.open_buy" #title><span>فعال کردن خرید</span></template>
|
||||
<template v-else #title><span>غیر فعال کردن خرید</span></template>
|
||||
<BaseIcon
|
||||
icon="vuesax-linear:money-receive"
|
||||
:color="record.open_buy ? '#16A34A' : '#DC2626'"
|
||||
@click="activeBuy(record)"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-tooltip class="cursor-pointer" :key="record.open_sell">
|
||||
<template v-if="!record.open_sell" #title><span>فعال کردن فروش</span></template>
|
||||
<template v-else #title><span>غیر فعال کردن فروش</span></template>
|
||||
<BaseIcon
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-tooltip class="cursor-pointer" :key="`sell-${record.id}-${record.open_sell}`">
|
||||
<template v-if="!record.open_sell" #title><span>فعال کردن فروش</span></template>
|
||||
<template v-else #title><span>غیر فعال کردن فروش</span></template>
|
||||
<BaseIcon
|
||||
icon="vuesax-linear:money-send"
|
||||
:color="record.open_sell ? '#16A34A' : '#DC2626'"
|
||||
@click="activeSell(record)"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-tooltip class="cursor-pointer" :key="record.active">
|
||||
<template v-if="!record.active" #title><span>فعال کردن محصول</span></template>
|
||||
<template v-else #title><span>غیر فعال کردن محصول</span></template>
|
||||
<BaseIcon
|
||||
v-if="!record.active"
|
||||
icon="vuesax-linear:close-circle"
|
||||
color="#DC2626"
|
||||
@click="activeProduct(record)"
|
||||
/>
|
||||
<BaseIcon
|
||||
v-else
|
||||
icon="vuesax-linear:tick-circle"
|
||||
color="#16A34A"
|
||||
@click="activeProduct(record)"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<!-- <a-tooltip class="cursor-pointer" :key="record.show">
|
||||
<template v-if="!record.show" #title><span>نشان دادن محصول</span></template>
|
||||
<template v-else #title><span>مخفی کردن محصول</span></template>
|
||||
<BaseIcon
|
||||
v-if="!record.show"
|
||||
icon="vuesax-linear:chart-fail"
|
||||
color="#DC2626"
|
||||
@click="visibilityProduct(record)"
|
||||
/>
|
||||
<BaseIcon
|
||||
v-else
|
||||
icon="vuesax-linear:chart-success"
|
||||
color="#16A34A"
|
||||
@click="visibilityProduct(record)"
|
||||
/>
|
||||
</a-tooltip>-->
|
||||
<a-tooltip
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="emits('edit', record)"
|
||||
>
|
||||
<template #title>
|
||||
<span>ویرایش</span>
|
||||
</template>
|
||||
<BaseIcon icon="vuesax-linear:edit" color="#4572E9" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<!-- <div class="md:hidden">-->
|
||||
<!-- <a-popover title="" placement="right" @cancel="">
|
||||
<template #content>
|
||||
<div class="flex flex-col gap-3">
|
||||
<a-tooltip :key="record.is_archive" placement="bottom" class="cursor-pointer">
|
||||
<template #title>
|
||||
<span>{{
|
||||
record.is_archive ? 'بیرون بردن از بایگانی' : 'بایگانی کردن'
|
||||
}}</span>
|
||||
</template>
|
||||
<a-popconfirm
|
||||
title="آیا مشتری را بایگانی می کنید؟"
|
||||
ok-text="تایید"
|
||||
cancel-text="رد"
|
||||
placement="topLeft"
|
||||
@confirm="archiveFunction(record.id)"
|
||||
@cancel=""
|
||||
>
|
||||
<!– <template #icon><question-circle-outlined style="color: red" /></template>–>
|
||||
<Icon
|
||||
class="cursor-pointer"
|
||||
icon="vuesax-linear:archive-tick"
|
||||
color="#e3bf00"
|
||||
/>
|
||||
</a-popconfirm>
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
:key="record.active"
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="changeCustomerActivationFunction(record.id)"
|
||||
>
|
||||
<template #title>
|
||||
<span>
|
||||
{{ record.active ? 'غیرفعال کردن حساب کاربری' : 'فعال کردن حساب کاربری' }}
|
||||
</span>
|
||||
</template>
|
||||
<Icon
|
||||
:icon="
|
||||
record.active ? 'vuesax-linear:user-tick' : 'vuesax-linear:user-remove'
|
||||
"
|
||||
:color="record.active ? '#54AD06' : '#ad0627'"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="emits('chargeMoney', record)"
|
||||
>
|
||||
<template #title>
|
||||
<span>شارژ کیف پول ریالی</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:wallet-add" color="#9333ea" />
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="emits('chargeGold', record)"
|
||||
>
|
||||
<template #title>
|
||||
<span>شارژ کیف پول طلایی</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:wallet-money" color="#ea580c" />
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="emits('info', record)"
|
||||
>
|
||||
<template #title>
|
||||
<span>مشخصات</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="emits('edit', record)"
|
||||
>
|
||||
<template #title>
|
||||
<span>ویرایش</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:edit" color="#4572E9" />
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
:key="record.trade_active"
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="changeCustomerOrderActivationFunction(record.id)"
|
||||
>
|
||||
<template #title>
|
||||
<span>
|
||||
{{
|
||||
record.trade_active ? 'غیر مجاز کردن سفارش دهی' : 'مجاز کردن سفارش دهی'
|
||||
}}
|
||||
</span>
|
||||
</template>
|
||||
<Icon
|
||||
:icon="
|
||||
record.trade_active === 1
|
||||
? 'vuesax-linear:box-tick'
|
||||
: 'vuesax-linear:box-remove'
|
||||
"
|
||||
:color="record.trade_active === 1 ? '#54AD06' : '#ad0627'"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
v-if="!record.is_legal"
|
||||
:key="record.trade_active"
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="openConfirmUser(record)"
|
||||
>
|
||||
<template #title>
|
||||
<span>تایید کاربر</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:user-search" color="#0d9488" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<!– <template #icon><question-circle-outlined style="color: red" /></template>–>
|
||||
<Icon class="cursor-pointer" icon="vuesax-linear:setting" color="#000" />
|
||||
</a-popover>
|
||||
</div>
|
||||
<div class="gap-2 hidden md:flex justify-center">
|
||||
<a-tooltip :key="record.is_archive" placement="bottom" class="cursor-pointer">
|
||||
<template #title>
|
||||
<span>{{ record.is_archive ? 'بیرون بردن از بایگانی' : 'بایگانی کردن' }}</span>
|
||||
</template>
|
||||
<a-popconfirm
|
||||
title="آیا مشتری را بایگانی می کنید؟"
|
||||
ok-text="تایید"
|
||||
cancel-text="رد"
|
||||
placement="topLeft"
|
||||
@confirm="archiveFunction(record.id)"
|
||||
@cancel=""
|
||||
>
|
||||
<!– <template #icon><question-circle-outlined style="color: red" /></template>–>
|
||||
<Icon class="cursor-pointer" icon="vuesax-linear:archive-tick" color="#e3bf00" />
|
||||
</a-popconfirm>
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
:key="record.active"
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="changeCustomerActivationFunction(record.id)"
|
||||
>
|
||||
<template #title>
|
||||
<span>{{
|
||||
record.active ? 'غیرفعال کردن حساب کاربری' : 'فعال کردن حساب کاربری'
|
||||
}}</span>
|
||||
</template>
|
||||
<Icon
|
||||
:icon="record.active ? 'vuesax-linear:user-tick' : 'vuesax-linear:user-remove'"
|
||||
:color="record.active ? '#54AD06' : '#ad0627'"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="emits('chargeMoney', record)"
|
||||
>
|
||||
<template #title>
|
||||
<span>شارژ کیف پول ریالی</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:wallet-add" color="#9333ea" />
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="emits('chargeGold', record)"
|
||||
>
|
||||
<template #title>
|
||||
<span>شارژ کیف پول طلایی</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:wallet-money" color="#ea580c" />
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('info', record)">
|
||||
<template #title>
|
||||
<span>مشخصات</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('edit', record)">
|
||||
<template #title>
|
||||
<span>ویرایش</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:edit" color="#4572E9" />
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
:key="record.trade_active"
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="changeCustomerOrderActivationFunction(record.id)"
|
||||
>
|
||||
<template #title>
|
||||
<span>{{
|
||||
record.trade_active ? 'غیر مجاز کردن سفارش دهی' : 'مجاز کردن سفارش دهی'
|
||||
}}</span>
|
||||
</template>
|
||||
<Icon
|
||||
:icon="
|
||||
record.trade_active === 1 ? 'vuesax-linear:box-tick' : 'vuesax-linear:box-remove'
|
||||
"
|
||||
:color="record.trade_active === 1 ? '#54AD06' : '#ad0627'"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-tooltip
|
||||
v-if="!record.is_legal"
|
||||
:key="record.trade_active"
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="openConfirmUser(record)"
|
||||
>
|
||||
<template #title>
|
||||
<span>تایید کاربر</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:user-search" color="#0d9488" />
|
||||
</a-tooltip>
|
||||
<!– <a-tooltip
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="emits('delete', record.id)"
|
||||
>
|
||||
<template #title>
|
||||
<span>حذف</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:trash" color="#EB3535" />
|
||||
</a-tooltip>–>
|
||||
</div>-->
|
||||
</template>
|
||||
</BaseTable>
|
||||
<BasicModal
|
||||
v-model:visible="confirmVisible"
|
||||
width="50vw"
|
||||
title="تایید مشتری"
|
||||
@ok="confirmUserFunction"
|
||||
ok-text="تایید"
|
||||
:loading="loading"
|
||||
@close="confirmVisible = false"
|
||||
>
|
||||
<div class="mt-[-10px] mb-10 font-bold">آیا از اطلاعات وارد شده اطمینان دارید؟</div>
|
||||
<div class="grid grid-cols-4 gap-2">
|
||||
<BasicInfoItem title="نام و نام خانوادگی" :value="confirmInfo?.name" />
|
||||
<BasicInfoItem title="شماره همراه" :value="confirmInfo?.mobile" />
|
||||
<BasicInfoItem
|
||||
title="تاریخ تولد"
|
||||
:value="confirmInfo?.birth_date ? toJalaliNoTime(confirmInfo?.birth_date) : '_'"
|
||||
/>
|
||||
<BasicInfoItem title="شماره تلفن" :value="confirmInfo?.phone_number ?? '_'" />
|
||||
<BasicInfoItem title="نام استان" :value="confirmInfo?.province" />
|
||||
<BasicInfoItem title="نام شهر" :value="confirmInfo?.city" />
|
||||
<BasicInfoItem title="شماره ملی" :value="confirmInfo?.n_code" />
|
||||
</div>
|
||||
</BasicModal>
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-tooltip class="cursor-pointer" :key="`active-${record.id}-${record.active}`">
|
||||
<template v-if="!record.active" #title><span>فعال کردن محصول</span></template>
|
||||
<template v-else #title><span>غیر فعال کردن محصول</span></template>
|
||||
<BaseIcon
|
||||
v-if="!record.active"
|
||||
icon="vuesax-linear:close-circle"
|
||||
color="#DC2626"
|
||||
@click="activeProduct(record)"
|
||||
/>
|
||||
<BaseIcon
|
||||
v-else
|
||||
icon="vuesax-linear:tick-circle"
|
||||
color="#16A34A"
|
||||
@click="activeProduct(record)"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('edit', record)">
|
||||
<template #title><span>ویرایش</span></template>
|
||||
<BaseIcon icon="vuesax-linear:edit" color="#4572E9" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</BaseTable>
|
||||
|
||||
<ProductMobileCardList
|
||||
v-else
|
||||
ref="mobileListRef"
|
||||
class="min-h-0 flex-1"
|
||||
@edit="emits('edit', $event)"
|
||||
@toggleBuy="activeBuy"
|
||||
@toggleSell="activeSell"
|
||||
@toggleActive="activeProduct"
|
||||
/>
|
||||
</div>
|
||||
</BasicShowContext>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="tsx" setup>
|
||||
import type { TableInput } from '@core/type/baseTable'
|
||||
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||
<script setup lang="ts">
|
||||
import { message, Modal, TimePicker } from 'ant-design-vue'
|
||||
import locale from 'ant-design-vue/es/date-picker/locale/fa_IR'
|
||||
import dayjs from 'dayjs'
|
||||
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
||||
import { ref } from 'vue'
|
||||
import {
|
||||
changeActiveProduct,
|
||||
changeBuyActiveProduct,
|
||||
changeCustomerArchive,
|
||||
changeCustomerOrderActivation,
|
||||
changeProductActivation, changeSellActiveProduct, changeVisibilityProduct,
|
||||
confirmUser,
|
||||
getCustomerTable, setActiveTime
|
||||
} from "@/api/product";
|
||||
import { cols } from '@/pages/menuPages/products/base/hooks/customerColumns'
|
||||
import { useUserStore } from '@/store/userStore'
|
||||
import { usePrefix } from '@core/composable/usePrefix'
|
||||
import { message, type TimePickerProps } from "ant-design-vue";
|
||||
import { inputRules } from '@/utils/inputRules'
|
||||
import type { User } from '@/model/user'
|
||||
import { toJalaliNoTime } from '@core/utils/useDataTime'
|
||||
import type { Customer } from '@/model/customer'
|
||||
import { onMounted } from 'vue'
|
||||
import { Modal } from 'ant-design-vue'
|
||||
import type {Product} from "@/model/product";
|
||||
import {resolve} from "path";
|
||||
changeSellActiveProduct,
|
||||
getCustomerTable,
|
||||
setActiveTime
|
||||
} from '@/api/product'
|
||||
import { cols } from '@/pages/menuPages/products/base/hooks/productColumns'
|
||||
import ProductMobileCardList from '@/pages/menuPages/products/base/components/ProductMobileCardList.vue'
|
||||
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
||||
import type { TableInput } from '@core/type/baseTable'
|
||||
import type { Product } from '@/model/product'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
const [submit, reload] = useBaseTable()
|
||||
/*const select = (val) => {
|
||||
val.sts === 1 ? return ['1'] : ['2']
|
||||
}*/
|
||||
//emits
|
||||
const emits = defineEmits(['delete', 'edit', 'add', 'info', 'chargeMoney', 'chargeGold'])
|
||||
const emits = defineEmits(['edit', 'add'])
|
||||
|
||||
const { prefixCls } = usePrefix('pophoverstatus')
|
||||
|
||||
//permission
|
||||
function checkPermission(temp: string) {
|
||||
const store = useUserStore()
|
||||
return store.canAccess(temp)
|
||||
}
|
||||
|
||||
//define table
|
||||
const table = ref(null)
|
||||
const data = ref()
|
||||
const confirmVisible = ref<boolean>(false)
|
||||
const confirmInfo = ref<Customer>()
|
||||
|
||||
const tblProps = ref<any>({
|
||||
columns: cols,
|
||||
api: async (params: TableInput) => {
|
||||
const data = await getCustomerTable(params)
|
||||
startTime.value = dayjs(data?.auto_open_at, 'HH:mm:ss')
|
||||
endTime.value = dayjs(data?.auto_close_at, 'HH:mm:ss')
|
||||
return data
|
||||
},
|
||||
//bordered: true
|
||||
})
|
||||
const refPop = ref()
|
||||
|
||||
// function edit(recordId){
|
||||
// emits('edit', recordId);
|
||||
// console.log( recordId);
|
||||
// console.log( visibleAction.value[parseInt(recordId)]);
|
||||
// visibleAction.value[recordId]=false
|
||||
// console.log('ddddd')
|
||||
// }
|
||||
const loading = ref<boolean>(false)
|
||||
|
||||
async function changeCustomerActivationFunction(id: number) {
|
||||
try {
|
||||
loading.value = true
|
||||
// await changeCustomerActivation(id)
|
||||
reload()
|
||||
message.success('تغییر وضعیت با موفقیت انجام شد')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function openConfirmUser(user: Customer) {
|
||||
confirmInfo.value = user
|
||||
confirmVisible.value = true
|
||||
}
|
||||
|
||||
async function archiveFunction(id: number) {
|
||||
try {
|
||||
loading.value = true
|
||||
await changeCustomerArchive(id)
|
||||
reload()
|
||||
message.success('مشتری با موفقیت بایگانی شد.')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmUserFunction() {
|
||||
try {
|
||||
loading.value = true
|
||||
await confirmUser(confirmInfo.value?.id)
|
||||
reload()
|
||||
confirmVisible.value = false
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function changeCustomerOrderActivationFunction(id: number) {
|
||||
try {
|
||||
loading.value = true
|
||||
await changeCustomerOrderActivation(id)
|
||||
reload()
|
||||
message.success('تغییر وضعیت سفارش دهی با موفقیت انجام شد')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const fetch = () => {
|
||||
// console.log('sss')
|
||||
reload()
|
||||
}
|
||||
// const spinTable = (val: boolean) => {
|
||||
// // console.log('sss')
|
||||
// table?.value?.loading.value = val
|
||||
// }
|
||||
defineExpose({
|
||||
fetch
|
||||
// spinTable
|
||||
})
|
||||
|
||||
const activeSell = async (product: Product) => {
|
||||
Modal.confirm({
|
||||
title: 'وضعیت فروش محصول تغییر خواهد یافت.آیا مطمئن هستید؟',
|
||||
okText: 'بله',
|
||||
cancelText: 'انصراف',
|
||||
centered: true,
|
||||
onOk: async () => {
|
||||
await changeSellActiveProduct(product.id)
|
||||
reload()
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const activeBuy = async (product: Product) => {
|
||||
Modal.confirm({
|
||||
title: 'وضعیت خرید محصول تغییر خواهد یافت.آیا مطمئن هستید؟',
|
||||
okText: 'بله',
|
||||
cancelText: 'انصراف',
|
||||
centered: true,
|
||||
onOk: async () => {
|
||||
await changeBuyActiveProduct(product.id)
|
||||
reload()
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel')
|
||||
}
|
||||
})
|
||||
}
|
||||
const activeProduct = async (product: Product) => {
|
||||
Modal.confirm({
|
||||
title: 'آیا مطمئن به انجام این عملیات هستید؟',
|
||||
okText: 'بله',
|
||||
cancelText: 'انصراف',
|
||||
centered: true,
|
||||
onOk: async () => {
|
||||
await changeActiveProduct(product.id)
|
||||
reload()
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const visibilityProduct = async (product: Product) => {
|
||||
Modal.confirm({
|
||||
title: 'آیا مطمئن به انجام این عملیات هستید؟',
|
||||
okText: 'بله',
|
||||
cancelText: 'انصراف',
|
||||
centered: true,
|
||||
onOk: async () => {
|
||||
await changeVisibilityProduct(product.id)
|
||||
reload()
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
import { ref } from 'vue'
|
||||
import { TimePicker } from 'ant-design-vue'
|
||||
import dayjs from 'dayjs';
|
||||
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
||||
import locale from "ant-design-vue/es/date-picker/locale/fa_IR"
|
||||
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||
const startTime = ref(dayjs('00:00:00', 'HH:mm:ss'))
|
||||
const endTime = ref(dayjs('00:00:00', 'HH:mm:ss'))
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
const customLocale = ref({
|
||||
...locale,
|
||||
lang: {
|
||||
...locale.lang,
|
||||
now: "اکنون",
|
||||
ok: "ثبت",
|
||||
},
|
||||
})
|
||||
async function setTimeActive(start: string, end: string) {
|
||||
try {
|
||||
const startTimeFormatted = dayjs(start, 'HH:mm:ss').format('HH:mm:ss')
|
||||
const endTimeFormatted = dayjs(end, 'HH:mm:ss').format('HH:mm:ss')
|
||||
await setActiveTime(startTimeFormatted, endTimeFormatted)
|
||||
message.success('با موفقیت ثبت شد.')
|
||||
} catch (e) {
|
||||
message.error('زمان را وارد کنید.')
|
||||
// console.log(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
now: 'اکنون',
|
||||
ok: 'ثبت'
|
||||
}
|
||||
})
|
||||
|
||||
const tblProps = ref<any>({
|
||||
columns: cols,
|
||||
api: async (params: TableInput) => {
|
||||
const data = (await getCustomerTable(params)) as any
|
||||
startTime.value = dayjs(data?.auto_open_at, 'HH:mm:ss')
|
||||
endTime.value = dayjs(data?.auto_close_at, 'HH:mm:ss')
|
||||
return data
|
||||
}
|
||||
})
|
||||
|
||||
const fetch = async () => {
|
||||
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||
}
|
||||
|
||||
defineExpose({ fetch })
|
||||
|
||||
function confirmToggle(title: string, onOk: () => Promise<void>) {
|
||||
Modal.confirm({
|
||||
title,
|
||||
okText: 'بله',
|
||||
cancelText: 'انصراف',
|
||||
centered: true,
|
||||
onOk
|
||||
})
|
||||
}
|
||||
|
||||
const activeSell = (product: Product) =>
|
||||
confirmToggle('وضعیت فروش محصول تغییر خواهد یافت.آیا مطمئن هستید؟', async () => {
|
||||
await changeSellActiveProduct(product.id)
|
||||
await fetch()
|
||||
})
|
||||
|
||||
const activeBuy = (product: Product) =>
|
||||
confirmToggle('وضعیت خرید محصول تغییر خواهد یافت.آیا مطمئن هستید؟', async () => {
|
||||
await changeBuyActiveProduct(product.id)
|
||||
await fetch()
|
||||
})
|
||||
|
||||
const activeProduct = (product: Product) =>
|
||||
confirmToggle('آیا مطمئن به انجام این عملیات هستید؟', async () => {
|
||||
await changeActiveProduct(product.id)
|
||||
await fetch()
|
||||
})
|
||||
|
||||
async function setTimeActive(start: dayjs.Dayjs | string, end: dayjs.Dayjs | string) {
|
||||
try {
|
||||
const startTimeFormatted = dayjs(start).format('HH:mm:ss')
|
||||
const endTimeFormatted = dayjs(end).format('HH:mm:ss')
|
||||
await setActiveTime(startTimeFormatted, endTimeFormatted)
|
||||
message.success('با موفقیت ثبت شد.')
|
||||
} catch {
|
||||
message.error('زمان را وارد کنید.')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@prefix: ~'@{prefixCls}-pophoverstatus';
|
||||
.ant-popover,
|
||||
.ant-tooltip {
|
||||
z-index: 1001 !important;
|
||||
}
|
||||
|
||||
.@{prefix} {
|
||||
.ant-menu-rtl.ant-menu-inline,
|
||||
.ant-menu-rtl.ant-menu-vertical {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.ant-menu-item-only-child {
|
||||
border-radius: @border-radius-base;
|
||||
}
|
||||
|
||||
//bor
|
||||
/* .ant-input-group-addon:first-child {
|
||||
!* border: #d9d9d9 1px solid;
|
||||
border-radius: 0 6px 6px 0;*!
|
||||
}*/
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,291 +0,0 @@
|
||||
import type { BaseTableColumn } from '@core/type/baseTable'
|
||||
import {toJalali, toJalaliNoTime, toTime} from '@core/utils/useDataTime'
|
||||
import CustomTag from '@/components/CustomTag.vue'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
/*import BudgetSelect from '@/components/BudgetSelect.vue'
|
||||
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
||||
import type { FilterSelect } from '@/model/filterSelect'
|
||||
import type { DocItem } from '@/model/docType'*/
|
||||
|
||||
//define store
|
||||
//const store = useStaticApiListsStore()
|
||||
//document types
|
||||
/*let docTypes: DocItem[]*/
|
||||
/*onMounted(async () => {
|
||||
docTypes = await store.readyDocTypeList()
|
||||
})
|
||||
const filteredDocTypes: FilterSelect = docTypes?.map((x: DocItem) => {
|
||||
return {
|
||||
label: x.title,
|
||||
value: x.id
|
||||
}
|
||||
})*/
|
||||
|
||||
export const cols: BaseTableColumn[] = [
|
||||
{
|
||||
title: 'ردیف',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
filterable: false,
|
||||
sortable: false,
|
||||
customRender: (record: any) => {
|
||||
return record.index + 1
|
||||
}
|
||||
},
|
||||
// {
|
||||
// title: 'کد کاربری',
|
||||
// dataIndex: 'id',
|
||||
// key: 'id',
|
||||
// type: 'Number'
|
||||
// // className: 'column-money'
|
||||
// },
|
||||
{
|
||||
title: 'نام',
|
||||
dataIndex: 'title',
|
||||
key: 'title',
|
||||
type: 'Text'
|
||||
},
|
||||
{
|
||||
title: 'تصویر',
|
||||
dataIndex: 'title',
|
||||
key: 'title',
|
||||
type: 'Text'
|
||||
},
|
||||
{
|
||||
title: 'وزن',
|
||||
dataIndex: 'amount',
|
||||
key: 'amount',
|
||||
type: 'Number'
|
||||
},
|
||||
{
|
||||
title: 'واحد',
|
||||
dataIndex: 'unit',
|
||||
key: 'unit',
|
||||
type: 'Text'
|
||||
},
|
||||
{
|
||||
title: 'دسته بندی',
|
||||
dataIndex: 'group_type',
|
||||
key: 'group_type',
|
||||
type: 'Text'
|
||||
},
|
||||
{
|
||||
title: 'قیمت',
|
||||
dataIndex: 'price',
|
||||
key: 'price',
|
||||
type: 'Text',
|
||||
},
|
||||
{
|
||||
title: 'متد',
|
||||
dataIndex: 'reading_method',
|
||||
key: 'reading_method',
|
||||
type: 'Text',
|
||||
customRender: (record) => {
|
||||
return (
|
||||
<div class="flex justify-center">
|
||||
<CustomTag>
|
||||
{record?.record.reading_method ? record?.record.reading_method : 'دستی'}
|
||||
</CustomTag>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
/*{
|
||||
title: 'کارمزد درصدی موقع خرید',
|
||||
dataIndex: 'percent_fee_buy',
|
||||
key: 'percent_fee_buy',
|
||||
type: 'Number'
|
||||
},
|
||||
{
|
||||
title: 'کارمزد درصدی موقع فروش',
|
||||
dataIndex: 'percent_fee_sell',
|
||||
key: 'percent_fee_sell',
|
||||
type: 'Number'
|
||||
},*/
|
||||
{
|
||||
title: 'هزینه نقدی موقع خرید',
|
||||
dataIndex: 'cash_fee_buy',
|
||||
key: 'cash_fee_buy',
|
||||
type: 'Number'
|
||||
},
|
||||
{
|
||||
title: 'هزینه نقدی موقع فروش',
|
||||
dataIndex: 'cash_fee_sell',
|
||||
key: 'cash_fee_sell',
|
||||
type: 'Number'
|
||||
},
|
||||
{
|
||||
title: 'وضعیت محصول',
|
||||
dataIndex: 'active',
|
||||
key: 'active',
|
||||
type: 'Boolean',
|
||||
customRender: (record) => {
|
||||
return (
|
||||
<div class="flex justify-center">
|
||||
<CustomTag
|
||||
class="px-3 font-bold py-1"
|
||||
textColor={record?.record.active ? '#65a30d' : '#dc2626'}
|
||||
bgColor={record?.record.active ? '#ecfccb' : '#fee2e2'}
|
||||
>
|
||||
{record?.record.active ? 'باز' : 'بسته'}
|
||||
</CustomTag>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'عملیات',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
slot: 'action',
|
||||
filterable: false
|
||||
}
|
||||
|
||||
// {
|
||||
// title: 'کد معرف',
|
||||
// dataIndex: 'referral_id',
|
||||
// key: 'referral_id',
|
||||
// type: 'Text'
|
||||
// },
|
||||
// {
|
||||
// title: 'نام معرف کاربر',
|
||||
// dataIndex: 'referral.name',
|
||||
// key: 'referral.name',
|
||||
// type: 'Text'
|
||||
// },
|
||||
// {
|
||||
// title: 'موجودی ریالی (ریال)',
|
||||
// dataIndex: 'wallet.cash',
|
||||
// key: 'wallet.cash',
|
||||
// type: 'Number',
|
||||
// customRender(data) {
|
||||
// return usePrice(data.record?.wallet[0]?.cash ?? 0, true, '')
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// title: 'موجودی طلایی (گرم)',
|
||||
// dataIndex: 'wallet.gold',
|
||||
// key: 'wallet.gold',
|
||||
// type: 'Number',
|
||||
// filterable: false,
|
||||
// customRender(data) {
|
||||
// let sum = 0
|
||||
// data.record.wallet.forEach((x) => (sum += x.gold))
|
||||
// return sum.toFixed(3)
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// title: 'اخرین بازدید',
|
||||
// dataIndex: 'last_seen_at',
|
||||
// key: 'last_seen_at',
|
||||
// type: 'DateTime',
|
||||
// customRender(data) {
|
||||
// return toJalali(data.record.last_seen_at)
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// title: 'وضعیت کاربر',
|
||||
// dataIndex: 'active',
|
||||
// key: 'active',
|
||||
// type: 'Boolean',
|
||||
// customRender: ({ record }) => {
|
||||
// return record.active ? (
|
||||
// <div class="flex justify-center">
|
||||
// <CustomTag textColor="#25855A" bgColor="#E3F4E9" class={'font-bold px-6'}>
|
||||
// فعال
|
||||
// </CustomTag>
|
||||
// </div>
|
||||
// ) : (
|
||||
// <div class="flex justify-center">
|
||||
// <CustomTag textColor="#C53030" bgColor="#FECACA" class={'font-bold px-6'}>
|
||||
// غیر فعال
|
||||
// </CustomTag>
|
||||
// </div>
|
||||
// )
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// title: 'وضعیت سفارش دهی',
|
||||
// dataIndex: 'trade_active',
|
||||
// key: 'trade_active',
|
||||
// sortable: false,
|
||||
// type: 'Boolean',
|
||||
// customRender: ({ record }) => {
|
||||
// return record.trade_active ? (
|
||||
// <div class="flex justify-center">
|
||||
// <CustomTag textColor="#25855A" bgColor="#E3F4E9" class={'font-bold px-6'}>
|
||||
// مجاز
|
||||
// </CustomTag>
|
||||
// </div>
|
||||
// ) : (
|
||||
// <div class="flex justify-center">
|
||||
// <CustomTag textColor="#C53030" bgColor="#FECACA" class={'font-bold px-6'}>
|
||||
// غیر مجاز
|
||||
// </CustomTag>
|
||||
// </div>
|
||||
// )
|
||||
// }
|
||||
// },
|
||||
/*{
|
||||
title: 'نقش',
|
||||
dataIndex: 'user_type_record',
|
||||
key: 'user_type_record',
|
||||
type: 'Select',
|
||||
filterable: false,
|
||||
sortable: false,
|
||||
filterComponentProps: {
|
||||
multiple: true
|
||||
},
|
||||
filterField: 'user_type_obj.id',
|
||||
filterComponent: ReadyUserTypeSelect,
|
||||
/!* customRender: (record: any) => {
|
||||
return record.record.user_type_record?.value
|
||||
}*!/
|
||||
customRender: ({ record }) => {
|
||||
const items = record.is_admin ? (
|
||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9">
|
||||
ادمین
|
||||
</CustomTag>
|
||||
) : (
|
||||
record.user_type_obj.map((item: { title: any }, index: any) => {
|
||||
if (item) {
|
||||
return (
|
||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9">
|
||||
{item?.title}
|
||||
</CustomTag>
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
return <div class="flex justify-center items-center gap-2">{items}</div>
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'حوزه',
|
||||
dataIndex: 'section',
|
||||
key: 'section'
|
||||
},
|
||||
{
|
||||
title: 'شماره موبایل',
|
||||
dataIndex: 'mobile',
|
||||
type: 'Number',
|
||||
key: 'mobile'
|
||||
},
|
||||
{
|
||||
title: 'تاریخ ثبت',
|
||||
dataIndex: 'created_at',
|
||||
key: 'created_at',
|
||||
type: 'DateTime',
|
||||
customRender: (record: any) => {
|
||||
return toJalaliNoTime(record.record.created_at)
|
||||
}
|
||||
},*/
|
||||
// {
|
||||
// title: 'عملیات',
|
||||
// dataIndex: 'action',
|
||||
// key: 'action',
|
||||
// slot: 'action',
|
||||
// fixed: 'right',
|
||||
// filterable: false
|
||||
// }
|
||||
]
|
||||
@@ -1,54 +0,0 @@
|
||||
import type { BaseTableColumn } from '@core/type/baseTable'
|
||||
import { toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
||||
import CustomTag from '@/components/CustomTag.vue'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
/*import BudgetSelect from '@/components/BudgetSelect.vue'
|
||||
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
||||
import type { FilterSelect } from '@/model/filterSelect'
|
||||
import type { DocItem } from '@/model/docType'*/
|
||||
|
||||
//define store
|
||||
//const store = useStaticApiListsStore()
|
||||
//document types
|
||||
/*let docTypes: DocItem[]*/
|
||||
/*onMounted(async () => {
|
||||
docTypes = await store.readyDocTypeList()
|
||||
})
|
||||
const filteredDocTypes: FilterSelect = docTypes?.map((x: DocItem) => {
|
||||
return {
|
||||
label: x.title,
|
||||
value: x.id
|
||||
}
|
||||
})*/
|
||||
|
||||
export const cols: BaseTableColumn[] = [
|
||||
{
|
||||
title: 'ردیف',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
filterable: false,
|
||||
sortable: false,
|
||||
customRender: (record: any) => {
|
||||
return record.index + 1
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'کد قلک',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
type: 'Number'
|
||||
// className: 'column-money'
|
||||
},
|
||||
{
|
||||
title: 'نام قلک',
|
||||
dataIndex: 'fund.title',
|
||||
key: 'fund.title',
|
||||
type: 'Text'
|
||||
},
|
||||
{
|
||||
title: 'موجودی طلا',
|
||||
dataIndex: 'gold',
|
||||
key: 'gold',
|
||||
type: 'Number'
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,92 @@
|
||||
import type { BaseTableColumn } from '@core/type/baseTable'
|
||||
import CustomTag from '@/components/CustomTag.vue'
|
||||
|
||||
export const cols: BaseTableColumn[] = [
|
||||
{
|
||||
title: 'ردیف',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
filterable: false,
|
||||
sortable: false,
|
||||
customRender: (record: any) => record.index + 1
|
||||
},
|
||||
{
|
||||
title: 'نام',
|
||||
dataIndex: 'title',
|
||||
key: 'title',
|
||||
type: 'Text'
|
||||
},
|
||||
{
|
||||
title: 'وزن',
|
||||
dataIndex: 'amount',
|
||||
key: 'amount',
|
||||
type: 'Number'
|
||||
},
|
||||
{
|
||||
title: 'واحد',
|
||||
dataIndex: 'unit',
|
||||
key: 'unit',
|
||||
type: 'Text'
|
||||
},
|
||||
{
|
||||
title: 'دسته بندی',
|
||||
dataIndex: 'group_type',
|
||||
key: 'group_type',
|
||||
type: 'Text'
|
||||
},
|
||||
{
|
||||
title: 'قیمت',
|
||||
dataIndex: 'price',
|
||||
key: 'price',
|
||||
type: 'Text'
|
||||
},
|
||||
{
|
||||
title: 'متد',
|
||||
dataIndex: 'reading_method',
|
||||
key: 'reading_method',
|
||||
type: 'Text',
|
||||
customRender: (record) => (
|
||||
<div class="flex justify-center">
|
||||
<CustomTag textColor="#1d4ed8" bgColor="#dbeafe">
|
||||
{record?.record.reading_method ? record?.record.reading_method : 'دستی'}
|
||||
</CustomTag>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'هزینه نقدی موقع خرید',
|
||||
dataIndex: 'cash_fee_buy',
|
||||
key: 'cash_fee_buy',
|
||||
type: 'Number'
|
||||
},
|
||||
{
|
||||
title: 'هزینه نقدی موقع فروش',
|
||||
dataIndex: 'cash_fee_sell',
|
||||
key: 'cash_fee_sell',
|
||||
type: 'Number'
|
||||
},
|
||||
{
|
||||
title: 'وضعیت محصول',
|
||||
dataIndex: 'active',
|
||||
key: 'active',
|
||||
type: 'Boolean',
|
||||
customRender: (record) => (
|
||||
<div class="flex justify-center">
|
||||
<CustomTag
|
||||
class="px-3 font-bold py-1"
|
||||
textColor={record?.record.active ? '#65a30d' : '#dc2626'}
|
||||
bgColor={record?.record.active ? '#ecfccb' : '#fee2e2'}
|
||||
>
|
||||
{record?.record.active ? 'باز' : 'بسته'}
|
||||
</CustomTag>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'عملیات',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
slot: 'action',
|
||||
filterable: false
|
||||
}
|
||||
]
|
||||
@@ -1,163 +0,0 @@
|
||||
import type { BaseTableColumn } from '@core/type/baseTable'
|
||||
import { toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
||||
import CustomTag from '@/components/CustomTag.vue'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
/*import BudgetSelect from '@/components/BudgetSelect.vue'
|
||||
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
||||
import type { FilterSelect } from '@/model/filterSelect'
|
||||
import type { DocItem } from '@/model/docType'*/
|
||||
|
||||
//define store
|
||||
//const store = useStaticApiListsStore()
|
||||
//document types
|
||||
/*let docTypes: DocItem[]*/
|
||||
/*onMounted(async () => {
|
||||
docTypes = await store.readyDocTypeList()
|
||||
})
|
||||
const filteredDocTypes: FilterSelect = docTypes?.map((x: DocItem) => {
|
||||
return {
|
||||
label: x.title,
|
||||
value: x.id
|
||||
}
|
||||
})*/
|
||||
|
||||
export const cols: BaseTableColumn[] = [
|
||||
{
|
||||
title: 'ردیف',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
filterable: false,
|
||||
sortable: false,
|
||||
customRender: (record: any) => {
|
||||
return record.index + 1
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'کد کاربری',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
type: 'Number'
|
||||
// className: 'column-money'
|
||||
},
|
||||
{
|
||||
title: 'نام و نام خانوادگی',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
type: 'Text'
|
||||
},
|
||||
{
|
||||
title: 'شماره موبایل',
|
||||
dataIndex: 'mobile',
|
||||
key: 'mobile',
|
||||
type: 'Number'
|
||||
},
|
||||
{
|
||||
title: 'نوع کاربر',
|
||||
dataIndex: 'type_str',
|
||||
key: 'type_str',
|
||||
type: 'Text'
|
||||
},
|
||||
{
|
||||
title: 'کد معرف',
|
||||
dataIndex: 'referral_id',
|
||||
key: 'referral_id',
|
||||
type: 'Text'
|
||||
},
|
||||
{
|
||||
title: 'وضعیت کاربر',
|
||||
dataIndex: 'active',
|
||||
key: 'active',
|
||||
type: 'Boolean',
|
||||
customRender: ({ record }) => {
|
||||
return record.active ? (
|
||||
<div class="flex justify-center">
|
||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9" class={'font-bold px-6'}>
|
||||
فعال
|
||||
</CustomTag>
|
||||
</div>
|
||||
) : (
|
||||
<div class="flex justify-center">
|
||||
<CustomTag textColor="#C53030" bgColor="#FECACA" class={'font-bold px-6'}>
|
||||
غیر فعال
|
||||
</CustomTag>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'وضعیت سفارش دهی',
|
||||
dataIndex: 'active',
|
||||
key: 'active',
|
||||
filterable: false,
|
||||
sortable: false,
|
||||
customRender: ({ record }) => {
|
||||
return record.trade_active === 1 ? (
|
||||
<div class="flex justify-center">
|
||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9" class={'font-bold px-6'}>
|
||||
مجاز
|
||||
</CustomTag>
|
||||
</div>
|
||||
) : (
|
||||
<div class="flex justify-center">
|
||||
<CustomTag textColor="#C53030" bgColor="#FECACA" class={'font-bold px-6'}>
|
||||
غیر مجاز
|
||||
</CustomTag>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
/*{
|
||||
title: 'نقش',
|
||||
dataIndex: 'user_type_record',
|
||||
key: 'user_type_record',
|
||||
type: 'Select',
|
||||
filterable: false,
|
||||
sortable: false,
|
||||
filterComponentProps: {
|
||||
multiple: true
|
||||
},
|
||||
filterField: 'user_type_obj.id',
|
||||
filterComponent: ReadyUserTypeSelect,
|
||||
/!* customRender: (record: any) => {
|
||||
return record.record.user_type_record?.value
|
||||
}*!/
|
||||
customRender: ({ record }) => {
|
||||
const items = record.is_admin ? (
|
||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9">
|
||||
ادمین
|
||||
</CustomTag>
|
||||
) : (
|
||||
record.user_type_obj.map((item: { title: any }, index: any) => {
|
||||
if (item) {
|
||||
return (
|
||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9">
|
||||
{item?.title}
|
||||
</CustomTag>
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
return <div class="flex justify-center items-center gap-2">{items}</div>
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'حوزه',
|
||||
dataIndex: 'section',
|
||||
key: 'section'
|
||||
},
|
||||
{
|
||||
title: 'شماره موبایل',
|
||||
dataIndex: 'mobile',
|
||||
type: 'Number',
|
||||
key: 'mobile'
|
||||
},
|
||||
{
|
||||
title: 'تاریخ ثبت',
|
||||
dataIndex: 'created_at',
|
||||
key: 'created_at',
|
||||
type: 'DateTime',
|
||||
customRender: (record: any) => {
|
||||
return toJalaliNoTime(record.record.created_at)
|
||||
}
|
||||
},*/
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,14 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full min-h-0">
|
||||
<BasicMainCard>
|
||||
<div class="flex flex-col md:flex-row md:h-full">
|
||||
<!-- {{ menu }}-->
|
||||
<BasicMenu width="11rem" :menu="menu" mode="horizontal" class="md:hidden overflow-auto" />
|
||||
<div class="flex h-full min-h-0 min-w-0 flex-col md:flex-row">
|
||||
<BasicMenu width="11rem" :menu="menu" mode="horizontal" class="shrink-0 overflow-auto md:hidden" />
|
||||
<BasicMenu
|
||||
width="13rem"
|
||||
:menu="menu"
|
||||
class="hidden md:inline overflow-auto min-w-[13.5rem]"
|
||||
class="hidden min-w-[13.5rem] shrink-0 overflow-auto md:inline"
|
||||
/>
|
||||
<!-- <component :is="selectedComponent" class="mr-5" />-->
|
||||
<RouterView class="md:mr-5 md:mt-0 mt-5 w-full"></RouterView>
|
||||
<RouterView class="mt-5 min-h-0 min-w-0 w-full flex-1 overflow-hidden md:mr-5 md:mt-0" />
|
||||
</div>
|
||||
</BasicMainCard>
|
||||
</div>
|
||||
@@ -20,21 +18,9 @@
|
||||
import BasicMenu from '@/components/BasicMenu/index.vue'
|
||||
import { useRouterStore } from '@core/stores/router'
|
||||
import { get } from 'lodash'
|
||||
//import Nature from '@/pages/menuPages/setting/limitedOrder/index.vue'
|
||||
//s import test from './limitedOrder'
|
||||
//import Budget from '@/pages/menuPages/setting/budget/index.vue'
|
||||
//import ProjectType from '@/pages/menuPages/setting/projectType/index.vue'
|
||||
//import DocumentType from '@/pages/menuPages/setting/documentType/index.vue'
|
||||
// import Management from '@/pages/menuPages/setting/management/index.vue'
|
||||
//import Accessibility from '@/pages/menuPages/setting/accessibility/index.vue'
|
||||
//import SubmitProcess from '@/pages/menuPages/setting/submitProcess/index.vue'
|
||||
//import documentGroup from '@/pages/menuPages/setting/documentGroup/index.vue'
|
||||
//import userType from '@/pages/menuPages/setting/userType/index.vue'
|
||||
|
||||
const routerStore = useRouterStore()
|
||||
const menu = computed(() => {
|
||||
const item = routerStore.routes.find((item) => item.name === 'products')
|
||||
//console.log('aaaa', get(item, 'children[0].children', []))
|
||||
const item = routerStore.routes.find((item) => item.name === 'products')
|
||||
return get(item, 'children[0].children', [])
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<div>
|
||||
<div
|
||||
:style="{ backgroundImage: `url(${bg1})` }"
|
||||
class="w-[80vw] h-full bg-cover flex items-center justify-center"
|
||||
class="flex min-h-[50vh] w-full items-center justify-center bg-cover px-4 py-6 md:h-full md:w-[80vw] md:px-0 md:py-0"
|
||||
>
|
||||
<div class="shadow-2xl h-[25vh] w-1/3 rounded-lg p-10 bg-white">
|
||||
<div class="w-full max-w-md rounded-lg bg-white p-4 shadow-2xl md:h-[25vh] md:w-1/3 md:p-10">
|
||||
<a-form :model="form" ref="formRef" autocomplete="off" @finish="ok">
|
||||
<div class="gap-4">
|
||||
<a-form-item label="سقف واریز اولیه (ریال)" name="value" :rules="inputRules()">
|
||||
@@ -16,7 +16,7 @@
|
||||
</a-form-item>-->
|
||||
<a-form-item>
|
||||
<BaseButton
|
||||
class="mt-10 w-full"
|
||||
class="mt-4 w-full md:mt-10"
|
||||
:disabled="loading"
|
||||
type="primary"
|
||||
size="middle"
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
<template>
|
||||
<a-form :model="form" ref="formRef" autocomplete="off">
|
||||
<div class="space-y-3 md:grid md:grid-cols-4 md:gap-2 md:space-y-0">
|
||||
<a-form-item label="انتخاب گروه">
|
||||
<BasicSelect
|
||||
v-model:value="selectedGroup"
|
||||
:items="groupItems"
|
||||
valueName="name"
|
||||
keyName="function"
|
||||
mode="multiple"
|
||||
class="w-full"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="شماره های تکی" name="numbers" class="md:col-span-3">
|
||||
<BasicSelect
|
||||
v-model:value="form.single"
|
||||
mode="tags"
|
||||
valueName="name"
|
||||
keyName="mobile"
|
||||
type="tel"
|
||||
noData="لطفا شماره موبایل را وارد کنید و سپس روی کیبورد خود کلید تایید را بزنید"
|
||||
class="w-full sms-selector"
|
||||
>
|
||||
<template #notFoundContent>
|
||||
<a-spin v-if="loading" size="small" />
|
||||
<div v-else class="w-full text-center font-bold">لطفا شماره را وارد کنید</div>
|
||||
</template>
|
||||
</BasicSelect>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="انتخاب کاربر" class="md:col-span-4 mb-0">
|
||||
<BasicInput
|
||||
place-holder="جستجو"
|
||||
class="mb-2"
|
||||
@onchange="filterNumbers"
|
||||
:disabled="selectedGroup.length === 0"
|
||||
/>
|
||||
|
||||
<a-table
|
||||
class="hidden md:block"
|
||||
rowKey="mobile"
|
||||
:row-selection="{ selectedRowKeys: form.numbers, onChange: onSelectChange }"
|
||||
:columns="columns"
|
||||
:scroll="{ y: '20vh', x: 500 }"
|
||||
:pagination="false"
|
||||
:data-source="groupNumbers"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="md:hidden max-h-48 overflow-y-auto rounded-xl border border-gray-200 bg-gray-50 p-2 space-y-1"
|
||||
>
|
||||
<div v-if="customersLoading" class="flex justify-center py-6">
|
||||
<a-spin size="small" />
|
||||
</div>
|
||||
<div v-else-if="!groupNumbers.length" class="py-6 text-center text-sm text-gray-500">
|
||||
کاربری یافت نشد
|
||||
</div>
|
||||
<label
|
||||
v-for="customer in groupNumbers"
|
||||
:key="customer.mobile"
|
||||
class="flex cursor-pointer items-center gap-2 rounded-lg border border-transparent bg-white px-3 py-2 active:bg-gray-100"
|
||||
:class="isSelected(customer.mobile) ? 'border-blue-200 bg-blue-50' : ''"
|
||||
>
|
||||
<a-checkbox
|
||||
:checked="isSelected(customer.mobile)"
|
||||
@change="(e) => toggleCustomer(customer.mobile, e.target.checked)"
|
||||
@click.stop
|
||||
/>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="truncate text-sm font-600 text-gray-900">{{ customer.name || '-' }}</div>
|
||||
<div class="truncate text-xs text-gray-500">{{ customer.mobile }}</div>
|
||||
</div>
|
||||
<span class="shrink-0 text-xs text-gray-400">#{{ customer.id }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="متن پیام" name="message" class="md:col-span-4 mb-0">
|
||||
<BasicTextArea v-model:value="form.message" />
|
||||
<div class="mt-2 space-y-2 md:flex md:items-center md:justify-between md:flex-row-reverse md:space-y-0">
|
||||
<BaseButton type="primary" class="w-full md:w-10rem" @click="addName">افزودن نام کاربر</BaseButton>
|
||||
<p class="text-xs leading-5 text-gray-500 md:max-w-[70%]">
|
||||
برای استفاده از نام اشخاص در پیام از گزینه افزودن نام کاربر استفاده کنید. این متغیر با
|
||||
عنوان (%name) به متن پیام اضافه میگردد.
|
||||
</p>
|
||||
</div>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormInstance } from 'ant-design-vue'
|
||||
import type { FunctionItem } from '@/model/Item'
|
||||
import type { Customer } from '@/model/customer'
|
||||
import type { Sms } from '@/model/sms'
|
||||
import { getCustomerList } from '@/api/sms'
|
||||
|
||||
const props = defineProps<{
|
||||
form: Sms
|
||||
groupItems?: FunctionItem[]
|
||||
loading?: boolean
|
||||
}>()
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const selectedGroup = ref<string[]>([])
|
||||
const groupNumbers = ref<Customer[]>([])
|
||||
const customersLoading = ref(false)
|
||||
let baseGroupNumbers: Customer[] = []
|
||||
|
||||
const columns = [
|
||||
{ title: 'کد کاربر', dataIndex: 'id' },
|
||||
{ title: 'نام و نام خانوادگی', dataIndex: 'name' },
|
||||
{ title: 'شماره همراه', dataIndex: 'mobile' }
|
||||
]
|
||||
|
||||
function addName() {
|
||||
props.form.message = (props.form.message ?? '').concat('', '%name')
|
||||
}
|
||||
|
||||
function filterNumbers(val: string) {
|
||||
if (val !== '') {
|
||||
groupNumbers.value = baseGroupNumbers.filter((v) => v.name?.includes(val))
|
||||
} else {
|
||||
groupNumbers.value = baseGroupNumbers
|
||||
}
|
||||
}
|
||||
|
||||
async function loadGroupNumbers(val: string[]) {
|
||||
if (!val.length) {
|
||||
groupNumbers.value = []
|
||||
baseGroupNumbers = []
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
customersLoading.value = true
|
||||
const { customers } = await getCustomerList(val)
|
||||
groupNumbers.value = customers
|
||||
baseGroupNumbers = customers
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
customersLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function onSelectChange(changableRowKeys: string[]) {
|
||||
props.form.numbers = changableRowKeys
|
||||
}
|
||||
|
||||
function isSelected(mobile?: string) {
|
||||
if (!mobile) return false
|
||||
return props.form.numbers?.includes(mobile) ?? false
|
||||
}
|
||||
|
||||
function toggleCustomer(mobile: string | undefined, checked: boolean) {
|
||||
if (!mobile) return
|
||||
const numbers = new Set(props.form.numbers ?? [])
|
||||
if (checked) numbers.add(mobile)
|
||||
else numbers.delete(mobile)
|
||||
props.form.numbers = Array.from(numbers)
|
||||
}
|
||||
|
||||
watch(selectedGroup, (val) => {
|
||||
loadGroupNumbers(val ?? [])
|
||||
})
|
||||
|
||||
function resetAll() {
|
||||
formRef.value?.resetFields()
|
||||
selectedGroup.value = []
|
||||
groupNumbers.value = []
|
||||
baseGroupNumbers = []
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
validateFields: () => formRef.value?.validateFields(),
|
||||
resetAll
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sms-selector :deep(.ant-select-selector) {
|
||||
max-height: 5vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.sms-selector :deep(.ant-select-selector) {
|
||||
max-height: 12vh;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<MobileCardList
|
||||
v-model:search="search"
|
||||
:loading="loading"
|
||||
:has-more="hasMore"
|
||||
:show-empty="!loading && items.length === 0"
|
||||
empty-text="پیامکی یافت نشد"
|
||||
search-placeholder="جستجو پیامک"
|
||||
show-refresh
|
||||
@search="reload"
|
||||
@refresh="reload"
|
||||
@load-more="loadMore"
|
||||
>
|
||||
<SmsMobileListItem
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
:hide-recipient="!!mobile"
|
||||
@open="openSheet"
|
||||
/>
|
||||
</MobileCardList>
|
||||
|
||||
<SmsMobileDetailSheet v-model:visible="sheetVisible" :item="selectedItem" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getSMSTable } from '@/api/sms'
|
||||
import type { SmsRecord } from '@/model/sms'
|
||||
import MobileCardList from '@/components/mobile/MobileCardList.vue'
|
||||
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
||||
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
||||
import type { TableInput } from '@core/type/baseTable'
|
||||
import SmsMobileDetailSheet from './SmsMobileDetailSheet.vue'
|
||||
import SmsMobileListItem from './SmsMobileListItem.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
mobile?: number | string
|
||||
}>()
|
||||
|
||||
const { sheetVisible, selectedItem, openSheet, syncSelectedItem } = useMobileDetailSheet<SmsRecord>()
|
||||
|
||||
function fetchSms(params: TableInput) {
|
||||
if (props.mobile) {
|
||||
params.filters = params.filters ?? []
|
||||
params.filters.push({
|
||||
field: 'receiver',
|
||||
type: 'text',
|
||||
value: props.mobile,
|
||||
cr: 'and',
|
||||
op: 'eq',
|
||||
hidden: false
|
||||
})
|
||||
}
|
||||
return getSMSTable(params)
|
||||
}
|
||||
|
||||
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<SmsRecord>({
|
||||
fetchApi: fetchSms,
|
||||
onFetched: (rows) => syncSelectedItem(rows)
|
||||
})
|
||||
|
||||
onMounted(() => reload())
|
||||
|
||||
defineExpose({ fetch, openSheet })
|
||||
</script>
|
||||
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<MobileDetailSheet v-model:visible="visibleProxy" :item="item" :title="sheetTitle">
|
||||
<template #default="{ item: row }">
|
||||
<MobileSheetHeader :title="sheetTitle" :subtitle="dateLabel(row)" :show-title="isMobile" />
|
||||
|
||||
<MobileInfoGrid>
|
||||
<MobileDetailRow label="کد" :value="row.id ?? '-'" />
|
||||
<MobileDetailRow label="شماره تلفن" :value="phoneLabel(row)" />
|
||||
<MobileDetailRow label="نام دریافت کننده" full-width :value="recipientName(row)" />
|
||||
<MobileDetailRow label="تاریخ و ساعت" full-width :value="dateLabel(row)" />
|
||||
<MobileDetailRow label="پیام" full-width :value="row.sms || '-'" />
|
||||
</MobileInfoGrid>
|
||||
</template>
|
||||
</MobileDetailSheet>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { SmsRecord } from '@/model/sms'
|
||||
import MobileDetailRow from '@/components/mobile/MobileDetailRow.vue'
|
||||
import MobileDetailSheet from '@/components/mobile/MobileDetailSheet.vue'
|
||||
import MobileInfoGrid from '@/components/mobile/MobileInfoGrid.vue'
|
||||
import MobileSheetHeader from '@/components/mobile/MobileSheetHeader.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import { toJalali } from '@core/utils/useDataTime'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
item: SmsRecord | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
}>()
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
set: (value: boolean) => emit('update:visible', value)
|
||||
})
|
||||
|
||||
const sheetTitle = computed(() => recipientName(props.item) || `پیامک #${props.item?.id ?? '-'}`)
|
||||
|
||||
function recipientName(row: SmsRecord | null) {
|
||||
if (!row) return '-'
|
||||
if (row.person?.name) return row.person.name
|
||||
return row.person?.mobile || '-'
|
||||
}
|
||||
|
||||
function phoneLabel(row: SmsRecord) {
|
||||
return row.person?.mobile || row.receiver || '-'
|
||||
}
|
||||
|
||||
function dateLabel(row: SmsRecord) {
|
||||
return row.created_at ? toJalali(row.created_at) : '-'
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<MobileCard clickable @click="$emit('open', item)">
|
||||
<div class="flex items-start justify-between gap-2">
|
||||
<span class="min-w-0 truncate text-sm font-700 text-gray-900">{{ recipientName }}</span>
|
||||
<div class="shrink-0 space-y-0.5 text-left">
|
||||
<div class="text-xs text-gray-400">{{ dateLabel }}</div>
|
||||
<div v-if="!hideRecipient" class="text-xs text-gray-600">{{ phoneLabel }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</MobileCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { SmsRecord } from '@/model/sms'
|
||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||
import { toJalali } from '@core/utils/useDataTime'
|
||||
|
||||
const props = defineProps<{
|
||||
item: SmsRecord
|
||||
hideRecipient?: boolean
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'open', item: SmsRecord): void
|
||||
}>()
|
||||
|
||||
const recipientName = computed(() => {
|
||||
if (props.item.person?.name) return props.item.person.name
|
||||
return props.item.person?.mobile || '-'
|
||||
})
|
||||
|
||||
const phoneLabel = computed(() => props.item.person?.mobile || props.item.receiver || '-')
|
||||
const dateLabel = computed(() => (props.item.created_at ? toJalali(props.item.created_at) : '-'))
|
||||
</script>
|
||||
@@ -1,11 +1,19 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||
<BasicShowContext title="لیست پیامک ها">
|
||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template v-if="!mobile" #extra>
|
||||
<BaseButton type="primary" size="small" class="flex shrink-0 md:hidden" @click="emits('add')">
|
||||
<div>ارسال پیامک</div>
|
||||
<BaseIcon icon="vuesax-linear:sms" Size="16px" />
|
||||
</BaseButton>
|
||||
</template>
|
||||
|
||||
<div class="flex h-full min-h-0 flex-col">
|
||||
<BaseTable v-if="!isMobileScreen" class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #extraHeader>
|
||||
<BaseButton v-if="!mobile" type="primary" size="small" class="flex" @click="emits('add')">
|
||||
<div>ارسال پیامک</div>
|
||||
<BaseIcon icon="vuesax-linear:sms" Size="16px"/>
|
||||
<BaseIcon icon="vuesax-linear:sms" Size="16px" />
|
||||
</BaseButton>
|
||||
</template>
|
||||
<template #action="{ column, record }">
|
||||
@@ -19,42 +27,30 @@
|
||||
</div>-->
|
||||
</template>
|
||||
</BaseTable>
|
||||
|
||||
<SmsMobileCardList v-else ref="mobileListRef" class="min-h-0 flex-1" :mobile="mobile" />
|
||||
</div>
|
||||
</BasicShowContext>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="tsx" setup>
|
||||
import type {TableInput} from '@core/type/baseTable'
|
||||
import type { TableInput } from '@core/type/baseTable'
|
||||
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||
import {changeCustomerActivation, getCustomerTable} from '@/api/customer'
|
||||
import {cols} from '../hooks/smsColumns'
|
||||
import {useUserStore} from '@/store/userStore'
|
||||
import {usePrefix} from '@core/composable/usePrefix'
|
||||
import {message} from 'ant-design-vue'
|
||||
import {getOrderTable} from '@/api/order'
|
||||
import {getPriceTable} from '@/api/price'
|
||||
import {getSMSTable} from '@/api/sms'
|
||||
|
||||
const [submit, reload] = useBaseTable()
|
||||
import { cols } from '../hooks/smsColumns'
|
||||
import { getSMSTable } from '@/api/sms'
|
||||
import SmsMobileCardList from './SmsMobileCardList.vue'
|
||||
import { isMobile as isMobileScreen } from '@core/utils/is'
|
||||
|
||||
interface Prop {
|
||||
mobile: number
|
||||
mobile?: number | string
|
||||
}
|
||||
|
||||
const props = defineProps<Prop>()
|
||||
const emits = defineEmits(['delete', 'edit', 'add', 'info'])
|
||||
|
||||
// const { prefixCls } = usePrefix('pophoverstatus')
|
||||
|
||||
//permission
|
||||
/*function checkPermission(temp: string) {
|
||||
const store = useUserStore()
|
||||
return store.canAccess(temp)
|
||||
}*/
|
||||
|
||||
//define table
|
||||
const table = ref(null)
|
||||
const [submit, reload] = useBaseTable()
|
||||
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||
|
||||
const tblProps = ref<any>({
|
||||
columns: cols,
|
||||
@@ -62,52 +58,24 @@ const tblProps = ref<any>({
|
||||
if (props.mobile) {
|
||||
params.filters = []
|
||||
params.filters?.push({
|
||||
field: "receiver",
|
||||
type: "text",
|
||||
field: 'receiver',
|
||||
type: 'text',
|
||||
value: props.mobile,
|
||||
cr: "and",
|
||||
op: "eq",
|
||||
cr: 'and',
|
||||
op: 'eq',
|
||||
hidden: false
|
||||
})
|
||||
}
|
||||
|
||||
return getSMSTable(params)
|
||||
}
|
||||
//bordered: true
|
||||
})
|
||||
// const loading = ref<boolean>(false)
|
||||
|
||||
const fetch = () => {
|
||||
// console.log('sss')
|
||||
reload()
|
||||
async function fetch() {
|
||||
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||
}
|
||||
// const spinTable = (val: boolean) => {
|
||||
// // console.log('sss')
|
||||
// table?.value?.loading.value = val
|
||||
// }
|
||||
|
||||
defineExpose({
|
||||
fetch
|
||||
// spinTable
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@prefix: ~'@{prefixCls}-pophoverstatus';
|
||||
|
||||
.@{prefix} {
|
||||
.ant-menu-rtl.ant-menu-inline,
|
||||
.ant-menu-rtl.ant-menu-vertical {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.ant-menu-item-only-child {
|
||||
border-radius: @border-radius-base;
|
||||
}
|
||||
|
||||
//bor
|
||||
/* .ant-input-group-addon:first-child {
|
||||
!* border: #d9d9d9 1px solid;
|
||||
border-radius: 0 6px 6px 0;*!
|
||||
}*/
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,117 +6,54 @@
|
||||
</div>
|
||||
</BasicMainCard>
|
||||
|
||||
<BasicModal
|
||||
v-model:visible="priceConfirmVisible"
|
||||
width="25rem"
|
||||
<BasicActionSheet
|
||||
v-model:visible="addSMSVisible"
|
||||
title="ارسال پیامک"
|
||||
modal-width="80vw"
|
||||
drawer-height="90vh"
|
||||
:loading="loading"
|
||||
:showButton="true"
|
||||
:showCancel="true"
|
||||
:showOk="true"
|
||||
ok-text="ارسال"
|
||||
cancel-text="انصراف"
|
||||
@ok="ok"
|
||||
:confirm-loading="loading"
|
||||
@close="priceConfirmVisible = false"
|
||||
@close="closeSmsSheet"
|
||||
@cancel="closeSmsSheet"
|
||||
>
|
||||
<div class="flex items-center gap-2 font-bold">
|
||||
<BaseIcon icon="vuesax-linear:info-circle" color="orange" />
|
||||
<div>آیا مطمئن به تغییر قیمت می باشید؟</div>
|
||||
</div>
|
||||
</BasicModal>
|
||||
<div v-if="isMobile" class="mb-3 text-base font-700">ارسال پیامک</div>
|
||||
<SmsEditorFormBody ref="smsEditorRef" :form="form" :group-items="groupItems" :loading="loading" />
|
||||
</BasicActionSheet>
|
||||
</div>
|
||||
<BasicModal
|
||||
v-model:visible="addSMSVisible"
|
||||
width="80vw"
|
||||
title=""
|
||||
@ok="ok"
|
||||
:loading="loading"
|
||||
@close="addSMSVisible = false"
|
||||
>
|
||||
<!-- <a-tabs v-model:activeKey="activeKey">
|
||||
<a-tab-pane :key="1" tab="ارسال تکی" />
|
||||
<a-tab-pane :key="2" tab="ارسال گروهی" />
|
||||
</a-tabs>-->
|
||||
<a-form :model="form" ref="formRef" autocomplete="off" class="md:grid md:grid-cols-4 gap-2">
|
||||
<a-form-item label="انتخاب گروه">
|
||||
<BasicSelect
|
||||
v-model:value="selectedGroup"
|
||||
:items="groupItems"
|
||||
valueName="name"
|
||||
keyName="function"
|
||||
mode="multiple"
|
||||
class="w-full"
|
||||
@change="getGroupNumbers"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="شماره های تکی" name="numbers" class="md:col-span-3">
|
||||
<BasicSelect
|
||||
v-model:value="form.single"
|
||||
mode="tags"
|
||||
valueName="name"
|
||||
keyName="mobile"
|
||||
type="tel"
|
||||
noData="لطفا شماره موبایل را وارد کنید و سپس روی کیبورد خود کلید تایید را بزنید"
|
||||
class="w-full smsSelector"
|
||||
>
|
||||
<template #notFoundContent>
|
||||
<a-spin v-if="loading" size="small" />
|
||||
<div v-else class="w-full text-center font-bold">لطفا شماره را وارد کنید</div>
|
||||
</template>
|
||||
</BasicSelect>
|
||||
</a-form-item>
|
||||
<a-form-item label="انتخاب کاربر" class="md:col-span-4">
|
||||
<BasicInput
|
||||
place-holder="جستجو"
|
||||
class="mb-2"
|
||||
@onchange="filterNumbers"
|
||||
:disabled="selectedGroup.length === 0"
|
||||
/>
|
||||
<a-table
|
||||
rowKey="mobile"
|
||||
:row-selection="{ selectedRowKeys: form.numbers, onChange: onSelectChange }"
|
||||
:columns="columns"
|
||||
:scroll="{ y: '20vh', x: 500 }"
|
||||
:pagination="false"
|
||||
:data-source="groupNumbers"
|
||||
>
|
||||
</a-table>
|
||||
</a-form-item>
|
||||
<a-form-item label="متن پیام" name="message" class="md:col-span-4">
|
||||
<BasicTextArea v-model:value="form.message" />
|
||||
<div class="md:flex items-center justify-between mt-2 md:flex-row-reverse">
|
||||
<BaseButton @click="addName()" type="primary" class="w-full md:w-10rem"
|
||||
>افزودن نام کاربر</BaseButton
|
||||
>
|
||||
<div class="text-gray-500">
|
||||
برای استفاده از نام اشخاص در پیام از گزینه افزودن نام کاربر استفاده کنید این متغیر با
|
||||
عنوان( name% )به متن پیام اضافه میگردد
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item class="col-span-4">
|
||||
<!-- <NumbersTable
|
||||
v-if="selectedGroup.length > 0"
|
||||
type="check"
|
||||
:items="form.numbers"
|
||||
:filter="selectedGroup"
|
||||
/>-->
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// import BasicInfoModal from '@/components/BasicInfoModal'
|
||||
import { ref } from 'vue'
|
||||
import tbl from './components/table.vue'
|
||||
import type { FormInstance } from 'ant-design-vue'
|
||||
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
||||
import SmsEditorFormBody from './components/SmsEditorFormBody.vue'
|
||||
import { message } from 'ant-design-vue/es'
|
||||
import type { FunctionItem } from '@/model/Item'
|
||||
import type { Customer } from '@/model/customer'
|
||||
import { createSMS, getCustomerList, getGroupsList } from '@/api/sms'
|
||||
import { createSMS, getGroupsList } from '@/api/sms'
|
||||
import type { Sms } from '@/model/sms'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
//loading
|
||||
const loading = ref<boolean>(false)
|
||||
const selectedGroup = ref<string[]>([])
|
||||
const groupItems = ref<FunctionItem[]>()
|
||||
const groupNumbers = ref<Customer[]>()
|
||||
let baseGroupNumbers: Customer[] = []
|
||||
const table = ref<null | { fetch: () => void }>(null)
|
||||
const smsEditorRef = ref<{
|
||||
validateFields?: () => ReturnType<NonNullable<FormInstance['validateFields']>>
|
||||
resetAll?: () => void
|
||||
}>()
|
||||
|
||||
const form = ref<Sms>({
|
||||
message: '',
|
||||
numbers: [],
|
||||
single: []
|
||||
})
|
||||
|
||||
const addSMSVisible = ref<boolean>(false)
|
||||
|
||||
onMounted(() => getGroups())
|
||||
|
||||
@@ -125,160 +62,50 @@ async function getGroups() {
|
||||
loading.value = true
|
||||
groupItems.value = await getGroupsList()
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
console.error(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const addName = () => {
|
||||
form.value.message = form.value.message.concat('', '%name')
|
||||
function refreshTable() {
|
||||
table.value?.fetch()
|
||||
}
|
||||
|
||||
function filterNumbers(val: string) {
|
||||
// console.log(val)
|
||||
if (val !== '') {
|
||||
groupNumbers.value = baseGroupNumbers?.filter((v) => v.name.includes(val))
|
||||
} else groupNumbers.value = baseGroupNumbers
|
||||
function closeSmsSheet() {
|
||||
addSMSVisible.value = false
|
||||
}
|
||||
|
||||
// const refreshTable = () => table?.value?.fetch()
|
||||
|
||||
async function getGroupNumbers(val: string[]) {
|
||||
if (val) {
|
||||
try {
|
||||
loading.value = true
|
||||
const { customers } = await getCustomerList(val)
|
||||
groupNumbers.value = customers
|
||||
baseGroupNumbers = customers
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(selectedGroup, (val) => {
|
||||
console.log(val)
|
||||
if (val) {
|
||||
getGroupNumbers(val)
|
||||
}
|
||||
})
|
||||
|
||||
const table = ref(null)
|
||||
const refreshTable = () => table?.value?.fetch()
|
||||
const activeKey = ref(1)
|
||||
|
||||
let updateKey = ref<number>(0)
|
||||
|
||||
/*
|
||||
function onselected(val) {
|
||||
console.log()
|
||||
}
|
||||
*/
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'کد کاربر',
|
||||
dataIndex: 'id'
|
||||
},
|
||||
{
|
||||
title: 'نام و نام خانوادگی',
|
||||
dataIndex: 'name'
|
||||
},
|
||||
{
|
||||
title: 'شماره همراه',
|
||||
dataIndex: 'mobile'
|
||||
}
|
||||
]
|
||||
const onSelectChange = (changableRowKeys: string[]) => {
|
||||
console.log('selectedRowKeys changed: ', changableRowKeys)
|
||||
// if (changableRowKeys !== undefined) {
|
||||
form.value.numbers = changableRowKeys
|
||||
// }
|
||||
}
|
||||
//role confirm items
|
||||
|
||||
//form
|
||||
const form = ref<Sms>({
|
||||
message: '',
|
||||
numbers: [],
|
||||
single: []
|
||||
})
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
//edit
|
||||
let editId: number | undefined = undefined
|
||||
|
||||
//add
|
||||
async function add() {
|
||||
loading.value = true
|
||||
editId = undefined
|
||||
// await store.getUsersList()
|
||||
addSMSVisible.value = true
|
||||
updateKey.value++
|
||||
resetData()
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
//reset data
|
||||
const resetData = () => {
|
||||
formRef.value?.resetFields()
|
||||
function resetData() {
|
||||
form.value = {
|
||||
message: '',
|
||||
numbers: [],
|
||||
single: []
|
||||
}
|
||||
groupNumbers.value = []
|
||||
selectedGroup.value = []
|
||||
smsEditorRef.value?.resetAll?.()
|
||||
}
|
||||
//add project modal
|
||||
const addSMSVisible = ref<boolean>(false)
|
||||
const priceConfirmVisible = ref<boolean>(false)
|
||||
|
||||
//submit user
|
||||
function ok() {
|
||||
formRef.value?.validateFields().then(async () => {
|
||||
smsEditorRef.value?.validateFields?.().then(async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
if (!form.value.numbers) form.value.numbers = []
|
||||
form.value.numbers?.push(...(form.value.single ?? []))
|
||||
// console.log(form.value.numbers)
|
||||
// delete form.value.single
|
||||
form.value.numbers.push(...(form.value.single ?? []))
|
||||
await createSMS(form.value)
|
||||
// form.value.single = []
|
||||
resetData()
|
||||
addSMSVisible.value = false
|
||||
refreshTable()
|
||||
message.success('با موفقیت ارسال شد')
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
console.error(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/*async function ok() {
|
||||
loading.value = true
|
||||
try {
|
||||
await createPrice(form.value)
|
||||
refreshTable()
|
||||
priceConfirmVisible.value = false
|
||||
addSMSVisible.value = false
|
||||
// await confirmActOnApi('آیا از اعمال قیمت جدید اطمینان دارید؟', form.value, createPrice)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
// message.error('خطا در بارگذاری اطلاعات')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}*/
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.smsSelector .ant-select-selector {
|
||||
max-height: 5vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<MobileCardList
|
||||
v-model:search="search"
|
||||
:loading="loading"
|
||||
:has-more="hasMore"
|
||||
:show-empty="!loading && items.length === 0"
|
||||
empty-text="گزارشی یافت نشد"
|
||||
search-placeholder="جستجو گزارش"
|
||||
show-refresh
|
||||
@search="reload"
|
||||
@refresh="reload"
|
||||
@load-more="loadMore"
|
||||
>
|
||||
<TransactionMobileListItem
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
:hide-customer="!!customerId"
|
||||
@open="openSheet"
|
||||
/>
|
||||
</MobileCardList>
|
||||
|
||||
<TransactionMobileDetailSheet v-model:visible="sheetVisible" :item="selectedItem" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getTransactionTable } from '@/api/transaction'
|
||||
import type { Transaction } from '@/model/transaction'
|
||||
import MobileCardList from '@/components/mobile/MobileCardList.vue'
|
||||
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
||||
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
||||
import type { TableInput } from '@core/type/baseTable'
|
||||
import TransactionMobileDetailSheet from './TransactionMobileDetailSheet.vue'
|
||||
import TransactionMobileListItem from './TransactionMobileListItem.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
customerId?: number
|
||||
}>()
|
||||
|
||||
const { sheetVisible, selectedItem, openSheet, syncSelectedItem } = useMobileDetailSheet<Transaction>()
|
||||
|
||||
function fetchTransactions(params: TableInput) {
|
||||
if (props.customerId) {
|
||||
params.filters = params.filters ?? []
|
||||
params.filters.push({
|
||||
field: 'customer_id',
|
||||
type: 'text',
|
||||
value: props.customerId,
|
||||
cr: 'and',
|
||||
op: 'eq',
|
||||
hidden: false
|
||||
})
|
||||
}
|
||||
return getTransactionTable(params)
|
||||
}
|
||||
|
||||
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<Transaction>({
|
||||
fetchApi: fetchTransactions,
|
||||
onFetched: (rows) => syncSelectedItem(rows)
|
||||
})
|
||||
|
||||
onMounted(() => reload())
|
||||
|
||||
defineExpose({ fetch, openSheet })
|
||||
</script>
|
||||
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<MobileDetailSheet v-model:visible="visibleProxy" :item="item" :title="sheetTitle">
|
||||
<template #default="{ item: row }">
|
||||
<MobileSheetHeader :title="sheetTitle" :subtitle="dateLabel(row)" :show-title="isMobile" />
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="rounded-lg border px-2 py-0.5 text-xs font-600" :class="typeTagClass(row)">
|
||||
{{ typeLabel(row) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<MobileStatCard
|
||||
bordered
|
||||
:variant="(row.gold ?? 0) > 0 ? 'success' : 'danger'"
|
||||
label="مقدار طلا (گرم)"
|
||||
:value="goldLabel(row)"
|
||||
>
|
||||
<template #prefix>
|
||||
<BaseIcon icon="vuesax-linear:coin" size="18" :color="(row.gold ?? 0) > 0 ? '#15803d' : '#dc2626'" />
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
<MobileStatCard
|
||||
bordered
|
||||
:variant="(row.cash ?? 0) > 0 ? 'success' : 'danger'"
|
||||
label="مقدار پول (ریال)"
|
||||
:value="usePrice(row.cash ?? 0, true, '')"
|
||||
>
|
||||
<template #prefix>
|
||||
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" :color="(row.cash ?? 0) > 0 ? '#15803d' : '#dc2626'" />
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
</div>
|
||||
|
||||
<MobileInfoGrid>
|
||||
<MobileDetailRow label="کد تراکنش" :value="row.id ?? '-'" />
|
||||
<MobileDetailRow label="نام تراکنش" :value="row.type_str || '-'" />
|
||||
<MobileDetailRow label="علت تراکنش" full-width :value="noteLabel(row)" />
|
||||
<MobileDetailRow label="نام مشتری" full-width :value="row.customer?.name || '-'" />
|
||||
<MobileDetailRow label="تاریخ تراکنش" :value="dateLabel(row)" />
|
||||
</MobileInfoGrid>
|
||||
</template>
|
||||
</MobileDetailSheet>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Transaction } from '@/model/transaction'
|
||||
import MobileDetailRow from '@/components/mobile/MobileDetailRow.vue'
|
||||
import MobileDetailSheet from '@/components/mobile/MobileDetailSheet.vue'
|
||||
import MobileInfoGrid from '@/components/mobile/MobileInfoGrid.vue'
|
||||
import MobileSheetHeader from '@/components/mobile/MobileSheetHeader.vue'
|
||||
import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import { toJalaliNoTime } from '@core/utils/useDataTime'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
item: Transaction | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
}>()
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
set: (value: boolean) => emit('update:visible', value)
|
||||
})
|
||||
|
||||
const sheetTitle = computed(() => props.item?.type_str || `تراکنش #${props.item?.id ?? '-'}`)
|
||||
|
||||
function isMoneyType(row: Transaction) {
|
||||
return row.type === 4 || row.type === 3
|
||||
}
|
||||
|
||||
function typeLabel(row: Transaction) {
|
||||
return isMoneyType(row) ? 'پول' : 'طلا'
|
||||
}
|
||||
|
||||
function typeTagClass(row: Transaction) {
|
||||
return isMoneyType(row)
|
||||
? 'border-green-300 bg-green-50 text-green-700'
|
||||
: 'border-amber-300 bg-amber-50 text-amber-700'
|
||||
}
|
||||
|
||||
function dateLabel(row: Transaction) {
|
||||
return row.created_at ? toJalaliNoTime(row.created_at) : '-'
|
||||
}
|
||||
|
||||
function goldLabel(row: Transaction) {
|
||||
const gold = row.gold
|
||||
if (gold == null) return '-'
|
||||
return `${Number(Number(gold).toFixed(3))} گرم`
|
||||
}
|
||||
|
||||
function noteLabel(row: Transaction) {
|
||||
if (row.note == null) return '-'
|
||||
return String(row.note)
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<MobileCard clickable @click="$emit('open', item)">
|
||||
<div class="space-y-1">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="flex min-w-0 items-center gap-2">
|
||||
<span class="shrink-0 rounded-md border px-1.5 py-0.5 text-xs font-600" :class="typeTagClass">
|
||||
{{ typeLabel }}
|
||||
</span>
|
||||
<span class="truncate text-sm font-700 text-gray-900">{{ item.type_str || '-' }}</span>
|
||||
</div>
|
||||
<span class="shrink-0 text-xs text-gray-400">{{ dateLabel }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="!hideCustomer" class="truncate text-xs text-gray-600">{{ customerName }}</div>
|
||||
</div>
|
||||
</MobileCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Transaction } from '@/model/transaction'
|
||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||
import { toJalaliNoTime } from '@core/utils/useDataTime'
|
||||
|
||||
const props = defineProps<{
|
||||
item: Transaction
|
||||
hideCustomer?: boolean
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'open', item: Transaction): void
|
||||
}>()
|
||||
|
||||
const isMoneyType = computed(() => props.item.type === 4 || props.item.type === 3)
|
||||
const typeLabel = computed(() => (isMoneyType.value ? 'پول' : 'طلا'))
|
||||
const typeTagClass = computed(() =>
|
||||
isMoneyType.value
|
||||
? 'border-green-300 bg-green-50 text-green-700'
|
||||
: 'border-amber-300 bg-amber-50 text-amber-700'
|
||||
)
|
||||
|
||||
const customerName = computed(() => props.item.customer?.name || '-')
|
||||
const dateLabel = computed(() => (props.item.created_at ? toJalaliNoTime(props.item.created_at) : '-'))
|
||||
</script>
|
||||
@@ -1,7 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||
<BasicShowContext title="لیست گزارشات">
|
||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<div class="flex h-full min-h-0 flex-col">
|
||||
<BaseTable v-if="!isMobile" class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<!-- <template #extraHeader>
|
||||
<BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
||||
<div>افزودن کیف پول</div>
|
||||
@@ -49,6 +50,14 @@
|
||||
</div>
|
||||
</template>
|
||||
</BaseTable>
|
||||
|
||||
<TransactionMobileCardList
|
||||
v-else
|
||||
ref="mobileListRef"
|
||||
class="min-h-0 flex-1"
|
||||
:customer-id="customerId"
|
||||
/>
|
||||
</div>
|
||||
</BasicShowContext>
|
||||
</div>
|
||||
</template>
|
||||
@@ -63,17 +72,18 @@ import {useUserStore} from '@/store/userStore'
|
||||
import {usePrefix} from '@core/composable/usePrefix'
|
||||
import {message} from 'ant-design-vue'
|
||||
import {getWalletTable} from '@/api/wallet'
|
||||
import {getTransactionTable} from '@/api/transaction'
|
||||
import {getSMSTable} from "@/api/sms";
|
||||
|
||||
import { getTransactionTable } from '@/api/transaction'
|
||||
import TransactionMobileCardList from './TransactionMobileCardList.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
interface Prop {
|
||||
customerId: number
|
||||
customerId?: number
|
||||
}
|
||||
|
||||
const props = defineProps<Prop>()
|
||||
|
||||
const [submit, reload] = useBaseTable()
|
||||
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||
/*const select = (val) => {
|
||||
val.sts === 1 ? return ['1'] : ['2']
|
||||
}*/
|
||||
@@ -124,9 +134,8 @@ const loading = ref<boolean>(false)
|
||||
}
|
||||
}*/
|
||||
|
||||
const fetch = () => {
|
||||
// console.log('sss')
|
||||
reload()
|
||||
async function fetch() {
|
||||
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||
}
|
||||
// const spinTable = (val: boolean) => {
|
||||
// // console.log('sss')
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<MobileCardList
|
||||
v-model:search="search"
|
||||
:loading="loading"
|
||||
:has-more="hasMore"
|
||||
:show-empty="!loading && items.length === 0"
|
||||
empty-text="مدیری یافت نشد"
|
||||
search-placeholder="جستجو مدیر"
|
||||
show-refresh
|
||||
@search="reload"
|
||||
@refresh="reload"
|
||||
@load-more="loadMore"
|
||||
>
|
||||
<ManagerMobileListItem
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
@open="openSheet"
|
||||
@edit="(row) => emit('edit', row)"
|
||||
/>
|
||||
</MobileCardList>
|
||||
|
||||
<ManagerMobileDetailSheet
|
||||
v-model:visible="sheetVisible"
|
||||
:item="selectedItem"
|
||||
@delete="(id) => emit('delete', id)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getUserTable } from '@/api/user'
|
||||
import type { User } from '@/model/user'
|
||||
import MobileCardList from '@/components/mobile/MobileCardList.vue'
|
||||
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
||||
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
||||
import ManagerMobileDetailSheet from './ManagerMobileDetailSheet.vue'
|
||||
import ManagerMobileListItem from './ManagerMobileListItem.vue'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'info', item: User): void
|
||||
(e: 'edit', item: User): void
|
||||
(e: 'delete', id: number): void
|
||||
}>()
|
||||
|
||||
const { sheetVisible, selectedItem, openSheet, syncSelectedItem } = useMobileDetailSheet<User>()
|
||||
|
||||
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<User>({
|
||||
fetchApi: getUserTable,
|
||||
onFetched: (rows) => syncSelectedItem(rows)
|
||||
})
|
||||
|
||||
onMounted(() => reload())
|
||||
|
||||
defineExpose({ fetch, openSheet })
|
||||
</script>
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<MobileDetailSheet v-model:visible="visibleProxy" :item="item" :title="sheetTitle">
|
||||
<template #default="{ item: row }">
|
||||
<MobileSheetHeader :title="sheetTitle" :subtitle="mobileLabel(row)" :show-title="isMobile">
|
||||
<template #title>
|
||||
<div class="flex w-full items-center justify-between gap-3">
|
||||
<span class="min-w-0 truncate">{{ sheetTitle }}</span>
|
||||
<MobileIconButton
|
||||
plain
|
||||
icon="vuesax-linear:user-remove"
|
||||
color="#EB3535"
|
||||
:size="22"
|
||||
@click="row.id && $emit('delete', row.id)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</MobileSheetHeader>
|
||||
|
||||
<MobileInfoGrid>
|
||||
<MobileDetailRow label="کد کاربری" :value="row.id ?? '-'" />
|
||||
<MobileDetailRow label="نام و نام خانوادگی" full-width :value="row.name || '-'" />
|
||||
<MobileDetailRow label="شماره موبایل" full-width :value="mobileLabel(row)" />
|
||||
<MobileDetailRow v-if="row.created_at" label="تاریخ ثبت" full-width :value="createdAtLabel(row)" />
|
||||
</MobileInfoGrid>
|
||||
</template>
|
||||
</MobileDetailSheet>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { User } from '@/model/user'
|
||||
import MobileDetailRow from '@/components/mobile/MobileDetailRow.vue'
|
||||
import MobileDetailSheet from '@/components/mobile/MobileDetailSheet.vue'
|
||||
import MobileIconButton from '@/components/mobile/MobileIconButton.vue'
|
||||
import MobileInfoGrid from '@/components/mobile/MobileInfoGrid.vue'
|
||||
import MobileSheetHeader from '@/components/mobile/MobileSheetHeader.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import { toJalaliNoTime } from '@core/utils/useDataTime'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
item: User | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
(e: 'delete', id: number): void
|
||||
}>()
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
set: (value: boolean) => emit('update:visible', value)
|
||||
})
|
||||
|
||||
const sheetTitle = computed(() => props.item?.name || `مدیر #${props.item?.id ?? '-'}`)
|
||||
|
||||
function mobileLabel(row: User) {
|
||||
if (row.mobile == null || row.mobile === '') return '-'
|
||||
return String(row.mobile)
|
||||
}
|
||||
|
||||
function createdAtLabel(row: User) {
|
||||
return row.created_at ? toJalaliNoTime(row.created_at) : '-'
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<MobileCard clickable @click="$emit('open', item)">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="min-w-0 flex-1 space-y-1">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="truncate text-sm font-700 text-gray-900">{{ item.name || '-' }}</span>
|
||||
<span class="shrink-0 text-xs text-gray-400">#{{ item.id ?? '-' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="truncate text-xs text-gray-600">{{ mobileLabel }}</div>
|
||||
</div>
|
||||
|
||||
<MobileIconButton
|
||||
icon="vuesax-linear:edit"
|
||||
color="#4572E9"
|
||||
:size="20"
|
||||
@click="$emit('edit', item)"
|
||||
/>
|
||||
</div>
|
||||
</MobileCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { User } from '@/model/user'
|
||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||
import MobileIconButton from '@/components/mobile/MobileIconButton.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
item: User
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'open', item: User): void
|
||||
(e: 'edit', item: User): void
|
||||
}>()
|
||||
|
||||
const mobileLabel = computed(() => {
|
||||
if (props.item.mobile == null || props.item.mobile === '') return '-'
|
||||
return String(props.item.mobile)
|
||||
})
|
||||
</script>
|
||||
@@ -1,7 +1,15 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||
<BasicShowContext title="لیست مدیران">
|
||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #extra>
|
||||
<BaseButton type="primary" size="small" class="flex shrink-0 md:hidden" @click="emits('add')">
|
||||
<div>افزودن مدیر</div>
|
||||
<BaseIcon icon="vuesax-linear:user-add" Size="16px" />
|
||||
</BaseButton>
|
||||
</template>
|
||||
|
||||
<div class="flex h-full min-h-0 flex-col">
|
||||
<BaseTable v-if="!isMobile" class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #extraHeader>
|
||||
<BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
||||
<div>افزودن مدیر</div>
|
||||
@@ -10,20 +18,6 @@
|
||||
</template>
|
||||
<template #action="{ column, record }">
|
||||
<div class="gap-2 flex justify-center">
|
||||
<!-- <a-tooltip
|
||||
:key="record.active"
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="changeUserActivationFunction(record.id)"
|
||||
>
|
||||
<template #title>
|
||||
<span>{{ record.active ? 'غیرفعال کردن' : 'فعال کردن' }}</span>
|
||||
</template>
|
||||
<Icon
|
||||
:icon="record.active ? 'vuesax-linear:tick-circle' : 'vuesax-linear:close-circle'"
|
||||
:color="record.active ? '#54AD06' : '#ad0627'"
|
||||
/>
|
||||
</a-tooltip>-->
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('info', record)">
|
||||
<template #title>
|
||||
<span>مشخصات</span>
|
||||
@@ -45,6 +39,16 @@
|
||||
</div>
|
||||
</template>
|
||||
</BaseTable>
|
||||
|
||||
<ManagerMobileCardList
|
||||
v-else
|
||||
ref="mobileListRef"
|
||||
class="min-h-0 flex-1"
|
||||
@info="(row) => emits('info', row)"
|
||||
@edit="(row) => emits('edit', row)"
|
||||
@delete="(id) => emits('delete', id)"
|
||||
/>
|
||||
</div>
|
||||
</BasicShowContext>
|
||||
</div>
|
||||
</template>
|
||||
@@ -52,64 +56,29 @@
|
||||
<script lang="tsx" setup>
|
||||
import type { TableInput } from '@core/type/baseTable'
|
||||
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
||||
import { toJalali, toJalaliNoTime } from '@core/utils/useDataTime'
|
||||
import { message, Tag } from 'ant-design-vue'
|
||||
import CustomTag from '@/components/CustomTag.vue'
|
||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||
import { changeUserActivation, getUserTable } from '@/api/user'
|
||||
import { useUserStore } from '@/store/userStore'
|
||||
import { getUserTable } from '@/api/user'
|
||||
import { cols } from '../hooks/usersColumns'
|
||||
import { changeCustomerActivation } from '@/api/customer'
|
||||
import ManagerMobileCardList from './ManagerMobileCardList.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
const [submit, reload] = useBaseTable()
|
||||
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||
|
||||
//emits
|
||||
const emits = defineEmits(['delete', 'edit', 'add', 'info'])
|
||||
|
||||
//define table
|
||||
const table = ref(null)
|
||||
|
||||
//permission
|
||||
function checkPermission(temp: string) {
|
||||
const store = useUserStore()
|
||||
return store.canAccess(temp)
|
||||
}
|
||||
|
||||
const loading = ref<boolean>(false)
|
||||
|
||||
async function changeUserActivationFunction(id: number) {
|
||||
try {
|
||||
loading.value = true
|
||||
await changeUserActivation(id)
|
||||
reload()
|
||||
message.success('تغییر وضعیت با موفقیت انجام شد')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const tblProps = ref<any>({
|
||||
columns: cols,
|
||||
api: (params: TableInput) => {
|
||||
return getUserTable(params)
|
||||
}
|
||||
//bordered: true
|
||||
})
|
||||
|
||||
const fetch = () => {
|
||||
// console.log('sss')
|
||||
reload()
|
||||
async function fetch() {
|
||||
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||
}
|
||||
// const spinTable = (val: boolean) => {
|
||||
// // console.log('sss')
|
||||
// table?.value?.loading.value = val
|
||||
// }
|
||||
|
||||
defineExpose({
|
||||
fetch
|
||||
// spinTable
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<MobileCardList
|
||||
v-model:search="search"
|
||||
:loading="loading"
|
||||
:has-more="hasMore"
|
||||
:show-empty="!loading && items.length === 0"
|
||||
empty-text="کیف پولی یافت نشد"
|
||||
search-placeholder="جستجو کیف پول"
|
||||
show-refresh
|
||||
@search="reload"
|
||||
@refresh="reload"
|
||||
@load-more="loadMore"
|
||||
>
|
||||
<WalletMobileListItem
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
@open="openSheet"
|
||||
/>
|
||||
</MobileCardList>
|
||||
|
||||
<WalletMobileDetailSheet
|
||||
v-model:visible="sheetVisible"
|
||||
:item="selectedItem"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getWalletTable } from '@/api/wallet'
|
||||
import type { Wallet } from '@/model/wallet'
|
||||
import MobileCardList from '@/components/mobile/MobileCardList.vue'
|
||||
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
||||
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
||||
import type { TableInput, TableOutput } from '@core/type/baseTable'
|
||||
import WalletMobileDetailSheet from './WalletMobileDetailSheet.vue'
|
||||
import WalletMobileListItem from './WalletMobileListItem.vue'
|
||||
|
||||
type WalletTableResponse = TableOutput<Wallet> & {
|
||||
sums?: {
|
||||
cash?: number
|
||||
gold?: number
|
||||
cashToGold?: number
|
||||
goldToCash?: number
|
||||
}
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'sums', sums: WalletTableResponse['sums']): void
|
||||
}>()
|
||||
|
||||
const { sheetVisible, selectedItem, openSheet, syncSelectedItem } = useMobileDetailSheet<Wallet>()
|
||||
|
||||
async function fetchWallet(params: TableInput) {
|
||||
const data = (await getWalletTable(params)) as WalletTableResponse
|
||||
emit('sums', data?.sums)
|
||||
return data
|
||||
}
|
||||
|
||||
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<Wallet>({
|
||||
fetchApi: fetchWallet,
|
||||
onFetched: (rows) => syncSelectedItem(rows)
|
||||
})
|
||||
|
||||
onMounted(() => reload())
|
||||
|
||||
defineExpose({ fetch, openSheet })
|
||||
</script>
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<MobileDetailSheet v-model:visible="visibleProxy" :item="item" :title="sheetTitle">
|
||||
<template #default="{ item: row }">
|
||||
<MobileSheetHeader :title="sheetTitle" :show-title="isMobile" />
|
||||
|
||||
<div class="space-y-2">
|
||||
<MobileStatCard bordered variant="success" label="موجودی ریالی" :value="usePrice(row.cash ?? 0, true, '')">
|
||||
<template #prefix>
|
||||
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" color="#15803d" />
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
<MobileStatCard bordered variant="warning" label="موجودی طلایی" :value="goldLabel(row)">
|
||||
<template #prefix>
|
||||
<BaseIcon icon="vuesax-linear:coin" size="18" color="#b45309" />
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
</div>
|
||||
|
||||
<MobileInfoGrid>
|
||||
<MobileDetailRow label="کد کیف پول" :value="row.id ?? '-'" />
|
||||
<MobileDetailRow label="نام مشتری" :value="row.customer?.name || '-'" />
|
||||
<MobileDetailRow label="شماره موبایل" :value="row.customer?.mobile ?? '-'" />
|
||||
</MobileInfoGrid>
|
||||
</template>
|
||||
</MobileDetailSheet>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Wallet } from '@/model/wallet'
|
||||
import MobileDetailRow from '@/components/mobile/MobileDetailRow.vue'
|
||||
import MobileDetailSheet from '@/components/mobile/MobileDetailSheet.vue'
|
||||
import MobileInfoGrid from '@/components/mobile/MobileInfoGrid.vue'
|
||||
import MobileSheetHeader from '@/components/mobile/MobileSheetHeader.vue'
|
||||
import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
item: Wallet | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
}>()
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
set: (value: boolean) => emit('update:visible', value)
|
||||
})
|
||||
|
||||
const sheetTitle = computed(() => props.item?.customer?.name || `کیف پول #${props.item?.id ?? '-'}`)
|
||||
|
||||
function goldLabel(row: Wallet) {
|
||||
const gold = row.gold
|
||||
if (gold == null) return '-'
|
||||
return `${Number(Number(gold).toFixed(3))} گرم`
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<MobileCard clickable @click="$emit('open', item)">
|
||||
<div class="space-y-1">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="min-w-0 truncate text-sm font-700 text-gray-900">{{ customerName }}</span>
|
||||
<span class="shrink-0 text-xs text-gray-400">#{{ item.id ?? '-' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between gap-2 text-xs text-gray-600">
|
||||
<span>
|
||||
<span class="text-gray-500">ریال:</span>
|
||||
<span class="font-600 text-gray-900">{{ cashLabel }}</span>
|
||||
</span>
|
||||
<span>
|
||||
<span class="text-gray-500">طلا:</span>
|
||||
<span class="font-700 text-gray-900">{{ goldLabel }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</MobileCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Wallet } from '@/model/wallet'
|
||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
|
||||
const props = defineProps<{
|
||||
item: Wallet
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'open', item: Wallet): void
|
||||
}>()
|
||||
|
||||
const customerName = computed(() => props.item.customer?.name || '-')
|
||||
const cashLabel = computed(() => usePrice(props.item.cash ?? 0, true, ''))
|
||||
const goldLabel = computed(() => {
|
||||
const gold = props.item.gold
|
||||
if (gold == null) return '-'
|
||||
return `${Number(Number(gold).toFixed(3))} گرم`
|
||||
})
|
||||
</script>
|
||||
@@ -1,110 +1,73 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full min-h-0">
|
||||
<BasicShowContext title="لیست کیف پول">
|
||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<!-- <template #extraHeader>
|
||||
<BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
||||
<div>افزودن کیف پول</div>
|
||||
<BaseIcon icon="vuesax-linear:wallet-add" Size="16px" />
|
||||
</BaseButton>
|
||||
</template>-->
|
||||
<template #extraHeader>
|
||||
<div class="w-60vw hidden md:block">
|
||||
<div class="grid grid-cols-2 gap-3 w-full py-2 px-5">
|
||||
<div
|
||||
:class="
|
||||
sums?.cash >= 0
|
||||
? sums?.cash === 0
|
||||
? 'bg-amber-100'
|
||||
: 'bg-green-100'
|
||||
: 'bg-red-100'
|
||||
"
|
||||
class="rounded-lg p-2"
|
||||
>
|
||||
<div class="mb-1">مجموع موجودی کیف پول:</div>
|
||||
<div class="flex gap-2">
|
||||
<div
|
||||
class="gap-5 rounded-lg border-1 border-solid border-gray flex-1 p-1 text-center"
|
||||
>
|
||||
<div class="font-bold">{{ usePrice(sums?.cash ?? 0) }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="gap-5 rounded-lg border-1 border-solid border-gray flex-1 p-1 text-center"
|
||||
>
|
||||
<div class="font-bold">
|
||||
{{ usePrice(sums?.cashToGold.toFixed(2) ?? 0, true, 'گرم') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex h-full min-h-0 flex-col gap-3">
|
||||
<div class="hidden shrink-0 md:grid md:grid-cols-1 md:gap-3 lg:grid-cols-2">
|
||||
<div
|
||||
class="rounded-lg p-2.5 text-sm"
|
||||
:class="walletSumTone(sums?.cash)"
|
||||
>
|
||||
<div class="mb-2 font-medium">مجموع موجودی کیف پول</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="rounded-lg border border-gray-200 bg-white/70 px-2 py-1.5 text-center">
|
||||
<div class="text-xs text-gray-500">ریال</div>
|
||||
<div class="mt-1 font-bold break-all">{{ usePrice(sums?.cash ?? 0) }}</div>
|
||||
</div>
|
||||
<div
|
||||
:class="
|
||||
sums?.gold >= 0
|
||||
? sums?.gold === 0
|
||||
? 'bg-amber-100'
|
||||
: 'bg-green-100'
|
||||
: 'bg-red-100'
|
||||
"
|
||||
class="rounded-lg p-2"
|
||||
>
|
||||
<div class="mb-1">مجموع موجودی کیف طلا:</div>
|
||||
<div class="flex gap-2">
|
||||
<div
|
||||
class="gap-5 rounded-lg border-1 border-solid border-gray flex-1 p-1 text-center"
|
||||
>
|
||||
<div class="font-bold">{{ usePrice(sums?.gold ?? 0, true, 'گرم') }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="gap-5 rounded-lg border-1 border-solid border-gray flex-1 p-1 text-center"
|
||||
>
|
||||
<div class="font-bold">{{ usePrice(sums?.goldToCash ?? 0) }}</div>
|
||||
</div>
|
||||
<div class="rounded-lg border border-gray-200 bg-white/70 px-2 py-1.5 text-center">
|
||||
<div class="text-xs text-gray-500">گرم</div>
|
||||
<div class="mt-1 font-bold">
|
||||
{{ usePrice(Number(sums?.cashToGold ?? 0).toFixed(2), true, 'گرم') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #action="{ column, record }">
|
||||
<div class="gap-2 flex justify-center">
|
||||
<!-- <a-tooltip
|
||||
:key="record.active"
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="changeCustomerActivationFunction(record.id)"
|
||||
>
|
||||
<template #title>
|
||||
<span>{{ record.active ? 'غیرفعال کردن' : 'فعال کردن' }}</span>
|
||||
</template>
|
||||
<Icon
|
||||
:icon="record.active ? 'vuesax-linear:tick-circle' : 'vuesax-linear:close-circle'"
|
||||
:color="record.active ? '#54AD06' : '#ad0627'"
|
||||
/>
|
||||
</a-tooltip>-->
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('info', record)">
|
||||
<template #title>
|
||||
<span>مشخصات</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('edit', record)">
|
||||
<template #title>
|
||||
<span>ویرایش</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:edit" color="#4572E9" />
|
||||
</a-tooltip>
|
||||
<!-- <a-tooltip
|
||||
placement="bottom"
|
||||
class="cursor-pointer"
|
||||
@click="emits('delete', record.id)"
|
||||
>
|
||||
<template #title>
|
||||
<span>حذف</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:trash" color="#EB3535" />
|
||||
</a-tooltip>-->
|
||||
|
||||
<div
|
||||
class="rounded-lg p-2.5 text-sm"
|
||||
:class="walletSumTone(sums?.gold)"
|
||||
>
|
||||
<div class="mb-2 font-medium">مجموع موجودی کیف طلا</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="rounded-lg border border-gray-200 bg-white/70 px-2 py-1.5 text-center">
|
||||
<div class="text-xs text-gray-500">گرم</div>
|
||||
<div class="mt-1 font-bold">
|
||||
{{ usePrice(sums?.gold ?? 0, true, 'گرم') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-lg border border-gray-200 bg-white/70 px-2 py-1.5 text-center">
|
||||
<div class="text-xs text-gray-500">ریال</div>
|
||||
<div class="mt-1 font-bold break-all">{{ usePrice(sums?.goldToCash ?? 0) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</BaseTable>
|
||||
</div>
|
||||
|
||||
<BaseTable v-if="!isMobile" class="min-h-0 min-w-0 flex-1" v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #action="{ column, record }">
|
||||
<div class="gap-2 flex justify-center">
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('info', record)">
|
||||
<template #title>
|
||||
<span>مشخصات</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('edit', record)">
|
||||
<template #title>
|
||||
<span>ویرایش</span>
|
||||
</template>
|
||||
<Icon icon="vuesax-linear:edit" color="#4572E9" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</BaseTable>
|
||||
|
||||
<WalletMobileCardList
|
||||
v-else
|
||||
ref="mobileListRef"
|
||||
class="min-h-0 flex-1"
|
||||
@sums="onMobileSums"
|
||||
/>
|
||||
</div>
|
||||
</BasicShowContext>
|
||||
</div>
|
||||
</template>
|
||||
@@ -113,30 +76,21 @@
|
||||
import type { TableInput } from '@core/type/baseTable'
|
||||
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||
import { changeCustomerActivation, getCustomerTable } from '@/api/customer'
|
||||
import { changeCustomerActivation } from '@/api/customer'
|
||||
import { cols } from '../hooks/walletColumns'
|
||||
import { useUserStore } from '@/store/userStore'
|
||||
import { usePrefix } from '@core/composable/usePrefix'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { getWalletTable } from '@/api/wallet'
|
||||
import { usePrice } from '../../../../core/utils/usePrice'
|
||||
import WalletMobileCardList from './WalletMobileCardList.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
const [submit, reload] = useBaseTable()
|
||||
/*const select = (val) => {
|
||||
val.sts === 1 ? return ['1'] : ['2']
|
||||
}*/
|
||||
//emits
|
||||
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||
const emits = defineEmits(['delete', 'edit', 'add', 'info', 'sums'])
|
||||
|
||||
const { prefixCls } = usePrefix('pophoverstatus')
|
||||
|
||||
//permission
|
||||
/*function checkPermission(temp: string) {
|
||||
const store = useUserStore()
|
||||
return store.canAccess(temp)
|
||||
}*/
|
||||
|
||||
//define table
|
||||
const table = ref(null)
|
||||
const sums = ref<any>()
|
||||
|
||||
@@ -146,13 +100,23 @@ const tblProps = ref<any>({
|
||||
const data = await getWalletTable(params)
|
||||
sums.value = data?.sums
|
||||
emits('sums', data?.sums)
|
||||
console.log(sums.value)
|
||||
return data
|
||||
}
|
||||
//bordered: true
|
||||
})
|
||||
const loading = ref<boolean>(false)
|
||||
|
||||
function walletSumTone(value?: number) {
|
||||
if (value == null || value >= 0) {
|
||||
return value === 0 ? 'bg-amber-100' : 'bg-green-100'
|
||||
}
|
||||
return 'bg-red-100'
|
||||
}
|
||||
|
||||
function onMobileSums(payload: unknown) {
|
||||
sums.value = payload
|
||||
emits('sums', payload)
|
||||
}
|
||||
|
||||
async function changeCustomerActivationFunction(id: number) {
|
||||
try {
|
||||
loading.value = true
|
||||
@@ -166,17 +130,12 @@ async function changeCustomerActivationFunction(id: number) {
|
||||
}
|
||||
}
|
||||
|
||||
const fetch = () => {
|
||||
// console.log('sss')
|
||||
reload()
|
||||
async function fetch() {
|
||||
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||
}
|
||||
// const spinTable = (val: boolean) => {
|
||||
// // console.log('sss')
|
||||
// table?.value?.loading.value = val
|
||||
// }
|
||||
|
||||
defineExpose({
|
||||
fetch
|
||||
// spinTable
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -192,11 +151,5 @@ defineExpose({
|
||||
.ant-menu-item-only-child {
|
||||
border-radius: @border-radius-base;
|
||||
}
|
||||
|
||||
//bor
|
||||
/* .ant-input-group-addon:first-child {
|
||||
!* border: #d9d9d9 1px solid;
|
||||
border-radius: 0 6px 6px 0;*!
|
||||
}*/
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full min-h-0">
|
||||
<BasicMainCard>
|
||||
<div class="md:hidden">
|
||||
<div class="flex h-full min-h-0 flex-col">
|
||||
<div class="shrink-0 md:hidden">
|
||||
<!-- <div class="bg-gray-100 grid grid-cols-2 gap-3 w-full py-2 px-5">
|
||||
<div class="gap-5 text-xs">
|
||||
<div>مجموع موجودی کیف پول:</div>
|
||||
@@ -70,10 +71,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex w-full h-full">
|
||||
<div class="flex min-h-0 flex-1 flex-col">
|
||||
<tbl
|
||||
ref="table"
|
||||
class="w-full h-full"
|
||||
class="h-full min-h-0 w-full"
|
||||
@info="info"
|
||||
@edit="edit"
|
||||
@sums="setSums"
|
||||
@@ -85,6 +86,7 @@
|
||||
<component :is="selectedComponent" class="mr-5" />-->
|
||||
<!-- <BasicShowContext title="پروژه ها"> <Search /> </BasicShowContext>-->
|
||||
</div>
|
||||
</div>
|
||||
</BasicMainCard>
|
||||
<BasicModal
|
||||
v-model:visible="addUserVisible"
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<MobileCardList
|
||||
v-model:search="search"
|
||||
:loading="loading"
|
||||
:has-more="hasMore"
|
||||
:show-empty="!loading && items.length === 0"
|
||||
empty-text="تراکنشی یافت نشد"
|
||||
search-placeholder="جستجو برداشت و واریز"
|
||||
show-refresh
|
||||
@search="reload"
|
||||
@refresh="reload"
|
||||
@load-more="loadMore"
|
||||
>
|
||||
<WithdrawMobileListItem
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
@open="openSheet"
|
||||
/>
|
||||
</MobileCardList>
|
||||
|
||||
<WithdrawMobileDetailSheet
|
||||
v-model:visible="sheetVisible"
|
||||
:item="selectedItem"
|
||||
@withdraw="(row) => emit('withdraw', row)"
|
||||
@weight="(row) => emit('weight', row)"
|
||||
@change-status="changeStatusHandler"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { message } from 'ant-design-vue'
|
||||
import { getWithdrawTable, updateSts } from '@/api/wallet'
|
||||
import type { Withdraw } from '@/model/wallet'
|
||||
import MobileCardList from '@/components/mobile/MobileCardList.vue'
|
||||
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
||||
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
||||
import { status } from '@/pages/menuPages/withdraw/information/hooks/status'
|
||||
import WithdrawMobileDetailSheet from './WithdrawMobileDetailSheet.vue'
|
||||
import WithdrawMobileListItem from './WithdrawMobileListItem.vue'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'withdraw', item: Withdraw): void
|
||||
(e: 'weight', item: Withdraw): void
|
||||
}>()
|
||||
|
||||
const { sheetVisible, selectedItem, openSheet, syncSelectedItem } = useMobileDetailSheet<Withdraw>()
|
||||
|
||||
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<Withdraw>({
|
||||
fetchApi: getWithdrawTable,
|
||||
onFetched: (rows) => syncSelectedItem(rows)
|
||||
})
|
||||
|
||||
async function changeStatusHandler(
|
||||
item: Withdraw,
|
||||
statusEntry: [string, (typeof status)[keyof typeof status]]
|
||||
) {
|
||||
if (!item.id) return
|
||||
try {
|
||||
await updateSts(item.id, { sts: statusEntry[0] })
|
||||
message.success('وضعیت ثبت شد')
|
||||
await fetch()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
message.error('خطا در ثبت اطلاعات')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => reload())
|
||||
|
||||
defineExpose({ fetch, openSheet })
|
||||
</script>
|
||||
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<MobileDetailSheet v-model:visible="visibleProxy" :item="item" :title="sheetTitle">
|
||||
<template #default="{ item: row }">
|
||||
<MobileSheetHeader :subtitle="dateLabel(row)" :show-title="isMobile">
|
||||
<template #title>
|
||||
<div class="flex w-full items-center justify-between gap-3">
|
||||
<span class="min-w-0 truncate">{{ sheetTitle }}</span>
|
||||
<div class="flex shrink-0 items-center gap-2">
|
||||
<MobileIconButton
|
||||
v-if="!row.tracked_number"
|
||||
plain
|
||||
icon="vuesax-linear:tick-circle"
|
||||
color="#16A34A"
|
||||
:size="22"
|
||||
@click="$emit('withdraw', row)"
|
||||
/>
|
||||
<MobileIconButton
|
||||
plain
|
||||
icon="vuesax-linear:3d-cube-scan"
|
||||
color="#1D4ED8"
|
||||
:size="22"
|
||||
@click="$emit('weight', row)"
|
||||
/>
|
||||
<MobileIconButton
|
||||
v-if="showStatusActions(row)"
|
||||
plain
|
||||
icon="vuesax-linear:document-download"
|
||||
color="#1D4ED8"
|
||||
:size="22"
|
||||
@click="goToFactor(row)"
|
||||
/>
|
||||
<ADropdown v-if="showStatusActions(row)" :trigger="['click']">
|
||||
<MobileIconButton plain icon="vuesax-linear:status-up" color="#CA8A04" :size="22" />
|
||||
<template #overlay>
|
||||
<a-menu class="flex flex-col">
|
||||
<a-menu-item
|
||||
v-for="(statusItem, index) in statusOptions(row)"
|
||||
:key="index"
|
||||
class="m-1 rounded border-none px-2 py-1"
|
||||
:style="{ color: statusItem[1].color }"
|
||||
@click="$emit('changeStatus', row, statusItem)"
|
||||
>
|
||||
<span class="font-normal">{{ statusItem[1].title }}</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</ADropdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</MobileSheetHeader>
|
||||
|
||||
<span class="inline-flex rounded-lg border px-2 py-0.5 text-xs font-600 border-amber-300 bg-amber-50 text-amber-700">
|
||||
{{ row.sts_str || '-' }}
|
||||
</span>
|
||||
|
||||
<div class="space-y-2">
|
||||
<MobileStatCard bordered variant="danger" label="مقدار (ریال)" :value="usePrice(row.cash ?? 0, true, '')">
|
||||
<template #prefix>
|
||||
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" color="#dc2626" />
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
<MobileStatCard bordered variant="warning" label="طلا" :value="goldLabel(row)">
|
||||
<template #prefix>
|
||||
<BaseIcon icon="vuesax-linear:coin" size="18" color="#b45309" />
|
||||
</template>
|
||||
</MobileStatCard>
|
||||
</div>
|
||||
|
||||
<MobileInfoGrid>
|
||||
<MobileDetailRow label="علت تراکنش" full-width :value="noteLabel(row)" />
|
||||
<MobileDetailRow label="شماره پیگیری" :value="row.tracked_number ?? '-'" />
|
||||
<MobileDetailRow label="کد سفارش" :value="row.id ?? '-'" />
|
||||
<MobileDetailRow label="کد کاربر" :value="row.customer_id ?? '-'" />
|
||||
<MobileDetailRow label="نام و نام خانوادگی" full-width :value="row.customer?.name || '-'" />
|
||||
<MobileDetailRow label="شماره کارت" full-width :value="row.bank_account?.cart_number || '-'" />
|
||||
<MobileDetailRow label="تاریخ ثبت" full-width :value="dateLabel(row)" />
|
||||
</MobileInfoGrid>
|
||||
</template>
|
||||
</MobileDetailSheet>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Withdraw } from '@/model/wallet'
|
||||
import MobileDetailRow from '@/components/mobile/MobileDetailRow.vue'
|
||||
import MobileDetailSheet from '@/components/mobile/MobileDetailSheet.vue'
|
||||
import MobileIconButton from '@/components/mobile/MobileIconButton.vue'
|
||||
import MobileInfoGrid from '@/components/mobile/MobileInfoGrid.vue'
|
||||
import MobileSheetHeader from '@/components/mobile/MobileSheetHeader.vue'
|
||||
import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
||||
import { status } from '@/pages/menuPages/withdraw/information/hooks/status'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import { toJalali } from '@core/utils/useDataTime'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
import { toNumber } from 'lodash'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
item: Withdraw | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
(e: 'withdraw', item: Withdraw): void
|
||||
(e: 'weight', item: Withdraw): void
|
||||
(e: 'changeStatus', item: Withdraw, statusEntry: [string, (typeof status)[keyof typeof status]]): void
|
||||
}>()
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
set: (value: boolean) => emit('update:visible', value)
|
||||
})
|
||||
|
||||
const sheetTitle = computed(() => props.item?.customer?.name || `برداشت #${props.item?.id ?? '-'}`)
|
||||
|
||||
function showStatusActions(row: Withdraw) {
|
||||
const sts = row.sts ?? 0
|
||||
return sts >= 50 && sts <= 70
|
||||
}
|
||||
|
||||
function statusOptions(row: Withdraw) {
|
||||
return Object.entries(status).filter((item) => toNumber(item[0]) !== row.sts) as [string, (typeof status)[keyof typeof status]][]
|
||||
}
|
||||
|
||||
function goToFactor(row: Withdraw) {
|
||||
if (!row.id) return
|
||||
router.push({ name: 'factor', params: { id: row.id } })
|
||||
}
|
||||
|
||||
function dateLabel(row: Withdraw) {
|
||||
return row.created_at ? toJalali(row.created_at) : '-'
|
||||
}
|
||||
|
||||
function noteLabel(row: Withdraw) {
|
||||
if (row.note == null || row.note === '') return '-'
|
||||
return String(row.note)
|
||||
}
|
||||
|
||||
function goldLabel(row: Withdraw) {
|
||||
if (row.gold == null) return '-'
|
||||
return String(row.gold)
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<MobileCard clickable @click="$emit('open', item)">
|
||||
<div class="space-y-1">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="truncate text-sm font-700 text-gray-900">{{ customerName }}</span>
|
||||
<span class="shrink-0 text-xs text-gray-400">{{ dateLabel }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between gap-2 text-xs">
|
||||
<span class="truncate text-gray-600">{{ noteLabel }}</span>
|
||||
<span class="shrink-0 font-700 text-red-600">{{ cashLabel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</MobileCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Withdraw } from '@/model/wallet'
|
||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||
import { toJalaliNoTime } from '@core/utils/useDataTime'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
|
||||
const props = defineProps<{
|
||||
item: Withdraw
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'open', item: Withdraw): void
|
||||
}>()
|
||||
|
||||
const noteLabel = computed(() => {
|
||||
if (props.item.note == null || props.item.note === '') return `#${props.item.id ?? '-'}`
|
||||
return String(props.item.note)
|
||||
})
|
||||
|
||||
const customerName = computed(() => props.item.customer?.name || '-')
|
||||
const dateLabel = computed(() => (props.item.created_at ? toJalaliNoTime(props.item.created_at) : '-'))
|
||||
const cashLabel = computed(() => usePrice(props.item.cash ?? 0, true, ''))
|
||||
</script>
|
||||
@@ -1,7 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||
<BasicShowContext title="لیست برداشت و واریز">
|
||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<div class="flex h-full min-h-0 flex-col">
|
||||
<BaseTable v-if="!isMobile" class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #extraHeader>
|
||||
<!-- <BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
||||
<div>افزودن مشتری عمده</div>
|
||||
@@ -61,6 +62,15 @@
|
||||
</div>
|
||||
</template>
|
||||
</BaseTable>
|
||||
|
||||
<WithdrawMobileCardList
|
||||
v-else
|
||||
ref="mobileListRef"
|
||||
class="min-h-0 flex-1"
|
||||
@withdraw="(row) => emits('withdraw', row)"
|
||||
@weight="(row) => emits('weight', row)"
|
||||
/>
|
||||
</div>
|
||||
</BasicShowContext>
|
||||
</div>
|
||||
</template>
|
||||
@@ -76,8 +86,11 @@ import { usePrefix } from '@core/composable/usePrefix'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { getOrderTable } from '@/api/order'
|
||||
import {getWithdrawTable, updateSts} from '@/api/wallet'
|
||||
import WithdrawMobileCardList from './WithdrawMobileCardList.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
const [submit, reload] = useBaseTable()
|
||||
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||
/*const select = (val) => {
|
||||
val.sts === 1 ? return ['1'] : ['2']
|
||||
}*/
|
||||
@@ -117,9 +130,8 @@ async function changeCustomerActivationFunction(id: number) {
|
||||
}
|
||||
}
|
||||
|
||||
const fetch = () => {
|
||||
// console.log('sss')
|
||||
reload()
|
||||
async function fetch() {
|
||||
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||
}
|
||||
// const spinTable = (val: boolean) => {
|
||||
// // console.log('sss')
|
||||
@@ -144,7 +156,7 @@ const test = (record: any) => {
|
||||
const changeStatusHandler = async (status: any) => {
|
||||
try {
|
||||
await changeStatus(withdrawId.value as number, status)
|
||||
reload()
|
||||
await fetch()
|
||||
} catch (e: any) {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
v-model:visible="confirmWithdrawVisible"
|
||||
width="15rem"
|
||||
title="تایید برداشت"
|
||||
wrap-class-name="withdraw-stack-modal"
|
||||
@ok="okWithdraw"
|
||||
:loading="loading"
|
||||
@close="confirmWithdrawVisible = false"
|
||||
@@ -51,22 +52,20 @@
|
||||
v-model:visible="confirmWeightVisible"
|
||||
width="40rem"
|
||||
title="ثبت اطلاعات"
|
||||
wrap-class-name="withdraw-stack-modal"
|
||||
:loading="loading"
|
||||
@close="confirmWeightVisible = false"
|
||||
>
|
||||
<div v-for="(item, index) in confirmWithdraw?.transaction">
|
||||
<div v-for="(item, index) in weightTransactions" :key="index">
|
||||
<div class="bg-gray-100 rounded-lg p-4 mt-2">
|
||||
<div class="mb-4">{{ item?.title }} ({{ (item.gold * -1) }} عدد)</div>
|
||||
<div class="mb-4">{{ item?.title }} ({{ weightCount(item) }} عدد)</div>
|
||||
<ADivider class="!my-0 "/>
|
||||
<div v-if="item.gold && item.gold * -1 > 0">
|
||||
<div v-if="weightCount(item) > 0">
|
||||
<div class="h-7 grid grid grid-cols-3">
|
||||
<span class="col-span-1 flex "></span>
|
||||
<span class="col-span-1 flex justify-center">وزن جدید</span>
|
||||
</div>
|
||||
<div v-for="i in Math.abs(item.gold * -1)" class="grid grid-cols-3 gap-2 mb-2" >
|
||||
<!-- <span class="col-span-1 text-xs font-medium py-[8px] px-[10px]">{{
|
||||
i
|
||||
}}</span>-->
|
||||
<div v-for="i in weightCount(item)" :key="i" class="grid grid-cols-3 gap-2 mb-2" >
|
||||
<span class="col-span-1 text-xs font-medium py-[8px] px-[10px]">{{ item?.title }} {{ i }}</span>
|
||||
<BasicInput class="col-span-1 text-xs font-medium w-10rem" v-model:value="newWeightArray[i]" />
|
||||
<BaseButton class="col-span-1 text-xs font-medium w-4rem mr-4" @click="ok(i)">ثبت</BaseButton>
|
||||
@@ -74,6 +73,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!weightTransactions.length" class="py-6 text-center text-sm text-gray-500">
|
||||
اطلاعات تراکنش یافت نشد
|
||||
</div>
|
||||
</BasicModal>
|
||||
</div>
|
||||
</template>
|
||||
@@ -97,7 +99,7 @@ import type { Customer } from '@/model/customer'
|
||||
import { createCustomer, deleteCustomer, updateCustomer } from '@/api/customer'
|
||||
import type { Order } from '@/model/order'
|
||||
import type { Wallet, Withdraw } from '@/model/wallet'
|
||||
import {completeWithdraw, sendWeights} from '@/api/wallet'
|
||||
import {completeWithdraw, getWithdrawById, sendWeights} from '@/api/wallet'
|
||||
import Button from "@core/components/BaseButton";
|
||||
//import DatePicker from '@core/components/_sample/components/datePicker.vue'
|
||||
|
||||
@@ -214,7 +216,8 @@ async function edit(record: Customer) {
|
||||
}*/
|
||||
|
||||
function openWithdraw(val: Withdraw) {
|
||||
confirmWithdraw = val
|
||||
confirmWithdraw.value = val
|
||||
resetData()
|
||||
confirmWithdrawVisible.value = true
|
||||
}
|
||||
|
||||
@@ -241,7 +244,17 @@ onMounted(async () => {
|
||||
//add project modal
|
||||
const confirmWithdrawVisible = ref<boolean>(false)
|
||||
const confirmWeightVisible = ref<boolean>(false)
|
||||
let confirmWithdraw: Withdraw
|
||||
const confirmWithdraw = ref<Withdraw | null>(null)
|
||||
|
||||
const weightTransactions = computed(() => {
|
||||
const tx = confirmWithdraw.value?.transaction
|
||||
return Array.isArray(tx) ? tx : []
|
||||
})
|
||||
|
||||
function weightCount(item: { gold?: number }) {
|
||||
const count = Math.floor(Math.abs(Number(item?.gold ?? 0)))
|
||||
return Number.isFinite(count) ? count : 0
|
||||
}
|
||||
// const userInfoVisible = ref<boolean>(false)
|
||||
|
||||
//submit weight
|
||||
@@ -251,7 +264,7 @@ async function okWithdraw() {
|
||||
// return
|
||||
loading.value = true
|
||||
try {
|
||||
form.value.id = confirmWithdraw
|
||||
form.value.id = confirmWithdraw.value?.id
|
||||
await completeWithdraw(form.value)
|
||||
confirmWithdrawVisible.value = false
|
||||
refreshTable()
|
||||
@@ -269,10 +282,24 @@ async function okWithdraw() {
|
||||
})
|
||||
}
|
||||
|
||||
const newWeightArray = ref([])
|
||||
function openWeight(val) {
|
||||
confirmWithdraw = val
|
||||
confirmWeightVisible.value = true
|
||||
const newWeightArray = ref<Record<number, number | undefined>>({})
|
||||
|
||||
async function openWeight(val: Withdraw) {
|
||||
try {
|
||||
loading.value = true
|
||||
newWeightArray.value = {}
|
||||
if (val.id && !Array.isArray(val.transaction)) {
|
||||
confirmWithdraw.value = await getWithdrawById(val.id)
|
||||
} else {
|
||||
confirmWithdraw.value = val
|
||||
}
|
||||
confirmWeightVisible.value = true
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
message.error('خطا در بارگذاری اطلاعات')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
function ok(i) {
|
||||
try {
|
||||
@@ -289,3 +316,9 @@ function ok(i) {
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
<style>
|
||||
.withdraw-stack-modal {
|
||||
z-index: 1100 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user