-
آخرین تراکنشها
-
-
-
-
- {{ formatMoney(item.amountRials) }}
-
-
{{ item.note || item.type }}
-
{{ new Date(item.createdAt).toLocaleString('fa-IR') }}
+
+
آستانههای رنگ موجودی
+
+
+
+
ذخیره آستانهها
-
-
+
+
+
+ گزارش
+
+
+
+
+
+ پیامک
+ {{ formatNumber(periodReport.summary.messages) }}
+
+
+ پارت
+ {{ formatNumber(periodReport.summary.parts) }}
+
+
+ مبلغ
+ {{ formatMoney(periodReport.summary.amount) }}
+
+
+
+
+
+
+
+
+
+
+ تعداد شارژ
+ {{ formatNumber(chargeReport.summary.charges) }}
+
+
+ جمع مبلغ
+ {{ formatMoney(chargeReport.summary.amount) }}
+
+
+
+
+
+
+
+
+ جداول
+
+
+
+
+
+
{ logsPage = 1; loadLogs() }"
+ />
+ { logsPage = 1; loadLogs() }">
+ اعمال
+
+
+
+
+
+
+
+
+
+
{ ledgerPage = 1; loadLedger() }"
+ />
+ { ledgerPage = 1; loadLedger() }">
+ اعمال
+
+
+
+
+
+
+
diff --git a/src/pages/TenantFormPage.vue b/src/pages/TenantFormPage.vue
index 375aa37..cee66d8 100644
--- a/src/pages/TenantFormPage.vue
+++ b/src/pages/TenantFormPage.vue
@@ -2,13 +2,14 @@
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { message } from 'ant-design-vue'
-import { ArrowRightOutlined } from '@ant-design/icons-vue'
+import { ArrowRightOutlined, PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue'
import api from '@/api/http'
const route = useRoute()
const router = useRouter()
const loading = ref(false)
const saving = ref(false)
+const uploading = ref(false)
const isEdit = computed(() => Boolean(route.params.id))
@@ -19,9 +20,7 @@ const form = reactive({
nameEn: '',
domain: '',
avatarUrl: '',
- primaryColor: '#1d4ed8',
- secondaryColor: '#0f172a',
- backgroundColor: '#ffffff',
+ appVersion: '1',
nodeServer: '',
nodeWs: '',
appUrl: '',
@@ -44,9 +43,6 @@ async function load() {
if (!isEdit.value) {
const q = String(route.query.product || '')
form.product = q === 'khanegi' ? 'khanegi' : 'hamkar'
- if (form.product === 'khanegi') {
- form.primaryColor = '#b45309'
- }
return
}
@@ -60,9 +56,7 @@ async function load() {
nameEn: data.nameEn || '',
domain: data.domain,
avatarUrl: data.avatarUrl || '',
- primaryColor: data.primaryColor || '#1d4ed8',
- secondaryColor: data.secondaryColor || '#0f172a',
- backgroundColor: data.backgroundColor || '#ffffff',
+ appVersion: data.appVersion || '1',
nodeServer: data.nodeServer,
nodeWs: data.nodeWs || '',
appUrl: data.appUrl || '',
@@ -78,6 +72,35 @@ async function load() {
}
}
+async function uploadLogo(options: {
+ file: string | Blob | File
+ onSuccess?: (body?: unknown) => void
+ onError?: (e: Error) => void
+}) {
+ const file = options.file
+ if (!(file instanceof File) && !(file instanceof Blob)) return
+
+ uploading.value = true
+ try {
+ const body = new FormData()
+ body.append('file', file)
+ const { data } = await api.post<{ url: string }>('/tenants/upload-avatar', body)
+ form.avatarUrl = data.url
+ options.onSuccess?.(data)
+ message.success('لوگو آپلود شد')
+ } catch (e: any) {
+ const msg = e?.response?.data?.message
+ options.onError?.(e instanceof Error ? e : new Error(String(msg || 'upload failed')))
+ message.error(Array.isArray(msg) ? msg.join('، ') : msg || 'آپلود ناموفق')
+ } finally {
+ uploading.value = false
+ }
+}
+
+function clearLogo() {
+ form.avatarUrl = ''
+}
+
async function save() {
saving.value = true
try {
@@ -85,7 +108,6 @@ async function save() {
if (!payload.remoteApiToken) {
delete (payload as any).remoteApiToken
}
- // node_ws فقط برای همکار حیاتی است؛ برای خانگی اختیاری (SSE)
if (isEdit.value) {
await api.patch(`/tenants/${route.params.id}`, payload)
message.success('بهروزرسانی شد')
@@ -150,27 +172,34 @@ onMounted(load)
+
+
diff --git a/src/pages/UiConfigPage.vue b/src/pages/UiConfigPage.vue
index 12a7cb2..9246275 100644
--- a/src/pages/UiConfigPage.vue
+++ b/src/pages/UiConfigPage.vue
@@ -1,11 +1,19 @@
@@ -112,20 +189,66 @@ onMounted(load)