feat(shop): 配送员管理功能新增门店选择功能
- 在ShopStoreRider模型中增加storeId和storeName字段 - 为配送员编辑表单添加门店选择组件和验证规则 - 实现门店选择的Modal组件和数据表格展示 - 添加门店搜索和筛选功能 - 优化表单回显逻辑支持门店名称显示 - 更新表单提交时的门店ID关联逻辑
This commit is contained in:
@@ -6,6 +6,10 @@ import type { PageParam } from '@/api';
|
|||||||
export interface ShopStoreRider {
|
export interface ShopStoreRider {
|
||||||
// 主键ID
|
// 主键ID
|
||||||
id?: string;
|
id?: string;
|
||||||
|
// 门店ID(shop_store.id)
|
||||||
|
storeId?: number;
|
||||||
|
// 门店名称(后端联表返回,提交时可不传)
|
||||||
|
storeName?: string;
|
||||||
// 配送点ID(shop_dealer.id)
|
// 配送点ID(shop_dealer.id)
|
||||||
dealerId?: number;
|
dealerId?: number;
|
||||||
// 骑手编号(可选)
|
// 骑手编号(可选)
|
||||||
|
|||||||
106
src/components/SelectShopStore/components/select-data.vue
Normal file
106
src/components/SelectShopStore/components/select-data.vue
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
<template>
|
||||||
|
<ele-modal
|
||||||
|
:width="800"
|
||||||
|
:visible="visible"
|
||||||
|
:maskClosable="false"
|
||||||
|
:title="title"
|
||||||
|
:footer="null"
|
||||||
|
:body-style="{ paddingBottom: '28px' }"
|
||||||
|
@update:visible="updateVisible"
|
||||||
|
>
|
||||||
|
<ele-pro-table
|
||||||
|
ref="tableRef"
|
||||||
|
row-key="id"
|
||||||
|
:datasource="datasource"
|
||||||
|
:columns="columns"
|
||||||
|
:pagination="false"
|
||||||
|
>
|
||||||
|
<template #toolbar>
|
||||||
|
<a-space>
|
||||||
|
<a-input-search
|
||||||
|
allow-clear
|
||||||
|
v-model:value="where.keywords"
|
||||||
|
placeholder="请输入门店关键词"
|
||||||
|
style="width: 240px"
|
||||||
|
@search="reload"
|
||||||
|
@pressEnter="reload"
|
||||||
|
/>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'action'">
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="done(record)">选择</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</ele-pro-table>
|
||||||
|
</ele-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import type {
|
||||||
|
ColumnItem,
|
||||||
|
DatasourceFunction
|
||||||
|
} from 'ele-admin-pro/es/ele-pro-table/types';
|
||||||
|
import { EleProTable } from 'ele-admin-pro';
|
||||||
|
import useSearch from '@/utils/use-search';
|
||||||
|
import { pageShopStore } from '@/api/shop/shopStore';
|
||||||
|
import type { ShopStoreParam } from '@/api/shop/shopStore/model';
|
||||||
|
import type { ShopStore } from '@/api/shop/shopStore/model';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
visible: boolean;
|
||||||
|
title?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'done', data: ShopStore): void;
|
||||||
|
(e: 'update:visible', visible: boolean): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const updateVisible = (value: boolean) => {
|
||||||
|
emit('update:visible', value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const { where } = useSearch<ShopStoreParam>({
|
||||||
|
id: undefined,
|
||||||
|
keywords: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||||
|
|
||||||
|
const columns = ref<ColumnItem[]>([
|
||||||
|
{ title: 'ID', dataIndex: 'id', width: 90 },
|
||||||
|
{ title: '门店名称', dataIndex: 'name' },
|
||||||
|
{ title: '电话', dataIndex: 'phone', width: 160 },
|
||||||
|
{ title: '地址', dataIndex: 'address' },
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
width: 120,
|
||||||
|
align: 'center',
|
||||||
|
hideInSetting: true
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => {
|
||||||
|
return pageShopStore({
|
||||||
|
...where,
|
||||||
|
...orders,
|
||||||
|
page,
|
||||||
|
limit
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const reload = () => {
|
||||||
|
tableRef?.value?.reload({ page: 1, where: where as ShopStoreParam });
|
||||||
|
};
|
||||||
|
|
||||||
|
const done = (record: ShopStore) => {
|
||||||
|
updateVisible(false);
|
||||||
|
emit('done', record);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
63
src/components/SelectShopStore/index.vue
Normal file
63
src/components/SelectShopStore/index.vue
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-input-group compact>
|
||||||
|
<a-input
|
||||||
|
disabled
|
||||||
|
style="width: calc(100% - 32px)"
|
||||||
|
v-model:value="content"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
/>
|
||||||
|
<a-button @click="openEdit">
|
||||||
|
<template #icon><BulbOutlined class="ele-text-warning" /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-input-group>
|
||||||
|
<!-- 选择弹窗 -->
|
||||||
|
<SelectData
|
||||||
|
v-model:visible="showEdit"
|
||||||
|
:title="placeholder"
|
||||||
|
@done="onChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { BulbOutlined } from '@ant-design/icons-vue';
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
import SelectData from './components/select-data.vue';
|
||||||
|
import type { ShopStore } from '@/api/shop/shopStore/model';
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
value?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
placeholder: '选择门店'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'done', data?: ShopStore): void;
|
||||||
|
(e: 'clear'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const showEdit = ref(false);
|
||||||
|
const content = ref<string>(props.value ?? '');
|
||||||
|
|
||||||
|
const openEdit = () => {
|
||||||
|
showEdit.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChange = (row?: ShopStore) => {
|
||||||
|
showEdit.value = false;
|
||||||
|
emit('done', row);
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.value,
|
||||||
|
(v) => {
|
||||||
|
content.value = v ?? '';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
:visible="visible"
|
:visible="visible"
|
||||||
:maskClosable="false"
|
:maskClosable="false"
|
||||||
:maxable="maxable"
|
:maxable="maxable"
|
||||||
|
:confirm-loading="loading"
|
||||||
:title="isUpdate ? '编辑配送员' : '添加配送员'"
|
:title="isUpdate ? '编辑配送员' : '添加配送员'"
|
||||||
:body-style="{ paddingBottom: '28px' }"
|
:body-style="{ paddingBottom: '28px' }"
|
||||||
@update:visible="updateVisible"
|
@update:visible="updateVisible"
|
||||||
@@ -28,6 +29,14 @@
|
|||||||
@done="onChooseUser"
|
@done="onChooseUser"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item label="所属门店" name="storeId">
|
||||||
|
<SelectShopStore
|
||||||
|
:key="String(form.storeId ?? '') + selectedStoreText"
|
||||||
|
:value="selectedStoreText"
|
||||||
|
:placeholder="`选择门店`"
|
||||||
|
@done="onChooseStore"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
<a-form-item label="配送点(小区)" name="dealerId">
|
<a-form-item label="配送点(小区)" name="dealerId">
|
||||||
<SelectCommunity
|
<SelectCommunity
|
||||||
:key="String(form.dealerId ?? '') + selectedCommunityText"
|
:key="String(form.dealerId ?? '') + selectedCommunityText"
|
||||||
@@ -165,6 +174,10 @@
|
|||||||
import { ShopStoreRider } from '@/api/shop/shopStoreRider/model';
|
import { ShopStoreRider } from '@/api/shop/shopStoreRider/model';
|
||||||
import { getUser } from '@/api/system/user';
|
import { getUser } from '@/api/system/user';
|
||||||
import { getShopCommunity } from '@/api/shop/shopCommunity';
|
import { getShopCommunity } from '@/api/shop/shopCommunity';
|
||||||
|
import { getShopStore } from '@/api/shop/shopStore';
|
||||||
|
import SelectUser from '@/components/SelectUser/index.vue';
|
||||||
|
import SelectCommunity from '@/components/SelectCommunity/index.vue';
|
||||||
|
import SelectShopStore from '@/components/SelectShopStore/index.vue';
|
||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
||||||
@@ -172,6 +185,7 @@
|
|||||||
import { FileRecord } from '@/api/system/file/model';
|
import { FileRecord } from '@/api/system/file/model';
|
||||||
import type { User } from '@/api/system/user/model';
|
import type { User } from '@/api/system/user/model';
|
||||||
import type { ShopCommunity } from '@/api/shop/shopCommunity/model';
|
import type { ShopCommunity } from '@/api/shop/shopCommunity/model';
|
||||||
|
import type { ShopStore } from '@/api/shop/shopStore/model';
|
||||||
|
|
||||||
// 是否是修改
|
// 是否是修改
|
||||||
const isUpdate = ref(false);
|
const isUpdate = ref(false);
|
||||||
@@ -201,10 +215,12 @@
|
|||||||
const images = ref<ItemType[]>([]);
|
const images = ref<ItemType[]>([]);
|
||||||
const selectedUserText = ref('');
|
const selectedUserText = ref('');
|
||||||
const selectedCommunityText = ref('');
|
const selectedCommunityText = ref('');
|
||||||
|
const selectedStoreText = ref('');
|
||||||
|
|
||||||
// 用户信息
|
// 用户信息
|
||||||
const form = reactive<ShopStoreRider>({
|
const form = reactive<ShopStoreRider>({
|
||||||
id: undefined,
|
id: undefined,
|
||||||
|
storeId: undefined,
|
||||||
dealerId: undefined,
|
dealerId: undefined,
|
||||||
riderNo: undefined,
|
riderNo: undefined,
|
||||||
realName: undefined,
|
realName: undefined,
|
||||||
@@ -236,6 +252,17 @@
|
|||||||
|
|
||||||
// 表单验证规则
|
// 表单验证规则
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
|
storeId: [
|
||||||
|
{
|
||||||
|
validator: (_rule: unknown, value: number | undefined) => {
|
||||||
|
if (!value) {
|
||||||
|
return Promise.reject(new Error('请选择门店'));
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
|
trigger: 'change'
|
||||||
|
}
|
||||||
|
]
|
||||||
// userId: [
|
// userId: [
|
||||||
// {
|
// {
|
||||||
// required: true,
|
// required: true,
|
||||||
@@ -317,6 +344,16 @@
|
|||||||
selectedCommunityText.value = community.name ?? String(community.id ?? '');
|
selectedCommunityText.value = community.name ?? String(community.id ?? '');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onChooseStore = (store?: ShopStore) => {
|
||||||
|
if (!store) {
|
||||||
|
selectedStoreText.value = '';
|
||||||
|
form.storeId = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
form.storeId = store.id;
|
||||||
|
selectedStoreText.value = store.name ?? String(store.id ?? '');
|
||||||
|
};
|
||||||
|
|
||||||
const { resetFields } = useForm(form, rules);
|
const { resetFields } = useForm(form, rules);
|
||||||
|
|
||||||
/* 保存编辑 */
|
/* 保存编辑 */
|
||||||
@@ -356,6 +393,7 @@
|
|||||||
images.value = [];
|
images.value = [];
|
||||||
selectedUserText.value = '';
|
selectedUserText.value = '';
|
||||||
selectedCommunityText.value = '';
|
selectedCommunityText.value = '';
|
||||||
|
selectedStoreText.value = '';
|
||||||
if (props.data) {
|
if (props.data) {
|
||||||
assignObject(form, props.data);
|
assignObject(form, props.data);
|
||||||
if (props.data.avatar) {
|
if (props.data.avatar) {
|
||||||
@@ -382,6 +420,24 @@
|
|||||||
selectedUserText.value = String(form.userId ?? '');
|
selectedUserText.value = String(form.userId ?? '');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (form.storeId) {
|
||||||
|
// 优先使用列表接口返回的 storeName 回显,避免额外请求
|
||||||
|
if ((props.data as any)?.storeName) {
|
||||||
|
selectedStoreText.value = String((props.data as any).storeName);
|
||||||
|
} else {
|
||||||
|
const sid = form.storeId;
|
||||||
|
getShopStore(form.storeId)
|
||||||
|
.then((store) => {
|
||||||
|
if (form.storeId !== sid) return;
|
||||||
|
selectedStoreText.value =
|
||||||
|
store.name ?? String(store.id ?? '');
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
if (form.storeId !== sid) return;
|
||||||
|
selectedStoreText.value = String(form.storeId ?? '');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
if (form.dealerId) {
|
if (form.dealerId) {
|
||||||
const dealerId = form.dealerId;
|
const dealerId = form.dealerId;
|
||||||
getShopCommunity(form.dealerId)
|
getShopCommunity(form.dealerId)
|
||||||
|
|||||||
Reference in New Issue
Block a user