admin-panel: convert wholesale-customer add form into a modal
bulkCustomer.vue (reached today only via /customer/add's AddCustomer.vue tab wrapper, and via /bulkCustomer/edit/:id from 5 different list variants) gets open/id modal support; both routes stay as a fallback. OmdeCustomerList.vue is shared by 5 list pages (OmdeList, BankerList, ShopkeeperList, OmdeNonVerifiedList, OmdeArchivedList). Per user decision, the add button and modal-based edit are wired only on OmdeList.vue (the one this form conceptually belongs to) via a new showAddButton prop, default false — the other four keep navigating to the edit route unchanged. singleCustomer.vue and ReagentCustomer.vue were also planned as add-form candidates but have no create entry point anywhere in the app (edit-only, reachable solely via /singleCustomer/edit/:id and /reagent/edit/:id) — left untouched, nothing to convert. offersIgnore-add-edit.vue is a standalone settings page with no list counterpart, not a create form — left as a page. Verified: pnpm build passes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
1bb9802d1f
commit
0e1dd0c36a
@@ -5,6 +5,11 @@
|
||||
wrapper around it (the rest of AddCustomer's markup is commented out in
|
||||
legacy). Field names, Farsi labels and validation messages are preserved.
|
||||
|
||||
Converted to a modal (BaseFormModal): /customer/add (via AddCustomer.vue)
|
||||
and /bulkCustomer/edit/:id (via EditBulkCustomer.vue) still exist and still
|
||||
render this component directly as a route-based fallback, but OmdeList.vue
|
||||
now opens it as a modal via open/id props.
|
||||
|
||||
Dropped: the MyUpload avatar widget and the Jalali DateInput, which belong to
|
||||
fields the redesigned Customer schema has no column for. Flagged in
|
||||
docs/ADMIN-PORT-PLAN.md rather than faked.
|
||||
@@ -12,7 +17,108 @@
|
||||
The نوع کاربری selector is live again now that Customer.type exists; its four
|
||||
options are legacy's own list, byte for byte.
|
||||
-->
|
||||
<a-form layout="vertical" :model="form" @submit.prevent="handleSubmitOmde">
|
||||
<BaseFormModal
|
||||
v-if="asModal"
|
||||
:open="Boolean(open)"
|
||||
:id="id"
|
||||
add-title="افزودن مشتری عمده"
|
||||
edit-title="ویرایش مشتری عمده"
|
||||
:loading="loading"
|
||||
:submitting="loading"
|
||||
width="lg"
|
||||
@update:open="$emit('update:open', $event)"
|
||||
@submit="handleSubmitOmde"
|
||||
>
|
||||
<a-card title="اطلاعات پایه" class="mb-base">
|
||||
<div class="grid-4">
|
||||
<a-form-item label="نام" :validate-status="err.fname ? 'error' : ''" :help="err.fname">
|
||||
<a-input v-model:value="form.fname" />
|
||||
</a-form-item>
|
||||
<a-form-item label="نام خانوادگی" :validate-status="err.lname ? 'error' : ''" :help="err.lname">
|
||||
<a-input v-model:value="form.lname" />
|
||||
</a-form-item>
|
||||
<a-form-item label="کد ملی">
|
||||
<a-input v-model:value="form.ncode" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="شماره تلفن همراه"
|
||||
:validate-status="err.mobile ? 'error' : ''"
|
||||
:help="err.mobile"
|
||||
>
|
||||
<a-input v-model:value="form.mobile" />
|
||||
</a-form-item>
|
||||
<a-form-item label="رمز عبور">
|
||||
<a-input-password v-model:value="form.password" />
|
||||
</a-form-item>
|
||||
<a-form-item label="تلفن ثابت">
|
||||
<a-input v-model:value="form.phone" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<a-card title="اطلاعات تجاری" class="mb-base">
|
||||
<div class="grid-4">
|
||||
<a-form-item label="نوع کاربری">
|
||||
<a-select
|
||||
v-model:value="form.type"
|
||||
show-search
|
||||
:filter-option="filterUserType"
|
||||
style="width: 100%; direction: rtl !important"
|
||||
>
|
||||
<a-select-option v-for="item in userTypes" :key="item.id" :value="item.id">
|
||||
{{ item.title }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="نام مغازه">
|
||||
<a-input v-model:value="form.store_name" />
|
||||
</a-form-item>
|
||||
<a-form-item label="نام پاساژ">
|
||||
<a-input v-model:value="form.pasazh_name" />
|
||||
</a-form-item>
|
||||
<a-form-item label="کد مشتری">
|
||||
<a-input v-model:value="form.mcode" />
|
||||
</a-form-item>
|
||||
<a-form-item label="کارشناس فروش">
|
||||
<a-select v-model:value="form.reagent_id" allow-clear placeholder="انتخاب کنید">
|
||||
<a-select-option v-for="r in reagent" :key="r.id" :value="r.id">
|
||||
{{ r.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="درصد">
|
||||
<a-input-number v-model:value="form.percent" class="w-full" />
|
||||
</a-form-item>
|
||||
<a-form-item label="سقف اعتبار (برحسب گرم)">
|
||||
<a-input-number v-model:value="form.gram_level" class="w-full" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<a-card title="اطلاعات تکمیلی" class="mb-base">
|
||||
<div class="grid-2">
|
||||
<a-form-item label="شهر">
|
||||
<a-select
|
||||
v-model:value="form.city_id"
|
||||
show-search
|
||||
allow-clear
|
||||
placeholder="انتخاب کنید"
|
||||
:filter-option="filterCity"
|
||||
>
|
||||
<a-select-option v-for="c in cities" :key="c.id" :value="c.id">
|
||||
{{ c.title }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="آدرس">
|
||||
<a-textarea v-model:value="form.address" :auto-size="{ minRows: 2, maxRows: 4 }" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-card>
|
||||
</BaseFormModal>
|
||||
|
||||
<!-- route-based fallback for /customer/add and /bulkCustomer/edit/:id -->
|
||||
<a-form v-else layout="vertical" :model="form" @submit.prevent="handleSubmitOmde">
|
||||
<a-spin :spinning="loading">
|
||||
<a-card title="اطلاعات پایه" class="mb-base">
|
||||
<div class="grid-4">
|
||||
@@ -112,12 +218,24 @@
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { message } from 'ant-design-vue';
|
||||
import BaseFormModal from '../../../components/common/BaseFormModal.vue';
|
||||
import api from '../../../api/client';
|
||||
|
||||
const props = defineProps<{
|
||||
open?: boolean;
|
||||
id?: string;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:open', value: boolean): void;
|
||||
(e: 'success'): void;
|
||||
}>();
|
||||
|
||||
const asModal = props.open !== undefined;
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const id = route.params.id as string | undefined;
|
||||
const id = asModal ? props.id : (route.params.id as string | undefined);
|
||||
const loading = ref(false);
|
||||
const cities = ref<{ id: string; title: string }[]>([]);
|
||||
const reagent = ref<{ id: string; name: string }[]>([]);
|
||||
@@ -210,7 +328,12 @@ async function handleSubmitOmde() {
|
||||
await api.post('customer/create', payload);
|
||||
}
|
||||
message.info('اطلاعات با موفقیت به ثبت رسید.');
|
||||
router.go(-1);
|
||||
if (asModal) {
|
||||
emit('success');
|
||||
emit('update:open', false);
|
||||
} else {
|
||||
router.go(-1);
|
||||
}
|
||||
} catch (e: any) {
|
||||
// legacy surfaced the server's mobile-uniqueness error specifically
|
||||
const mobileErr = e?.response?.data?.errors?.mobile?.[0];
|
||||
|
||||
@@ -13,6 +13,13 @@
|
||||
-->
|
||||
<div id="page-user-list">
|
||||
<a-card :title="title">
|
||||
<ListPageHeader
|
||||
v-if="showAddButton"
|
||||
rid="persons.create.client"
|
||||
add-label="افزودن مشتری عمده"
|
||||
@add="openAdd"
|
||||
/>
|
||||
|
||||
<div v-if="query && query.count && query.total">
|
||||
{{ query.total }} رکورد یافت شد -
|
||||
<span class="text-danger">{{ query.count }} رکورد غیر فعال</span>
|
||||
@@ -205,6 +212,15 @@
|
||||
</div>
|
||||
</template>
|
||||
</XTable>
|
||||
|
||||
<BulkCustomer
|
||||
v-if="showAddButton"
|
||||
:key="editingId ?? 'new'"
|
||||
:open="modalOpen"
|
||||
:id="editingId"
|
||||
@update:open="modalOpen = $event"
|
||||
@success="tbl?.fetch()"
|
||||
/>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
@@ -226,6 +242,8 @@ import {
|
||||
UnorderedListOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import XTable from '../../../components/x-table/XTable.vue';
|
||||
import ListPageHeader from '../../../components/common/ListPageHeader.vue';
|
||||
import BulkCustomer from '../../apps/customer/bulkCustomer.vue';
|
||||
import { useUsersStore } from '../../../stores/users';
|
||||
import api from '../../../api/client';
|
||||
import type { XTbl } from '../../../components/x-table/model';
|
||||
@@ -243,8 +261,10 @@ const props = withDefaults(
|
||||
* silently "fixed" — see docs/ADMIN-PORT-PLAN.md.
|
||||
*/
|
||||
rejectOnlyStepOne?: boolean;
|
||||
/** Only OmdeList.vue passes this — see the comment on its call site. */
|
||||
showAddButton?: boolean;
|
||||
}>(),
|
||||
{ rejectOnlyStepOne: false },
|
||||
{ rejectOnlyStepOne: false, showAddButton: false },
|
||||
);
|
||||
|
||||
const router = useRouter();
|
||||
@@ -252,6 +272,8 @@ const users = useUsersStore();
|
||||
const hasPermission = (rid: string) => Boolean(users.hasPermission(rid));
|
||||
|
||||
const tbl = ref<{ fetch: () => Promise<void> } | null>(null);
|
||||
const modalOpen = ref(false);
|
||||
const editingId = ref<string | undefined>(undefined);
|
||||
const query = ref<{ count?: number; total?: number } | undefined>(undefined);
|
||||
|
||||
/** legacy setCount(): the "N رکورد غیر فعال" banner above the grid */
|
||||
@@ -399,7 +421,20 @@ async function toggleArchive(id: string) {
|
||||
}
|
||||
|
||||
function toEdit(id: string) {
|
||||
router.push(`/bulkCustomer/edit/${id}`);
|
||||
// Only the OmdeList variant (showAddButton) has the modal wired up; the
|
||||
// other four (BankerList, ShopkeeperList, OmdeNonVerifiedList,
|
||||
// OmdeArchivedList) keep navigating to the route, unchanged.
|
||||
if (props.showAddButton) {
|
||||
editingId.value = id;
|
||||
modalOpen.value = true;
|
||||
} else {
|
||||
router.push(`/bulkCustomer/edit/${id}`);
|
||||
}
|
||||
}
|
||||
|
||||
function openAdd() {
|
||||
editingId.value = undefined;
|
||||
modalOpen.value = true;
|
||||
}
|
||||
function toView(id: string) {
|
||||
router.push(`/bulkCustomer/view/${id}`);
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<template>
|
||||
<!-- Ported from legacy views/customer/omde/OmdeList.vue (615 lines). -->
|
||||
<OmdeCustomerList :title="title" :model="model" />
|
||||
<!--
|
||||
Ported from legacy views/customer/omde/OmdeList.vue (615 lines).
|
||||
The only one of OmdeCustomerList's five callers with an add button — see
|
||||
OmdeCustomerList.vue's showAddButton prop for why it isn't on all five.
|
||||
-->
|
||||
<OmdeCustomerList :title="title" :model="model" show-add-button />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
Reference in New Issue
Block a user