|
@@ -764,218 +764,7 @@
|
|
|
|
|
|
|
|
<StationMapModal v-model:open="stationMapModalVisible" :stations="stationMapPoints" :title="stationMapModalTitle" />
|
|
<StationMapModal v-model:open="stationMapModalVisible" :stations="stationMapPoints" :title="stationMapModalTitle" />
|
|
|
|
|
|
|
|
- <Modal
|
|
|
|
|
- v-model:open="groupModalVisible"
|
|
|
|
|
- :footer="null"
|
|
|
|
|
- :centered="true"
|
|
|
|
|
- :width="960"
|
|
|
|
|
- wrapClassName="object-group-modal"
|
|
|
|
|
- @cancel="closeGroupModal"
|
|
|
|
|
- >
|
|
|
|
|
- <template #title>
|
|
|
|
|
- <div class="portrait-modal-title-block">
|
|
|
|
|
- <div class="portrait-modal-title">对象分组</div>
|
|
|
|
|
- <div class="portrait-modal-subtitle">维护分组结构、查看成员状态并设置主要对象</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </template>
|
|
|
|
|
-
|
|
|
|
|
- <div class="group-modal-content">
|
|
|
|
|
- <div class="group-modal-actions">
|
|
|
|
|
- <div class="group-modal-actions__info">左侧切换分组,右侧维护成员与主要对象。</div>
|
|
|
|
|
- <div class="group-modal-actions__buttons">
|
|
|
|
|
- <Button class="head-btn head-btn--primary" @click="openCreateGroupModal">新增分组</Button>
|
|
|
|
|
- <Button class="head-btn" @click="handleEditGroup">编辑分组</Button>
|
|
|
|
|
- <Button class="head-btn head-btn--danger" @click="handleDeleteGroup">删除分组</Button>
|
|
|
|
|
- <Button class="head-btn head-btn--accent" @click="handleAddObject">添加对象</Button>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div class="group-modal-main">
|
|
|
|
|
- <div class="group-left">
|
|
|
|
|
- <div class="group-panel-head">
|
|
|
|
|
- <div class="group-panel-title">分组列表</div>
|
|
|
|
|
- <div class="group-panel-badge">{{ objectGroups.length }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <List :data-source="objectGroups" size="small" class="group-list">
|
|
|
|
|
- <template #renderItem="{ item }">
|
|
|
|
|
- <List.Item>
|
|
|
|
|
- <Button
|
|
|
|
|
- block
|
|
|
|
|
- class="group-select-btn"
|
|
|
|
|
- :type="item.id === activeGroupId ? 'primary' : 'default'"
|
|
|
|
|
- @click="activeGroupId = item.id"
|
|
|
|
|
- >
|
|
|
|
|
- <span class="group-select-btn__name">{{ item.name }}</span>
|
|
|
|
|
- </Button>
|
|
|
|
|
- </List.Item>
|
|
|
|
|
- </template>
|
|
|
|
|
- </List>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div class="group-right">
|
|
|
|
|
- <div class="group-panel-head group-panel-head--right">
|
|
|
|
|
- <div class="group-panel-title-wrap">
|
|
|
|
|
- <div class="group-panel-title">{{ activeGroup?.name || '分组成员' }}</div>
|
|
|
|
|
- <div class="group-panel-subtitle">
|
|
|
|
|
- {{ activeGroup ? `当前分组共 ${activeGroupMembers.length} 人` : '请选择左侧分组查看成员' }}
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <List :data-source="activeGroupMembers" class="member-list" item-layout="horizontal">
|
|
|
|
|
- <template #renderItem="{ item }">
|
|
|
|
|
- <List.Item class="member-item">
|
|
|
|
|
- <template #actions>
|
|
|
|
|
- <Button
|
|
|
|
|
- type="link"
|
|
|
|
|
- class="member-main-btn"
|
|
|
|
|
- :disabled="item.hasMain"
|
|
|
|
|
- @click="handleChangeMain(item.id)"
|
|
|
|
|
- >
|
|
|
|
|
- 设为主对象
|
|
|
|
|
- </Button>
|
|
|
|
|
- <Button type="link" class="member-remove-btn" @click="handleRemoveMember(item.id)">移出分组</Button>
|
|
|
|
|
- </template>
|
|
|
|
|
- <List.Item.Meta>
|
|
|
|
|
- <template #avatar>
|
|
|
|
|
- <Avatar :size="34" class="member-avatar-icon">
|
|
|
|
|
- <svg viewBox="0 0 24 24" fill="currentColor">
|
|
|
|
|
- <path
|
|
|
|
|
- d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"
|
|
|
|
|
- />
|
|
|
|
|
- </svg>
|
|
|
|
|
- </Avatar>
|
|
|
|
|
- </template>
|
|
|
|
|
- <template #title>
|
|
|
|
|
- <div class="member-name-row">
|
|
|
|
|
- <span class="member-name">{{ item.personName }}</span>
|
|
|
|
|
- <span v-if="item.hasMain" class="member-main-tag">主要</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- </template>
|
|
|
|
|
- <template #description>
|
|
|
|
|
- <span class="member-desc">{{ item.hasMain ? '当前主对象' : '分组成员' }}</span>
|
|
|
|
|
- </template>
|
|
|
|
|
- </List.Item.Meta>
|
|
|
|
|
- </List.Item>
|
|
|
|
|
- </template>
|
|
|
|
|
- </List>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </Modal>
|
|
|
|
|
-
|
|
|
|
|
- <Modal
|
|
|
|
|
- v-model:open="createGroupModalVisible"
|
|
|
|
|
- :title="isEditGroupMode ? '编辑分组' : '新建分组'"
|
|
|
|
|
- :confirmLoading="creatingGroup"
|
|
|
|
|
- :width="640"
|
|
|
|
|
- :centered="true"
|
|
|
|
|
- :okText="isEditGroupMode ? '保存分组' : '创建分组'"
|
|
|
|
|
- cancelText="取消"
|
|
|
|
|
- wrapClassName="create-group-modal"
|
|
|
|
|
- @ok="handleCreateGroup"
|
|
|
|
|
- @cancel="closeCreateGroupModal"
|
|
|
|
|
- >
|
|
|
|
|
- <Form layout="vertical" class="create-group-form">
|
|
|
|
|
- <div class="create-group-section">
|
|
|
|
|
- <div class="create-group-section__head">
|
|
|
|
|
- <div class="create-group-section__title">分组信息</div>
|
|
|
|
|
- <div class="create-group-section__desc">建议使用便于识别的名称,主体对象可按需选择。</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <Form.Item label="分组名称" required :validateStatus="groupNameError ? 'error' : ''" :help="groupNameError">
|
|
|
|
|
- <Input
|
|
|
|
|
- v-model:value="createGroupForm.groupName"
|
|
|
|
|
- placeholder="请输入分组名称"
|
|
|
|
|
- @blur="() => validateGroupName(true)"
|
|
|
|
|
- />
|
|
|
|
|
- </Form.Item>
|
|
|
|
|
- <Form.Item
|
|
|
|
|
- label="分组主体对象"
|
|
|
|
|
- :required="!isEditGroupMode"
|
|
|
|
|
- :validateStatus="mainPersonError ? 'error' : ''"
|
|
|
|
|
- :help="mainPersonError"
|
|
|
|
|
- >
|
|
|
|
|
- <Select
|
|
|
|
|
- v-model:value="createGroupForm.mainPerson"
|
|
|
|
|
- :options="personOptions"
|
|
|
|
|
- :placeholder="isEditGroupMode ? '请选择主体对象' : '请选择主体对象'"
|
|
|
|
|
- allowClear
|
|
|
|
|
- :getPopupContainer="getModalPopupContainer"
|
|
|
|
|
- />
|
|
|
|
|
- </Form.Item>
|
|
|
|
|
- <Form.Item label="备注">
|
|
|
|
|
- <Input.TextArea v-model:value="createGroupForm.remark" :rows="4" placeholder="请输入分组说明" />
|
|
|
|
|
- </Form.Item>
|
|
|
|
|
- </div>
|
|
|
|
|
- </Form>
|
|
|
|
|
- </Modal>
|
|
|
|
|
-
|
|
|
|
|
- <Modal
|
|
|
|
|
- v-model:open="addObjectModalVisible"
|
|
|
|
|
- title="添加对象"
|
|
|
|
|
- :confirmLoading="addingObjectToGroup"
|
|
|
|
|
- :width="720"
|
|
|
|
|
- :centered="true"
|
|
|
|
|
- okText="加入分组"
|
|
|
|
|
- cancelText="取消"
|
|
|
|
|
- wrapClassName="add-group-person-modal"
|
|
|
|
|
- @ok="handleConfirmAddObject"
|
|
|
|
|
- @cancel="closeAddObjectModal"
|
|
|
|
|
- >
|
|
|
|
|
- <div class="add-object-modal">
|
|
|
|
|
- <div class="add-object-modal__search">
|
|
|
|
|
- <div class="add-object-modal__search-head">
|
|
|
|
|
- <div class="add-object-modal__title-block">
|
|
|
|
|
- <div class="add-object-modal__title">选择对象</div>
|
|
|
|
|
- <div class="add-object-modal__desc">
|
|
|
|
|
- {{ activeGroup ? `当前分组:${activeGroup.name}` : '请选择需要加入分组的对象' }}
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="add-object-modal__stats">共 {{ filteredAddObjectCandidates.length }} 个对象</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <Input v-model:value="addObjectKeyword" allowClear placeholder="请输入姓名 / 证件号 / 手机号" />
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div class="add-object-modal__content">
|
|
|
|
|
- <div v-if="filteredAddObjectCandidates.length" class="add-object-list">
|
|
|
|
|
- <div
|
|
|
|
|
- v-for="item in filteredAddObjectCandidates"
|
|
|
|
|
- :key="item.key"
|
|
|
|
|
- class="add-object-item"
|
|
|
|
|
- :class="{
|
|
|
|
|
- 'is-selected': selectedAddPersonNames.includes(item.name),
|
|
|
|
|
- 'is-disabled': item.disabled,
|
|
|
|
|
- }"
|
|
|
|
|
- @click="handleToggleAddObject(item)"
|
|
|
|
|
- >
|
|
|
|
|
- <Checkbox
|
|
|
|
|
- :checked="selectedAddPersonNames.includes(item.name)"
|
|
|
|
|
- :disabled="item.disabled"
|
|
|
|
|
- @click.stop
|
|
|
|
|
- @change="(e) => handleCheckAddObject(item, e.target.checked)"
|
|
|
|
|
- />
|
|
|
|
|
- <PersonAvatar
|
|
|
|
|
- class="add-object-item__avatar"
|
|
|
|
|
- :src="item.photograph || undefined"
|
|
|
|
|
- :alt="item.name"
|
|
|
|
|
- :size="42"
|
|
|
|
|
- />
|
|
|
|
|
- <div class="add-object-item__body">
|
|
|
|
|
- <div class="add-object-item__head">
|
|
|
|
|
- <div class="add-object-item__name">{{ item.name }}</div>
|
|
|
|
|
- <span v-if="item.disabled" class="add-object-item__tag">已在分组</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="add-object-item__meta">
|
|
|
|
|
- <span>证件号:{{ maskCertNo(item.personCertNo) }}</span>
|
|
|
|
|
- <span>手机号:{{ item.primaryPhone }}</span>
|
|
|
|
|
- <span>银行卡:{{ item.cardCount }} 张</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div v-else class="add-object-modal__empty">{{ addObjectEmptyText }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </Modal>
|
|
|
|
|
|
|
+ <PersonGroupManageModal ref="groupManageModalRef" v-model:open="groupModalVisible" />
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
@@ -993,19 +782,7 @@
|
|
|
h,
|
|
h,
|
|
|
} from 'vue';
|
|
} from 'vue';
|
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
|
- import {
|
|
|
|
|
- Button,
|
|
|
|
|
- DatePicker,
|
|
|
|
|
- Input,
|
|
|
|
|
- Select,
|
|
|
|
|
- Modal,
|
|
|
|
|
- Form,
|
|
|
|
|
- Popover,
|
|
|
|
|
- List,
|
|
|
|
|
- Avatar,
|
|
|
|
|
- Radio,
|
|
|
|
|
- Checkbox,
|
|
|
|
|
- } from 'ant-design-vue';
|
|
|
|
|
|
|
+ import { Button, DatePicker, Input, Select, Modal, Form, Popover, Radio } from 'ant-design-vue';
|
|
|
import {
|
|
import {
|
|
|
SearchOutlined,
|
|
SearchOutlined,
|
|
|
PlusOutlined,
|
|
PlusOutlined,
|
|
@@ -1035,9 +812,9 @@
|
|
|
DEFAULT_COMPARE_OPERATOR,
|
|
DEFAULT_COMPARE_OPERATOR,
|
|
|
normalizeCompareOperator,
|
|
normalizeCompareOperator,
|
|
|
} from '@/core/utils/compareOperator';
|
|
} from '@/core/utils/compareOperator';
|
|
|
- import { maskCertNo } from '@/core/utils/sensitive';
|
|
|
|
|
import { EmptyState } from '@/core/components/EmptyState';
|
|
import { EmptyState } from '@/core/components/EmptyState';
|
|
|
import { PersonAvatar } from '@/core/components/PersonAvatar';
|
|
import { PersonAvatar } from '@/core/components/PersonAvatar';
|
|
|
|
|
+ import PersonGroupManageModal from '@/person/components/PersonGroupManageModal.vue';
|
|
|
import QueryTableModal from './components/QueryTableModal.vue';
|
|
import QueryTableModal from './components/QueryTableModal.vue';
|
|
|
import IntimacyPanel from './components/IntimacyPanel.vue';
|
|
import IntimacyPanel from './components/IntimacyPanel.vue';
|
|
|
import StationMapModal from './components/StationMapModal.vue';
|
|
import StationMapModal from './components/StationMapModal.vue';
|
|
@@ -1051,13 +828,6 @@
|
|
|
createPersonBasicInfo,
|
|
createPersonBasicInfo,
|
|
|
updatePersonBasicInfo,
|
|
updatePersonBasicInfo,
|
|
|
deletePersonBasicInfo,
|
|
deletePersonBasicInfo,
|
|
|
- listPersonGroups,
|
|
|
|
|
- createPersonGroup,
|
|
|
|
|
- updatePersonGroup,
|
|
|
|
|
- deletePersonGroup,
|
|
|
|
|
- addPersonToGroup,
|
|
|
|
|
- remPersonFromGroup,
|
|
|
|
|
- changeMainPerson,
|
|
|
|
|
} from '../../../person/api/personApi';
|
|
} from '../../../person/api/personApi';
|
|
|
import type {
|
|
import type {
|
|
|
PersonBasicInfo,
|
|
PersonBasicInfo,
|
|
@@ -1125,14 +895,8 @@
|
|
|
const transRecordModalZIndex = 1210;
|
|
const transRecordModalZIndex = 1210;
|
|
|
const stationMapModalVisible = ref(false);
|
|
const stationMapModalVisible = ref(false);
|
|
|
const groupModalVisible = ref(false);
|
|
const groupModalVisible = ref(false);
|
|
|
- const createGroupModalVisible = ref(false);
|
|
|
|
|
- const creatingGroup = ref(false);
|
|
|
|
|
- const isEditGroupMode = ref(false);
|
|
|
|
|
- const editingGroupData = ref<{ id: string; name: string; remark?: string } | null>(null);
|
|
|
|
|
|
|
+ const groupManageModalRef = ref<InstanceType<typeof PersonGroupManageModal> | null>(null);
|
|
|
const isEditMode = ref(false);
|
|
const isEditMode = ref(false);
|
|
|
- const objectGroups = ref<{ id: string; name: string; remark?: string }[]>([]);
|
|
|
|
|
- const activeGroupId = ref<string | null>(null);
|
|
|
|
|
- const groupMembers = ref<{ id: string; groupId: string; personName: string; hasMain?: boolean }[]>([]);
|
|
|
|
|
const saving = ref(false);
|
|
const saving = ref(false);
|
|
|
const editingPerson = ref<PersonBasicInfo | null>(null);
|
|
const editingPerson = ref<PersonBasicInfo | null>(null);
|
|
|
const loadingProfile = ref(false);
|
|
const loadingProfile = ref(false);
|
|
@@ -1225,18 +989,6 @@
|
|
|
const phoneListErrors = ref<string[]>([]);
|
|
const phoneListErrors = ref<string[]>([]);
|
|
|
const cardListErrors = ref<string[]>([]);
|
|
const cardListErrors = ref<string[]>([]);
|
|
|
const personNameError = ref('');
|
|
const personNameError = ref('');
|
|
|
- const groupNameError = ref('');
|
|
|
|
|
- const mainPersonError = ref('');
|
|
|
|
|
-
|
|
|
|
|
- const createGroupForm = reactive({
|
|
|
|
|
- groupName: '',
|
|
|
|
|
- mainPerson: undefined as string | undefined,
|
|
|
|
|
- remark: '',
|
|
|
|
|
- });
|
|
|
|
|
- const addObjectModalVisible = ref(false);
|
|
|
|
|
- const addingObjectToGroup = ref(false);
|
|
|
|
|
- const addObjectKeyword = ref('');
|
|
|
|
|
- const selectedAddPersonNames = ref<string[]>([]);
|
|
|
|
|
|
|
|
|
|
const createCallSummaryQuery = () => ({
|
|
const createCallSummaryQuery = () => ({
|
|
|
keyword: '',
|
|
keyword: '',
|
|
@@ -2185,53 +1937,27 @@
|
|
|
personModalVisible.value = false;
|
|
personModalVisible.value = false;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const loadGroupData = async () => {
|
|
|
|
|
- try {
|
|
|
|
|
- const groups = await listPersonGroups();
|
|
|
|
|
- objectGroups.value = (groups || []).map((item) => ({
|
|
|
|
|
- id: item.id || '',
|
|
|
|
|
- name: item.groupName || `分组${item.id}`,
|
|
|
|
|
- remark: item.remark,
|
|
|
|
|
- }));
|
|
|
|
|
- if (objectGroups.value.length) {
|
|
|
|
|
- const currentExists = objectGroups.value.some((item) => item.id === activeGroupId.value);
|
|
|
|
|
- activeGroupId.value = currentExists ? activeGroupId.value : objectGroups.value[0].id;
|
|
|
|
|
- } else {
|
|
|
|
|
- activeGroupId.value = null;
|
|
|
|
|
- }
|
|
|
|
|
- groupMembers.value = (groups || []).flatMap((group) =>
|
|
|
|
|
- (group.personList || []).map((rel) => ({
|
|
|
|
|
- id: rel.id || '',
|
|
|
|
|
- groupId: rel.groupId || group.id || '',
|
|
|
|
|
- personName: rel.personName || '-',
|
|
|
|
|
- hasMain: rel.hasMain,
|
|
|
|
|
- })),
|
|
|
|
|
- );
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error('获取分组列表失败', error);
|
|
|
|
|
- createMessage.error('获取分组列表失败');
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const openGroupModal = async () => {
|
|
|
|
|
|
|
+ const openGroupModal = () => {
|
|
|
groupModalVisible.value = true;
|
|
groupModalVisible.value = true;
|
|
|
- await loadGroupData();
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const openGroupModalFromRoute = async () => {
|
|
const openGroupModalFromRoute = async () => {
|
|
|
if (route.query.openGroupModal !== '1') return;
|
|
if (route.query.openGroupModal !== '1') return;
|
|
|
- await openGroupModal();
|
|
|
|
|
|
|
+ groupModalVisible.value = true;
|
|
|
if (route.query.openCreateGroupModal === '1') {
|
|
if (route.query.openCreateGroupModal === '1') {
|
|
|
await nextTick();
|
|
await nextTick();
|
|
|
- openCreateGroupModal();
|
|
|
|
|
|
|
+ groupManageModalRef.value?.startCreateGroup();
|
|
|
}
|
|
}
|
|
|
const { openGroupModal: _openGroupModal, openCreateGroupModal: _openCreateGroupModal, ...query } = route.query;
|
|
const { openGroupModal: _openGroupModal, openCreateGroupModal: _openCreateGroupModal, ...query } = route.query;
|
|
|
await router.replace({ path: route.path, query });
|
|
await router.replace({ path: route.path, query });
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const closeGroupModal = () => {
|
|
|
|
|
- groupModalVisible.value = false;
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ watch(
|
|
|
|
|
+ () => [route.query.openGroupModal, route.query.openCreateGroupModal],
|
|
|
|
|
+ () => {
|
|
|
|
|
+ void openGroupModalFromRoute();
|
|
|
|
|
+ },
|
|
|
|
|
+ );
|
|
|
|
|
|
|
|
const resolvePageTotal = (resp: any, records: unknown[] = []) => {
|
|
const resolvePageTotal = (resp: any, records: unknown[] = []) => {
|
|
|
const total = Number(resp?.total ?? resp?.count ?? resp?.page?.total ?? resp?.page?.count);
|
|
const total = Number(resp?.total ?? resp?.count ?? resp?.page?.total ?? resp?.page?.count);
|
|
@@ -4825,327 +4551,6 @@
|
|
|
.filter(Boolean)
|
|
.filter(Boolean)
|
|
|
.slice(0, stationDisplayLimit);
|
|
.slice(0, stationDisplayLimit);
|
|
|
});
|
|
});
|
|
|
-
|
|
|
|
|
- const activeGroupMembers = computed(() => {
|
|
|
|
|
- return groupMembers.value.filter((item) => item.groupId === activeGroupId.value);
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- const activeGroup = computed(() => {
|
|
|
|
|
- return objectGroups.value.find((item) => item.id === activeGroupId.value) || null;
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- const groupedPersonNameSet = computed(() => {
|
|
|
|
|
- return new Set(
|
|
|
|
|
- groupMembers.value
|
|
|
|
|
- .map((item) => item.personName?.trim())
|
|
|
|
|
- .filter((item): item is string => !!item && item !== '-'),
|
|
|
|
|
- );
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- const personOptions = computed(() => {
|
|
|
|
|
- const currentMainPerson = createGroupForm.mainPerson?.trim() || '';
|
|
|
|
|
- return personList.value
|
|
|
|
|
- .map((item) => {
|
|
|
|
|
- const personName = item.personName?.trim() || '';
|
|
|
|
|
- return { label: personName || '-', value: personName };
|
|
|
|
|
- })
|
|
|
|
|
- .filter((item) => !!item.value)
|
|
|
|
|
- .filter((item) => {
|
|
|
|
|
- if (isEditGroupMode.value && currentMainPerson && item.value === currentMainPerson) {
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
- return !groupedPersonNameSet.value.has(item.value);
|
|
|
|
|
- })
|
|
|
|
|
- .filter((item) => !!item.value);
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- const addObjectCandidates = computed(() => {
|
|
|
|
|
- return personList.value
|
|
|
|
|
- .map((item, index) => {
|
|
|
|
|
- const name = item.personName?.trim() || '';
|
|
|
|
|
- const phoneValues = (item.phoneList || []).map((phone) => phone.personNo || '').filter(Boolean);
|
|
|
|
|
- const cardCount = (item.cardList || []).filter((card) => !!card?.personNo).length;
|
|
|
|
|
- return {
|
|
|
|
|
- key: String(item.id ?? item.objId ?? name ?? index),
|
|
|
|
|
- name: name || `未命名对象${index + 1}`,
|
|
|
|
|
- personCertNo: item.personCertNo || '-',
|
|
|
|
|
- primaryPhone: phoneValues[0] || '-',
|
|
|
|
|
- cardCount,
|
|
|
|
|
- photograph: item.photograph || '',
|
|
|
|
|
- disabled: false,
|
|
|
|
|
- };
|
|
|
|
|
- })
|
|
|
|
|
- .filter((item) => !!item.name && !groupedPersonNameSet.value.has(item.name))
|
|
|
|
|
- .sort((a, b) => a.name.localeCompare(b.name, 'zh-CN'));
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- const filteredAddObjectCandidates = computed(() => {
|
|
|
|
|
- const keyword = addObjectKeyword.value.trim().toLocaleLowerCase();
|
|
|
|
|
- if (!keyword) return addObjectCandidates.value;
|
|
|
|
|
- return addObjectCandidates.value.filter((item) =>
|
|
|
|
|
- [item.name, item.personCertNo, item.primaryPhone].some((value) => value.toLocaleLowerCase().includes(keyword)),
|
|
|
|
|
- );
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- const addObjectEmptyText = computed(() => {
|
|
|
|
|
- if (!addObjectCandidates.value.length) {
|
|
|
|
|
- return '暂无未分组对象,对象限定在单一分组';
|
|
|
|
|
- }
|
|
|
|
|
- return '暂无匹配对象';
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- const normalizeGroupName = (value?: string | null) => value?.trim().toLocaleLowerCase() || '';
|
|
|
|
|
-
|
|
|
|
|
- const isDuplicateGroupName = (groupName: string) => {
|
|
|
|
|
- const normalizedName = normalizeGroupName(groupName);
|
|
|
|
|
- if (!normalizedName) return false;
|
|
|
|
|
- const editingId = editingGroupData.value?.id !== undefined ? String(editingGroupData.value.id) : '';
|
|
|
|
|
- return objectGroups.value.some((item) => {
|
|
|
|
|
- const groupId = item.id !== undefined && item.id !== null ? String(item.id) : '';
|
|
|
|
|
- if (editingId && groupId === editingId) return false;
|
|
|
|
|
- return normalizeGroupName(item.name) === normalizedName;
|
|
|
|
|
- });
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const validateGroupName = (showRequired = false) => {
|
|
|
|
|
- const groupName = createGroupForm.groupName.trim();
|
|
|
|
|
- if (!groupName) {
|
|
|
|
|
- groupNameError.value = showRequired ? '请输入分组名称' : '';
|
|
|
|
|
- return !showRequired;
|
|
|
|
|
- }
|
|
|
|
|
- if (isDuplicateGroupName(groupName)) {
|
|
|
|
|
- groupNameError.value = '分组名称已存在,请重新输入';
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- groupNameError.value = '';
|
|
|
|
|
- return true;
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const validateMainPerson = (showRequired = false) => {
|
|
|
|
|
- if (isEditGroupMode.value) {
|
|
|
|
|
- mainPersonError.value = '';
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
- if (!createGroupForm.mainPerson) {
|
|
|
|
|
- mainPersonError.value = showRequired ? '请选择分组主体对象' : '';
|
|
|
|
|
- return !showRequired;
|
|
|
|
|
- }
|
|
|
|
|
- mainPersonError.value = '';
|
|
|
|
|
- return true;
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- watch(
|
|
|
|
|
- () => createGroupForm.groupName,
|
|
|
|
|
- () => {
|
|
|
|
|
- if (!createGroupModalVisible.value) return;
|
|
|
|
|
- if (!createGroupForm.groupName.trim() && !groupNameError.value) return;
|
|
|
|
|
- validateGroupName(Boolean(createGroupForm.groupName.trim()));
|
|
|
|
|
- },
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- watch(
|
|
|
|
|
- () => createGroupForm.mainPerson,
|
|
|
|
|
- () => {
|
|
|
|
|
- if (!createGroupModalVisible.value) return;
|
|
|
|
|
- if (!createGroupForm.mainPerson && !mainPersonError.value) return;
|
|
|
|
|
- validateMainPerson(Boolean(createGroupForm.mainPerson));
|
|
|
|
|
- },
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- const openCreateGroupModal = () => {
|
|
|
|
|
- isEditGroupMode.value = false;
|
|
|
|
|
- editingGroupData.value = null;
|
|
|
|
|
- createGroupForm.groupName = '';
|
|
|
|
|
- createGroupForm.mainPerson = undefined;
|
|
|
|
|
- createGroupForm.remark = '';
|
|
|
|
|
- groupNameError.value = '';
|
|
|
|
|
- mainPersonError.value = '';
|
|
|
|
|
- createGroupModalVisible.value = true;
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const closeCreateGroupModal = () => {
|
|
|
|
|
- createGroupModalVisible.value = false;
|
|
|
|
|
- isEditGroupMode.value = false;
|
|
|
|
|
- editingGroupData.value = null;
|
|
|
|
|
- groupNameError.value = '';
|
|
|
|
|
- mainPersonError.value = '';
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const closeAddObjectModal = () => {
|
|
|
|
|
- addObjectModalVisible.value = false;
|
|
|
|
|
- addObjectKeyword.value = '';
|
|
|
|
|
- selectedAddPersonNames.value = [];
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const handleCreateGroup = async () => {
|
|
|
|
|
- const groupName = createGroupForm.groupName.trim();
|
|
|
|
|
- if (!validateGroupName(true)) {
|
|
|
|
|
- createMessage.warning(groupNameError.value || '请输入分组名称');
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (!validateMainPerson(true)) {
|
|
|
|
|
- createMessage.warning(mainPersonError.value || '请选择分组主体对象');
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- creatingGroup.value = true;
|
|
|
|
|
- try {
|
|
|
|
|
- if (isEditGroupMode.value && editingGroupData.value) {
|
|
|
|
|
- await updatePersonGroup({
|
|
|
|
|
- id: editingGroupData.value.id,
|
|
|
|
|
- groupName,
|
|
|
|
|
- remark: createGroupForm.remark || undefined,
|
|
|
|
|
- });
|
|
|
|
|
- createMessage.success('更新分组成功');
|
|
|
|
|
- } else {
|
|
|
|
|
- await createPersonGroup({
|
|
|
|
|
- groupName,
|
|
|
|
|
- remark: createGroupForm.remark || undefined,
|
|
|
|
|
- personList: createGroupForm.mainPerson
|
|
|
|
|
- ? [
|
|
|
|
|
- {
|
|
|
|
|
- personName: createGroupForm.mainPerson,
|
|
|
|
|
- hasMain: true,
|
|
|
|
|
- },
|
|
|
|
|
- ]
|
|
|
|
|
- : undefined,
|
|
|
|
|
- });
|
|
|
|
|
- createMessage.success('新增分组成功');
|
|
|
|
|
- }
|
|
|
|
|
- createGroupModalVisible.value = false;
|
|
|
|
|
- isEditGroupMode.value = false;
|
|
|
|
|
- editingGroupData.value = null;
|
|
|
|
|
- groupNameError.value = '';
|
|
|
|
|
- mainPersonError.value = '';
|
|
|
|
|
- await loadGroupData();
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error(isEditGroupMode.value ? '更新分组失败' : '新增分组失败', error);
|
|
|
|
|
- createMessage.error(isEditGroupMode.value ? '更新分组失败' : '新增分组失败');
|
|
|
|
|
- } finally {
|
|
|
|
|
- creatingGroup.value = false;
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const handleEditGroup = () => {
|
|
|
|
|
- if (!activeGroupId.value) {
|
|
|
|
|
- createMessage.warning('请先选择分组');
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- const current = objectGroups.value.find((item) => item.id === activeGroupId.value);
|
|
|
|
|
- if (!current) return;
|
|
|
|
|
- const mainMember = activeGroupMembers.value.find(
|
|
|
|
|
- (item) => item.hasMain && item.personName && item.personName !== '-',
|
|
|
|
|
- );
|
|
|
|
|
- editingGroupData.value = { id: current.id, name: current.name, remark: current.remark };
|
|
|
|
|
- isEditGroupMode.value = true;
|
|
|
|
|
- createGroupForm.groupName = current.name;
|
|
|
|
|
- createGroupForm.mainPerson = mainMember?.personName || undefined;
|
|
|
|
|
- createGroupForm.remark = current.remark || '';
|
|
|
|
|
- groupNameError.value = '';
|
|
|
|
|
- mainPersonError.value = '';
|
|
|
|
|
- createGroupModalVisible.value = true;
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const handleDeleteGroup = () => {
|
|
|
|
|
- if (!activeGroupId.value) {
|
|
|
|
|
- createMessage.warning('请先选择分组');
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- const current = objectGroups.value.find((item) => item.id === activeGroupId.value);
|
|
|
|
|
- createConfirm({
|
|
|
|
|
- iconType: 'warning',
|
|
|
|
|
- centered: true,
|
|
|
|
|
- title: '确认删除分组',
|
|
|
|
|
- content: `确定要删除分组“${current?.name || ''}”吗?删除后无法恢复。`,
|
|
|
|
|
- okButtonProps: { type: 'primary' },
|
|
|
|
|
- autoFocusButton: 'ok',
|
|
|
|
|
- async onOk() {
|
|
|
|
|
- try {
|
|
|
|
|
- await deletePersonGroup(activeGroupId.value!);
|
|
|
|
|
- createMessage.success('删除分组成功');
|
|
|
|
|
- await loadGroupData();
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error('删除分组失败', error);
|
|
|
|
|
- createMessage.error('删除分组失败');
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- });
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const handleAddObject = () => {
|
|
|
|
|
- if (!activeGroupId.value) {
|
|
|
|
|
- createMessage.warning('请先选择分组');
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- addObjectKeyword.value = '';
|
|
|
|
|
- selectedAddPersonNames.value = [];
|
|
|
|
|
- addObjectModalVisible.value = true;
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const handleCheckAddObject = (item: { name: string; disabled: boolean }, checked: boolean) => {
|
|
|
|
|
- if (item.disabled) return;
|
|
|
|
|
- if (checked) {
|
|
|
|
|
- selectedAddPersonNames.value = Array.from(new Set([...selectedAddPersonNames.value, item.name]));
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- selectedAddPersonNames.value = selectedAddPersonNames.value.filter((name) => name !== item.name);
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const handleToggleAddObject = (item: { name: string; disabled: boolean }) => {
|
|
|
|
|
- if (item.disabled) return;
|
|
|
|
|
- const checked = !selectedAddPersonNames.value.includes(item.name);
|
|
|
|
|
- handleCheckAddObject(item, checked);
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const handleConfirmAddObject = async () => {
|
|
|
|
|
- if (!activeGroupId.value) {
|
|
|
|
|
- createMessage.warning('请先选择分组');
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (!selectedAddPersonNames.value.length) {
|
|
|
|
|
- createMessage.warning('请选择要添加的对象');
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- addingObjectToGroup.value = true;
|
|
|
|
|
- try {
|
|
|
|
|
- await addPersonToGroup({
|
|
|
|
|
- groupId: activeGroupId.value,
|
|
|
|
|
- personList: selectedAddPersonNames.value,
|
|
|
|
|
- });
|
|
|
|
|
- createMessage.success('添加对象成功');
|
|
|
|
|
- closeAddObjectModal();
|
|
|
|
|
- await loadGroupData();
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error('添加对象失败', error);
|
|
|
|
|
- createMessage.error('添加对象失败');
|
|
|
|
|
- } finally {
|
|
|
|
|
- addingObjectToGroup.value = false;
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const handleRemoveMember = async (memberId: string) => {
|
|
|
|
|
- try {
|
|
|
|
|
- await remPersonFromGroup(memberId);
|
|
|
|
|
- createMessage.success('移出分组成功');
|
|
|
|
|
- await loadGroupData();
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error('移出分组失败', error);
|
|
|
|
|
- createMessage.error('移出分组失败');
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const handleChangeMain = async (relId: string) => {
|
|
|
|
|
- if (!activeGroupId.value) return;
|
|
|
|
|
- try {
|
|
|
|
|
- await changeMainPerson(relId, activeGroupId.value);
|
|
|
|
|
- createMessage.success('变更主要人员成功');
|
|
|
|
|
- await loadGroupData();
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error('变更主要人员失败', error);
|
|
|
|
|
- createMessage.error('变更主要人员失败');
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="less">
|
|
<style lang="less">
|
|
@@ -9565,7 +8970,7 @@
|
|
|
弹窗被 teleport 到 body,用本页挂载时加的 body 标记限定作用域,避免影响其他页面弹窗。
|
|
弹窗被 teleport 到 body,用本页挂载时加的 body 标记限定作用域,避免影响其他页面弹窗。
|
|
|
需要 !important:全局弹窗页脚样式(core/design/ant/index.less)把页脚按钮定成了 36px,
|
|
需要 !important:全局弹窗页脚样式(core/design/ant/index.less)把页脚按钮定成了 36px,
|
|
|
选择器层级更高,普通覆盖不生效。 */
|
|
选择器层级更高,普通覆盖不生效。 */
|
|
|
- body.case-portrait-active .ant-modal-wrap .ant-btn {
|
|
|
|
|
|
|
+ body.case-portrait-active .ant-modal-wrap:not(.pg-manage-modal) .ant-btn {
|
|
|
height: 32px !important;
|
|
height: 32px !important;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -9577,20 +8982,20 @@
|
|
|
/* 数据画像页弹窗主按钮:统一使用项目主色 #2a50ec。
|
|
/* 数据画像页弹窗主按钮:统一使用项目主色 #2a50ec。
|
|
|
组件内部(ModalFooter / QueryTableModal 等)对主按钮有更高优先级的历史配色覆盖,
|
|
组件内部(ModalFooter / QueryTableModal 等)对主按钮有更高优先级的历史配色覆盖,
|
|
|
这里用 !important 统一成本页要求的项目主色。 */
|
|
这里用 !important 统一成本页要求的项目主色。 */
|
|
|
- body.case-portrait-active .ant-modal-wrap .ant-btn-primary {
|
|
|
|
|
|
|
+ body.case-portrait-active .ant-modal-wrap:not(.pg-manage-modal) .ant-btn-primary {
|
|
|
border-color: #2a50ec !important;
|
|
border-color: #2a50ec !important;
|
|
|
background: #2a50ec !important;
|
|
background: #2a50ec !important;
|
|
|
color: #fff;
|
|
color: #fff;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- body.case-portrait-active .ant-modal-wrap .ant-btn-primary:not(:disabled):hover,
|
|
|
|
|
- body.case-portrait-active .ant-modal-wrap .ant-btn-primary:not(:disabled):focus {
|
|
|
|
|
|
|
+ body.case-portrait-active .ant-modal-wrap:not(.pg-manage-modal) .ant-btn-primary:not(:disabled):hover,
|
|
|
|
|
+ body.case-portrait-active .ant-modal-wrap:not(.pg-manage-modal) .ant-btn-primary:not(:disabled):focus {
|
|
|
border-color: #4b6bf0 !important;
|
|
border-color: #4b6bf0 !important;
|
|
|
background: #4b6bf0 !important;
|
|
background: #4b6bf0 !important;
|
|
|
color: #fff;
|
|
color: #fff;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- body.case-portrait-active .ant-modal-wrap .ant-btn-primary:not(:disabled):active {
|
|
|
|
|
|
|
+ body.case-portrait-active .ant-modal-wrap:not(.pg-manage-modal) .ant-btn-primary:not(:disabled):active {
|
|
|
border-color: #1e3ed0 !important;
|
|
border-color: #1e3ed0 !important;
|
|
|
background: #1e3ed0 !important;
|
|
background: #1e3ed0 !important;
|
|
|
color: #fff;
|
|
color: #fff;
|