feat(shopExpressTemplateDetail): 支持省市两级多选配送区域

- 新增 model 字段 provinceIds 和 cityIds,兼容旧单值 provinceId 和 cityId
- 引入 RegionMultiSelect 组件替代旧的单选级联选择器
- 编辑弹窗支持多选城市和省份,提交时转换为逗号分隔字符串
- 列表页配送区域列支持多选展示,超长文本截断并显示 tooltip
- 新增 RegionMultiSelect 组件,实现省市两级树形多选穿梭弹窗
- 通过 computed 和 watch 同步组件选中状态和表单数据
- 更新相关类型定义和导入,确保编辑及展示逻辑正确
- 新增样式优化,提示默认配送全国规则说明
This commit is contained in:
2026-08-11 09:01:30 +08:00
parent aae0c03dd8
commit ae2ea4d841
6 changed files with 493 additions and 31 deletions
@@ -20,13 +20,13 @@
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="配送地区" name="regions">
<RegionsSelect
v-model:value="regions"
type="provinceCity"
:show-search="true"
placeholder="请选择配送省份/城市(不选则默认全国)"
<a-form-item label="配送地区">
<RegionMultiSelect
:value="cityIds"
@update:value="onCityIdsChange"
@update:provinceIds="onProvinceIdsChange"
/>
<div class="rms-tip">不选则默认全国</div>
</a-form-item>
<a-form-item :label="templateType ? '首件数量' : '首重重量'" name="firstNum">
<a-input-number
@@ -94,7 +94,7 @@
import {
ShopExpressTemplateDetail
} from '@/api/shop/shopExpressTemplateDetail/model';
import RegionsSelect from '@/components/RegionsSelect/index.vue';
import RegionMultiSelect from '@/components/RegionMultiSelect/index.vue';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { FormInstance } from 'ant-design-vue/es/form';
@@ -128,8 +128,10 @@
const maxable = ref(true);
// 表单实例
const formRef = ref<FormInstance | null>(null);
// 级联选择器选中值(字符串ID数组)
const regions = ref<string[]>([]);
// 已选城市ID数组
const cityIds = ref<string[]>([]);
// 已选省份ID数组(由城市推导,随城市变化同步)
const provinceIds = ref<string[]>([]);
// 表单数据
const form = reactive<ShopExpressTemplateDetail>({
@@ -138,6 +140,8 @@
type: true,
provinceId: undefined,
cityId: undefined,
provinceIds: undefined,
cityIds: undefined,
firstNum: undefined,
firstAmount: undefined,
extraAmount: undefined,
@@ -186,16 +190,17 @@
const { resetFields } = useForm(form, rules);
/* 级联选择器值变化时,同步到 form.provinceId / form.cityId */
watch(regions, (val) => {
if (val && val.length > 0) {
form.provinceId = Number(val[0]);
form.cityId = val.length > 1 ? Number(val[1]) : undefined;
} else {
form.provinceId = undefined;
form.cityId = undefined;
}
});
/* 城市选中变化,同步到 form.cityIds */
const onCityIdsChange = (val: string[]) => {
cityIds.value = val;
form.cityIds = val.length ? val.join(',') : undefined;
};
/* 省份选中变化(由组件根据已选城市推导),同步到 form.provinceIds */
const onProvinceIdsChange = (val: string[]) => {
provinceIds.value = val;
form.provinceIds = val.length ? val.join(',') : undefined;
};
/* 保存编辑 */
const save = () => {
@@ -210,6 +215,13 @@
// 确保templateId和type从父级继承
formData.templateId = props.templateId;
formData.type = props.templateType ?? true;
// 多选配送区域(逗号分隔,空则默认全国)
formData.cityIds = cityIds.value.length
? cityIds.value.join(',')
: undefined;
formData.provinceIds = provinceIds.value.length
? provinceIds.value.join(',')
: undefined;
const saveOrUpdate = isUpdate.value
? updateShopExpressTemplateDetail
: addShopExpressTemplateDetail;
@@ -240,21 +252,36 @@
form.templateId = props.templateId;
form.type = props.templateType ?? true;
}
// 回显级联选择器
if (form.provinceId) {
const arr = [String(form.provinceId)];
if (form.cityId) {
arr.push(String(form.cityId));
}
regions.value = arr;
// 回显多选配送区域
if (form.cityIds) {
cityIds.value = form.cityIds.split(',').filter(Boolean);
} else if (form.cityId) {
// 兼容旧单值数据
cityIds.value = [String(form.cityId)];
} else {
regions.value = [];
cityIds.value = [];
}
if (form.provinceIds) {
provinceIds.value = form.provinceIds.split(',').filter(Boolean);
} else if (form.provinceId) {
provinceIds.value = [String(form.provinceId)];
} else {
provinceIds.value = [];
}
} else {
resetFields();
regions.value = [];
cityIds.value = [];
provinceIds.value = [];
}
},
{ immediate: true }
);
</script>
<style scoped>
.rms-tip {
margin-top: 4px;
color: var(--ant-color-text-secondary, #999);
font-size: 12px;
}
</style>
@@ -51,7 +51,7 @@
</template>
<script lang="ts" setup>
import { createVNode, ref, computed, onMounted } from 'vue';
import { createVNode, ref, computed, onMounted, h } from 'vue';
import { useRoute } from 'vue-router';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
@@ -128,6 +128,26 @@
align: 'center',
width: 150,
customRender: ({ record }) => {
// 多选配送区域(cityIds 逗号分隔)
const cityIds = (record.cityIds || '')
.split(',')
.filter(Boolean);
if (cityIds.length) {
const names = cityIds.map(
(id: string) => regionMap.value[id] || id
);
const text = names.join('、');
// 过长截断并支持 tooltip 悬浮查看全部
if (text.length > 22) {
return h(
'span',
{ title: text },
text.slice(0, 22) + '…'
);
}
return text;
}
// 兼容旧单值数据
const province = record.provinceId
? regionMap.value[String(record.provinceId)]
: '';