improve business info from admin panel
This commit is contained in:
parent
ca043a913f
commit
a97a29d50e
|
@ -19,6 +19,7 @@ use App\Entity\WalletTransaction;
|
|||
use App\Service\Extractor;
|
||||
use App\Service\Jdate;
|
||||
use App\Service\JsonResp;
|
||||
use App\Service\Log;
|
||||
use App\Service\Notification;
|
||||
use App\Service\Provider;
|
||||
use App\Service\registryMGR;
|
||||
|
@ -603,12 +604,16 @@ class AdminController extends AbstractController
|
|||
}
|
||||
$temp['totalPays'] = $totalPays;
|
||||
|
||||
$walletIncomes = $entityManager->getRepository(WalletTransaction::class)->findAllIncome($bid);
|
||||
$totalIcome = 0;
|
||||
foreach ($walletIncomes as $walletIncome) {
|
||||
$totalIcome += $walletIncome->getAmount();
|
||||
// محاسبه درآمد از تراکنشهای sell
|
||||
$walletSells = $entityManager->getRepository(WalletTransaction::class)->findBy(['bid' => $bid, 'type' => 'sell']);
|
||||
$totalIncome = 0;
|
||||
foreach ($walletSells as $walletSell) {
|
||||
$totalIncome += (float) $walletSell->getAmount();
|
||||
}
|
||||
$temp['totalIncome'] = $totalIcome;
|
||||
$temp['totalIncome'] = $totalIncome;
|
||||
|
||||
// محاسبه موجودی (درآمد - هزینه)
|
||||
$temp['walletBalance'] = $totalIncome - $totalPays;
|
||||
|
||||
$temp['id'] = $bid->getId();
|
||||
$temp['bidName'] = $bid->getName();
|
||||
|
@ -724,4 +729,457 @@ class AdminController extends AbstractController
|
|||
return $this->json($res);
|
||||
}
|
||||
|
||||
#[Route('/api/admin/business/charge/add', name: 'admin_business_charge_add', methods: ['POST'])]
|
||||
public function admin_business_charge_add(
|
||||
Request $request,
|
||||
EntityManagerInterface $entityManager,
|
||||
Log $logService,
|
||||
Jdate $jdate
|
||||
): JsonResponse {
|
||||
$params = json_decode($request->getContent(), true);
|
||||
|
||||
if (!isset($params['businessId']) || !isset($params['amount']) || !isset($params['description'])) {
|
||||
return $this->json(['success' => false, 'message' => 'تمام فیلدهای ضروری را وارد کنید']);
|
||||
}
|
||||
|
||||
$business = $entityManager->getRepository(Business::class)->find($params['businessId']);
|
||||
if (!$business) {
|
||||
return $this->json(['success' => false, 'message' => 'کسب و کار یافت نشد']);
|
||||
}
|
||||
|
||||
$currentCharge = (float) ($business->getSmsCharge() ?? 0);
|
||||
$newAmount = (float) $params['amount'];
|
||||
$newCharge = $currentCharge + $newAmount;
|
||||
|
||||
$business->setSmsCharge((string) $newCharge);
|
||||
$entityManager->persist($business);
|
||||
$entityManager->flush();
|
||||
|
||||
// ثبت لاگ
|
||||
$logService->insert(
|
||||
'مدیریت اعتبار',
|
||||
"افزایش اعتبار پیامک به مبلغ {$newAmount} ریال. اعتبار قبلی: {$currentCharge} ریال، اعتبار جدید: {$newCharge} ریال. توضیحات: {$params['description']}",
|
||||
$this->getUser(),
|
||||
$business
|
||||
);
|
||||
|
||||
return $this->json([
|
||||
'success' => true,
|
||||
'message' => 'اعتبار با موفقیت افزایش یافت',
|
||||
'data' => [
|
||||
'previousCharge' => $currentCharge,
|
||||
'newCharge' => $newCharge,
|
||||
'addedAmount' => $newAmount
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/api/admin/business/plugin/activate', name: 'admin_business_plugin_activate', methods: ['POST'])]
|
||||
public function admin_business_plugin_activate(
|
||||
Request $request,
|
||||
EntityManagerInterface $entityManager,
|
||||
Log $logService
|
||||
): JsonResponse {
|
||||
$params = json_decode($request->getContent(), true);
|
||||
|
||||
if (!isset($params['businessId']) || !isset($params['pluginCode']) || !isset($params['duration'])) {
|
||||
return $this->json(['success' => false, 'message' => 'تمام فیلدهای ضروری را وارد کنید']);
|
||||
}
|
||||
|
||||
$business = $entityManager->getRepository(Business::class)->find($params['businessId']);
|
||||
if (!$business) {
|
||||
return $this->json(['success' => false, 'message' => 'کسب و کار یافت نشد']);
|
||||
}
|
||||
|
||||
$pluginProduct = $entityManager->getRepository(\App\Entity\PluginProdect::class)->findOneBy(['code' => $params['pluginCode']]);
|
||||
if (!$pluginProduct) {
|
||||
return $this->json(['success' => false, 'message' => 'افزونه یافت نشد']);
|
||||
}
|
||||
|
||||
// بررسی اینکه آیا افزونه قبلاً فعال شده یا خیر
|
||||
$existingPlugin = $entityManager->getRepository(\App\Entity\Plugin::class)->findOneBy([
|
||||
'bid' => $business,
|
||||
'name' => $params['pluginCode']
|
||||
]);
|
||||
|
||||
$currentTime = time();
|
||||
$expireTime = $currentTime + ($params['duration'] * 86400); // تبدیل روز به ثانیه
|
||||
|
||||
if ($existingPlugin) {
|
||||
// اگر افزونه قبلاً فعال بوده، تاریخ انقضا را تمدید کن
|
||||
$oldExpire = $existingPlugin->getDateExpire();
|
||||
$existingPlugin->setDateExpire((string) $expireTime);
|
||||
$existingPlugin->setStatus('100');
|
||||
$entityManager->persist($existingPlugin);
|
||||
} else {
|
||||
// ایجاد افزونه جدید
|
||||
$plugin = new \App\Entity\Plugin();
|
||||
$plugin->setBid($business);
|
||||
$plugin->setName($params['pluginCode']);
|
||||
$plugin->setDateSubmit((string) $currentTime);
|
||||
$plugin->setDateExpire((string) $expireTime);
|
||||
$plugin->setStatus('100');
|
||||
$plugin->setSubmitter($this->getUser());
|
||||
$plugin->setPrice('0'); // رایگان برای ادمین
|
||||
$plugin->setDes($params['description'] ?? 'فعالسازی توسط ادمین');
|
||||
$entityManager->persist($plugin);
|
||||
}
|
||||
|
||||
$entityManager->flush();
|
||||
|
||||
// ثبت لاگ
|
||||
$durationText = $params['duration'] . ' روز';
|
||||
$logService->insert(
|
||||
'مدیریت افزونه',
|
||||
"فعالسازی افزونه {$pluginProduct->getName()} برای مدت {$durationText}. توضیحات: " . (isset($params['description']) ? $params['description'] : 'فعالسازی توسط ادمین'),
|
||||
$this->getUser(),
|
||||
$business
|
||||
);
|
||||
|
||||
return $this->json([
|
||||
'success' => true,
|
||||
'message' => 'افزونه با موفقیت فعال شد',
|
||||
'data' => [
|
||||
'pluginName' => $pluginProduct->getName(),
|
||||
'expireDate' => date('Y-m-d H:i:s', $expireTime),
|
||||
'duration' => $params['duration']
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/api/admin/business/report/{id}', name: 'admin_business_report', methods: ['GET'])]
|
||||
public function admin_business_report(
|
||||
string $id,
|
||||
EntityManagerInterface $entityManager,
|
||||
Jdate $jdate
|
||||
): JsonResponse {
|
||||
$business = $entityManager->getRepository(Business::class)->find($id);
|
||||
if (!$business) {
|
||||
return $this->json(['success' => false, 'message' => 'کسب و کار یافت نشد']);
|
||||
}
|
||||
|
||||
// آمار اشخاص
|
||||
$personsCount = count($entityManager->getRepository(\App\Entity\Person::class)->findBy(['bid' => $business]));
|
||||
|
||||
// آمار کالا و خدمات
|
||||
$commodityCount = count($entityManager->getRepository(\App\Entity\Commodity::class)->findBy(['bid' => $business]));
|
||||
|
||||
// آمار اسناد حسابداری
|
||||
$hesabdariDocsCount = count($entityManager->getRepository(\App\Entity\HesabdariDoc::class)->findBy(['bid' => $business]));
|
||||
|
||||
// آمار اسناد انبار
|
||||
$storeroomDocsCount = count($entityManager->getRepository(\App\Entity\StoreroomTicket::class)->findBy(['bid' => $business]));
|
||||
|
||||
// آمار بانکها
|
||||
$bankAccountsCount = count($entityManager->getRepository(\App\Entity\BankAccount::class)->findBy(['bid' => $business]));
|
||||
|
||||
// آمار سالهای مالی
|
||||
$yearsCount = count($entityManager->getRepository(\App\Entity\Year::class)->findBy(['bid' => $business]));
|
||||
|
||||
// آمار افزونههای فعال
|
||||
$activePlugins = $entityManager->getRepository(\App\Entity\Plugin::class)->findBy([
|
||||
'bid' => $business,
|
||||
'status' => '100'
|
||||
]);
|
||||
$activePluginsCount = count($activePlugins);
|
||||
|
||||
// لیست افزونههای فعال
|
||||
$activePluginsList = [];
|
||||
foreach ($activePlugins as $plugin) {
|
||||
$pluginProduct = $entityManager->getRepository(\App\Entity\PluginProdect::class)->findOneBy(['code' => $plugin->getName()]);
|
||||
$activePluginsList[] = [
|
||||
'name' => $pluginProduct ? $pluginProduct->getName() : $plugin->getName(),
|
||||
'expireDate' => $jdate->jdate('Y/n/d H:i', $plugin->getDateExpire()),
|
||||
'isExpired' => $plugin->getDateExpire() < time()
|
||||
];
|
||||
}
|
||||
|
||||
// محاسبه فضای آرشیو
|
||||
$archiveFiles = $entityManager->getRepository(\App\Entity\ArchiveFile::class)->findBy(['bid' => $business]);
|
||||
$totalArchiveSize = 0;
|
||||
foreach ($archiveFiles as $file) {
|
||||
$totalArchiveSize += (int) ($file->getFileSize() ? $file->getFileSize() : 0);
|
||||
}
|
||||
|
||||
// آمار کیف پول
|
||||
$walletTransactions = $entityManager->getRepository(\App\Entity\WalletTransaction::class)->findBy(['bid' => $business]);
|
||||
$walletIncome = 0;
|
||||
$walletExpense = 0;
|
||||
foreach ($walletTransactions as $transaction) {
|
||||
if ($transaction->getType() === 'sell') {
|
||||
$walletIncome += (float) $transaction->getAmount();
|
||||
} elseif ($transaction->getType() === 'pay') {
|
||||
$walletExpense += (float) $transaction->getAmount();
|
||||
}
|
||||
}
|
||||
|
||||
$report = [
|
||||
'businessInfo' => [
|
||||
'id' => $business->getId(),
|
||||
'name' => $business->getName(),
|
||||
'legalName' => $business->getLegalName(),
|
||||
'owner' => $business->getOwner()->getFullName(),
|
||||
'ownerMobile' => $business->getOwner()->getMobile(),
|
||||
'ownerEmail' => $business->getOwner()->getEmail(),
|
||||
'dateRegister' => $jdate->jdate('Y/n/d H:i', $business->getDateSubmit()),
|
||||
'field' => $business->getField(),
|
||||
'type' => $business->getType(),
|
||||
'address' => $business->getAddress(),
|
||||
'tel' => $business->getTel(),
|
||||
'mobile' => $business->getMobile(),
|
||||
'email' => $business->getEmail(),
|
||||
'website' => $business->getWesite(),
|
||||
'shenasemeli' => $business->getShenasemeli(),
|
||||
'codeeghtesadi' => $business->getCodeeghtesadi(),
|
||||
'shomaresabt' => $business->getShomaresabt(),
|
||||
'country' => $business->getCountry(),
|
||||
'ostan' => $business->getOstan(),
|
||||
'shahrestan' => $business->getShahrestan(),
|
||||
'postalcode' => $business->getPostalcode(),
|
||||
'maliyatafzode' => $business->getMaliyatafzode(),
|
||||
'avatar' => $business->getAvatar(),
|
||||
'sealFile' => $business->getSealFile(),
|
||||
],
|
||||
'statistics' => [
|
||||
'personsCount' => $personsCount,
|
||||
'commodityCount' => $commodityCount,
|
||||
'hesabdariDocsCount' => $hesabdariDocsCount,
|
||||
'storeroomDocsCount' => $storeroomDocsCount,
|
||||
'bankAccountsCount' => $bankAccountsCount,
|
||||
'yearsCount' => $yearsCount,
|
||||
'activePluginsCount' => $activePluginsCount,
|
||||
],
|
||||
'financial' => [
|
||||
'smsCharge' => (float) ($business->getSmsCharge() ?? 0),
|
||||
'walletEnabled' => $business->isWalletEnable(),
|
||||
'walletIncome' => $walletIncome,
|
||||
'walletExpense' => $walletExpense,
|
||||
'walletBalance' => $walletIncome - $walletExpense,
|
||||
],
|
||||
'storage' => [
|
||||
'archiveSize' => $business->getArchiveSize(),
|
||||
'totalArchiveSize' => $totalArchiveSize,
|
||||
'archiveFilesCount' => count($archiveFiles),
|
||||
],
|
||||
'plugins' => [
|
||||
'activeCount' => $activePluginsCount,
|
||||
'activeList' => $activePluginsList,
|
||||
],
|
||||
'features' => [
|
||||
'storeOnline' => $business->isStoreOnline(),
|
||||
'shortlinks' => $business->isShortlinks(),
|
||||
'walletEnable' => $business->isWalletEnable(),
|
||||
'commodityUpdateSellPriceAuto' => $business->isCommodityUpdateSellPriceAuto(),
|
||||
'commodityUpdateBuyPriceAuto' => $business->isCommodityUpdateBuyPriceAuto(),
|
||||
'profitCalcType' => $business->getProfitCalcType(),
|
||||
]
|
||||
];
|
||||
|
||||
return $this->json([
|
||||
'success' => true,
|
||||
'data' => $report
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/api/admin/business/wallet/balance/{id}', name: 'admin_business_wallet_balance', methods: ['GET'])]
|
||||
public function admin_business_wallet_balance(
|
||||
string $id,
|
||||
EntityManagerInterface $entityManager,
|
||||
Jdate $jdate
|
||||
): JsonResponse {
|
||||
$business = $entityManager->getRepository(Business::class)->find($id);
|
||||
if (!$business) {
|
||||
return $this->json(['success' => false, 'message' => 'کسب و کار یافت نشد']);
|
||||
}
|
||||
|
||||
if (!$business->isWalletEnable()) {
|
||||
return $this->json(['success' => false, 'message' => 'کیف پول برای این کسب و کار فعال نیست']);
|
||||
}
|
||||
|
||||
// محاسبه موجودی با استفاده از repository
|
||||
$walletBalance = $entityManager->getRepository(\App\Entity\WalletTransaction::class)->calculateWalletBalance($business);
|
||||
|
||||
// محاسبه درآمد و هزینه جداگانه
|
||||
$walletSells = $entityManager->getRepository(\App\Entity\WalletTransaction::class)->findBy(['bid' => $business, 'type' => 'sell']);
|
||||
$walletPays = $entityManager->getRepository(\App\Entity\WalletTransaction::class)->findBy(['bid' => $business, 'type' => 'pay']);
|
||||
|
||||
$totalIncome = 0;
|
||||
foreach ($walletSells as $sell) {
|
||||
$totalIncome += (float) $sell->getAmount();
|
||||
}
|
||||
|
||||
$totalExpense = 0;
|
||||
foreach ($walletPays as $pay) {
|
||||
$totalExpense += (float) $pay->getAmount();
|
||||
}
|
||||
|
||||
return $this->json([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'businessId' => $business->getId(),
|
||||
'businessName' => $business->getName(),
|
||||
'walletBalance' => $walletBalance,
|
||||
'totalIncome' => $totalIncome,
|
||||
'totalExpense' => $totalExpense,
|
||||
'transactionsCount' => [
|
||||
'sell' => count($walletSells),
|
||||
'pay' => count($walletPays)
|
||||
],
|
||||
'lastTransactions' => [
|
||||
'sells' => array_slice(array_map(function($sell) use ($jdate) {
|
||||
return [
|
||||
'id' => $sell->getId(),
|
||||
'amount' => (float) $sell->getAmount(),
|
||||
'date' => $jdate->jdate('Y/n/d H:i', $sell->getDateSubmit()),
|
||||
'description' => $sell->getDes()
|
||||
];
|
||||
}, $walletSells), 0, 5),
|
||||
'pays' => array_slice(array_map(function($pay) use ($jdate) {
|
||||
return [
|
||||
'id' => $pay->getId(),
|
||||
'amount' => (float) $pay->getAmount(),
|
||||
'date' => $jdate->jdate('Y/n/d H:i', $pay->getDateSubmit()),
|
||||
'description' => $pay->getDes(),
|
||||
'refID' => $pay->getRefID()
|
||||
];
|
||||
}, $walletPays), 0, 5)
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/api/admin/business/wallet/transactions/{id}', name: 'admin_business_wallet_transactions', methods: ['GET'])]
|
||||
public function admin_business_wallet_transactions(
|
||||
string $id,
|
||||
EntityManagerInterface $entityManager,
|
||||
Jdate $jdate,
|
||||
Request $request
|
||||
): JsonResponse {
|
||||
$business = $entityManager->getRepository(Business::class)->find($id);
|
||||
if (!$business) {
|
||||
return $this->json(['success' => false, 'message' => 'کسب و کار یافت نشد']);
|
||||
}
|
||||
|
||||
if (!$business->isWalletEnable()) {
|
||||
return $this->json(['success' => false, 'message' => 'کیف پول برای این کسب و کار فعال نیست']);
|
||||
}
|
||||
|
||||
// پارامترهای صفحهبندی
|
||||
$page = max(1, (int) ($request->query->get('page', 1)));
|
||||
$limit = max(1, min(100, (int) ($request->query->get('limit', 20))));
|
||||
$offset = ($page - 1) * $limit;
|
||||
|
||||
// فیلتر نوع تراکنش
|
||||
$type = $request->query->get('type'); // 'sell' یا 'pay' یا null برای همه
|
||||
|
||||
$qb = $entityManager->createQueryBuilder();
|
||||
$qb->select('w')
|
||||
->from(\App\Entity\WalletTransaction::class, 'w')
|
||||
->where('w.bid = :business')
|
||||
->setParameter('business', $business)
|
||||
->orderBy('w.dateSubmit', 'DESC');
|
||||
|
||||
if ($type && in_array($type, ['sell', 'pay'])) {
|
||||
$qb->andWhere('w.type = :type')
|
||||
->setParameter('type', $type);
|
||||
}
|
||||
|
||||
// محاسبه تعداد کل
|
||||
$countQb = clone $qb;
|
||||
$totalCount = $countQb->select('COUNT(w.id)')->getQuery()->getSingleScalarResult();
|
||||
|
||||
// اعمال صفحهبندی
|
||||
$qb->setFirstResult($offset)
|
||||
->setMaxResults($limit);
|
||||
|
||||
$transactions = $qb->getQuery()->getResult();
|
||||
|
||||
$transactionsData = [];
|
||||
foreach ($transactions as $transaction) {
|
||||
$transactionsData[] = [
|
||||
'id' => $transaction->getId(),
|
||||
'type' => $transaction->getType(),
|
||||
'amount' => (float) $transaction->getAmount(),
|
||||
'date' => $jdate->jdate('Y/n/d H:i', $transaction->getDateSubmit()),
|
||||
'description' => $transaction->getDes(),
|
||||
'refID' => $transaction->getRefID(),
|
||||
'shaba' => $transaction->getShaba(),
|
||||
'cardPan' => $transaction->getCardPan(),
|
||||
'gatePay' => $transaction->getGatePay(),
|
||||
'bank' => $transaction->getBank(),
|
||||
'submitter' => $transaction->getSubmitter() ? $transaction->getSubmitter()->getFullName() : null
|
||||
];
|
||||
}
|
||||
|
||||
return $this->json([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'businessId' => $business->getId(),
|
||||
'businessName' => $business->getName(),
|
||||
'transactions' => $transactionsData,
|
||||
'pagination' => [
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
'total' => (int) $totalCount,
|
||||
'totalPages' => ceil($totalCount / $limit)
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/api/admin/business/plugins/list/{id}', name: 'admin_business_plugins_list', methods: ['GET'])]
|
||||
public function admin_business_plugins_list(
|
||||
string $id,
|
||||
EntityManagerInterface $entityManager,
|
||||
Jdate $jdate
|
||||
): JsonResponse {
|
||||
$business = $entityManager->getRepository(Business::class)->find($id);
|
||||
if (!$business) {
|
||||
return $this->json(['success' => false, 'message' => 'کسب و کار یافت نشد']);
|
||||
}
|
||||
|
||||
// دریافت همه افزونههای موجود
|
||||
$allPlugins = $entityManager->getRepository(\App\Entity\PluginProdect::class)->findAll();
|
||||
|
||||
// دریافت افزونههای فعال این کسب و کار
|
||||
$businessPlugins = $entityManager->getRepository(\App\Entity\Plugin::class)->findBy([
|
||||
'bid' => $business,
|
||||
'status' => '100'
|
||||
]);
|
||||
$businessPluginCodes = array_map(fn($p) => $p->getName(), $businessPlugins);
|
||||
|
||||
$pluginsList = [];
|
||||
foreach ($allPlugins as $plugin) {
|
||||
$isActive = in_array($plugin->getCode(), $businessPluginCodes);
|
||||
$businessPlugin = null;
|
||||
|
||||
if ($isActive) {
|
||||
$businessPlugin = $entityManager->getRepository(\App\Entity\Plugin::class)->findOneBy([
|
||||
'bid' => $business,
|
||||
'name' => $plugin->getCode(),
|
||||
'status' => '100'
|
||||
]);
|
||||
}
|
||||
|
||||
$pluginsList[] = [
|
||||
'id' => $plugin->getId(),
|
||||
'name' => $plugin->getName(),
|
||||
'code' => $plugin->getCode(),
|
||||
'price' => $plugin->getPrice(),
|
||||
'timeLabel' => $plugin->getTimelabel(),
|
||||
'icon' => $plugin->getIcon(),
|
||||
'defaultOn' => $plugin->isDefaultOn(),
|
||||
'isActive' => $isActive,
|
||||
'expireDate' => $businessPlugin ? $jdate->jdate('Y/n/d H:i', $businessPlugin->getDateExpire()) : null,
|
||||
'isExpired' => $businessPlugin ? $businessPlugin->getDateExpire() < time() : false,
|
||||
'status' => $businessPlugin ? $businessPlugin->getStatus() : null,
|
||||
];
|
||||
}
|
||||
|
||||
return $this->json([
|
||||
'success' => true,
|
||||
'data' => $pluginsList
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ class WalletTransactionRepository extends ServiceEntityRepository
|
|||
{
|
||||
return $this->createQueryBuilder('w')
|
||||
->andWhere('w.bid = :val')
|
||||
->andWhere("w.type != 'pay'")
|
||||
->andWhere("w.type = 'sell'")
|
||||
->setParameter('val', $business)
|
||||
->orderBy('w.id', 'DESC')
|
||||
->getQuery()
|
||||
|
@ -37,6 +37,31 @@ class WalletTransactionRepository extends ServiceEntityRepository
|
|||
;
|
||||
}
|
||||
|
||||
public function calculateWalletBalance(Business $business): float
|
||||
{
|
||||
$qb = $this->createQueryBuilder('w');
|
||||
|
||||
// محاسبه مجموع تراکنشهای sell (درآمد)
|
||||
$incomeQuery = $qb->select('SUM(CAST(w.amount AS DECIMAL(10,2)))')
|
||||
->where('w.bid = :business')
|
||||
->andWhere("w.type = 'sell'")
|
||||
->setParameter('business', $business)
|
||||
->getQuery();
|
||||
|
||||
$income = $incomeQuery->getSingleScalarResult() ?? 0;
|
||||
|
||||
// محاسبه مجموع تراکنشهای pay (هزینه)
|
||||
$expenseQuery = $qb->select('SUM(CAST(w.amount AS DECIMAL(10,2)))')
|
||||
->where('w.bid = :business')
|
||||
->andWhere("w.type = 'pay'")
|
||||
->setParameter('business', $business)
|
||||
->getQuery();
|
||||
|
||||
$expense = $expenseQuery->getSingleScalarResult() ?? 0;
|
||||
|
||||
return (float) $income - (float) $expense;
|
||||
}
|
||||
|
||||
// public function findOneBySomeField($value): ?WalletTransaction
|
||||
// {
|
||||
// return $this->createQueryBuilder('w')
|
||||
|
|
126
webUI/src/components/admin/BusinessChargeDialog.vue
Normal file
126
webUI/src/components/admin/BusinessChargeDialog.vue
Normal file
|
@ -0,0 +1,126 @@
|
|||
<template>
|
||||
<v-dialog v-model="dialog" max-width="500px" persistent>
|
||||
<v-card>
|
||||
<v-toolbar color="primary" dark>
|
||||
<v-icon class="me-3">mdi-credit-card</v-icon>
|
||||
<v-toolbar-title>افزایش اعتبار پیامک - {{ business?.name || 'انتخاب نشده' }}</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click="closeDialog">
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
|
||||
<v-card-text class="pa-6">
|
||||
<v-form @submit.prevent="addCharge">
|
||||
<v-text-field
|
||||
v-model="amount"
|
||||
label="مبلغ (ریال)"
|
||||
type="number"
|
||||
required
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
class="mb-4"
|
||||
:rules="[v => !!v || 'مبلغ الزامی است', v => v > 0 || 'مبلغ باید بزرگتر از صفر باشد']"
|
||||
prepend-inner-icon="mdi-currency-usd"
|
||||
></v-text-field>
|
||||
<v-textarea
|
||||
v-model="description"
|
||||
label="توضیحات"
|
||||
required
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
rows="4"
|
||||
class="mb-4"
|
||||
:rules="[v => !!v || 'توضیحات الزامی است']"
|
||||
prepend-inner-icon="mdi-text"
|
||||
></v-textarea>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions class="pa-6 pt-0">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="error" variant="outlined" @click="closeDialog" class="me-3">
|
||||
<v-icon class="me-2">mdi-close</v-icon>
|
||||
انصراف
|
||||
</v-btn>
|
||||
<v-btn color="primary" @click="addCharge" :loading="loading">
|
||||
<v-icon class="me-2">mdi-check</v-icon>
|
||||
افزایش اعتبار
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import axios from 'axios'
|
||||
import Swal from 'sweetalert2'
|
||||
|
||||
interface Business {
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean
|
||||
business: Business | null | undefined
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
(e: 'success'): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const dialog = ref(false)
|
||||
const amount = ref('')
|
||||
const description = ref('')
|
||||
const loading = ref(false)
|
||||
|
||||
watch(() => props.modelValue, (newVal) => {
|
||||
dialog.value = newVal
|
||||
if (newVal && props.business) {
|
||||
amount.value = ''
|
||||
description.value = ''
|
||||
}
|
||||
})
|
||||
|
||||
watch(dialog, (newVal) => {
|
||||
emit('update:modelValue', newVal)
|
||||
})
|
||||
|
||||
const closeDialog = () => {
|
||||
dialog.value = false
|
||||
}
|
||||
|
||||
const addCharge = async () => {
|
||||
if (!amount.value || !description.value) {
|
||||
Swal.fire('خطا', 'لطفاً تمام فیلدها را پر کنید', 'error')
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await axios.post('/api/admin/business/charge/add', {
|
||||
businessId: props.business?.id,
|
||||
amount: parseFloat(amount.value),
|
||||
description: description.value
|
||||
})
|
||||
|
||||
if (response.data.success) {
|
||||
Swal.fire('موفق', response.data.message, 'success')
|
||||
closeDialog()
|
||||
emit('success')
|
||||
} else {
|
||||
Swal.fire('خطا', response.data.message, 'error')
|
||||
}
|
||||
} catch (error) {
|
||||
Swal.fire('خطا', 'خطا در ارتباط با سرور', 'error')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
179
webUI/src/components/admin/BusinessPluginDialog.vue
Normal file
179
webUI/src/components/admin/BusinessPluginDialog.vue
Normal file
|
@ -0,0 +1,179 @@
|
|||
<template>
|
||||
<v-dialog v-model="dialog" max-width="600px" persistent>
|
||||
<v-card>
|
||||
<v-toolbar color="success" dark>
|
||||
<v-icon class="me-3">mdi-puzzle</v-icon>
|
||||
<v-toolbar-title>فعالسازی افزونه - {{ business?.name || 'انتخاب نشده' }}</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click="closeDialog">
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
|
||||
<v-card-text class="pa-6">
|
||||
<v-form @submit.prevent="activatePlugin">
|
||||
<v-select
|
||||
v-model="selectedPlugin"
|
||||
:items="pluginsList"
|
||||
item-title="name"
|
||||
item-value="code"
|
||||
label="انتخاب افزونه"
|
||||
required
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
class="mb-4"
|
||||
:rules="[v => !!v || 'انتخاب افزونه الزامی است']"
|
||||
prepend-inner-icon="mdi-apps"
|
||||
>
|
||||
<template v-slot:item="{ props, item }">
|
||||
<v-list-item v-bind="props" class="py-2">
|
||||
<v-list-item-title class="text-body-1">{{ item.raw.name }}</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-caption">
|
||||
{{ item.raw.timeLabel }} - {{ item.raw.price }} ریال
|
||||
<v-chip v-if="item.raw.isActive" color="success" size="small" class="ms-2">فعال</v-chip>
|
||||
<v-chip v-else-if="item.raw.isExpired" color="error" size="small" class="ms-2">منقضی شده</v-chip>
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</template>
|
||||
</v-select>
|
||||
<v-text-field
|
||||
v-model="duration"
|
||||
label="مدت زمان (روز)"
|
||||
type="number"
|
||||
required
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
class="mb-4"
|
||||
:rules="[v => !!v || 'مدت زمان الزامی است', v => v > 0 || 'مدت زمان باید بزرگتر از صفر باشد']"
|
||||
prepend-inner-icon="mdi-calendar-clock"
|
||||
></v-text-field>
|
||||
<v-textarea
|
||||
v-model="description"
|
||||
label="توضیحات (اختیاری)"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
rows="3"
|
||||
class="mb-4"
|
||||
prepend-inner-icon="mdi-text"
|
||||
></v-textarea>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions class="pa-6 pt-0">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="error" variant="outlined" @click="closeDialog" class="me-3">
|
||||
<v-icon class="me-2">mdi-close</v-icon>
|
||||
انصراف
|
||||
</v-btn>
|
||||
<v-btn color="success" @click="activatePlugin" :loading="loading">
|
||||
<v-icon class="me-2">mdi-check</v-icon>
|
||||
فعالسازی
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import axios from 'axios'
|
||||
import Swal from 'sweetalert2'
|
||||
|
||||
interface Business {
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
|
||||
interface Plugin {
|
||||
id: number
|
||||
name: string
|
||||
code: string
|
||||
price: string
|
||||
timeLabel: string
|
||||
icon: string
|
||||
defaultOn: boolean
|
||||
isActive: boolean
|
||||
expireDate: string | null
|
||||
isExpired: boolean
|
||||
status: string | null
|
||||
}
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean
|
||||
business: Business | null | undefined
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
(e: 'success'): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const dialog = ref(false)
|
||||
const selectedPlugin = ref('')
|
||||
const duration = ref(30)
|
||||
const description = ref('')
|
||||
const pluginsList = ref<Plugin[]>([])
|
||||
const loading = ref(false)
|
||||
|
||||
watch(() => props.modelValue, async (newVal) => {
|
||||
dialog.value = newVal
|
||||
if (newVal && props.business) {
|
||||
selectedPlugin.value = ''
|
||||
duration.value = 30
|
||||
description.value = ''
|
||||
await loadPlugins()
|
||||
}
|
||||
})
|
||||
|
||||
watch(dialog, (newVal) => {
|
||||
emit('update:modelValue', newVal)
|
||||
})
|
||||
|
||||
const closeDialog = () => {
|
||||
dialog.value = false
|
||||
}
|
||||
|
||||
const loadPlugins = async () => {
|
||||
try {
|
||||
const response = await axios.get(`/api/admin/business/plugins/list/${props.business?.id}`)
|
||||
if (response.data.success) {
|
||||
pluginsList.value = response.data.data
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('خطا در بارگذاری لیست افزونهها:', error)
|
||||
Swal.fire('خطا', 'خطا در بارگذاری لیست افزونهها', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
const activatePlugin = async () => {
|
||||
if (!selectedPlugin.value || !duration.value) {
|
||||
Swal.fire('خطا', 'لطفاً افزونه و مدت زمان را انتخاب کنید', 'error')
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await axios.post('/api/admin/business/plugin/activate', {
|
||||
businessId: props.business?.id,
|
||||
pluginCode: selectedPlugin.value,
|
||||
duration: duration.value,
|
||||
description: description.value
|
||||
})
|
||||
|
||||
if (response.data.success) {
|
||||
Swal.fire('موفق', response.data.message, 'success')
|
||||
closeDialog()
|
||||
emit('success')
|
||||
} else {
|
||||
Swal.fire('خطا', response.data.message, 'error')
|
||||
}
|
||||
} catch (error) {
|
||||
Swal.fire('خطا', 'خطا در ارتباط با سرور', 'error')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
701
webUI/src/components/admin/BusinessReportDialog.vue
Normal file
701
webUI/src/components/admin/BusinessReportDialog.vue
Normal file
|
@ -0,0 +1,701 @@
|
|||
<template>
|
||||
<v-dialog v-model="dialog" max-width="1200px" persistent scrollable>
|
||||
<v-card v-if="businessReport" class="business-report-dialog">
|
||||
<!-- Header -->
|
||||
<v-toolbar color="primary" dark elevation="0">
|
||||
<v-avatar color="white" size="40" class="me-3">
|
||||
<v-icon color="primary" size="24">mdi-domain</v-icon>
|
||||
</v-avatar>
|
||||
<div>
|
||||
<v-toolbar-title class="text-h6 font-weight-bold">
|
||||
گزارش کامل کسب و کار
|
||||
</v-toolbar-title>
|
||||
<div class="text-caption">
|
||||
{{ businessReport?.businessInfo?.name || 'انتخاب نشده' }}
|
||||
</div>
|
||||
</div>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click="closeDialog" variant="text">
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
|
||||
<!-- Loading State -->
|
||||
<v-card-text v-if="loading" class="d-flex justify-center align-center" style="min-height: 400px;">
|
||||
<v-progress-circular indeterminate color="primary" size="64"></v-progress-circular>
|
||||
</v-card-text>
|
||||
|
||||
<!-- Content -->
|
||||
<v-card-text v-else class="pa-0">
|
||||
<!-- Quick Stats Bar -->
|
||||
<v-sheet color="grey-lighten-4" class="pa-4 mb-4">
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<div class="text-center">
|
||||
<div class="text-h4 font-weight-bold text-primary">{{ businessReport.statistics.personsCount }}</div>
|
||||
<div class="text-caption text-grey-600">اشخاص</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<div class="text-center">
|
||||
<div class="text-h4 font-weight-bold text-success">{{ businessReport.statistics.commodityCount }}</div>
|
||||
<div class="text-caption text-grey-600">کالا و خدمات</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<div class="text-center">
|
||||
<div class="text-h4 font-weight-bold text-info">{{ businessReport.statistics.hesabdariDocsCount }}</div>
|
||||
<div class="text-caption text-grey-600">اسناد حسابداری</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<div class="text-center">
|
||||
<div class="text-h4 font-weight-bold text-warning">{{ businessReport.statistics.storeroomDocsCount }}</div>
|
||||
<div class="text-caption text-grey-600">اسناد انبار</div>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-sheet>
|
||||
|
||||
<!-- Tabs -->
|
||||
<v-tabs v-model="activeTab" color="primary" class="px-4" show-arrows>
|
||||
<v-tab value="overview" prepend-icon="mdi-view-dashboard">
|
||||
<span class="d-none d-sm-inline">نمای کلی</span>
|
||||
</v-tab>
|
||||
<v-tab value="info" prepend-icon="mdi-information">
|
||||
<span class="d-none d-sm-inline">اطلاعات پایه</span>
|
||||
</v-tab>
|
||||
<v-tab value="financial" prepend-icon="mdi-currency-usd">
|
||||
<span class="d-none d-sm-inline">مالی</span>
|
||||
</v-tab>
|
||||
<v-tab value="plugins" prepend-icon="mdi-puzzle">
|
||||
<span class="d-none d-sm-inline">افزونهها</span>
|
||||
</v-tab>
|
||||
<v-tab value="storage" prepend-icon="mdi-cloud">
|
||||
<span class="d-none d-sm-inline">فضای ذخیره</span>
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-window v-model="activeTab" class="pa-6">
|
||||
<!-- نمای کلی -->
|
||||
<v-window-item value="overview">
|
||||
<v-row>
|
||||
<!-- Business Info Card -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-card class="h-100" variant="outlined">
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon color="primary" class="me-2">mdi-domain</v-icon>
|
||||
اطلاعات کسب و کار
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-list density="compact" class="pa-0">
|
||||
<v-list-item class="px-0">
|
||||
<template v-slot:prepend>
|
||||
<v-icon size="20" color="grey">mdi-account</v-icon>
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2">مالک</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-body-1 font-weight-medium">{{ businessReport.businessInfo.owner }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item class="px-0">
|
||||
<template v-slot:prepend>
|
||||
<v-icon size="20" color="grey">mdi-phone</v-icon>
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2">موبایل</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-body-1 font-weight-medium">{{ businessReport.businessInfo.ownerMobile }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item class="px-0">
|
||||
<template v-slot:prepend>
|
||||
<v-icon size="20" color="grey">mdi-email</v-icon>
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2">ایمیل</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-body-1 font-weight-medium">{{ businessReport.businessInfo.ownerEmail }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item class="px-0">
|
||||
<template v-slot:prepend>
|
||||
<v-icon size="20" color="grey">mdi-calendar</v-icon>
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2">تاریخ ثبت</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-body-1 font-weight-medium">{{ businessReport.businessInfo.dateRegister }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<!-- Financial Summary Card -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-card class="h-100" variant="outlined">
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon color="success" class="me-2">mdi-wallet</v-icon>
|
||||
خلاصه مالی
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<div class="mb-4">
|
||||
<div class="d-flex justify-space-between align-center mb-2">
|
||||
<span class="text-body-2">اعتبار پیامک</span>
|
||||
<span class="text-h6 font-weight-bold text-primary">{{ Math.floor(businessReport.financial.smsCharge).toLocaleString() }} ریال</span>
|
||||
</div>
|
||||
<v-progress-linear
|
||||
:model-value="Math.min((businessReport.financial.smsCharge / 100000) * 100, 100)"
|
||||
color="primary"
|
||||
height="8"
|
||||
rounded
|
||||
></v-progress-linear>
|
||||
</div>
|
||||
|
||||
<div v-if="businessReport.financial.walletEnabled" class="mb-4">
|
||||
<div class="d-flex justify-space-between align-center mb-2">
|
||||
<span class="text-body-2">موجودی کیف پول</span>
|
||||
<span class="text-h6 font-weight-bold" :class="businessReport.financial.walletBalance >= 0 ? 'text-success' : 'text-error'">
|
||||
{{ Math.floor(businessReport.financial.walletBalance).toLocaleString() }} ریال
|
||||
</span>
|
||||
</div>
|
||||
<v-progress-linear
|
||||
:model-value="Math.min(Math.abs(businessReport.financial.walletBalance / 1000000) * 100, 100)"
|
||||
:color="businessReport.financial.walletBalance >= 0 ? 'success' : 'error'"
|
||||
height="8"
|
||||
rounded
|
||||
></v-progress-linear>
|
||||
<div class="text-caption text-grey-600 mt-1">
|
||||
موجودی = تراکنشهای فروش - تراکنشهای پرداخت
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-space-between">
|
||||
<div class="text-center">
|
||||
<div class="text-caption text-grey-600">سالهای مالی</div>
|
||||
<div class="text-h6 font-weight-bold text-info">{{ businessReport.statistics.yearsCount }}</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-caption text-grey-600">حسابهای بانکی</div>
|
||||
<div class="text-h6 font-weight-bold text-warning">{{ businessReport.statistics.bankAccountsCount }}</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-caption text-grey-600">افزونههای فعال</div>
|
||||
<div class="text-h6 font-weight-bold text-secondary">{{ businessReport.plugins.activeCount }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<!-- Storage Usage Card -->
|
||||
<v-col cols="12">
|
||||
<v-card variant="outlined">
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon color="info" class="me-2">mdi-cloud</v-icon>
|
||||
استفاده از فضای ذخیره
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<div class="d-flex align-center justify-space-between mb-4">
|
||||
<div>
|
||||
<div class="text-h5 font-weight-bold">{{ (businessReport.storage.totalArchiveSize / 1024 / 1024).toFixed(2) }} مگابایت</div>
|
||||
<div class="text-subtitle-1 text-grey-600">{{ businessReport.storage.archiveFilesCount }} فایل</div>
|
||||
</div>
|
||||
<v-avatar size="80" color="info" variant="tonal">
|
||||
<v-icon size="40" color="info">mdi-cloud</v-icon>
|
||||
</v-avatar>
|
||||
</div>
|
||||
<v-progress-linear
|
||||
:model-value="Math.min((businessReport.storage.totalArchiveSize / 1024 / 1024 / 100) * 100, 100)"
|
||||
color="info"
|
||||
height="12"
|
||||
rounded
|
||||
></v-progress-linear>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-window-item>
|
||||
|
||||
<!-- اطلاعات پایه -->
|
||||
<v-window-item value="info">
|
||||
<v-row>
|
||||
<v-col cols="12" md="6">
|
||||
<v-card variant="outlined">
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon color="primary" class="me-2">mdi-information</v-icon>
|
||||
اطلاعات اصلی
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-list density="compact" class="pa-0">
|
||||
<v-list-item class="px-0">
|
||||
<template v-slot:prepend>
|
||||
<v-icon size="20" color="grey">mdi-domain</v-icon>
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2">نام کسب و کار</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-body-1 font-weight-medium">{{ businessReport.businessInfo.name }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item class="px-0">
|
||||
<template v-slot:prepend>
|
||||
<v-icon size="20" color="grey">mdi-file-document</v-icon>
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2">نام قانونی</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-body-1 font-weight-medium">{{ businessReport.businessInfo.legalName }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item class="px-0">
|
||||
<template v-slot:prepend>
|
||||
<v-icon size="20" color="grey">mdi-briefcase</v-icon>
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2">رشته فعالیت</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-body-1 font-weight-medium">{{ businessReport.businessInfo.field || 'تعریف نشده' }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item class="px-0">
|
||||
<template v-slot:prepend>
|
||||
<v-icon size="20" color="grey">mdi-tag</v-icon>
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2">نوع</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-body-1 font-weight-medium">{{ businessReport.businessInfo.type || 'تعریف نشده' }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="6">
|
||||
<v-card variant="outlined">
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon color="primary" class="me-2">mdi-map-marker</v-icon>
|
||||
اطلاعات تماس
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-list density="compact" class="pa-0">
|
||||
<v-list-item class="px-0">
|
||||
<template v-slot:prepend>
|
||||
<v-icon size="20" color="grey">mdi-map-marker</v-icon>
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2">آدرس</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-body-1 font-weight-medium">{{ businessReport.businessInfo.address || 'تعریف نشده' }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item class="px-0">
|
||||
<template v-slot:prepend>
|
||||
<v-icon size="20" color="grey">mdi-phone</v-icon>
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2">تلفن</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-body-1 font-weight-medium">{{ businessReport.businessInfo.tel || 'تعریف نشده' }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item class="px-0">
|
||||
<template v-slot:prepend>
|
||||
<v-icon size="20" color="grey">mdi-cellphone</v-icon>
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2">موبایل</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-body-1 font-weight-medium">{{ businessReport.businessInfo.mobile || 'تعریف نشده' }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item class="px-0">
|
||||
<template v-slot:prepend>
|
||||
<v-icon size="20" color="grey">mdi-email</v-icon>
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2">ایمیل</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-body-1 font-weight-medium">{{ businessReport.businessInfo.email || 'تعریف نشده' }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-window-item>
|
||||
|
||||
<!-- مالی -->
|
||||
<v-window-item value="financial">
|
||||
<v-row>
|
||||
<!-- SMS Credit -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-card variant="outlined" class="h-100">
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon color="primary" class="me-2">mdi-message-text</v-icon>
|
||||
اعتبار پیامک
|
||||
</v-card-title>
|
||||
<v-card-text class="text-center">
|
||||
<v-avatar size="120" color="primary" variant="tonal" class="mb-4">
|
||||
<v-icon size="60" color="primary">mdi-message-text</v-icon>
|
||||
</v-avatar>
|
||||
<div class="text-h3 font-weight-bold text-primary mb-2">{{ Math.floor(businessReport.financial.smsCharge).toLocaleString() }}</div>
|
||||
<div class="text-subtitle-1 text-grey-600">ریال</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<!-- Wallet -->
|
||||
<v-col cols="12" md="6" v-if="businessReport.financial.walletEnabled">
|
||||
<v-card variant="outlined" class="h-100">
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon color="success" class="me-2">mdi-wallet</v-icon>
|
||||
کیف پول
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col cols="4" class="text-center">
|
||||
<v-tooltip location="top">
|
||||
<template v-slot:activator="{ props }">
|
||||
<div v-bind="props">
|
||||
<v-avatar size="60" color="success" variant="tonal" class="mb-2">
|
||||
<v-icon size="30" color="success">mdi-trending-up</v-icon>
|
||||
</v-avatar>
|
||||
<div class="text-h6 font-weight-bold text-success">{{ Math.floor(businessReport.financial.walletIncome).toLocaleString() }}</div>
|
||||
<div class="text-caption text-grey-600">درآمد (فروش)</div>
|
||||
</div>
|
||||
</template>
|
||||
<span>مجموع تراکنشهای فروش (sell)</span>
|
||||
</v-tooltip>
|
||||
</v-col>
|
||||
<v-col cols="4" class="text-center">
|
||||
<v-tooltip location="top">
|
||||
<template v-slot:activator="{ props }">
|
||||
<div v-bind="props">
|
||||
<v-avatar size="60" color="error" variant="tonal" class="mb-2">
|
||||
<v-icon size="30" color="error">mdi-trending-down</v-icon>
|
||||
</v-avatar>
|
||||
<div class="text-h6 font-weight-bold text-error">{{ Math.floor(businessReport.financial.walletExpense).toLocaleString() }}</div>
|
||||
<div class="text-caption text-grey-600">هزینه (پرداخت)</div>
|
||||
</div>
|
||||
</template>
|
||||
<span>مجموع تراکنشهای پرداخت (pay)</span>
|
||||
</v-tooltip>
|
||||
</v-col>
|
||||
<v-col cols="4" class="text-center">
|
||||
<v-tooltip location="top">
|
||||
<template v-slot:activator="{ props }">
|
||||
<div v-bind="props">
|
||||
<v-avatar size="60" color="primary" variant="tonal" class="mb-2">
|
||||
<v-icon size="30" color="primary">mdi-wallet</v-icon>
|
||||
</v-avatar>
|
||||
<div class="text-h6 font-weight-bold text-primary">{{ Math.floor(businessReport.financial.walletBalance).toLocaleString() }}</div>
|
||||
<div class="text-caption text-grey-600">موجودی</div>
|
||||
</div>
|
||||
</template>
|
||||
<span>موجودی = درآمد (فروش) - هزینه (پرداخت)</span>
|
||||
</v-tooltip>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<!-- Financial Statistics -->
|
||||
<v-col cols="12">
|
||||
<v-card variant="outlined">
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon color="info" class="me-2">mdi-chart-line</v-icon>
|
||||
آمار مالی
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<div class="text-center pa-4">
|
||||
<v-avatar size="80" color="primary" variant="tonal" class="mb-3">
|
||||
<v-icon size="40" color="primary">mdi-calendar</v-icon>
|
||||
</v-avatar>
|
||||
<div class="text-h4 font-weight-bold text-primary">{{ businessReport.statistics.yearsCount }}</div>
|
||||
<div class="text-subtitle-1">سالهای مالی</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<div class="text-center pa-4">
|
||||
<v-avatar size="80" color="success" variant="tonal" class="mb-3">
|
||||
<v-icon size="40" color="success">mdi-bank</v-icon>
|
||||
</v-avatar>
|
||||
<div class="text-h4 font-weight-bold text-success">{{ businessReport.statistics.bankAccountsCount }}</div>
|
||||
<div class="text-subtitle-1">حسابهای بانکی</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<div class="text-center pa-4">
|
||||
<v-avatar size="80" color="info" variant="tonal" class="mb-3">
|
||||
<v-icon size="40" color="info">mdi-file-document</v-icon>
|
||||
</v-avatar>
|
||||
<div class="text-h4 font-weight-bold text-info">{{ businessReport.statistics.hesabdariDocsCount }}</div>
|
||||
<div class="text-subtitle-1">اسناد حسابداری</div>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<div class="text-center pa-4">
|
||||
<v-avatar size="80" color="warning" variant="tonal" class="mb-3">
|
||||
<v-icon size="40" color="warning">mdi-warehouse</v-icon>
|
||||
</v-avatar>
|
||||
<div class="text-h4 font-weight-bold text-warning">{{ businessReport.statistics.storeroomDocsCount }}</div>
|
||||
<div class="text-subtitle-1">اسناد انبار</div>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-window-item>
|
||||
|
||||
<!-- افزونهها -->
|
||||
<v-window-item value="plugins">
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-card variant="outlined">
|
||||
<v-card-title class="d-flex align-center justify-space-between">
|
||||
<div class="d-flex align-center">
|
||||
<v-icon color="secondary" class="me-2">mdi-puzzle</v-icon>
|
||||
افزونههای فعال
|
||||
</div>
|
||||
<v-chip color="secondary" variant="tonal">
|
||||
{{ businessReport.plugins.activeCount }} افزونه
|
||||
</v-chip>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<div v-if="businessReport.plugins.activeList.length > 0">
|
||||
<v-row>
|
||||
<v-col cols="12" md="6" v-for="plugin in businessReport.plugins.activeList" :key="plugin.name">
|
||||
<v-card variant="outlined" class="plugin-card">
|
||||
<v-card-text class="pa-4">
|
||||
<div class="d-flex align-center justify-space-between mb-3">
|
||||
<div class="d-flex align-center">
|
||||
<v-avatar size="40" :color="plugin.isExpired ? 'error' : 'success'" variant="tonal" class="me-3">
|
||||
<v-icon :color="plugin.isExpired ? 'error' : 'success'">
|
||||
{{ plugin.isExpired ? 'mdi-alert' : 'mdi-check' }}
|
||||
</v-icon>
|
||||
</v-avatar>
|
||||
<div>
|
||||
<div class="text-h6 font-weight-medium">{{ plugin.name }}</div>
|
||||
<div class="text-caption text-grey-600">انقضا: {{ plugin.expireDate }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<v-chip
|
||||
:color="plugin.isExpired ? 'error' : 'success'"
|
||||
size="small"
|
||||
variant="tonal"
|
||||
>
|
||||
{{ plugin.isExpired ? 'منقضی شده' : 'فعال' }}
|
||||
</v-chip>
|
||||
</div>
|
||||
<v-progress-linear
|
||||
:model-value="plugin.isExpired ? 100 : 75"
|
||||
:color="plugin.isExpired ? 'error' : 'success'"
|
||||
height="6"
|
||||
rounded
|
||||
></v-progress-linear>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
<v-alert v-else type="info" variant="tonal" class="mt-4">
|
||||
<v-icon class="me-2">mdi-information</v-icon>
|
||||
هیچ افزونه فعالی یافت نشد
|
||||
</v-alert>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-window-item>
|
||||
|
||||
<!-- فضای ذخیره -->
|
||||
<v-window-item value="storage">
|
||||
<v-row>
|
||||
<v-col cols="12" md="8">
|
||||
<v-card variant="outlined">
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon color="info" class="me-2">mdi-cloud</v-icon>
|
||||
فضای آرشیو
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<div class="d-flex align-center justify-space-between mb-6">
|
||||
<div>
|
||||
<div class="text-h3 font-weight-bold text-info mb-2">{{ (businessReport.storage.totalArchiveSize / 1024 / 1024).toFixed(2) }}</div>
|
||||
<div class="text-subtitle-1 text-grey-600">مگابایت استفاده شده</div>
|
||||
</div>
|
||||
<v-avatar size="120" color="info" variant="tonal">
|
||||
<v-icon size="60" color="info">mdi-cloud</v-icon>
|
||||
</v-avatar>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="d-flex justify-space-between align-center mb-2">
|
||||
<span class="text-body-1">فضای استفاده شده</span>
|
||||
<span class="text-body-1 font-weight-medium">{{ (businessReport.storage.totalArchiveSize / 1024 / 1024).toFixed(2) }} MB</span>
|
||||
</div>
|
||||
<v-progress-linear
|
||||
:model-value="Math.min((businessReport.storage.totalArchiveSize / 1024 / 1024 / 100) * 100, 100)"
|
||||
color="info"
|
||||
height="16"
|
||||
rounded
|
||||
>
|
||||
<template v-slot:default="{ value }">
|
||||
<strong>{{ Math.ceil(value) }}%</strong>
|
||||
</template>
|
||||
</v-progress-linear>
|
||||
</div>
|
||||
|
||||
<v-divider class="my-4"></v-divider>
|
||||
|
||||
<div class="text-center">
|
||||
<div class="text-h5 font-weight-bold text-grey-600 mb-2">{{ businessReport.storage.archiveFilesCount }}</div>
|
||||
<div class="text-subtitle-1">تعداد فایلهای آرشیو شده</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="4">
|
||||
<v-card variant="outlined" class="h-100">
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon color="warning" class="me-2">mdi-information</v-icon>
|
||||
نکات مهم
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-list density="compact" class="pa-0">
|
||||
<v-list-item class="px-0">
|
||||
<template v-slot:prepend>
|
||||
<v-icon size="20" color="info">mdi-check-circle</v-icon>
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2">فضای آرشیو برای نگهداری فایلهای قدیمی</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item class="px-0">
|
||||
<template v-slot:prepend>
|
||||
<v-icon size="20" color="warning">mdi-alert</v-icon>
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2">پیشنهاد میشود فضای خالی کافی داشته باشید</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item class="px-0">
|
||||
<template v-slot:prepend>
|
||||
<v-icon size="20" color="success">mdi-download</v-icon>
|
||||
</template>
|
||||
<v-list-item-title class="text-body-2">امکان دانلود فایلهای آرشیو</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import axios from 'axios'
|
||||
import Swal from 'sweetalert2'
|
||||
|
||||
interface Business {
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
|
||||
interface BusinessReport {
|
||||
businessInfo: {
|
||||
id: number
|
||||
name: string
|
||||
legalName: string
|
||||
owner: string
|
||||
ownerMobile: string
|
||||
ownerEmail: string
|
||||
dateRegister: string
|
||||
field: string
|
||||
type: string
|
||||
address: string
|
||||
tel: string
|
||||
mobile: string
|
||||
email: string
|
||||
}
|
||||
statistics: {
|
||||
personsCount: number
|
||||
commodityCount: number
|
||||
hesabdariDocsCount: number
|
||||
storeroomDocsCount: number
|
||||
bankAccountsCount: number
|
||||
yearsCount: number
|
||||
activePluginsCount: number
|
||||
}
|
||||
financial: {
|
||||
smsCharge: number
|
||||
walletEnabled: boolean
|
||||
walletIncome: number
|
||||
walletExpense: number
|
||||
walletBalance: number
|
||||
}
|
||||
storage: {
|
||||
archiveSize: string
|
||||
totalArchiveSize: number
|
||||
archiveFilesCount: number
|
||||
}
|
||||
plugins: {
|
||||
activeCount: number
|
||||
activeList: Array<{
|
||||
name: string
|
||||
expireDate: string
|
||||
isExpired: boolean
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean
|
||||
business: Business | null | undefined
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const dialog = ref(false)
|
||||
const activeTab = ref('overview')
|
||||
const businessReport = ref<BusinessReport | null>(null)
|
||||
const loading = ref(false)
|
||||
|
||||
watch(() => props.modelValue, async (newVal) => {
|
||||
dialog.value = newVal
|
||||
if (newVal && props.business) {
|
||||
await loadReport()
|
||||
}
|
||||
})
|
||||
|
||||
watch(dialog, (newVal) => {
|
||||
emit('update:modelValue', newVal)
|
||||
})
|
||||
|
||||
const closeDialog = () => {
|
||||
dialog.value = false
|
||||
}
|
||||
|
||||
const loadReport = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await axios.get(`/api/admin/business/report/${props.business?.id}`)
|
||||
if (response.data.success) {
|
||||
businessReport.value = response.data.data
|
||||
}
|
||||
} catch (error) {
|
||||
Swal.fire('خطا', 'خطا در بارگذاری گزارش', 'error')
|
||||
closeDialog()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.business-report-dialog {
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.plugin-card {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.plugin-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.v-tab {
|
||||
text-transform: none;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.v-window-item {
|
||||
min-height: 400px;
|
||||
}
|
||||
</style>
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
export const formatNumber = (number) => {
|
||||
if (!number) return '0'
|
||||
return new Intl.NumberFormat('fa-IR').format(number)
|
||||
return new Intl.NumberFormat('fa-IR').format(Math.floor(number))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -582,7 +582,7 @@ export default {
|
|||
|
||||
// فرمت کردن عدد به ریال
|
||||
formatCurrency(number) {
|
||||
return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
return Math.floor(number).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
},
|
||||
|
||||
// تعیین رنگ کارت اعتبار بر اساس میزان اعتبار
|
||||
|
|
|
@ -2,67 +2,147 @@
|
|||
import { defineComponent, ref, watch } from 'vue'
|
||||
import axios from "axios";
|
||||
import Swal from "sweetalert2";
|
||||
import type { Header, Item, ServerOptions } from "vue3-easy-data-table";
|
||||
import BusinessChargeDialog from '@/components/admin/BusinessChargeDialog.vue'
|
||||
import BusinessPluginDialog from '@/components/admin/BusinessPluginDialog.vue'
|
||||
import BusinessReportDialog from '@/components/admin/BusinessReportDialog.vue'
|
||||
|
||||
|
||||
const headers: Header[] = [
|
||||
{ text: "نام", value: "name" },
|
||||
{ text: "مالک", value: "owner" },
|
||||
{ text: "موبایل", value: "ownerMobile" },
|
||||
{ text: "تاریخ ایجاد", value: "dateRegister" },
|
||||
{ text: "کالا و خدمات", value: "commodityCount" },
|
||||
{ text: "اشخاص", value: "personsCount" },
|
||||
{ text: "اسناد حسابداری", value: "hesabdariDocsCount" },
|
||||
{ text: "اسناد انبار", value: "StoreroomDocsCount" },
|
||||
const headers = [
|
||||
{ title: "نام", key: "name", sortable: true },
|
||||
{ title: "مالک", key: "owner", sortable: true },
|
||||
{ title: "موبایل", key: "ownerMobile", sortable: false },
|
||||
{ title: "تاریخ ایجاد", key: "dateRegister", sortable: true },
|
||||
{ title: "کالا و خدمات", key: "commodityCount", sortable: true },
|
||||
{ title: "اشخاص", key: "personsCount", sortable: true },
|
||||
{ title: "اسناد حسابداری", key: "hesabdariDocsCount", sortable: true },
|
||||
{ title: "اسناد انبار", key: "StoreroomDocsCount", sortable: true },
|
||||
{ title: "عملیات", key: "actions", sortable: false, width: '200px' },
|
||||
];
|
||||
|
||||
const items = ref<Item[]>([]);
|
||||
const items = ref<any[]>([]);
|
||||
|
||||
const loading = ref(false);
|
||||
const serverItemsLength = ref(0);
|
||||
const totalItems = ref(0);
|
||||
const searchValue = ref('');
|
||||
const serverOptions = ref<ServerOptions>({
|
||||
const options = ref({
|
||||
page: 1,
|
||||
rowsPerPage: 25,
|
||||
sortBy: 'id',
|
||||
sortType: 'desc',
|
||||
itemsPerPage: 25,
|
||||
sortBy: ['id'],
|
||||
sortDesc: [true],
|
||||
});
|
||||
|
||||
// متغیرهای مربوط به دیالوگها
|
||||
const showChargeDialog = ref(false);
|
||||
const showPluginDialog = ref(false);
|
||||
const showReportDialog = ref(false);
|
||||
const selectedBusiness = ref<any>(null);
|
||||
|
||||
const loadFromServer = async () => {
|
||||
loading.value = true;
|
||||
axios.post('/api/admin/business/count')
|
||||
.then((response) => {
|
||||
serverItemsLength.value = response.data;
|
||||
});
|
||||
//load items
|
||||
await axios.post('/api/admin/business/search', {
|
||||
options: serverOptions.value,
|
||||
search: searchValue.value
|
||||
})
|
||||
.then((response) => {
|
||||
items.value = response.data.data;
|
||||
loading.value = false;
|
||||
try {
|
||||
// دریافت تعداد کل
|
||||
const countResponse = await axios.post('/api/admin/business/count');
|
||||
totalItems.value = countResponse.data;
|
||||
|
||||
// تنظیم مقادیر پیشفرض برای sort
|
||||
const sortBy = options.value.sortBy?.[0] || 'id';
|
||||
const sortDesc = options.value.sortDesc?.[0] || true;
|
||||
|
||||
// دریافت آیتمها
|
||||
const response = await axios.post('/api/admin/business/search', {
|
||||
options: {
|
||||
page: options.value.page,
|
||||
rowsPerPage: options.value.itemsPerPage,
|
||||
sortBy: sortBy,
|
||||
sortType: sortDesc ? 'desc' : 'asc'
|
||||
},
|
||||
search: searchValue.value
|
||||
});
|
||||
|
||||
items.value = response.data.data;
|
||||
} catch (error) {
|
||||
console.error('خطا در بارگذاری دادهها:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// تابع باز کردن دیالوگ افزایش اعتبار
|
||||
const openChargeDialog = (business: any) => {
|
||||
console.log('openChargeDialog called with:', business);
|
||||
if (!business) {
|
||||
console.log('business is null/undefined, returning');
|
||||
return;
|
||||
}
|
||||
selectedBusiness.value = business;
|
||||
showChargeDialog.value = true;
|
||||
console.log('Charge dialog should be open, showChargeDialog:', showChargeDialog.value);
|
||||
};
|
||||
|
||||
// تابع باز کردن دیالوگ فعالسازی افزونه
|
||||
const openPluginDialog = (business: any) => {
|
||||
console.log('openPluginDialog called with:', business);
|
||||
if (!business) {
|
||||
console.log('business is null/undefined, returning');
|
||||
return;
|
||||
}
|
||||
selectedBusiness.value = business;
|
||||
showPluginDialog.value = true;
|
||||
console.log('Plugin dialog should be open, showPluginDialog:', showPluginDialog.value);
|
||||
};
|
||||
|
||||
// تابع نمایش گزارش کسب و کار
|
||||
const showBusinessReport = (business: any) => {
|
||||
console.log('showBusinessReport called with:', business);
|
||||
if (!business) {
|
||||
console.log('business is null/undefined, returning');
|
||||
return;
|
||||
}
|
||||
selectedBusiness.value = business;
|
||||
showReportDialog.value = true;
|
||||
console.log('Report dialog should be open, showReportDialog:', showReportDialog.value);
|
||||
};
|
||||
|
||||
// تابع موفقیت عملیات
|
||||
const onSuccess = () => {
|
||||
loadFromServer(); // بارگذاری مجدد لیست
|
||||
};
|
||||
|
||||
// initial load
|
||||
loadFromServer();
|
||||
|
||||
watch(serverOptions, (value) => { loadFromServer(); }, { deep: true });
|
||||
watch(searchValue, (value) => { loadFromServer(); }, { deep: true });
|
||||
// watch برای تغییرات options
|
||||
watch(options, () => {
|
||||
loadFromServer();
|
||||
}, { deep: true });
|
||||
|
||||
// watch برای تغییرات search
|
||||
watch(searchValue, () => {
|
||||
options.value.page = 1; // بازگشت به صفحه اول
|
||||
loadFromServer();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-toolbar color="toolbar" :title="$t('user.businesses') + ' : (' + serverItemsLength + ')'">
|
||||
<v-toolbar color="toolbar" :title="$t('user.businesses') + ' : (' + totalItems + ')'">
|
||||
<v-spacer></v-spacer>
|
||||
</v-toolbar>
|
||||
<v-container class="pa-0 ma-0">
|
||||
<v-card :loading="loading ? 'red' : null" :disabled="loading">
|
||||
<v-card :loading="loading" :disabled="loading">
|
||||
<v-card-text class="pa-0">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field color="info" hide-details="auto" rounded="0" variant="outlined"
|
||||
density="compact" :placeholder="$t('dialog.search_txt')" v-model="searchValue" type="text" clearable>
|
||||
<v-text-field
|
||||
color="info"
|
||||
hide-details="auto"
|
||||
rounded="0"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
:placeholder="$t('dialog.search_txt')"
|
||||
v-model="searchValue"
|
||||
type="text"
|
||||
clearable
|
||||
class="mb-4"
|
||||
>
|
||||
<template v-slot:prepend-inner>
|
||||
<v-tooltip location="bottom" :text="$t('dialog.search')">
|
||||
<template v-slot:activator="{ props }">
|
||||
|
@ -71,25 +151,92 @@ watch(searchValue, (value) => { loadFromServer(); }, { deep: true });
|
|||
</v-tooltip>
|
||||
</template>
|
||||
</v-text-field>
|
||||
<EasyDataTable table-class-name="customize-table" show-index alternating :headers="headers" :items="items"
|
||||
rowsPerPageMessage="تعداد سطر" emptyMessage="اطلاعاتی برای نمایش وجود ندارد"
|
||||
rowsOfPageSeparatorMessage="از" theme-color="#1d90ff" header-text-direction="center"
|
||||
body-text-direction="center" :loading="loading" v-model:server-options="serverOptions"
|
||||
:server-items-length="serverItemsLength">
|
||||
<template #pagination="{ prevPage, nextPage, isFirstPage, isLastPage }">
|
||||
<v-btn size="small" color="success" class="me-1" :disabled="isFirstPage" @click="prevPage" prepend-icon="mdi-skip-next">
|
||||
{{ $t('dialog.prev_page') }}
|
||||
</v-btn>
|
||||
<v-btn size="small" color="success" class="me-1" :disabled="isLastPage" @click="nextPage" append-icon="mdi-skip-previous">
|
||||
{{ $t('dialog.next_page') }}
|
||||
</v-btn>
|
||||
|
||||
<v-data-table-server
|
||||
v-model:options="options"
|
||||
:headers="headers"
|
||||
:items="items"
|
||||
:items-length="totalItems"
|
||||
:loading="loading"
|
||||
class="elevation-1"
|
||||
>
|
||||
<template #item.actions="{ item }">
|
||||
<v-menu location="bottom end" :close-on-content-click="true" offset="5">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-tooltip location="top" text="عملیات">
|
||||
<template v-slot:activator="{ props: tooltipProps }">
|
||||
<v-btn
|
||||
v-bind="{ ...props, ...tooltipProps }"
|
||||
size="small"
|
||||
color="primary"
|
||||
variant="tonal"
|
||||
icon="mdi-dots-horizontal"
|
||||
class="ma-1"
|
||||
>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</template>
|
||||
<v-card min-width="240" class="pa-2" elevation="8">
|
||||
<v-list density="compact" class="py-0">
|
||||
<v-list-item
|
||||
@click="openChargeDialog(item)"
|
||||
prepend-icon="mdi-credit-card"
|
||||
class="text-primary rounded mb-1"
|
||||
hover
|
||||
active-class="bg-primary-lighten-5"
|
||||
>
|
||||
<v-list-item-title class="text-body-2 font-weight-medium">افزایش اعتبار</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-caption">افزایش اعتبار پیامک</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item
|
||||
@click="openPluginDialog(item)"
|
||||
prepend-icon="mdi-puzzle"
|
||||
class="text-success rounded mb-1"
|
||||
hover
|
||||
active-class="bg-success-lighten-5"
|
||||
>
|
||||
<v-list-item-title class="text-body-2 font-weight-medium">فعالسازی افزونه</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-caption">فعالسازی یا تمدید افزونه</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item
|
||||
@click="showBusinessReport(item)"
|
||||
prepend-icon="mdi-chart-line"
|
||||
class="text-info rounded"
|
||||
hover
|
||||
active-class="bg-info-lighten-5"
|
||||
>
|
||||
<v-list-item-title class="text-body-2 font-weight-medium">گزارش کامل</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-caption">مشاهده گزارش جامع</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</v-menu>
|
||||
</template>
|
||||
</EasyDataTable>
|
||||
</v-data-table-server>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-container>
|
||||
|
||||
<!-- کامپوننتهای دیالوگ -->
|
||||
<BusinessChargeDialog
|
||||
v-model="showChargeDialog"
|
||||
:business="selectedBusiness"
|
||||
@success="onSuccess"
|
||||
/>
|
||||
|
||||
<BusinessPluginDialog
|
||||
v-model="showPluginDialog"
|
||||
:business="selectedBusiness"
|
||||
@success="onSuccess"
|
||||
/>
|
||||
|
||||
<BusinessReportDialog
|
||||
v-model="showReportDialog"
|
||||
:business="selectedBusiness"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
Loading…
Reference in a new issue