hookehuyr

refactor(person-picker): 调整为使用配置的单一人员类型搜索

适配更新后的人员搜索接口,移除不再需要的多类型筛选功能
更新接口文档注释,修正type参数说明并添加备注
移除顶部的类型筛选栏UI,清理未使用的计算属性和工具函数
调整人员记录映射和类型格式化逻辑,使用配置的人员类型作为搜索参数
修复人员类型缺失时的回退逻辑,优先使用当前字段配置的类型
...@@ -49,8 +49,9 @@ export const searchUserDeptRoleAPI = (params) => fn(fetch.get(Api.SEARCH_USER_DE ...@@ -49,8 +49,9 @@ export const searchUserDeptRoleAPI = (params) => fn(fetch.get(Api.SEARCH_USER_DE
49 * @param: form_code 表单code 49 * @param: form_code 表单code
50 * @param: field_name 字段名 50 * @param: field_name 字段名
51 * @param: keyword 搜索关键字 51 * @param: keyword 搜索关键字
52 - * @param: type volunteer=义工、contact=联系人 52 + * @param: type 人员类型编码,直接使用配置接口返回的 person_type,例如 contact / volunteer
53 * @param: page 页码,从0开始 53 * @param: page 页码,从0开始
54 * @param: limit 每页数量 54 * @param: limit 每页数量
55 + * @note: 配置接口里的 person_type_title 仅用于前端中文展示,这个搜索接口不接收该字段
55 */ 56 */
56 export const searchPersonPickerAPI = (params) => fn(fetch.get(Api.SEARCH_PERSON_PICKER, params)); 57 export const searchPersonPickerAPI = (params) => fn(fetch.get(Api.SEARCH_PERSON_PICKER, params));
......
...@@ -54,16 +54,6 @@ ...@@ -54,16 +54,6 @@
54 </template> 54 </template>
55 </van-field> 55 </van-field>
56 </div> 56 </div>
57 - <div v-if="typeFilterOptions.length > 1" class="type-filter-row">
58 - <div
59 - v-for="option in typeFilterOptions"
60 - :key="option.value || 'all'"
61 - :class="['type-filter-chip', { 'type-filter-chip--active': selectedTypeFilter === option.value }]"
62 - @click="onSelectSearchType(option)"
63 - >
64 - {{ option.text }}
65 - </div>
66 - </div>
67 </div> 57 </div>
68 58
69 <div class="selected-box" style="margin: 1rem;"> 59 <div class="selected-box" style="margin: 1rem;">
...@@ -126,7 +116,7 @@ ...@@ -126,7 +116,7 @@
126 </template> 116 </template>
127 117
128 <script setup> 118 <script setup>
129 -import { computed, inject, onMounted, ref } from 'vue'; 119 +import { inject, onMounted, ref } from 'vue';
130 import { useRoute } from 'vue-router'; 120 import { useRoute } from 'vue-router';
131 import { useCustomFieldValue } from '@vant/use'; 121 import { useCustomFieldValue } from '@vant/use';
132 import Cookies from 'js-cookie'; 122 import Cookies from 'js-cookie';
...@@ -155,11 +145,28 @@ const fieldValue = ref([]); ...@@ -155,11 +145,28 @@ const fieldValue = ref([]);
155 const searchPage = ref(0); 145 const searchPage = ref(0);
156 const searchLimit = ref(Number(props.component_props.limit) || 20); 146 const searchLimit = ref(Number(props.component_props.limit) || 20);
157 const searchTotal = ref(0); 147 const searchTotal = ref(0);
158 -const selectedTypeFilter = ref(''); 148 +
149 +/**
150 + * 当前字段配置的人员类型编码,直接传给搜索接口的 type 参数。
151 + *
152 + * @returns {string}
153 + */
154 +const getConfiguredPersonType = () => {
155 + return String(props.component_props.person_type || '').trim();
156 +};
157 +
158 +/**
159 + * 当前字段配置的人员类型中文名,用于已选项和结果列表展示。
160 + *
161 + * @returns {string}
162 + */
163 +const getConfiguredPersonTypeTitle = () => {
164 + return String(props.component_props.person_type_title || '').trim();
165 +};
159 166
160 /** 167 /**
161 * 将人员接口记录整理成组件内部统一结构。 168 * 将人员接口记录整理成组件内部统一结构。
162 - * 这里直接按后端约定字段读取,只补齐展示时会用到的空字符串 169 + * 这里直接按后端约定字段读取;如果历史数据里没带 type,就回退到当前字段配置的 person_type
163 * 170 *
164 * @param {Object} person 人员记录 171 * @param {Object} person 人员记录
165 * @returns {{ id: number, name: string, nickname: string, phone: string, idcard: string, type: string, is_blacklist: boolean }} 172 * @returns {{ id: number, name: string, nickname: string, phone: string, idcard: string, type: string, is_blacklist: boolean }}
...@@ -171,7 +178,7 @@ const mapPersonRecord = (person) => { ...@@ -171,7 +178,7 @@ const mapPersonRecord = (person) => {
171 nickname: person.nickname || '', 178 nickname: person.nickname || '',
172 phone: person.phone || '', 179 phone: person.phone || '',
173 idcard: person.idcard || '', 180 idcard: person.idcard || '',
174 - type: person.type || '', 181 + type: String(person.type || getConfiguredPersonType()).trim(),
175 is_blacklist: normalizeBlacklistFlag(person.is_blacklist), 182 is_blacklist: normalizeBlacklistFlag(person.is_blacklist),
176 }; 183 };
177 }; 184 };
...@@ -204,24 +211,6 @@ const normalizeStoredPersonList = (value) => { ...@@ -204,24 +211,6 @@ const normalizeStoredPersonList = (value) => {
204 }; 211 };
205 212
206 /** 213 /**
207 - * 后端返回的人员类型配置已经自带 title/value,这里只做结构清洗和按 value 去重。
208 - *
209 - * @param {Array} personTypes 人员类型配置
210 - * @returns {{ title: string, value: string }[]}
211 - */
212 -const normalizePersonTypes = (personTypes = []) => {
213 - return _.uniqBy(
214 - personTypes
215 - .map((item) => ({
216 - title: String(item?.title || '').trim(),
217 - value: String(item?.value || '').trim(),
218 - }))
219 - .filter((item) => item.title && item.value),
220 - 'value',
221 - );
222 -};
223 -
224 -/**
225 * 将已确认人员同步到自定义字段值,保证表单提交拿到的是最终确认结果。 214 * 将已确认人员同步到自定义字段值,保证表单提交拿到的是最终确认结果。
226 */ 215 */
227 const syncFieldValue = () => { 216 const syncFieldValue = () => {
...@@ -229,16 +218,15 @@ const syncFieldValue = () => { ...@@ -229,16 +218,15 @@ const syncFieldValue = () => {
229 props.value = fieldValue.value; 218 props.value = fieldValue.value;
230 }; 219 };
231 220
232 -const findPersonTypeOption = (type) => { 221 +const formatPersonType = (type) => {
233 - if (!type) { 222 + const configuredType = getConfiguredPersonType();
234 - return null; 223 + const configuredTypeTitle = getConfiguredPersonTypeTitle();
235 - }
236 224
237 - return allowedPersonTypes.value.find((item) => item.value === type) || null; 225 + if (configuredTypeTitle && (!type || type === configuredType)) {
238 -}; 226 + return configuredTypeTitle;
227 + }
239 228
240 -const formatPersonType = (type) => { 229 + return type || configuredTypeTitle || '人员';
241 - return findPersonTypeOption(type)?.title || type || '人员';
242 }; 230 };
243 231
244 const formatPersonTitle = (person) => { 232 const formatPersonTitle = (person) => {
...@@ -253,44 +241,6 @@ const isPersonBlacklisted = (person) => { ...@@ -253,44 +241,6 @@ const isPersonBlacklisted = (person) => {
253 }; 241 };
254 242
255 /** 243 /**
256 - * 当前字段允许搜索的人员类型列表,直接取后端配置。
257 - */
258 -const allowedPersonTypes = computed(() => {
259 - return normalizePersonTypes(props.component_props.person_types);
260 -});
261 -
262 -/**
263 - * 多类型字段默认停在“全部类型”,发请求时显式传空字符串给后端。
264 - */
265 -const defaultSearchType = computed(() => {
266 - return '';
267 -});
268 -
269 -/**
270 - * 根据后端返回的 person_types 生成顶部筛选项。
271 - */
272 -const typeFilterOptions = computed(() => {
273 - if (!allowedPersonTypes.value.length) {
274 - return [];
275 - }
276 -
277 - const allowedOptions = allowedPersonTypes.value.map((typeOption) => ({
278 - text: typeOption.title,
279 - value: typeOption.value,
280 - }));
281 -
282 - if (allowedOptions.length === 1) {
283 - return allowedOptions;
284 - }
285 -
286 - return [{ text: '全部类型', value: '' }, ...allowedOptions];
287 -});
288 -
289 -const getSearchType = () => {
290 - return selectedTypeFilter.value || defaultSearchType.value || '';
291 -};
292 -
293 -/**
294 * 根据当前草稿已选项回填搜索结果勾选状态,保证翻页后勾选表现一致。 244 * 根据当前草稿已选项回填搜索结果勾选状态,保证翻页后勾选表现一致。
295 */ 245 */
296 const syncSearchChecked = () => { 246 const syncSearchChecked = () => {
...@@ -384,14 +334,6 @@ const onClearSearch = () => { ...@@ -384,14 +334,6 @@ const onClearSearch = () => {
384 resetSearchState(); 334 resetSearchState();
385 }; 335 };
386 336
387 -const onSelectSearchType = async (action) => {
388 - selectedTypeFilter.value = action.value;
389 -
390 - if (searchKeyword.value.trim() && hasSearched.value) {
391 - await onSearch();
392 - }
393 -};
394 -
395 /** 337 /**
396 * 组装人员搜索接口参数。 338 * 组装人员搜索接口参数。
397 * 339 *
...@@ -405,7 +347,7 @@ const buildSearchParams = (page) => { ...@@ -405,7 +347,7 @@ const buildSearchParams = (page) => {
405 keyword: searchKeyword.value.trim(), 347 keyword: searchKeyword.value.trim(),
406 page, 348 page,
407 limit: searchLimit.value, 349 limit: searchLimit.value,
408 - type: getSearchType(), 350 + type: getConfiguredPersonType(),
409 }; 351 };
410 if ($route.query.force_back) { 352 if ($route.query.force_back) {
411 params.force_back = $route.query.force_back; 353 params.force_back = $route.query.force_back;
...@@ -430,7 +372,7 @@ const fetchSearchPage = async (page) => { ...@@ -430,7 +372,7 @@ const fetchSearchPage = async (page) => {
430 }; 372 };
431 } 373 }
432 374
433 - // 搜索结果以接口返回为准,前端不再额外按 person_types 二次过滤,避免把有效数据误筛掉 375 + // 搜索结果以接口返回为准,当前字段只会请求配置里的单一 person_type
434 const remoteResults = result.data.map(mapPersonRecord); 376 const remoteResults = result.data.map(mapPersonRecord);
435 377
436 return { 378 return {
...@@ -532,7 +474,6 @@ useCustomFieldValue(() => fieldValue.value); ...@@ -532,7 +474,6 @@ useCustomFieldValue(() => fieldValue.value);
532 onMounted(() => { 474 onMounted(() => {
533 confirmedPersons.value = normalizeStoredPersonList(props.component_props.default); 475 confirmedPersons.value = normalizeStoredPersonList(props.component_props.default);
534 draftPersons.value = _.cloneDeep(confirmedPersons.value); 476 draftPersons.value = _.cloneDeep(confirmedPersons.value);
535 - selectedTypeFilter.value = defaultSearchType.value || '';
536 syncFieldValue(); 477 syncFieldValue();
537 }); 478 });
538 </script> 479 </script>
......