change responsive]
This commit is contained in:
Vendored
+5
-2
@@ -10,7 +10,6 @@ export {}
|
|||||||
declare module '@vue/runtime-core' {
|
declare module '@vue/runtime-core' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
_index: typeof import('./src/core/components/_sample/_index.vue')['default']
|
_index: typeof import('./src/core/components/_sample/_index.vue')['default']
|
||||||
AAffix: typeof import('ant-design-vue/es')['Affix']
|
|
||||||
AAvatar: typeof import('ant-design-vue/es')['Avatar']
|
AAvatar: typeof import('ant-design-vue/es')['Avatar']
|
||||||
ABadge: typeof import('ant-design-vue/es')['Badge']
|
ABadge: typeof import('ant-design-vue/es')['Badge']
|
||||||
AButton: typeof import('ant-design-vue/es')['Button']
|
AButton: typeof import('ant-design-vue/es')['Button']
|
||||||
@@ -34,7 +33,6 @@ declare module '@vue/runtime-core' {
|
|||||||
AModal: typeof import('ant-design-vue/es')['Modal']
|
AModal: typeof import('ant-design-vue/es')['Modal']
|
||||||
APagination: typeof import('ant-design-vue/es')['Pagination']
|
APagination: typeof import('ant-design-vue/es')['Pagination']
|
||||||
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
||||||
APopover: typeof import('ant-design-vue/es')['Popover']
|
|
||||||
ARadio: typeof import('ant-design-vue/es')['Radio']
|
ARadio: typeof import('ant-design-vue/es')['Radio']
|
||||||
ARadioButton: typeof import('ant-design-vue/es')['RadioButton']
|
ARadioButton: typeof import('ant-design-vue/es')['RadioButton']
|
||||||
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
|
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
|
||||||
@@ -51,6 +49,7 @@ declare module '@vue/runtime-core' {
|
|||||||
AUpload: typeof import('ant-design-vue/es')['Upload']
|
AUpload: typeof import('ant-design-vue/es')['Upload']
|
||||||
BaseButton: typeof import('./src/core/components/BaseButton/index.ts')['default']
|
BaseButton: typeof import('./src/core/components/BaseButton/index.ts')['default']
|
||||||
BaseCard: typeof import('./src/core/components/BaseCard/index.ts')['default']
|
BaseCard: typeof import('./src/core/components/BaseCard/index.ts')['default']
|
||||||
|
BaseCollapse: typeof import('./src/components/BaseCollapse.vue')['default']
|
||||||
BaseDatePicker: typeof import('./src/core/components/BaseDatePicker/index.ts')['default']
|
BaseDatePicker: typeof import('./src/core/components/BaseDatePicker/index.ts')['default']
|
||||||
BaseIcon: typeof import('./src/core/components/BaseIcon/index.ts')['default']
|
BaseIcon: typeof import('./src/core/components/BaseIcon/index.ts')['default']
|
||||||
BaseInputNumber: typeof import('./src/core/components/BaseInputNumber/index.ts')['default']
|
BaseInputNumber: typeof import('./src/core/components/BaseInputNumber/index.ts')['default']
|
||||||
@@ -58,12 +57,16 @@ declare module '@vue/runtime-core' {
|
|||||||
BaseSwitch: typeof import('./src/core/components/BaseSwitch/index.ts')['default']
|
BaseSwitch: typeof import('./src/core/components/BaseSwitch/index.ts')['default']
|
||||||
BaseTable: typeof import('./src/core/components/BaseTable/index.ts')['default']
|
BaseTable: typeof import('./src/core/components/BaseTable/index.ts')['default']
|
||||||
BaseTableOld: typeof import('./src/core/components/BaseTableOld/index.ts')['default']
|
BaseTableOld: typeof import('./src/core/components/BaseTableOld/index.ts')['default']
|
||||||
|
BasicActionSheet: typeof import('./src/components/BasicActionSheet.vue')['default']
|
||||||
BasicCard: typeof import('./src/components/BasicCard.vue')['default']
|
BasicCard: typeof import('./src/components/BasicCard.vue')['default']
|
||||||
BasicInfoItem: typeof import('./src/components/BasicInfoItem.vue')['default']
|
BasicInfoItem: typeof import('./src/components/BasicInfoItem.vue')['default']
|
||||||
BasicInput: typeof import('./src/components/BasicInput.vue')['default']
|
BasicInput: typeof import('./src/components/BasicInput.vue')['default']
|
||||||
BasicMainCard: typeof import('./src/components/BasicMainCard.vue')['default']
|
BasicMainCard: typeof import('./src/components/BasicMainCard.vue')['default']
|
||||||
BasicMainCardFull: typeof import('./src/components/BasicMainCardFull.vue')['default']
|
BasicMainCardFull: typeof import('./src/components/BasicMainCardFull.vue')['default']
|
||||||
|
BasicMobileBottomNav: typeof import('./src/components/BasicMobileBottomNav.vue')['default']
|
||||||
BasicMobileHeader: typeof import('./src/components/BasicMobileHeader.vue')['default']
|
BasicMobileHeader: typeof import('./src/components/BasicMobileHeader.vue')['default']
|
||||||
|
BasicMobileHeaderLogo: typeof import('./src/components/BasicMobileHeaderLogo.vue')['default']
|
||||||
|
BasicMobileOverflowMenu: typeof import('./src/components/BasicMobileOverflowMenu.vue')['default']
|
||||||
BasicModal: typeof import('./src/components/BasicModal.vue')['default']
|
BasicModal: typeof import('./src/components/BasicModal.vue')['default']
|
||||||
BasicSelect: typeof import('./src/components/BasicSelect.vue')['default']
|
BasicSelect: typeof import('./src/components/BasicSelect.vue')['default']
|
||||||
BasicShowContext: typeof import('./src/components/BasicShowContext.vue')['default']
|
BasicShowContext: typeof import('./src/components/BasicShowContext.vue')['default']
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import useAxios from "@core/utils/useAxios";
|
import useAxios from '@core/utils/useAxios'
|
||||||
import type {Accounting, TotalAccounting} from "@/model/accounting";
|
import type { TableInput } from '@core/type/baseTable'
|
||||||
|
import type { Accounting, TotalAccounting } from '@/model/accounting'
|
||||||
|
|
||||||
export const getAccountingTable = async (start: string,end: string) =>
|
export const getAccountingTable = async (start: string,end: string) =>
|
||||||
await useAxios.post<Accounting>('/admin/dashboard/finance', {startDate: start, endDate: end});
|
await useAxios.post<Accounting>('/admin/dashboard/finance', {startDate: start, endDate: end});
|
||||||
|
|
||||||
//Accounting Tab
|
//Accounting Tab
|
||||||
export const getTotalAccountingTable = async () =>
|
export const getTotalAccountingTable = async (params: TableInput) =>
|
||||||
await useAxios.post<TotalAccounting>('/admin/finance');
|
await useAxios.post<TotalAccounting[]>('/admin/finance', params)
|
||||||
+1
-1
@@ -28,7 +28,7 @@ export const changeCustomerArchive = async (id: number) =>
|
|||||||
export const confirmUser = async (id: number) =>
|
export const confirmUser = async (id: number) =>
|
||||||
await useAxios.get(`/admin/product/is_valid/${id}`)
|
await useAxios.get(`/admin/product/is_valid/${id}`)
|
||||||
|
|
||||||
export const getCustomerArchiveTable = async (params: TableInput) =>
|
export const getProductArchiveTable = async (params: TableInput) =>
|
||||||
await useAxios.post<Array<Customer>>('/admin/product/archive/table', params)
|
await useAxios.post<Array<Customer>>('/admin/product/archive/table', params)
|
||||||
|
|
||||||
export const getSingleTable = async (params: TableInput) =>
|
export const getSingleTable = async (params: TableInput) =>
|
||||||
|
|||||||
+2
-2
@@ -5,10 +5,10 @@ import type { TableInput } from '@core/type/baseTable'
|
|||||||
import type { Order } from '@/model/order'
|
import type { Order } from '@/model/order'
|
||||||
import type { FunctionItem } from '@/model/Item'
|
import type { FunctionItem } from '@/model/Item'
|
||||||
import sms from '@/router/routes/sms'
|
import sms from '@/router/routes/sms'
|
||||||
import type { Sms } from '@/model/sms'
|
import type { Sms, SmsRecord } from '@/model/sms'
|
||||||
|
|
||||||
export const getSMSTable = async (params: TableInput) =>
|
export const getSMSTable = async (params: TableInput) =>
|
||||||
await useAxios.post<Array<Price>>('/admin/messages/table', params)
|
await useAxios.post<Array<SmsRecord>>('/admin/messages/table', params)
|
||||||
|
|
||||||
export const getNumbersTable = async (params: TableInput) =>
|
export const getNumbersTable = async (params: TableInput) =>
|
||||||
await useAxios.post<Array<Price>>('/admin/messages/get_customer_list/table', params)
|
await useAxios.post<Array<Price>>('/admin/messages/get_customer_list/table', params)
|
||||||
|
|||||||
+4
-9
@@ -14,26 +14,22 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { CollapsePanel } from 'ant-design-vue'
|
||||||
import {CollapsePanel} from "ant-design-vue";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string,
|
title: string
|
||||||
activeKey: string | number,
|
activeKey: string | number
|
||||||
actives: string[] | number[]
|
actives: string[] | number[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {});
|
const props = withDefaults(defineProps<Props>(), {})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
|
|
||||||
.open {
|
.open {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background-color: #E5E7EB;
|
background-color: #E5E7EB;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.rotate {
|
.rotate {
|
||||||
@@ -67,5 +63,4 @@ const props = withDefaults(defineProps<Props>(), {});
|
|||||||
.ant-collapse > .ant-collapse-item > .ant-collapse-header {
|
.ant-collapse > .ant-collapse-item > .ant-collapse-header {
|
||||||
padding: 12px 16px 0 !important;
|
padding: 12px 16px 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
<template>
|
||||||
|
<BasicModal
|
||||||
|
v-if="!isMobileView"
|
||||||
|
:key="`action-sheet-modal-${overlayKey}`"
|
||||||
|
v-model:visible="visibleProxy"
|
||||||
|
:title="title"
|
||||||
|
:width="modalWidth"
|
||||||
|
:loading="loading"
|
||||||
|
custom-footer
|
||||||
|
@close="handleCancel"
|
||||||
|
@ok="$emit('ok')"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<template #footer>
|
||||||
|
<div
|
||||||
|
v-if="showButton && (showCancel || showOk)"
|
||||||
|
class="grid gap-3"
|
||||||
|
:class="[!showOk || !showCancel ? 'grid-cols-1' : 'grid-cols-12']"
|
||||||
|
>
|
||||||
|
<AButton
|
||||||
|
v-if="showCancel"
|
||||||
|
type="primary"
|
||||||
|
:class="[{ 'col-span-4': showOk }]"
|
||||||
|
size="large"
|
||||||
|
ghost
|
||||||
|
block
|
||||||
|
@click="handleCancel"
|
||||||
|
>
|
||||||
|
{{ cancelText }}
|
||||||
|
</AButton>
|
||||||
|
<AButton
|
||||||
|
v-if="showOk"
|
||||||
|
type="primary"
|
||||||
|
:class="[{ 'col-span-8': showCancel }]"
|
||||||
|
size="large"
|
||||||
|
block
|
||||||
|
@click="$emit('ok')"
|
||||||
|
>
|
||||||
|
{{ okText }}
|
||||||
|
</AButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</BasicModal>
|
||||||
|
|
||||||
|
<ADrawer
|
||||||
|
v-else-if="isMobileView"
|
||||||
|
:key="`action-sheet-drawer-${overlayKey}`"
|
||||||
|
v-model:visible="visibleProxy"
|
||||||
|
placement="bottom"
|
||||||
|
:closable="false"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:height="drawerHeight"
|
||||||
|
class="basic-action-sheet"
|
||||||
|
>
|
||||||
|
<div class="basic-action-sheet__container">
|
||||||
|
<div class="flex cursor-pointer justify-center pt-2" @click="visibleProxy = false">
|
||||||
|
<span class="w-12 rounded-sm border-t-2 border-t-solid border-t-gray-500"></span>
|
||||||
|
</div>
|
||||||
|
<div class="basic-action-sheet__content">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="showButton && (showCancel || showOk)"
|
||||||
|
class="basic-action-sheet__footer grid gap-3"
|
||||||
|
:class="[!showOk || !showCancel ? 'grid-cols-1' : 'grid-cols-12']"
|
||||||
|
>
|
||||||
|
<AButton
|
||||||
|
v-if="showCancel"
|
||||||
|
type="primary"
|
||||||
|
:class="[{ 'col-span-4': showOk }]"
|
||||||
|
size="large"
|
||||||
|
ghost
|
||||||
|
block
|
||||||
|
@click="handleCancel"
|
||||||
|
>
|
||||||
|
{{ cancelText }}
|
||||||
|
</AButton>
|
||||||
|
<AButton
|
||||||
|
v-if="showOk"
|
||||||
|
type="primary"
|
||||||
|
:class="[{ 'col-span-8': showCancel }]"
|
||||||
|
size="large"
|
||||||
|
block
|
||||||
|
@click="$emit('ok')"
|
||||||
|
>
|
||||||
|
{{ okText }}
|
||||||
|
</AButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ADrawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
import BasicModal from '@/components/BasicModal.vue'
|
||||||
|
import { ACTION_SHEET_HEIGHT } from '@/constants/actionSheet'
|
||||||
|
import { isMobile } from '@core/utils/is'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
visible?: boolean
|
||||||
|
title?: string
|
||||||
|
modalWidth?: string
|
||||||
|
loading?: boolean
|
||||||
|
drawerHeight?: string
|
||||||
|
cancelText?: string
|
||||||
|
showButton?: boolean
|
||||||
|
showCancel?: boolean
|
||||||
|
showOk?: boolean
|
||||||
|
okText?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
visible: false,
|
||||||
|
title: '',
|
||||||
|
modalWidth: '36rem',
|
||||||
|
loading: false,
|
||||||
|
drawerHeight: ACTION_SHEET_HEIGHT,
|
||||||
|
cancelText: 'بازگشت',
|
||||||
|
showButton: false,
|
||||||
|
showCancel: true,
|
||||||
|
showOk: true,
|
||||||
|
okText: 'ثبت'
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'update:visible', value: boolean): void
|
||||||
|
(e: 'update:show', value: boolean): void
|
||||||
|
(e: 'ok'): void
|
||||||
|
(e: 'cancel'): void
|
||||||
|
(e: 'close'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const isMobileView = computed(() => isMobile.value)
|
||||||
|
const overlayKey = computed(() => (isMobileView.value ? 'drawer' : 'modal'))
|
||||||
|
|
||||||
|
const visibleProxy = computed({
|
||||||
|
get: () => props.visible,
|
||||||
|
set: (value: boolean) => {
|
||||||
|
emit('update:visible', value)
|
||||||
|
emit('update:show', value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleCancel() {
|
||||||
|
emit('cancel')
|
||||||
|
emit('close')
|
||||||
|
visibleProxy.value = false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.basic-action-sheet {
|
||||||
|
.ant-drawer-content {
|
||||||
|
border-radius: 16px 16px 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-drawer-body {
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.basic-action-sheet__container {
|
||||||
|
height: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.basic-action-sheet__content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 0 16px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.basic-action-sheet__footer {
|
||||||
|
position: sticky;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 2;
|
||||||
|
background: #fff;
|
||||||
|
padding: 8px 16px calc(16px + env(safe-area-inset-bottom));
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,11 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-card class="m-5 rounded-xl md:h-[91vh]">
|
<div class="box-border flex h-full min-h-0 min-w-0 w-full max-w-full flex-col overflow-hidden p-3 md:p-5">
|
||||||
|
<a-card class="basic-main-card flex min-h-0 flex-1 flex-col overflow-hidden rounded-xl">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script></script>
|
<script></script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.basic-main-card :deep(.ant-card-body) {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<!-- {{ props.menu }}-->
|
|
||||||
<AMenu
|
<AMenu
|
||||||
v-model:selectedKeys="selectedKeys"
|
v-model:selectedKeys="selectedKeys"
|
||||||
:style="{ width: props.width }"
|
:style="{ width: props.width }"
|
||||||
:mode="props.mode"
|
:mode="props.mode"
|
||||||
:theme="props.theme"
|
:theme="props.theme"
|
||||||
>
|
>
|
||||||
<!-- <a-menu-item key="1"> Navigation One </a-menu-item>-->
|
|
||||||
<!-- {{ props.menu }}-->
|
|
||||||
<BasicMenuItem :menu="props.menu" />
|
<BasicMenuItem :menu="props.menu" />
|
||||||
</AMenu>
|
</AMenu>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,176 @@
|
|||||||
|
<template>
|
||||||
|
<nav class="mobile-bottom-nav md:hidden" aria-label="منوی اصلی">
|
||||||
|
<button
|
||||||
|
v-for="item in navItems"
|
||||||
|
:key="String(item.name)"
|
||||||
|
type="button"
|
||||||
|
class="mobile-bottom-nav__item"
|
||||||
|
:class="{ 'mobile-bottom-nav__item--active': isRouteActive(item) }"
|
||||||
|
@click="navigateTo(item)"
|
||||||
|
>
|
||||||
|
<span class="mobile-bottom-nav__icon-wrap">
|
||||||
|
<BaseIcon
|
||||||
|
v-if="item.meta?.icon"
|
||||||
|
:icon="item.meta.icon as string"
|
||||||
|
:size="menuIconSize"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span class="mobile-bottom-nav__label">{{ item.meta?.title ?? item.name }}</span>
|
||||||
|
</button>
|
||||||
|
<BasicMobileOverflowMenu :items="overflowMenuItems" @select="navigateTo" />
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import BaseIcon from '@core/components/BaseIcon'
|
||||||
|
import { menuIconSize } from '@root/config'
|
||||||
|
import { useRouterStore } from '@core/stores/router'
|
||||||
|
import BasicMobileOverflowMenu from '@/components/BasicMobileOverflowMenu.vue'
|
||||||
|
import { sortBy } from 'lodash'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import type { RouteRecordName, RouteRecordRaw } from 'vue-router'
|
||||||
|
|
||||||
|
const BOTTOM_NAV_PRIORITY: (string | symbol)[] = ['dashboard', 'orders', 'accounting', 'customers']
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const currentRoute = useRoute()
|
||||||
|
const routerStore = useRouterStore()
|
||||||
|
|
||||||
|
const allMenuItems = computed(() =>
|
||||||
|
sortBy(
|
||||||
|
routerStore.routes.filter((item) => !item.meta?.hideMenu),
|
||||||
|
(item) => item.meta?.order
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const navItems = computed(() => {
|
||||||
|
const items: RouteRecordRaw[] = []
|
||||||
|
|
||||||
|
for (const name of BOTTOM_NAV_PRIORITY) {
|
||||||
|
const route = allMenuItems.value.find((item) => item.name === name)
|
||||||
|
if (route) items.push(route)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const route of allMenuItems.value) {
|
||||||
|
if (items.length >= 4) break
|
||||||
|
if (!items.some((item) => item.name === route.name)) {
|
||||||
|
items.push(route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return items.slice(0, 4)
|
||||||
|
})
|
||||||
|
|
||||||
|
const navItemNames = computed(() => new Set(navItems.value.map((item) => item.name)))
|
||||||
|
|
||||||
|
const overflowMenuItems = computed(() =>
|
||||||
|
allMenuItems.value.filter((item) => !navItemNames.value.has(item.name))
|
||||||
|
)
|
||||||
|
|
||||||
|
function isRouteActive(route: RouteRecordRaw): boolean {
|
||||||
|
if (currentRoute.name === route.name) return true
|
||||||
|
|
||||||
|
function checkChildren(children?: RouteRecordRaw[]): boolean {
|
||||||
|
if (!children?.length) return false
|
||||||
|
for (const child of children) {
|
||||||
|
if (child.name === currentRoute.name) return true
|
||||||
|
if (checkChildren(child.children)) return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkChildren(route.children)
|
||||||
|
}
|
||||||
|
|
||||||
|
function navigateTo(item: RouteRecordRaw) {
|
||||||
|
const redirect = item.redirect
|
||||||
|
if (redirect && typeof redirect === 'object' && 'name' in redirect) {
|
||||||
|
router.push({ name: redirect.name as RouteRecordName })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
router.push({ name: item.name })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.mobile-bottom-nav {
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-content: space-around;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 6px 8px calc(6px + env(safe-area-inset-bottom));
|
||||||
|
background: #fff;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-bottom-nav__item {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4px;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 6px 4px 8px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
color: #595959;
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
color: #1677ff;
|
||||||
|
|
||||||
|
.mobile-bottom-nav__icon-wrap {
|
||||||
|
background: #e6f4ff;
|
||||||
|
color: #1677ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 28px;
|
||||||
|
height: 3px;
|
||||||
|
border-radius: 3px 3px 0 0;
|
||||||
|
background: #1677ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-bottom-nav__icon-wrap {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 10px;
|
||||||
|
color: #292d32;
|
||||||
|
|
||||||
|
:deep(svg path) {
|
||||||
|
stroke: currentColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-bottom-nav__label {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.2;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,22 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div theme="light" :class="[prefixCls]" class="md:hidden">
|
<div theme="light" :class="[prefixCls]" class="md:hidden">
|
||||||
<div class="flex items-center justify-center gap-2 relative">
|
<div class="mobile-header-bar">
|
||||||
<BaseIcon
|
<span class="mobile-header-bar__name">{{ displayName }}</span>
|
||||||
icon="vuesax-linear:menu"
|
<div class="mobile-header-bar__logo">
|
||||||
class="p-3 absolute right-2 top-1"
|
<BasicMobileHeaderLogo />
|
||||||
@click="showDrawer"
|
|
||||||
/>
|
|
||||||
<img :src="getConfigApp('avatar')" class="w-4rem mr-2 my-5" />
|
|
||||||
<div class="absolute left-2 top-3">
|
|
||||||
<div class="flex items-center w-full justify-center"></div>
|
|
||||||
<ADropdown placement="bottomLeft">
|
|
||||||
<a class="ant-dropdown-link" @click.prevent>
|
|
||||||
<!-- Hover me-->
|
|
||||||
<div class="flex items-center gap-1 text-gray-400">
|
|
||||||
<AAvatar :src="placeholderM" />
|
|
||||||
<Icon icon="vuesax-linear:arrow-down" size="15" />
|
|
||||||
</div>
|
</div>
|
||||||
</a>
|
<div class="mobile-header-bar__actions">
|
||||||
|
<button type="button" class="mobile-header-bar__icon" aria-label="رفرش صفحه" @click="handleRefresh">
|
||||||
|
<Icon icon="vuesax-linear:refresh" :size="26" />
|
||||||
|
</button>
|
||||||
|
<ADropdown placement="bottomLeft">
|
||||||
|
<button type="button" class="mobile-header-bar__icon" aria-label="پروفایل">
|
||||||
|
<Icon icon="vuesax-linear:profile-circle" :size="26" />
|
||||||
|
</button>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<AMenu>
|
<AMenu>
|
||||||
<AMenuItem>
|
<AMenuItem>
|
||||||
@@ -41,13 +37,7 @@
|
|||||||
</AMenu>
|
</AMenu>
|
||||||
</template>
|
</template>
|
||||||
</ADropdown>
|
</ADropdown>
|
||||||
<BaseButton
|
</div>
|
||||||
iconSize="20"
|
|
||||||
icon="vuesax-linear:setting"
|
|
||||||
type="text"
|
|
||||||
v-if="layoutStore.showSettingsIcon"
|
|
||||||
@click="handleShowSettings"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<BasicModal
|
<BasicModal
|
||||||
@@ -76,63 +66,24 @@
|
|||||||
</a-form>
|
</a-form>
|
||||||
</BasicModal>
|
</BasicModal>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<ADrawer height="460px" :placement="placement" v-model:visible="open" :closable="false" @close="onClose">
|
|
||||||
<AMenu v-model:selectedKeys="selectedKeys" mode="vertical">
|
|
||||||
<template v-for="item in menu">
|
|
||||||
<BasicSubMenu
|
|
||||||
v-if="!item.meta?.hideChildren && item.children && item.children.length > 0"
|
|
||||||
:menu-item="item"
|
|
||||||
:key="item.name"
|
|
||||||
:level="1"
|
|
||||||
@click="onClose"
|
|
||||||
/>
|
|
||||||
<BaseMenuItem v-else :item="item" :key="item.name" @click="onClose" />
|
|
||||||
</template>
|
|
||||||
</AMenu>
|
|
||||||
</ADrawer>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// import Logo from '@/assets/img/saminLogo.png'
|
|
||||||
import type { DrawerProps } from 'ant-design-vue'
|
|
||||||
import { inputLenRules, inputRules } from '@/utils/inputRules'
|
import { inputLenRules, inputRules } from '@/utils/inputRules'
|
||||||
|
|
||||||
const placement = ref<DrawerProps['placement']>('bottom')
|
|
||||||
import { usePrefix } from '@core/composable/usePrefix'
|
import { usePrefix } from '@core/composable/usePrefix'
|
||||||
import { useLayoutStore } from '@core/stores/layout'
|
|
||||||
import BasicMenu from '@core/layouts/composnts/BasicMenu.vue'
|
|
||||||
import placeholderM from '@core/assets/img/avatars/placeholder-m.jpg'
|
|
||||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||||
import { useSettingsStore } from '@core/stores/settings'
|
import BasicMobileHeaderLogo from '@/components/BasicMobileHeaderLogo.vue'
|
||||||
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
|
||||||
import { useUserStore } from '@/store/userStore'
|
import { useUserStore } from '@/store/userStore'
|
||||||
import type { User } from '@/model/user'
|
import type { User } from '@/model/user'
|
||||||
import { createUser, updateMyInformation, updateUser } from '@/api/user'
|
import { updateMyInformation } from '@/api/user'
|
||||||
import { message } from 'ant-design-vue/es'
|
import { message } from 'ant-design-vue/es'
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref, computed } from 'vue'
|
||||||
import type { FormInstance } from 'ant-design-vue'
|
import type { FormInstance } from 'ant-design-vue'
|
||||||
import { cloneDeep, sortBy } from 'lodash'
|
import { cloneDeep } from 'lodash'
|
||||||
import { useWindowSize } from '@vueuse/core'
|
|
||||||
import { computed } from 'vue'
|
|
||||||
import type { ComputedRef } from 'vue'
|
|
||||||
import type { RouteRecordName, RouteRecordRaw } from 'vue-router'
|
|
||||||
import { useRouterStore } from '@core/stores/router'
|
|
||||||
import BasicSubMenu from '@core/layouts/composnts/BasicSubMenu.vue'
|
|
||||||
import BaseMenuItem from '@core/layouts/composnts/BaseMenuItem.vue'
|
|
||||||
import {getConfigApp} from "@/utils/config";
|
|
||||||
|
|
||||||
const { prefixCls } = usePrefix('basic-header')
|
const { prefixCls } = usePrefix('basic-header')
|
||||||
const layoutStore = useLayoutStore()
|
|
||||||
const store = useStaticApiListsStore()
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const settingsStore = useSettingsStore()
|
|
||||||
const currentRoute = useRoute()
|
|
||||||
const selectedKeys = ref<(RouteRecordName | undefined)[]>([])
|
|
||||||
const routerStore = useRouterStore()
|
|
||||||
|
|
||||||
const { width } = useWindowSize()
|
|
||||||
//my info modal
|
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
const myInfoVisible = ref<boolean>(false)
|
const myInfoVisible = ref<boolean>(false)
|
||||||
const formRef = ref<FormInstance>()
|
const formRef = ref<FormInstance>()
|
||||||
@@ -150,45 +101,17 @@ const form = ref<User>({
|
|||||||
user_type: undefined,
|
user_type: undefined,
|
||||||
permission: []
|
permission: []
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const displayName = computed(() => {
|
||||||
|
const info = userStore.userInfo
|
||||||
|
if (info?.name) return info.name
|
||||||
|
return [info?.fname, info?.lname].filter(Boolean).join(' ') || 'کاربر'
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
form.value = cloneDeep(userStore?.userInfo) as User
|
form.value = cloneDeep(userStore?.userInfo) as User
|
||||||
if (width.value <= 426) {
|
|
||||||
layoutStore.layout = 'side'
|
|
||||||
layoutStore.sideMenu.mode = 'vertical'
|
|
||||||
layoutStore.sideMenu.collapsed = false
|
|
||||||
console.log(layoutStore.layout + width.value)
|
|
||||||
}
|
|
||||||
const path: (RouteRecordName | undefined)[] = []
|
|
||||||
|
|
||||||
function walkInRoutes(routes: RouteRecordRaw[]) {
|
|
||||||
for (const route of routes) {
|
|
||||||
if (route.name == currentRoute.name) {
|
|
||||||
path.push(route.name)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (route.children && route.children.length > 0) {
|
|
||||||
path.push(route.name)
|
|
||||||
const res = walkInRoutes(route.children)
|
|
||||||
if (!res) {
|
|
||||||
path.pop()
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
walkInRoutes(routerStore.routes)
|
|
||||||
selectedKeys.value = path
|
|
||||||
})
|
|
||||||
const menu: ComputedRef<RouteRecordRaw[]> = computed(() => {
|
|
||||||
return sortBy(routerStore.routes, (item) => {
|
|
||||||
return item.meta?.order
|
|
||||||
}).filter((item) => !item.meta?.hideMenu)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
//submit user
|
|
||||||
async function ok() {
|
async function ok() {
|
||||||
formRef.value?.validateFields().then(async () => {
|
formRef.value?.validateFields().then(async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@@ -198,7 +121,6 @@ async function ok() {
|
|||||||
message.success('اطلاعات با موفقیت ویرایش شد')
|
message.success('اطلاعات با موفقیت ویرایش شد')
|
||||||
myInfoVisible.value = false
|
myInfoVisible.value = false
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// message.error('خطا در بارگذاری اطلاعات')
|
|
||||||
console.error(e)
|
console.error(e)
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
@@ -206,30 +128,79 @@ async function ok() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//const temp = 12
|
|
||||||
function handleCollapseSideMenu() {
|
|
||||||
layoutStore.sideMenu.collapsed = !layoutStore.sideMenu.collapsed
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleShowSettings() {
|
|
||||||
settingsStore.showSettingsDrawer = !settingsStore.showSettingsDrawer
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearLocalStorage() {
|
function clearLocalStorage() {
|
||||||
localStorage.clear()
|
localStorage.clear()
|
||||||
location.reload()
|
location.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
const open = ref<boolean>(false)
|
function handleRefresh() {
|
||||||
|
window.location.reload()
|
||||||
const showDrawer = () => {
|
|
||||||
open.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const onClose = () => {
|
|
||||||
open.value = false
|
|
||||||
console.log(open.value)
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less"></style>
|
<style scoped lang="less">
|
||||||
|
@prefix: ~'@{prefixCls}-basic-header';
|
||||||
|
|
||||||
|
.@{prefix} {
|
||||||
|
padding: 12px 16px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-header-bar {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-header-bar__name {
|
||||||
|
max-width: 34%;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f1f1f;
|
||||||
|
line-height: 1.4;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-header-bar__logo {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-header-bar__actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-header-bar__icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: #292d32;
|
||||||
|
cursor: pointer;
|
||||||
|
line-height: 0;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<template>
|
||||||
|
<img :src="getConfigApp('avatar')" alt="لوگو" class="mobile-header-logo" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { getConfigApp } from '@/utils/config'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.mobile-header-logo {
|
||||||
|
height: 30px;
|
||||||
|
width: auto;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="menuRef" class="mobile-overflow-menu">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="mobile-overflow-menu__trigger"
|
||||||
|
:class="{ 'mobile-overflow-menu__trigger--active': open }"
|
||||||
|
@click="toggleMenu"
|
||||||
|
>
|
||||||
|
<span class="mobile-overflow-menu__icon-wrap">
|
||||||
|
<BaseIcon icon="vuesax-linear:menu" :size="menuIconSize" />
|
||||||
|
</span>
|
||||||
|
<span class="mobile-overflow-menu__label">منو</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div v-if="open" class="mobile-overflow-menu__panel">
|
||||||
|
<button
|
||||||
|
v-for="item in items"
|
||||||
|
:key="String(item.name)"
|
||||||
|
type="button"
|
||||||
|
class="mobile-overflow-menu__panel-item"
|
||||||
|
@click="handleSelect(item)"
|
||||||
|
>
|
||||||
|
<span class="mobile-overflow-menu__panel-label">{{ item.meta?.title ?? item.name }}</span>
|
||||||
|
<BaseIcon
|
||||||
|
v-if="item.meta?.icon"
|
||||||
|
:icon="item.meta.icon as string"
|
||||||
|
:size="menuIconSize"
|
||||||
|
class="mobile-overflow-menu__panel-icon"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import BaseIcon from '@core/components/BaseIcon'
|
||||||
|
import { menuIconSize } from '@root/config'
|
||||||
|
import { onClickOutside } from '@vueuse/core'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
items: RouteRecordRaw[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'select', item: RouteRecordRaw): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const open = ref(false)
|
||||||
|
const menuRef = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
|
onClickOutside(menuRef, () => {
|
||||||
|
open.value = false
|
||||||
|
})
|
||||||
|
|
||||||
|
function toggleMenu() {
|
||||||
|
open.value = !open.value
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSelect(item: RouteRecordRaw) {
|
||||||
|
open.value = false
|
||||||
|
emit('select', item)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.mobile-overflow-menu {
|
||||||
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-overflow-menu__trigger {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4px;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 6px 4px 8px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
color: #595959;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-overflow-menu__trigger--active {
|
||||||
|
color: #1677ff;
|
||||||
|
|
||||||
|
.mobile-overflow-menu__icon-wrap {
|
||||||
|
background: #e6f4ff;
|
||||||
|
color: #1677ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 28px;
|
||||||
|
height: 3px;
|
||||||
|
border-radius: 3px 3px 0 0;
|
||||||
|
background: #1677ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-overflow-menu__icon-wrap {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 10px;
|
||||||
|
color: #292d32;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-overflow-menu__label {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-overflow-menu__panel {
|
||||||
|
position: absolute;
|
||||||
|
bottom: calc(100% + 12px);
|
||||||
|
left: 8px;
|
||||||
|
min-width: 220px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 10px 22px rgba(0, 0, 0, 0.12);
|
||||||
|
padding: 10px;
|
||||||
|
z-index: 210;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-overflow-menu__panel-item {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 10px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px 8px;
|
||||||
|
color: #262626;
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: #e9e9e9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-overflow-menu__panel-icon {
|
||||||
|
color: #262626;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="py-2 pr-3 w-full h-full relative">
|
<div class="relative flex h-full min-h-0 min-w-0 w-full max-w-full flex-col overflow-hidden py-2 pr-3">
|
||||||
<div class="text-black font-bold">{{ props.title }}</div>
|
<div class="flex shrink-0 min-w-0 items-center justify-between gap-2">
|
||||||
<div class="mt-2 overflow-y-auto p-1 h-[96%]">
|
<div class="truncate text-black font-bold">{{ props.title }}</div>
|
||||||
|
<slot name="extra" />
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 min-h-0 flex-1 overflow-hidden p-1">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -13,5 +16,3 @@ interface Prop {
|
|||||||
}
|
}
|
||||||
const props = defineProps<Prop>()
|
const props = defineProps<Prop>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<template>
|
||||||
|
<article
|
||||||
|
class="rounded-2xl border border-gray-200 bg-white shadow-sm"
|
||||||
|
:class="[paddingClass, clickable ? 'cursor-pointer' : '']"
|
||||||
|
@click="clickable ? $emit('click') : undefined"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</article>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
clickable?: boolean
|
||||||
|
compact?: boolean
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
clickable: false,
|
||||||
|
compact: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
(e: 'click'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const paddingClass = computed(() => (props.compact ? 'px-3 py-3' : 'p-4'))
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
<template>
|
||||||
|
<div class="min-w-0 h-full max-w-full overflow-x-hidden overflow-y-auto space-y-3 md:hidden">
|
||||||
|
<div v-if="$slots.toolbar" class="flex">
|
||||||
|
<slot name="toolbar" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="showSearch" class="flex items-center gap-2">
|
||||||
|
<a-input
|
||||||
|
v-model:value="searchModel"
|
||||||
|
:placeholder="searchPlaceholder"
|
||||||
|
class="w-full flex-1"
|
||||||
|
@keyup.enter="$emit('search')"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<BaseIcon icon="vuesax-linear:search-normal" size="18" color="#9ca3af" />
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
<BaseButton v-if="showRefresh" type="text" icon="vuesax-linear:refresh" iconSize="18" @click="$emit('refresh')" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-spin :spinning="loading" tip="در حال دریافت اطلاعات...">
|
||||||
|
<div class="space-y-2">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="showEmpty"
|
||||||
|
class="rounded-2xl border border-dashed border-gray-300 bg-gray-50 px-3 py-6 text-center text-sm text-gray-500"
|
||||||
|
>
|
||||||
|
{{ emptyText }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="hasMore" class="mt-3 flex justify-center">
|
||||||
|
<BaseButton size="small" @click="$emit('loadMore')">بارگذاری بیشتر</BaseButton>
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
|
||||||
|
<slot name="footer" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
loading: boolean
|
||||||
|
hasMore: boolean
|
||||||
|
emptyText?: string
|
||||||
|
showEmpty?: boolean
|
||||||
|
search?: string
|
||||||
|
searchPlaceholder?: string
|
||||||
|
showSearch?: boolean
|
||||||
|
showRefresh?: boolean
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
emptyText: 'موردی یافت نشد',
|
||||||
|
showEmpty: false,
|
||||||
|
searchPlaceholder: 'جستجو...',
|
||||||
|
showSearch: true,
|
||||||
|
showRefresh: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'search'): void
|
||||||
|
(e: 'refresh'): void
|
||||||
|
(e: 'loadMore'): void
|
||||||
|
(e: 'update:search', value: string): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const searchModel = computed({
|
||||||
|
get: () => props.search ?? '',
|
||||||
|
set: (value: string) => emit('update:search', value)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex justify-between rounded-lg border px-3 py-2" :class="rowClass">
|
||||||
|
<div class="text-xs" :class="labelClass">{{ label }}</div>
|
||||||
|
<div class="truncate font-700 mr-1" :class="valueClass">
|
||||||
|
<slot>{{ value }}</slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
label: string
|
||||||
|
value?: string | number
|
||||||
|
variant?: 'neutral' | 'success' | 'warning' | 'danger'
|
||||||
|
fullWidth?: boolean
|
||||||
|
valueClass?: string
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
variant: 'neutral',
|
||||||
|
fullWidth: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const rowClass = computed(() => {
|
||||||
|
const span = props.fullWidth ? 'col-span-2' : ''
|
||||||
|
const variants: Record<string, string> = {
|
||||||
|
neutral: 'border-gray-200 bg-gray-50',
|
||||||
|
success: 'border-green-200 bg-green-50',
|
||||||
|
warning: 'border-amber-200 bg-amber-50',
|
||||||
|
danger: 'border-red-200 bg-red-50'
|
||||||
|
}
|
||||||
|
return `${span} ${variants[props.variant]}`
|
||||||
|
})
|
||||||
|
|
||||||
|
const labelClass = computed(() => {
|
||||||
|
if (props.variant === 'success') return 'text-green-700'
|
||||||
|
if (props.variant === 'warning') return 'text-amber-700'
|
||||||
|
if (props.variant === 'danger') return 'text-red-700'
|
||||||
|
return 'text-gray-500'
|
||||||
|
})
|
||||||
|
|
||||||
|
const valueClass = computed(() => {
|
||||||
|
if (props.valueClass) return props.valueClass
|
||||||
|
if (props.variant === 'success') return 'text-green-700'
|
||||||
|
if (props.variant === 'warning') return 'text-amber-700'
|
||||||
|
if (props.variant === 'danger') return 'text-red-600'
|
||||||
|
return 'text-gray-900'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<template>
|
||||||
|
<BasicActionSheet
|
||||||
|
v-model:visible="visibleModel"
|
||||||
|
:title="title"
|
||||||
|
:showButton="false"
|
||||||
|
:drawerHeight="drawerHeight"
|
||||||
|
>
|
||||||
|
<div v-if="item" class="space-y-3 pb-2">
|
||||||
|
<slot :item="item" />
|
||||||
|
</div>
|
||||||
|
</BasicActionSheet>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" generic="T">
|
||||||
|
import { ACTION_SHEET_HEIGHT } from '@/constants/actionSheet'
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
visible: boolean
|
||||||
|
item: T | null
|
||||||
|
title?: string
|
||||||
|
drawerHeight?: string
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
drawerHeight: ACTION_SHEET_HEIGHT
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'update:visible', value: boolean): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const visibleModel = computed({
|
||||||
|
get: () => props.visible,
|
||||||
|
set: (value: boolean) => emit('update:visible', value)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<template>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="flex shrink-0 items-center justify-center transition-colors"
|
||||||
|
:class="
|
||||||
|
plain
|
||||||
|
? 'h-auto w-auto border-none bg-transparent p-0'
|
||||||
|
: 'h-9 w-9 rounded-full border-none bg-white active:bg-gray-50'
|
||||||
|
"
|
||||||
|
@click.stop="$emit('click')"
|
||||||
|
>
|
||||||
|
<BaseIcon :icon="icon" :size="size" :color="color" />
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
icon: string
|
||||||
|
color?: string
|
||||||
|
size?: number
|
||||||
|
plain?: boolean
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
color: '#374151',
|
||||||
|
size: 18,
|
||||||
|
plain: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
(e: 'click'): void
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<div class="min-w-0">
|
||||||
|
<p class="text-xs text-gray-500">{{ label }}</p>
|
||||||
|
<p class="truncate text-sm font-medium text-gray-900" :class="valueClass">
|
||||||
|
<slot>{{ value }}</slot>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{
|
||||||
|
label: string
|
||||||
|
value?: string | number
|
||||||
|
valueClass?: string
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<div class="grid gap-3" :class="gridClass">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
cols?: 1 | 2
|
||||||
|
}>(),
|
||||||
|
{ cols: 2 }
|
||||||
|
)
|
||||||
|
|
||||||
|
const gridClass = computed(() => (props.cols === 1 ? 'grid-cols-1' : 'grid-cols-2'))
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<template>
|
||||||
|
<div class="space-y-3">
|
||||||
|
<div class="min-w-0 space-y-1">
|
||||||
|
<div v-if="showTitle" class="text-lg font-700 text-gray-900">
|
||||||
|
<slot name="title">{{ title }}</slot>
|
||||||
|
</div>
|
||||||
|
<div v-if="subtitle || $slots.subtitle" class="text-xs text-gray-500">
|
||||||
|
<slot name="subtitle">{{ subtitle }}</slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
title?: string
|
||||||
|
subtitle?: string
|
||||||
|
showTitle?: boolean
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
showTitle: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex items-center justify-between px-4 py-3" :class="containerClass">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<slot name="prefix" />
|
||||||
|
<span class="font-700" :class="[labelClass, $slots.prefix ? 'mr-2' : '']">{{ label }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="font-800" :class="valueClass">
|
||||||
|
<slot>{{ value }}</slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
label: string
|
||||||
|
value?: string | number
|
||||||
|
variant?: 'success' | 'danger' | 'neutral'
|
||||||
|
bordered?: boolean
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
variant: 'neutral',
|
||||||
|
bordered: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const containerClass = computed(() => {
|
||||||
|
const radius = props.bordered ? 'rounded-xl border' : 'rounded-xl'
|
||||||
|
if (props.variant === 'success') return `${radius} border-green-200 bg-green-50`
|
||||||
|
if (props.variant === 'danger') return `${radius} border-red-200 bg-red-50`
|
||||||
|
return `${radius} border-gray-200 bg-gray-50`
|
||||||
|
})
|
||||||
|
|
||||||
|
const labelClass = computed(() => {
|
||||||
|
if (props.variant === 'success') return 'text-green-700'
|
||||||
|
if (props.variant === 'danger') return 'text-red-600'
|
||||||
|
return 'text-gray-500'
|
||||||
|
})
|
||||||
|
|
||||||
|
const valueClass = computed(() => {
|
||||||
|
if (props.variant === 'success') return 'text-lg text-green-700'
|
||||||
|
if (props.variant === 'danger') return 'text-lg text-red-600'
|
||||||
|
return 'text-gray-900'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
export function useMobileDetailSheet<T extends { id?: number }>() {
|
||||||
|
const sheetVisible = ref(false)
|
||||||
|
const selectedItem = ref<T | null>(null)
|
||||||
|
|
||||||
|
function openSheet(item: T) {
|
||||||
|
selectedItem.value = item
|
||||||
|
sheetVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncSelectedItem(items: T[]) {
|
||||||
|
if (!selectedItem.value?.id) return
|
||||||
|
const updated = items.find((item) => item.id === selectedItem.value?.id)
|
||||||
|
if (updated) selectedItem.value = updated
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
sheetVisible,
|
||||||
|
selectedItem,
|
||||||
|
openSheet,
|
||||||
|
syncSelectedItem
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import type { Ref } from 'vue'
|
||||||
|
import type { TableInput, TableOutput } from '@core/type/baseTable'
|
||||||
|
|
||||||
|
interface Options<T> {
|
||||||
|
fetchApi: (params: TableInput) => Promise<TableOutput<T>>
|
||||||
|
onFetched?: (items: T[]) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useMobileTableList<T extends { id?: number }>(options: Options<T>) {
|
||||||
|
const loading = ref(false)
|
||||||
|
const search = ref('')
|
||||||
|
const query = ref<TableInput>({ page: 1, limit: 10, select: [] })
|
||||||
|
const total = ref(0)
|
||||||
|
const items = ref<T[]>([]) as Ref<T[]>
|
||||||
|
|
||||||
|
const hasMore = computed(() => items.value.length < total.value)
|
||||||
|
|
||||||
|
async function fetchList(append = false) {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const params: TableInput = {
|
||||||
|
...query.value,
|
||||||
|
search: search.value || null
|
||||||
|
}
|
||||||
|
const res = await options.fetchApi(params)
|
||||||
|
query.value = res.query
|
||||||
|
total.value = res.query.total ?? 0
|
||||||
|
items.value = append ? [...items.value, ...res.rows] : res.rows
|
||||||
|
options.onFetched?.(items.value)
|
||||||
|
} catch {
|
||||||
|
total.value = 0
|
||||||
|
items.value = []
|
||||||
|
options.onFetched?.([])
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function reload() {
|
||||||
|
query.value.page = 1
|
||||||
|
await fetchList(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadMore() {
|
||||||
|
query.value.page = (query.value.page ?? 1) + 1
|
||||||
|
await fetchList(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
loading,
|
||||||
|
search,
|
||||||
|
items,
|
||||||
|
hasMore,
|
||||||
|
reload,
|
||||||
|
loadMore,
|
||||||
|
fetch: reload
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import type { Wallet } from '@/model/wallet'
|
||||||
|
|
||||||
|
export function getWallets(wallet?: Wallet | Wallet[]): Wallet[] {
|
||||||
|
if (!wallet) return []
|
||||||
|
return Array.isArray(wallet) ? wallet : [wallet]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCashBalance(wallet?: Wallet | Wallet[]): number {
|
||||||
|
return getWallets(wallet)[0]?.cash ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getGoldBalance(wallet?: Wallet | Wallet[]): string {
|
||||||
|
const sum = getWallets(wallet).reduce((total, item) => total + (item.gold ?? 0), 0)
|
||||||
|
return sum.toFixed(3)
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
/** ارتفاع یکسان برای تمام bottom action sheet ها */
|
||||||
|
export const ACTION_SHEET_HEIGHT = '80vh'
|
||||||
@@ -47,6 +47,11 @@
|
|||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-drawer-bottom .ant-drawer-content {
|
||||||
|
border-radius: 16px 16px 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.ant-card-body{
|
.ant-card-body{
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
//ok Code
|
html,
|
||||||
/*html {
|
body,
|
||||||
overflow: scroll;
|
#app {
|
||||||
|
max-width: 100%;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}*/
|
}
|
||||||
|
|
||||||
.scroll-none{
|
.scroll-none{
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
|
|||||||
@@ -1,20 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="[prefixCls]">
|
<div :class="[prefixCls, 'min-w-0 max-w-full']">
|
||||||
<ASpin tip="در حال دریافت اطلاعات..." :indicator="indicator" :spinning="loading">
|
<ASpin tip="در حال دریافت اطلاعات..." :indicator="indicator" :spinning="loading">
|
||||||
<!-- <div ref="skeletonWidth" class="w-full"></div>-->
|
|
||||||
<!-- <transition>-->
|
|
||||||
<!-- TODO Check -->
|
|
||||||
<!-- relative h-full-->
|
|
||||||
<div class="scrollable-container" id="components-affix-demo-target" ref="containerRef">
|
<div class="scrollable-container" id="components-affix-demo-target" ref="containerRef">
|
||||||
<!--- TODO remove AFFix -->
|
<div class="table-toolbar sticky top-0 z-10 mb-2 shrink-0 bg-white">
|
||||||
<AAffix :target="() => containerRef">
|
<div class="flex flex-col gap-2 xl:flex-row xl:items-center xl:justify-between">
|
||||||
<div class="flex items-center justify-between mb-2">
|
<div
|
||||||
<div class="flex items-center gap-1" v-if="showOptionHeader">
|
v-if="showOptionHeader"
|
||||||
<!-- <AInputSearch placeholder="جستجو" enter-button />-->
|
class="flex min-w-0 flex-1 flex-wrap items-center gap-2"
|
||||||
|
>
|
||||||
<AInput
|
<AInput
|
||||||
@keyup.enter="handleSearchText"
|
@keyup.enter="handleSearchText"
|
||||||
placeholder="جستجو"
|
placeholder="جستجو"
|
||||||
class="pl-1"
|
class="table-toolbar-search min-w-[11rem] flex-1 basis-52 sm:max-w-md"
|
||||||
v-model:value="searchText"
|
v-model:value="searchText"
|
||||||
>
|
>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
@@ -26,7 +23,7 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</AInput>
|
</AInput>
|
||||||
<div class="flex flex-wrap md:flex-nowrap gap-1 items-center">
|
<div class="flex shrink-0 flex-wrap items-center gap-1">
|
||||||
<BaseButton
|
<BaseButton
|
||||||
type="text"
|
type="text"
|
||||||
class="text-gray-500"
|
class="text-gray-500"
|
||||||
@@ -79,11 +76,12 @@
|
|||||||
</BaseButton>
|
</BaseButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div v-if="$slots.extraHeader" class="w-full min-w-0 shrink-0 xl:w-auto xl:max-w-[55%]">
|
||||||
<slot name="extraHeader" />
|
<slot name="extraHeader" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</AAffix>
|
</div>
|
||||||
|
<div class="table-body min-h-0 min-w-0 flex-1 overflow-auto">
|
||||||
<ATable
|
<ATable
|
||||||
:columns="_columns"
|
:columns="_columns"
|
||||||
:data-source="data"
|
:data-source="data"
|
||||||
@@ -100,9 +98,8 @@
|
|||||||
</template>
|
</template>
|
||||||
<!-- <template #loading> sdfsdfsdfsd </template>-->
|
<!-- <template #loading> sdfsdfsdfsd </template>-->
|
||||||
</ATable>
|
</ATable>
|
||||||
<!-- absolute bottom-0 w-full-->
|
</div>
|
||||||
<!-- absolute bottom-0-->
|
<div class="table-footer mt-5 flex w-full shrink-0 items-center justify-between" v-if="showPaginate">
|
||||||
<div class="flex items-center justify-between mt-5 w-full" v-if="showPaginate">
|
|
||||||
<span> {{ totalRecord }} مورد یافت شد </span>
|
<span> {{ totalRecord }} مورد یافت شد </span>
|
||||||
<div class="flex items-center gap-2 justify-between">
|
<div class="flex items-center gap-2 justify-between">
|
||||||
<APagination
|
<APagination
|
||||||
@@ -683,13 +680,47 @@ defineExpose({
|
|||||||
@prefix:~ '@{prefixCls}-base-table';
|
@prefix:~ '@{prefixCls}-base-table';
|
||||||
|
|
||||||
.@{prefix} {
|
.@{prefix} {
|
||||||
@apply h-full;
|
@apply h-full max-w-full flex flex-col min-h-0 min-w-0;
|
||||||
|
|
||||||
|
.scrollable-container {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
max-width: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-body {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-footer {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-toolbar-search {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-toolbar-extra:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.ant-table-wrapper {
|
.ant-table-wrapper {
|
||||||
border: 1px solid #eee;
|
border: 1px solid #eee;
|
||||||
border-radius: var(--border-radius-base);
|
border-radius: var(--border-radius-base);
|
||||||
}
|
}
|
||||||
.ant-spin-container,
|
.ant-spin-container,
|
||||||
.ant-spin-nested-loading {
|
.ant-spin-nested-loading {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@apply relative;
|
@apply relative;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -663,13 +663,24 @@ defineExpose({
|
|||||||
@prefix:~ '@{prefixCls}-base-table';
|
@prefix:~ '@{prefixCls}-base-table';
|
||||||
|
|
||||||
.@{prefix} {
|
.@{prefix} {
|
||||||
@apply h-full;
|
@apply h-full max-w-full flex flex-col min-h-0;
|
||||||
|
.scrollable-container {
|
||||||
|
max-width: 100%;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
.ant-table-wrapper {
|
.ant-table-wrapper {
|
||||||
border: 1px solid #eee;
|
border: 1px solid #eee;
|
||||||
border-radius: var(--border-radius-base);
|
border-radius: var(--border-radius-base);
|
||||||
}
|
}
|
||||||
.ant-spin-container,
|
.ant-spin-container,
|
||||||
.ant-spin-nested-loading {
|
.ant-spin-nested-loading {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@apply relative;
|
@apply relative;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,23 +5,21 @@
|
|||||||
:style="`height:${layoutStore.header.height};line-height:${layoutStore.header.height}`"
|
:style="`height:${layoutStore.header.height};line-height:${layoutStore.header.height}`"
|
||||||
>
|
>
|
||||||
<!-- :style="`height:${layoutStore.header.height};line-height:${layoutStore.header.height}`"-->
|
<!-- :style="`height:${layoutStore.header.height};line-height:${layoutStore.header.height}`"-->
|
||||||
<div class="flex items-center">
|
<div class="flex w-full min-w-0 items-center overflow-hidden">
|
||||||
<div class="flex-1 flex">
|
<div class="flex min-w-0 flex-1 items-center overflow-hidden">
|
||||||
<img :src="getConfigApp('avatar')" style="height: 38px" class="mr-5 mt-1" />
|
<img :src="getConfigApp('avatar')" style="height: 38px" class="mr-5 mt-1 shrink-0" />
|
||||||
<BaseButton
|
<BaseButton
|
||||||
type="text"
|
type="text"
|
||||||
icon="vuesax-linear:menu"
|
icon="vuesax-linear:menu"
|
||||||
@click="handleCollapseSideMenu"
|
@click="handleCollapseSideMenu"
|
||||||
v-if="layoutStore.layout == 'side'"
|
v-if="layoutStore.layout == 'side'"
|
||||||
|
class="shrink-0"
|
||||||
/>
|
/>
|
||||||
<!-- justify-center flex-1 -->
|
|
||||||
<!-- <div class="">-->
|
|
||||||
<BasicMenu />
|
<BasicMenu />
|
||||||
<!-- </div>-->
|
|
||||||
</div>
|
</div>
|
||||||
<!-- <BaseButton icon="vuesax-linear:menu" @click="handleCollapseSideMenu"></BaseButton>-->
|
<!-- <BaseButton icon="vuesax-linear:menu" @click="handleCollapseSideMenu"></BaseButton>-->
|
||||||
<!-- <div @click="handleCollapseSideMenu">بستن منو</div>-->
|
<!-- <div @click="handleCollapseSideMenu">بستن منو</div>-->
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex shrink-0 items-center gap-3">
|
||||||
<!-- <div>
|
<!-- <div>
|
||||||
<BaseButton size="small" @click="openNewTabBalance">تراز</BaseButton>
|
<BaseButton size="small" @click="openNewTabBalance">تراز</BaseButton>
|
||||||
</div>-->
|
</div>-->
|
||||||
@@ -235,6 +233,8 @@ function clearLocalStorage() {
|
|||||||
|
|
||||||
.@{prefix} {
|
.@{prefix} {
|
||||||
padding: unset;
|
padding: unset;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 100%;
|
||||||
//height: unset;
|
//height: unset;
|
||||||
//@apply flex flex-col justify-center ;
|
//@apply flex flex-col justify-center ;
|
||||||
//height: 64px;
|
//height: 64px;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 'admin-layout', 'beauty-scroll'-->
|
<!-- 'admin-layout', 'beauty-scroll'-->
|
||||||
<ALayout
|
<ALayout
|
||||||
:class="[prefixCls, { 'overflow-y-scroll': !layoutStore.header.fixed }]"
|
:class="[prefixCls]"
|
||||||
class="h-screen"
|
class="h-screen max-w-[100vw] overflow-hidden"
|
||||||
>
|
>
|
||||||
<!-- v-model="drawerOpen"-->
|
<!-- v-model="drawerOpen"-->
|
||||||
<ADrawer v-if="isMobile">
|
<ADrawer v-if="isMobile">
|
||||||
@@ -36,10 +36,10 @@
|
|||||||
<!-- <setting />-->
|
<!-- <setting />-->
|
||||||
</ADrawer>
|
</ADrawer>
|
||||||
<!-- class="admin-layout-main beauty-scroll"-->
|
<!-- class="admin-layout-main beauty-scroll"-->
|
||||||
<ALayout>
|
<ALayout class="flex min-h-0 min-w-0 max-w-full flex-1 flex-col overflow-hidden">
|
||||||
<!-- 111111 this is header-->
|
<!-- 111111 this is header-->
|
||||||
<BasicHeader class="hidden md:block"></BasicHeader>
|
<BasicHeader v-if="!isMobile"></BasicHeader>
|
||||||
<BasicMobileHeader class="md:!hidden"></BasicMobileHeader>
|
<BasicMobileHeader v-else></BasicMobileHeader>
|
||||||
<!-- <admin-header
|
<!-- <admin-header
|
||||||
:class="[{ 'fixed-tabs': fixedTabs, 'fixed-header': fixedHeader, 'multi-page': multiPage }]"
|
:class="[{ 'fixed-tabs': fixedTabs, 'fixed-header': fixedHeader, 'multi-page': multiPage }]"
|
||||||
:style="headerStyle"
|
:style="headerStyle"
|
||||||
@@ -55,10 +55,7 @@
|
|||||||
v-show="fixedHeader"
|
v-show="fixedHeader"
|
||||||
></ALayoutHeader>-->
|
></ALayoutHeader>-->
|
||||||
<!-- :style="`min-height: ${minHeight}px;`"-->
|
<!-- :style="`min-height: ${minHeight}px;`"-->
|
||||||
<ALayoutContent
|
<ALayoutContent class="admin-layout-content mobile-layout-content">
|
||||||
class="admin-layout-content"
|
|
||||||
:class="[{ 'overflow-y-scroll': layoutStore.header.fixed }]"
|
|
||||||
>
|
|
||||||
<!-- :style="{`min-height: ${minHeight}px;`}"-->
|
<!-- :style="{`min-height: ${minHeight}px;`}"-->
|
||||||
<!-- this is content-->
|
<!-- this is content-->
|
||||||
<!-- <Btn1></Btn1>-->
|
<!-- <Btn1></Btn1>-->
|
||||||
@@ -82,7 +79,7 @@
|
|||||||
<RouterView v-slot="{ Component }">
|
<RouterView v-slot="{ Component }">
|
||||||
<!-- mode="out-in"-->
|
<!-- mode="out-in"-->
|
||||||
<!-- <transition>-->
|
<!-- <transition>-->
|
||||||
<component :is="Component" />
|
<component :is="Component" class="h-full min-h-0" />
|
||||||
<!-- </transition>-->
|
<!-- </transition>-->
|
||||||
</RouterView>
|
</RouterView>
|
||||||
</ALayoutContent>
|
</ALayoutContent>
|
||||||
@@ -91,6 +88,7 @@
|
|||||||
<!– <page-footer :link-list="footerLinks" :copyright="copyright" />–>
|
<!– <page-footer :link-list="footerLinks" :copyright="copyright" />–>
|
||||||
</LayoutFooter>-->
|
</LayoutFooter>-->
|
||||||
</ALayout>
|
</ALayout>
|
||||||
|
<BasicMobileBottomNav v-if="isMobile" />
|
||||||
<BaseSettings />
|
<BaseSettings />
|
||||||
</ALayout>
|
</ALayout>
|
||||||
</template>
|
</template>
|
||||||
@@ -102,7 +100,8 @@ import { usePrefix } from '@core/composable/usePrefix'
|
|||||||
import { useLayoutStore } from '@core/stores/layout'
|
import { useLayoutStore } from '@core/stores/layout'
|
||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
import BaseSettings from '@core/layouts/BaseSettings.vue'
|
import BaseSettings from '@core/layouts/BaseSettings.vue'
|
||||||
import BasicMobileheader from "@/components/BasicMobileHeader.vue";
|
import BasicMobileHeader from '@/components/BasicMobileHeader.vue'
|
||||||
|
import BasicMobileBottomNav from '@/components/BasicMobileBottomNav.vue'
|
||||||
const { prefixCls } = usePrefix('basic-layout')
|
const { prefixCls } = usePrefix('basic-layout')
|
||||||
const layoutStore = useLayoutStore()
|
const layoutStore = useLayoutStore()
|
||||||
</script>
|
</script>
|
||||||
@@ -115,6 +114,21 @@ const layoutStore = useLayoutStore()
|
|||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.admin-layout-content {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.mobile-layout-content {
|
||||||
|
padding-bottom: calc(72px + env(safe-area-inset-bottom));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* .fade-enter-active,
|
/* .fade-enter-active,
|
||||||
.fade-leave-active {
|
.fade-leave-active {
|
||||||
transition: opacity 0.5s ease;
|
transition: opacity 0.5s ease;
|
||||||
|
|||||||
@@ -1,8 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div
|
||||||
<div :class="[prefixCls, `${layoutStore.layout}-menu`]">
|
:class="[
|
||||||
<!-- :selectedKeys="selectedKeys"-->
|
prefixCls,
|
||||||
<AMenu :mode="layoutStore.sideMenu.mode" v-model:selectedKeys="selectedKeys" >
|
layoutStore.layout === 'top'
|
||||||
|
? 'top-menu-nav flex min-w-0 flex-1 items-center'
|
||||||
|
: `${layoutStore.layout}-menu`
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
ref="menuScrollRef"
|
||||||
|
:class="[isTopMenu ? 'top-menu top-menu-scroll scroll-none min-w-0 flex-1' : '']"
|
||||||
|
@scroll="updateScrollArrows"
|
||||||
|
>
|
||||||
|
<AMenu
|
||||||
|
:mode="layoutStore.sideMenu.mode"
|
||||||
|
v-model:selectedKeys="selectedKeys"
|
||||||
|
:disabled-overflow="isTopMenu"
|
||||||
|
>
|
||||||
<template v-for="item in menu">
|
<template v-for="item in menu">
|
||||||
<BasicSubMenu
|
<BasicSubMenu
|
||||||
v-if="!item.meta?.hideChildren && item.children && item.children.length > 0"
|
v-if="!item.meta?.hideChildren && item.children && item.children.length > 0"
|
||||||
@@ -10,75 +24,151 @@
|
|||||||
:key="item.name"
|
:key="item.name"
|
||||||
:level="1"
|
:level="1"
|
||||||
/>
|
/>
|
||||||
<!-- <AMenuItem
|
|
||||||
v-else
|
|
||||||
:disabled="item.meta.disabled ?? false"
|
|
||||||
:key="item.name"
|
|
||||||
@click="handleClickMenuItem(item)"
|
|
||||||
>
|
|
||||||
<template #icon>
|
|
||||||
<BaseIcon :size="menuIconSize" v-if="item.meta.icon" :icon="item.meta.icon" />
|
|
||||||
</template>
|
|
||||||
<span>{{ item.meta.title ?? item.name }}</span>
|
|
||||||
<!– <template #title>1111</template>–>
|
|
||||||
<!– <div class="flex"></div>–>
|
|
||||||
</AMenuItem>-->
|
|
||||||
<BaseMenuItem v-else :item="item" :key="item.name" />
|
<BaseMenuItem v-else :item="item" :key="item.name" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
</AMenu>
|
</AMenu>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
v-if="isTopMenu"
|
||||||
|
v-show="showLeftArrow"
|
||||||
|
type="button"
|
||||||
|
class="top-menu-nav__btn top-menu-nav__btn--left"
|
||||||
|
aria-label="مشاهده ادامه منو"
|
||||||
|
@click="scrollMenu('left')"
|
||||||
|
>
|
||||||
|
<BaseIcon icon="vuesax-linear:arrow-left-1" size="18" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
v-if="isTopMenu"
|
||||||
|
v-show="showRightArrow"
|
||||||
|
type="button"
|
||||||
|
class="top-menu-nav__btn top-menu-nav__btn--right"
|
||||||
|
aria-label="بازگشت در منو"
|
||||||
|
@click="scrollMenu('right')"
|
||||||
|
>
|
||||||
|
<BaseIcon icon="vuesax-linear:arrow-right-1" size="18" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {usePrefix} from '@core/composable/usePrefix'
|
import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
||||||
//import { menuIconSize } from '@root/config'
|
|
||||||
import {computed} from 'vue'
|
|
||||||
import type { ComputedRef } from 'vue'
|
import type { ComputedRef } from 'vue'
|
||||||
import { sortBy } from 'lodash'
|
import { sortBy } from 'lodash'
|
||||||
//import { routes } from '@/core/router/routes'
|
import { useResizeObserver } from '@vueuse/core'
|
||||||
|
import { usePrefix } from '@core/composable/usePrefix'
|
||||||
import BasicSubMenu from '@core/layouts/composnts/BasicSubMenu.vue'
|
import BasicSubMenu from '@core/layouts/composnts/BasicSubMenu.vue'
|
||||||
import { useLayoutStore } from '@core/stores/layout'
|
import { useLayoutStore } from '@core/stores/layout'
|
||||||
import BaseMenuItem from '@core/layouts/composnts/BaseMenuItem.vue'
|
import BaseMenuItem from '@core/layouts/composnts/BaseMenuItem.vue'
|
||||||
import type { RouteRecordName, RouteRecordRaw } from 'vue-router'
|
import type { RouteRecordName, RouteRecordRaw } from 'vue-router'
|
||||||
import { useRouterStore } from '@core/stores/router'
|
import { useRouterStore } from '@core/stores/router'
|
||||||
|
|
||||||
|
const SCROLL_STEP = 180
|
||||||
|
const EDGE_THRESHOLD = 2
|
||||||
|
|
||||||
const { prefixCls } = usePrefix('basic-menu')
|
const { prefixCls } = usePrefix('basic-menu')
|
||||||
const layoutStore = useLayoutStore()
|
const layoutStore = useLayoutStore()
|
||||||
const currentRoute = useRoute()
|
const currentRoute = useRoute()
|
||||||
const selectedKeys = ref<(RouteRecordName | undefined)[]>([])
|
const selectedKeys = ref<(RouteRecordName | undefined)[]>([])
|
||||||
const routerStore = useRouterStore()
|
const routerStore = useRouterStore()
|
||||||
/*const selectedKeys = computed(() => {
|
const menuScrollRef = ref<HTMLElement | null>(null)
|
||||||
const path: (RouteRecordName | undefined)[] = []
|
const showLeftArrow = ref(false)
|
||||||
|
const showRightArrow = ref(false)
|
||||||
|
const rtlScrollMode = ref<'ltr' | 'negative' | 'positive'>('ltr')
|
||||||
|
|
||||||
function walkInRoutes(routes: RouteRecordRaw[]) {
|
const isTopMenu = computed(() => layoutStore.layout === 'top')
|
||||||
for (const route of routes) {
|
|
||||||
if (route.name == currentRoute.name) {
|
function detectRtlScrollMode(el: HTMLElement): 'ltr' | 'negative' | 'positive' {
|
||||||
path.push(route.name)
|
if (getComputedStyle(el).direction !== 'rtl') return 'ltr'
|
||||||
return true
|
|
||||||
}
|
const max = el.scrollWidth - el.clientWidth
|
||||||
if (route.children && route.children.length > 0) {
|
if (max <= 0) return 'ltr'
|
||||||
path.push(route.name)
|
|
||||||
const res = walkInRoutes(route.children)
|
const original = el.scrollLeft
|
||||||
if (!res) {
|
el.scrollLeft = 1
|
||||||
path.pop()
|
const mode = el.scrollLeft === 0 ? 'positive' : 'negative'
|
||||||
} else {
|
el.scrollLeft = original
|
||||||
return true
|
return mode
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
walkInRoutes(routes)
|
function ensureRtlScrollMode(el: HTMLElement) {
|
||||||
|
if (rtlScrollMode.value !== 'ltr') return
|
||||||
|
rtlScrollMode.value = detectRtlScrollMode(el)
|
||||||
|
}
|
||||||
|
|
||||||
return path
|
function getNormalizedScroll(el: HTMLElement) {
|
||||||
})*/
|
const max = Math.max(0, el.scrollWidth - el.clientWidth)
|
||||||
|
if (max <= 1) return { max: 0, current: 0 }
|
||||||
|
|
||||||
|
ensureRtlScrollMode(el)
|
||||||
|
|
||||||
|
let current = el.scrollLeft
|
||||||
|
if (rtlScrollMode.value === 'negative') {
|
||||||
|
current = Math.abs(current)
|
||||||
|
} else if (rtlScrollMode.value === 'positive') {
|
||||||
|
current = max - current
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
max,
|
||||||
|
current: Math.max(0, Math.min(max, current))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNormalizedScroll(el: HTMLElement, normalized: number) {
|
||||||
|
const max = Math.max(0, el.scrollWidth - el.clientWidth)
|
||||||
|
const value = Math.max(0, Math.min(max, normalized))
|
||||||
|
|
||||||
|
if (rtlScrollMode.value === 'negative') {
|
||||||
|
el.scrollTo({ left: -value, behavior: 'smooth' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rtlScrollMode.value === 'positive') {
|
||||||
|
el.scrollTo({ left: max - value, behavior: 'smooth' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
el.scrollTo({ left: value, behavior: 'smooth' })
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateScrollArrows() {
|
||||||
|
const el = menuScrollRef.value
|
||||||
|
if (!el || !isTopMenu.value) {
|
||||||
|
showLeftArrow.value = false
|
||||||
|
showRightArrow.value = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { max, current } = getNormalizedScroll(el)
|
||||||
|
if (max <= 1) {
|
||||||
|
showLeftArrow.value = false
|
||||||
|
showRightArrow.value = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
showLeftArrow.value = current < max - EDGE_THRESHOLD
|
||||||
|
showRightArrow.value = current > EDGE_THRESHOLD
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollMenu(side: 'left' | 'right') {
|
||||||
|
const el = menuScrollRef.value
|
||||||
|
if (!el) return
|
||||||
|
|
||||||
|
const { max, current } = getNormalizedScroll(el)
|
||||||
|
if (max <= 1) return
|
||||||
|
|
||||||
|
setNormalizedScroll(el, side === 'left' ? current + SCROLL_STEP : current - SCROLL_STEP)
|
||||||
|
scheduleArrowUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
function scheduleArrowUpdate() {
|
||||||
|
window.setTimeout(updateScrollArrows, 350)
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
//console.log('aaa')
|
|
||||||
|
|
||||||
const path: (RouteRecordName | undefined)[] = []
|
const path: (RouteRecordName | undefined)[] = []
|
||||||
|
|
||||||
function walkInRoutes(routes: RouteRecordRaw[]) {
|
function walkInRoutes(routes: RouteRecordRaw[]) {
|
||||||
@@ -103,8 +193,7 @@ onMounted(() => {
|
|||||||
walkInRoutes(routerStore.routes)
|
walkInRoutes(routerStore.routes)
|
||||||
selectedKeys.value = path
|
selectedKeys.value = path
|
||||||
|
|
||||||
// return path
|
nextTick(updateScrollArrows)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const menu: ComputedRef<RouteRecordRaw[]> = computed(() => {
|
const menu: ComputedRef<RouteRecordRaw[]> = computed(() => {
|
||||||
@@ -112,33 +201,100 @@ const menu: ComputedRef<RouteRecordRaw[]> = computed(() => {
|
|||||||
return item.meta?.order
|
return item.meta?.order
|
||||||
}).filter((item) => !item.meta?.hideMenu)
|
}).filter((item) => !item.meta?.hideMenu)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(menu, () => nextTick(updateScrollArrows), { deep: true })
|
||||||
|
watch(isTopMenu, () => {
|
||||||
|
rtlScrollMode.value = 'ltr'
|
||||||
|
nextTick(updateScrollArrows)
|
||||||
|
})
|
||||||
|
watch(
|
||||||
|
() => currentRoute.name,
|
||||||
|
() => nextTick(updateScrollArrows)
|
||||||
|
)
|
||||||
|
|
||||||
|
useResizeObserver(menuScrollRef, () => {
|
||||||
|
updateScrollArrows()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
@prefix: ~ '@{prefixCls}-basic-menu';
|
@prefix: ~ '@{prefixCls}-basic-menu';
|
||||||
.@{prefix} {
|
.@{prefix} {
|
||||||
|
&.top-menu-nav {
|
||||||
&.top-menu {
|
position: relative;
|
||||||
@apply pr-5;
|
min-width: 0;
|
||||||
//display: inline-flex;
|
flex: 1;
|
||||||
.ant-menu {
|
|
||||||
// @apply gap-3;
|
|
||||||
//@apply flex-inline;
|
|
||||||
//@apply grid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item, .ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu {
|
.top-menu-nav__btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 30px;
|
||||||
|
height: 36px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #fff;
|
||||||
|
color: @primary-color;
|
||||||
|
cursor: pointer;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.06);
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: lighten(@primary-color, 53%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--left {
|
||||||
|
left: 0;
|
||||||
|
padding-right: 4px;
|
||||||
|
background: linear-gradient(to right, #fff 55%, rgba(255, 255, 255, 0.85));
|
||||||
|
}
|
||||||
|
|
||||||
|
&--right {
|
||||||
|
right: 0;
|
||||||
|
padding-left: 4px;
|
||||||
|
background: linear-gradient(to left, #fff 55%, rgba(255, 255, 255, 0.85));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.top-menu,
|
||||||
|
.top-menu-scroll {
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
scrollbar-width: none;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-menu {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
width: max-content;
|
||||||
|
border-bottom: none;
|
||||||
|
padding-top: 4px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item,
|
||||||
|
.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu {
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*TODO check for line bottom menu*/
|
.ant-menu-horizontal > .ant-menu-item::after,
|
||||||
|
.ant-menu-horizontal > .ant-menu-submenu::after {
|
||||||
.ant-menu-horizontal > .ant-menu-item::after, .ant-menu-horizontal > .ant-menu-submenu::after {
|
|
||||||
right: 0 !important;
|
right: 0 !important;
|
||||||
left: 0 !important;
|
left: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-menu-horizontal > .ant-menu-item-selected::after, .ant-menu-horizontal > .ant-menu-submenu-selected::after {
|
.ant-menu-horizontal > .ant-menu-item-selected::after,
|
||||||
|
.ant-menu-horizontal > .ant-menu-submenu-selected::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
border-bottom: 2px solid @primary-color;
|
border-bottom: 2px solid @primary-color;
|
||||||
@@ -147,42 +303,16 @@ const menu: ComputedRef<RouteRecordRaw[]> = computed(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ant-menu-item-selected {
|
.ant-menu-item-selected {
|
||||||
//background-color: red;
|
|
||||||
background-color: lighten(@primary-color, 53%);
|
background-color: lighten(@primary-color, 53%);
|
||||||
|
|
||||||
/*.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item:after{
|
|
||||||
border-bottom: 5px solid red;
|
|
||||||
background-color: red;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/*& > .ant-menu-item::after {
|
|
||||||
position: absolute;
|
|
||||||
right: 20px;
|
|
||||||
bottom: 0;
|
|
||||||
left: 20px;
|
|
||||||
border-bottom: 2px solid #0052d9;
|
|
||||||
transition: border-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
|
||||||
content: '';
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-menu-item {
|
.ant-menu-item {
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*.ant-menu-item:not(.ant-menu-item-disabled):not(.ant-menu-item-selected):hover,
|
|
||||||
.ant-menu-submenu .ant-menu-submenu-title:not(.ant-menu-item-disabled):hover
|
|
||||||
{
|
|
||||||
border-radius:unset !important;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
//display: inline-flex;
|
|
||||||
//justify-content: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-menu-item, .ant-menu-submenu-title {
|
.ant-menu-item,
|
||||||
|
.ant-menu-submenu-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
@@ -209,20 +339,10 @@ const menu: ComputedRef<RouteRecordRaw[]> = computed(() => {
|
|||||||
transition: background-color 0.9ms linear;
|
transition: background-color 0.9ms linear;
|
||||||
border-radius: var(--border-radius-base);
|
border-radius: var(--border-radius-base);
|
||||||
color: unset !important;
|
color: unset !important;
|
||||||
//color: red;
|
|
||||||
background-color: #f3f3f3 !important;
|
background-color: #f3f3f3 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* .ant-menu-inline-collapsed{
|
|
||||||
.ant-menu-item,.ant-menu-submenu-title{
|
|
||||||
//display: block;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
.ant-menu {
|
.ant-menu {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@@ -232,18 +352,14 @@ const menu: ComputedRef<RouteRecordRaw[]> = computed(() => {
|
|||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-menu-item, .ant-menu-submenu-title {
|
.ant-menu-item,
|
||||||
//padding: unset !important;
|
.ant-menu-submenu-title {
|
||||||
@apply px-2 !important;
|
@apply px-2 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.ant-menu.ant-menu-inline-collapsed > .ant-menu-item .ant-menu-item-icon + span,
|
.ant-menu.ant-menu-inline-collapsed > .ant-menu-item .ant-menu-item-icon + span,
|
||||||
.ant-menu.ant-menu-inline-collapsed > .ant-menu-submenu .ant-menu-item-icon + span {
|
.ant-menu.ant-menu-inline-collapsed > .ant-menu-submenu .ant-menu-item-icon + span {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
/*display: flex !important;
|
|
||||||
flex-direction: column !important;
|
|
||||||
justify-content: center !important;*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-menu.ant-menu-inline-collapsed > .ant-menu-submenu > .ant-menu-submenu-title {
|
.ant-menu.ant-menu-inline-collapsed > .ant-menu-submenu > .ant-menu-submenu-title {
|
||||||
@@ -253,8 +369,6 @@ const menu: ComputedRef<RouteRecordRaw[]> = computed(() => {
|
|||||||
justify-content: center !important;
|
justify-content: center !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//,.ant-menu.ant-menu-inline-collapsed > .ant-menu-submenu
|
|
||||||
.ant-menu.ant-menu-inline-collapsed > .ant-menu-item {
|
.ant-menu.ant-menu-inline-collapsed > .ant-menu-item {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
@@ -266,14 +380,7 @@ const menu: ComputedRef<RouteRecordRaw[]> = computed(() => {
|
|||||||
height: 36px;
|
height: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*.ant-menu-submenu-title,.ant-menu-submenu-arrow{
|
|
||||||
//color: #f3f3f3 !important;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
.ant-menu-item.ant-menu-item-selected {
|
.ant-menu-item.ant-menu-item-selected {
|
||||||
//border-radius: var(--border-radius-base);
|
|
||||||
&::after {
|
&::after {
|
||||||
content: unset;
|
content: unset;
|
||||||
}
|
}
|
||||||
@@ -287,6 +394,5 @@ const menu: ComputedRef<RouteRecordRaw[]> = computed(() => {
|
|||||||
.ant-menu-vertical > .ant-menu-item {
|
.ant-menu-vertical > .ant-menu-item {
|
||||||
height: 36px;
|
height: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ function isDev() {
|
|||||||
/**
|
/**
|
||||||
* is size screen mobile
|
* is size screen mobile
|
||||||
*/
|
*/
|
||||||
const isMobile = breakpoints.smaller('lg')
|
const isMobile = breakpoints.smaller('md')
|
||||||
/*function isMobile() {
|
/*function isMobile() {
|
||||||
const breakpoints = useBreakpoints(breakpointsTailwind)
|
const breakpoints = useBreakpoints(breakpointsTailwind)
|
||||||
console.log('change is mobile')
|
console.log('change is mobile')
|
||||||
|
|||||||
@@ -11,6 +11,6 @@
|
|||||||
|
|
||||||
import { useMediaQuery } from '@vueuse/core'
|
import { useMediaQuery } from '@vueuse/core'
|
||||||
|
|
||||||
export const isLargeScreen = useMediaQuery('(min-width: 1023px)')
|
export const isLargeScreen = useMediaQuery('(min-width: 1024px)')
|
||||||
export const isMediumScreen = useMediaQuery('(min-width: 767px)')
|
export const isMediumScreen = useMediaQuery('(min-width: 768px)')
|
||||||
export const isMobileScreen = useMediaQuery('(max-width: 767px)')
|
export const isMobileScreen = useMediaQuery('(max-width: 767px)')
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ export interface Accounting {
|
|||||||
sumAll:number
|
sumAll:number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AccountingCustomerRef {
|
||||||
|
id?: number
|
||||||
|
name?: string
|
||||||
|
mobile?: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface TotalAccounting {
|
export interface TotalAccounting {
|
||||||
id: number
|
id: number
|
||||||
customer_id: number
|
customer_id: number
|
||||||
@@ -33,4 +39,6 @@ export interface TotalAccounting{
|
|||||||
deleted_at: null
|
deleted_at: null
|
||||||
created_at: string
|
created_at: string
|
||||||
withdraw?: Withdraw
|
withdraw?: Withdraw
|
||||||
|
customer?: AccountingCustomerRef
|
||||||
|
customer_to?: AccountingCustomerRef
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
export interface Price {
|
export interface Price {
|
||||||
|
id?: number
|
||||||
|
user_id?: number
|
||||||
price?: number
|
price?: number
|
||||||
|
created_at?: string
|
||||||
|
time_at?: string
|
||||||
|
user?: { name?: string }
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface last {
|
export interface last {
|
||||||
|
|||||||
@@ -6,3 +6,11 @@ export interface Sms {
|
|||||||
message: string
|
message: string
|
||||||
single?: string[]
|
single?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SmsRecord {
|
||||||
|
id?: number
|
||||||
|
receiver?: string
|
||||||
|
sms?: string
|
||||||
|
created_at?: string
|
||||||
|
person?: { name?: string; mobile?: string }
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,12 +8,18 @@ export interface Wallet {
|
|||||||
customer: User
|
customer: User
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface WithdrawTransaction {
|
||||||
|
title?: string
|
||||||
|
gold?: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface Withdraw {
|
export interface Withdraw {
|
||||||
id?: number
|
id?: number
|
||||||
customer_id?: number
|
customer_id?: number
|
||||||
user_id?: number
|
user_id?: number
|
||||||
cash?: number
|
cash?: number
|
||||||
gold?: number
|
gold?: number
|
||||||
|
note?: string
|
||||||
tracked_number?: number
|
tracked_number?: number
|
||||||
date_at?: string
|
date_at?: string
|
||||||
sts?: number
|
sts?: number
|
||||||
@@ -22,6 +28,8 @@ export interface Withdraw {
|
|||||||
sts_str?: string
|
sts_str?: string
|
||||||
daj?: string
|
daj?: string
|
||||||
customer?: User
|
customer?: User
|
||||||
|
bank_account?: { cart_number?: string }
|
||||||
|
transaction?: WithdrawTransaction[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Charge {
|
export interface Charge {
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex">
|
<div class="w-full min-w-0 rounded-lg border border-gray-100 bg-gray-50/60 px-3 py-2 lg:border-0 lg:bg-transparent lg:px-0 lg:py-0">
|
||||||
<div class="w-full flex md:block justify-between md:justify-unset items-center">
|
<div class="flex w-full min-w-0 items-center justify-between gap-2 lg:block">
|
||||||
<div class="flex items-center gap-2 mx-2">
|
<div class="flex min-w-0 items-center gap-2">
|
||||||
<div class="p-1 h-8 aspect-square rounded-full" :class="bgColor">
|
<div class="aspect-square h-8 shrink-0 rounded-full p-1" :class="bgColor">
|
||||||
<BaseIcon icon="vuesax-linear:profile-add" :class="textColor" />
|
<BaseIcon icon="vuesax-linear:profile-add" :class="textColor" />
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs font-bold">{{ title }}</div>
|
<div class="truncate text-xs font-bold">{{ title }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row-reverse gap-1 mt-5 items-center md:w-full pl-2 items-end">
|
<div class="flex shrink-0 flex-row-reverse items-baseline gap-1 pl-1 lg:mt-5 lg:w-full lg:pl-2">
|
||||||
<div class="text-xs">{{ unit }}</div>
|
<div class="text-xs">{{ unit }}</div>
|
||||||
<div class="font-bold text-xl">{{ count }}</div>
|
<div class="font-bold text-lg lg:text-xl">{{ count ?? 0 }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+115
-68
@@ -1,49 +1,49 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="p-5">
|
<div class="dashboard-page box-border h-full min-h-0 w-full max-w-full overflow-x-hidden overflow-y-auto p-3 md:p-4 lg:p-5">
|
||||||
<a-spin size="large" :spinning="loading">
|
<a-spin size="large" :spinning="loading" class="block min-w-0">
|
||||||
<div
|
<div
|
||||||
class="w-full bg-[#0A1B37] flex p-2 rounded-lg md:justify-evenly flex-col shadow-lg"
|
class="dashboard-price-banner grid w-full min-w-0 gap-2 rounded-lg bg-[#0A1B37] p-3 shadow-lg sm:grid-cols-2 lg:flex lg:justify-evenly"
|
||||||
>
|
>
|
||||||
<div class="flex text-white gap-2 items-center">
|
<div class="flex flex-wrap items-center gap-x-2 gap-y-1 text-sm text-white sm:col-span-2 lg:col-span-1">
|
||||||
<div>مظنه روز:</div>
|
<span class="shrink-0">مظنه روز:</span>
|
||||||
<div class="font-bold">
|
<span class="break-all font-bold">
|
||||||
{{ usePrice(parseInt(dashboardInf?.price ?? '0'), true, '') }}
|
{{ usePrice(parseInt(dashboardInf?.price ?? '0'), true, '') }}
|
||||||
|
</span>
|
||||||
|
<span class="text-xs">ریال</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs">ریال</div>
|
<div class="flex flex-wrap items-center gap-x-2 gap-y-1 text-sm text-white">
|
||||||
|
<span>آخرین تاریخ به روز رسانی:</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex text-white gap-2 items-center">
|
<div class="flex flex-wrap items-center gap-x-2 gap-y-1 text-sm text-white">
|
||||||
<div>آخرین تاریخ به روز رسانی:</div>
|
<span class="shrink-0">هر مثقال طلا:</span>
|
||||||
</div>
|
<span class="break-all font-bold"></span>
|
||||||
<div class="flex text-white gap-2 items-center">
|
<span class="text-xs">ریال</span>
|
||||||
<div>هر مثقال طلا:</div>
|
|
||||||
<div class="font-bold"></div>
|
|
||||||
<div class="text-xs">ریال</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-center md:items-unset md:grid md:grid-cols-7 gap-4 mt-3">
|
<div class="mt-3 flex w-full min-w-0 flex-col gap-3 md:gap-4 lg:grid lg:grid-cols-7 lg:items-start">
|
||||||
<BaseCard class="w-full md:w-none md:col-span-7">
|
<BaseCard class="w-full min-w-0 lg:col-span-7">
|
||||||
<div class="text-lg font-bold">مشتری</div>
|
<div class="text-lg font-bold">مشتری</div>
|
||||||
<div
|
<div
|
||||||
class="flex flex-col md:flex-row justify-around mt-5 justify-between items-dashboard h-full"
|
class="items-dashboard mt-3 flex h-full flex-col gap-2 lg:mt-5 lg:flex-row lg:justify-between lg:gap-0"
|
||||||
>
|
>
|
||||||
<DashboardItem
|
<DashboardItem
|
||||||
class="md:w-1/3"
|
class="lg:w-1/3"
|
||||||
color="blue"
|
color="blue"
|
||||||
title="مشتری جدید"
|
title="مشتری جدید"
|
||||||
unit="نفر"
|
unit="نفر"
|
||||||
:count="dashboardInf?.new_customer_single"
|
:count="dashboardInf?.new_customer_single"
|
||||||
/>
|
/>
|
||||||
<div class="h-[60%] hidden md:block bg-gray-200 w-[1px]" />
|
<div class="hidden h-[60%] w-[1px] bg-gray-200 lg:block" />
|
||||||
<DashboardItem
|
<DashboardItem
|
||||||
class="md:w-1/3"
|
class="lg:w-1/3"
|
||||||
color="green"
|
color="green"
|
||||||
title="کل مشتریان"
|
title="کل مشتریان"
|
||||||
unit="نفر"
|
unit="نفر"
|
||||||
:count="dashboardInf?.all_customer_single"
|
:count="dashboardInf?.all_customer_single"
|
||||||
/>
|
/>
|
||||||
<div class="h-[60%] hidden md:block bg-gray-200 w-[1px]" />
|
<div class="hidden h-[60%] w-[1px] bg-gray-200 lg:block" />
|
||||||
<DashboardItem
|
<DashboardItem
|
||||||
class="md:w-1/3"
|
class="lg:w-1/3"
|
||||||
color="orange"
|
color="orange"
|
||||||
title="مشتریان بایگانی شده"
|
title="مشتریان بایگانی شده"
|
||||||
unit="نفر"
|
unit="نفر"
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</BaseCard>
|
</BaseCard>
|
||||||
<BaseCard class="md:hidden w-full">
|
<BaseCard class="w-full min-w-0 lg:hidden">
|
||||||
<div class="grid grid-cols-3 gap-1 text-xs">
|
<div class="grid grid-cols-3 gap-1 text-xs">
|
||||||
<div class="bg-blue-600 text-white rounded p-1">مجموع</div>
|
<div class="bg-blue-600 text-white rounded p-1">مجموع</div>
|
||||||
<div class="bg-blue-600 text-white rounded p-1">ریال</div>
|
<div class="bg-blue-600 text-white rounded p-1">ریال</div>
|
||||||
@@ -108,36 +108,34 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</BaseCard>
|
</BaseCard>
|
||||||
<BaseCard class="w-full h-full md:col-span-7">
|
<BaseCard class="h-full w-full min-w-0 lg:col-span-7">
|
||||||
<ASpin :spinning="tarazLoading">
|
<ASpin :spinning="tarazLoading">
|
||||||
<div class="grid md:grid-cols-3 gap-3 mt-2">
|
<div class="mt-2 grid min-w-0 gap-3 lg:grid-cols-3">
|
||||||
<div>
|
<div class="min-w-0">
|
||||||
<div class="md:flex content-center gap-3">
|
<div class="flex w-full min-w-0 flex-col gap-3 rounded-lg bg-gray-100 p-3 lg:flex-row lg:items-end lg:p-2">
|
||||||
<div class="w-full md:flex items-baseline gap-2 bg-gray-100 p-2 rounded-lg">
|
<p class="shrink-0 font-bold lg:mr-2">تاریخ تراز</p>
|
||||||
<p class="font-bold mr-2">تاریخ تراز</p>
|
<div class="flex min-w-0 flex-1 flex-col gap-2 sm:flex-row sm:items-baseline sm:gap-3">
|
||||||
<div class="w-full flex items-baseline flex-1 gap-3">
|
<p class="shrink-0 font-bold">از:</p>
|
||||||
<p class="font-bold">از:</p>
|
|
||||||
<BaseDatePicker
|
<BaseDatePicker
|
||||||
v-model:value="startTaraz"
|
v-model:value="startTaraz"
|
||||||
format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
class="flex-1"
|
class="min-w-0 w-full flex-1"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full flex items-baseline flex-1 gap-3">
|
<div class="flex min-w-0 flex-1 flex-col gap-2 sm:flex-row sm:items-baseline sm:gap-3">
|
||||||
<p class="font-bold">تا:</p>
|
<p class="shrink-0 font-bold">تا:</p>
|
||||||
<BaseDatePicker
|
<BaseDatePicker
|
||||||
v-model:value="endTaraz"
|
v-model:value="endTaraz"
|
||||||
format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
class="flex-1"
|
class="min-w-0 w-full flex-1"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<BaseButton @click="getTaraz()">ثبت تاریخ</BaseButton>
|
<BaseButton class="w-full shrink-0 sm:w-auto" @click="getTaraz()">ثبت تاریخ</BaseButton>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex font-bold p-2">
|
<div class="flex flex-wrap items-center gap-2 p-2 font-bold">
|
||||||
<span>جمع واریزی : </span>
|
<span>جمع واریزی : </span>
|
||||||
<div class="mr-4">{{ usePrice(balance_?.income_money ?? 0) }}</div>
|
<div>{{ usePrice(balance_?.income_money ?? 0) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -147,9 +145,9 @@
|
|||||||
<span class="text-center text-base font-700 bg-yellow text-white rounded-lg">
|
<span class="text-center text-base font-700 bg-yellow text-white rounded-lg">
|
||||||
تراز طلایی
|
تراز طلایی
|
||||||
</span>
|
</span>
|
||||||
<div class="flex flex-col md:flex-row items-center justify-between md:gap-15">
|
<div class="flex flex-col items-stretch justify-between gap-3 lg:flex-row lg:items-center lg:gap-15">
|
||||||
<div class="w-full flex flex-col gap-3">
|
<div class="flex w-full flex-col gap-3">
|
||||||
<div class="flex justify-between mx-5">
|
<div class="flex justify-between gap-2 px-2 lg:mx-5">
|
||||||
<span class="font-600">تراز اصلی:</span>
|
<span class="font-600">تراز اصلی:</span>
|
||||||
<span
|
<span
|
||||||
class="text-base"
|
class="text-base"
|
||||||
@@ -175,9 +173,9 @@
|
|||||||
<span class="text-center text-base font-700 bg-blue-6 rounded-lg text-white">
|
<span class="text-center text-base font-700 bg-blue-6 rounded-lg text-white">
|
||||||
تراز ریالی
|
تراز ریالی
|
||||||
</span>
|
</span>
|
||||||
<div class="flex flex-col md:flex-row items-center justify-between md:gap-15">
|
<div class="flex flex-col items-stretch justify-between gap-3 lg:flex-row lg:items-center lg:gap-15">
|
||||||
<div class="w-full flex flex-col gap-3">
|
<div class="flex w-full flex-col gap-3">
|
||||||
<div class="flex justify-between mx-5">
|
<div class="flex justify-between gap-2 px-2 lg:mx-5">
|
||||||
<span class="font-600">تراز اصلی:</span>
|
<span class="font-600">تراز اصلی:</span>
|
||||||
<span
|
<span
|
||||||
class="text-base"
|
class="text-base"
|
||||||
@@ -292,34 +290,34 @@
|
|||||||
:series="sumOfSellCoworkerSeries"
|
:series="sumOfSellCoworkerSeries"
|
||||||
></apexchart>
|
></apexchart>
|
||||||
</BaseCard>-->
|
</BaseCard>-->
|
||||||
<BaseCard class="w-full md:grid md:col-span-7">
|
<BaseCard class="w-full min-w-0 lg:col-span-7">
|
||||||
<div class="font-bold text-lg mt-2 text-center md:text-right">
|
<div class="mt-2 text-center text-lg font-bold lg:text-right">
|
||||||
حسابداری گلد در یک نگاه
|
حسابداری گلد در یک نگاه
|
||||||
</div>
|
</div>
|
||||||
<div class="md:flex mt-6">
|
<div class="mt-4 flex flex-col gap-3 lg:mt-6 lg:flex-row">
|
||||||
<div class="flex items-baseline flex-1 gap-3">
|
<div class="flex min-w-0 flex-1 flex-col gap-2 sm:flex-row sm:items-baseline sm:gap-3">
|
||||||
<p>از:</p>
|
<p class="shrink-0">از:</p>
|
||||||
<BaseDatePicker
|
<BaseDatePicker
|
||||||
v-model:value="startDate"
|
v-model:value="startDate"
|
||||||
@on-change="getTaraz"
|
@on-change="getTaraz"
|
||||||
format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
class="flex-1"
|
class="min-w-0 w-full flex-1"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-baseline flex-1 gap-3">
|
<div class="flex min-w-0 flex-1 flex-col gap-2 sm:flex-row sm:items-baseline sm:gap-3">
|
||||||
<p class="md:mr-6">تا:</p>
|
<p class="shrink-0 lg:mr-6">تا:</p>
|
||||||
<BaseDatePicker
|
<BaseDatePicker
|
||||||
v-model:value="endDate"
|
v-model:value="endDate"
|
||||||
@on-change="getTaraz"
|
@on-change="getTaraz"
|
||||||
format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
class="flex-1"
|
class="min-w-0 w-full flex-1"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-cols-1 grid gap-2 bg-green-100 rounded-lg shadow-lg">
|
<div class="grid-cols-1 grid gap-2 rounded-lg bg-green-100 shadow-lg">
|
||||||
<div class="p-4">
|
<div class="p-3 lg:p-4">
|
||||||
<div class="font-bold text-lg">دریافتی</div>
|
<div class="text-lg font-bold">دریافتی</div>
|
||||||
<div class="md:grid-cols-5 grid gap-2 mt-4">
|
<div class="mt-4 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-5 lg:gap-2">
|
||||||
<div>
|
<div>
|
||||||
<div>کل سود دریافت شده</div>
|
<div>کل سود دریافت شده</div>
|
||||||
<div class="font-bold">{{ usePrice(balance?.fullPriceSood ?? 0) }}</div>
|
<div class="font-bold">{{ usePrice(balance?.fullPriceSood ?? 0) }}</div>
|
||||||
@@ -343,9 +341,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4 bg-red-100 p-4 rounded-lg flex-col md:flex-row shadow-lg">
|
<div class="mt-4 flex-col rounded-lg bg-red-100 p-3 shadow-lg lg:flex-row lg:p-4">
|
||||||
<div class="font-bold text-lg">خروجی</div>
|
<div class="text-lg font-bold">خروجی</div>
|
||||||
<div class="md:grid-cols-2 grid mt-4">
|
<div class="mt-4 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:gap-4">
|
||||||
<div>
|
<div>
|
||||||
<div>پاداش معرفی به گرم</div>
|
<div>پاداش معرفی به گرم</div>
|
||||||
<div class="font-bold">{{ balance?.Gifts }}</div>
|
<div class="font-bold">{{ balance?.Gifts }}</div>
|
||||||
@@ -357,7 +355,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mt-4 bg-blue-100 p-4 rounded-lg md:justify-evenly flex-col md:flex-row shadow-lg"
|
class="mt-4 flex-col rounded-lg bg-blue-100 p-3 shadow-lg lg:flex-row lg:justify-evenly lg:p-4"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div class="font-bold text-lg">قیمت کل</div>
|
<div class="font-bold text-lg">قیمت کل</div>
|
||||||
@@ -368,11 +366,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</BaseCard>
|
</BaseCard>
|
||||||
<BaseCard class="w-full md:w-none md:col-span-7">
|
<BaseCard class="w-full min-w-0 lg:col-span-7">
|
||||||
<div>
|
<div>
|
||||||
<div class="font-bold text-lg mt-2 text-center md:text-right">نوسانات قیمت</div>
|
<div class="mt-2 text-center text-lg font-bold lg:text-right">نوسانات قیمت</div>
|
||||||
<div id="chart">
|
<div id="chart" class="min-w-0">
|
||||||
<div class="toolbar text-center md:text-right mt-2 mb-2">
|
<div class="dashboard-chart-toolbar mt-2 mb-2 text-center lg:text-right">
|
||||||
<button
|
<button
|
||||||
id="DAILY"
|
id="DAILY"
|
||||||
@click="updateData('DAILY')"
|
@click="updateData('DAILY')"
|
||||||
@@ -398,10 +396,10 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="chart-timeline">
|
<div id="chart-timeline" class="min-w-0 overflow-hidden">
|
||||||
<apexchart
|
<apexchart
|
||||||
type="area"
|
type="area"
|
||||||
height="350"
|
:height="chartHeight"
|
||||||
ref="chart"
|
ref="chart"
|
||||||
:options="priceChartOptions"
|
:options="priceChartOptions"
|
||||||
:series="priceChartSeries"
|
:series="priceChartSeries"
|
||||||
@@ -432,6 +430,9 @@ import type { OthersPrices } from '@/model/othersPrices'
|
|||||||
import { getNow, toJalaliNoTime } from '@core/utils/useDataTime'
|
import { getNow, toJalaliNoTime } from '@core/utils/useDataTime'
|
||||||
import type { Balance } from '@/model/product'
|
import type { Balance } from '@/model/product'
|
||||||
import { getBalance } from "@/api/product";
|
import { getBalance } from "@/api/product";
|
||||||
|
import { isLargeScreen } from '@core/utils/responsive'
|
||||||
|
|
||||||
|
const chartHeight = computed(() => (isLargeScreen.value ? 350 : 280))
|
||||||
const tarazLoading = ref<boolean>(false)
|
const tarazLoading = ref<boolean>(false)
|
||||||
const main = ref(null)
|
const main = ref(null)
|
||||||
const type = ref<string>('week')
|
const type = ref<string>('week')
|
||||||
@@ -861,5 +862,51 @@ onMounted(() => {
|
|||||||
@apply ! bg- [ #0A1B37 ] ! text-white rounded-lg;
|
@apply ! bg- [ #0A1B37 ] ! text-white rounded-lg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dashboard-chart-toolbar {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
|
||||||
|
button {
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background: #fff;
|
||||||
|
color: #374151;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&.active,
|
||||||
|
&:hover {
|
||||||
|
background: #0a1b37;
|
||||||
|
border-color: #0a1b37;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.dashboard-chart-toolbar {
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-page :deep(.ant-card) {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-page :deep(.ant-picker) {
|
||||||
|
max-width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
//chart
|
//chart
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -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>
|
<template>
|
||||||
<div>
|
<div class="min-w-0 w-full max-w-full overflow-x-hidden">
|
||||||
<BasicShowContext title="حسابداری">
|
<BasicShowContext title="حسابداری">
|
||||||
|
<div v-if="!isMobile" class="min-w-0 w-full max-w-full overflow-x-auto">
|
||||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
||||||
<template #extraHeader>
|
<template #action="{ record }">
|
||||||
<!-- <BaseButton v-if="!customerId" type="primary" size="small" class="flex" @click="emits('add')">-->
|
<div class="flex justify-center gap-2">
|
||||||
<!-- <div>افزودن سفارش</div>-->
|
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('info', record)">
|
||||||
<!-- <BaseIcon icon="vuesax-linear:receipt-add" Size="16px" />-->
|
<template #title><span>برداشت و واریز</span></template>
|
||||||
<!-- </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" />
|
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="handleOrderModal(record)">
|
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('orders', record)">
|
||||||
<template #title>
|
<template #title><span>سفارشات</span></template>
|
||||||
<span>سفارشات</span>
|
|
||||||
</template>
|
|
||||||
<Icon icon="vuesax-linear:receipt-item" color="#10913A" />
|
<Icon icon="vuesax-linear:receipt-item" color="#10913A" />
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</BaseTable>
|
</BaseTable>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<AccountingMobileCardList
|
||||||
|
v-else
|
||||||
|
ref="mobileListRef"
|
||||||
|
@info="emits('info', $event)"
|
||||||
|
@orders="emits('orders', $event)"
|
||||||
|
/>
|
||||||
</BasicShowContext>
|
</BasicShowContext>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="tsx" setup>
|
<script setup lang="ts">
|
||||||
import type { TableInput } from '@core/type/baseTable'
|
import { ref } from 'vue'
|
||||||
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'
|
|
||||||
import { getTotalAccountingTable } from '@/api/accounting'
|
import { getTotalAccountingTable } from '@/api/accounting'
|
||||||
import {ref} from "vue";
|
import { cols } from '@/pages/menuPages/accounting/hooks/accountingColumns'
|
||||||
import {cloneDeep} from "lodash";
|
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)
|
defineProps<{
|
||||||
function handleOrderModal(record) {
|
customerId?: number
|
||||||
|
}>()
|
||||||
showOrderModal.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const emits = defineEmits<{
|
||||||
|
(e: 'info', record: unknown): void
|
||||||
|
(e: 'orders', record: unknown): void
|
||||||
|
}>()
|
||||||
|
|
||||||
const [submit, reload] = useBaseTable()
|
const [submit, reload] = useBaseTable()
|
||||||
/*const select = (val) => {
|
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||||
val.sts === 1 ? return ['1'] : ['2']
|
|
||||||
}*/
|
|
||||||
//emits
|
|
||||||
interface Prop {
|
|
||||||
customerId: number
|
|
||||||
}
|
|
||||||
const emits = defineEmits(['delete', 'edit', 'add', 'info'])
|
|
||||||
const props = defineProps<Prop>()
|
|
||||||
|
|
||||||
const { prefixCls } = usePrefix('pophoverstatus')
|
const tblProps = ref({
|
||||||
|
|
||||||
//permission
|
|
||||||
function checkPermission(temp: string) {
|
|
||||||
const store = useUserStore()
|
|
||||||
return store.canAccess(temp)
|
|
||||||
}
|
|
||||||
|
|
||||||
//define table
|
|
||||||
const table = ref(null)
|
|
||||||
|
|
||||||
const tblProps = ref<any>({
|
|
||||||
columns: cols,
|
columns: cols,
|
||||||
api: (params: TableInput) => {
|
api: (params: TableInput) => getTotalAccountingTable(params)
|
||||||
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
|
|
||||||
})
|
})
|
||||||
const loading = ref<boolean>(false)
|
|
||||||
|
|
||||||
async function changeCustomerActivationFunction(id: number) {
|
async function fetch() {
|
||||||
try {
|
if (isMobile.value) {
|
||||||
loading.value = true
|
await mobileListRef.value?.fetch?.()
|
||||||
await changeCustomerActivation(id)
|
return
|
||||||
reload()
|
|
||||||
message.success('تغییر وضعیت با موفقیت انجام شد')
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
}
|
||||||
|
await reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetch = () => {
|
defineExpose({ fetch })
|
||||||
// console.log('sss')
|
|
||||||
reload()
|
|
||||||
}
|
|
||||||
// const spinTable = (val: boolean) => {
|
|
||||||
// // console.log('sss')
|
|
||||||
// table?.value?.loading.value = val
|
|
||||||
// }
|
|
||||||
defineExpose({
|
|
||||||
fetch
|
|
||||||
// spinTable
|
|
||||||
})
|
|
||||||
</script>
|
</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',
|
dataIndex: 'action',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
slot: 'action',
|
slot: 'action',
|
||||||
fixed: 'right',
|
|
||||||
filterable: false
|
filterable: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,15 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<BasicMainCard>
|
<BasicMainCard>
|
||||||
<div class="flex w-full h-full">
|
<div class="flex h-full w-full min-w-0 max-w-full overflow-x-hidden">
|
||||||
<tbl
|
<tbl ref="table" class="h-full w-full min-w-0" @info="onAccountingWithdraw" @orders="onAccountingOrders" />
|
||||||
ref="table"
|
|
||||||
class="w-full h-full"
|
|
||||||
@info="info"
|
|
||||||
@edit="edit"
|
|
||||||
@delete="deleteItem"
|
|
||||||
@add="add"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- <BasicMenu :menu="menu" width="13rem" @onChange="tabSelected" />
|
<!-- <BasicMenu :menu="menu" width="13rem" @onChange="tabSelected" />
|
||||||
<component :is="selectedComponent" class="mr-5" />-->
|
<component :is="selectedComponent" class="mr-5" />-->
|
||||||
@@ -143,6 +136,7 @@ import { cloneDeep } from 'lodash'
|
|||||||
import type { Item } from '@/model/Item'
|
import type { Item } from '@/model/Item'
|
||||||
import type { Customer } from '@/model/customer'
|
import type { Customer } from '@/model/customer'
|
||||||
import { createCustomer, deleteCustomer, getCustomersList, updateCustomer } from '@/api/customer'
|
import { createCustomer, deleteCustomer, getCustomersList, updateCustomer } from '@/api/customer'
|
||||||
|
import type { TotalAccounting } from '@/model/accounting'
|
||||||
import type { Order } from '@/model/order'
|
import type { Order } from '@/model/order'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
import { getLastPrice } from '@/api/price'
|
import { getLastPrice } from '@/api/price'
|
||||||
@@ -234,6 +228,14 @@ function stopGetPrice() {
|
|||||||
//info
|
//info
|
||||||
let infoParams = reactive<Info>({})
|
let infoParams = reactive<Info>({})
|
||||||
|
|
||||||
|
function onAccountingWithdraw(_record: TotalAccounting) {
|
||||||
|
message.info('برداشت و واریز بهزودی در دسترس است')
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAccountingOrders(_record: TotalAccounting) {
|
||||||
|
message.info('لیست سفارشات بهزودی در دسترس است')
|
||||||
|
}
|
||||||
|
|
||||||
function info(val: Order) {
|
function info(val: Order) {
|
||||||
infoParams.name = val.customer.name
|
infoParams.name = val.customer.name
|
||||||
infoParams.type = val.customer.mobile
|
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>
|
<template>
|
||||||
<div>
|
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||||
<BasicShowContext title="لیست حساب ها">
|
<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>
|
<template #extraHeader>
|
||||||
<!-- <BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
<!-- <BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
||||||
<div>افزودن مشتری عمده</div>
|
<div>افزودن مشتری عمده</div>
|
||||||
@@ -27,6 +28,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</BaseTable>
|
</BaseTable>
|
||||||
|
|
||||||
|
<AccountMobileCardList
|
||||||
|
v-else
|
||||||
|
ref="mobileListRef"
|
||||||
|
class="min-h-0 flex-1"
|
||||||
|
@toggle-sts="changeAccountStsFunction"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</BasicShowContext>
|
</BasicShowContext>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -44,7 +53,11 @@ import { getOrderTable } from '@/api/order'
|
|||||||
import { getWithdrawTable } from '@/api/wallet'
|
import { getWithdrawTable } from '@/api/wallet'
|
||||||
import { changeAccountSts, getAccountTable } from '@/api/card'
|
import { changeAccountSts, getAccountTable } from '@/api/card'
|
||||||
import type { Card } from '@/model/card'
|
import type { Card } from '@/model/card'
|
||||||
|
import AccountMobileCardList from './AccountMobileCardList.vue'
|
||||||
|
import { isMobile } from '@core/utils/is'
|
||||||
|
|
||||||
const [submit, reload] = useBaseTable()
|
const [submit, reload] = useBaseTable()
|
||||||
|
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||||
/*const select = (val) => {
|
/*const select = (val) => {
|
||||||
val.sts === 1 ? return ['1'] : ['2']
|
val.sts === 1 ? return ['1'] : ['2']
|
||||||
}*/
|
}*/
|
||||||
@@ -64,7 +77,7 @@ async function changeAccountStsFunction(val: Card) {
|
|||||||
loading.value = true
|
loading.value = true
|
||||||
await changeAccountSts({ id: val.id, sts: val.sts !== 2 ? 2 : 3 })
|
await changeAccountSts({ id: val.id, sts: val.sts !== 2 ? 2 : 3 })
|
||||||
message.success(val.sts !== 2 ? 'با موفقیت تایید شد' : 'با موفقیت رد شد')
|
message.success(val.sts !== 2 ? 'با موفقیت تایید شد' : 'با موفقیت رد شد')
|
||||||
reload()
|
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
} finally {
|
} finally {
|
||||||
@@ -83,9 +96,8 @@ const tblProps = ref<any>({
|
|||||||
})
|
})
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
|
|
||||||
const fetch = () => {
|
async function fetch() {
|
||||||
// console.log('sss')
|
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||||
reload()
|
|
||||||
}
|
}
|
||||||
// const spinTable = (val: boolean) => {
|
// const spinTable = (val: boolean) => {
|
||||||
// // console.log('sss')
|
// // 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,15 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||||
<BasicShowContext title="لیست مشتریان بایگانی شده">
|
<BasicShowContext title="لیست مشتریان بایگانی شده">
|
||||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
<div class="flex h-full min-h-0 flex-col">
|
||||||
<!-- <template #extraHeader>
|
<BaseTable v-if="!isMobile" class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||||
<BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
<template #action="{ record }">
|
||||||
<div>افزودن مشتری عمده</div>
|
<div class="flex justify-center gap-2">
|
||||||
<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">
|
<a-tooltip :key="record.is_archive" placement="bottom" class="cursor-pointer">
|
||||||
<template #title>
|
<template #title>
|
||||||
<span>{{ record.is_archive ? 'بیرون بردن از بایگانی' : 'بایگانی کردن' }}</span>
|
<span>{{ record.is_archive ? 'بیرون بردن از بایگانی' : 'بایگانی کردن' }}</span>
|
||||||
@@ -20,109 +15,73 @@
|
|||||||
cancel-text="رد"
|
cancel-text="رد"
|
||||||
placement="topLeft"
|
placement="topLeft"
|
||||||
@confirm="archiveFunction(record.id)"
|
@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" />
|
<Icon class="cursor-pointer" icon="vuesax-linear:archive-slash" color="#e3bf00" />
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
|
|
||||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('info', record)">
|
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('info', record)">
|
||||||
<template #title>
|
<template #title><span>مشخصات</span></template>
|
||||||
<span>مشخصات</span>
|
|
||||||
</template>
|
|
||||||
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
||||||
</a-tooltip>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</BaseTable>
|
</BaseTable>
|
||||||
|
|
||||||
|
<CustomerArchiveMobileCardList
|
||||||
|
v-else
|
||||||
|
ref="mobileListRef"
|
||||||
|
class="min-h-0 flex-1"
|
||||||
|
@info="emits('info', $event)"
|
||||||
|
@unarchive="handleUnarchive"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</BasicShowContext>
|
</BasicShowContext>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="tsx" setup>
|
<script setup lang="ts">
|
||||||
import type { TableInput } from '@core/type/baseTable'
|
import { message, Modal } from 'ant-design-vue'
|
||||||
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
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 Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||||
import {
|
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
||||||
changeCustomerActivation,
|
import type { TableInput } from '@core/type/baseTable'
|
||||||
changeCustomerArchive,
|
import { isMobile } from '@core/utils/is'
|
||||||
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'
|
|
||||||
|
|
||||||
const [submit, reload] = useBaseTable()
|
const [submit, reload] = useBaseTable()
|
||||||
/*const select = (val) => {
|
|
||||||
val.sts === 1 ? return ['1'] : ['2']
|
|
||||||
}*/
|
|
||||||
//emits
|
|
||||||
const emits = defineEmits(['delete', 'edit', 'add', 'info', 'charge'])
|
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
|
const tblProps = ref({
|
||||||
function checkPermission(temp: string) {
|
|
||||||
const store = useUserStore()
|
|
||||||
return store.canAccess(temp)
|
|
||||||
}
|
|
||||||
|
|
||||||
//define table
|
|
||||||
const table = ref(null)
|
|
||||||
|
|
||||||
const tblProps = ref<any>({
|
|
||||||
columns: cols,
|
columns: cols,
|
||||||
api: (params: TableInput) => {
|
api: (params: TableInput) => getCustomerArchiveTable(params)
|
||||||
return getCustomerArchiveTable(params)
|
|
||||||
}
|
|
||||||
//bordered: true
|
|
||||||
})
|
})
|
||||||
const loading = ref<boolean>(false)
|
|
||||||
|
|
||||||
async function changeCustomerActivationFunction(id: number) {
|
async function fetch() {
|
||||||
try {
|
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||||
loading.value = true
|
|
||||||
await changeCustomerActivation(id)
|
|
||||||
reload()
|
|
||||||
message.success('تغییر وضعیت با موفقیت انجام شد')
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function changeCustomerOrderActivationFunction(id: number) {
|
function handleUnarchive(customer: Customer) {
|
||||||
try {
|
Modal.confirm({
|
||||||
loading.value = true
|
title: 'آیا مشتری را از بایگانی خارج می کنید؟',
|
||||||
await changeCustomerOrderActivation(id)
|
okText: 'تایید',
|
||||||
reload()
|
cancelText: 'رد',
|
||||||
message.success('تغییر وضعیت سفارش دهی با موفقیت انجام شد')
|
centered: true,
|
||||||
} catch (e) {
|
onOk: () => archiveFunction(customer.id)
|
||||||
console.error(e)
|
})
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function archiveFunction(id: number) {
|
async function archiveFunction(id: number) {
|
||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
await changeCustomerArchive(id)
|
await changeCustomerArchive(id)
|
||||||
reload()
|
await fetch()
|
||||||
message.success('مشتری با موفقیت از بایگانی خارج شد.')
|
message.success('مشتری با موفقیت از بایگانی خارج شد.')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
@@ -130,37 +89,6 @@ async function archiveFunction(id: number) {
|
|||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const fetch = () => {
|
|
||||||
// console.log('sss')
|
defineExpose({ fetch })
|
||||||
reload()
|
|
||||||
}
|
|
||||||
// const spinTable = (val: boolean) => {
|
|
||||||
// // console.log('sss')
|
|
||||||
// table?.value?.loading.value = val
|
|
||||||
// }
|
|
||||||
defineExpose({
|
|
||||||
fetch
|
|
||||||
// spinTable
|
|
||||||
})
|
|
||||||
</script>
|
</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',
|
dataIndex: 'action',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
slot: 'action',
|
slot: 'action',
|
||||||
fixed: 'right',
|
|
||||||
filterable: false
|
filterable: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="h-full min-h-0">
|
||||||
<!-- <BasicMainCard>-->
|
<!-- <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" @info="info" @edit="edit" @charge="walletCharge" />
|
<tbl ref="table" class="h-full min-h-0 w-full" @info="info" @edit="edit" @charge="walletCharge" />
|
||||||
|
|
||||||
<!-- <BasicMenu :menu="menu" width="13rem" @onChange="tabSelected" />
|
<!-- <BasicMenu :menu="menu" width="13rem" @onChange="tabSelected" />
|
||||||
<component :is="selectedComponent" class="mr-5" />-->
|
<component :is="selectedComponent" class="mr-5" />-->
|
||||||
@@ -55,15 +55,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</a-form>
|
</a-form>
|
||||||
</BasicModal>
|
</BasicModal>
|
||||||
<BasicModal
|
<BasicActionSheet
|
||||||
v-model:visible="userInfoVisible"
|
v-model:visible="userInfoVisible"
|
||||||
width="40vw"
|
|
||||||
title="اطلاعات کاربر"
|
title="اطلاعات کاربر"
|
||||||
custom-footer
|
modal-width="100vw"
|
||||||
|
:loading="loading"
|
||||||
|
:showButton="false"
|
||||||
@close="userInfoVisible = false"
|
@close="userInfoVisible = false"
|
||||||
>
|
>
|
||||||
<div class="grid grid-cols-4 gap-2">
|
<div v-if="isMobile" class="mb-3 text-base font-700">اطلاعات کاربر</div>
|
||||||
<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>
|
||||||
<div class="font-bold">{{ userInfo.name }}</div>
|
<div class="font-bold">{{ userInfo.name }}</div>
|
||||||
<div class="mt-1">{{ userInfo?.mobile }}</div>
|
<div class="mt-1">{{ userInfo?.mobile }}</div>
|
||||||
@@ -82,6 +84,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 gap-2 md:grid-cols-4">
|
||||||
<BasicInfoItem title="کد کاربری" :value="userInfo?.id" />
|
<BasicInfoItem title="کد کاربری" :value="userInfo?.id" />
|
||||||
<BasicInfoItem title="نوع کاربر" :value="userInfo?.type_str" />
|
<BasicInfoItem title="نوع کاربر" :value="userInfo?.type_str" />
|
||||||
<BasicInfoItem
|
<BasicInfoItem
|
||||||
@@ -101,7 +104,7 @@
|
|||||||
title="نام معرف کاربر"
|
title="نام معرف کاربر"
|
||||||
:value="userInfo?.referral ? userInfo?.referral?.name : '_'"
|
:value="userInfo?.referral ? userInfo?.referral?.name : '_'"
|
||||||
/>
|
/>
|
||||||
<BasicInfoItem title="وضعیت سفارش دهی">
|
<BasicInfoItem title="وضعیت سفارش دهی" class="col-span-2 md:col-span-1">
|
||||||
<CustomTag
|
<CustomTag
|
||||||
:textColor="userInfo?.trade_active === 1 ? '#25855A' : '#C53030'"
|
:textColor="userInfo?.trade_active === 1 ? '#25855A' : '#C53030'"
|
||||||
:bgColor="userInfo?.trade_active === 1 ? '#E3F4E9' : '#FECACA'"
|
:bgColor="userInfo?.trade_active === 1 ? '#E3F4E9' : '#FECACA'"
|
||||||
@@ -110,7 +113,9 @@
|
|||||||
{{ userInfo?.trade_active === 1 ? 'مجاز' : 'غیر مجاز' }}
|
{{ userInfo?.trade_active === 1 ? 'مجاز' : 'غیر مجاز' }}
|
||||||
</CustomTag>
|
</CustomTag>
|
||||||
</BasicInfoItem>
|
</BasicInfoItem>
|
||||||
<div class="col-span-4 grid grid-cols-2 gap-3">
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-3 md:grid-cols-2">
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in userInfo.wallet"
|
v-for="(item, index) in userInfo.wallet"
|
||||||
:key="index"
|
:key="index"
|
||||||
@@ -124,7 +129,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</BasicModal>
|
</BasicActionSheet>
|
||||||
|
|
||||||
<BasicModal
|
<BasicModal
|
||||||
v-model:visible="showWalletCharge"
|
v-model:visible="showWalletCharge"
|
||||||
@@ -150,7 +155,9 @@ import { useStaticApiListsStore } from '@/store/staticApisStore'
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import tbl from './components/table.vue'
|
import tbl from './components/table.vue'
|
||||||
import type { FormInstance } from 'ant-design-vue'
|
import type { FormInstance } from 'ant-design-vue'
|
||||||
|
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
||||||
import BasicInfoModal from '@/components/BasicInfoModal/index.vue'
|
import BasicInfoModal from '@/components/BasicInfoModal/index.vue'
|
||||||
|
import { isMobile } from '@core/utils/is'
|
||||||
import { message } from 'ant-design-vue/es'
|
import { message } from 'ant-design-vue/es'
|
||||||
import type { Info } from '@/components/BasicInfoModal/model/Info'
|
import type { Info } from '@/components/BasicInfoModal/model/Info'
|
||||||
import { toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
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>
|
<template>
|
||||||
<div>
|
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||||
<BasicShowContext title="لیست مشتریان">
|
<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>
|
<template #extraHeader>
|
||||||
<BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
<BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
||||||
<div>افزودن مشتری</div>
|
<div>افزودن مشتری</div>
|
||||||
<BaseIcon icon="vuesax-linear:user-add" Size="16px" />
|
<BaseIcon icon="vuesax-linear:user-add" Size="16px" />
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</template>
|
</template>
|
||||||
<template #action="{ column, record }">
|
<template #action="{ 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>
|
|
||||||
<div class="gap-2 hidden md:flex justify-center">
|
<div class="gap-2 hidden md:flex justify-center">
|
||||||
<a-tooltip :key="record.is_archive" placement="bottom" class="cursor-pointer">
|
<a-tooltip :key="record.is_archive" placement="bottom" class="cursor-pointer">
|
||||||
<template #title>
|
<template #title>
|
||||||
@@ -144,9 +27,7 @@
|
|||||||
cancel-text="رد"
|
cancel-text="رد"
|
||||||
placement="topLeft"
|
placement="topLeft"
|
||||||
@confirm="archiveFunction(record.id)"
|
@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" />
|
<Icon class="cursor-pointer" icon="vuesax-linear:archive-tick" color="#e3bf00" />
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
@@ -157,45 +38,27 @@
|
|||||||
@click="changeCustomerActivationFunction(record.id)"
|
@click="changeCustomerActivationFunction(record.id)"
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
<span>{{
|
<span>{{ record.active ? 'غیرفعال کردن حساب کاربری' : 'فعال کردن حساب کاربری' }}</span>
|
||||||
record.active ? 'غیرفعال کردن حساب کاربری' : 'فعال کردن حساب کاربری'
|
|
||||||
}}</span>
|
|
||||||
</template>
|
</template>
|
||||||
<Icon
|
<Icon
|
||||||
:icon="record.active ? 'vuesax-linear:user-tick' : 'vuesax-linear:user-remove'"
|
:icon="record.active ? 'vuesax-linear:user-tick' : 'vuesax-linear:user-remove'"
|
||||||
:color="record.active ? '#54AD06' : '#ad0627'"
|
:color="record.active ? '#54AD06' : '#ad0627'"
|
||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip
|
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('chargeMoney', record)">
|
||||||
placement="bottom"
|
<template #title><span>شارژ کیف پول ریالی</span></template>
|
||||||
class="cursor-pointer"
|
|
||||||
@click="emits('chargeMoney', record)"
|
|
||||||
>
|
|
||||||
<template #title>
|
|
||||||
<span>شارژ کیف پول ریالی</span>
|
|
||||||
</template>
|
|
||||||
<Icon icon="vuesax-linear:wallet-add" color="#9333ea" />
|
<Icon icon="vuesax-linear:wallet-add" color="#9333ea" />
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip
|
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('chargeGold', record)">
|
||||||
placement="bottom"
|
<template #title><span>شارژ کیف پول طلایی</span></template>
|
||||||
class="cursor-pointer"
|
|
||||||
@click="emits('chargeGold', record)"
|
|
||||||
>
|
|
||||||
<template #title>
|
|
||||||
<span>شارژ کیف پول طلایی</span>
|
|
||||||
</template>
|
|
||||||
<Icon icon="vuesax-linear:wallet-money" color="#ea580c" />
|
<Icon icon="vuesax-linear:wallet-money" color="#ea580c" />
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('info', record)">
|
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('info', record)">
|
||||||
<template #title>
|
<template #title><span>مشخصات</span></template>
|
||||||
<span>مشخصات</span>
|
|
||||||
</template>
|
|
||||||
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('edit', record)">
|
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('edit', record)">
|
||||||
<template #title>
|
<template #title><span>ویرایش</span></template>
|
||||||
<span>ویرایش</span>
|
|
||||||
</template>
|
|
||||||
<Icon icon="vuesax-linear:edit" color="#4572E9" />
|
<Icon icon="vuesax-linear:edit" color="#4572E9" />
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip
|
<a-tooltip
|
||||||
@@ -205,42 +68,41 @@
|
|||||||
@click="changeCustomerOrderActivationFunction(record.id)"
|
@click="changeCustomerOrderActivationFunction(record.id)"
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
<span>{{
|
<span>{{ record.trade_active ? 'غیر مجاز کردن سفارش دهی' : 'مجاز کردن سفارش دهی' }}</span>
|
||||||
record.trade_active ? 'غیر مجاز کردن سفارش دهی' : 'مجاز کردن سفارش دهی'
|
|
||||||
}}</span>
|
|
||||||
</template>
|
</template>
|
||||||
<Icon
|
<Icon
|
||||||
:icon="
|
:icon="record.trade_active === 1 ? 'vuesax-linear:box-tick' : 'vuesax-linear:box-remove'"
|
||||||
record.trade_active === 1 ? 'vuesax-linear:box-tick' : 'vuesax-linear:box-remove'
|
|
||||||
"
|
|
||||||
:color="record.trade_active === 1 ? '#54AD06' : '#ad0627'"
|
:color="record.trade_active === 1 ? '#54AD06' : '#ad0627'"
|
||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip
|
<a-tooltip
|
||||||
v-if="!record.is_legal"
|
v-if="!record.is_legal"
|
||||||
:key="record.trade_active"
|
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
@click="openConfirmUser(record)"
|
@click="openConfirmUser(record)"
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title><span>تایید کاربر</span></template>
|
||||||
<span>تایید کاربر</span>
|
|
||||||
</template>
|
|
||||||
<Icon icon="vuesax-linear:user-search" color="#0d9488" />
|
<Icon icon="vuesax-linear:user-search" color="#0d9488" />
|
||||||
</a-tooltip>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</BaseTable>
|
</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
|
<BasicModal
|
||||||
v-model:visible="confirmVisible"
|
v-model:visible="confirmVisible"
|
||||||
width="50vw"
|
width="50vw"
|
||||||
@@ -268,10 +130,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="tsx" setup>
|
<script setup lang="ts">
|
||||||
import type { TableInput } from '@core/type/baseTable'
|
import { message, Modal } from 'ant-design-vue'
|
||||||
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
import { ref } from 'vue'
|
||||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
|
||||||
import {
|
import {
|
||||||
changeCustomerActivation,
|
changeCustomerActivation,
|
||||||
changeCustomerArchive,
|
changeCustomerArchive,
|
||||||
@@ -280,59 +141,38 @@ import {
|
|||||||
getCustomerTable
|
getCustomerTable
|
||||||
} from '@/api/customer'
|
} from '@/api/customer'
|
||||||
import { cols } from '@/pages/menuPages/customers/base/hooks/customerColumns'
|
import { cols } from '@/pages/menuPages/customers/base/hooks/customerColumns'
|
||||||
import { useUserStore } from '@/store/userStore'
|
import CustomerMobileCardList from '@/pages/menuPages/customers/base/components/CustomerMobileCardList.vue'
|
||||||
import { usePrefix } from '@core/composable/usePrefix'
|
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||||
import { message } from 'ant-design-vue'
|
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
||||||
import { inputRules } from '@/utils/inputRules'
|
import type { TableInput } from '@core/type/baseTable'
|
||||||
import type { User } from '@/model/user'
|
|
||||||
import { toJalaliNoTime } from '@core/utils/useDataTime'
|
import { toJalaliNoTime } from '@core/utils/useDataTime'
|
||||||
import type { Customer } from '@/model/customer'
|
import type { Customer } from '@/model/customer'
|
||||||
import { onMounted } from 'vue'
|
import { isMobile } from '@core/utils/is'
|
||||||
|
|
||||||
const [submit, reload] = useBaseTable()
|
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(['delete', 'edit', 'add', 'info', 'chargeMoney', 'chargeGold'])
|
||||||
|
|
||||||
const { prefixCls } = usePrefix('pophoverstatus')
|
const loading = ref(false)
|
||||||
|
const confirmVisible = ref(false)
|
||||||
//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 confirmInfo = ref<Customer>()
|
||||||
|
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||||
|
|
||||||
const tblProps = ref<any>({
|
const tblProps = ref<any>({
|
||||||
columns: cols,
|
columns: cols,
|
||||||
api: (params: TableInput) => {
|
api: (params: TableInput) => getCustomerTable(params)
|
||||||
return getCustomerTable(params)
|
|
||||||
}
|
|
||||||
//bordered: true
|
|
||||||
})
|
})
|
||||||
const refPop = ref()
|
|
||||||
|
|
||||||
// function edit(recordId){
|
const fetch = async () => {
|
||||||
// emits('edit', recordId);
|
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||||
// console.log( recordId);
|
}
|
||||||
// console.log( visibleAction.value[parseInt(recordId)]);
|
|
||||||
// visibleAction.value[recordId]=false
|
defineExpose({ fetch })
|
||||||
// console.log('ddddd')
|
|
||||||
// }
|
|
||||||
const loading = ref<boolean>(false)
|
|
||||||
|
|
||||||
async function changeCustomerActivationFunction(id: number) {
|
async function changeCustomerActivationFunction(id: number) {
|
||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
await changeCustomerActivation(id)
|
await changeCustomerActivation(id)
|
||||||
reload()
|
await fetch()
|
||||||
message.success('تغییر وضعیت با موفقیت انجام شد')
|
message.success('تغییر وضعیت با موفقیت انجام شد')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
@@ -346,11 +186,21 @@ function openConfirmUser(user: Customer) {
|
|||||||
confirmVisible.value = true
|
confirmVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleArchive(customer: Customer) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: 'آیا مشتری را بایگانی می کنید؟',
|
||||||
|
okText: 'تایید',
|
||||||
|
cancelText: 'رد',
|
||||||
|
centered: true,
|
||||||
|
onOk: () => archiveFunction(customer.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async function archiveFunction(id: number) {
|
async function archiveFunction(id: number) {
|
||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
await changeCustomerArchive(id)
|
await changeCustomerArchive(id)
|
||||||
reload()
|
await fetch()
|
||||||
message.success('مشتری با موفقیت بایگانی شد.')
|
message.success('مشتری با موفقیت بایگانی شد.')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
@@ -363,7 +213,7 @@ async function confirmUserFunction() {
|
|||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
await confirmUser(confirmInfo.value?.id)
|
await confirmUser(confirmInfo.value?.id)
|
||||||
reload()
|
await fetch()
|
||||||
confirmVisible.value = false
|
confirmVisible.value = false
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
@@ -376,7 +226,7 @@ async function changeCustomerOrderActivationFunction(id: number) {
|
|||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
await changeCustomerOrderActivation(id)
|
await changeCustomerOrderActivation(id)
|
||||||
reload()
|
await fetch()
|
||||||
message.success('تغییر وضعیت سفارش دهی با موفقیت انجام شد')
|
message.success('تغییر وضعیت سفارش دهی با موفقیت انجام شد')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
@@ -384,42 +234,11 @@ async function changeCustomerOrderActivationFunction(id: number) {
|
|||||||
loading.value = false
|
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>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
@prefix: ~'@{prefixCls}-pophoverstatus';
|
|
||||||
.ant-popover,
|
.ant-popover,
|
||||||
.ant-tooltip {
|
.ant-tooltip {
|
||||||
z-index: 1001 !important;
|
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>
|
</style>
|
||||||
|
|||||||
@@ -208,7 +208,6 @@ export const cols: BaseTableColumn[] = [
|
|||||||
dataIndex: 'action',
|
dataIndex: 'action',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
slot: 'action',
|
slot: 'action',
|
||||||
fixed: 'right',
|
|
||||||
filterable: false
|
filterable: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="h-full min-h-0 min-w-0 overflow-hidden">
|
||||||
<!-- <BasicMainCard>-->
|
|
||||||
<div class="flex w-full h-full">
|
|
||||||
<tbl
|
<tbl
|
||||||
ref="table"
|
ref="table"
|
||||||
class="w-full h-full"
|
class="h-full min-h-0 w-full"
|
||||||
@info="info"
|
@info="info"
|
||||||
@edit="edit"
|
@edit="edit"
|
||||||
@add="add"
|
@add="add"
|
||||||
@@ -15,16 +13,21 @@
|
|||||||
<!-- <BasicMenu :menu="menu" width="13rem" @onChange="tabSelected" />
|
<!-- <BasicMenu :menu="menu" width="13rem" @onChange="tabSelected" />
|
||||||
<component :is="selectedComponent" class="mr-5" />-->
|
<component :is="selectedComponent" class="mr-5" />-->
|
||||||
<!-- <BasicShowContext title="پروژه ها"> <Search /> </BasicShowContext>-->
|
<!-- <BasicShowContext title="پروژه ها"> <Search /> </BasicShowContext>-->
|
||||||
</div>
|
|
||||||
<!-- </BasicMainCard>-->
|
<!-- </BasicMainCard>-->
|
||||||
<BasicModal
|
<BasicActionSheet
|
||||||
v-model:visible="addUserVisible"
|
v-model:visible="addUserVisible"
|
||||||
width="40rem"
|
|
||||||
:title="`${editId ? 'ویرایش' : 'افزودن'} کاربر`"
|
:title="`${editId ? 'ویرایش' : 'افزودن'} کاربر`"
|
||||||
@ok="ok"
|
modal-width="40rem"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
|
:showButton="true"
|
||||||
|
:showCancel="true"
|
||||||
|
:showOk="true"
|
||||||
|
ok-text="ثبت"
|
||||||
|
cancel-text="انصراف"
|
||||||
|
@ok="ok"
|
||||||
@close="addUserVisible = false"
|
@close="addUserVisible = false"
|
||||||
>
|
>
|
||||||
|
<div v-if="isMobile" class="mb-3 text-base font-700">{{ `${editId ? 'ویرایش' : 'افزودن'} کاربر` }}</div>
|
||||||
<a-form :model="form" ref="formRef" autocomplete="off">
|
<a-form :model="form" ref="formRef" autocomplete="off">
|
||||||
<div class="grid md:grid-cols-2 gap-4">
|
<div class="grid md:grid-cols-2 gap-4">
|
||||||
<a-form-item label="نام" name="fname" :rules="inputRules()">
|
<a-form-item label="نام" name="fname" :rules="inputRules()">
|
||||||
@@ -88,19 +91,20 @@
|
|||||||
</a-form-item>-->
|
</a-form-item>-->
|
||||||
</div>
|
</div>
|
||||||
</a-form>
|
</a-form>
|
||||||
</BasicModal>
|
</BasicActionSheet>
|
||||||
<BasicModal
|
<BasicActionSheet
|
||||||
v-model:visible="userInfoVisible"
|
v-model:visible="userInfoVisible"
|
||||||
width="100vw"
|
|
||||||
title="اطلاعات کاربر"
|
title="اطلاعات کاربر"
|
||||||
|
modal-width="100vw"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
custom-footer
|
:showButton="false"
|
||||||
@close="userInfoVisible = 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="اطلاعات جامع">
|
<a-tab-pane key="اطلاعات جامع" tab="اطلاعات جامع">
|
||||||
<div class="grid grid-cols-4 gap-2">
|
<div class="space-y-2">
|
||||||
<div class="col-span-4 bg-gray-100 w-full rounded-lg flex p-3 justify-between">
|
<div class="rounded-lg bg-gray-100 w-full flex flex-col gap-3 p-3 md:flex-row md:justify-between">
|
||||||
<div>
|
<div>
|
||||||
<div class="font-bold">{{ userInfo.name }}</div>
|
<div class="font-bold">{{ userInfo.name }}</div>
|
||||||
<div class="mt-1">{{ userInfo?.mobile }}</div>
|
<div class="mt-1">{{ userInfo?.mobile }}</div>
|
||||||
@@ -135,6 +139,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 gap-2 md:grid-cols-4">
|
||||||
<BasicInfoItem title="کد کاربری" :value="userInfo?.id" />
|
<BasicInfoItem title="کد کاربری" :value="userInfo?.id" />
|
||||||
<BasicInfoItem title="نوع کاربر" :value="userInfo?.type_str" />
|
<BasicInfoItem title="نوع کاربر" :value="userInfo?.type_str" />
|
||||||
<BasicInfoItem
|
<BasicInfoItem
|
||||||
@@ -166,7 +172,7 @@
|
|||||||
:value="userInfo?.add_user_limit ? `${userInfo?.add_user_limit} نفر` : '_'"
|
:value="userInfo?.add_user_limit ? `${userInfo?.add_user_limit} نفر` : '_'"
|
||||||
/>
|
/>
|
||||||
<BasicInfoItem title="آخرین بازدید" :value="toJalali(userInfo?.last_seen_at)" />
|
<BasicInfoItem title="آخرین بازدید" :value="toJalali(userInfo?.last_seen_at)" />
|
||||||
<BasicInfoItem title="وضعیت سفارش دهی">
|
<BasicInfoItem title="وضعیت سفارش دهی" class="col-span-2 md:col-span-1">
|
||||||
<CustomTag
|
<CustomTag
|
||||||
:textColor="userInfo?.trade_active === 1 ? '#25855A' : '#C53030'"
|
:textColor="userInfo?.trade_active === 1 ? '#25855A' : '#C53030'"
|
||||||
:bgColor="userInfo?.trade_active === 1 ? '#E3F4E9' : '#FECACA'"
|
:bgColor="userInfo?.trade_active === 1 ? '#E3F4E9' : '#FECACA'"
|
||||||
@@ -175,7 +181,9 @@
|
|||||||
{{ userInfo?.trade_active === 1 ? 'مجاز' : 'غیر مجاز' }}
|
{{ userInfo?.trade_active === 1 ? 'مجاز' : 'غیر مجاز' }}
|
||||||
</CustomTag>
|
</CustomTag>
|
||||||
</BasicInfoItem>
|
</BasicInfoItem>
|
||||||
<div class="col-span-4 grid grid-cols-2 gap-3">
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-3 md:grid-cols-2">
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in userInfo.wallet"
|
v-for="(item, index) in userInfo.wallet"
|
||||||
:key="index"
|
:key="index"
|
||||||
@@ -206,7 +214,7 @@
|
|||||||
<FundsTable ref="fundTable" :customerId="userInfo?.id" />
|
<FundsTable ref="fundTable" :customerId="userInfo?.id" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
</BasicModal>
|
</BasicActionSheet>
|
||||||
<BasicModal
|
<BasicModal
|
||||||
v-model:visible="showMoneyWalletCharge"
|
v-model:visible="showMoneyWalletCharge"
|
||||||
width="20rem"
|
width="20rem"
|
||||||
@@ -275,6 +283,7 @@ import { ref, watch } from 'vue'
|
|||||||
import tbl from './components/table.vue'
|
import tbl from './components/table.vue'
|
||||||
import type { FormInstance } from 'ant-design-vue'
|
import type { FormInstance } from 'ant-design-vue'
|
||||||
import BasicInfoModal from '@/components/BasicInfoModal/index.vue'
|
import BasicInfoModal from '@/components/BasicInfoModal/index.vue'
|
||||||
|
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
||||||
import { message } from 'ant-design-vue/es'
|
import { message } from 'ant-design-vue/es'
|
||||||
import type { Info } from '@/components/BasicInfoModal/model/Info'
|
import type { Info } from '@/components/BasicInfoModal/model/Info'
|
||||||
import { toJalali, toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
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 ReferralsTable from '@/pages/menuPages/customers/base/components/referralsTable.vue'
|
||||||
import type { Order } from '@/model/order'
|
import type { Order } from '@/model/order'
|
||||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||||
|
import { isMobile } from '@core/utils/is'
|
||||||
|
|
||||||
//tabs
|
//tabs
|
||||||
const activeKey = ref('اطلاعات جامع')
|
const activeKey = ref('اطلاعات جامع')
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="h-full min-h-0">
|
||||||
<BasicMainCard>
|
<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 }}-->
|
<!-- {{ 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
|
<BasicMenu
|
||||||
width="13rem"
|
width="13rem"
|
||||||
:menu="menu"
|
: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" />-->
|
<!-- <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>
|
</div>
|
||||||
</BasicMainCard>
|
</BasicMainCard>
|
||||||
</div>
|
</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>
|
<template>
|
||||||
<div>
|
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||||
<BasicShowContext title="لیست کارت های هدیه">
|
<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>
|
<template #extraHeader>
|
||||||
<BaseButton v-if="!customerId" type="primary" size="small" class="flex" @click="emits('add')">
|
<BaseButton v-if="!customerId" type="primary" size="small" class="flex" @click="emits('add')">
|
||||||
<div>افزودن کارت هدیه</div>
|
<div>افزودن کارت هدیه</div>
|
||||||
@@ -25,6 +33,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</BaseTable>
|
</BaseTable>
|
||||||
|
|
||||||
|
<GiftCardMobileCardList
|
||||||
|
v-else
|
||||||
|
ref="mobileListRef"
|
||||||
|
class="min-h-0 flex-1"
|
||||||
|
:customer-id="customerId"
|
||||||
|
@gift-card-codes="emits('giftcardcodes', $event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</BasicShowContext>
|
</BasicShowContext>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -33,81 +50,47 @@
|
|||||||
import type { TableInput } from '@core/type/baseTable'
|
import type { TableInput } from '@core/type/baseTable'
|
||||||
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
||||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||||
import { changeCustomerActivation, getCustomerTable } from '@/api/customer'
|
|
||||||
import { cols } from '../hooks/orderColumns'
|
import { cols } from '../hooks/orderColumns'
|
||||||
import { useUserStore } from '@/store/userStore'
|
|
||||||
import { usePrefix } from '@core/composable/usePrefix'
|
import { usePrefix } from '@core/composable/usePrefix'
|
||||||
import { message } from 'ant-design-vue'
|
import { getGiftCardTable } from '@/api/giftCard'
|
||||||
import { getOrderTable } from '@/api/order'
|
import GiftCardMobileCardList from './GiftCardMobileCardList.vue'
|
||||||
import {getGiftCardTable} from "@/api/giftCard";
|
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 [submit, reload] = useBaseTable()
|
||||||
/*const select = (val) => {
|
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||||
val.sts === 1 ? return ['1'] : ['2']
|
|
||||||
}*/
|
|
||||||
//emits
|
|
||||||
interface Prop {
|
|
||||||
customerId: number
|
|
||||||
}
|
|
||||||
const emits = defineEmits(['delete', 'edit', 'add', 'info', 'giftcardcodes'])
|
|
||||||
const props = defineProps<Prop>()
|
|
||||||
|
|
||||||
const { prefixCls } = usePrefix('pophoverstatus')
|
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<any>({
|
||||||
columns: cols,
|
columns: cols,
|
||||||
api: (params: TableInput) => {
|
api: (params: TableInput) => {
|
||||||
if (props.customerId) {
|
if (props.customerId) {
|
||||||
params.filters = []
|
params.filters = []
|
||||||
params.filters?.push({
|
params.filters?.push({
|
||||||
field: "customer_id",
|
field: 'customer_id',
|
||||||
type: "text",
|
type: 'text',
|
||||||
value: props.customerId,
|
value: props.customerId,
|
||||||
cr: "and",
|
cr: 'and',
|
||||||
op: "eq",
|
op: 'eq',
|
||||||
hidden: false
|
hidden: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return getGiftCardTable(params)
|
return getGiftCardTable(params)
|
||||||
}
|
}
|
||||||
//bordered: true
|
|
||||||
})
|
})
|
||||||
const loading = ref<boolean>(false)
|
|
||||||
|
|
||||||
async function changeCustomerActivationFunction(id: number) {
|
async function fetch() {
|
||||||
try {
|
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||||
loading.value = true
|
|
||||||
await changeCustomerActivation(id)
|
|
||||||
reload()
|
|
||||||
message.success('تغییر وضعیت با موفقیت انجام شد')
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetch = () => {
|
defineExpose({ fetch })
|
||||||
// console.log('sss')
|
|
||||||
reload()
|
|
||||||
}
|
|
||||||
// const spinTable = (val: boolean) => {
|
|
||||||
// // console.log('sss')
|
|
||||||
// table?.value?.loading.value = val
|
|
||||||
// }
|
|
||||||
defineExpose({
|
|
||||||
fetch
|
|
||||||
// spinTable
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
@@ -122,11 +105,5 @@ defineExpose({
|
|||||||
.ant-menu-item-only-child {
|
.ant-menu-item-only-child {
|
||||||
border-radius: @border-radius-base;
|
border-radius: @border-radius-base;
|
||||||
}
|
}
|
||||||
|
|
||||||
//bor
|
|
||||||
/* .ant-input-group-addon:first-child {
|
|
||||||
!* border: #d9d9d9 1px solid;
|
|
||||||
border-radius: 0 6px 6px 0;*!
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -17,69 +17,24 @@
|
|||||||
<!-- <BasicShowContext title="پروژه ها"> <Search /> </BasicShowContext>-->
|
<!-- <BasicShowContext title="پروژه ها"> <Search /> </BasicShowContext>-->
|
||||||
</div>
|
</div>
|
||||||
</BasicMainCard>
|
</BasicMainCard>
|
||||||
<BasicModal
|
<BasicActionSheet
|
||||||
v-model:visible="addGiftCardVisible"
|
v-model:visible="addGiftCardVisible"
|
||||||
width="30rem"
|
:title="giftCardSheetTitle"
|
||||||
:title="`${editId ? 'ویرایش' : 'افزودن'} کارت هدیه`"
|
modal-width="30rem"
|
||||||
@ok="ok"
|
drawer-height="90vh"
|
||||||
:loading="loading"
|
: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 v-if="isMobile" class="mb-3 text-base font-700">{{ giftCardSheetTitle }}</div>
|
||||||
<div class="grid md:grid-cols-1 gap-4">
|
<GiftCardEditorFormBody ref="giftCardEditorRef" :form="form" />
|
||||||
<a-form-item label="شماره موبایل مشتری" name="mobile" :rules="inputLenRules(11)">
|
</BasicActionSheet>
|
||||||
<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>
|
|
||||||
<BasicInfoModal
|
<BasicInfoModal
|
||||||
v-model:visible="userInfoVisible"
|
v-model:visible="userInfoVisible"
|
||||||
@close="userInfoVisible = false"
|
@close="userInfoVisible = false"
|
||||||
@@ -90,94 +45,20 @@
|
|||||||
:name="infoParams.name"
|
:name="infoParams.name"
|
||||||
:items="infoParams.items"
|
:items="infoParams.items"
|
||||||
/>
|
/>
|
||||||
<BasicModal
|
<GiftCardCodesSheet v-model:visible="giftCardCodesVisible" :codes="selectedGiftCardCodes" />
|
||||||
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>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// import BasicInfoModal from '@/components/BasicInfoModal'
|
// import BasicInfoModal from '@/components/BasicInfoModal'
|
||||||
import { inputLenRules, inputRules, inputRulesNumber } from '@/utils/inputRules'
|
|
||||||
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import tbl from './components/table.vue'
|
import tbl from './components/table.vue'
|
||||||
import type { FormInstance } from 'ant-design-vue'
|
import type { FormInstance } from 'ant-design-vue'
|
||||||
import BasicInfoModal from '@/components/BasicInfoModal/index.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 { message } from 'ant-design-vue/es'
|
||||||
import type { Info } from '@/components/BasicInfoModal/model/Info'
|
import type { Info } from '@/components/BasicInfoModal/model/Info'
|
||||||
import { toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
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 { createCustomer, deleteCustomer, getCustomersList, updateCustomer } from '@/api/customer'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
import type { Giftcard, Giftcardcode, GiftcardInput } from '@/model/giftCard'
|
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 { createGiftCard } from '@/api/giftCard'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
import { isMobile } from '@core/utils/is'
|
||||||
|
|
||||||
//loading
|
//loading
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
@@ -203,6 +83,17 @@ const store = useStaticApiListsStore()
|
|||||||
const table = ref(null)
|
const table = ref(null)
|
||||||
const refreshTable = () => table?.value?.fetch()
|
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
|
//form
|
||||||
const form = ref<GiftcardInput>({
|
const form = ref<GiftcardInput>({
|
||||||
mobile: '',
|
mobile: '',
|
||||||
@@ -213,61 +104,12 @@ const form = ref<GiftcardInput>({
|
|||||||
expiry_date: '',
|
expiry_date: '',
|
||||||
description: ''
|
description: ''
|
||||||
})
|
})
|
||||||
const formRef = ref<FormInstance>()
|
|
||||||
|
|
||||||
//info
|
//info
|
||||||
let infoParams = reactive<Info>({})
|
let infoParams = reactive<Info>({})
|
||||||
const giftCardCodesVisible = ref<boolean>(false)
|
const giftCardCodesVisible = ref<boolean>(false)
|
||||||
const selectedGiftCardCodes = ref<Giftcardcode[]>([])
|
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) {
|
function info(val: Giftcard) {
|
||||||
// نمایش اطلاعات اصلی در BasicInfoModal
|
// نمایش اطلاعات اصلی در BasicInfoModal
|
||||||
infoParams.title = 'اطلاعات کارت هدیه'
|
infoParams.title = 'اطلاعات کارت هدیه'
|
||||||
@@ -361,7 +203,7 @@ async function add() {
|
|||||||
|
|
||||||
//reset data
|
//reset data
|
||||||
const resetData = () => {
|
const resetData = () => {
|
||||||
formRef.value?.resetFields()
|
giftCardEditorRef.value?.resetFields?.()
|
||||||
form.value = {
|
form.value = {
|
||||||
mobile: '',
|
mobile: '',
|
||||||
name: '',
|
name: '',
|
||||||
@@ -382,7 +224,7 @@ const userInfoVisible = ref<boolean>(false)
|
|||||||
|
|
||||||
//submit gift card
|
//submit gift card
|
||||||
async function ok() {
|
async function ok() {
|
||||||
formRef.value?.validateFields().then(async () => {
|
await giftCardEditorRef.value?.validateFields?.().then(async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
// تنظیم تاریخ انقضا به آخر همان روز (23:59:59) و تبدیل به میلادی
|
// تنظیم تاریخ انقضا به آخر همان روز (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,7 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||||
<BasicShowContext title="لیست سفارشات">
|
<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>
|
<template #extraHeader>
|
||||||
<BaseButton v-if="!customerId" type="primary" size="small" class="flex" @click="emits('add')">
|
<BaseButton v-if="!customerId" type="primary" size="small" class="flex" @click="emits('add')">
|
||||||
<div>افزودن سفارش</div>
|
<div>افزودن سفارش</div>
|
||||||
@@ -19,6 +26,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</BaseTable>
|
</BaseTable>
|
||||||
|
|
||||||
|
<OrderMobileCardList
|
||||||
|
v-else
|
||||||
|
ref="mobileListRef"
|
||||||
|
class="min-h-0 flex-1"
|
||||||
|
:customer-id="customerId"
|
||||||
|
@info="emits('info', $event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</BasicShowContext>
|
</BasicShowContext>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -33,14 +49,17 @@ import { useUserStore } from '@/store/userStore'
|
|||||||
import { usePrefix } from '@core/composable/usePrefix'
|
import { usePrefix } from '@core/composable/usePrefix'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import { getOrderTable } from '@/api/order'
|
import { getOrderTable } from '@/api/order'
|
||||||
|
import OrderMobileCardList from './OrderMobileCardList.vue'
|
||||||
|
import { isMobile } from '@core/utils/is'
|
||||||
|
|
||||||
const [submit, reload] = useBaseTable()
|
const [submit, reload] = useBaseTable()
|
||||||
|
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||||
/*const select = (val) => {
|
/*const select = (val) => {
|
||||||
val.sts === 1 ? return ['1'] : ['2']
|
val.sts === 1 ? return ['1'] : ['2']
|
||||||
}*/
|
}*/
|
||||||
//emits
|
//emits
|
||||||
interface Prop {
|
interface Prop {
|
||||||
customerId: number
|
customerId?: number
|
||||||
}
|
}
|
||||||
const emits = defineEmits(['delete', 'edit', 'add', 'info'])
|
const emits = defineEmits(['delete', 'edit', 'add', 'info'])
|
||||||
const props = defineProps<Prop>()
|
const props = defineProps<Prop>()
|
||||||
@@ -89,9 +108,8 @@ async function changeCustomerActivationFunction(id: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetch = () => {
|
async function fetch() {
|
||||||
// console.log('sss')
|
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||||
reload()
|
|
||||||
}
|
}
|
||||||
// const spinTable = (val: boolean) => {
|
// const spinTable = (val: boolean) => {
|
||||||
// // console.log('sss')
|
// // console.log('sss')
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="h-full min-h-0">
|
||||||
<BasicMainCard>
|
<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
|
<tbl
|
||||||
ref="table"
|
ref="table"
|
||||||
class="w-full h-full"
|
class="h-full min-h-0 w-full"
|
||||||
@info="info"
|
@info="info"
|
||||||
@edit="edit"
|
@edit="edit"
|
||||||
@delete="deleteItem"
|
@delete="deleteItem"
|
||||||
@@ -16,107 +16,35 @@
|
|||||||
<!-- <BasicShowContext title="پروژه ها"> <Search /> </BasicShowContext>-->
|
<!-- <BasicShowContext title="پروژه ها"> <Search /> </BasicShowContext>-->
|
||||||
</div>
|
</div>
|
||||||
</BasicMainCard>
|
</BasicMainCard>
|
||||||
<BasicModal
|
<BasicActionSheet
|
||||||
v-model:visible="addOrderVisible"
|
v-model:visible="addOrderVisible"
|
||||||
width="25rem"
|
:title="orderSheetTitle"
|
||||||
:title="`${editId ? 'ویرایش' : 'افزودن'} سفارش`"
|
modal-width="25rem"
|
||||||
@ok="ok"
|
drawer-height="90vh"
|
||||||
:loading="loading"
|
: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 v-if="isMobile" class="mb-3 text-base font-700">{{ orderSheetTitle }}</div>
|
||||||
<div class="grid md:grid-cols-1 gap-4">
|
<OrderEditorFormBody
|
||||||
<div class="bg-gray-50 px-10 py-3 flex justify-center gap-4">
|
ref="orderEditorRef"
|
||||||
<div>قیمت {{ form.direction === 1 ? 'خرید' : 'فروش' }} طلا:</div>
|
:form="form"
|
||||||
<div class="font-bold">
|
:data-last="dataLast"
|
||||||
{{ usePrice(form.direction === 1 ? dataLast?.buy ?? 0 : dataLast?.sell ?? 0) }}
|
:user-items="userItems"
|
||||||
</div>
|
:selected-customer="selectedCustomer"
|
||||||
</div>
|
@change-price="changePrice"
|
||||||
<a-form-item label="نوع تراکنش" name="direction" :rules="inputRules()">
|
@change-price-value="changePriceValue"
|
||||||
<a-radio-group
|
@change-user="changeUser"
|
||||||
v-model:value="form.direction"
|
@calculate-gold="calculateGold"
|
||||||
button-style="solid"
|
@calculate-cash="calculateCash"
|
||||||
@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>
|
</BasicActionSheet>
|
||||||
<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>
|
|
||||||
<BasicInfoModal
|
<BasicInfoModal
|
||||||
v-model:visible="userInfoVisible"
|
v-model:visible="userInfoVisible"
|
||||||
@close="userInfoVisible = false"
|
@close="userInfoVisible = false"
|
||||||
@@ -132,25 +60,26 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// import BasicInfoModal from '@/components/BasicInfoModal'
|
// import BasicInfoModal from '@/components/BasicInfoModal'
|
||||||
import { inputLenRules, inputRules, inputRulesNumber } from '@/utils/inputRules'
|
|
||||||
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import tbl from './components/table.vue'
|
import tbl from './components/table.vue'
|
||||||
import type { FormInstance } from 'ant-design-vue'
|
import type { FormInstance } from 'ant-design-vue'
|
||||||
import BasicInfoModal from '@/components/BasicInfoModal/index.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 { message } from 'ant-design-vue/es'
|
||||||
import type { Info } from '@/components/BasicInfoModal/model/Info'
|
import type { Info } from '@/components/BasicInfoModal/model/Info'
|
||||||
import { toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
import { toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
||||||
import { confirmDeleteItem } from '@/utils/confirmModal'
|
import { confirmDeleteItem } from '@/utils/confirmModal'
|
||||||
import { cloneDeep } from 'lodash'
|
import { cloneDeep } from 'lodash'
|
||||||
import type { Item } from '@/model/Item'
|
|
||||||
import type { Customer } from '@/model/customer'
|
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 type { Order } from '@/model/order'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
import { getLastPrice } from '@/api/price'
|
import { getLastPrice } from '@/api/price'
|
||||||
import type { last } from '@/model/price'
|
import type { last } from '@/model/price'
|
||||||
import { createOrder } from '@/api/order'
|
import { createOrder } from '@/api/order'
|
||||||
|
import { isMobile } from '@core/utils/is'
|
||||||
|
|
||||||
//loading
|
//loading
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
@@ -191,9 +120,18 @@ const form = ref<Order>({
|
|||||||
goldCash: undefined,
|
goldCash: undefined,
|
||||||
goldValue: undefined
|
goldValue: undefined
|
||||||
})
|
})
|
||||||
const formRef = ref<FormInstance>()
|
const orderEditorRef = ref<{
|
||||||
|
validateFields?: () => ReturnType<NonNullable<FormInstance['validateFields']>>
|
||||||
|
resetFields?: () => void
|
||||||
|
}>()
|
||||||
const dataLast = ref<last>()
|
const dataLast = ref<last>()
|
||||||
let interval: any
|
let interval: ReturnType<typeof setInterval> | undefined
|
||||||
|
|
||||||
|
const orderSheetTitle = computed(() => `${editId ? 'ویرایش' : 'افزودن'} سفارش`)
|
||||||
|
|
||||||
|
function closeAddOrder() {
|
||||||
|
stopGetPrice()
|
||||||
|
}
|
||||||
|
|
||||||
async function setIntervalGetPrice() {
|
async function setIntervalGetPrice() {
|
||||||
try {
|
try {
|
||||||
@@ -226,12 +164,11 @@ async function getPrices() {
|
|||||||
|
|
||||||
async function changeUser(val: Customer) {
|
async function changeUser(val: Customer) {
|
||||||
selectedCustomer.value = cloneDeep(val)
|
selectedCustomer.value = cloneDeep(val)
|
||||||
console.log(selectedCustomer.value.wallet[0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopGetPrice() {
|
function stopGetPrice() {
|
||||||
clearInterval(interval)
|
if (interval) clearInterval(interval)
|
||||||
console.log('stop')
|
interval = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
//info
|
//info
|
||||||
@@ -384,7 +321,7 @@ async function add() {
|
|||||||
|
|
||||||
//reset data
|
//reset data
|
||||||
const resetData = () => {
|
const resetData = () => {
|
||||||
formRef.value?.resetFields()
|
orderEditorRef.value?.resetFields?.()
|
||||||
form.value = {
|
form.value = {
|
||||||
direction: 1,
|
direction: 1,
|
||||||
price: 0,
|
price: 0,
|
||||||
@@ -415,9 +352,7 @@ const userInfoVisible = ref<boolean>(false)
|
|||||||
|
|
||||||
//submit user
|
//submit user
|
||||||
async function ok() {
|
async function ok() {
|
||||||
formRef.value?.validateFields().then(async () => {
|
await orderEditorRef.value?.validateFields?.().then(async () => {
|
||||||
// console.log('salam', form.value)
|
|
||||||
// return
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
form.value.amount = form.value.goldValue
|
form.value.amount = form.value.goldValue
|
||||||
@@ -427,14 +362,11 @@ async function ok() {
|
|||||||
await createOrder(form.value)
|
await createOrder(form.value)
|
||||||
message.success('سفارش با موفقیت ثبت شد')
|
message.success('سفارش با موفقیت ثبت شد')
|
||||||
addOrderVisible.value = false
|
addOrderVisible.value = false
|
||||||
|
closeAddOrder()
|
||||||
resetData()
|
resetData()
|
||||||
refreshTable()
|
refreshTable()
|
||||||
// await getResponsible()
|
|
||||||
// await getQC()
|
|
||||||
// await getManager()
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
// message.error('خطا در بارگذاری اطلاعات')
|
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
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>
|
<template>
|
||||||
<div>
|
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||||
<BasicShowContext title="تاریخچه قیمت">
|
<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>
|
<template #extraHeader>
|
||||||
<BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
<BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
||||||
<div>قیمت گذاری</div>
|
<div>قیمت گذاری</div>
|
||||||
@@ -19,6 +27,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</BaseTable>
|
</BaseTable>
|
||||||
|
|
||||||
|
<PriceMobileCardList v-else ref="mobileListRef" class="min-h-0 flex-1" />
|
||||||
|
</div>
|
||||||
</BasicShowContext>
|
</BasicShowContext>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -27,63 +38,32 @@
|
|||||||
import type { TableInput } from '@core/type/baseTable'
|
import type { TableInput } from '@core/type/baseTable'
|
||||||
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
||||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||||
import { changeCustomerActivation, getCustomerTable } from '@/api/customer'
|
|
||||||
import { cols } from '../hooks/priceColumns'
|
import { cols } from '../hooks/priceColumns'
|
||||||
import { useUserStore } from '@/store/userStore'
|
|
||||||
import { usePrefix } from '@core/composable/usePrefix'
|
import { usePrefix } from '@core/composable/usePrefix'
|
||||||
import { message } from 'ant-design-vue'
|
|
||||||
import { getOrderTable } from '@/api/order'
|
|
||||||
import { getPriceTable } from '@/api/price'
|
import { getPriceTable } from '@/api/price'
|
||||||
|
import PriceMobileCardList from './PriceMobileCardList.vue'
|
||||||
|
import { isMobile } from '@core/utils/is'
|
||||||
|
|
||||||
const [submit, reload] = useBaseTable()
|
const [submit, reload] = useBaseTable()
|
||||||
/*const select = (val) => {
|
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||||
val.sts === 1 ? return ['1'] : ['2']
|
|
||||||
}*/
|
|
||||||
//emits
|
|
||||||
const emits = defineEmits(['delete', 'edit', 'add', 'info'])
|
const emits = defineEmits(['delete', 'edit', 'add', 'info'])
|
||||||
|
|
||||||
const { prefixCls } = usePrefix('pophoverstatus')
|
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<any>({
|
||||||
columns: cols,
|
columns: cols,
|
||||||
api: (params: TableInput) => {
|
api: (params: TableInput) => {
|
||||||
return getPriceTable(params)
|
return getPriceTable(params)
|
||||||
}
|
}
|
||||||
//bordered: true
|
|
||||||
})
|
})
|
||||||
const loading = ref<boolean>(false)
|
|
||||||
async function changeCustomerActivationFunction(id: number) {
|
async function fetch() {
|
||||||
try {
|
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||||
loading.value = true
|
|
||||||
await changeCustomerActivation(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({
|
defineExpose({
|
||||||
fetch
|
fetch
|
||||||
// spinTable
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -98,10 +78,5 @@ defineExpose({
|
|||||||
.ant-menu-item-only-child {
|
.ant-menu-item-only-child {
|
||||||
border-radius: @border-radius-base;
|
border-radius: @border-radius-base;
|
||||||
}
|
}
|
||||||
//bor
|
|
||||||
/* .ant-input-group-addon:first-child {
|
|
||||||
!* border: #d9d9d9 1px solid;
|
|
||||||
border-radius: 0 6px 6px 0;*!
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
</style>
|
</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>
|
<template>
|
||||||
<div>
|
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||||
<BasicShowContext title="لیست محصولات بایگانی شده">
|
<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>
|
<!-- <template #extraHeader>
|
||||||
<BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
<BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
||||||
<div>افزودن مشتری عمده</div>
|
<div>افزودن مشتری عمده</div>
|
||||||
@@ -46,6 +47,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</BaseTable>
|
</BaseTable>
|
||||||
|
<ArchiveMobileCardList
|
||||||
|
v-else
|
||||||
|
ref="mobileListRef"
|
||||||
|
class="min-h-0 flex-1"
|
||||||
|
@info="emits('info', $event)"
|
||||||
|
@unarchive="archiveFunction($event.id)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</BasicShowContext>
|
</BasicShowContext>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -65,6 +74,9 @@ import { cols } from '../hooks/archiveColumns'
|
|||||||
import { useUserStore } from '@/store/userStore'
|
import { useUserStore } from '@/store/userStore'
|
||||||
import { usePrefix } from '@core/composable/usePrefix'
|
import { usePrefix } from '@core/composable/usePrefix'
|
||||||
import { message } from 'ant-design-vue'
|
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 [submit, reload] = useBaseTable()
|
||||||
/*const select = (val) => {
|
/*const select = (val) => {
|
||||||
@@ -87,7 +99,7 @@ const table = ref(null)
|
|||||||
const tblProps = ref<any>({
|
const tblProps = ref<any>({
|
||||||
columns: cols,
|
columns: cols,
|
||||||
api: (params: TableInput) => {
|
api: (params: TableInput) => {
|
||||||
return getCustomerArchiveTable(params)
|
return getProductArchiveTable(params)
|
||||||
}
|
}
|
||||||
//bordered: true
|
//bordered: true
|
||||||
})
|
})
|
||||||
@@ -122,7 +134,7 @@ async function archiveFunction(id: number) {
|
|||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
await changeCustomerArchive(id)
|
await changeCustomerArchive(id)
|
||||||
reload()
|
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||||
message.success('مشتری با موفقیت از بایگانی خارج شد.')
|
message.success('مشتری با موفقیت از بایگانی خارج شد.')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
@@ -130,9 +142,10 @@ async function archiveFunction(id: number) {
|
|||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const fetch = () => {
|
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||||
// console.log('sss')
|
|
||||||
reload()
|
const fetch = async () => {
|
||||||
|
await Promise.allSettled([reload(), mobileListRef.value?.fetch?.() as Promise<void>])
|
||||||
}
|
}
|
||||||
// const spinTable = (val: boolean) => {
|
// const spinTable = (val: boolean) => {
|
||||||
// // console.log('sss')
|
// // console.log('sss')
|
||||||
|
|||||||
@@ -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,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="flex h-full min-h-0 min-w-0 max-w-full flex-col overflow-hidden">
|
||||||
<div class="md:flex pr-3">
|
<div class="shrink-0 pr-3 md:flex">
|
||||||
<div class="content-center">فعال سازی محصولات:</div>
|
<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>
|
<div>
|
||||||
@@ -20,19 +20,27 @@
|
|||||||
:defaultValue="dayjs('00:00:00', 'HH:mm:ss')"
|
:defaultValue="dayjs('00:00:00', 'HH:mm:ss')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
<BasicShowContext title=" لیست محصولات">
|
|
||||||
<BaseTable v-bind="tblProps" @register="submit" ref="tbl">
|
<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>
|
<template #extraHeader>
|
||||||
<BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
<BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
||||||
<div>افزودن محصول</div>
|
<div>افزودن محصول</div>
|
||||||
<BaseIcon icon="vuesax-linear:box" Size="16px" />
|
<BaseIcon icon="vuesax-linear:box" Size="16px" />
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</template>
|
</template>
|
||||||
<template #action="{ column, record }">
|
<template #action="{ record }">
|
||||||
<div class="gap-2 flex justify-center">
|
<div class="gap-2 flex justify-center">
|
||||||
<a-tooltip class="cursor-pointer" :key="record.open_buy">
|
<a-tooltip class="cursor-pointer" :key="`buy-${record.id}-${record.open_buy}`">
|
||||||
<template v-if="!record.open_buy" #title><span>فعال کردن خرید</span></template>
|
<template v-if="!record.open_buy" #title><span>فعال کردن خرید</span></template>
|
||||||
<template v-else #title><span>غیر فعال کردن خرید</span></template>
|
<template v-else #title><span>غیر فعال کردن خرید</span></template>
|
||||||
<BaseIcon
|
<BaseIcon
|
||||||
@@ -41,7 +49,7 @@
|
|||||||
@click="activeBuy(record)"
|
@click="activeBuy(record)"
|
||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip class="cursor-pointer" :key="record.open_sell">
|
<a-tooltip class="cursor-pointer" :key="`sell-${record.id}-${record.open_sell}`">
|
||||||
<template v-if="!record.open_sell" #title><span>فعال کردن فروش</span></template>
|
<template v-if="!record.open_sell" #title><span>فعال کردن فروش</span></template>
|
||||||
<template v-else #title><span>غیر فعال کردن فروش</span></template>
|
<template v-else #title><span>غیر فعال کردن فروش</span></template>
|
||||||
<BaseIcon
|
<BaseIcon
|
||||||
@@ -50,7 +58,7 @@
|
|||||||
@click="activeSell(record)"
|
@click="activeSell(record)"
|
||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip class="cursor-pointer" :key="record.active">
|
<a-tooltip class="cursor-pointer" :key="`active-${record.id}-${record.active}`">
|
||||||
<template v-if="!record.active" #title><span>فعال کردن محصول</span></template>
|
<template v-if="!record.active" #title><span>فعال کردن محصول</span></template>
|
||||||
<template v-else #title><span>غیر فعال کردن محصول</span></template>
|
<template v-else #title><span>غیر فعال کردن محصول</span></template>
|
||||||
<BaseIcon
|
<BaseIcon
|
||||||
@@ -66,546 +74,125 @@
|
|||||||
@click="activeProduct(record)"
|
@click="activeProduct(record)"
|
||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<!-- <a-tooltip class="cursor-pointer" :key="record.show">
|
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('edit', record)">
|
||||||
<template v-if="!record.show" #title><span>نشان دادن محصول</span></template>
|
<template #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" />
|
<BaseIcon icon="vuesax-linear:edit" color="#4572E9" />
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</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>
|
</template>
|
||||||
</BaseTable>
|
</BaseTable>
|
||||||
<BasicModal
|
|
||||||
v-model:visible="confirmVisible"
|
<ProductMobileCardList
|
||||||
width="50vw"
|
v-else
|
||||||
title="تایید مشتری"
|
ref="mobileListRef"
|
||||||
@ok="confirmUserFunction"
|
class="min-h-0 flex-1"
|
||||||
ok-text="تایید"
|
@edit="emits('edit', $event)"
|
||||||
:loading="loading"
|
@toggleBuy="activeBuy"
|
||||||
@close="confirmVisible = false"
|
@toggleSell="activeSell"
|
||||||
>
|
@toggleActive="activeProduct"
|
||||||
<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>
|
</div>
|
||||||
</BasicModal>
|
|
||||||
</BasicShowContext>
|
</BasicShowContext>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="tsx" setup>
|
<script setup lang="ts">
|
||||||
import type { TableInput } from '@core/type/baseTable'
|
import { message, Modal, TimePicker } from 'ant-design-vue'
|
||||||
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
import locale from 'ant-design-vue/es/date-picker/locale/fa_IR'
|
||||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
import dayjs from 'dayjs'
|
||||||
|
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
||||||
|
import { ref } from 'vue'
|
||||||
import {
|
import {
|
||||||
changeActiveProduct,
|
changeActiveProduct,
|
||||||
changeBuyActiveProduct,
|
changeBuyActiveProduct,
|
||||||
changeCustomerArchive,
|
changeSellActiveProduct,
|
||||||
changeCustomerOrderActivation,
|
getCustomerTable,
|
||||||
changeProductActivation, changeSellActiveProduct, changeVisibilityProduct,
|
setActiveTime
|
||||||
confirmUser,
|
} from '@/api/product'
|
||||||
getCustomerTable, setActiveTime
|
import { cols } from '@/pages/menuPages/products/base/hooks/productColumns'
|
||||||
} from "@/api/product";
|
import ProductMobileCardList from '@/pages/menuPages/products/base/components/ProductMobileCardList.vue'
|
||||||
import { cols } from '@/pages/menuPages/products/base/hooks/customerColumns'
|
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
||||||
import { useUserStore } from '@/store/userStore'
|
import type { TableInput } from '@core/type/baseTable'
|
||||||
import { usePrefix } from '@core/composable/usePrefix'
|
import type { Product } from '@/model/product'
|
||||||
import { message, type TimePickerProps } from "ant-design-vue";
|
import { isMobile } from '@core/utils/is'
|
||||||
import { inputRules } from '@/utils/inputRules'
|
|
||||||
import type { User } from '@/model/user'
|
dayjs.extend(customParseFormat)
|
||||||
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";
|
|
||||||
|
|
||||||
const [submit, reload] = useBaseTable()
|
const [submit, reload] = useBaseTable()
|
||||||
/*const select = (val) => {
|
const emits = defineEmits(['edit', 'add'])
|
||||||
val.sts === 1 ? return ['1'] : ['2']
|
|
||||||
}*/
|
|
||||||
//emits
|
|
||||||
const emits = defineEmits(['delete', 'edit', 'add', 'info', 'chargeMoney', 'chargeGold'])
|
|
||||||
|
|
||||||
const { prefixCls } = usePrefix('pophoverstatus')
|
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 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 startTime = ref(dayjs('00:00:00', 'HH:mm:ss'))
|
const startTime = ref(dayjs('00:00:00', 'HH:mm:ss'))
|
||||||
const endTime = ref(dayjs('00:00:00', 'HH:mm:ss'))
|
const endTime = ref(dayjs('00:00:00', 'HH:mm:ss'))
|
||||||
dayjs.extend(customParseFormat)
|
|
||||||
const customLocale = ref({
|
const customLocale = ref({
|
||||||
...locale,
|
...locale,
|
||||||
lang: {
|
lang: {
|
||||||
...locale.lang,
|
...locale.lang,
|
||||||
now: "اکنون",
|
now: 'اکنون',
|
||||||
ok: "ثبت",
|
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
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
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>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
@prefix: ~'@{prefixCls}-pophoverstatus';
|
|
||||||
.ant-popover,
|
.ant-popover,
|
||||||
.ant-tooltip {
|
.ant-tooltip {
|
||||||
z-index: 1001 !important;
|
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>
|
</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>
|
<template>
|
||||||
<div>
|
<div class="h-full min-h-0">
|
||||||
<BasicMainCard>
|
<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="shrink-0 overflow-auto md:hidden" />
|
||||||
<BasicMenu width="11rem" :menu="menu" mode="horizontal" class="md:hidden overflow-auto" />
|
|
||||||
<BasicMenu
|
<BasicMenu
|
||||||
width="13rem"
|
width="13rem"
|
||||||
:menu="menu"
|
: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="mt-5 min-h-0 min-w-0 w-full flex-1 overflow-hidden md:mr-5 md:mt-0" />
|
||||||
<RouterView class="md:mr-5 md:mt-0 mt-5 w-full"></RouterView>
|
|
||||||
</div>
|
</div>
|
||||||
</BasicMainCard>
|
</BasicMainCard>
|
||||||
</div>
|
</div>
|
||||||
@@ -20,21 +18,9 @@
|
|||||||
import BasicMenu from '@/components/BasicMenu/index.vue'
|
import BasicMenu from '@/components/BasicMenu/index.vue'
|
||||||
import { useRouterStore } from '@core/stores/router'
|
import { useRouterStore } from '@core/stores/router'
|
||||||
import { get } from 'lodash'
|
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 routerStore = useRouterStore()
|
||||||
const menu = computed(() => {
|
const menu = computed(() => {
|
||||||
const item = routerStore.routes.find((item) => item.name === 'products')
|
const item = routerStore.routes.find((item) => item.name === 'products')
|
||||||
//console.log('aaaa', get(item, 'children[0].children', []))
|
|
||||||
return get(item, 'children[0].children', [])
|
return get(item, 'children[0].children', [])
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
:style="{ backgroundImage: `url(${bg1})` }"
|
: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">
|
<a-form :model="form" ref="formRef" autocomplete="off" @finish="ok">
|
||||||
<div class="gap-4">
|
<div class="gap-4">
|
||||||
<a-form-item label="سقف واریز اولیه (ریال)" name="value" :rules="inputRules()">
|
<a-form-item label="سقف واریز اولیه (ریال)" name="value" :rules="inputRules()">
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
</a-form-item>-->
|
</a-form-item>-->
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
class="mt-10 w-full"
|
class="mt-4 w-full md:mt-10"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
type="primary"
|
type="primary"
|
||||||
size="middle"
|
size="middle"
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user