diff --git a/.workbuddy/memory/2026-08-11.md b/.workbuddy/memory/2026-08-11.md new file mode 100644 index 0000000..c68e693 --- /dev/null +++ b/.workbuddy/memory/2026-08-11.md @@ -0,0 +1,28 @@ +# 2026-08-11 工作记录 + +## 运费模板配送地区改为「省+市两级多选穿梭弹窗」 + +### 需求背景 +客户要求运费模板的配送地区选择改成仿截图的双栏穿梭弹窗(左侧候选 + 右侧已选), +选择粒度从原「省+市单选(级联)」升级为「多条规则覆盖多城市」,并保留「不选=全国」规则。 + +### 改动清单(纯前端,后端字段由用户确认可改) +1. **新建组件** `src/components/RegionMultiSelect/index.vue` + - 树形可展开:省级可展开看市级;省级"选择"=选该省全部市,市级可单独选。 + - 左栏搜索(按省/市名过滤)、右栏已选列表(单项是"省/市"+取消)。 + - 底部:全部选择 / 取消 / 确认。 + - `v-model:value` 绑定已选**城市ID数组**;额外 emit `update:provinceIds`(由城市推导的省份ID数组)。 + - 复用 `src/components/RegionsSelect/load-data.ts` 的 getRegionsData()(public/json/regions-data.json,取省+市两级,丢弃区级)。 +2. **model** `src/api/shop/shopExpressTemplateDetail/model/index.ts` + - 新增 `provinceIds?: string`(逗号分隔)、`cityIds?: string`(逗号分隔);保留旧 `provinceId/cityId` 兼容历史数据。 +3. **编辑弹窗** `src/views/shop/shopExpressTemplateDetail/components/shopExpressTemplateDetailEdit.vue` + - 移除 `RegionsSelect type="provinceCity"`,改用 `RegionMultiSelect`。 + - 打开时从 `record.cityIds`(兼容旧 `cityId`)回填;保存时数组→逗号串提交 `cityIds/provinceIds`。 +4. **列表页** `src/views/shop/shopExpressTemplateDetail/index.vue` + - "配送地区"列 customRender 改为遍历 `cityIds` 多值反查 regionMap;超22字截断+tooltip;旧单值数据兼容。 + - 新增 `h` 函数 import。 + +### 待办 / 风险 +- **后端需同步改造**:表加 `province_ids` / `city_ids`(varchar 逗号分隔),接口 add/update 接收这两个字段。前端已按此约定提交,后端未改则数据落不了库。 +- antd 为 v4,`var(--ant-color-border)` 在暗色下可能回退浅灰(边框略浅,不影响功能);如要完美暗色适配可后续改 less 变量。 +- 构建验证(vite build)已触发,待结果确认无编译错误。 diff --git a/.workbuddy/memory/MEMORY.md b/.workbuddy/memory/MEMORY.md index d471394..0561ad1 100644 --- a/.workbuddy/memory/MEMORY.md +++ b/.workbuddy/memory/MEMORY.md @@ -55,3 +55,11 @@ - 一键上下架、批量上下架均通过 `updateShopGoods({ ...record, status })` 直接改 status 实现(不额外用 isShow 字段)。 - 列表 tag 仍显示:0=已上架、1=待上架、2=待审核、3=审核不通过。 +### 运费模板配送地区多选(2026-08-11 已实现前端) +- 需求:配送地区选择从「省+市单选级联」升级为「省+市两级多选穿梭弹窗」(仿客户截图双栏:左候选/右已选)。 +- 前端组件:`src/components/RegionMultiSelect/index.vue`(树形可展开,省级"选择"=选该省全部市,市级可单独选;@update:value 城市ID数组 + @update:provinceIds 省份ID数组)。 +- 数据字段:model 新增 `provinceIds` / `cityIds`(逗号分隔字符串);保留旧 `provinceId`/`cityId` 兼容(编辑回显时旧单值会自动补进 cityIds)。 +- 列表多值展示:`index.vue` customRender 遍历 `cityIds` 反查 regionMap,超 22 字截断 + tooltip。 +- ⚠️ 后端待改:`shop_express_template_detail` 表加 `province_ids`/`city_ids`(varchar 逗号分隔),add/update 接口接收这两个字段;前端已按此约定提交,后端未改前数据落不了库。 +- 注:与"不发货地区(黑名单) regionMode/regionIds"是不同需求,勿混淆。 + diff --git a/src/api/shop/shopExpressTemplateDetail/model/index.ts b/src/api/shop/shopExpressTemplateDetail/model/index.ts index eeee907..1ad36ad 100644 --- a/src/api/shop/shopExpressTemplateDetail/model/index.ts +++ b/src/api/shop/shopExpressTemplateDetail/model/index.ts @@ -12,10 +12,14 @@ export interface ShopExpressTemplateDetail { templateId?: number; /** 计费方式: true=按件, false=按重量 */ type?: boolean; - /** 省份ID */ + /** 省份ID(旧单值字段,兼容历史数据) */ provinceId?: number; - /** 城市ID */ + /** 城市ID(旧单值字段,兼容历史数据) */ cityId?: number; + /** 省份IDs(逗号分隔,多选配送区域) */ + provinceIds?: string; + /** 城市IDs(逗号分隔,多选配送区域) */ + cityIds?: string; /** 首件数量/重量 */ firstNum?: string; /** 首件价格 */ diff --git a/src/components/RegionMultiSelect/index.vue b/src/components/RegionMultiSelect/index.vue new file mode 100644 index 0000000..396e952 --- /dev/null +++ b/src/components/RegionMultiSelect/index.vue @@ -0,0 +1,375 @@ + + + + + + diff --git a/src/views/shop/shopExpressTemplateDetail/components/shopExpressTemplateDetailEdit.vue b/src/views/shop/shopExpressTemplateDetail/components/shopExpressTemplateDetailEdit.vue index 39cb439..0d99197 100644 --- a/src/views/shop/shopExpressTemplateDetail/components/shopExpressTemplateDetailEdit.vue +++ b/src/views/shop/shopExpressTemplateDetail/components/shopExpressTemplateDetailEdit.vue @@ -20,13 +20,13 @@ styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' } " > - - + +
不选则默认全国
(null); - // 级联选择器选中值(字符串ID数组) - const regions = ref([]); + // 已选城市ID数组 + const cityIds = ref([]); + // 已选省份ID数组(由城市推导,随城市变化同步) + const provinceIds = ref([]); // 表单数据 const form = reactive({ @@ -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 } ); + + diff --git a/src/views/shop/shopExpressTemplateDetail/index.vue b/src/views/shop/shopExpressTemplateDetail/index.vue index b8994dc..8616a99 100644 --- a/src/views/shop/shopExpressTemplateDetail/index.vue +++ b/src/views/shop/shopExpressTemplateDetail/index.vue @@ -51,7 +51,7 @@