feat(person-picker): 新增人员筛选组件并完善开发配置
新增完整的PersonPickerField人员筛选组件,支持搜索、多选、类型筛选及黑名单展示 封装搜索人员的后端API接口 调整开发环境默认反向代理目标地址至开发服务器 在首页添加临时测试字段方便联调测试
Showing
6 changed files
with
128 additions
and
11 deletions
| 1 | -### | ||
| 2 | - # @Date: 2023-02-13 14:56:34 | ||
| 3 | - # @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | - # @LastEditTime: 2025-11-27 16:16:59 | ||
| 5 | - # @FilePath: /data-table/.env.development | ||
| 6 | - # @Description: 文件描述 | ||
| 7 | -### | ||
| 8 | # 资源公共路径 | 1 | # 资源公共路径 |
| 9 | VITE_BASE = / | 2 | VITE_BASE = / |
| 10 | 3 | ||
| ... | @@ -23,11 +16,11 @@ VITE_PIN = | ... | @@ -23,11 +16,11 @@ VITE_PIN = |
| 23 | 16 | ||
| 24 | # 反向代理服务器地址 | 17 | # 反向代理服务器地址 |
| 25 | # VITE_PROXY_TARGET = https://oa.anxinchashi.com/ | 18 | # VITE_PROXY_TARGET = https://oa.anxinchashi.com/ |
| 26 | -# VITE_PROXY_TARGET = http://oa-dev.onwall.cn | 19 | +VITE_PROXY_TARGET = http://oa-dev.onwall.cn |
| 27 | # VITE_PROXY_TARGET = http://oa.onwall.cn | 20 | # VITE_PROXY_TARGET = http://oa.onwall.cn |
| 28 | # VITE_PROXY_TARGET = https://www.wxgzjs.cn/ | 21 | # VITE_PROXY_TARGET = https://www.wxgzjs.cn/ |
| 29 | # VITE_PROXY_TARGET = https://oa.baorongsi.com/ | 22 | # VITE_PROXY_TARGET = https://oa.baorongsi.com/ |
| 30 | -VITE_PROXY_TARGET = https://oa.jcedu.org/ | 23 | +# VITE_PROXY_TARGET = https://oa.jcedu.org/ |
| 31 | 24 | ||
| 32 | # PC端地址 | 25 | # PC端地址 |
| 33 | VITE_MOBILE_URL = http://localhost:5173/ | 26 | VITE_MOBILE_URL = http://localhost:5173/ | ... | ... |
| 1 | /* | 1 | /* |
| 2 | * @Date: 2022-06-17 14:54:29 | 2 | * @Date: 2022-06-17 14:54:29 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2024-06-05 10:12:06 | 4 | + * @LastEditTime: 2026-05-25 13:48:13 |
| 5 | * @FilePath: /data-table/src/api/component.js | 5 | * @FilePath: /data-table/src/api/component.js |
| 6 | * @Description: 组件接口 | 6 | * @Description: 组件接口 |
| 7 | */ | 7 | */ |
| ... | @@ -12,6 +12,7 @@ const Api = { | ... | @@ -12,6 +12,7 @@ const Api = { |
| 12 | FLOW_DEPT_LIST: '/srv/?a=flow_setting&t=flow_dept_list', | 12 | FLOW_DEPT_LIST: '/srv/?a=flow_setting&t=flow_dept_list', |
| 13 | FLOW_ROLE_LIST: '/srv/?a=flow_setting&t=flow_role_list', | 13 | FLOW_ROLE_LIST: '/srv/?a=flow_setting&t=flow_role_list', |
| 14 | SEARCH_USER_DEPT_ROLE: '/srv/?a=flow_setting&t=search_user_dept_role', | 14 | SEARCH_USER_DEPT_ROLE: '/srv/?a=flow_setting&t=search_user_dept_role', |
| 15 | + SEARCH_PERSON_PICKER: '/srv/?a=person_picker_search', | ||
| 15 | } | 16 | } |
| 16 | 17 | ||
| 17 | /** | 18 | /** |
| ... | @@ -42,3 +43,14 @@ export const getFlowRoleListAPI = (params) => fn(fetch.get(Api.FLOW_ROLE_LIST, p | ... | @@ -42,3 +43,14 @@ export const getFlowRoleListAPI = (params) => fn(fetch.get(Api.FLOW_ROLE_LIST, p |
| 42 | * @param: word 搜索内容 | 43 | * @param: word 搜索内容 |
| 43 | */ | 44 | */ |
| 44 | export const searchUserDeptRoleAPI = (params) => fn(fetch.get(Api.SEARCH_USER_DEPT_ROLE, params)); | 45 | export const searchUserDeptRoleAPI = (params) => fn(fetch.get(Api.SEARCH_USER_DEPT_ROLE, params)); |
| 46 | + | ||
| 47 | +/** | ||
| 48 | + * @description: 搜索人员 | ||
| 49 | + * @param: form_code 表单code | ||
| 50 | + * @param: field_name 字段名 | ||
| 51 | + * @param: keyword 搜索关键字 | ||
| 52 | + * @param: type volunteer=义工、contact=联系人 | ||
| 53 | + * @param: page 页码,从0开始 | ||
| 54 | + * @param: limit 每页数量 | ||
| 55 | + */ | ||
| 56 | +export const searchPersonPickerAPI = (params) => fn(fetch.get(Api.SEARCH_PERSON_PICKER, params)); | ... | ... |
This diff is collapsed. Click to expand it.
src/components/PersonPickerField/index.vue
0 → 100644
| 1 | +<!-- | ||
| 2 | + * @Date: 2026-05-25 17:10:00 | ||
| 3 | + * @LastEditors: Codex | ||
| 4 | + * @LastEditTime: 2026-05-25 17:10:00 | ||
| 5 | + * @FilePath: /data-table/src/components/PersonPickerField/index.vue | ||
| 6 | + * @Description: 人员筛选控件 | ||
| 7 | +--> | ||
| 8 | +<template> | ||
| 9 | + <div v-if="HideShow" class="person-picker-field-page"> | ||
| 10 | + <div :class="[isGroup ? 'group-label' : 'label']"> | ||
| 11 | + <span v-if="item.component_props.disabled_show"><van-icon name="https://cdn.ipadbiz.cn/custom_form/icon/closed-eye1.png" /></span> | ||
| 12 | + <span v-if="item.component_props.required" style="color: red"> *</span> | ||
| 13 | + <span :class="[ReadonlyShow ? 'readonly-show' : '']">{{ item.component_props.label }}</span> | ||
| 14 | + </div> | ||
| 15 | + | ||
| 16 | + <van-field :name="item.key" :rules="rules" style="padding: 0 1rem;"> | ||
| 17 | + <template #input> | ||
| 18 | + <my-component /> | ||
| 19 | + </template> | ||
| 20 | + </van-field> | ||
| 21 | + </div> | ||
| 22 | +</template> | ||
| 23 | + | ||
| 24 | +<script setup> | ||
| 25 | +import { useRoute } from 'vue-router'; | ||
| 26 | +import MyComponent from './MyComponent.vue'; | ||
| 27 | + | ||
| 28 | +const $route = useRoute(); | ||
| 29 | +const props = defineProps({ | ||
| 30 | + item: Object, | ||
| 31 | +}); | ||
| 32 | + | ||
| 33 | +// 注入子组件属性,复用现有字段组件的表单接入方式。 | ||
| 34 | +provide('props', props.item); | ||
| 35 | + | ||
| 36 | +// 隐藏显示 | ||
| 37 | +const HideShow = computed(() => { | ||
| 38 | + return !props.item.component_props.disabled; | ||
| 39 | +}); | ||
| 40 | + | ||
| 41 | +// 只读显示-流程模式 | ||
| 42 | +const ReadonlyShow = computed(() => { | ||
| 43 | + return ($route.query.page_type === 'flow' || $route.query.page_type === 'edit') && !props.item.component_props.readonly; | ||
| 44 | +}); | ||
| 45 | + | ||
| 46 | +// 集合组标识 | ||
| 47 | +const isGroup = computed(() => { | ||
| 48 | + return props.item.component_props.is_field_group; | ||
| 49 | +}); | ||
| 50 | + | ||
| 51 | +const required = props.item.component_props.required; | ||
| 52 | +const validator = (val) => { | ||
| 53 | + if (!required) { | ||
| 54 | + return true; | ||
| 55 | + } | ||
| 56 | + return Array.isArray(val) && val.length > 0; | ||
| 57 | +}; | ||
| 58 | +const validatorMessage = (val, rule) => { | ||
| 59 | + if (required && (!Array.isArray(val) || !val.length)) { | ||
| 60 | + return '选择不能为空'; | ||
| 61 | + } | ||
| 62 | +}; | ||
| 63 | +const rules = [{ validator, message: validatorMessage }]; | ||
| 64 | +</script> | ||
| 65 | + | ||
| 66 | +<style lang="less" scoped> | ||
| 67 | +.person-picker-field-page { | ||
| 68 | + .label { | ||
| 69 | + padding: 1rem 1rem 0 1rem; | ||
| 70 | + font-size: 0.9rem; | ||
| 71 | + font-weight: bold; | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + .group-label { | ||
| 75 | + padding: 0.75rem 0 0.75rem 1rem; | ||
| 76 | + font-size: 0.9rem; | ||
| 77 | + font-weight: bold; | ||
| 78 | + background-color: #f9f9f9; | ||
| 79 | + color: #666; | ||
| 80 | + border-top: 1px solid #eee; | ||
| 81 | + border-bottom: 1px solid #eee; | ||
| 82 | + | ||
| 83 | + span { | ||
| 84 | + color: red; | ||
| 85 | + } | ||
| 86 | + } | ||
| 87 | +} | ||
| 88 | +</style> |
| ... | @@ -33,6 +33,7 @@ import AppointmentField from '@/components/AppointmentField/index.vue'; | ... | @@ -33,6 +33,7 @@ import AppointmentField from '@/components/AppointmentField/index.vue'; |
| 33 | import CustomField from '@/components/CustomField/index.vue'; | 33 | import CustomField from '@/components/CustomField/index.vue'; |
| 34 | import GroupField from '@/components/GroupField/index.vue'; | 34 | import GroupField from '@/components/GroupField/index.vue'; |
| 35 | import OrgPickerField from '@/components/OrgPickerField/index.vue'; | 35 | import OrgPickerField from '@/components/OrgPickerField/index.vue'; |
| 36 | +import PersonPickerField from '@/components/PersonPickerField/index.vue'; | ||
| 36 | import VolunteerGroupField from '@/components/VolunteerGroupField/index.vue'; | 37 | import VolunteerGroupField from '@/components/VolunteerGroupField/index.vue'; |
| 37 | 38 | ||
| 38 | /** | 39 | /** |
| ... | @@ -66,6 +67,7 @@ import VolunteerGroupField from '@/components/VolunteerGroupField/index.vue'; | ... | @@ -66,6 +67,7 @@ import VolunteerGroupField from '@/components/VolunteerGroupField/index.vue'; |
| 66 | * @type appointment 预约控件 AppointmentField | 67 | * @type appointment 预约控件 AppointmentField |
| 67 | * @type group 组集合输入控件 GroupField | 68 | * @type group 组集合输入控件 GroupField |
| 68 | * @type org_picker 树形选择控件 OrgPickerField | 69 | * @type org_picker 树形选择控件 OrgPickerField |
| 70 | + * @type person_picker 人员筛选控件 PersonPickerField | ||
| 69 | * @type volunteer_group 义工组别选择控件 VolunteerGroupField | 71 | * @type volunteer_group 义工组别选择控件 VolunteerGroupField |
| 70 | * @type table 表格控件 TableField | 72 | * @type table 表格控件 TableField |
| 71 | */ | 73 | */ |
| ... | @@ -183,6 +185,9 @@ export function createComponentType(data) { | ... | @@ -183,6 +185,9 @@ export function createComponentType(data) { |
| 183 | if (item.component_props.tag === 'org_picker') { | 185 | if (item.component_props.tag === 'org_picker') { |
| 184 | item.component = OrgPickerField; | 186 | item.component = OrgPickerField; |
| 185 | } | 187 | } |
| 188 | + if (item.component_props.tag === 'person_picker') { | ||
| 189 | + item.component = PersonPickerField; | ||
| 190 | + } | ||
| 186 | if (item.component_props.tag === 'volunteer_group') { | 191 | if (item.component_props.tag === 'volunteer_group') { |
| 187 | item.component = VolunteerGroupField; | 192 | item.component = VolunteerGroupField; |
| 188 | } | 193 | } | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2022-07-18 10:22:22 | 2 | * @Date: 2022-07-18 10:22:22 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-11-27 16:25:10 | 4 | + * @LastEditTime: 2026-05-25 13:58:52 |
| 5 | * @FilePath: /data-table/src/views/index.vue | 5 | * @FilePath: /data-table/src/views/index.vue |
| 6 | * @Description: 首页 | 6 | * @Description: 首页 |
| 7 | --> | 7 | --> |
| ... | @@ -509,6 +509,25 @@ onMounted(async () => { | ... | @@ -509,6 +509,25 @@ onMounted(async () => { |
| 509 | // "interaction_type": "h5edit" | 509 | // "interaction_type": "h5edit" |
| 510 | // }) | 510 | // }) |
| 511 | 511 | ||
| 512 | + // 临时测试字段:直接挂到页面渲染链里,方便联调人员搜索多选组件。 | ||
| 513 | + page_form.push({ | ||
| 514 | + tag: 'person_picker', | ||
| 515 | + name: 'person_picker_temp', | ||
| 516 | + index: 999999, | ||
| 517 | + label: '人员筛选(临时测试)', | ||
| 518 | + unique: false, | ||
| 519 | + default: [], | ||
| 520 | + disabled: false, | ||
| 521 | + field_id: 999999, | ||
| 522 | + readonly: false, | ||
| 523 | + required: false, | ||
| 524 | + data_type: 'text', | ||
| 525 | + field_name: 'field_person_picker_temp', | ||
| 526 | + placeholder: '请输入关键词后搜索人员', | ||
| 527 | + interaction_type: 'h5edit', | ||
| 528 | + person_type: '', | ||
| 529 | + }); | ||
| 530 | + | ||
| 512 | formData.value = formatData(page_form); | 531 | formData.value = formatData(page_form); |
| 513 | // TAG: 构建分页组件 | 532 | // TAG: 构建分页组件 |
| 514 | if (page_type === 'add' || page_type === undefined || model === 'preview') { | 533 | if (page_type === 'add' || page_type === undefined || model === 'preview') { | ... | ... |
-
Please register or login to post a comment