mirror of
https://github.com/1Panel-dev/CordysCRM.git
synced 2026-05-14 11:22:10 +08:00
refactor: followDetail
This commit is contained in:
@@ -38,3 +38,4 @@ export const BatchAssignOpenSeaCustomerUrl = '/pool/customer/batch-assign'; //
|
||||
export const AssignOpenSeaCustomerUrl = '/pool/customer/assign'; // 分配公海客户
|
||||
export const GetOpenSeaOptionsUrl = '/pool/customer/options'; // 获取公海选项
|
||||
export const DeleteOpenSeaCustomerUrl = '/pool/customer/delete'; // 删除公海客户
|
||||
export const CancelCustomerFollowPlanUrl = '/customer/follow/plan/cancel'; // 取消客户跟进计划
|
||||
|
||||
@@ -5,9 +5,9 @@ export enum FormDesignKeyEnum {
|
||||
CUSTOMER = 'customer', // 客户
|
||||
CUSTOMER_OPEN_SEA = 'customerOpenSea', // 公海客户
|
||||
CONTACT = 'contact', // 联系人
|
||||
FOLLOW_RECORD_CUSTOMER = 'recordCustomer', // 客户跟进记录
|
||||
FOLLOW_PLAN_CUSTOMER = 'planCustomer', // 客户跟进计划
|
||||
BUSINESS = 'business', // 商机
|
||||
FOLLOW_RECORD_CUSTOMER = 'record', // 客户跟进记录
|
||||
FOLLOW_PLAN_CUSTOMER = 'plan', // 客户跟进计划
|
||||
BUSINESS = 'opportunity', // 商机
|
||||
FOLLOW_RECORD_BUSINESS = 'recordBusiness', // 商机跟进记录
|
||||
FOLLOW_PLAN_BUSINESS = 'planBusiness', // 商机跟进计划
|
||||
PRODUCT = 'product', // 产品
|
||||
|
||||
@@ -25,3 +25,10 @@ export enum StageResultEnum {
|
||||
/** 失败 */
|
||||
FAIL = 'FAIL',
|
||||
}
|
||||
|
||||
export enum OpportunitySearchTypeEnum {
|
||||
ALL = 'ALL',
|
||||
SELF = 'SELF',
|
||||
DEPARTMENT = 'DEPARTMENT',
|
||||
DEAL = 'DEAL',
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
BatchDeleteOpenSeaCustomerUrl,
|
||||
BatchPickOpenSeaCustomerUrl,
|
||||
BatchTransferCustomerUrl,
|
||||
CancelCustomerFollowPlanUrl,
|
||||
DeleteCustomerContactUrl,
|
||||
DeleteCustomerOpenSeaUrl,
|
||||
DeleteCustomerUrl,
|
||||
@@ -154,6 +155,11 @@ export function getCustomerFollowPlanList(data: CustomerFollowPlanTableParams) {
|
||||
return CDR.post<CommonList<CustomerFollowPlanListItem>>({ url: GetCustomerFollowPlanListUrl, data });
|
||||
}
|
||||
|
||||
// 取消客户跟进计划
|
||||
export function cancelCustomerFollowPlan(id: string) {
|
||||
return CDR.get({ url: `${CancelCustomerFollowPlanUrl}/${id}` });
|
||||
}
|
||||
|
||||
// 获取客户跟进计划表单配置
|
||||
export function getCustomerFollowPlanFormConfig() {
|
||||
return CDR.get<FormDesignConfigDetailParams>({ url: GetCustomerFollowPlanFormConfigUrl });
|
||||
|
||||
@@ -355,6 +355,7 @@
|
||||
.n-base-selection-popover {
|
||||
.n-base-selection-tags {
|
||||
padding: 4px;
|
||||
height: 26px;
|
||||
}
|
||||
.n-base-selection-tag-wrapper {
|
||||
padding-bottom: 0;
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
<template>
|
||||
<div :class="`crm-follow-detail ${props.noPadding ? '' : 'p-[24px]'} ${props.wrapperClass}`">
|
||||
<div :class="`crm-follow-detail p-[24px] ${props.wrapperClass}`">
|
||||
<div class="mb-[16px] flex items-center justify-between">
|
||||
<div v-if="props.showTitle" class="font-medium text-[var(--text-n1)]">
|
||||
<div v-if="props.activeType === 'followRecord'" class="font-medium text-[var(--text-n1)]">
|
||||
{{ t('crmFollowRecord.followRecord') }}
|
||||
</div>
|
||||
<CrmTab
|
||||
v-if="props.type === 'followPlan'"
|
||||
v-if="props.activeType === 'followPlan'"
|
||||
v-model:active-tab="activeStatus"
|
||||
no-content
|
||||
:tab-list="statusTabList"
|
||||
type="segment"
|
||||
@change="() => emit('search')"
|
||||
@change="() => loadFollowList()"
|
||||
>
|
||||
</CrmTab>
|
||||
<CrmSearchInput
|
||||
v-if="props.showSearchInput"
|
||||
v-model:value="followKeyword"
|
||||
:placeholder="t('common.byKeywordSearch')"
|
||||
class="!w-[240px]"
|
||||
@search="() => emit('search')"
|
||||
@search="(val) => searchData(val)"
|
||||
/>
|
||||
</div>
|
||||
<n-spin :show="props.loading" class="h-full">
|
||||
<n-spin :show="loading" class="h-full">
|
||||
<FollowRecord
|
||||
v-model:data="data"
|
||||
v-model:keyword="followKeyword"
|
||||
@@ -29,17 +28,17 @@
|
||||
:get-description-fun="getDescriptionFun"
|
||||
key-field="id"
|
||||
:empty-text="
|
||||
props.type === 'followPlan' ? t('crmFollowRecord.noFollowPlan') : t('crmFollowRecord.noFollowRecord')
|
||||
props.activeType === 'followPlan' ? t('crmFollowRecord.noFollowPlan') : t('crmFollowRecord.noFollowRecord')
|
||||
"
|
||||
@reach-bottom="() => emit('reachBottom')"
|
||||
@reach-bottom="handleReachBottom"
|
||||
>
|
||||
<template #headerAction="{ item }">
|
||||
<div class="flex items-center gap-[12px]">
|
||||
<n-button
|
||||
v-if="props.type === 'followPlan' && item.status !== CustomerFollowPlanStatusEnum.CANCELLED"
|
||||
v-if="props.activeType === 'followPlan' && item.status !== CustomerFollowPlanStatusEnum.CANCELLED"
|
||||
type="primary"
|
||||
text
|
||||
@click="cancelPlan(item)"
|
||||
@click="handleCancelPlan(item)"
|
||||
>
|
||||
{{ t('common.cancelPlan') }}
|
||||
</n-button>
|
||||
@@ -60,6 +59,12 @@
|
||||
</template>
|
||||
</FollowRecord>
|
||||
</n-spin>
|
||||
<CrmFormCreateDrawer
|
||||
v-model:visible="formDrawerVisible"
|
||||
:form-key="realFormKey"
|
||||
:source-id="realFollowSourceId"
|
||||
@saved="() => loadFollowList()"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -68,52 +73,50 @@
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { CustomerFollowPlanStatusEnum } from '@lib/shared/enums/customerEnum';
|
||||
import { FormDesignKeyEnum } from '@lib/shared/enums/formDesignEnum';
|
||||
import type { FollowDetailItem } from '@lib/shared/models/customer';
|
||||
|
||||
import type { Description } from '@/components/pure/crm-detail-card/index.vue';
|
||||
import CrmSearchInput from '@/components/pure/crm-search-input/index.vue';
|
||||
import CrmTab from '@/components/pure/crm-tab/index.vue';
|
||||
import CrmFormCreateDrawer from '@/components/business/crm-form-create-drawer/index.vue';
|
||||
import FollowRecord from './followRecord.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import useFollowApi, { type followEnumType } from './useFollowApi';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
export type ActiveType = 'followPlan' | 'followRecord';
|
||||
|
||||
interface FollowDetailProps {
|
||||
type: string; // 跟进记录|跟进计划
|
||||
showSearchInput?: boolean; // 是否显示检索框
|
||||
showTitle?: boolean; // 是否显示标题
|
||||
noPadding?: boolean; // 无边距
|
||||
activeType: 'followRecord' | 'followPlan'; // 跟进记录|跟进计划
|
||||
followApiKey: followEnumType; // 跟进计划apiKey
|
||||
|
||||
virtualScrollHeight?: string; // 虚拟高度
|
||||
wrapperClass?: string;
|
||||
loading: boolean;
|
||||
sourceId: string; // 资源id
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<FollowDetailProps>(), {
|
||||
showSearchInput: true,
|
||||
noPadding: false,
|
||||
});
|
||||
const props = defineProps<FollowDetailProps>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search'): void;
|
||||
(e: 'handleEdit', item: FollowDetailItem): void;
|
||||
(e: 'cancelPlan', item: FollowDetailItem): void;
|
||||
(e: 'reachBottom'): void;
|
||||
}>();
|
||||
const formDrawerVisible = ref(false);
|
||||
|
||||
const data = defineModel<FollowDetailItem[]>('data', {
|
||||
required: true,
|
||||
default: [],
|
||||
});
|
||||
|
||||
const activeStatus = defineModel<string | number>('activeStatus', {
|
||||
default: '',
|
||||
});
|
||||
|
||||
const innerKeyword = defineModel<string>('keyword', {
|
||||
default: '',
|
||||
const {
|
||||
data,
|
||||
loading,
|
||||
handleReachBottom,
|
||||
searchData,
|
||||
activeStatus,
|
||||
loadFollowList,
|
||||
handleCancelPlan,
|
||||
followKeyword,
|
||||
followFormKeyMap,
|
||||
} = useFollowApi({
|
||||
type: toRef(props, 'activeType'),
|
||||
followApiKey: props.followApiKey,
|
||||
sourceId: toRef(props, 'sourceId'),
|
||||
});
|
||||
|
||||
// 跟进计划状态
|
||||
@@ -182,26 +185,19 @@
|
||||
})) || []) as Description[];
|
||||
}
|
||||
|
||||
// 取消计划
|
||||
function cancelPlan(item: FollowDetailItem) {
|
||||
emit('cancelPlan', item);
|
||||
}
|
||||
// 编辑记录或计划
|
||||
const realFormKey = ref<FormDesignKeyEnum>(FormDesignKeyEnum.FOLLOW_RECORD_CUSTOMER);
|
||||
const realFollowSourceId = ref<string | undefined>('');
|
||||
|
||||
// 编辑记录
|
||||
function handleEdit(item: FollowDetailItem) {
|
||||
emit('handleEdit', item);
|
||||
realFormKey.value = followFormKeyMap[props.followApiKey as keyof typeof followFormKeyMap][props.activeType];
|
||||
realFollowSourceId.value = item.id;
|
||||
formDrawerVisible.value = true;
|
||||
}
|
||||
|
||||
const followKeyword = ref<string>('');
|
||||
|
||||
watch(
|
||||
() => innerKeyword.value,
|
||||
(val) => {
|
||||
if (val && !props.showSearchInput) {
|
||||
followKeyword.value = val;
|
||||
}
|
||||
}
|
||||
);
|
||||
onBeforeMount(() => {
|
||||
loadFollowList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
import { ref } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
|
||||
import { CustomerFollowPlanStatusEnum } from '@lib/shared/enums/customerEnum';
|
||||
import { FormDesignKeyEnum } from '@lib/shared/enums/formDesignEnum';
|
||||
import type { CommonList } from '@lib/shared/models/common';
|
||||
import type { FollowDetailItem } from '@lib/shared/models/customer';
|
||||
|
||||
import { cancelClueFollowPlan, getClueFollowPlanList, getClueFollowRecordList } from '@/api/modules/clue/index';
|
||||
import {
|
||||
cancelCustomerFollowPlan,
|
||||
getCustomerFollowPlanList,
|
||||
getCustomerFollowRecordList,
|
||||
} from '@/api/modules/customer/index';
|
||||
import { cancelOptFollowPlan, getOptFollowPlanList, getOptFollowRecordList } from '@/api/modules/opportunity';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
export type followEnumType =
|
||||
| typeof FormDesignKeyEnum.CUSTOMER
|
||||
| typeof FormDesignKeyEnum.BUSINESS
|
||||
| typeof FormDesignKeyEnum.CLUE;
|
||||
|
||||
type FollowApiMapType = Record<
|
||||
followEnumType,
|
||||
{
|
||||
list: {
|
||||
followRecord: (params: any) => Promise<CommonList<FollowDetailItem>>;
|
||||
followPlan: (params: any) => Promise<CommonList<FollowDetailItem>>;
|
||||
};
|
||||
cancel: {
|
||||
followPlan: typeof cancelOptFollowPlan;
|
||||
};
|
||||
}
|
||||
>;
|
||||
|
||||
const followApiMap: FollowApiMapType = {
|
||||
[FormDesignKeyEnum.BUSINESS]: {
|
||||
list: {
|
||||
followRecord: getOptFollowRecordList,
|
||||
followPlan: getOptFollowPlanList,
|
||||
},
|
||||
cancel: {
|
||||
followPlan: cancelOptFollowPlan,
|
||||
},
|
||||
},
|
||||
[FormDesignKeyEnum.CUSTOMER]: {
|
||||
list: {
|
||||
followRecord: getCustomerFollowRecordList,
|
||||
followPlan: getCustomerFollowPlanList,
|
||||
},
|
||||
cancel: {
|
||||
followPlan: cancelCustomerFollowPlan,
|
||||
},
|
||||
},
|
||||
[FormDesignKeyEnum.CLUE]: {
|
||||
list: {
|
||||
followRecord: getClueFollowRecordList,
|
||||
followPlan: getClueFollowPlanList,
|
||||
},
|
||||
cancel: {
|
||||
followPlan: cancelClueFollowPlan,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const followFormKeyMap: Record<
|
||||
followEnumType,
|
||||
{
|
||||
followRecord: FormDesignKeyEnum;
|
||||
followPlan: FormDesignKeyEnum;
|
||||
}
|
||||
> = {
|
||||
[FormDesignKeyEnum.CUSTOMER]: {
|
||||
followRecord: FormDesignKeyEnum.FOLLOW_RECORD_CUSTOMER, // 客户跟进记录
|
||||
followPlan: FormDesignKeyEnum.FOLLOW_PLAN_CUSTOMER, // 客户跟进计划
|
||||
},
|
||||
[FormDesignKeyEnum.BUSINESS]: {
|
||||
followRecord: FormDesignKeyEnum.FOLLOW_RECORD_BUSINESS, // 商机跟进记录
|
||||
followPlan: FormDesignKeyEnum.FOLLOW_PLAN_BUSINESS, // 商机跟进计划
|
||||
},
|
||||
[FormDesignKeyEnum.CLUE]: {
|
||||
followRecord: FormDesignKeyEnum.FOLLOW_RECORD_CLUE, // 线索跟进记录
|
||||
followPlan: FormDesignKeyEnum.FOLLOW_PLAN_CLUE, // 线索跟进计划
|
||||
},
|
||||
};
|
||||
|
||||
export default function useFollowApi(followProps: {
|
||||
followApiKey: (typeof FormDesignKeyEnum)['CUSTOMER' | 'BUSINESS' | 'CLUE'];
|
||||
type: Ref<'followRecord' | 'followPlan'>;
|
||||
sourceId: Ref<string>;
|
||||
}) {
|
||||
const { t } = useI18n();
|
||||
|
||||
const Message = useMessage();
|
||||
|
||||
const data = ref<FollowDetailItem[]>([]);
|
||||
|
||||
const activeStatus = ref<CustomerFollowPlanStatusEnum>(CustomerFollowPlanStatusEnum.ALL);
|
||||
|
||||
const followKeyword = ref<string>('');
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
const pageNation = ref({
|
||||
total: 0,
|
||||
pageSize: 10,
|
||||
current: 1,
|
||||
});
|
||||
const { type, followApiKey, sourceId } = followProps;
|
||||
|
||||
const apis = followApiMap[followApiKey];
|
||||
|
||||
async function loadFollowList() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const params = {
|
||||
sourceId: sourceId.value,
|
||||
current: pageNation.value.current || 1,
|
||||
pageSize: pageNation.value.pageSize,
|
||||
keyword: followKeyword.value,
|
||||
...(type.value === 'followPlan' && { status: activeStatus.value }),
|
||||
};
|
||||
|
||||
const res = await apis.list[type.value](params);
|
||||
data.value = res.list;
|
||||
pageNation.value.total = res.total;
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 取消计划
|
||||
async function handleCancelPlan(item: FollowDetailItem) {
|
||||
try {
|
||||
await apis.cancel.followPlan(item.id);
|
||||
Message.success(t('common.cancelSuccess'));
|
||||
loadFollowList();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
function handleReachBottom() {
|
||||
pageNation.value.current += 1;
|
||||
if (pageNation.value.current > Math.ceil(pageNation.value.total / pageNation.value.pageSize)) {
|
||||
return;
|
||||
}
|
||||
loadFollowList();
|
||||
}
|
||||
|
||||
function searchData(keyword: string) {
|
||||
followKeyword.value = keyword;
|
||||
loadFollowList();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => type.value,
|
||||
(val) => {
|
||||
if (['followPlan', 'followRecord'].includes(val)) {
|
||||
loadFollowList();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
data,
|
||||
loading,
|
||||
handleReachBottom,
|
||||
followKeyword,
|
||||
loadFollowList,
|
||||
handleCancelPlan,
|
||||
followFormKeyMap,
|
||||
searchData,
|
||||
activeStatus,
|
||||
};
|
||||
}
|
||||
@@ -2,12 +2,12 @@ export default {
|
||||
'crmFormCreate.drawer.clue': 'Clue',
|
||||
'crmFormCreate.drawer.customer': 'Customer',
|
||||
'crmFormCreate.drawer.contact': 'Contact',
|
||||
'crmFormCreate.drawer.recordCustomer': 'Follow-up record',
|
||||
'crmFormCreate.drawer.planCustomer': 'Follow-up plan',
|
||||
'crmFormCreate.drawer.record': 'Follow-up record',
|
||||
'crmFormCreate.drawer.plan': 'Follow-up plan',
|
||||
'crmFormCreate.drawer.recordBusiness': 'Follow-up record',
|
||||
'crmFormCreate.drawer.planBusiness': 'Follow-up plan',
|
||||
'crmFormCreate.drawer.recordClue': 'Follow-up record',
|
||||
'crmFormCreate.drawer.planClue': 'Follow-up plan',
|
||||
'crmFormCreate.drawer.business': 'Business',
|
||||
'crmFormCreate.drawer.opportunity': 'Business',
|
||||
'crmFormCreate.drawer.product': 'Product',
|
||||
};
|
||||
|
||||
@@ -2,12 +2,12 @@ export default {
|
||||
'crmFormCreate.drawer.clue': '线索',
|
||||
'crmFormCreate.drawer.customer': '客户',
|
||||
'crmFormCreate.drawer.contact': '联系人',
|
||||
'crmFormCreate.drawer.recordCustomer': '跟进记录',
|
||||
'crmFormCreate.drawer.planCustomer': '跟进计划',
|
||||
'crmFormCreate.drawer.record': '跟进记录',
|
||||
'crmFormCreate.drawer.plan': '跟进计划',
|
||||
'crmFormCreate.drawer.recordBusiness': '跟进记录',
|
||||
'crmFormCreate.drawer.planBusiness': '跟进计划',
|
||||
'crmFormCreate.drawer.recordClue': '跟进记录',
|
||||
'crmFormCreate.drawer.planClue': '跟进计划',
|
||||
'crmFormCreate.drawer.business': '商机',
|
||||
'crmFormCreate.drawer.opportunity': '商机',
|
||||
'crmFormCreate.drawer.product': '产品',
|
||||
};
|
||||
|
||||
@@ -29,12 +29,12 @@
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'select', key: string): void;
|
||||
(e: 'select', key: string, done?: () => void): void;
|
||||
(e: 'cancel'): void;
|
||||
}>();
|
||||
|
||||
function handleSelect(key: string) {
|
||||
emit('select', key);
|
||||
function handleSelect(key: string, done?: () => void) {
|
||||
emit('select', key, done);
|
||||
}
|
||||
|
||||
function handleMoreSelect(item: ActionsItem) {
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'buttonSelect', key: string): void;
|
||||
(e: 'buttonSelect', key: string, done?: () => void): void;
|
||||
}>();
|
||||
|
||||
const showDrawer = defineModel<boolean>('show', {
|
||||
@@ -118,7 +118,7 @@
|
||||
const realFormKey = ref<FormDesignKeyEnum>(props.formKey);
|
||||
const realSourceId = ref<string | undefined>(''); // 编辑时传入
|
||||
|
||||
function handleButtonClick(key: string) {
|
||||
function handleButtonClick(key: string, done?: () => void) {
|
||||
switch (key) {
|
||||
case 'addContract':
|
||||
realFormKey.value = FormDesignKeyEnum.CONTACT;
|
||||
@@ -152,7 +152,7 @@
|
||||
default:
|
||||
break;
|
||||
}
|
||||
emit('buttonSelect', key);
|
||||
emit('buttonSelect', key, done);
|
||||
}
|
||||
|
||||
watch(
|
||||
|
||||
@@ -1,56 +1,69 @@
|
||||
<template>
|
||||
<div class="bg-[var(--text-n10)] p-[16px]">
|
||||
<WorkflowStep v-model:status="currentStatus" :workflow-list="workflowList">
|
||||
<template #action="{ currentStatusIndex }">
|
||||
<n-button
|
||||
v-if="props.showErrorBtn"
|
||||
type="error"
|
||||
ghost
|
||||
class="n-btn-outline-error mr-[12px]"
|
||||
@click="handleUpdateStatus(currentStatusIndex, true)"
|
||||
>
|
||||
{{ t('common.followFailed') }}
|
||||
</n-button>
|
||||
<n-button type="primary" :loading="updateStageLoading" @click="handleUpdateStatus(currentStatusIndex)">
|
||||
{{ t('common.updateToCurrentProgress') }}
|
||||
</n-button>
|
||||
</template>
|
||||
</WorkflowStep>
|
||||
<n-spin :show="updateStageLoading">
|
||||
<WorkflowStep v-model:status="currentStatus" :workflow-list="workflowList">
|
||||
<template #action="{ currentStatusIndex }">
|
||||
<n-button
|
||||
v-if="props.showErrorBtn"
|
||||
type="error"
|
||||
ghost
|
||||
class="n-btn-outline-error mr-[12px]"
|
||||
@click="handleUpdateStatus(currentStatusIndex, true)"
|
||||
>
|
||||
{{ t('common.followFailed') }}
|
||||
</n-button>
|
||||
<n-button type="primary" @click="handleUpdateStatus(currentStatusIndex)">
|
||||
{{ t('common.updateToCurrentProgress') }}
|
||||
</n-button>
|
||||
</template>
|
||||
</WorkflowStep>
|
||||
|
||||
<CrmModal
|
||||
v-model:show="updateStatusModal"
|
||||
:title="t('common.complete')"
|
||||
:ok-loading="loading"
|
||||
size="small"
|
||||
@confirm="handleConfirm"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<n-form ref="formRef" :model="form" :rules="rules" label-placement="left" require-mark-placement="left">
|
||||
<n-form-item
|
||||
require-mark-placement="left"
|
||||
label-placement="left"
|
||||
path="stage"
|
||||
:show-feedback="false"
|
||||
:label="t('common.result')"
|
||||
>
|
||||
<n-radio-group v-model:value="form.stage" name="radiogroup">
|
||||
<n-space>
|
||||
<n-radio key="success" :value="StageResultEnum.SUCCESS">
|
||||
{{ t('common.success') }}
|
||||
</n-radio>
|
||||
<n-radio key="fail" :value="StageResultEnum.FAIL">
|
||||
{{ t('common.fail') }}
|
||||
</n-radio>
|
||||
</n-space>
|
||||
</n-radio-group>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</CrmModal>
|
||||
<CrmModal
|
||||
v-model:show="updateStatusModal"
|
||||
:title="t('common.complete')"
|
||||
:ok-loading="updateStageLoading"
|
||||
size="small"
|
||||
@confirm="handleConfirm"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<n-form ref="formRef" :model="form" :rules="rules" label-placement="left" require-mark-placement="left">
|
||||
<n-form-item
|
||||
require-mark-placement="left"
|
||||
label-placement="left"
|
||||
path="stage"
|
||||
:show-feedback="false"
|
||||
:label="t('common.result')"
|
||||
>
|
||||
<n-radio-group v-model:value="form.stage" name="radiogroup">
|
||||
<n-space>
|
||||
<n-radio key="success" :value="StageResultEnum.SUCCESS">
|
||||
{{ t('common.success') }}
|
||||
</n-radio>
|
||||
<n-radio key="fail" :value="StageResultEnum.FAIL">
|
||||
{{ t('common.fail') }}
|
||||
</n-radio>
|
||||
</n-space>
|
||||
</n-radio-group>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</CrmModal>
|
||||
</n-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { FormInst, FormRules, NButton, NForm, NFormItem, NRadio, NRadioGroup, NSpace, SelectOption } from 'naive-ui';
|
||||
import {
|
||||
FormInst,
|
||||
FormRules,
|
||||
NButton,
|
||||
NForm,
|
||||
NFormItem,
|
||||
NRadio,
|
||||
NRadioGroup,
|
||||
NSpace,
|
||||
NSpin,
|
||||
SelectOption,
|
||||
} from 'naive-ui';
|
||||
|
||||
import { StageResultEnum } from '@lib/shared/enums/opportunityEnum';
|
||||
|
||||
@@ -95,36 +108,27 @@
|
||||
}
|
||||
|
||||
const formRef = ref<FormInst | null>(null);
|
||||
|
||||
async function executeWithLoading(cb: () => Promise<void>, loadingRef: Ref<boolean>) {
|
||||
try {
|
||||
loadingRef.value = true;
|
||||
await cb();
|
||||
emit('loadDetail');
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
loadingRef.value = false;
|
||||
}
|
||||
}
|
||||
const updateStageLoading = ref(false);
|
||||
|
||||
async function handleSave(stage: string) {
|
||||
try {
|
||||
updateStageLoading.value = true;
|
||||
if (props.updateApi) {
|
||||
await props.updateApi({
|
||||
id: props.sourceId,
|
||||
stage,
|
||||
});
|
||||
emit('loadDetail');
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
updateStageLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新状态
|
||||
const updateStageLoading = ref(false);
|
||||
async function handleUpdateStatus(currentStatusIndex: number, isError = false) {
|
||||
if (props.showConfirmStatus && currentStatusIndex === props.workflowList.length - 2) {
|
||||
updateStatusModal.value = true;
|
||||
@@ -132,15 +136,14 @@
|
||||
}
|
||||
|
||||
const nextStage = isError ? StageResultEnum.FAIL : props.workflowList[currentStatusIndex + 1]?.value;
|
||||
await executeWithLoading(() => handleSave(nextStage as string), updateStageLoading);
|
||||
await handleSave(nextStage as string);
|
||||
}
|
||||
|
||||
// 确认更新
|
||||
const loading = ref(false);
|
||||
async function handleConfirm() {
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (!errors) {
|
||||
await executeWithLoading(() => handleSave(form.value.stage), loading);
|
||||
await handleSave(form.value.stage);
|
||||
handleCancel();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
<slot :name="item.slotName" :item="item" :index="index">
|
||||
<template v-if="item.popConfirmProps">
|
||||
<CrmPopConfirm
|
||||
:show="item.popShow as boolean"
|
||||
v-model:show="popShow"
|
||||
placement="bottom-end"
|
||||
v-bind="item.popConfirmProps"
|
||||
@confirm="emit('select', `pop-${item.key}` as string)"
|
||||
@confirm="emit('select', `pop-${item.key}` as string, cancel)"
|
||||
@cancel="emit('cancel')"
|
||||
>
|
||||
<n-button
|
||||
@@ -15,7 +15,7 @@
|
||||
v-bind="item"
|
||||
type="primary"
|
||||
:class="item.text === false ? '' : '!p-0'"
|
||||
@click="() => (item.popShow = true)"
|
||||
@click="() => (popShow = true)"
|
||||
>
|
||||
{{ item.label }}
|
||||
</n-button>
|
||||
@@ -52,10 +52,16 @@
|
||||
notShowDivider?: boolean; // 不显示分割线
|
||||
}>();
|
||||
|
||||
const popShow = ref(false);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'select', key: string): void;
|
||||
(e: 'select', key: string, done?: () => void): void;
|
||||
(e: 'cancel'): void;
|
||||
}>();
|
||||
|
||||
function cancel() {
|
||||
popShow.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
@@ -29,18 +29,12 @@
|
||||
<template #right>
|
||||
<FollowDetail
|
||||
v-if="['followRecord', 'followPlan'].includes(activeTab)"
|
||||
v-model:data="followList"
|
||||
v-model:active-status="activeStatus"
|
||||
class="mt-[16px]"
|
||||
:loading="followLoading"
|
||||
:show-title="activeTab === 'followRecord'"
|
||||
:type="activeTab"
|
||||
wrapper-class="h-[calc(100vh-258px)]"
|
||||
virtual-scroll-height="calc(100vh - 290px)"
|
||||
@reach-bottom="handleReachBottom"
|
||||
@search="() => loadFollowList()"
|
||||
@cancel-plan="handleCancelPlan"
|
||||
@handle-edit="handleEditFollow"
|
||||
:active-type="(activeTab as 'followRecord'| 'followPlan')"
|
||||
wrapper-class="h-[calc(100vh-290px)]"
|
||||
virtual-scroll-height="calc(100vh - 322px)"
|
||||
:follow-api-key="FormDesignKeyEnum.CLUE"
|
||||
:source-id="sourceId"
|
||||
/>
|
||||
|
||||
<!-- TODO getUserList接口换了 -->
|
||||
|
||||
@@ -20,17 +20,12 @@
|
||||
<template #right>
|
||||
<FollowDetail
|
||||
v-if="['followRecord', 'followPlan'].includes(activeTab)"
|
||||
v-model:data="followList"
|
||||
v-model:active-status="activeStatus"
|
||||
class="mt-[16px]"
|
||||
:loading="followLoading"
|
||||
:show-title="activeTab === 'followRecord'"
|
||||
:type="activeTab"
|
||||
:active-type="(activeTab as 'followRecord'| 'followPlan')"
|
||||
wrapper-class="h-[calc(100vh-162px)]"
|
||||
virtual-scroll-height="calc(100vh - 258px)"
|
||||
@reach-bottom="handleReachBottom"
|
||||
@search="() => loadFollowList()"
|
||||
@handle-edit="handleEditFollow"
|
||||
:follow-api-key="FormDesignKeyEnum.CLUE"
|
||||
:source-id="sourceId"
|
||||
/>
|
||||
|
||||
<!-- TODO getUserList接口换了 -->
|
||||
|
||||
@@ -28,6 +28,15 @@
|
||||
<div class="mt-[16px]">
|
||||
<div v-if="activeTab === 'overview'" class="mt-[16px] h-[100px] bg-[var(--text-n10)]"></div>
|
||||
<ContactTable v-else-if="activeTab === 'contact'" class="h-[calc(100vh-161px)]" is-overview />
|
||||
<FollowDetail
|
||||
v-else-if="['followRecord', 'followPlan'].includes(activeTab)"
|
||||
class="mt-[16px]"
|
||||
:active-type="(activeTab as 'followRecord'| 'followPlan')"
|
||||
wrapper-class="h-[calc(100vh-162px)]"
|
||||
virtual-scroll-height="calc(100vh - 194px)"
|
||||
:follow-api-key="FormDesignKeyEnum.CUSTOMER"
|
||||
:source-id="props.sourceId"
|
||||
/>
|
||||
<HeaderTable
|
||||
v-else-if="activeTab === 'headRecord'"
|
||||
class="h-[calc(100vh-161px)]"
|
||||
@@ -48,6 +57,7 @@
|
||||
import { ModuleConfigEnum } from '@lib/shared/enums/moduleEnum';
|
||||
|
||||
import type { ActionsItem } from '@/components/pure/crm-more-action/type';
|
||||
import FollowDetail from '@/components/business/crm-follow-detail/index.vue';
|
||||
import ContactTable from '@/components/business/crm-form-create-table/contactTable.vue';
|
||||
import HeaderTable from '@/components/business/crm-form-create-table/headerTable.vue';
|
||||
import CrmFormDescription from '@/components/business/crm-form-description/index.vue';
|
||||
|
||||
@@ -21,39 +21,28 @@
|
||||
class="mb-[16px]"
|
||||
:workflow-list="workflowList"
|
||||
:source-id="sourceId"
|
||||
:save-api="updateOptStage"
|
||||
:update-api="updateOptStage"
|
||||
@load-detail="loadStageDetail"
|
||||
/>
|
||||
</template>
|
||||
<template #right>
|
||||
<FollowDetail
|
||||
v-if="['followRecord', 'followPlan'].includes(activeTab)"
|
||||
v-model:data="followList"
|
||||
v-model:active-status="activeStatus"
|
||||
class="mt-[16px]"
|
||||
:loading="followLoading"
|
||||
:show-title="activeTab === 'followRecord'"
|
||||
:type="activeTab"
|
||||
wrapper-class="h-[calc(100vh-162px)]"
|
||||
virtual-scroll-height="calc(100vh - 194px)"
|
||||
@reach-bottom="handleReachBottom"
|
||||
@search="() => loadFollowList()"
|
||||
@cancel-plan="handleCancelPlan"
|
||||
@handle-edit="handleEditFollow"
|
||||
:active-type="(activeTab as 'followRecord'| 'followPlan')"
|
||||
wrapper-class="h-[calc(100vh-290px)]"
|
||||
virtual-scroll-height="calc(100vh - 322px)"
|
||||
:follow-api-key="FormDesignKeyEnum.BUSINESS"
|
||||
:source-id="sourceId"
|
||||
/>
|
||||
|
||||
<HeaderTable
|
||||
v-if="activeTab === 'headRecord'"
|
||||
class="mt-[16px] h-[calc(100vh-161px)]"
|
||||
class="mt-[16px] h-[calc(100vh-290px)]"
|
||||
:source-id="sourceId"
|
||||
:load-list-api="getUserList"
|
||||
/>
|
||||
|
||||
<CrmFormCreateDrawer
|
||||
v-model:visible="formDrawerVisible"
|
||||
:form-key="realFormKey"
|
||||
:source-id="realFollowSourceId"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #transferPopContent>
|
||||
@@ -65,15 +54,13 @@
|
||||
<script setup lang="ts">
|
||||
import { SelectOption, useMessage } from 'naive-ui';
|
||||
|
||||
import { CustomerFollowPlanStatusEnum } from '@lib/shared/enums/customerEnum';
|
||||
import { FormDesignKeyEnum } from '@lib/shared/enums/formDesignEnum';
|
||||
import { OpportunityStatusEnum, StageResultEnum } from '@lib/shared/enums/opportunityEnum';
|
||||
import type { FollowDetailItem, TransferParams } from '@lib/shared/models/customer';
|
||||
import type { TransferParams } from '@lib/shared/models/customer';
|
||||
import type { OpportunityItem } from '@lib/shared/models/opportunity';
|
||||
|
||||
import type { ActionsItem } from '@/components/pure/crm-more-action/type';
|
||||
import FollowDetail from '@/components/business/crm-follow-detail/index.vue';
|
||||
import CrmFormCreateDrawer from '@/components/business/crm-form-create-drawer/index.vue';
|
||||
import HeaderTable from '@/components/business/crm-form-create-table/headerTable.vue';
|
||||
import CrmFormDescription from '@/components/business/crm-form-description/index.vue';
|
||||
import CrmOverviewDrawer from '@/components/business/crm-overview-drawer/index.vue';
|
||||
@@ -81,15 +68,7 @@
|
||||
import TransferForm from '@/components/business/crm-transfer-modal/transferForm.vue';
|
||||
import CrmWorkflowCard from '@/components/business/crm-workflow-card/index.vue';
|
||||
|
||||
import {
|
||||
cancelOptFollowPlan,
|
||||
deleteOpt,
|
||||
getOptFollowPlanList,
|
||||
getOptFollowRecordList,
|
||||
getOptStageDetail,
|
||||
transferOpt,
|
||||
updateOptStage,
|
||||
} from '@/api/modules/opportunity';
|
||||
import { deleteOpt, getOptStageDetail, transferOpt, updateOptStage } from '@/api/modules/opportunity';
|
||||
import { getUserList } from '@/api/modules/system/org';
|
||||
import { defaultTransferForm } from '@/config/opportunity';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
@@ -221,81 +200,9 @@
|
||||
return [...props.baseSteps, endStage.value];
|
||||
});
|
||||
|
||||
const pageNation = ref({
|
||||
total: 0,
|
||||
pageSize: 10,
|
||||
current: 1,
|
||||
});
|
||||
|
||||
const followList = ref<FollowDetailItem[]>([]);
|
||||
const activeStatus = ref(CustomerFollowPlanStatusEnum.ALL);
|
||||
const followLoading = ref<boolean>(false);
|
||||
|
||||
async function loadFollowList() {
|
||||
try {
|
||||
followLoading.value = true;
|
||||
const params = {
|
||||
sourceId: sourceId.value,
|
||||
current: pageNation.value.current || 1,
|
||||
pageSize: pageNation.value.pageSize,
|
||||
};
|
||||
|
||||
let res;
|
||||
if (activeTab.value === 'followPlan') {
|
||||
res = await getOptFollowPlanList({
|
||||
...params,
|
||||
status: activeStatus.value,
|
||||
});
|
||||
} else {
|
||||
res = await getOptFollowRecordList(params);
|
||||
}
|
||||
|
||||
followList.value = res.list || [];
|
||||
pageNation.value.total = res.total;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
followLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleReachBottom() {
|
||||
pageNation.value.current += 1;
|
||||
if (pageNation.value.current > Math.ceil(pageNation.value.total / pageNation.value.pageSize)) {
|
||||
return;
|
||||
}
|
||||
loadFollowList();
|
||||
}
|
||||
|
||||
const formDrawerVisible = ref(false);
|
||||
const realFormKey = ref<FormDesignKeyEnum>(FormDesignKeyEnum.FOLLOW_RECORD_BUSINESS);
|
||||
|
||||
// 取消计划
|
||||
async function handleCancelPlan(item: FollowDetailItem) {
|
||||
try {
|
||||
await cancelOptFollowPlan(item.id);
|
||||
Message.success(t('common.cancelSuccess'));
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑跟进内容
|
||||
const realFollowSourceId = ref<string | undefined>('');
|
||||
function handleEditFollow(item: FollowDetailItem) {
|
||||
realFormKey.value =
|
||||
activeTab.value === 'followRecord'
|
||||
? FormDesignKeyEnum.FOLLOW_RECORD_BUSINESS
|
||||
: FormDesignKeyEnum.FOLLOW_PLAN_BUSINESS;
|
||||
realFollowSourceId.value = item.id;
|
||||
formDrawerVisible.value = true;
|
||||
}
|
||||
|
||||
// 转移
|
||||
const transferFormRef = ref<InstanceType<typeof TransferForm>>();
|
||||
function handleTransfer() {
|
||||
function handleTransfer(done?: () => void) {
|
||||
transferFormRef.value?.formRef?.validate(async (error) => {
|
||||
if (!error) {
|
||||
try {
|
||||
@@ -307,6 +214,7 @@
|
||||
Message.success(t('common.transferSuccess'));
|
||||
transferForm.value = { ...defaultTransferForm };
|
||||
showOptOverviewDrawer.value = false;
|
||||
done?.();
|
||||
emit('refresh');
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
@@ -349,10 +257,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
function handleSelect(key: string) {
|
||||
function handleSelect(key: string, done?: () => void) {
|
||||
switch (key) {
|
||||
case 'pop-transfer':
|
||||
handleTransfer();
|
||||
handleTransfer(done);
|
||||
break;
|
||||
case 'delete':
|
||||
handleDelete();
|
||||
@@ -362,20 +270,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => activeTab.value,
|
||||
(val) => {
|
||||
if (['followPlan', 'followRecord'].includes(val)) {
|
||||
loadFollowList();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => showOptOverviewDrawer.value,
|
||||
(val) => {
|
||||
if (val) {
|
||||
loadFollowList();
|
||||
loadStageDetail();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<CrmCard no-content-padding hide-footer auto-height class="mb-[16px]">
|
||||
<CrmTab v-model:active-tab="activeTab" no-content :tab-list="tabList" type="line" />
|
||||
<CrmTab v-model:active-tab="activeTab" no-content :tab-list="tabList" type="line" @change="() => searchData()" />
|
||||
</CrmCard>
|
||||
<CrmCard hide-footer>
|
||||
<CrmTable
|
||||
@@ -72,7 +72,7 @@
|
||||
import { DataTableRowKey, NButton, SelectOption, TabPaneProps, useMessage } from 'naive-ui';
|
||||
|
||||
import { FieldTypeEnum, FormDesignKeyEnum } from '@lib/shared/enums/formDesignEnum';
|
||||
import { OpportunityStatusEnum, StageResultEnum } from '@lib/shared/enums/opportunityEnum';
|
||||
import { OpportunitySearchTypeEnum, OpportunityStatusEnum, StageResultEnum } from '@lib/shared/enums/opportunityEnum';
|
||||
import type { TransferParams } from '@lib/shared/models/customer/index';
|
||||
import type { OpportunityItem } from '@lib/shared/models/opportunity';
|
||||
|
||||
@@ -123,25 +123,25 @@
|
||||
};
|
||||
|
||||
const tableRefreshId = ref(0);
|
||||
const activeTab = ref('all');
|
||||
const activeTab = ref(OpportunitySearchTypeEnum.ALL);
|
||||
|
||||
const tabList = computed<TabPaneProps[]>(() => {
|
||||
// TODO 根据不同的用户展示tab
|
||||
return [
|
||||
{
|
||||
name: 'all',
|
||||
name: OpportunitySearchTypeEnum.ALL,
|
||||
tab: t('opportunity.allOpportunities'),
|
||||
},
|
||||
{
|
||||
name: 'my',
|
||||
name: OpportunitySearchTypeEnum.SELF,
|
||||
tab: t('opportunity.myOpportunities'),
|
||||
},
|
||||
{
|
||||
name: 'department',
|
||||
name: OpportunitySearchTypeEnum.DEPARTMENT,
|
||||
tab: t('opportunity.departmentOpportunities'),
|
||||
},
|
||||
{
|
||||
name: 'converted',
|
||||
name: OpportunitySearchTypeEnum.DEAL,
|
||||
tab: t('opportunity.convertedOpportunities'),
|
||||
},
|
||||
];
|
||||
@@ -192,7 +192,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
const showOverviewDrawer = ref<boolean>(true);
|
||||
const showOverviewDrawer = ref<boolean>(false);
|
||||
const activeSourceId = ref('');
|
||||
const activeOpportunity = ref<OpportunityItem>();
|
||||
const formCreateDrawerVisible = ref(false);
|
||||
@@ -250,7 +250,7 @@
|
||||
const transferLoading = ref(false);
|
||||
|
||||
// 转移
|
||||
function handleTransfer(row: OpportunityItem) {
|
||||
function handleTransfer(row: OpportunityItem, done?: () => void) {
|
||||
transferFormRef.value?.formRef?.validate(async (error) => {
|
||||
if (!error) {
|
||||
try {
|
||||
@@ -262,6 +262,7 @@
|
||||
Message.success(t('common.transferSuccess'));
|
||||
transferForm.value = { ...defaultTransferForm };
|
||||
tableRefreshId.value += 1;
|
||||
done?.();
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(e);
|
||||
@@ -272,7 +273,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
function handleActionSelect(row: OpportunityItem, actionKey: string) {
|
||||
function handleActionSelect(row: OpportunityItem, actionKey: string, done?: () => void) {
|
||||
switch (actionKey) {
|
||||
case 'edit':
|
||||
handleEdit(row.id);
|
||||
@@ -281,7 +282,7 @@
|
||||
handleFollowUp(row.id);
|
||||
break;
|
||||
case 'pop-transfer':
|
||||
handleTransfer(row);
|
||||
handleTransfer(row, done);
|
||||
break;
|
||||
case 'delete':
|
||||
handleDelete(row);
|
||||
@@ -331,7 +332,7 @@
|
||||
CrmOperationButton,
|
||||
{
|
||||
groupList: operationGroupList.value,
|
||||
onSelect: (key: string) => handleActionSelect(row, key),
|
||||
onSelect: (key: string, done?: () => void) => handleActionSelect(row, key, done),
|
||||
onCancel: () => {
|
||||
transferForm.value = { ...defaultTransferForm };
|
||||
},
|
||||
@@ -364,16 +365,14 @@
|
||||
},
|
||||
customerName: (row: OpportunityItem) => {
|
||||
return h(
|
||||
NButton,
|
||||
CrmTableButton,
|
||||
{
|
||||
text: true,
|
||||
type: 'primary',
|
||||
onClick: () => {
|
||||
activeSourceId.value = row.customerId;
|
||||
realFormKey.value = FormDesignKeyEnum.CUSTOMER;
|
||||
},
|
||||
},
|
||||
{ default: () => row.customerName }
|
||||
{ default: () => row.customerName, trigger: () => row.customerName }
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -460,6 +459,7 @@
|
||||
function searchData() {
|
||||
setLoadListParams({
|
||||
keyword: keyword.value,
|
||||
searchType: activeTab.value,
|
||||
});
|
||||
loadList();
|
||||
}
|
||||
|
||||
@@ -208,4 +208,4 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="less" scoped></style>
|
||||
|
||||
Reference in New Issue
Block a user