---初始化后台管理web页面项目
This commit is contained in:
236
src/views/erp/customer/Collection.vue
Normal file
236
src/views/erp/customer/Collection.vue
Normal file
@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<PageWrapper dense fixedHeight contentFullHeight>
|
||||
<CollectionInfo :id="recordId" @return-page="returnPage" v-if="isShowPage" />
|
||||
<BasicTableErp @register="registerTable" v-else>
|
||||
<template #toolbar>
|
||||
<a-button type="primary" @click="handleCreate"> {{ t('新增') }} </a-button>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex == 'overDay'">
|
||||
<span :style="{ color: `${Number(record.overDay) > 0 ? '#F56C6C' : ''}` }">
|
||||
{{ record.overDay }}
|
||||
</span>
|
||||
</template>
|
||||
<template v-if="column.dataIndex == 'state'">
|
||||
<a-tag :color="getState(record.state, false)">
|
||||
{{ getState(record.state, true) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-if="column.dataIndex == 'action'">
|
||||
<a-button type="link" class="actionTxt" @click="showPage(record)">回款记录</a-button>
|
||||
<a-button type="link" class="actionTxt" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button type="link" class="actionTxt" @click="handleDelete(record)">删除</a-button>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTableErp>
|
||||
<CollectionModal @register="registerModal" @success="reload" />
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { createVNode, ref } from 'vue';
|
||||
import { useTable, BasicColumn, FormSchema } from '/@/components/Table';
|
||||
import BasicTableErp from '/@/components/Table/src/BasicTableErp.vue';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { PageWrapper } from '/@/components/Page';
|
||||
import { getCollectionPageList, deleteCollection } from '/@/api/erp/customer/collection';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import CollectionModal from './components/CollectionModal.vue';
|
||||
import CollectionInfo from './components/CollectionInfo.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '客户名称',
|
||||
dataIndex: 'name',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '计划回款金额',
|
||||
dataIndex: 'waitAmount',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '已回款金额',
|
||||
dataIndex: 'alreadyAmount',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '未回款金额',
|
||||
dataIndex: 'unpaidAmount',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '计划回款日期',
|
||||
dataIndex: 'receivedDate',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '最迟回款日期',
|
||||
dataIndex: 'finallyDate',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '逾期天数',
|
||||
dataIndex: 'overDay',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '合同标题',
|
||||
dataIndex: 'title',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '合同负责人',
|
||||
dataIndex: 'principalNames',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '创建日期',
|
||||
dataIndex: 'createDate',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
dataIndex: 'createUserName',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
width: 80,
|
||||
},
|
||||
];
|
||||
|
||||
const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'keyword',
|
||||
label: '关键字',
|
||||
component: 'Input',
|
||||
colProps: { span: 8 },
|
||||
componentProps: {
|
||||
placeholder: '请输入关键字',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'receivedDate',
|
||||
label: '计划回款时间',
|
||||
component: 'RangePicker',
|
||||
colProps: { span: 8 },
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD',
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'finallyDate',
|
||||
label: '最迟回款时间',
|
||||
component: 'RangePicker',
|
||||
colProps: { span: 8 },
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD',
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const { notification } = useMessage();
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
|
||||
const recordId = ref('');
|
||||
const isShowPage = ref<boolean>(false);
|
||||
|
||||
const [registerTable, { reload }] = useTable({
|
||||
title: '回款列表',
|
||||
api: getCollectionPageList,
|
||||
rowKey: 'id',
|
||||
columns,
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: searchFormSchema,
|
||||
fieldMapToTime: [
|
||||
['receivedDate', ['receivedStartTime', 'receivedEndTime'], 'YYYY-MM-DD', true],
|
||||
['finallyDate', ['finallyStartTime', 'finallyEndTime'], 'YYYY-MM-DD', true],
|
||||
],
|
||||
},
|
||||
beforeFetch: (params) => {
|
||||
return { ...params, state: 0 };
|
||||
},
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
striped: false,
|
||||
actionColumn: {
|
||||
width: 150,
|
||||
title: t('操作'),
|
||||
dataIndex: 'action',
|
||||
},
|
||||
});
|
||||
|
||||
const getState = (state, isText) => {
|
||||
switch (state) {
|
||||
case -1:
|
||||
return isText ? '未开始' : 'error';
|
||||
case 0:
|
||||
return isText ? '进行中' : 'processing';
|
||||
case 1:
|
||||
return isText ? '已完成' : 'success';
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreate = () => {
|
||||
openModal(true, {
|
||||
state: 0,
|
||||
isUpdate: false,
|
||||
});
|
||||
};
|
||||
|
||||
const handleEdit = (record) => {
|
||||
openModal(true, {
|
||||
state: 0,
|
||||
id: record.id,
|
||||
isUpdate: true,
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record) => {
|
||||
Modal.confirm({
|
||||
title: t('提示信息'),
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
content: t('是否确认删除?'),
|
||||
okText: t('确认'),
|
||||
cancelText: t('取消'),
|
||||
async onOk() {
|
||||
await deleteCollection(record.id);
|
||||
notification.success({
|
||||
message: t('提示'),
|
||||
description: t('删除成功'),
|
||||
});
|
||||
reload();
|
||||
},
|
||||
onCancel() {},
|
||||
});
|
||||
};
|
||||
|
||||
const showPage = (record) => {
|
||||
isShowPage.value = true;
|
||||
recordId.value = record.id;
|
||||
};
|
||||
|
||||
const returnPage = () => {
|
||||
isShowPage.value = false;
|
||||
reload();
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.actionTxt {
|
||||
padding: 4px 2px;
|
||||
|
||||
&:last-child {
|
||||
color: #f56c6c;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
248
src/views/erp/customer/Common.vue
Normal file
248
src/views/erp/customer/Common.vue
Normal file
@ -0,0 +1,248 @@
|
||||
<template>
|
||||
<PageWrapper dense fixedHeight contentFullHeight>
|
||||
<CustomerInfo :id="recordId" @return-page="returnPage" :isCommon="true" v-if="isShowPage" />
|
||||
<BasicTableErp @register="registerTable" v-else>
|
||||
<template #toolbar>
|
||||
<a-button type="primary" @click="handleCreate"> {{ t('新增') }} </a-button>
|
||||
<a-button type="primary" @click="handleImport"> {{ t('导入') }} </a-button>
|
||||
<a-button type="primary" @click="handleExport"> {{ t('导出') }} </a-button>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex == 'action'">
|
||||
<a-button type="link" class="actionTxt" @click="handleCustomer(record)">领取</a-button>
|
||||
<a-button type="link" class="actionTxt" @click="showPage(record)"> 详情 </a-button>
|
||||
<a-button type="link" class="actionTxt" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button type="link" class="actionTxt" @click="handleDelete(record)">删除</a-button>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTableErp>
|
||||
<CustomerModal @register="registerModal" @success="reload" />
|
||||
<ImportModal
|
||||
@register="registerImportModal"
|
||||
importUrl="/caseErpCustomer/caseErpCustomer/import-common"
|
||||
@success="reload"
|
||||
/>
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { createVNode, ref } from 'vue';
|
||||
import { useTable, BasicColumn, FormSchema } from '/@/components/Table';
|
||||
import BasicTableErp from '/@/components/Table/src/BasicTableErp.vue';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { PageWrapper } from '/@/components/Page';
|
||||
import { getCustomerPageList, deleteCustomer } from '/@/api/erp/customer/list';
|
||||
import { getFromCommon, exportInfo, downloadTemplate } from '/@/api/erp/customer/common';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { ImportModal } from '/@/components/Import';
|
||||
import { downloadByData } from '/@/utils/file/download';
|
||||
import CustomerModal from './components/CustomerModal.vue';
|
||||
import CustomerInfo from './components/CustomerInfo.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '客户名称',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: '客户类型',
|
||||
dataIndex: 'typeName',
|
||||
},
|
||||
{
|
||||
title: '联系人',
|
||||
dataIndex: 'defaultName',
|
||||
},
|
||||
{
|
||||
title: '手机号码',
|
||||
dataIndex: 'defaultPhone',
|
||||
},
|
||||
{
|
||||
title: '所在行业',
|
||||
dataIndex: 'industry',
|
||||
},
|
||||
{
|
||||
title: '来源',
|
||||
dataIndex: 'sourceName',
|
||||
},
|
||||
{
|
||||
title: '加入公海日期',
|
||||
dataIndex: 'inOpenDate',
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
dataIndex: 'createUserName',
|
||||
},
|
||||
];
|
||||
|
||||
const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'typeId',
|
||||
label: '客户类型',
|
||||
component: 'XjrSelect',
|
||||
colProps: { span: 8 },
|
||||
componentProps: {
|
||||
datasourceType: 'dic',
|
||||
params: { itemId: '1679007059387240450' },
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
placeholder: '请选择客户类型',
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'createDate',
|
||||
label: '加入时间',
|
||||
component: 'RangePicker',
|
||||
colProps: { span: 8 },
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD',
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'sourceId',
|
||||
label: '来源',
|
||||
component: 'XjrSelect',
|
||||
colProps: { span: 8 },
|
||||
componentProps: {
|
||||
datasourceType: 'dic',
|
||||
params: { itemId: '1679008505423876097' },
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
placeholder: '请选择来源',
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '客户名称',
|
||||
component: 'Input',
|
||||
colProps: { span: 8 },
|
||||
componentProps: {
|
||||
placeholder: '请输入客户名称',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const { notification } = useMessage();
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
|
||||
const [registerImportModal, { openModal: openImportModal }] = useModal();
|
||||
|
||||
const recordId = ref('');
|
||||
const isShowPage = ref<boolean>(false);
|
||||
const [registerTable, { reload }] = useTable({
|
||||
title: '客户公海',
|
||||
api: getCustomerPageList,
|
||||
rowKey: 'id',
|
||||
columns,
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: searchFormSchema,
|
||||
fieldMapToTime: [['createDate', ['startTime', 'endTime'], 'YYYY-MM-DD', true]],
|
||||
},
|
||||
beforeFetch: (params) => {
|
||||
return { ...params, state: 1 };
|
||||
},
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
striped: false,
|
||||
actionColumn: {
|
||||
width: 160,
|
||||
title: t('操作'),
|
||||
dataIndex: 'action',
|
||||
},
|
||||
});
|
||||
|
||||
const handleCreate = () => {
|
||||
openModal(true, {
|
||||
state: 1,
|
||||
isUpdate: false,
|
||||
});
|
||||
};
|
||||
|
||||
const handleEdit = (record) => {
|
||||
openModal(true, {
|
||||
state: 1,
|
||||
id: record.id,
|
||||
isUpdate: true,
|
||||
});
|
||||
};
|
||||
|
||||
const handleCustomer = (record) => {
|
||||
Modal.confirm({
|
||||
title: t('提示信息'),
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
content: '确认也要领取该客户吗?',
|
||||
okText: t('确认'),
|
||||
cancelText: t('取消'),
|
||||
async onOk() {
|
||||
await getFromCommon(record.id);
|
||||
notification.success({
|
||||
message: t('提示'),
|
||||
description: '领取成功',
|
||||
});
|
||||
reload();
|
||||
},
|
||||
onCancel() {},
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record) => {
|
||||
Modal.confirm({
|
||||
title: t('提示信息'),
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
content: t('是否确认删除?'),
|
||||
okText: t('确认'),
|
||||
cancelText: t('取消'),
|
||||
async onOk() {
|
||||
await deleteCustomer(record.id);
|
||||
notification.success({
|
||||
message: t('提示'),
|
||||
description: t('删除成功'),
|
||||
});
|
||||
reload();
|
||||
},
|
||||
onCancel() {},
|
||||
});
|
||||
};
|
||||
const handleImport = () => {
|
||||
openImportModal(true, {
|
||||
title: t('快速导入'),
|
||||
api: downloadTemplate,
|
||||
templateTitle: '客户公海模板',
|
||||
});
|
||||
};
|
||||
const handleExport = async () => {
|
||||
const res = await exportInfo();
|
||||
downloadByData(
|
||||
res.data,
|
||||
'客户公海.xlsx',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
);
|
||||
};
|
||||
|
||||
const showPage = (record) => {
|
||||
isShowPage.value = true;
|
||||
recordId.value = record.id;
|
||||
};
|
||||
|
||||
const returnPage = () => {
|
||||
isShowPage.value = false;
|
||||
reload();
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.actionTxt {
|
||||
padding: 4px 2px;
|
||||
|
||||
&:last-child {
|
||||
color: #f56c6c;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
318
src/views/erp/customer/List.vue
Normal file
318
src/views/erp/customer/List.vue
Normal file
@ -0,0 +1,318 @@
|
||||
<template>
|
||||
<PageWrapper dense fixedHeight contentFullHeight>
|
||||
<CustomerInfo :id="recordId" @return-page="returnPage" v-if="isShowPage" />
|
||||
<BasicTableErp @register="registerTable" v-else>
|
||||
<template #toolbar>
|
||||
<a-button type="primary" @click="handleCreate"> {{ t('新增') }} </a-button>
|
||||
<a-button type="primary" @click="handleImport"> {{ t('导入') }} </a-button>
|
||||
<a-button type="primary" @click="handleExport"> {{ t('导出') }} </a-button>
|
||||
<a-button type="primary" @click="handleTransfer"> 移入公海 </a-button>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex == 'action'">
|
||||
<a-button type="link" class="actionTxt" @click="handleFollow(record)">跟进</a-button>
|
||||
<a-button type="link" class="actionTxt" @click="showPage(record)"> 详情查看 </a-button>
|
||||
<a-button type="link" class="actionTxt" @click="handleEdit(record)">编辑</a-button>
|
||||
<SelectUser
|
||||
:selectedIds="userIds"
|
||||
:multiple="false"
|
||||
@change="(ids) => handleTransferUser(ids, record)"
|
||||
:style="{ display: 'inline' }"
|
||||
>
|
||||
<a-button type="link" class="actionTxt" :style="{ color: '#5e95ff' }">转移</a-button>
|
||||
</SelectUser>
|
||||
<a-button type="link" class="actionTxt" @click="handleDelete(record)">删除</a-button>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTableErp>
|
||||
<CustomerModal @register="registerModal" @success="reload" />
|
||||
<FollowModal @register="registerFollowModal" @success="reload" />
|
||||
<ImportModal
|
||||
@register="registerImportModal"
|
||||
importUrl="/caseErpCustomer/caseErpCustomer/import"
|
||||
@success="reload"
|
||||
/>
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { createVNode, ref } from 'vue';
|
||||
import { useTable, BasicColumn, FormSchema } from '/@/components/Table';
|
||||
import BasicTableErp from '/@/components/Table/src/BasicTableErp.vue';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { PageWrapper } from '/@/components/Page';
|
||||
import {
|
||||
getCustomerPageList,
|
||||
deleteCustomer,
|
||||
transferCommon,
|
||||
exportInfo,
|
||||
downloadTemplate,
|
||||
transferUser,
|
||||
} from '/@/api/erp/customer/list';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { ImportModal } from '/@/components/Import';
|
||||
import { downloadByData } from '/@/utils/file/download';
|
||||
import { SelectUser } from '/@/components/SelectOrganizational/index';
|
||||
import CustomerModal from './components/CustomerModal.vue';
|
||||
import CustomerInfo from './components/CustomerInfo.vue';
|
||||
import FollowModal from './components/FollowModal.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '客户名称',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: '客户类型',
|
||||
dataIndex: 'typeName',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '联系人',
|
||||
dataIndex: 'defaultName',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '手机号码',
|
||||
dataIndex: 'defaultPhone',
|
||||
},
|
||||
{
|
||||
title: '所在行业',
|
||||
dataIndex: 'industry',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '来源',
|
||||
dataIndex: 'sourceName',
|
||||
},
|
||||
{
|
||||
title: '归属',
|
||||
dataIndex: 'saleName',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '创建日期',
|
||||
dataIndex: 'createDate',
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
dataIndex: 'createUserName',
|
||||
width: 100,
|
||||
},
|
||||
];
|
||||
|
||||
const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
label: '客户名称',
|
||||
component: 'Input',
|
||||
colProps: { span: 8 },
|
||||
componentProps: {
|
||||
placeholder: '请输入客户名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'typeId',
|
||||
label: '客户类型',
|
||||
component: 'XjrSelect',
|
||||
colProps: { span: 8 },
|
||||
componentProps: {
|
||||
datasourceType: 'dic',
|
||||
params: { itemId: '1679007059387240450' },
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
placeholder: '请选择客户类型',
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'sourceId',
|
||||
label: '来源',
|
||||
component: 'XjrSelect',
|
||||
colProps: { span: 8 },
|
||||
componentProps: {
|
||||
datasourceType: 'dic',
|
||||
params: { itemId: '1679008505423876097' },
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
placeholder: '请选择来源',
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'createDate',
|
||||
label: '创建时间',
|
||||
component: 'RangePicker',
|
||||
colProps: { span: 8 },
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD',
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const { notification } = useMessage();
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
const [registerFollowModal, { openModal: openFollowModal }] = useModal();
|
||||
|
||||
const [registerImportModal, { openModal: openImportModal }] = useModal();
|
||||
|
||||
const recordId = ref('');
|
||||
const isShowPage = ref<boolean>(false);
|
||||
const userIds = ref([]);
|
||||
|
||||
const customRow = (record) => {
|
||||
return {
|
||||
onClick: () => {
|
||||
let selectedRowKeys = [...getSelectRowKeys()];
|
||||
if (selectedRowKeys.indexOf(record.id) >= 0) {
|
||||
let index = selectedRowKeys.indexOf(record.id);
|
||||
selectedRowKeys.splice(index, 1);
|
||||
} else {
|
||||
selectedRowKeys.push(record.id);
|
||||
}
|
||||
setSelectedRowKeys(selectedRowKeys);
|
||||
},
|
||||
};
|
||||
};
|
||||
const [registerTable, { reload, getSelectRowKeys, setSelectedRowKeys }] = useTable({
|
||||
title: '客户列表',
|
||||
api: getCustomerPageList,
|
||||
rowKey: 'id',
|
||||
columns,
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: searchFormSchema,
|
||||
fieldMapToTime: [['createDate', ['startTime', 'endTime'], 'YYYY-MM-DD', true]],
|
||||
},
|
||||
beforeFetch: (params) => {
|
||||
return { ...params, state: 0 };
|
||||
},
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
striped: false,
|
||||
actionColumn: {
|
||||
width: 220,
|
||||
title: t('操作'),
|
||||
dataIndex: 'action',
|
||||
},
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
},
|
||||
customRow,
|
||||
});
|
||||
|
||||
const handleCreate = () => {
|
||||
openModal(true, {
|
||||
state: 0,
|
||||
isUpdate: false,
|
||||
});
|
||||
};
|
||||
|
||||
const handleEdit = (record) => {
|
||||
openModal(true, {
|
||||
state: 0,
|
||||
id: record.id,
|
||||
isUpdate: true,
|
||||
});
|
||||
};
|
||||
|
||||
const handleFollow = (record) => {
|
||||
openFollowModal(true, {
|
||||
record,
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record) => {
|
||||
Modal.confirm({
|
||||
title: t('提示信息'),
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
content: t('是否确认删除?'),
|
||||
okText: t('确认'),
|
||||
cancelText: t('取消'),
|
||||
async onOk() {
|
||||
await deleteCustomer(record.id);
|
||||
notification.success({
|
||||
message: t('提示'),
|
||||
description: t('删除成功'),
|
||||
});
|
||||
reload();
|
||||
},
|
||||
onCancel() {},
|
||||
});
|
||||
};
|
||||
const handleImport = () => {
|
||||
openImportModal(true, {
|
||||
title: t('快速导入'),
|
||||
api: downloadTemplate,
|
||||
templateTitle: '客户列表模板',
|
||||
});
|
||||
};
|
||||
const handleExport = async () => {
|
||||
if (!getSelectRowKeys().length) {
|
||||
notification.warning({
|
||||
message: 'Tip',
|
||||
description: '请选择需要导出的数据',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
const res = await exportInfo(getSelectRowKeys());
|
||||
downloadByData(
|
||||
res.data,
|
||||
'客户列表.xlsx',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
);
|
||||
};
|
||||
|
||||
const handleTransfer = async () => {
|
||||
if (!getSelectRowKeys().length) {
|
||||
notification.warning({
|
||||
message: 'Tip',
|
||||
description: '请选择需要移入公海的数据',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
await transferCommon(getSelectRowKeys());
|
||||
notification.success({
|
||||
message: t('提示'),
|
||||
description: '移入公海',
|
||||
});
|
||||
reload();
|
||||
};
|
||||
|
||||
const handleTransferUser = async (ids, record) => {
|
||||
const params = {
|
||||
id: record.id,
|
||||
saleIds: ids.length ? ids[0] : '',
|
||||
};
|
||||
await transferUser(params);
|
||||
notification.success({
|
||||
message: t('提示'),
|
||||
description: '转移成功',
|
||||
});
|
||||
reload();
|
||||
};
|
||||
const showPage = (record) => {
|
||||
isShowPage.value = true;
|
||||
recordId.value = record.id;
|
||||
};
|
||||
|
||||
const returnPage = () => {
|
||||
isShowPage.value = false;
|
||||
reload();
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.actionTxt {
|
||||
padding: 4px 2px;
|
||||
|
||||
&:last-child {
|
||||
color: #f56c6c;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
330
src/views/erp/customer/Workbench.vue
Normal file
330
src/views/erp/customer/Workbench.vue
Normal file
@ -0,0 +1,330 @@
|
||||
<template>
|
||||
<div style="padding: 10px; box-sizing: border-box">
|
||||
<div>
|
||||
<div class="mumber-box">
|
||||
<div class="mumber" v-for="(it, index) in data.users" :key="index">
|
||||
<div class="mum" :style="{ color: index > 2 ? '#FF8080' : '#5E95FF' }">{{ it.num }}</div>
|
||||
<div class="mum-title">{{ it.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<a-row :gutter="10">
|
||||
<a-col :span="6">
|
||||
<div class="box-bg">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">客户来源统计</span>
|
||||
</div>
|
||||
<div class="item" ref="homeVolChart"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<div class="box-bg">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">回款面积图</span>
|
||||
<div class="title-chose">
|
||||
<span
|
||||
v-for="item in data.priceArray"
|
||||
:key="item.key"
|
||||
:class="item.key == data.cur ? 'cur' : ''"
|
||||
@click="changePrice(item)"
|
||||
>{{ item.name }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item" ref="homeLineChart"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<div class="box-bg">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">客户类型分布统计</span>
|
||||
</div>
|
||||
<div class="item" ref="homePieChart"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, nextTick, reactive, ref, unref, markRaw } from 'vue';
|
||||
import {
|
||||
getSourceInfo,
|
||||
getTypeInfo,
|
||||
getGatherInfo,
|
||||
getCustomerInfo,
|
||||
} from '/@/api/erp/customer/workbench';
|
||||
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
const formate = (name) => {
|
||||
let str = name + ':';
|
||||
data.dataOne.forEach((o: any) => {
|
||||
if (name == o.name) {
|
||||
str += o.value + '家';
|
||||
}
|
||||
});
|
||||
return str;
|
||||
};
|
||||
const formateTwo = (name) => {
|
||||
let str = name + ':';
|
||||
data.dataTwo.forEach((o: any) => {
|
||||
if (name == o.name) {
|
||||
str += o.value + '家';
|
||||
}
|
||||
});
|
||||
return str;
|
||||
};
|
||||
const data = reactive({
|
||||
users: [] as any[],
|
||||
// 玫瑰饼图
|
||||
dataOne: [],
|
||||
pieOption: {
|
||||
color: ['#7EAAFF', '#6D68F8', '#9E9BFC'],
|
||||
legend: {
|
||||
orient: 'horizontal',
|
||||
x: 'center',
|
||||
y: 'bottom',
|
||||
top: '78%',
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemGap: 20,
|
||||
padding: [5, 5, 5, 5],
|
||||
textStyle: {
|
||||
color: '#85878e',
|
||||
},
|
||||
formatter: formate,
|
||||
},
|
||||
series: {
|
||||
name: '',
|
||||
type: 'pie',
|
||||
radius: [0, 85],
|
||||
center: ['50%', '32%'],
|
||||
roseType: 'area',
|
||||
itemStyle: {
|
||||
borderRadius: 0,
|
||||
},
|
||||
label: {
|
||||
color: '#6e7079',
|
||||
overflow: 'none',
|
||||
formatter: (params) => {
|
||||
let per = params.percent || 0;
|
||||
return params.name + '\n' + per + '%';
|
||||
},
|
||||
},
|
||||
data: [],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
//漏斗图
|
||||
dataTwo: [],
|
||||
FunnelOption: {
|
||||
color: ['#9E9BFC', '#6D68F8', '#5887E3', '#7EAAFF', '#8CB1F9'],
|
||||
legend: {
|
||||
orient: 'horizontal',
|
||||
x: 'center',
|
||||
y: 'bottom',
|
||||
top: '78%',
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemGap: 20,
|
||||
padding: [5, 5, 5, 5],
|
||||
textStyle: {
|
||||
color: '#85878e',
|
||||
},
|
||||
formatter: formateTwo,
|
||||
},
|
||||
series: {
|
||||
name: '',
|
||||
type: 'funnel',
|
||||
width: '60%',
|
||||
height: '60%',
|
||||
left: '5%',
|
||||
top: 30,
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
label: {
|
||||
color: '#6e7079',
|
||||
formatter: '{b} {d}%',
|
||||
},
|
||||
data: [],
|
||||
},
|
||||
},
|
||||
// 折线图
|
||||
priceArray: [] as any[],
|
||||
cur: 0,
|
||||
myChart3: null as any,
|
||||
lineOption: {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
grid: {
|
||||
left: 50,
|
||||
right: 40,
|
||||
},
|
||||
color: ['#9863c1'],
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: [],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '单位:万元',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: [],
|
||||
type: 'line',
|
||||
showSymbol: false,
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(140, 125, 252, 0.6)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(140, 125, 252, 0)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
smooth: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const homePieChart = ref<HTMLDivElement>();
|
||||
const homeLineChart = ref<HTMLDivElement>();
|
||||
const homeVolChart = ref<HTMLDivElement>();
|
||||
|
||||
onMounted(async () => {
|
||||
let res = await getCustomerInfo();
|
||||
data.users = res || [];
|
||||
let res1 = await getSourceInfo();
|
||||
data.dataTwo = res1 || [];
|
||||
let res2 = await getGatherInfo();
|
||||
data.priceArray = res2 || [];
|
||||
let res3 = await getTypeInfo();
|
||||
data.dataOne = res3 || [];
|
||||
|
||||
data.pieOption.series.data = data.dataOne;
|
||||
data.FunnelOption.series.data = data.dataTwo;
|
||||
nextTick(() => {
|
||||
let myChart = markRaw(echarts.init(unref(homePieChart) as HTMLDivElement));
|
||||
myChart.setOption(data.pieOption, true);
|
||||
myChart.resize(); //显示区域大小发生改变更新图表
|
||||
|
||||
data.myChart3 = markRaw(echarts.init(unref(homeLineChart) as HTMLDivElement));
|
||||
changePrice(data.priceArray[0]);
|
||||
|
||||
let myChart1 = markRaw(echarts.init(unref(homeVolChart) as HTMLDivElement));
|
||||
myChart1.setOption(data.FunnelOption, true);
|
||||
myChart1.resize(); //显示区域大小发生改变更新图表
|
||||
});
|
||||
});
|
||||
|
||||
const changePrice = (o) => {
|
||||
data.cur = o.key;
|
||||
data.lineOption.xAxis.data = o.category;
|
||||
data.lineOption.series[0].data = o.data;
|
||||
data.myChart3.setOption(data.lineOption, true);
|
||||
data.myChart3.resize(); //显示区域大小发生改变更新图表
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.title-chose {
|
||||
float: right;
|
||||
margin-right: 20px;
|
||||
|
||||
span {
|
||||
text-align: center;
|
||||
padding: 0 12px;
|
||||
display: inline-block;
|
||||
color: #333;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
margin-left: 10px;
|
||||
|
||||
&.cur,
|
||||
&:active {
|
||||
background-color: #5e95ff;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.box-bg {
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 3px 6px 1px rgb(0 0 0 / 10%);
|
||||
|
||||
.box-bg-title {
|
||||
border-bottom: 1px solid #eee;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
font-size: 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 10px 0 0 10px;
|
||||
width: 100%;
|
||||
height: 370px;
|
||||
}
|
||||
}
|
||||
|
||||
.mumber-box {
|
||||
margin: 2px -5px 10px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.mumber {
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
margin: 0 5px;
|
||||
flex: 1;
|
||||
height: 120px;
|
||||
background: #fff;
|
||||
box-shadow: 0 3px 6px 1px rgb(0 0 0 / 10%);
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.mum-title {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.mum {
|
||||
font-size: 48px;
|
||||
font-weight: bolder;
|
||||
margin: -10px 0 10px;
|
||||
height: 75%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-col {
|
||||
padding: 0 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
45
src/views/erp/customer/components/AttachmentModal.vue
Normal file
45
src/views/erp/customer/components/AttachmentModal.vue
Normal file
@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="附件查看">
|
||||
<div class="img-box">
|
||||
<a-image-preview-group v-if="imgList.length">
|
||||
<a-image v-for="(item, index) in imgList" :key="index" :src="item.fileUrl" />
|
||||
</a-image-preview-group>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { getFileList } from '/@/api/system/file';
|
||||
|
||||
const imgList = ref<any>([]);
|
||||
defineEmits(['register']);
|
||||
const [registerModal, { setModalProps }] = useModalInner(async (data) => {
|
||||
setModalProps({
|
||||
confirmLoading: false,
|
||||
destroyOnClose: true,
|
||||
showOkBtn: false,
|
||||
showCancelBtn: false,
|
||||
footer: null,
|
||||
bodyStyle: {
|
||||
height: '300px',
|
||||
},
|
||||
});
|
||||
imgList.value = await getFileList({ folderId: data.filePath });
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-image-img) {
|
||||
width: 100px;
|
||||
height: 75px;
|
||||
padding: 5px;
|
||||
border: 1px solid silver;
|
||||
border-radius: 4px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.img-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
129
src/views/erp/customer/components/CollectionDetailModal.vue
Normal file
129
src/views/erp/customer/components/CollectionDetailModal.vue
Normal file
@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit">
|
||||
<BasicForm @register="registerForm">
|
||||
<template #imgUpload="{ model }">
|
||||
<Upload
|
||||
v-model:value="model.filePath"
|
||||
listType="picture"
|
||||
:maxSize="2"
|
||||
accept=".bmp,.jpg,.jpeg,.png,.gif,.svg"
|
||||
/>
|
||||
</template>
|
||||
</BasicForm>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { FormSchema } from '/@/components/Table';
|
||||
import {
|
||||
addCollectionDetail,
|
||||
updateCollectionDetail,
|
||||
getCollectionDetailInfo,
|
||||
} from '/@/api/erp/customer/collection';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import Upload from '/@/components/Form/src/components/Upload.vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
const FormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'amountCollect',
|
||||
label: '回款金额',
|
||||
component: 'InputNumber',
|
||||
required: true,
|
||||
colProps: { span: 24 },
|
||||
componentProps: {
|
||||
placeholder: '请输入回款金额',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'date',
|
||||
label: '回款时间',
|
||||
component: 'DatePicker',
|
||||
required: true,
|
||||
colProps: { span: 24 },
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '请选择回款时间',
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
label: '备注',
|
||||
component: 'InputTextArea',
|
||||
colProps: { span: 24 },
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'filePath',
|
||||
label: '附件上传',
|
||||
component: 'Upload',
|
||||
slot: 'imgUpload',
|
||||
colProps: { span: 24 },
|
||||
},
|
||||
];
|
||||
|
||||
const { notification } = useMessage();
|
||||
const isUpdate = ref(true);
|
||||
const gatherId = ref('');
|
||||
const rowId = ref('');
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
|
||||
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
|
||||
labelWidth: 100,
|
||||
schemas: FormSchema,
|
||||
showActionButtonGroup: false,
|
||||
actionColOptions: {
|
||||
span: 23,
|
||||
},
|
||||
});
|
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
resetFields();
|
||||
setModalProps({ confirmLoading: false, destroyOnClose: true });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
gatherId.value = data.gatherId;
|
||||
if (unref(isUpdate)) {
|
||||
rowId.value = data.id;
|
||||
const record = await getCollectionDetailInfo(data.id);
|
||||
setFieldsValue({
|
||||
...record,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const getTitle = computed(() => (!unref(isUpdate) ? '新增回款记录' : '编辑回款记录'));
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
const values = await validate();
|
||||
values.gatherId = gatherId.value;
|
||||
setModalProps({ confirmLoading: true });
|
||||
|
||||
if (!unref(isUpdate)) {
|
||||
await addCollectionDetail(values);
|
||||
notification.success({
|
||||
message: '新增回款记录详情',
|
||||
description: t('成功'),
|
||||
});
|
||||
} else {
|
||||
values.id = rowId.value;
|
||||
await updateCollectionDetail(values);
|
||||
notification.success({
|
||||
message: '编辑回款记录详情',
|
||||
description: t('成功'),
|
||||
});
|
||||
}
|
||||
|
||||
closeModal();
|
||||
emit('success');
|
||||
} catch (error) {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
};
|
||||
</script>
|
||||
209
src/views/erp/customer/components/CollectionInfo.vue
Normal file
209
src/views/erp/customer/components/CollectionInfo.vue
Normal file
@ -0,0 +1,209 @@
|
||||
<template>
|
||||
<div class="info-box">
|
||||
<div class="top-box">
|
||||
<div>
|
||||
<a-button @click="emit('returnPage')">返回</a-button>
|
||||
<span class="text-base font-bold pl-5">回款记录</span>
|
||||
</div>
|
||||
<a-button type="primary" @click="handleCreate">新增回款记录</a-button>
|
||||
</div>
|
||||
<a-divider />
|
||||
<a-card :bordered="false">
|
||||
<div class="title-name">{{ baseInfo?.name }}</div>
|
||||
<div class="title-info">
|
||||
<span>计划回款金额:{{ baseInfo?.waitAmount }}</span>
|
||||
<span>计划回款日期:{{ baseInfo?.receivedDate }}</span>
|
||||
<span>最迟回款日期:{{ baseInfo?.finallyDate }}</span>
|
||||
<span>合同标题:{{ baseInfo?.title }}</span>
|
||||
<span>合同负责人:{{ baseInfo?.principalNames }}</span>
|
||||
</div>
|
||||
</a-card>
|
||||
<BasicTable @register="registerTable">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex == 'action'">
|
||||
<a-button type="link" class="actionTxt" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button
|
||||
type="link"
|
||||
class="actionTxt"
|
||||
@click="handleView(record)"
|
||||
:disabled="!record.filePath"
|
||||
>
|
||||
附件查看
|
||||
</a-button>
|
||||
<a-button type="link" class="actionTxt" @click="handleDelete(record)">删除</a-button>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
<CollectionDetailModal @register="registerModal" @success="reloadTable()" />
|
||||
<AttachmentModal @register="registerAttachmentModal" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, createVNode } from 'vue';
|
||||
import { BasicTable, useTable, BasicColumn } from '/@/components/Table';
|
||||
import { getDetailInfo, deleteCollectionDetail } from '/@/api/erp/customer/collection';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import CollectionDetailModal from './CollectionDetailModal.vue';
|
||||
import AttachmentModal from './AttachmentModal.vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
const columns: BasicColumn[] = [
|
||||
{
|
||||
dataIndex: 'amountCollect',
|
||||
title: '回款金额',
|
||||
align: 'center',
|
||||
},
|
||||
|
||||
{
|
||||
dataIndex: 'remark',
|
||||
title: '备注',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
dataIndex: 'date',
|
||||
title: '回款时间',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
dataIndex: 'createDate',
|
||||
title: '创建时间',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
dataIndex: 'createUserName',
|
||||
title: '创建人',
|
||||
align: 'center',
|
||||
},
|
||||
];
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
id: String,
|
||||
});
|
||||
const emit = defineEmits(['returnPage']);
|
||||
const dataSource = ref<any>([]);
|
||||
const baseInfo = ref();
|
||||
const { notification } = useMessage();
|
||||
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
const [registerAttachmentModal, { openModal: openAttachmentModal }] = useModal();
|
||||
|
||||
onMounted(() => {
|
||||
getInfo();
|
||||
});
|
||||
|
||||
const [registerTable, { reload }] = useTable({
|
||||
dataSource,
|
||||
rowKey: 'id',
|
||||
columns,
|
||||
bordered: true,
|
||||
pagination: false,
|
||||
actionColumn: {
|
||||
width: 150,
|
||||
title: t('操作'),
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
},
|
||||
});
|
||||
|
||||
const getInfo = async () => {
|
||||
const res = await getDetailInfo(props.id!);
|
||||
baseInfo.value = res;
|
||||
dataSource.value = res?.caseErpCustomerGatherDetailVoList;
|
||||
};
|
||||
|
||||
const reloadTable = () => {
|
||||
getInfo();
|
||||
reload();
|
||||
};
|
||||
|
||||
const handleCreate = () => {
|
||||
openModal(true, {
|
||||
gatherId: props.id,
|
||||
});
|
||||
};
|
||||
|
||||
const handleEdit = (record) => {
|
||||
openModal(true, {
|
||||
gatherId: props.id,
|
||||
id: record.id,
|
||||
isUpdate: true,
|
||||
});
|
||||
};
|
||||
|
||||
const handleView = (record) => {
|
||||
openAttachmentModal(true, {
|
||||
filePath: record.filePath,
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record) => {
|
||||
Modal.confirm({
|
||||
title: t('提示信息'),
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
content: t('是否确认删除?'),
|
||||
okText: t('确认'),
|
||||
cancelText: t('取消'),
|
||||
async onOk() {
|
||||
await deleteCollectionDetail(record.id);
|
||||
notification.success({
|
||||
message: t('提示'),
|
||||
description: t('删除成功'),
|
||||
});
|
||||
reloadTable();
|
||||
},
|
||||
onCancel() {},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.info-box {
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 16px;
|
||||
margin-right: 8px;
|
||||
|
||||
.top-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.title-name {
|
||||
font-size: 18px;
|
||||
color: #444;
|
||||
font-weight: 700;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.title-info span {
|
||||
margin-right: 40px;
|
||||
}
|
||||
|
||||
.actionTxt {
|
||||
padding: 4px 2px;
|
||||
|
||||
&:last-child {
|
||||
color: #f56c6c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-divider) {
|
||||
height: 1px;
|
||||
background-color: #dcdfe6;
|
||||
}
|
||||
|
||||
:deep(.ant-card-body) {
|
||||
background-color: #f8f8f8;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
:deep(.vben-basic-table) {
|
||||
height: calc(100% - 200px);
|
||||
}
|
||||
</style>
|
||||
165
src/views/erp/customer/components/CollectionModal.vue
Normal file
165
src/views/erp/customer/components/CollectionModal.vue
Normal file
@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit">
|
||||
<BasicForm @register="registerForm">
|
||||
<template #user="{ model }">
|
||||
<SelectUser
|
||||
v-model:value="model.principalIds"
|
||||
suffix="ant-design:setting-outlined"
|
||||
placeholder="请选择负责人"
|
||||
/>
|
||||
</template>
|
||||
<template #imgUpload="{ model }">
|
||||
<Upload v-model:value="model.filePath" listType="picture" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { FormSchema } from '/@/components/Table';
|
||||
import {
|
||||
addCollection,
|
||||
updateCollection,
|
||||
getCollectionInfo,
|
||||
} from '/@/api/erp/customer/collection';
|
||||
import { getCustomerList } from '/@/api/erp/customer/list';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import SelectUser from '/@/components/Form/src/components/SelectUser.vue';
|
||||
import Upload from '/@/components/Form/src/components/Upload.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const FormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'customerId',
|
||||
label: '客户名称',
|
||||
component: 'ApiSelect',
|
||||
required: true,
|
||||
colProps: { span: 24 },
|
||||
componentProps: {
|
||||
placeholder: '请选择客户名称',
|
||||
api: getCustomerList,
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'waitAmount',
|
||||
label: '计划回款金额',
|
||||
component: 'InputNumber',
|
||||
required: true,
|
||||
colProps: { span: 24 },
|
||||
componentProps: {
|
||||
placeholder: '请输入计划回款金额',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'receivedDate',
|
||||
label: '计划回款日期',
|
||||
component: 'DatePicker',
|
||||
required: true,
|
||||
colProps: { span: 24 },
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '请选择回款日期',
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'finallyDate',
|
||||
label: '最迟回款日期',
|
||||
component: 'DatePicker',
|
||||
required: true,
|
||||
colProps: { span: 24 },
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '请选择最迟回款日期',
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'principalIds',
|
||||
label: '合同负责人',
|
||||
component: 'Input',
|
||||
slot: 'user',
|
||||
required: true,
|
||||
colProps: { span: 24 },
|
||||
},
|
||||
{
|
||||
field: 'title',
|
||||
label: '合同标题',
|
||||
component: 'Input',
|
||||
colProps: { span: 24 },
|
||||
componentProps: {
|
||||
placeholder: '请输入合同标题',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'filePath',
|
||||
label: '合同附件',
|
||||
component: 'Upload',
|
||||
slot: 'imgUpload',
|
||||
colProps: { span: 24 },
|
||||
},
|
||||
];
|
||||
|
||||
const { notification } = useMessage();
|
||||
const isUpdate = ref(true);
|
||||
const rowId = ref('');
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
|
||||
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
|
||||
labelWidth: 100,
|
||||
schemas: FormSchema,
|
||||
showActionButtonGroup: false,
|
||||
actionColOptions: {
|
||||
span: 23,
|
||||
},
|
||||
});
|
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
resetFields();
|
||||
setModalProps({ confirmLoading: false, destroyOnClose: true });
|
||||
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
if (unref(isUpdate)) {
|
||||
rowId.value = data.id;
|
||||
const record = await getCollectionInfo(data.id);
|
||||
setFieldsValue({
|
||||
...record,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const getTitle = computed(() => (!unref(isUpdate) ? '新增回款计划' : '编辑回款计划'));
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
const values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
|
||||
if (!unref(isUpdate)) {
|
||||
await addCollection(values);
|
||||
notification.success({
|
||||
message: '新增回款计划',
|
||||
description: t('成功'),
|
||||
});
|
||||
} else {
|
||||
values.id = rowId.value;
|
||||
await updateCollection(values);
|
||||
notification.success({
|
||||
message: '编辑回款计划',
|
||||
description: t('成功'),
|
||||
});
|
||||
}
|
||||
|
||||
closeModal();
|
||||
emit('success');
|
||||
} catch (error) {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
};
|
||||
</script>
|
||||
186
src/views/erp/customer/components/ContactsModal.vue
Normal file
186
src/views/erp/customer/components/ContactsModal.vue
Normal file
@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
v-bind="$attrs"
|
||||
@register="registerModal"
|
||||
:width="800"
|
||||
:body-style="{
|
||||
height: '400px !important',
|
||||
}"
|
||||
:title="getTitle"
|
||||
@ok="handleSubmit"
|
||||
>
|
||||
<div class="config-title">
|
||||
<span class="font-semibold">联系人</span>
|
||||
<span class="add-span" @click="addConfig" v-if="!isUpdate">+ 增加</span>
|
||||
</div>
|
||||
<a-radio-group v-model:value="defaultIndex" style="width: 100%">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="personList"
|
||||
name="personList"
|
||||
:label-col="{ span: 4 }"
|
||||
:wrapper-col="{ span: 20 }"
|
||||
autocomplete="off"
|
||||
>
|
||||
<template v-for="(item, index) in personList.list" :key="index">
|
||||
<a-row style="margin: 0 0 20px 10px">
|
||||
<a-col :span="20" style="color: #999">联系人{{ index + 1 }}</a-col>
|
||||
<a-col :span="4" align="right">
|
||||
<a-radio :value="index">默认</a-radio>
|
||||
<span @click="deleteConfig(index)" class="delete-span" v-if="index !== 0">删除</span>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="姓名"
|
||||
:name="['list', index, 'name']"
|
||||
:rules="{ required: true, message: '请输入姓名', trigger: 'change' }"
|
||||
>
|
||||
<a-input v-model:value="item.name" placeholder="请输入姓名" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="电话" :name="['list', index, 'phone']">
|
||||
<a-input v-model:value="item.phone" placeholder="请输入电话" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="职位" :name="['list', index, 'post']">
|
||||
<a-input v-model:value="item.post" placeholder="请输入职位" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="部门" :name="['list', index, 'dept']">
|
||||
<a-input v-model:value="item.dept" placeholder="请输入部门" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
</a-form>
|
||||
</a-radio-group>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { addContacts, updateContacts, getContactsInfo } from '/@/api/erp/customer/list';
|
||||
const { t } = useI18n();
|
||||
|
||||
const { notification } = useMessage();
|
||||
const isUpdate = ref(true);
|
||||
const rowId = ref('');
|
||||
const customerId = ref('');
|
||||
const defaultIndex = ref<any>();
|
||||
const formRef = ref();
|
||||
const personList = ref<any>({
|
||||
list: [
|
||||
{
|
||||
isDefault: false,
|
||||
dept: '',
|
||||
name: '',
|
||||
phone: '',
|
||||
post: '',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
setModalProps({ confirmLoading: false, destroyOnClose: true });
|
||||
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
customerId.value = data.customerId;
|
||||
defaultIndex.value = null;
|
||||
|
||||
if (unref(isUpdate)) {
|
||||
rowId.value = data.id;
|
||||
const info = await getContactsInfo(data.id);
|
||||
personList.value.list[0] = info;
|
||||
defaultIndex.value = info.isDefault ? 0 : null;
|
||||
} else {
|
||||
personList.value = {
|
||||
list: [
|
||||
{
|
||||
isDefault: false,
|
||||
dept: '',
|
||||
name: '',
|
||||
phone: '',
|
||||
post: '',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const getTitle = computed(() => (!unref(isUpdate) ? '新增联系人' : '编辑联系人'));
|
||||
|
||||
const addConfig = () => {
|
||||
personList.value.list.push({ isDefault: false, dept: '', name: '', phone: '', post: '' });
|
||||
};
|
||||
|
||||
const deleteConfig = (index) => {
|
||||
personList.value.list.splice(index, 1);
|
||||
};
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
await formRef.value.validate();
|
||||
personList.value.list.map((x, idx) => {
|
||||
x.isDefault = idx === defaultIndex.value ? 1 : 0;
|
||||
});
|
||||
|
||||
setModalProps({ confirmLoading: true });
|
||||
|
||||
if (!unref(isUpdate)) {
|
||||
const params = {
|
||||
customerId: customerId.value,
|
||||
caseErpCustomerContactsDtoList: [...personList.value.list],
|
||||
};
|
||||
await addContacts(params);
|
||||
notification.success({
|
||||
message: '新增联系人',
|
||||
description: t('成功'),
|
||||
});
|
||||
} else {
|
||||
const params = {
|
||||
id: rowId.value,
|
||||
customerId: customerId.value,
|
||||
...personList.value.list[0],
|
||||
};
|
||||
await updateContacts(params);
|
||||
notification.success({
|
||||
message: '编辑联系人',
|
||||
description: t('成功'),
|
||||
});
|
||||
}
|
||||
closeModal();
|
||||
emit('success');
|
||||
} catch (error) {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.config-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 18px;
|
||||
font-weight: 700;
|
||||
|
||||
.add-span {
|
||||
color: #5e95ff;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.delete-span {
|
||||
color: #ff8080;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
439
src/views/erp/customer/components/CustomerInfo.vue
Normal file
439
src/views/erp/customer/components/CustomerInfo.vue
Normal file
@ -0,0 +1,439 @@
|
||||
<template>
|
||||
<div class="info-box">
|
||||
<div>
|
||||
<a-button @click="emit('returnPage')">返回</a-button>
|
||||
<span class="text-base font-bold pl-5">客户详情查看</span>
|
||||
</div>
|
||||
<a-divider />
|
||||
<a-card :bordered="false">
|
||||
<div class="title-name">{{ baseInfo?.name }}</div>
|
||||
<div class="title-info">
|
||||
<span>客户类型:{{ baseInfo?.typeName }}</span>
|
||||
<span>所在行业:{{ baseInfo?.industry }}</span>
|
||||
<span>来源:{{ baseInfo?.sourceName }}</span>
|
||||
<span>规模:{{ baseInfo?.scaleName }}</span>
|
||||
<span>公司性质:{{ baseInfo?.natureName }}</span>
|
||||
<span>地址:{{ baseInfo?.address }}</span>
|
||||
<span v-if="!isCommon">归属:{{ baseInfo?.saleName }}</span>
|
||||
</div>
|
||||
</a-card>
|
||||
<a-tabs v-model:activeKey="activeKey">
|
||||
<a-tab-pane key="1" tab="跟进记录" v-if="!isCommon">
|
||||
<BasicTable @register="registerFollowTable">
|
||||
<template #toolbar>
|
||||
<a-button type="primary" @click="handleFollowCreate">新增跟进</a-button>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex == 'action'">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
icon: 'clarity:note-edit-line',
|
||||
onClick: handleFollowEdit.bind(null, record),
|
||||
},
|
||||
{
|
||||
icon: 'ant-design:delete-outlined',
|
||||
color: 'error',
|
||||
onClick: handleFollowDelete.bind(null, record),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" tab="客户信息">
|
||||
<div class="sub-title">客户信息</div>
|
||||
<a-card :bordered="false">
|
||||
<div class="title-info flex">
|
||||
图片资料:
|
||||
<a-image-preview-group v-if="imgList.length">
|
||||
<a-image v-for="(item, index) in imgList" :key="index" :src="item.fileUrl" />
|
||||
</a-image-preview-group>
|
||||
<span v-else>暂无图片</span>
|
||||
</div>
|
||||
</a-card>
|
||||
<div class="sub-title other-info">其他信息</div>
|
||||
<a-card :bordered="false">
|
||||
<div class="title-info">
|
||||
<span>创建日期:{{ baseInfo?.createDate }}</span>
|
||||
<span>创建人:{{ baseInfo?.createUserName }}</span>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="3" tab="联系人">
|
||||
<BasicTable @register="registerContactsTable">
|
||||
<template #toolbar>
|
||||
<a-button type="primary" @click="handleContactsCreate">新增联系人</a-button>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex == 'action'">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
icon: 'clarity:note-edit-line',
|
||||
onClick: handleContactsEdit.bind(null, record),
|
||||
},
|
||||
{
|
||||
icon: 'ant-design:delete-outlined',
|
||||
color: 'error',
|
||||
onClick: handleContactsDelete.bind(null, record),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="4" tab="操作记录">
|
||||
<BasicTable @register="registerLogTable" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
<FollowModal @register="registerFollowModal" @success="reload('follow')" />
|
||||
<ContactsModal @register="registerContactsModal" @success="reload('contact')" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, createVNode, watch } from 'vue';
|
||||
import { BasicTable, TableAction, useTable, BasicColumn, FormSchema } from '/@/components/Table';
|
||||
import { getDetailInfo, deleteFollowCustomer, deleteContacts } from '/@/api/erp/customer/list';
|
||||
import { getFileList } from '/@/api/system/file';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import FollowModal from './FollowModal.vue';
|
||||
import ContactsModal from './ContactsModal.vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { isNil } from 'lodash-es';
|
||||
|
||||
const contactsColumns: BasicColumn[] = [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: '联系人',
|
||||
align: 'center',
|
||||
},
|
||||
|
||||
{
|
||||
dataIndex: 'phone',
|
||||
title: '电话',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
dataIndex: 'post',
|
||||
title: '职位',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
dataIndex: 'dept',
|
||||
title: '部门',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
dataIndex: 'createDate',
|
||||
title: '创建时间',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
dataIndex: 'createUserName',
|
||||
title: '创建人',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
dataIndex: 'isDefault',
|
||||
title: '是否默认',
|
||||
align: 'center',
|
||||
customRender: ({ text }) => {
|
||||
return text ? '是' : '否';
|
||||
},
|
||||
},
|
||||
];
|
||||
const followColumns: BasicColumn[] = [
|
||||
{
|
||||
dataIndex: 'followTime',
|
||||
title: '跟进时间',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
dataIndex: 'nextFollowTime',
|
||||
title: '下次跟进时间',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
dataIndex: 'followTypeName',
|
||||
title: '跟进方式',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
dataIndex: 'content',
|
||||
title: '说明',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
dataIndex: 'createUserName',
|
||||
title: '跟进人',
|
||||
align: 'center',
|
||||
},
|
||||
];
|
||||
const logColumns: BasicColumn[] = [
|
||||
{
|
||||
dataIndex: 'operateUserAccount',
|
||||
title: '操作人',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
dataIndex: 'createDate',
|
||||
title: '操作时间',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
dataIndex: 'executeResultJson',
|
||||
title: '操作内容',
|
||||
align: 'center',
|
||||
},
|
||||
];
|
||||
|
||||
const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'keyword',
|
||||
label: '',
|
||||
component: 'Input',
|
||||
colProps: { span: 4 },
|
||||
componentProps: {
|
||||
placeholder: '请输入要查询的关键字',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
id: String,
|
||||
isCommon: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['returnPage']);
|
||||
const followInfoDataSource = ref<any>([]);
|
||||
const contactsInfoDataSource = ref<any>([]);
|
||||
const logInfoDataSource = ref<any>([]);
|
||||
const activeKey = ref('1');
|
||||
const baseInfo = ref();
|
||||
const imgList = ref<any>([]);
|
||||
const { notification } = useMessage();
|
||||
|
||||
const [registerFollowModal, { openModal: openFollowModal }] = useModal();
|
||||
const [registerContactsModal, { openModal: openContactsModal }] = useModal();
|
||||
|
||||
watch(
|
||||
() => props.isCommon,
|
||||
(val) => {
|
||||
activeKey.value = val ? '2' : '1';
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
const [registerFollowTable, { reload: reloadFollow }] = useTable({
|
||||
dataSource: followInfoDataSource,
|
||||
rowKey: 'id',
|
||||
columns: followColumns,
|
||||
bordered: true,
|
||||
pagination: false,
|
||||
actionColumn: {
|
||||
width: 120,
|
||||
title: t('操作'),
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
},
|
||||
});
|
||||
const [registerContactsTable, { reload: reloadContact }] = useTable({
|
||||
dataSource: contactsInfoDataSource,
|
||||
rowKey: 'id',
|
||||
columns: contactsColumns,
|
||||
bordered: true,
|
||||
pagination: false,
|
||||
actionColumn: {
|
||||
width: 120,
|
||||
title: t('操作'),
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
},
|
||||
});
|
||||
const [registerLogTable, { setProps }] = useTable({
|
||||
dataSource: logInfoDataSource,
|
||||
rowKey: 'id',
|
||||
columns: logColumns,
|
||||
handleSearchInfoFn(info) {
|
||||
const filterData = logInfoDataSource.value.filter((item) => {
|
||||
for (const key in item) {
|
||||
if (!isNil(item[key]) && item[key].toString().indexOf(info.keyword) > -1) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
});
|
||||
setProps({ dataSource: filterData });
|
||||
return info;
|
||||
},
|
||||
formConfig: {
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
useSearchForm: true,
|
||||
bordered: true,
|
||||
pagination: false,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getInfo();
|
||||
});
|
||||
|
||||
const getInfo = async () => {
|
||||
const res = await getDetailInfo(props.id!);
|
||||
baseInfo.value = res?.caseErpCustomerVo;
|
||||
followInfoDataSource.value = res?.caseErpCustomerFollowInfoVoList;
|
||||
contactsInfoDataSource.value = res?.caseErpCustomerContactsInfoVoList;
|
||||
logInfoDataSource.value = res?.caseErpLogList.map((x) => {
|
||||
return {
|
||||
operateUserAccount: x.operateUserAccount,
|
||||
createDate: x.createDate,
|
||||
executeResultJson: x.executeResultJson,
|
||||
};
|
||||
});
|
||||
if (baseInfo.value.filePath) {
|
||||
imgList.value = await getFileList({ folderId: baseInfo.value.filePath });
|
||||
}
|
||||
};
|
||||
|
||||
const reload = (type) => {
|
||||
getInfo();
|
||||
type === 'follow' ? reloadFollow() : reloadContact();
|
||||
};
|
||||
|
||||
const handleFollowCreate = () => {
|
||||
openFollowModal(true, {
|
||||
record: baseInfo.value,
|
||||
});
|
||||
};
|
||||
|
||||
const handleFollowEdit = (record) => {
|
||||
openFollowModal(true, {
|
||||
record: baseInfo.value,
|
||||
id: record.id,
|
||||
isUpdate: true,
|
||||
});
|
||||
};
|
||||
|
||||
const handleFollowDelete = (record) => {
|
||||
Modal.confirm({
|
||||
title: t('提示信息'),
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
content: t('是否确认删除?'),
|
||||
okText: t('确认'),
|
||||
cancelText: t('取消'),
|
||||
async onOk() {
|
||||
await deleteFollowCustomer(record.id);
|
||||
notification.success({
|
||||
message: t('提示'),
|
||||
description: t('删除成功'),
|
||||
});
|
||||
reload('follow');
|
||||
},
|
||||
onCancel() {},
|
||||
});
|
||||
};
|
||||
|
||||
const handleContactsCreate = () => {
|
||||
openContactsModal(true, {
|
||||
customerId: props.id,
|
||||
isUpdate: false,
|
||||
});
|
||||
};
|
||||
|
||||
const handleContactsEdit = (record) => {
|
||||
openContactsModal(true, {
|
||||
customerId: props.id,
|
||||
id: record.id,
|
||||
isUpdate: true,
|
||||
});
|
||||
};
|
||||
|
||||
const handleContactsDelete = (record) => {
|
||||
Modal.confirm({
|
||||
title: t('提示信息'),
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
content: t('是否确认删除?'),
|
||||
okText: t('确认'),
|
||||
cancelText: t('取消'),
|
||||
async onOk() {
|
||||
await deleteContacts(record.id);
|
||||
notification.success({
|
||||
message: t('提示'),
|
||||
description: t('删除成功'),
|
||||
});
|
||||
reload('contact');
|
||||
},
|
||||
onCancel() {},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.info-box {
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 16px;
|
||||
margin-right: 8px;
|
||||
|
||||
.title-name {
|
||||
font-size: 18px;
|
||||
color: #444;
|
||||
font-weight: 700;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.title-info span {
|
||||
margin-right: 40px;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
font-weight: 700;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.other-info {
|
||||
margin-top: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-image) {
|
||||
width: 100px;
|
||||
height: 75px;
|
||||
padding: 5px;
|
||||
border: 1px solid silver;
|
||||
border-radius: 4px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
:deep(.ant-image-img) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.ant-divider) {
|
||||
height: 1px;
|
||||
background-color: #dcdfe6;
|
||||
}
|
||||
|
||||
:deep(.ant-card-body) {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
:deep(.ant-tabs) {
|
||||
height: 70%;
|
||||
}
|
||||
|
||||
:deep(.ant-tabs-content) {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
305
src/views/erp/customer/components/CustomerModal.vue
Normal file
305
src/views/erp/customer/components/CustomerModal.vue
Normal file
@ -0,0 +1,305 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
v-bind="$attrs"
|
||||
@register="registerModal"
|
||||
:width="800"
|
||||
:body-style="{
|
||||
height: '400px !important',
|
||||
}"
|
||||
:title="getTitle"
|
||||
@ok="handleSubmit"
|
||||
>
|
||||
<div class="sub-title">基本信息</div>
|
||||
<BasicForm @register="registerForm">
|
||||
<template #imgUpload="{ model }">
|
||||
<Upload v-model:value="model.filePath" listType="picture" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
<div class="config-title">
|
||||
<span class="font-semibold">联系人</span>
|
||||
<span class="add-span" @click="addConfig">+ 增加</span>
|
||||
</div>
|
||||
<a-radio-group v-model:value="defaultIndex" style="width: 100%">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="personList"
|
||||
name="personList"
|
||||
:label-col="{ span: 4 }"
|
||||
:wrapper-col="{ span: 20 }"
|
||||
autocomplete="off"
|
||||
>
|
||||
<template v-for="(item, index) in personList.list" :key="index">
|
||||
<a-row style="margin: 0 0 20px 10px">
|
||||
<a-col :span="20" style="color: #999">联系人{{ index + 1 }}</a-col>
|
||||
<a-col :span="4" align="right">
|
||||
<a-radio :value="index">默认</a-radio>
|
||||
<span @click="deleteConfig(index)" class="delete-span">删除</span>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
label="姓名"
|
||||
:name="['list', index, 'name']"
|
||||
:rules="{ required: true, message: '请输入姓名', trigger: 'change' }"
|
||||
>
|
||||
<a-input v-model:value="item.name" placeholder="请输入姓名" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="电话" :name="['list', index, 'phone']">
|
||||
<a-input v-model:value="item.phone" placeholder="请输入电话" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="职位" :name="['list', index, 'post']">
|
||||
<a-input v-model:value="item.post" placeholder="请输入职位" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="部门" :name="['list', index, 'dept']">
|
||||
<a-input v-model:value="item.dept" placeholder="请输入部门" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
</a-form>
|
||||
</a-radio-group>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { FormSchema } from '/@/components/Table';
|
||||
import Upload from '/@/components/Form/src/components/Upload.vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { addCustomer, updateCustomer, getCustomerInfo } from '/@/api/erp/customer/list';
|
||||
const { t } = useI18n();
|
||||
|
||||
const FormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
label: '客户名称',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请输入客户名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'typeId',
|
||||
label: '客户类型',
|
||||
component: 'DicSelect',
|
||||
required: true,
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请选择客户类型',
|
||||
itemId: '1679007059387240450',
|
||||
isShowAdd: false,
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'industry',
|
||||
label: '所在行业',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请输入所在行业',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'sourceId',
|
||||
label: '来源',
|
||||
component: 'DicSelect',
|
||||
required: true,
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请选择来源',
|
||||
itemId: '1679008505423876097',
|
||||
isShowAdd: false,
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'scaleId',
|
||||
label: '规模',
|
||||
component: 'DicSelect',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请选择规模',
|
||||
itemId: '1679009315172012034',
|
||||
isShowAdd: false,
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'natureId',
|
||||
label: '公司性质',
|
||||
component: 'DicSelect',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请选择公司性质',
|
||||
itemId: '1679010661178691585',
|
||||
isShowAdd: false,
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
colProps: { span: 24 },
|
||||
componentProps: {
|
||||
placeholder: '请输入地址',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'filePath',
|
||||
label: '图片上传',
|
||||
component: 'Upload',
|
||||
slot: 'imgUpload',
|
||||
colProps: { span: 24 },
|
||||
},
|
||||
];
|
||||
|
||||
const { notification } = useMessage();
|
||||
const isUpdate = ref(true);
|
||||
const rowId = ref('');
|
||||
const state = ref();
|
||||
const defaultIndex = ref(0);
|
||||
const formRef = ref();
|
||||
const personList = ref<any>({
|
||||
list: [
|
||||
{
|
||||
isDefault: false,
|
||||
dept: '',
|
||||
name: '',
|
||||
phone: '',
|
||||
post: '',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
|
||||
const [registerForm, { setFieldsValue, validate }] = useForm({
|
||||
labelWidth: 100,
|
||||
schemas: FormSchema,
|
||||
showActionButtonGroup: false,
|
||||
actionColOptions: {
|
||||
span: 23,
|
||||
},
|
||||
});
|
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
setModalProps({ confirmLoading: false, destroyOnClose: true });
|
||||
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
state.value = data?.state;
|
||||
defaultIndex.value = 0;
|
||||
if (unref(isUpdate)) {
|
||||
rowId.value = data.id;
|
||||
const record = await getCustomerInfo(data.id);
|
||||
setFieldsValue({ ...record });
|
||||
personList.value.list = [...record.caseErpCustomerContacts];
|
||||
personList.value.list.some((x, idx) => {
|
||||
if (x.isDefault) {
|
||||
defaultIndex.value = idx;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
personList.value = {
|
||||
list: [
|
||||
{
|
||||
isDefault: false,
|
||||
dept: '',
|
||||
name: '',
|
||||
phone: '',
|
||||
post: '',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const getTitle = computed(() => (!unref(isUpdate) ? '新增客户' : '编辑客户'));
|
||||
|
||||
const addConfig = () => {
|
||||
personList.value.list.push({ isDefault: false, dept: '', name: '', phone: '', post: '' });
|
||||
};
|
||||
|
||||
const deleteConfig = (index) => {
|
||||
personList.value.list.splice(index, 1);
|
||||
};
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
await formRef.value.validate();
|
||||
|
||||
if (personList.value.list.length) {
|
||||
personList.value.list.map((x, idx) => (x.isDefault = idx === defaultIndex.value ? 1 : 0));
|
||||
}
|
||||
|
||||
const info = {
|
||||
...values,
|
||||
state: state.value,
|
||||
addCaseErpCustomerContactsDtoList: personList.value.list,
|
||||
};
|
||||
setModalProps({ confirmLoading: true });
|
||||
|
||||
if (!unref(isUpdate)) {
|
||||
await addCustomer(info);
|
||||
notification.success({
|
||||
message: '新增客户',
|
||||
description: t('成功'),
|
||||
});
|
||||
} else {
|
||||
info.id = rowId.value;
|
||||
await updateCustomer(info);
|
||||
notification.success({
|
||||
message: '编辑客户',
|
||||
description: t('成功'),
|
||||
});
|
||||
}
|
||||
|
||||
closeModal();
|
||||
emit('success');
|
||||
} catch (error) {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.sub-title {
|
||||
font-weight: 700;
|
||||
margin: 0 18px 18px;
|
||||
}
|
||||
|
||||
.config-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 18px;
|
||||
font-weight: 700;
|
||||
|
||||
.add-span {
|
||||
color: #5e95ff;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.delete-span {
|
||||
color: #ff8080;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
157
src/views/erp/customer/components/FollowModal.vue
Normal file
157
src/views/erp/customer/components/FollowModal.vue
Normal file
@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
v-bind="$attrs"
|
||||
@register="registerModal"
|
||||
:width="800"
|
||||
title="客户跟进"
|
||||
@ok="handleSubmit"
|
||||
>
|
||||
<div class="sub-title">基本信息</div>
|
||||
<a-card :bordered="false">
|
||||
<div class="title-info">
|
||||
<span>客户名称:{{ baseInfo?.name }}</span>
|
||||
<span>客户类型:{{ baseInfo?.typeName }}</span>
|
||||
<span>所在行业:{{ baseInfo?.industry }}</span>
|
||||
<span>来源:{{ baseInfo?.sourceName }}</span>
|
||||
</div>
|
||||
</a-card>
|
||||
<div class="sub-title">客户跟进</div>
|
||||
<BasicForm @register="registerForm">
|
||||
<template #imgUpload="{ model }">
|
||||
<Upload v-model:value="model.filePath" listType="picture" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { FormSchema } from '/@/components/Table';
|
||||
import Upload from '/@/components/Form/src/components/Upload.vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { addFollowCustomer, updateFollowCustomer, getFollowInfo } from '/@/api/erp/customer/list';
|
||||
const { t } = useI18n();
|
||||
|
||||
const FormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'followTypeId',
|
||||
label: '跟进方式',
|
||||
component: 'DicSelect',
|
||||
required: true,
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请选择跟进方式',
|
||||
itemId: '1679011374365560833',
|
||||
isShowAdd: false,
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'followTime',
|
||||
label: '跟进时间',
|
||||
component: 'DatePicker',
|
||||
required: true,
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '请选择跟进时间',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'nextFollowTime',
|
||||
label: '下次跟进',
|
||||
component: 'DatePicker',
|
||||
colProps: { span: 24 },
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '请选择下次跟进时间',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'content',
|
||||
label: '说明',
|
||||
component: 'Input',
|
||||
colProps: { span: 24 },
|
||||
componentProps: {
|
||||
placeholder: '请输入说明',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'filePath',
|
||||
label: '图片上传',
|
||||
component: 'Upload',
|
||||
slot: 'imgUpload',
|
||||
colProps: { span: 24 },
|
||||
},
|
||||
];
|
||||
|
||||
const { notification } = useMessage();
|
||||
const baseInfo = ref();
|
||||
const isUpdate = ref(false);
|
||||
const rowId = ref('');
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
|
||||
const [registerForm, { validate, setFieldsValue }] = useForm({
|
||||
labelWidth: 100,
|
||||
schemas: FormSchema,
|
||||
showActionButtonGroup: false,
|
||||
actionColOptions: {
|
||||
span: 23,
|
||||
},
|
||||
});
|
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
setModalProps({ confirmLoading: false, destroyOnClose: true });
|
||||
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
baseInfo.value = data.record;
|
||||
|
||||
if (unref(isUpdate)) {
|
||||
rowId.value = data.id;
|
||||
const record = await getFollowInfo(data.id);
|
||||
setFieldsValue({ ...record });
|
||||
}
|
||||
});
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
values.customerId = baseInfo.value.id;
|
||||
setModalProps({ confirmLoading: true });
|
||||
if (!unref(isUpdate)) {
|
||||
await addFollowCustomer(values);
|
||||
notification.success({
|
||||
message: '新增跟进客户',
|
||||
description: t('成功'),
|
||||
});
|
||||
} else {
|
||||
values.id = rowId.value;
|
||||
await updateFollowCustomer(values);
|
||||
notification.success({
|
||||
message: '编辑跟进客户',
|
||||
description: t('成功'),
|
||||
});
|
||||
}
|
||||
|
||||
closeModal();
|
||||
emit('success');
|
||||
} catch (error) {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.sub-title {
|
||||
font-weight: 700;
|
||||
margin: 0 18px 18px;
|
||||
}
|
||||
|
||||
.title-info span {
|
||||
margin-right: 40px;
|
||||
}
|
||||
|
||||
:deep(.ant-card-body) {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user