feat: move brand name to one config and redesign the accounting card
اپ: نام برند در همهی صفحهها و پیامها یکسان شده و از یک تنظیم مرکزی خوانده میشود. اپ: کارت حسابداری در صفحهی مشتری بازطراحی شد؛ ماندهها و وضعیت لینک واضحتر نمایش داده میشوند. پنل: کارت حسابداری در پروندهی مشتری بازطراحی شد؛ ماندهها، آخرین ارسال و فرم لینک دستی واضحتر شدهاند. پنل: ستون «کد» در جدولها بهطور پیشفرض پنهان است و از انتخاب ستونها قابل نمایش است. پنل: تغییر نمایش ستونها در جدولها بلافاصله اعمال میشود. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 5.5
parent
5d2a1efb0c
commit
532ceee189
@@ -4,50 +4,86 @@
|
||||
linked to, and both balances side by side (the customer sees the
|
||||
accounting one; the credit limit is checked against the app's own).
|
||||
-->
|
||||
<a-card v-if="visible" size="small" title="حسابداری" class="mt-4">
|
||||
<section v-if="visible" class="acc-card" :style="themeVars">
|
||||
<header class="acc-head">
|
||||
<h2>حسابداری</h2>
|
||||
<a-tag v-if="info?.link" color="success">لینک شده</a-tag>
|
||||
<a-tag v-else color="warning">لینک نشده</a-tag>
|
||||
</header>
|
||||
|
||||
<a-spin :spinning="loading">
|
||||
<a-descriptions bordered size="small" :column="3">
|
||||
<a-descriptions-item label="کد حساب">
|
||||
<div class="acc-body">
|
||||
<div class="acc-code">
|
||||
<span class="label">کد حساب</span>
|
||||
<template v-if="info?.link">
|
||||
{{ info.link.externalCode }}
|
||||
<a-tag class="ms-2">{{ ORIGIN[info.link.origin] ?? info.link.origin }}</a-tag>
|
||||
</template>
|
||||
<span v-else class="muted">لینک نشده</span>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="ماندهی حسابداری (گرم / ریال)">
|
||||
<template v-if="info?.accounting">
|
||||
<span :class="sign(info.accounting.gold)">{{ num(info.accounting.gold, 3) }}</span> /
|
||||
<span :class="sign(info.accounting.rial)">{{ num(info.accounting.rial, 0) }}</span>
|
||||
<div class="muted small">{{ jdt(info.accounting.at) }}</div>
|
||||
<span class="code" dir="ltr">{{ info.link.externalCode }}</span>
|
||||
<span class="origin">{{ ORIGIN[info.link.origin] ?? info.link.origin }}</span>
|
||||
</template>
|
||||
<span v-else class="muted">—</span>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="ماندهی دفتر اپ (گرم / ریال)">
|
||||
<span :class="sign(info?.internal?.gold)">{{ num(info?.internal?.gold, 3) }}</span> /
|
||||
<span :class="sign(info?.internal?.rial)">{{ num(info?.internal?.rial, 0) }}</span>
|
||||
<div class="muted small">مبنای سقف اعتبار</div>
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</div>
|
||||
|
||||
<div v-if="lastJob" class="muted small mt-2">
|
||||
آخرین ارسال: {{ STATUS[lastJob.status] ?? lastJob.status }} — {{ jdt(lastJob.updatedAt) }}
|
||||
<span v-if="lastJob.lastError" class="warn"> — {{ lastJob.lastError }}</span>
|
||||
<div class="balances">
|
||||
<div class="balance">
|
||||
<div class="label">ماندهی حسابداری</div>
|
||||
<template v-if="info?.accounting">
|
||||
<div class="line">
|
||||
<span :class="sign(info.accounting.gold)">{{ num(info.accounting.gold, 3) }}</span>
|
||||
<small>گرم</small>
|
||||
</div>
|
||||
<div class="line">
|
||||
<span :class="sign(info.accounting.rial)">{{ num(info.accounting.rial, 0) }}</span>
|
||||
<small>ریال</small>
|
||||
</div>
|
||||
<div class="hint">{{ jdt(info.accounting.at) }}</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="line muted">—</div>
|
||||
<div class="hint">هنوز دریافت نشده</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="balance">
|
||||
<div class="label">ماندهی دفتر اپ</div>
|
||||
<div class="line">
|
||||
<span :class="sign(info?.internal?.gold)">{{ num(info?.internal?.gold, 3) }}</span>
|
||||
<small>گرم</small>
|
||||
</div>
|
||||
<div class="line">
|
||||
<span :class="sign(info?.internal?.rial)">{{ num(info?.internal?.rial, 0) }}</span>
|
||||
<small>ریال</small>
|
||||
</div>
|
||||
<div class="hint">مبنای سقف اعتبار</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="lastJob" class="last-job">
|
||||
<a-badge :status="JOB_BADGE[lastJob.status] ?? 'default'" />
|
||||
آخرین ارسال: {{ STATUS[lastJob.status] ?? lastJob.status }} — {{ jdt(lastJob.updatedAt) }}
|
||||
<div v-if="lastJob.lastError" class="warn">{{ lastJob.lastError }}</div>
|
||||
</div>
|
||||
|
||||
<div class="link-form">
|
||||
<div class="label">{{ info?.link ? 'تغییر لینک' : 'لینک دستی' }}</div>
|
||||
<div class="link-row">
|
||||
<a-input
|
||||
v-model:value="code"
|
||||
dir="ltr"
|
||||
placeholder="کد حساب در حسابداری"
|
||||
@press-enter="code.trim() && link()"
|
||||
/>
|
||||
<a-button type="primary" ghost :disabled="!code.trim()" @click="link">لینک</a-button>
|
||||
</div>
|
||||
<a-popconfirm v-if="info?.link" title="لینک حذف شود؟" @confirm="unlink">
|
||||
<a-button type="link" danger size="small" class="unlink">حذف لینک فعلی</a-button>
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-space class="mt-2">
|
||||
<a-input v-model:value="code" dir="ltr" placeholder="کد حساب در حسابداری" style="width: 200px" />
|
||||
<a-button size="small" :disabled="!code.trim()" @click="link">لینک دستی</a-button>
|
||||
<a-popconfirm v-if="info?.link" title="لینک حذف شود؟" @confirm="unlink">
|
||||
<a-button size="small" danger>حذف لینک</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</a-spin>
|
||||
</a-card>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message, theme } from 'ant-design-vue';
|
||||
import moment from 'moment-jalaali';
|
||||
import api from '../../api/client';
|
||||
import { useUsersStore } from '../../stores/users';
|
||||
@@ -68,12 +104,32 @@ const STATUS: Record<string, string> = {
|
||||
NEEDS_REVIEW: 'نیاز به بررسی',
|
||||
RESOLVED: 'حلشده',
|
||||
};
|
||||
const JOB_BADGE: Record<string, 'success' | 'processing' | 'error' | 'warning' | 'default'> = {
|
||||
PENDING: 'default',
|
||||
SENDING: 'processing',
|
||||
SENT: 'success',
|
||||
FAILED: 'error',
|
||||
NEEDS_REVIEW: 'warning',
|
||||
RESOLVED: 'success',
|
||||
};
|
||||
|
||||
const users = useUsersStore();
|
||||
const { token } = theme.useToken();
|
||||
const info = ref<any>(null);
|
||||
const loading = ref(false);
|
||||
const code = ref('');
|
||||
|
||||
const themeVars = computed(() => ({
|
||||
'--acc-bg': token.value.colorBgContainer,
|
||||
'--acc-border': token.value.colorBorderSecondary,
|
||||
'--acc-fill': token.value.colorFillQuaternary,
|
||||
'--acc-text': token.value.colorText,
|
||||
'--acc-text-2': token.value.colorTextSecondary,
|
||||
'--acc-text-3': token.value.colorTextTertiary,
|
||||
'--acc-error': token.value.colorError,
|
||||
'--acc-success': token.value.colorSuccess,
|
||||
}));
|
||||
|
||||
const visible = computed(() => Boolean(users.hasPermission('config.accounting')) && info.value && info.value.provider !== 'none');
|
||||
const lastJob = computed(() => info.value?.jobs?.[0]);
|
||||
|
||||
@@ -115,28 +171,116 @@ onMounted(load);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mt-4 {
|
||||
margin-top: 1rem;
|
||||
.acc-card {
|
||||
background: var(--acc-bg);
|
||||
border: 1px solid var(--acc-border);
|
||||
border-radius: 12px;
|
||||
color: var(--acc-text);
|
||||
}
|
||||
.mt-2 {
|
||||
margin-top: 0.5rem;
|
||||
.acc-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 14px 18px;
|
||||
border-bottom: 1px solid var(--acc-border);
|
||||
}
|
||||
.ms-2 {
|
||||
margin-inline-start: 0.5rem;
|
||||
.acc-head h2 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.muted {
|
||||
color: #888;
|
||||
.acc-head :deep(.ant-tag) {
|
||||
margin: 0;
|
||||
}
|
||||
.small {
|
||||
.acc-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
padding: 14px 18px 16px;
|
||||
}
|
||||
.label {
|
||||
font-size: 13px;
|
||||
color: var(--acc-text-2);
|
||||
}
|
||||
.acc-code {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
gap: 4px 10px;
|
||||
}
|
||||
.acc-code .label {
|
||||
margin-inline-end: auto;
|
||||
}
|
||||
.code {
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
.origin {
|
||||
font-size: 12px;
|
||||
color: var(--acc-text-3);
|
||||
}
|
||||
.balances {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
.balance {
|
||||
padding: 10px 12px;
|
||||
border-radius: 10px;
|
||||
background: var(--acc-fill);
|
||||
}
|
||||
.balance .label {
|
||||
margin-bottom: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.line {
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
font-variant-numeric: tabular-nums;
|
||||
line-height: 1.7;
|
||||
}
|
||||
.line small {
|
||||
margin-inline-start: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
color: var(--acc-text-3);
|
||||
}
|
||||
.hint {
|
||||
margin-top: 2px;
|
||||
font-size: 11px;
|
||||
color: var(--acc-text-3);
|
||||
}
|
||||
.last-job {
|
||||
font-size: 12px;
|
||||
color: var(--acc-text-2);
|
||||
}
|
||||
.link-form {
|
||||
padding-top: 12px;
|
||||
border-top: 1px dashed var(--acc-border);
|
||||
}
|
||||
.link-form .label {
|
||||
margin-bottom: 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.link-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
.unlink {
|
||||
margin-top: 4px;
|
||||
padding-inline: 0;
|
||||
}
|
||||
.muted {
|
||||
color: var(--acc-text-3);
|
||||
}
|
||||
.warn {
|
||||
color: #d4380d;
|
||||
margin-top: 2px;
|
||||
color: var(--acc-error);
|
||||
}
|
||||
.debt {
|
||||
color: #cf1322;
|
||||
color: var(--acc-error);
|
||||
}
|
||||
.credit {
|
||||
color: #389e0d;
|
||||
color: var(--acc-success);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user