CRM里面的客户、线索管理适配手机端

This commit is contained in:
2026-03-12 17:31:23 +08:00
parent 0432e2430a
commit 41d7bd6c86
13 changed files with 1802 additions and 983 deletions

View File

@@ -1,21 +1,28 @@
<template> <template>
<Dialog v-model="dialogVisible" :title="dialogTitle"> <el-drawer
<el-form v-model="dialogVisible"
ref="formRef" :title="dialogTitle"
v-loading="formLoading" direction="rtl"
:model="formData" size="100%"
:rules="formRules" :close-on-press-escape="true"
label-width="100px" :destroy-on-close="true"
> class="mobile-form-drawer"
<el-row> >
<el-col :span="12"> <div class="mobile-form" v-loading="formLoading">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-position="top"
>
<!-- 基本信息 -->
<div class="mobile-form__section">
<div class="mobile-form__section-title">基本信息</div>
<el-form-item label="线索名称" prop="name"> <el-form-item label="线索名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入线索名称" /> <el-input v-model="formData.name" placeholder="请输入线索名称" />
</el-form-item> </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="客户来源" prop="source"> <el-form-item label="客户来源" prop="source">
<el-select v-model="formData.source" placeholder="请选择客户来源" class="w-1/1"> <el-select v-model="formData.source" placeholder="请选择客户来源" style="width: 100%">
<el-option <el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)" v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)"
:key="dict.value" :key="dict.value"
@@ -24,20 +31,11 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="手机" prop="mobile">
<el-input v-model="formData.mobile" placeholder="请输入手机" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="负责人" prop="ownerUserId"> <el-form-item label="负责人" prop="ownerUserId">
<el-select <el-select
v-model="formData.ownerUserId" v-model="formData.ownerUserId"
:disabled="formType !== 'create'" :disabled="formType !== 'create'"
class="w-1/1" style="width: 100%"
> >
<el-option <el-option
v-for="item in userOptions" v-for="item in userOptions"
@@ -47,36 +45,33 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </div>
</el-row>
<el-row> <!-- 联系方式 -->
<el-col :span="12"> <div class="mobile-form__section">
<div class="mobile-form__section-title">联系方式</div>
<el-form-item label="手机" prop="mobile">
<el-input v-model="formData.mobile" placeholder="请输入手机" />
</el-form-item>
<el-form-item label="电话" prop="telephone"> <el-form-item label="电话" prop="telephone">
<el-input v-model="formData.telephone" placeholder="请输入电话" /> <el-input v-model="formData.telephone" placeholder="请输入电话" />
</el-form-item> </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="邮箱" prop="email"> <el-form-item label="邮箱" prop="email">
<el-input v-model="formData.email" placeholder="请输入邮箱" /> <el-input v-model="formData.email" placeholder="请输入邮箱" />
</el-form-item> </el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="微信" prop="wechat"> <el-form-item label="微信" prop="wechat">
<el-input v-model="formData.wechat" placeholder="请输入微信" /> <el-input v-model="formData.wechat" placeholder="请输入微信" />
</el-form-item> </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="QQ" prop="qq"> <el-form-item label="QQ" prop="qq">
<el-input v-model="formData.qq" placeholder="请输入 QQ" /> <el-input v-model="formData.qq" placeholder="请输入 QQ" />
</el-form-item> </el-form-item>
</el-col> </div>
</el-row>
<el-row> <!-- 客户信息 -->
<el-col :span="12"> <div class="mobile-form__section">
<div class="mobile-form__section-title">客户信息</div>
<el-form-item label="客户行业" prop="industryId"> <el-form-item label="客户行业" prop="industryId">
<el-select v-model="formData.industryId" placeholder="请选择客户行业" class="w-1/1"> <el-select v-model="formData.industryId" placeholder="请选择客户行业" style="width: 100%">
<el-option <el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)" v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)"
:key="dict.value" :key="dict.value"
@@ -85,10 +80,8 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="客户级别" prop="level"> <el-form-item label="客户级别" prop="level">
<el-select v-model="formData.level" placeholder="请选择客户级别" class="w-1/1"> <el-select v-model="formData.level" placeholder="请选择客户级别" style="width: 100%">
<el-option <el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)" v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)"
:key="dict.value" :key="dict.value"
@@ -97,52 +90,52 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </div>
</el-row>
<el-row> <!-- 地址信息 -->
<el-col :span="12"> <div class="mobile-form__section">
<div class="mobile-form__section-title">地址信息</div>
<el-form-item label="地址" prop="areaId"> <el-form-item label="地址" prop="areaId">
<el-cascader <el-cascader
v-model="formData.areaId" v-model="formData.areaId"
:options="areaList" :options="areaList"
:props="defaultProps" :props="defaultProps"
class="w-1/1" style="width: 100%"
clearable clearable
filterable filterable
placeholder="请选择城市" placeholder="请选择城市"
/> />
</el-form-item> </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="详细地址" prop="detailAddress"> <el-form-item label="详细地址" prop="detailAddress">
<el-input v-model="formData.detailAddress" placeholder="请输入详细地址" /> <el-input v-model="formData.detailAddress" placeholder="请输入详细地址" />
</el-form-item> </el-form-item>
</el-col> </div>
</el-row>
<el-row> <!-- 其他信息 -->
<el-col :span="12"> <div class="mobile-form__section">
<div class="mobile-form__section-title">其他信息</div>
<el-form-item label="下次联系时间" prop="contactNextTime"> <el-form-item label="下次联系时间" prop="contactNextTime">
<el-date-picker <el-date-picker
v-model="formData.contactNextTime" v-model="formData.contactNextTime"
placeholder="选择下次联系时间" placeholder="选择下次联系时间"
type="datetime" type="datetime"
value-format="x" value-format="x"
class="!w-1/1" style="width: 100%"
/> />
</el-form-item> </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="formData.remark" placeholder="请输入备注" /> <el-input type="textarea" v-model="formData.remark" placeholder="请输入备注" :rows="3" />
</el-form-item> </el-form-item>
</el-col> </div>
</el-row> </el-form>
</el-form>
<template #footer> <!-- 底部操作按钮 -->
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button> <div class="mobile-form__footer">
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false"> </el-button>
</template> <el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
</Dialog> </div>
</div>
</el-drawer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
@@ -258,3 +251,44 @@ const resetForm = () => {
formRef.value?.resetFields() formRef.value?.resetFields()
} }
</script> </script>
<style lang="scss" scoped>
.mobile-form {
padding: 0 4px;
}
.mobile-form__section {
background: #fff;
border-radius: 10px;
padding: 14px;
margin-bottom: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.mobile-form__section-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
.mobile-form__footer {
position: sticky;
bottom: 0;
background: #fff;
padding: 12px 16px;
padding-bottom: calc(12px + constant(safe-area-inset-bottom));
padding-bottom: calc(12px + env(safe-area-inset-bottom));
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 12px;
z-index: 10;
margin: 0 -4px;
.el-button {
flex: 1;
height: 40px;
font-size: 15px;
}
}
</style>

View File

@@ -1,34 +1,36 @@
<template> <template>
<div v-loading="loading"> <div class="mobile-detail-header" v-loading="loading">
<div class="flex items-start justify-between"> <!-- 标题和操作按钮 -->
<div> <div class="mobile-detail-header__top">
<!-- 左上线索基本信息 --> <div class="mobile-detail-header__title">{{ clue.name }}</div>
<el-col> <div class="mobile-detail-header__actions">
<el-row>
<span class="text-xl font-bold">{{ clue.name }}</span>
</el-row>
</el-col>
</div>
<div>
<!-- 右上按钮 -->
<slot></slot> <slot></slot>
</div> </div>
</div> </div>
<!-- 关键信息卡片 -->
<div class="mobile-detail-header__card">
<div class="mobile-info-list">
<div class="mobile-info-row">
<span class="mobile-info-row__label">线索来源</span>
<span class="mobile-info-row__value">
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="clue.source" />
</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">手机</span>
<span class="mobile-info-row__value">{{ clue.mobile || '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">负责人</span>
<span class="mobile-info-row__value">{{ clue.ownerUserName || '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">创建时间</span>
<span class="mobile-info-row__value">{{ formatDate(clue.createTime) || '-' }}</span>
</div>
</div>
</div>
</div> </div>
<ContentWrap class="mt-10px">
<el-descriptions :column="5" direction="vertical">
<el-descriptions-item label="线索来源">
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="clue.source" />
</el-descriptions-item>
<el-descriptions-item label="手机"> {{ clue.mobile }} </el-descriptions-item>
<el-descriptions-item label="负责人">
{{ clue.ownerUserName }}
</el-descriptions-item>
<el-descriptions-item label="创建时间">
{{ formatDate(clue.createTime) }}
</el-descriptions-item>
</el-descriptions>
</ContentWrap>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { DICT_TYPE } from '@/utils/dict' import { DICT_TYPE } from '@/utils/dict'
@@ -41,3 +43,57 @@ defineProps<{
loading: boolean // 加载中 loading: boolean // 加载中
}>() }>()
</script> </script>
<style lang="scss" scoped>
.mobile-detail-header {
padding: 12px;
background: #f5f7fa;
}
.mobile-detail-header__top {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 12px;
gap: 12px;
}
.mobile-detail-header__title {
font-size: 18px;
font-weight: 600;
color: #303133;
flex: 1;
word-break: break-all;
}
.mobile-detail-header__actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
flex-shrink: 0;
}
.mobile-detail-header__card {
background: #fff;
border-radius: 10px;
padding: 14px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.mobile-info-list {
font-size: 13px;
}
.mobile-info-row {
display: flex;
justify-content: space-between;
padding: 6px 0;
border-bottom: 1px solid #f5f5f5;
&:last-child {
border-bottom: none;
}
&__label {
color: #909399;
flex-shrink: 0;
margin-right: 12px;
}
&__value {
color: #303133;
text-align: right;
}
}
</style>

View File

@@ -1,61 +1,97 @@
<template> <template>
<ContentWrap> <div class="mobile-detail-info">
<el-collapse v-model="activeNames" class=""> <!-- 基本信息 -->
<el-collapse-item name="basicInfo"> <div class="mobile-form__section">
<template #title> <div class="mobile-form__section-title">基本信息</div>
<span class="text-base font-bold">基本信息</span> <div class="mobile-info-list">
</template> <div class="mobile-info-row">
<el-descriptions :column="4"> <span class="mobile-info-row__label">线索名称</span>
<el-descriptions-item label="线索名称"> <span class="mobile-info-row__value">{{ clue.name || '-' }}</span>
{{ clue.name }} </div>
</el-descriptions-item> <div class="mobile-info-row">
<el-descriptions-item label="客户来源"> <span class="mobile-info-row__label">客户来源</span>
<span class="mobile-info-row__value">
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="clue.source" /> <dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="clue.source" />
</el-descriptions-item> </span>
<el-descriptions-item label="手机">{{ clue.mobile }}</el-descriptions-item> </div>
<el-descriptions-item label="电话">{{ clue.telephone }}</el-descriptions-item> <div class="mobile-info-row">
<el-descriptions-item label="邮箱">{{ clue.email }}</el-descriptions-item> <span class="mobile-info-row__label">手机</span>
<el-descriptions-item label="地址"> <span class="mobile-info-row__value">{{ clue.mobile || '-' }}</span>
{{ clue.areaName }} {{ clue.detailAddress }} </div>
</el-descriptions-item> <div class="mobile-info-row">
<el-descriptions-item label="QQ">{{ clue.qq }}</el-descriptions-item> <span class="mobile-info-row__label">电话</span>
<el-descriptions-item label="微信">{{ clue.wechat }}</el-descriptions-item> <span class="mobile-info-row__value">{{ clue.telephone || '-' }}</span>
<el-descriptions-item label="客户行业"> </div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">邮箱</span>
<span class="mobile-info-row__value">{{ clue.email || '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">地址</span>
<span class="mobile-info-row__value">{{ clue.areaName || '' }} {{ clue.detailAddress || '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">QQ</span>
<span class="mobile-info-row__value">{{ clue.qq || '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">微信</span>
<span class="mobile-info-row__value">{{ clue.wechat || '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">客户行业</span>
<span class="mobile-info-row__value">
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="clue.industryId" /> <dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="clue.industryId" />
</el-descriptions-item> </span>
<el-descriptions-item label="客户级别"> </div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">客户级别</span>
<span class="mobile-info-row__value">
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="clue.level" /> <dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="clue.level" />
</el-descriptions-item> </span>
<el-descriptions-item label="下次联系时间"> </div>
{{ formatDate(clue.contactNextTime) }} <div class="mobile-info-row">
</el-descriptions-item> <span class="mobile-info-row__label">下次联系时间</span>
<el-descriptions-item label="备注">{{ clue.remark }}</el-descriptions-item> <span class="mobile-info-row__value">{{ formatDate(clue.contactNextTime) || '-' }}</span>
</el-descriptions> </div>
</el-collapse-item> <div class="mobile-info-row" v-if="clue.remark">
<el-collapse-item name="systemInfo"> <span class="mobile-info-row__label">备注</span>
<template #title> <span class="mobile-info-row__value">{{ clue.remark }}</span>
<span class="text-base font-bold">系统信息</span> </div>
</template> </div>
<el-descriptions :column="4"> </div>
<el-descriptions-item label="负责人">{{ clue.ownerUserName }}</el-descriptions-item>
<el-descriptions-item label="最后跟进记录"> <!-- 系统信息 -->
{{ clue.contactLastContent }} <div class="mobile-form__section">
</el-descriptions-item> <div class="mobile-form__section-title">系统信息</div>
<el-descriptions-item label="最后跟进时间"> <div class="mobile-info-list">
{{ formatDate(clue.contactLastTime) }} <div class="mobile-info-row">
</el-descriptions-item> <span class="mobile-info-row__label">负责人</span>
<el-descriptions-item label="">&nbsp;</el-descriptions-item> <span class="mobile-info-row__value">{{ clue.ownerUserName || '-' }}</span>
<el-descriptions-item label="创建人">{{ clue.creatorName }}</el-descriptions-item> </div>
<el-descriptions-item label="创建时间"> <div class="mobile-info-row" v-if="clue.contactLastContent">
{{ formatDate(clue.createTime) }} <span class="mobile-info-row__label">最后跟进记录</span>
</el-descriptions-item> <span class="mobile-info-row__value">{{ clue.contactLastContent }}</span>
<el-descriptions-item label="更新时间"> </div>
{{ formatDate(clue.updateTime) }} <div class="mobile-info-row">
</el-descriptions-item> <span class="mobile-info-row__label">最后跟进时间</span>
</el-descriptions> <span class="mobile-info-row__value">{{ formatDate(clue.contactLastTime) || '-' }}</span>
</el-collapse-item> </div>
</el-collapse> <div class="mobile-info-row">
</ContentWrap> <span class="mobile-info-row__label">创建人</span>
<span class="mobile-info-row__value">{{ clue.creatorName || '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">创建时间</span>
<span class="mobile-info-row__value">{{ formatDate(clue.createTime) || '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">更新时间</span>
<span class="mobile-info-row__value">{{ formatDate(clue.updateTime) || '-' }}</span>
</div>
</div>
</div>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import * as ClueApi from '@/api/crm/clue' import * as ClueApi from '@/api/crm/clue'
@@ -66,7 +102,48 @@ defineOptions({ name: 'CrmClueDetailsInfo' })
const { clue } = defineProps<{ const { clue } = defineProps<{
clue: ClueApi.ClueVO // 线索明细 clue: ClueApi.ClueVO // 线索明细
}>() }>()
const activeNames = ref(['basicInfo', 'systemInfo']) // 展示的折叠面板
</script> </script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.mobile-detail-info {
padding: 12px;
background: #f5f7fa;
}
.mobile-form__section {
background: #fff;
border-radius: 10px;
padding: 14px;
margin-bottom: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.mobile-form__section-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
.mobile-info-list {
font-size: 13px;
}
.mobile-info-row {
display: flex;
justify-content: space-between;
padding: 6px 0;
border-bottom: 1px solid #f5f5f5;
&:last-child {
border-bottom: none;
}
&__label {
color: #909399;
flex-shrink: 0;
margin-right: 12px;
}
&__value {
color: #303133;
text-align: right;
word-break: break-all;
}
}
</style>

View File

@@ -1,52 +1,18 @@
<template> <template>
<doc-alert title="【线索】线索管理" url="https://doc.iocoder.cn/crm/clue/" /> <div class="mobile-page">
<doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" /> <!-- 搜索头部 -->
<div class="mobile-page__header">
<ContentWrap> <div class="mobile-page__search">
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="线索名称" prop="name">
<el-input <el-input
v-model="queryParams.name" v-model="queryParams.name"
placeholder="请输入线索名称" placeholder="搜索线索名称"
clearable clearable
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
class="!w-240px" :prefix-icon="Search"
/> />
</el-form-item> <el-button type="primary" :icon="Filter" @click="filterDrawerVisible = true" />
<el-form-item label="转化状态" prop="transformStatus"> </div>
<el-select v-model="queryParams.transformStatus" class="!w-240px"> <div class="mobile-page__actions">
<el-option :value="false" label="未转化" />
<el-option :value="true" label="已转化" />
</el-select>
</el-form-item>
<el-form-item label="手机号" prop="mobile">
<el-input
v-model="queryParams.mobile"
placeholder="请输入手机号"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="电话" prop="telephone">
<el-input
v-model="queryParams.telephone"
placeholder="请输入电话"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button type="primary" @click="openForm('create')" v-hasPermi="['crm:clue:create']"> <el-button type="primary" @click="openForm('create')" v-hasPermi="['crm:clue:create']">
<Icon icon="ep:plus" class="mr-5px" /> 新增 <Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button> </el-button>
@@ -57,108 +23,129 @@
:loading="exportLoading" :loading="exportLoading"
v-hasPermi="['crm:clue:export']" v-hasPermi="['crm:clue:export']"
> >
<Icon icon="ep:download" class="mr-5px" /> 导出 导出
</el-button> </el-button>
</el-form-item> </div>
</el-form> </div>
</ContentWrap>
<!-- 列表 --> <!-- Tab 切换 -->
<ContentWrap> <div class="mobile-page__tabs">
<el-tabs v-model="activeName" @tab-click="handleTabClick"> <el-tabs v-model="activeName" @tab-click="handleTabClick">
<el-tab-pane label="我负责的" name="1" /> <el-tab-pane label="我负责的" name="1" />
<el-tab-pane label="我参与的" name="2" /> <el-tab-pane label="我参与的" name="2" />
<el-tab-pane label="下属负责的" name="3" /> <el-tab-pane label="下属负责的" name="3" />
</el-tabs> </el-tabs>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> </div>
<el-table-column label="线索名称" align="center" prop="name" fixed="left" width="160">
<template #default="scope"> <!-- 线索列表 -->
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)"> <div class="mobile-page__content" v-loading="loading">
{{ scope.row.name }} <div class="mobile-item-list">
</el-link> <div
</template> v-for="item in list"
</el-table-column> :key="item.id"
<el-table-column label="线索来源" align="center" prop="source" width="100"> class="mobile-item-card mobile-item-card--clickable"
<template #default="scope"> @click="openDetail(item.id)"
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" /> >
</template> <div class="mobile-item-card__header">
</el-table-column> <span class="mobile-item-card__name">{{ item.name }}</span>
<el-table-column label="手机" align="center" prop="mobile" width="120" /> <el-tag v-if="item.transformStatus" type="success" size="small">已转化</el-tag>
<el-table-column label="电话" align="center" prop="telephone" width="130" /> <el-tag v-else type="info" size="small">未转化</el-tag>
<el-table-column label="邮箱" align="center" prop="email" width="180" /> </div>
<el-table-column label="地址" align="center" prop="detailAddress" width="180" /> <div class="mobile-item-card__body">
<el-table-column align="center" label="客户行业" prop="industryId" width="100"> <div class="mobile-item-card__info-row">
<template #default="scope"> <span class="mobile-item-card__info-label">线索来源</span>
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="scope.row.industryId" /> <span class="mobile-item-card__info-value">
</template> <dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="item.source" />
</el-table-column> </span>
<el-table-column align="center" label="客户级别" prop="level" width="135"> </div>
<template #default="scope"> <div class="mobile-item-card__info-row">
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" /> <span class="mobile-item-card__info-label">手机</span>
</template> <span class="mobile-item-card__info-value">{{ item.mobile || '-' }}</span>
</el-table-column> </div>
<el-table-column <div class="mobile-item-card__info-row">
:formatter="dateFormatter" <span class="mobile-item-card__info-label">负责人</span>
align="center" <span class="mobile-item-card__info-value">{{ item.ownerUserName || '-' }}</span>
label="下次联系时间" </div>
prop="contactNextTime" <div class="mobile-item-card__info-row">
width="180px" <span class="mobile-item-card__info-label">客户级别</span>
/> <span class="mobile-item-card__info-value">
<el-table-column align="center" label="备注" prop="remark" width="200" /> <dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="item.level" />
<el-table-column </span>
label="最后跟进时间" </div>
align="center" <div class="mobile-item-card__info-row">
prop="contactLastTime" <span class="mobile-item-card__info-label">最后跟进</span>
:formatter="dateFormatter" <span class="mobile-item-card__info-value">{{ item.contactLastTime ? dateFormatter(null, null, item.contactLastTime) : '-' }}</span>
width="180px" </div>
/> </div>
<el-table-column align="center" label="最后跟进记录" prop="contactLastContent" width="200" /> <div class="mobile-item-card__footer" @click.stop>
<el-table-column align="center" label="负责人" prop="ownerUserName" width="100px" /> <el-button
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100" /> size="small"
<el-table-column type="primary"
label="更新时间" @click="openForm('update', item.id)"
align="center" v-hasPermi="['crm:clue:update']"
prop="updateTime" >编辑</el-button>
:formatter="dateFormatter" <el-button
width="180px" size="small"
/> type="danger"
<el-table-column @click="handleDelete(item.id)"
label="创建时间" v-hasPermi="['crm:clue:delete']"
align="center" >删除</el-button>
prop="createTime" </div>
:formatter="dateFormatter" </div>
width="180px" <div v-if="list.length === 0 && !loading" class="mobile-empty-tip">暂无线索数据</div>
/> </div>
<el-table-column align="center" label="创建人" prop="creatorName" width="100px" /> <!-- 分页 -->
<el-table-column label="操作" align="center" min-width="110" fixed="right"> <div class="mobile-pagination" v-if="total > 0">
<template #default="scope"> <el-pagination
<el-button v-model:current-page="queryParams.pageNo"
link v-model:page-size="queryParams.pageSize"
type="primary" :total="total"
@click="openForm('update', scope.row.id)" :page-sizes="[10, 20]"
v-hasPermi="['crm:clue:update']" layout="total, prev, pager, next"
> :pager-count="5"
编辑 @size-change="getList"
</el-button> @current-change="getList"
<el-button />
link </div>
type="danger" </div>
@click="handleDelete(scope.row.id)" </div>
v-hasPermi="['crm:clue:delete']"
> <!-- 筛选抽屉 -->
删除 <el-drawer
</el-button> v-model="filterDrawerVisible"
</template> title="筛选条件"
</el-table-column> direction="rtl"
</el-table> size="100%"
<!-- 分页 --> :append-to-body="true"
<Pagination class="mobile-form-drawer"
:total="total" >
v-model:page="queryParams.pageNo" <div class="mobile-form">
v-model:limit="queryParams.pageSize" <div class="mobile-form__section">
@pagination="getList" <div class="mobile-form__section-title">筛选条件</div>
/> <el-form :model="queryParams" ref="queryFormRef" label-position="top">
</ContentWrap> <el-form-item label="线索名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入线索名称" clearable />
</el-form-item>
<el-form-item label="转化状态" prop="transformStatus">
<el-select v-model="queryParams.transformStatus" style="width: 100%">
<el-option :value="false" label="未转化" />
<el-option :value="true" label="已转化" />
</el-select>
</el-form-item>
<el-form-item label="手机号" prop="mobile">
<el-input v-model="queryParams.mobile" placeholder="请输入手机号" clearable />
</el-form-item>
<el-form-item label="电话" prop="telephone">
<el-input v-model="queryParams.telephone" placeholder="请输入电话" clearable />
</el-form-item>
</el-form>
</div>
<div class="mobile-form__footer">
<el-button @click="resetQuery" style="flex: 1">重置</el-button>
<el-button type="primary" @click="handleFilterConfirm" style="flex: 1">确认</el-button>
</div>
</div>
</el-drawer>
<!-- 表单弹窗添加/修改 --> <!-- 表单弹窗添加/修改 -->
<ClueForm ref="formRef" @success="getList" /> <ClueForm ref="formRef" @success="getList" />
@@ -171,6 +158,7 @@ import download from '@/utils/download'
import * as ClueApi from '@/api/crm/clue' import * as ClueApi from '@/api/crm/clue'
import ClueForm from './ClueForm.vue' import ClueForm from './ClueForm.vue'
import { TabsPaneContext } from 'element-plus' import { TabsPaneContext } from 'element-plus'
import { Search, Filter } from '@element-plus/icons-vue'
defineOptions({ name: 'CrmClue' }) defineOptions({ name: 'CrmClue' })
@@ -180,6 +168,7 @@ const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中 const loading = ref(true) // 列表的加载中
const total = ref(0) // 列表的总页数 const total = ref(0) // 列表的总页数
const list = ref([]) // 列表的数据 const list = ref([]) // 列表的数据
const filterDrawerVisible = ref(false) // 筛选抽屉
const queryParams = reactive({ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
@@ -263,8 +252,149 @@ const handleExport = async () => {
} }
} }
/** 筛选确认 */
const handleFilterConfirm = () => {
filterDrawerVisible.value = false
handleQuery()
}
/** 初始化 **/ /** 初始化 **/
onMounted(() => { onMounted(() => {
getList() getList()
}) })
</script> </script>
<style lang="scss" scoped>
.mobile-page {
padding: 12px;
background: #f5f7fa;
min-height: 100vh;
}
.mobile-page__header {
margin-bottom: 12px;
}
.mobile-page__search {
display: flex;
gap: 8px;
margin-bottom: 10px;
}
.mobile-page__actions {
display: flex;
gap: 8px;
}
.mobile-page__tabs {
background: #fff;
border-radius: 10px;
padding: 0 12px;
margin-bottom: 12px;
}
.mobile-page__content {
min-height: 200px;
}
.mobile-item-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.mobile-item-card {
background: #fff;
border-radius: 10px;
padding: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
&--clickable {
cursor: pointer;
transition: all 0.2s;
&:active {
background: #f5f5f5;
}
}
&__header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
&__name {
flex: 1;
font-size: 15px;
font-weight: 600;
color: #303133;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&__body {
font-size: 13px;
}
&__info-row {
display: flex;
justify-content: space-between;
padding: 4px 0;
}
&__info-label {
color: #909399;
flex-shrink: 0;
}
&__info-value {
color: #606266;
text-align: right;
}
&__footer {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid #f0f0f0;
}
}
.mobile-empty-tip {
text-align: center;
color: #909399;
padding: 40px 0;
font-size: 14px;
}
.mobile-pagination {
margin-top: 12px;
display: flex;
justify-content: center;
:deep(.el-pagination) {
flex-wrap: wrap;
justify-content: center;
}
}
.mobile-form {
padding: 0 4px;
}
.mobile-form__section {
background: #fff;
border-radius: 10px;
padding: 14px;
margin-bottom: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.mobile-form__section-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
.mobile-form__footer {
position: sticky;
bottom: 0;
background: #fff;
padding: 12px 16px;
padding-bottom: calc(12px + constant(safe-area-inset-bottom));
padding-bottom: calc(12px + env(safe-area-inset-bottom));
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 12px;
z-index: 10;
margin: 0 -4px;
}
</style>

View File

@@ -1,21 +1,28 @@
<template> <template>
<Dialog v-model="dialogVisible" :title="dialogTitle"> <el-drawer
<el-form v-model="dialogVisible"
ref="formRef" :title="dialogTitle"
v-loading="formLoading" direction="rtl"
:model="formData" size="100%"
:rules="formRules" :close-on-press-escape="true"
label-width="100px" :destroy-on-close="true"
> class="mobile-form-drawer"
<el-row> >
<el-col :span="12"> <div class="mobile-form" v-loading="formLoading">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-position="top"
>
<!-- 基本信息 -->
<div class="mobile-form__section">
<div class="mobile-form__section-title">基本信息</div>
<el-form-item label="客户名称" prop="name"> <el-form-item label="客户名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入客户名称" /> <el-input v-model="formData.name" placeholder="请输入客户名称" />
</el-form-item> </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="客户来源" prop="source"> <el-form-item label="客户来源" prop="source">
<el-select v-model="formData.source" placeholder="请选择客户来源" class="w-1/1"> <el-select v-model="formData.source" placeholder="请选择客户来源" style="width: 100%">
<el-option <el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)" v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)"
:key="dict.value" :key="dict.value"
@@ -24,20 +31,11 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="手机" prop="mobile">
<el-input v-model="formData.mobile" placeholder="请输入手机" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="负责人" prop="ownerUserId"> <el-form-item label="负责人" prop="ownerUserId">
<el-select <el-select
v-model="formData.ownerUserId" v-model="formData.ownerUserId"
:disabled="formType !== 'create'" :disabled="formType !== 'create'"
class="w-1/1" style="width: 100%"
> >
<el-option <el-option
v-for="item in userOptions" v-for="item in userOptions"
@@ -47,36 +45,33 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </div>
</el-row>
<el-row> <!-- 联系方式 -->
<el-col :span="12"> <div class="mobile-form__section">
<div class="mobile-form__section-title">联系方式</div>
<el-form-item label="手机" prop="mobile">
<el-input v-model="formData.mobile" placeholder="请输入手机" />
</el-form-item>
<el-form-item label="电话" prop="telephone"> <el-form-item label="电话" prop="telephone">
<el-input v-model="formData.telephone" placeholder="请输入电话" /> <el-input v-model="formData.telephone" placeholder="请输入电话" />
</el-form-item> </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="邮箱" prop="email"> <el-form-item label="邮箱" prop="email">
<el-input v-model="formData.email" placeholder="请输入邮箱" /> <el-input v-model="formData.email" placeholder="请输入邮箱" />
</el-form-item> </el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="微信" prop="wechat"> <el-form-item label="微信" prop="wechat">
<el-input v-model="formData.wechat" placeholder="请输入微信" /> <el-input v-model="formData.wechat" placeholder="请输入微信" />
</el-form-item> </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="QQ" prop="qq"> <el-form-item label="QQ" prop="qq">
<el-input v-model="formData.qq" placeholder="请输入 QQ" /> <el-input v-model="formData.qq" placeholder="请输入 QQ" />
</el-form-item> </el-form-item>
</el-col> </div>
</el-row>
<el-row> <!-- 客户信息 -->
<el-col :span="12"> <div class="mobile-form__section">
<div class="mobile-form__section-title">客户信息</div>
<el-form-item label="客户行业" prop="industryId"> <el-form-item label="客户行业" prop="industryId">
<el-select v-model="formData.industryId" placeholder="请选择客户行业" class="w-1/1"> <el-select v-model="formData.industryId" placeholder="请选择客户行业" style="width: 100%">
<el-option <el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)" v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)"
:key="dict.value" :key="dict.value"
@@ -85,10 +80,8 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="客户级别" prop="level"> <el-form-item label="客户级别" prop="level">
<el-select v-model="formData.level" placeholder="请选择客户级别" class="w-1/1"> <el-select v-model="formData.level" placeholder="请选择客户级别" style="width: 100%">
<el-option <el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)" v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)"
:key="dict.value" :key="dict.value"
@@ -97,52 +90,52 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </div>
</el-row>
<el-row> <!-- 地址信息 -->
<el-col :span="12"> <div class="mobile-form__section">
<div class="mobile-form__section-title">地址信息</div>
<el-form-item label="地址" prop="areaId"> <el-form-item label="地址" prop="areaId">
<el-cascader <el-cascader
v-model="formData.areaId" v-model="formData.areaId"
:options="areaList" :options="areaList"
:props="defaultProps" :props="defaultProps"
class="w-1/1" style="width: 100%"
clearable clearable
filterable filterable
placeholder="请选择城市" placeholder="请选择城市"
/> />
</el-form-item> </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="详细地址" prop="detailAddress"> <el-form-item label="详细地址" prop="detailAddress">
<el-input v-model="formData.detailAddress" placeholder="请输入详细地址" /> <el-input v-model="formData.detailAddress" placeholder="请输入详细地址" />
</el-form-item> </el-form-item>
</el-col> </div>
</el-row>
<el-row> <!-- 其他信息 -->
<el-col :span="12"> <div class="mobile-form__section">
<div class="mobile-form__section-title">其他信息</div>
<el-form-item label="下次联系时间" prop="contactNextTime"> <el-form-item label="下次联系时间" prop="contactNextTime">
<el-date-picker <el-date-picker
v-model="formData.contactNextTime" v-model="formData.contactNextTime"
placeholder="选择下次联系时间" placeholder="选择下次联系时间"
type="datetime" type="datetime"
value-format="x" value-format="x"
class="!w-1/1" style="width: 100%"
/> />
</el-form-item> </el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="formData.remark" placeholder="请输入备注" /> <el-input type="textarea" v-model="formData.remark" placeholder="请输入备注" :rows="3" />
</el-form-item> </el-form-item>
</el-col> </div>
</el-row> </el-form>
</el-form>
<template #footer> <!-- 底部操作按钮 -->
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button> <div class="mobile-form__footer">
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false"> </el-button>
</template> <el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
</Dialog> </div>
</div>
</el-drawer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
@@ -258,3 +251,44 @@ const resetForm = () => {
formRef.value?.resetFields() formRef.value?.resetFields()
} }
</script> </script>
<style lang="scss" scoped>
.mobile-form {
padding: 0 4px;
}
.mobile-form__section {
background: #fff;
border-radius: 10px;
padding: 14px;
margin-bottom: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.mobile-form__section-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
.mobile-form__footer {
position: sticky;
bottom: 0;
background: #fff;
padding: 12px 16px;
padding-bottom: calc(12px + constant(safe-area-inset-bottom));
padding-bottom: calc(12px + env(safe-area-inset-bottom));
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 12px;
z-index: 10;
margin: 0 -4px;
.el-button {
flex: 1;
height: 40px;
font-size: 15px;
}
}
</style>

View File

@@ -1,53 +1,73 @@
<!-- 客户导入窗口 --> <!-- 客户导入窗口 -->
<template> <template>
<Dialog v-model="dialogVisible" title="客户导入" width="400"> <el-drawer
<div class="flex items-center my-10px"> v-model="dialogVisible"
<span class="mr-10px">负责人</span> title="客户导入"
<el-select v-model="ownerUserId" class="!w-240px" clearable> direction="rtl"
<el-option size="100%"
v-for="item in userOptions" :close-on-press-escape="true"
:key="item.id" :destroy-on-close="true"
:label="item.nickname" class="mobile-form-drawer"
:value="item.id" >
/> <div class="mobile-form" v-loading="formLoading">
</el-select> <div class="mobile-form__section">
<div class="mobile-form__section-title">导入设置</div>
<el-form label-position="top">
<el-form-item label="负责人">
<el-select v-model="ownerUserId" style="width: 100%" clearable>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-form>
</div>
<div class="mobile-form__section">
<div class="mobile-form__section-title">上传文件</div>
<el-upload
ref="uploadRef"
v-model:file-list="fileList"
:auto-upload="false"
:disabled="formLoading"
:limit="1"
:on-exceed="handleExceed"
accept=".xlsx, .xls"
action="none"
drag
>
<Icon icon="ep:upload" />
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip text-center">
<div class="el-upload__tip">
<el-checkbox v-model="updateSupport" />
是否更新已经存在的客户数据客户名称重复
</div>
<span>仅允许导入 xlsxlsx 格式文件</span>
<el-link
:underline="false"
style="font-size: 12px; vertical-align: baseline"
type="primary"
@click="importTemplate"
>
下载模板
</el-link>
</div>
</template>
</el-upload>
</div>
<!-- 底部操作按钮 -->
<div class="mobile-form__footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
</div>
</div> </div>
<el-upload </el-drawer>
ref="uploadRef"
v-model:file-list="fileList"
:auto-upload="false"
:disabled="formLoading"
:limit="1"
:on-exceed="handleExceed"
accept=".xlsx, .xls"
action="none"
drag
>
<Icon icon="ep:upload" />
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip text-center">
<div class="el-upload__tip">
<el-checkbox v-model="updateSupport" />
是否更新已经存在的客户数据客户名称重复
</div>
<span>仅允许导入 xlsxlsx 格式文件</span>
<el-link
:underline="false"
style="font-size: 12px; vertical-align: baseline"
type="primary"
@click="importTemplate"
>
下载模板
</el-link>
</div>
</template>
</el-upload>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import * as CustomerApi from '@/api/crm/customer' import * as CustomerApi from '@/api/crm/customer'
@@ -156,3 +176,44 @@ const importTemplate = async () => {
download.excel(res, '客户导入模版.xls') download.excel(res, '客户导入模版.xls')
} }
</script> </script>
<style lang="scss" scoped>
.mobile-form {
padding: 0 4px;
}
.mobile-form__section {
background: #fff;
border-radius: 10px;
padding: 14px;
margin-bottom: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.mobile-form__section-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
.mobile-form__footer {
position: sticky;
bottom: 0;
background: #fff;
padding: 12px 16px;
padding-bottom: calc(12px + constant(safe-area-inset-bottom));
padding-bottom: calc(12px + env(safe-area-inset-bottom));
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 12px;
z-index: 10;
margin: 0 -4px;
.el-button {
flex: 1;
height: 40px;
font-size: 15px;
}
}
</style>

View File

@@ -1,219 +1,175 @@
<template> <template>
<doc-alert title="【客户】客户管理、公海客户" url="https://doc.iocoder.cn/crm/customer/" /> <div class="mobile-page">
<doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" /> <!-- 搜索头部 -->
<div class="mobile-page__header">
<ContentWrap> <div class="mobile-page__search">
<!-- 搜索工作栏 -->
<el-form
ref="queryFormRef"
:inline="true"
:model="queryParams"
class="-mb-15px"
label-width="68px"
>
<el-form-item label="客户名称" prop="name">
<el-input <el-input
v-model="queryParams.name" v-model="queryParams.name"
class="!w-240px" placeholder="搜索客户名称"
clearable clearable
placeholder="请输入客户名称"
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
:prefix-icon="Search"
/> />
</el-form-item> <el-button type="primary" :icon="Filter" @click="filterDrawerVisible = true" />
<el-form-item label="手机" prop="mobile"> </div>
<el-input <div class="mobile-page__actions">
v-model="queryParams.mobile" <el-button type="primary" @click="openForm('create')" v-hasPermi="['crm:customer:create']">
class="!w-240px" <Icon icon="ep:plus" class="mr-5px" /> 新增
clearable
placeholder="请输入手机"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="所属行业" prop="industryId">
<el-select
v-model="queryParams.industryId"
class="!w-240px"
clearable
placeholder="请选择所属行业"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="客户级别" prop="level">
<el-select
v-model="queryParams.level"
class="!w-240px"
clearable
placeholder="请选择客户级别"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="客户来源" prop="source">
<el-select
v-model="queryParams.source"
class="!w-240px"
clearable
placeholder="请选择客户来源"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon class="mr-5px" icon="ep:search" />
搜索
</el-button> </el-button>
<el-button @click="resetQuery"> <el-button type="warning" plain @click="handleImport" v-hasPermi="['crm:customer:import']">
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
<el-button v-hasPermi="['crm:customer:create']" type="primary" @click="openForm('create')">
<Icon class="mr-5px" icon="ep:plus" />
新增
</el-button>
<el-button v-hasPermi="['crm:customer:import']" plain type="warning" @click="handleImport">
<Icon icon="ep:upload" />
导入 导入
</el-button> </el-button>
<el-button <el-button
v-hasPermi="['crm:customer:export']"
:loading="exportLoading"
plain
type="success" type="success"
plain
@click="handleExport" @click="handleExport"
:loading="exportLoading"
v-hasPermi="['crm:customer:export']"
> >
<Icon class="mr-5px" icon="ep:download" />
导出 导出
</el-button> </el-button>
</el-form-item> </div>
</el-form> </div>
</ContentWrap>
<!-- 列表 --> <!-- Tab 切换 -->
<ContentWrap> <div class="mobile-page__tabs">
<el-tabs v-model="activeName" @tab-click="handleTabClick"> <el-tabs v-model="activeName" @tab-click="handleTabClick">
<el-tab-pane label="我负责的" name="1" /> <el-tab-pane label="我负责的" name="1" />
<el-tab-pane label="我参与的" name="2" /> <el-tab-pane label="我参与的" name="2" />
<el-tab-pane label="下属负责的" name="3" /> <el-tab-pane label="下属负责的" name="3" />
</el-tabs> </el-tabs>
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true"> </div>
<el-table-column align="center" fixed="left" label="客户名称" prop="name" width="160">
<template #default="scope"> <!-- 客户列表 -->
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)"> <div class="mobile-page__content" v-loading="loading">
{{ scope.row.name }} <div class="mobile-item-list">
</el-link> <div
</template> v-for="item in list"
</el-table-column> :key="item.id"
<el-table-column align="center" label="客户来源" prop="source" width="100"> class="mobile-item-card mobile-item-card--clickable"
<template #default="scope"> @click="openDetail(item.id)"
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" /> >
</template> <div class="mobile-item-card__header">
</el-table-column> <span class="mobile-item-card__name">{{ item.name }}</span>
<el-table-column align="center" label="手机" prop="mobile" width="120" /> <el-tag v-if="item.dealStatus" type="success" size="small">已成交</el-tag>
<el-table-column align="center" label="电话" prop="telephone" width="130" /> <el-tag v-else type="info" size="small">未成交</el-tag>
<el-table-column align="center" label="邮箱" prop="email" width="180" /> </div>
<el-table-column align="center" label="客户级别" prop="level" width="135"> <div class="mobile-item-card__body">
<template #default="scope"> <div class="mobile-item-card__info-row">
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" /> <span class="mobile-item-card__info-label">客户来源</span>
</template> <span class="mobile-item-card__info-value">
</el-table-column> <dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="item.source" />
<el-table-column align="center" label="客户行业" prop="industryId" width="100"> </span>
<template #default="scope"> </div>
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="scope.row.industryId" /> <div class="mobile-item-card__info-row">
</template> <span class="mobile-item-card__info-label">手机</span>
</el-table-column> <span class="mobile-item-card__info-value">{{ item.mobile || '-' }}</span>
<el-table-column </div>
:formatter="dateFormatter" <div class="mobile-item-card__info-row">
align="center" <span class="mobile-item-card__info-label">负责人</span>
label="下次联系时间" <span class="mobile-item-card__info-value">{{ item.ownerUserName || '-' }}</span>
prop="contactNextTime" </div>
width="180px" <div class="mobile-item-card__info-row">
/> <span class="mobile-item-card__info-label">客户级别</span>
<el-table-column align="center" label="备注" prop="remark" width="200" /> <span class="mobile-item-card__info-value">
<el-table-column align="center" label="锁定状态" prop="lockStatus"> <dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="item.level" />
<template #default="scope"> </span>
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.lockStatus" /> </div>
</template> <div class="mobile-item-card__info-row">
</el-table-column> <span class="mobile-item-card__info-label">最后跟进</span>
<el-table-column align="center" label="成交状态" prop="dealStatus"> <span class="mobile-item-card__info-value">{{ item.contactLastTime ? dateFormatter(null, null, item.contactLastTime) : '-' }}</span>
<template #default="scope"> </div>
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.dealStatus" /> </div>
</template> <div class="mobile-item-card__footer" @click.stop>
</el-table-column> <el-button
<el-table-column size="small"
:formatter="dateFormatter" type="primary"
align="center" @click="openForm('update', item.id)"
label="最后跟进时间" v-hasPermi="['crm:customer:update']"
prop="contactLastTime" >编辑</el-button>
width="180px" <el-button
/> size="small"
<el-table-column align="center" label="最后跟进记录" prop="contactLastContent" width="200" /> type="danger"
<el-table-column align="center" label="地址" prop="detailAddress" width="180" /> @click="handleDelete(item.id)"
<el-table-column align="center" label="距离进入公海天数" prop="poolDay" width="140"> v-hasPermi="['crm:customer:delete']"
<template #default="scope"> {{ scope.row.poolDay }} </template> >删除</el-button>
</el-table-column> </div>
<el-table-column align="center" label="负责人" prop="ownerUserName" width="100px" /> </div>
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" /> <div v-if="list.length === 0 && !loading" class="mobile-empty-tip">暂无客户数据</div>
<el-table-column </div>
:formatter="dateFormatter" <!-- 分页 -->
align="center" <div class="mobile-pagination" v-if="total > 0">
label="更新时间" <el-pagination
prop="updateTime" v-model:current-page="queryParams.pageNo"
width="180px" v-model:page-size="queryParams.pageSize"
/> :total="total"
<el-table-column :page-sizes="[10, 20]"
:formatter="dateFormatter" layout="total, prev, pager, next"
align="center" :pager-count="5"
label="创建时间" @size-change="getList"
prop="createTime" @current-change="getList"
width="180px" />
/> </div>
<el-table-column align="center" label="创建人" prop="creatorName" width="100px" /> </div>
<el-table-column align="center" fixed="right" label="操作" min-width="150"> </div>
<template #default="scope">
<el-button <!-- 筛选抽屉 -->
v-hasPermi="['crm:customer:update']" <el-drawer
link v-model="filterDrawerVisible"
type="primary" title="筛选条件"
@click="openForm('update', scope.row.id)" direction="rtl"
> size="100%"
编辑 :append-to-body="true"
</el-button> class="mobile-form-drawer"
<el-button >
v-hasPermi="['crm:customer:delete']" <div class="mobile-form">
link <div class="mobile-form__section">
type="danger" <div class="mobile-form__section-title">筛选条件</div>
@click="handleDelete(scope.row.id)" <el-form :model="queryParams" ref="queryFormRef" label-position="top">
> <el-form-item label="客户名称" prop="name">
删除 <el-input v-model="queryParams.name" placeholder="请输入客户名称" clearable />
</el-button> </el-form-item>
</template> <el-form-item label="手机" prop="mobile">
</el-table-column> <el-input v-model="queryParams.mobile" placeholder="请输入手机" clearable />
</el-table> </el-form-item>
<!-- 分页 --> <el-form-item label="所属行业" prop="industryId">
<Pagination <el-select v-model="queryParams.industryId" placeholder="请选择所属行业" clearable style="width: 100%">
v-model:limit="queryParams.pageSize" <el-option
v-model:page="queryParams.pageNo" v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)"
:total="total" :key="dict.value"
@pagination="getList" :label="dict.label"
/> :value="dict.value"
</ContentWrap> />
</el-select>
</el-form-item>
<el-form-item label="客户级别" prop="level">
<el-select v-model="queryParams.level" placeholder="请选择客户级别" clearable style="width: 100%">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="客户来源" prop="source">
<el-select v-model="queryParams.source" placeholder="请选择客户来源" clearable style="width: 100%">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-form>
</div>
<div class="mobile-form__footer">
<el-button @click="resetQuery" style="flex: 1">重置</el-button>
<el-button type="primary" @click="handleFilterConfirm" style="flex: 1">确认</el-button>
</div>
</div>
</el-drawer>
<!-- 表单弹窗添加/修改 --> <!-- 表单弹窗添加/修改 -->
<CustomerForm ref="formRef" @success="getList" /> <CustomerForm ref="formRef" @success="getList" />
@@ -228,6 +184,7 @@ import * as CustomerApi from '@/api/crm/customer'
import CustomerForm from './CustomerForm.vue' import CustomerForm from './CustomerForm.vue'
import CustomerImportForm from './CustomerImportForm.vue' import CustomerImportForm from './CustomerImportForm.vue'
import { TabsPaneContext } from 'element-plus' import { TabsPaneContext } from 'element-plus'
import { Search, Filter } from '@element-plus/icons-vue'
defineOptions({ name: 'CrmCustomer' }) defineOptions({ name: 'CrmCustomer' })
@@ -237,6 +194,7 @@ const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中 const loading = ref(true) // 列表的加载中
const total = ref(0) // 列表的总页数 const total = ref(0) // 列表的总页数
const list = ref([]) // 列表的数据 const list = ref([]) // 列表的数据
const filterDrawerVisible = ref(false) // 筛选抽屉
const queryParams = reactive({ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
@@ -328,6 +286,12 @@ const handleExport = async () => {
} }
} }
/** 筛选确认 */
const handleFilterConfirm = () => {
filterDrawerVisible.value = false
handleQuery()
}
/** 监听路由变化更新列表 */ /** 监听路由变化更新列表 */
watch( watch(
() => currentRoute.value, () => currentRoute.value,
@@ -341,3 +305,138 @@ onMounted(() => {
getList() getList()
}) })
</script> </script>
<style lang="scss" scoped>
.mobile-page {
padding: 12px;
background: #f5f7fa;
min-height: 100vh;
}
.mobile-page__header {
margin-bottom: 12px;
}
.mobile-page__search {
display: flex;
gap: 8px;
margin-bottom: 10px;
}
.mobile-page__actions {
display: flex;
gap: 8px;
}
.mobile-page__tabs {
background: #fff;
border-radius: 10px;
padding: 0 12px;
margin-bottom: 12px;
}
.mobile-page__content {
min-height: 200px;
}
.mobile-item-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.mobile-item-card {
background: #fff;
border-radius: 10px;
padding: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
&--clickable {
cursor: pointer;
transition: all 0.2s;
&:active {
background: #f5f5f5;
}
}
&__header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
&__name {
flex: 1;
font-size: 15px;
font-weight: 600;
color: #303133;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&__body {
font-size: 13px;
}
&__info-row {
display: flex;
justify-content: space-between;
padding: 4px 0;
}
&__info-label {
color: #909399;
flex-shrink: 0;
}
&__info-value {
color: #606266;
text-align: right;
}
&__footer {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid #f0f0f0;
}
}
.mobile-empty-tip {
text-align: center;
color: #909399;
padding: 40px 0;
font-size: 14px;
}
.mobile-pagination {
margin-top: 12px;
display: flex;
justify-content: center;
:deep(.el-pagination) {
flex-wrap: wrap;
justify-content: center;
}
}
.mobile-form {
padding: 0 4px;
}
.mobile-form__section {
background: #fff;
border-radius: 10px;
padding: 14px;
margin-bottom: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.mobile-form__section-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
.mobile-form__footer {
position: sticky;
bottom: 0;
background: #fff;
padding: 12px 16px;
padding-bottom: calc(12px + constant(safe-area-inset-bottom));
padding-bottom: calc(12px + env(safe-area-inset-bottom));
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 12px;
z-index: 10;
margin: 0 -4px;
}
</style>

View File

@@ -1,57 +1,72 @@
<template> <template>
<Dialog :title="dialogTitle" v-model="dialogVisible"> <el-drawer
<el-form v-model="dialogVisible"
ref="formRef" :title="dialogTitle"
:model="formData" direction="rtl"
:rules="formRules" size="100%"
label-width="200px" :close-on-press-escape="true"
v-loading="formLoading" :destroy-on-close="true"
> class="mobile-form-drawer"
<el-form-item label="规则适用人群" prop="userIds"> >
<el-select multiple filterable v-model="formData.userIds"> <div class="mobile-form" v-loading="formLoading">
<el-option <el-form
v-for="item in userOptions" ref="formRef"
:key="item.id" :model="formData"
:label="item.nickname" :rules="formRules"
:value="item.id" label-position="top"
/>
</el-select>
</el-form-item>
<el-form-item label="规则适用部门" prop="deptIds">
<el-tree-select
v-model="formData.deptIds"
:data="deptTree"
:props="defaultProps"
multiple
filterable
check-strictly
node-key="id"
placeholder="请选择规则适用部门"
/>
</el-form-item>
<el-form-item
:label="
formData.type === LimitConfType.CUSTOMER_QUANTITY_LIMIT
? '拥有客户数上限'
: '锁定客户数上限'
"
prop="maxCount"
> >
<el-input-number v-model="formData.maxCount" placeholder="请输入数量上限" /> <div class="mobile-form__section">
</el-form-item> <div class="mobile-form__section-title">规则设置</div>
<el-form-item <el-form-item label="规则适用人群" prop="userIds">
label="成交客户是否占用拥有客户数" <el-select multiple filterable v-model="formData.userIds" style="width: 100%">
v-if="formData.type === LimitConfType.CUSTOMER_QUANTITY_LIMIT" <el-option
prop="dealCountEnabled" v-for="item in userOptions"
> :key="item.id"
<el-switch v-model="formData.dealCountEnabled" /> :label="item.nickname"
</el-form-item> :value="item.id"
</el-form> />
<template #footer> </el-select>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button> </el-form-item>
<el-button @click="dialogVisible = false"> </el-button> <el-form-item label="规则适用部门" prop="deptIds">
</template> <el-tree-select
</Dialog> v-model="formData.deptIds"
:data="deptTree"
:props="defaultProps"
multiple
filterable
check-strictly
node-key="id"
placeholder="请选择规则适用部门"
style="width: 100%"
/>
</el-form-item>
<el-form-item
:label="
formData.type === LimitConfType.CUSTOMER_QUANTITY_LIMIT
? '拥有客户数上限'
: '锁定客户数上限'
"
prop="maxCount"
>
<el-input-number v-model="formData.maxCount" placeholder="请输入数量上限" style="width: 100%" />
</el-form-item>
<el-form-item
label="成交客户是否占用拥有客户数"
v-if="formData.type === LimitConfType.CUSTOMER_QUANTITY_LIMIT"
prop="dealCountEnabled"
>
<el-switch v-model="formData.dealCountEnabled" />
</el-form-item>
</div>
</el-form>
<!-- 底部操作按钮 -->
<div class="mobile-form__footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
</div>
</div>
</el-drawer>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import * as CustomerLimitConfigApi from '@/api/crm/customer/limitConfig' import * as CustomerLimitConfigApi from '@/api/crm/customer/limitConfig'
@@ -148,3 +163,44 @@ const resetForm = () => {
formRef.value?.resetFields() formRef.value?.resetFields()
} }
</script> </script>
<style lang="scss" scoped>
.mobile-form {
padding: 0 4px;
}
.mobile-form__section {
background: #fff;
border-radius: 10px;
padding: 14px;
margin-bottom: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.mobile-form__section-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
.mobile-form__footer {
position: sticky;
bottom: 0;
background: #fff;
padding: 12px 16px;
padding-bottom: calc(12px + constant(safe-area-inset-bottom));
padding-bottom: calc(12px + env(safe-area-inset-bottom));
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 12px;
z-index: 10;
margin: 0 -4px;
.el-button {
flex: 1;
height: 40px;
font-size: 15px;
}
}
</style>

View File

@@ -1,84 +1,83 @@
<template> <template>
<el-button plain @click="handleQuery"> <Icon icon="ep:refresh" class="mr-5px" /> 刷新 </el-button> <div class="mobile-list-page">
<el-button <!-- 操作按钮 -->
type="primary" <div class="mobile-list-page__actions">
plain <el-button plain size="small" @click="handleQuery">
@click="openForm('create')" <Icon icon="ep:refresh" class="mr-5px" /> 刷新
v-hasPermi="['crm:customer-limit-config:create']" </el-button>
> <el-button
<Icon icon="ep:plus" class="mr-5px" /> 新增 type="primary"
</el-button> size="small"
<el-table @click="openForm('create')"
v-loading="loading" v-hasPermi="['crm:customer-limit-config:create']"
:data="list" >
:stripe="true" <Icon icon="ep:plus" class="mr-5px" /> 新增
:show-overflow-tooltip="true" </el-button>
class="mt-4" </div>
>
<el-table-column label="编号" align="center" prop="id" /> <!-- 列表 -->
<el-table-column <div class="mobile-item-list" v-loading="loading">
label="规则适用人群" <div
align="center" v-for="item in list"
:formatter="(row) => row.users?.map((user: any) => user.nickname).join('')" :key="item.id"
/> class="mobile-item-card"
<el-table-column >
label="规则适用部门" <div class="mobile-item-card__header">
align="center" <span class="mobile-item-card__name">规则 #{{ item.id }}</span>
:formatter="(row) => row.depts?.map((dept: any) => dept.name).join('')" <el-tag type="primary" size="small">上限 {{ item.maxCount }}</el-tag>
/> </div>
<el-table-column <div class="mobile-item-card__body">
:label=" <div class="mobile-item-card__info-row">
confType === LimitConfType.CUSTOMER_QUANTITY_LIMIT ? '拥有客户数上限' : '锁定客户数上限' <span class="mobile-item-card__info-label">适用人群</span>
" <span class="mobile-item-card__info-value">{{ item.users?.map((user: any) => user.nickname).join('') || '-' }}</span>
align="center" </div>
prop="maxCount" <div class="mobile-item-card__info-row">
/> <span class="mobile-item-card__info-label">适用部门</span>
<el-table-column <span class="mobile-item-card__info-value">{{ item.depts?.map((dept: any) => dept.name).join('') || '-' }}</span>
v-if="confType === LimitConfType.CUSTOMER_QUANTITY_LIMIT" </div>
label="成交客户是否占用拥有客户数" <div class="mobile-item-card__info-row" v-if="confType === LimitConfType.CUSTOMER_QUANTITY_LIMIT">
align="center" <span class="mobile-item-card__info-label">成交客户占用</span>
prop="dealCountEnabled" <span class="mobile-item-card__info-value">
min-width="100" <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="item.dealCountEnabled" />
> </span>
<template #default="scope"> </div>
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.dealCountEnabled" /> <div class="mobile-item-card__info-row">
</template> <span class="mobile-item-card__info-label">创建时间</span>
</el-table-column> <span class="mobile-item-card__info-value">{{ dateFormatter(null, null, item.createTime) }}</span>
<el-table-column </div>
label="创建时间" </div>
align="center" <div class="mobile-item-card__footer">
prop="createTime" <el-button
:formatter="dateFormatter" size="small"
width="180px" type="primary"
/> @click="openForm('update', item.id)"
<el-table-column label="操作" align="center" min-width="110" fixed="right"> v-hasPermi="['crm:customer-limit-config:update']"
<template #default="scope"> >编辑</el-button>
<el-button <el-button
link size="small"
type="primary" type="danger"
@click="openForm('update', scope.row.id)" @click="handleDelete(item.id)"
v-hasPermi="['crm:customer-limit-config:update']" v-hasPermi="['crm:customer-limit-config:delete']"
> >删除</el-button>
编辑 </div>
</el-button> </div>
<el-button <div v-if="list.length === 0 && !loading" class="mobile-empty-tip">暂无配置数据</div>
link </div>
type="danger"
@click="handleDelete(scope.row.id)" <!-- 分页 -->
v-hasPermi="['crm:customer-limit-config:delete']" <div class="mobile-pagination" v-if="total > 0">
> <el-pagination
删除 v-model:current-page="queryParams.pageNo"
</el-button> v-model:page-size="queryParams.pageSize"
</template> :total="total"
</el-table-column> :page-sizes="[10, 20]"
</el-table> layout="total, prev, pager, next"
<!-- 分页 --> :pager-count="5"
<Pagination @size-change="getList"
:total="total" @current-change="getList"
v-model:page="queryParams.pageNo" />
v-model:limit="queryParams.pageSize" </div>
@pagination="getList" </div>
/>
<!-- 表单弹窗添加/修改 --> <!-- 表单弹窗添加/修改 -->
<CustomerLimitConfigForm ref="formRef" @success="getList" /> <CustomerLimitConfigForm ref="formRef" @success="getList" />
@@ -148,3 +147,79 @@ onMounted(() => {
getList() getList()
}) })
</script> </script>
<style lang="scss" scoped>
.mobile-list-page {
padding: 12px 0;
}
.mobile-list-page__actions {
display: flex;
gap: 8px;
margin-bottom: 12px;
}
.mobile-item-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.mobile-item-card {
background: #fff;
border-radius: 10px;
padding: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
&__header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
&__name {
flex: 1;
font-size: 15px;
font-weight: 600;
color: #303133;
}
&__body {
font-size: 13px;
}
&__info-row {
display: flex;
justify-content: space-between;
padding: 4px 0;
}
&__info-label {
color: #909399;
flex-shrink: 0;
}
&__info-value {
color: #606266;
text-align: right;
word-break: break-all;
}
&__footer {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid #f0f0f0;
}
}
.mobile-empty-tip {
text-align: center;
color: #909399;
padding: 40px 0;
font-size: 14px;
}
.mobile-pagination {
margin-top: 12px;
display: flex;
justify-content: center;
:deep(.el-pagination) {
flex-wrap: wrap;
justify-content: center;
}
}
</style>

View File

@@ -1,22 +1,36 @@
<template> <template>
<doc-alert title="【客户】客户管理、公海客户" url="https://doc.iocoder.cn/crm/customer/" /> <div class="mobile-page">
<doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" /> <!-- Tab 切换 -->
<div class="mobile-page__tabs">
<!-- 列表 --> <el-tabs v-model="activeTab">
<ContentWrap> <el-tab-pane label="拥有客户数限制" name="quantity">
<el-tabs> <CustomerLimitConfigList :confType="LimitConfType.CUSTOMER_QUANTITY_LIMIT" />
<el-tab-pane label="拥有客户数限制"> </el-tab-pane>
<CustomerLimitConfigList :confType="LimitConfType.CUSTOMER_QUANTITY_LIMIT" /> <el-tab-pane label="锁定客户数限制" name="lock">
</el-tab-pane> <CustomerLimitConfigList :confType="LimitConfType.CUSTOMER_LOCK_LIMIT" />
<el-tab-pane label="锁定客户数限制"> </el-tab-pane>
<CustomerLimitConfigList :confType="LimitConfType.CUSTOMER_LOCK_LIMIT" /> </el-tabs>
</el-tab-pane> </div>
</el-tabs> </div>
</ContentWrap>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import CustomerLimitConfigList from './CustomerLimitConfigList.vue' import CustomerLimitConfigList from './CustomerLimitConfigList.vue'
import { LimitConfType } from '@/api/crm/customer/limitConfig' import { LimitConfType } from '@/api/crm/customer/limitConfig'
defineOptions({ name: 'CrmCustomerLimitConfig' }) defineOptions({ name: 'CrmCustomerLimitConfig' })
const activeTab = ref('quantity')
</script> </script>
<style lang="scss" scoped>
.mobile-page {
padding: 12px;
background: #f5f7fa;
min-height: 100vh;
}
.mobile-page__tabs {
background: #fff;
border-radius: 10px;
padding: 0 12px;
}
</style>

View File

@@ -1,28 +1,42 @@
<template> <template>
<Dialog v-model="dialogVisible" title="分配客户"> <el-drawer
<el-form v-model="dialogVisible"
ref="formRef" title="分配客户"
v-loading="formLoading" direction="rtl"
:model="formData" size="100%"
:rules="formRules" :close-on-press-escape="true"
label-width="100px" :destroy-on-close="true"
> class="mobile-form-drawer"
<el-form-item label="负责人" prop="ownerUserId"> >
<el-select v-model="formData.ownerUserId" class="w-1/1"> <div class="mobile-form" v-loading="formLoading">
<el-option <el-form
v-for="item in userOptions" ref="formRef"
:key="item.id" :model="formData"
:label="item.nickname" :rules="formRules"
:value="item.id" label-position="top"
/> >
</el-select> <div class="mobile-form__section">
</el-form-item> <div class="mobile-form__section-title">分配设置</div>
</el-form> <el-form-item label="负责人" prop="ownerUserId">
<template #footer> <el-select v-model="formData.ownerUserId" style="width: 100%">
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button> <el-option
<el-button @click="dialogVisible = false"> </el-button> v-for="item in userOptions"
</template> :key="item.id"
</Dialog> :label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
</div>
</el-form>
<!-- 底部操作按钮 -->
<div class="mobile-form__footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
</div>
</div>
</el-drawer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import * as CustomerApi from '@/api/crm/customer' import * as CustomerApi from '@/api/crm/customer'
@@ -83,3 +97,44 @@ const resetForm = () => {
formRef.value?.resetFields() formRef.value?.resetFields()
} }
</script> </script>
<style lang="scss" scoped>
.mobile-form {
padding: 0 4px;
}
.mobile-form__section {
background: #fff;
border-radius: 10px;
padding: 14px;
margin-bottom: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.mobile-form__section-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
.mobile-form__footer {
position: sticky;
bottom: 0;
background: #fff;
padding: 12px 16px;
padding-bottom: calc(12px + constant(safe-area-inset-bottom));
padding-bottom: calc(12px + env(safe-area-inset-bottom));
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 12px;
z-index: 10;
margin: 0 -4px;
.el-button {
flex: 1;
height: 40px;
font-size: 15px;
}
}
</style>

View File

@@ -1,175 +1,142 @@
<template> <template>
<doc-alert title="【客户】客户管理、公海客户" url="https://doc.iocoder.cn/crm/customer/" /> <div class="mobile-page">
<doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" /> <!-- 搜索头部 -->
<div class="mobile-page__header">
<ContentWrap> <div class="mobile-page__search">
<!-- 搜索工作栏 -->
<el-form
ref="queryFormRef"
:inline="true"
:model="queryParams"
class="-mb-15px"
label-width="68px"
>
<el-form-item label="客户名称" prop="name">
<el-input <el-input
v-model="queryParams.name" v-model="queryParams.name"
class="!w-240px" placeholder="搜索客户名称"
clearable clearable
placeholder="请输入客户名称"
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
:prefix-icon="Search"
/> />
</el-form-item> <el-button type="primary" :icon="Filter" @click="filterDrawerVisible = true" />
<el-form-item label="手机" prop="mobile"> </div>
<el-input <div class="mobile-page__actions">
v-model="queryParams.mobile"
class="!w-240px"
clearable
placeholder="请输入手机"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="所属行业" prop="industryId">
<el-select
v-model="queryParams.industryId"
class="!w-240px"
clearable
placeholder="请选择所属行业"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="客户级别" prop="level">
<el-select
v-model="queryParams.level"
class="!w-240px"
clearable
placeholder="请选择客户级别"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="客户来源" prop="source">
<el-select
v-model="queryParams.source"
class="!w-240px"
clearable
placeholder="请选择客户来源"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon class="mr-5px" icon="ep:search" />
搜索
</el-button>
<el-button @click="resetQuery(undefined)">
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
<el-button <el-button
v-hasPermi="['crm:customer:export']"
:loading="exportLoading"
plain
type="success" type="success"
plain
@click="handleExport" @click="handleExport"
:loading="exportLoading"
v-hasPermi="['crm:customer:export']"
> >
<Icon class="mr-5px" icon="ep:download" />
导出 导出
</el-button> </el-button>
</el-form-item> </div>
</el-form> </div>
</ContentWrap>
<!-- 列表 --> <!-- 公海客户列表 -->
<ContentWrap> <div class="mobile-page__content" v-loading="loading">
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true"> <div class="mobile-item-list">
<el-table-column align="center" label="客户名称" fixed="left" prop="name" width="160"> <div
<template #default="scope"> v-for="item in list"
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)"> :key="item.id"
{{ scope.row.name }} class="mobile-item-card mobile-item-card--clickable"
</el-link> @click="openDetail(item.id)"
</template> >
</el-table-column> <div class="mobile-item-card__header">
<el-table-column align="center" label="客户来源" prop="source" width="100"> <span class="mobile-item-card__name">{{ item.name }}</span>
<template #default="scope"> <el-tag v-if="item.dealStatus" type="success" size="small">已成交</el-tag>
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" /> <el-tag v-else type="info" size="small">未成交</el-tag>
</template> </div>
</el-table-column> <div class="mobile-item-card__body">
<el-table-column label="手机" align="center" prop="mobile" width="120" /> <div class="mobile-item-card__info-row">
<el-table-column label="电话" align="center" prop="telephone" width="130" /> <span class="mobile-item-card__info-label">客户来源</span>
<el-table-column label="邮箱" align="center" prop="email" width="180" /> <span class="mobile-item-card__info-value">
<el-table-column align="center" label="客户级别" prop="level" width="135"> <dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="item.source" />
<template #default="scope"> </span>
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" /> </div>
</template> <div class="mobile-item-card__info-row">
</el-table-column> <span class="mobile-item-card__info-label">手机</span>
<el-table-column align="center" label="客户行业" prop="industryId" width="100"> <span class="mobile-item-card__info-value">{{ item.mobile || '-' }}</span>
<template #default="scope"> </div>
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="scope.row.industryId" /> <div class="mobile-item-card__info-row">
</template> <span class="mobile-item-card__info-label">客户级别</span>
</el-table-column> <span class="mobile-item-card__info-value">
<el-table-column <dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="item.level" />
:formatter="dateFormatter" </span>
align="center" </div>
label="下次联系时间" <div class="mobile-item-card__info-row">
prop="contactNextTime" <span class="mobile-item-card__info-label">最后跟进</span>
width="180px" <span class="mobile-item-card__info-value">{{ item.contactLastTime ? dateFormatter(null, null, item.contactLastTime) : '-' }}</span>
/> </div>
<el-table-column align="center" label="备注" prop="remark" width="200" /> </div>
<el-table-column align="center" label="成交状态" prop="dealStatus"> </div>
<template #default="scope"> <div v-if="list.length === 0 && !loading" class="mobile-empty-tip">暂无公海客户数据</div>
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.dealStatus" /> </div>
</template> <!-- 分页 -->
</el-table-column> <div class="mobile-pagination" v-if="total > 0">
<el-table-column <el-pagination
:formatter="dateFormatter" v-model:current-page="queryParams.pageNo"
align="center" v-model:page-size="queryParams.pageSize"
label="最后跟进时间" :total="total"
prop="contactLastTime" :page-sizes="[10, 20]"
width="180px" layout="total, prev, pager, next"
/> :pager-count="5"
<el-table-column align="center" label="最后跟进记录" prop="contactLastContent" width="200" /> @size-change="getList"
<el-table-column @current-change="getList"
:formatter="dateFormatter" />
align="center" </div>
label="更新时间" </div>
prop="updateTime" </div>
width="180px"
/> <!-- 筛选抽屉 -->
<el-table-column <el-drawer
:formatter="dateFormatter" v-model="filterDrawerVisible"
align="center" title="筛选条件"
label="创建时间" direction="rtl"
prop="createTime" size="100%"
width="180px" :append-to-body="true"
/> class="mobile-form-drawer"
<el-table-column align="center" label="创建人" prop="creatorName" width="100px" /> >
</el-table> <div class="mobile-form">
<!-- 分页 --> <div class="mobile-form__section">
<Pagination <div class="mobile-form__section-title">筛选条件</div>
v-model:limit="queryParams.pageSize" <el-form :model="queryParams" ref="queryFormRef" label-position="top">
v-model:page="queryParams.pageNo" <el-form-item label="客户名称" prop="name">
:total="total" <el-input v-model="queryParams.name" placeholder="请输入客户名称" clearable />
@pagination="getList" </el-form-item>
/> <el-form-item label="手机" prop="mobile">
</ContentWrap> <el-input v-model="queryParams.mobile" placeholder="请输入手机" clearable />
</el-form-item>
<el-form-item label="所属行业" prop="industryId">
<el-select v-model="queryParams.industryId" placeholder="请选择所属行业" clearable style="width: 100%">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="客户级别" prop="level">
<el-select v-model="queryParams.level" placeholder="请选择客户级别" clearable style="width: 100%">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="客户来源" prop="source">
<el-select v-model="queryParams.source" placeholder="请选择客户来源" clearable style="width: 100%">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-form>
</div>
<div class="mobile-form__footer">
<el-button @click="resetQuery" style="flex: 1">重置</el-button>
<el-button type="primary" @click="handleFilterConfirm" style="flex: 1">确认</el-button>
</div>
</div>
</el-drawer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@@ -177,6 +144,7 @@ import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download' import download from '@/utils/download'
import * as CustomerApi from '@/api/crm/customer' import * as CustomerApi from '@/api/crm/customer'
import { Search, Filter } from '@element-plus/icons-vue'
defineOptions({ name: 'CrmCustomerPool' }) defineOptions({ name: 'CrmCustomerPool' })
@@ -185,6 +153,7 @@ const message = useMessage() // 消息弹窗
const loading = ref(true) // 列表的加载中 const loading = ref(true) // 列表的加载中
const total = ref(0) // 列表的总页数 const total = ref(0) // 列表的总页数
const list = ref([]) // 列表的数据 const list = ref([]) // 列表的数据
const filterDrawerVisible = ref(false) // 筛选抽屉
const queryParams = ref({ const queryParams = ref({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
@@ -255,6 +224,12 @@ const handleExport = async () => {
} }
} }
/** 筛选确认 */
const handleFilterConfirm = () => {
filterDrawerVisible.value = false
handleQuery()
}
/** 监听路由变化更新列表 */ /** 监听路由变化更新列表 */
watch( watch(
() => currentRoute.value, () => currentRoute.value,
@@ -268,3 +243,124 @@ onMounted(() => {
getList() getList()
}) })
</script> </script>
<style lang="scss" scoped>
.mobile-page {
padding: 12px;
background: #f5f7fa;
min-height: 100vh;
}
.mobile-page__header {
margin-bottom: 12px;
}
.mobile-page__search {
display: flex;
gap: 8px;
margin-bottom: 10px;
}
.mobile-page__actions {
display: flex;
gap: 8px;
}
.mobile-page__content {
min-height: 200px;
}
.mobile-item-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.mobile-item-card {
background: #fff;
border-radius: 10px;
padding: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
&--clickable {
cursor: pointer;
transition: all 0.2s;
&:active {
background: #f5f5f5;
}
}
&__header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
&__name {
flex: 1;
font-size: 15px;
font-weight: 600;
color: #303133;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&__body {
font-size: 13px;
}
&__info-row {
display: flex;
justify-content: space-between;
padding: 4px 0;
}
&__info-label {
color: #909399;
flex-shrink: 0;
}
&__info-value {
color: #606266;
text-align: right;
}
}
.mobile-empty-tip {
text-align: center;
color: #909399;
padding: 40px 0;
font-size: 14px;
}
.mobile-pagination {
margin-top: 12px;
display: flex;
justify-content: center;
:deep(.el-pagination) {
flex-wrap: wrap;
justify-content: center;
}
}
.mobile-form {
padding: 0 4px;
}
.mobile-form__section {
background: #fff;
border-radius: 10px;
padding: 14px;
margin-bottom: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.mobile-form__section-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
.mobile-form__footer {
position: sticky;
bottom: 0;
background: #fff;
padding: 12px 16px;
padding-bottom: calc(12px + constant(safe-area-inset-bottom));
padding-bottom: calc(12px + env(safe-area-inset-bottom));
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 12px;
z-index: 10;
margin: 0 -4px;
}
</style>

View File

@@ -1,62 +1,55 @@
<template> <template>
<doc-alert title="【客户】客户管理、公海客户" url="https://doc.iocoder.cn/crm/customer/" /> <div class="mobile-page" v-loading="formLoading">
<doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" />
<ContentWrap>
<el-form <el-form
ref="formRef" ref="formRef"
:model="formData" :model="formData"
:rules="formRules" :rules="formRules"
label-width="160px" label-position="top"
v-loading="formLoading"
> >
<el-card shadow="never"> <!-- 公海规则设置 -->
<!-- 操作 --> <div class="mobile-form__section">
<template #header> <div class="mobile-form__section-header">
<div class="flex items-center justify-between"> <span class="mobile-form__section-title">客户公海规则设置</span>
<CardTitle title="客户公海规则设置" /> <el-button
<el-button type="primary"
type="primary" size="small"
@click="onSubmit" @click="onSubmit"
v-hasPermi="['crm:customer-pool-config:update']" v-hasPermi="['crm:customer-pool-config:update']"
> >
保存 保存
</el-button> </el-button>
</div> </div>
</template> <el-form-item label="客户公海规则" prop="enabled">
<!-- 表单 --> <el-radio-group v-model="formData.enabled" @change="changeEnable">
<el-form-item label="客户公海规则设置" prop="enabled"> <el-radio :value="false">不启用</el-radio>
<el-radio-group v-model="formData.enabled" @change="changeEnable" class="ml-4"> <el-radio :value="true">启用</el-radio>
<el-radio :value="false" size="large">不启用</el-radio>
<el-radio :value="true" size="large">启用</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<div v-if="formData.enabled"> </div>
<el-form-item>
<el-input-number class="mr-2" v-model="formData.contactExpireDays" /> <!-- 规则详情 -->
天不跟进或 <div class="mobile-form__section" v-if="formData.enabled">
<el-input-number class="mx-2" v-model="formData.dealExpireDays" /> <div class="mobile-form__section-title">规则详情</div>
天未成交 <div class="mobile-form__inline-group">
</el-form-item> <el-input-number v-model="formData.contactExpireDays" :min="1" style="width: 80px" />
<el-form-item label="提前提醒设置" prop="notifyEnabled"> <span class="mobile-form__inline-text">天不跟进或</span>
<el-radio-group <el-input-number v-model="formData.dealExpireDays" :min="1" style="width: 80px" />
v-model="formData.notifyEnabled" <span class="mobile-form__inline-text">天未成交</span>
@change="changeNotifyEnable"
class="ml-4"
>
<el-radio :value="false" size="large">不提醒</el-radio>
<el-radio :value="true" size="large">提醒</el-radio>
</el-radio-group>
</el-form-item>
<div v-if="formData.notifyEnabled">
<el-form-item>
提前 <el-input-number class="mx-2" v-model="formData.notifyDays" /> 天提醒
</el-form-item>
</div>
</div> </div>
</el-card> <el-form-item label="提前提醒设置" prop="notifyEnabled" style="margin-top: 16px">
<el-radio-group v-model="formData.notifyEnabled" @change="changeNotifyEnable">
<el-radio :value="false">不提醒</el-radio>
<el-radio :value="true">提醒</el-radio>
</el-radio-group>
</el-form-item>
<div class="mobile-form__inline-group" v-if="formData.notifyEnabled">
<span class="mobile-form__inline-text">提前</span>
<el-input-number v-model="formData.notifyDays" :min="1" style="width: 80px" />
<span class="mobile-form__inline-text">天提醒</span>
</div>
</div>
</el-form> </el-form>
</ContentWrap> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import * as CustomerPoolConfigApi from '@/api/crm/customer/poolConfig' import * as CustomerPoolConfigApi from '@/api/crm/customer/poolConfig'
@@ -134,3 +127,42 @@ onMounted(() => {
getConfig() getConfig()
}) })
</script> </script>
<style lang="scss" scoped>
.mobile-page {
padding: 12px;
background: #f5f7fa;
min-height: 100vh;
}
.mobile-form__section {
background: #fff;
border-radius: 10px;
padding: 14px;
margin-bottom: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.mobile-form__section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
.mobile-form__section-title {
font-size: 15px;
font-weight: 600;
color: #303133;
}
.mobile-form__inline-group {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin-top: 8px;
}
.mobile-form__inline-text {
color: #606266;
font-size: 14px;
}
</style>