Approved departure from the port's byte-faithful rule (docs/ADMIN-PORT-PLAN.md
bug rows 4-6), fixed and logged rather than silently changed:
- farsi.less: bold/700 @font-face blocks now load iran-yekan-700.woff2
instead of the 400 file, so bold text actually renders bold. No source
file exists for weight 300, so that face is dropped rather than faked.
- format("woff") corrected to format("woff2") on all faces.
- Removed 'font-weight: inherit' from the body,* reset, which was flattening
every heading's weight.
Also deduped 24 view files that each redeclared .grid-2/.grid-3/.grid-4 in
a scoped <style> block with drifted minmax values, though utilities.less
already defines all three globally. Removed the scoped copies; kept
PromotionForm's genuinely different 0.5rem gap as a small .grid-4--tight
modifier instead of a full redeclaration.
Verified: pnpm build passes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
157 lines
5.4 KiB
Vue
157 lines
5.4 KiB
Vue
<template>
|
|
<!--
|
|
Ported from legacy henza_admin/src/views/manage/wallet.vue — the wallet
|
|
settings screen. Two tabs, each writing one settings key.
|
|
|
|
Legacy quirk preserved: the "تاریخ تولد" tab is declared FIRST in the markup
|
|
but carries key="2", while "پس از خرید" carries key="1" and is the default
|
|
active tab — so the visually-second tab opens first.
|
|
-->
|
|
<div>
|
|
<a-card v-if="hasPermission('config.wallet')" title="مدیریت کیف پول">
|
|
<a-tabs default-active-key="1">
|
|
<a-tab-pane key="2" tab="تاریخ تولد" force-render>
|
|
<a-spin :spinning="loading" tip="لطفا کمی تامل کنید">
|
|
<a-form layout="vertical" @submit.prevent="setWallet2">
|
|
<div class="grid-2">
|
|
<a-form-item
|
|
label="انقضا (روز)"
|
|
:validate-status="err2.birthday_expire ? 'error' : ''"
|
|
:help="err2.birthday_expire"
|
|
>
|
|
<a-input-number v-model:value="form2.birthday_expire" class="w-full" :min="0" />
|
|
</a-form-item>
|
|
<a-form-item
|
|
label="میزان (تومان)"
|
|
:validate-status="err2.birthday_credit ? 'error' : ''"
|
|
:help="err2.birthday_credit"
|
|
>
|
|
<a-input-number v-model:value="form2.birthday_credit" class="w-full" :min="0" />
|
|
</a-form-item>
|
|
</div>
|
|
<a-button type="primary" class="mb-4 mt-4 mr-4" @click="setWallet2">ارسال</a-button>
|
|
</a-form>
|
|
</a-spin>
|
|
</a-tab-pane>
|
|
|
|
<a-tab-pane key="1" tab="پس از خرید" force-render>
|
|
<a-spin :spinning="loading" tip="لطفا کمی تامل کنید">
|
|
<a-form layout="vertical" @submit.prevent="setWallet1">
|
|
<div class="grid-2">
|
|
<a-form-item
|
|
label="انقضا (روز)"
|
|
:validate-status="err1.expire ? 'error' : ''"
|
|
:help="err1.expire"
|
|
>
|
|
<a-input-number v-model:value="form1.expire" class="w-full" :min="0" />
|
|
</a-form-item>
|
|
<a-form-item
|
|
label="درصد"
|
|
:validate-status="err1.percent ? 'error' : ''"
|
|
:help="err1.percent"
|
|
>
|
|
<a-input-number
|
|
v-model:value="form1.percent"
|
|
class="w-full"
|
|
:min="0"
|
|
:max="100"
|
|
:formatter="(v: number | string) => `${v}%`"
|
|
:parser="(v: string) => v.replace('%', '')"
|
|
/>
|
|
</a-form-item>
|
|
</div>
|
|
<a-button type="primary" class="mb-4 mt-4 mr-4" @click="setWallet1">ارسال</a-button>
|
|
</a-form>
|
|
</a-spin>
|
|
</a-tab-pane>
|
|
</a-tabs>
|
|
</a-card>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onMounted, reactive, ref } from 'vue';
|
|
import { message } from 'ant-design-vue';
|
|
import { useUsersStore } from '../../stores/users';
|
|
import api from '../../api/client';
|
|
|
|
const users = useUsersStore();
|
|
const hasPermission = (rid: string) => Boolean(users.hasPermission(rid));
|
|
|
|
const loading = ref(false);
|
|
|
|
const form1 = reactive<Record<string, any>>({ expire: undefined, percent: undefined });
|
|
const form2 = reactive<Record<string, any>>({
|
|
birthday_expire: undefined,
|
|
birthday_credit: undefined,
|
|
});
|
|
const err1 = reactive<Record<string, string>>({});
|
|
const err2 = reactive<Record<string, string>>({});
|
|
|
|
const REQUIRED = 'لطفا مقدار را وارد کنید';
|
|
|
|
async function setWallet1() {
|
|
Object.keys(err1).forEach((k) => delete err1[k]);
|
|
if (form1.expire === undefined || form1.expire === null) err1.expire = REQUIRED;
|
|
if (form1.percent === undefined || form1.percent === null) err1.percent = REQUIRED;
|
|
if (Object.keys(err1).length) return;
|
|
|
|
try {
|
|
loading.value = true;
|
|
await api.post('wallet/config', { expire: form1.expire, percent: form1.percent });
|
|
message.success('تغییرات ثبت شد');
|
|
} catch (e: any) {
|
|
message.error(e?.response?.data?.message ?? 'خطا در ثبت تغییرات');
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
async function setWallet2() {
|
|
Object.keys(err2).forEach((k) => delete err2[k]);
|
|
if (form2.birthday_expire === undefined || form2.birthday_expire === null) {
|
|
err2.birthday_expire = REQUIRED;
|
|
}
|
|
if (form2.birthday_credit === undefined || form2.birthday_credit === null) {
|
|
err2.birthday_credit = REQUIRED;
|
|
}
|
|
if (Object.keys(err2).length) return;
|
|
|
|
try {
|
|
loading.value = true;
|
|
await api.post('wallet/birthday/config', {
|
|
birthday_expire: form2.birthday_expire,
|
|
birthday_credit: form2.birthday_credit,
|
|
});
|
|
message.success('تغییرات ثبت شد');
|
|
} catch (e: any) {
|
|
message.error(e?.response?.data?.message ?? 'خطا در ثبت تغییرات');
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
onMounted(async () => {
|
|
try {
|
|
const [c1, c2] = await Promise.all([
|
|
api.get('wallet/config'),
|
|
api.get('wallet/birthday/config'),
|
|
]);
|
|
Object.assign(form1, c1.data ?? {});
|
|
Object.assign(form2, c2.data ?? {});
|
|
} catch (e) {
|
|
console.log(e);
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* .grid-2 now comes from the global utilities.less (dedup) */
|
|
.mb-4 {
|
|
margin-bottom: 1rem;
|
|
}
|
|
.mt-4 {
|
|
margin-top: 1rem;
|
|
}
|
|
</style>
|