添加系统码

This commit is contained in:
浅念
2025-07-21 18:12:16 +08:00
parent 31cd18fade
commit c7d890809c
7 changed files with 150 additions and 103 deletions
+4 -4
View File
@@ -65,16 +65,16 @@ export class CrmService {
}) })
} }
//标签列表/crm/tagGroup/list //标签列表/crm/tagGroup/list
static tagGrouplist() { static tagGrouplist(params: any) {
return request.get<any>({ return request.get<any>({
url: '/crm/tagGroup/list' url: '/crm/tagGroup/list',
params
}) })
} }
//更新客户标签 /crm/tagGroup/updatePivot //更新客户标签 /crm/tagGroup/updatePivot
static tagGroupupdatePivot(params: any) { static tagGroupupdatePivot(params: any) {
return request.get<Api.Work.AddResponse>({ return request.get<Api.Work.AddResponse>({
url: '/crm/tagGroup/updatePivot', url: '/crm/tagGroup/updatePivot'
params
}) })
} }
//跟进列表crm/follow/list //跟进列表crm/follow/list
+6
View File
@@ -6,6 +6,12 @@ export class UserService {
return request.get<Api.Brand.BrandInfo>({ return request.get<Api.Brand.BrandInfo>({
url: '/common/preset/corps' url: '/common/preset/corps'
}) })
}
//获取系统
static getsystemsList() {
return request.get<Api.Brand.BrandInfo>({
url: '/common/preset/systems'
})
} }
//注册 //注册
static register(params: any) { static register(params: any) {
+10
View File
@@ -25,6 +25,8 @@ export const useUserStore = defineStore(
const refreshToken = ref('') const refreshToken = ref('')
const companyid = ref('') const companyid = ref('')
const companyabbr = ref('') const companyabbr = ref('')
const systemsid = ref('')
const systemsabbr = ref('')
const loginType = ref<'feishu' | 'upwd'>('upwd') const loginType = ref<'feishu' | 'upwd'>('upwd')
const tokenExpireAt = ref<number>(0) const tokenExpireAt = ref<number>(0)
@@ -76,6 +78,11 @@ export const useUserStore = defineStore(
companyid.value = id companyid.value = id
companyabbr.value = abbr companyabbr.value = abbr
} }
const setSystems= (id: string, abbr: string) => {
systemsid.value = id
systemsabbr.value = abbr
}
function restoreTokenTimer() { function restoreTokenTimer() {
const expireAt = Number(localStorage.getItem('token_expire_at') || '0') const expireAt = Number(localStorage.getItem('token_expire_at') || '0')
if (accessToken.value && expireAt > Date.now()) { if (accessToken.value && expireAt > Date.now()) {
@@ -139,6 +146,8 @@ export const useUserStore = defineStore(
info, info,
companyid, companyid,
companyabbr, companyabbr,
systemsid,
systemsabbr,
searchHistory, searchHistory,
loginType, loginType,
accessToken, accessToken,
@@ -155,6 +164,7 @@ export const useUserStore = defineStore(
setLockPassword, setLockPassword,
setToken, setToken,
setCompany, setCompany,
setSystems,
setLoginType, setLoginType,
logOut, logOut,
restoreTokenTimer restoreTokenTimer
+2 -1
View File
@@ -47,9 +47,10 @@ axiosInstance.interceptors.request.use(
const { language } = useUserStore() const { language } = useUserStore()
const { loginType } = useUserStore() const { loginType } = useUserStore()
const { companyabbr } = useUserStore() const { companyabbr } = useUserStore()
const { systemsabbr } = useUserStore()
request.headers.set('Fdesk-Language', language) request.headers.set('Fdesk-Language', language)
request.headers.set('Fdesk-Corp-Abbr', companyabbr) request.headers.set('Fdesk-Corp-Abbr', companyabbr)
request.headers.set('Fdesk-System-Code', 'fdesk') request.headers.set('Fdesk-System-Code', systemsabbr)
request.headers.set('Fdesk-Login-Type', loginType) request.headers.set('Fdesk-Login-Type', loginType)
// 设置 token 和 请求头 // 设置 token 和 请求头
if (accessToken) { if (accessToken) {
+46 -2
View File
@@ -72,6 +72,18 @@
</ElOption> </ElOption>
</ElSelect> </ElSelect>
</ElFormItem> </ElFormItem>
<ElFormItem prop="company" label="系统">
<ElSelect v-model="formData.systems" class="account-select" @change="changesystems">
<ElOption
v-for="account in systemsList"
:key="account.id"
:label="account.name"
:value="account.id"
>
<span>{{ account.name }}</span>
</ElOption>
</ElSelect>
</ElFormItem>
<div class="drag-verify"> <div class="drag-verify">
<div class="drag-verify-content" :class="{ error: !isPassing && isClickPass }"> <div class="drag-verify-content" :class="{ error: !isPassing && isClickPass }">
<ArtDragVerify <ArtDragVerify
@@ -127,6 +139,18 @@
</ElOption> </ElOption>
</ElSelect> </ElSelect>
</ElFormItem> </ElFormItem>
<ElFormItem prop="company" label="系统">
<ElSelect v-model="formData.systems" class="account-select" @change="changesystems">
<ElOption
v-for="account in systemsList"
:key="account.id"
:label="account.name"
:value="account.id"
>
<span>{{ account.name }}</span>
</ElOption>
</ElSelect>
</ElFormItem>
</ElForm> </ElForm>
<div class="qrcode-login" v-if="!isInFeishu"> <div class="qrcode-login" v-if="!isInFeishu">
<div id="login_container"></div> <div id="login_container"></div>
@@ -175,13 +199,18 @@
const formRef = ref<FormInstance>() const formRef = ref<FormInstance>()
const companyid = ref('') const companyid = ref('')
companyid.value = useUserStore().companyid companyid.value = useUserStore().companyid
const systemsid = ref('')
systemsid.value = useUserStore().systemsid
const iscompany = ref(false) const iscompany = ref(false)
const { companyabbr } = useUserStore() const { companyabbr } = useUserStore()
//获取公司 getCompanyList //获取公司 getCompanyList
const companyList = ref<any>() const companyList = ref<any>()
//系统
const systemsList = ref<any>()
const feishuresponse=ref() const feishuresponse=ref()
const formData = reactive({ const formData = reactive({
account: '', account: '',
systems: useUserStore().companyid?Number(useUserStore().companyid):'',
company: useUserStore().companyid?Number(useUserStore().companyid):'', company: useUserStore().companyid?Number(useUserStore().companyid):'',
username: '', username: '',
password: '', password: '',
@@ -198,11 +227,19 @@
const { width } = useWindowSize() const { width } = useWindowSize()
const getcompanyList = async () => { const getcompanyList = async () => {
try { try {
// 这里调用获取品牌列表的接口 // 这里调用公司列表的接口
const response = await UserService.getCompanyList() const response = await UserService.getCompanyList()
companyList.value = response || [] companyList.value = response || []
} catch (error) { } } catch (error) { }
} }
const getsystemsList = async () => {
try {
// 这里调用公司列表的接口
const response = await UserService.getsystemsList()
systemsList.value = response || []
} catch (error) { }
}
// 切换语言 // 切换语言
const { locale } = useI18n() const { locale } = useI18n()
@@ -214,7 +251,7 @@
const getfeishuAppList = async () => { const getfeishuAppList = async () => {
try { try {
// 这里调用获取品牌列表的接口 // 这里调用飞书
feishuresponse.value = await UserService.getfeishuAppList({ corp_id: companyid.value }) feishuresponse.value = await UserService.getfeishuAppList({ corp_id: companyid.value })
if(activeLoginType.value=='qrcode'){ if(activeLoginType.value=='qrcode'){
feishulogin() feishulogin()
@@ -222,6 +259,12 @@
console.log(feishuresponse) console.log(feishuresponse)
} catch (error) { } } catch (error) { }
} }
//切换系统
const changesystems = (systems: any) => {
const systemsdata = systemsList.value.find((c: any) => c.id === systems)
systemsid.value = systems
userStore.setSystems(systems,systemsdata?.code)
}
//切换公司 //切换公司
const changeCompany = (company: any) => { const changeCompany = (company: any) => {
@@ -383,6 +426,7 @@
onMounted(() => { onMounted(() => {
getcompanyList() getcompanyList()
getsystemsList()
// 自动读取 query 参数并存入 userStore // 自动读取 query 参数并存入 userStore
const companyid = Array.isArray(route.query.corp_id) ? route.query.corp_id[0] : route.query.corp_id const companyid = Array.isArray(route.query.corp_id) ? route.query.corp_id[0] : route.query.corp_id
const companyabbr = Array.isArray(route.query.corp_abbr) ? route.query.corp_abbr[0] : route.query.corp_abbr const companyabbr = Array.isArray(route.query.corp_abbr) ? route.query.corp_abbr[0] : route.query.corp_abbr
+1 -1
View File
@@ -185,7 +185,7 @@ function addtag(){
} }
const tagGrouplistfn = async () => { const tagGrouplistfn = async () => {
try { try {
const res = await CrmService.tagGrouplist() const res = await CrmService.tagGrouplist({})
tagGroups.value = res || [] tagGroups.value = res || []
// 回显选中 // 回显选中
if (customerInfo.value && Array.isArray(customerInfo.value.tags)) { if (customerInfo.value && Array.isArray(customerInfo.value.tags)) {
+81 -95
View File
@@ -19,46 +19,43 @@
<!-- 筛选标签 --> <!-- 筛选标签 -->
<div class="customer-tags boxshadow"> <div class="customer-tags boxshadow">
<div class="filter-row"> <!-- 前3组 -->
<span class="filter-label">价值标签</span> <div class="filter-row" v-for="item in visibleTagGroups" :key="item.id">
<span class="filter-label">{{item.name}}</span>
<div class="filter-tags"> <div class="filter-tags">
<span v-for="item in valueTags" :key="item.label" :class="['tag-item', { 'tag-active': tagActiveMap['value'] === item.label }]" @click="handleTagClick('value', item.label)"> <span
{{ item.label }}({{ item.count }}) v-for="item1 in item.tags"
:key="item1.id"
:class="['tag-item', { 'tag-active': tagActiveMap[item.id] === item1.id }]"
@click="handleTagClick(item.id, item1.id)"
>
{{ item1.name }}({{ item1.count }})
</span> </span>
</div> </div>
</div> </div>
<div class="filter-row"> <!-- 更多 -->
<span class="filter-label">生命周期标签</span>
<div class="filter-tags">
<span v-for="item in lifeTags" :key="item.label" :class="['tag-item', { 'tag-active': tagActiveMap['life'] === item.label }]" @click="handleTagClick('life', item.label)">
{{ item.label }}({{ item.count }})
</span>
</div>
</div>
<div class="filter-row">
<span class="filter-label">行为标签</span>
<div class="filter-tags">
<span v-for="item in actionTags" :key="item.label" :class="['tag-item', { 'tag-active': tagActiveMap['action'] === item.label }]" @click="handleTagClick('action', item.label)">
{{ item.label }}({{ item.count }})
</span>
</div>
</div>
<transition name="fade"> <transition name="fade">
<div v-if="showMore" class="filter-row"> <div v-if="showMore">
<span class="filter-label">更多标签</span> <div class="filter-row" v-for="item in moreTagGroups" :key="item.id">
<div class="filter-tags"> <span class="filter-label">{{item.name}}</span>
<span v-for="item in moreTags" :key="item.label" :class="['tag-item', { 'tag-active': tagActiveMap['more'] === item.label }]" @click="handleTagClick('more', item.label)"> <div class="filter-tags">
{{ item.label }}({{ item.count }}) <span
</span> v-for="item1 in item.tags"
:key="item1.id"
:class="['tag-item', { 'tag-active': tagActiveMap[item.id] === item1.id }]"
@click="handleTagClick(item.id, item1.id)"
>
{{ item1.name }}({{ item1.count }})
</span>
</div>
</div> </div>
</div> </div>
</transition> </transition>
<div class="filter-more-btn"> <div class="filter-more-btn" v-if="moreTagGroups.length">
<el-link :underline="false" @click="showMore = !showMore"> <el-link underline="never" @click="showMore = !showMore">
{{ showMore ? '收起' : '更多' }} {{ showMore ? '收起' : '更多' }}
<i v-if="showMore" class="iconfont-sys"> &#xe864;</i> <i v-if="showMore" class="iconfont-sys"> &#xe864;</i>
<i v-else class="iconfont-sys">&#xe867;</i> <i v-else class="iconfont-sys">&#xe867;</i>
</el-link> </el-link>
</div> </div>
</div> </div>
@@ -90,8 +87,8 @@
</el-table-column> </el-table-column>
<el-table-column fixed="right" label="操作" width="200px" align="center"> <el-table-column fixed="right" label="操作" width="200px" align="center">
<template #default="scope"> <template #default="scope">
<el-link type="primary" :underline="false" style="margin-right: 8px;" @click="goEdit(scope.row)"> <i class="iconfont-sys"> &#xe70f;</i>查看</el-link> <el-link type="primary" underline="never" style="margin-right: 8px;" @click="goEdit(scope.row)"> <i class="iconfont-sys"> &#xe70f;</i>查看</el-link>
<el-link type="primary" :underline="false" @click="goAddFollow(scope.row)"> <i class="iconfont-sys"> &#xe6bf;</i> 添加跟进</el-link> <el-link type="primary" underline="never" @click="goAddFollow(scope.row)"> <i class="iconfont-sys"> &#xe6bf;</i> 添加跟进</el-link>
</template> </template>
</el-table-column> </el-table-column>
</template> </template>
@@ -102,7 +99,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref, reactive, computed, onMounted, watch } from 'vue'
import { Plus } from '@element-plus/icons-vue' import { Plus } from '@element-plus/icons-vue'
import { CrmService } from '@/api/crmApi' import { CrmService } from '@/api/crmApi'
const searchValue = ref('') const searchValue = ref('')
@@ -121,40 +118,24 @@ const selectOptions = [
{ label: '近三天', value: '1' }, { label: '近三天', value: '1' },
{ label: '近七天', value: '2' } { label: '近七天', value: '2' }
] ]
const valueTags = [
{ label: '高价值', count: 10 },
{ label: '中价值', count: 10 },
{ label: '低价值', count: 10 },
{ label: '潜在高价值', count: 10 }
]
const lifeTags = [
{ label: '新客户', count: 10 },
{ label: '活跃', count: 10 },
{ label: '深度', count: 10 },
{ label: '流失', count: 10 }
]
const actionTags = [
{ label: '高频购买', count: 10 },
{ label: '大单', count: 10 },
{ label: '促销敏感', count: 10 }
]
const moreTags = [
{ label: '标签A', count: 5 },
{ label: '标签B', count: 3 }
]
//获取标签 //获取标签
const tagGroups = ref<any[]>([]) type TagGroup = { id: string; name: string; tags: { id: string; name: string; count: number }[] }
const tagGroups = ref<TagGroup[]>([])
const tagGrouplistfn = async () => { const tagGrouplistfn = async () => {
try { try {
const res = await CrmService.tagGrouplist() const res = await CrmService.tagGrouplist({scene:'customers'})
tagGroups.value = res || [] tagGroups.value = res || []
} catch (error) { } } catch (error) { }
} }
// 计算前3组和更多组
defineProps()
const visibleTagGroups = computed(() => tagGroups.value.slice(0, 3))
const moreTagGroups = computed(() => tagGroups.value.slice(3))
// 表格数据 // 表格数据
const tableData = ref<any[]>([]) const tableData = ref<any[]>([])
onMounted(() => { onMounted(() => {
gettableList() gettableList()
tagGrouplistfn()
}) })
// tabs 切换联动 // tabs 切换联动
watch([activeTab, searchValue, selectValue], () => { watch([activeTab, searchValue, selectValue], () => {
@@ -162,6 +143,7 @@ watch([activeTab, searchValue, selectValue], () => {
gettableList() gettableList()
}) })
// 获取列表数据 // 获取列表数据
const tagActiveMap = reactive<{ [groupId: string]: string }>({})
const gettableList = async () => { const gettableList = async () => {
loading.value = true loading.value = true
try { try {
@@ -173,11 +155,12 @@ const gettableList = async () => {
if (searchValue.value) params.keyword = searchValue.value if (searchValue.value) params.keyword = searchValue.value
if (selectValue.value) params.follow_last = selectValue.value if (selectValue.value) params.follow_last = selectValue.value
if (activeTab.value && activeTab.value !== '') params.follow = activeTab.value if (activeTab.value && activeTab.value !== '') params.follow = activeTab.value
// 标签参数 // 标签参数,所有选中的标签id组成数组
if (tagActiveMap.value) params.value_tag = tagActiveMap.value const selectedTagIds = Object.values(tagActiveMap).filter(Boolean)
if (tagActiveMap.life) params.life_tag = tagActiveMap.life if (selectedTagIds.length) {
if (tagActiveMap.action) params.action_tag = tagActiveMap.action params.tag_id = selectedTagIds
if (tagActiveMap.more) params.more_tag = tagActiveMap.more }
console.log(params)
const { data, current, size, total } = await CrmService.getcustomerList(params) const { data, current, size, total } = await CrmService.getcustomerList(params)
tableData.value = data.map((item: any, index: number) => ({ tableData.value = data.map((item: any, index: number) => ({
...item ...item
@@ -191,13 +174,12 @@ const gettableList = async () => {
} }
// 处理表格分页变化 // 处理表格分页变化
const handleSizeChange = (newPageSize: number) => { const handleSizeChange = (newPageSize: number) => {
pagination.pageSize = newPageSize pagination.pageSize = newPageSize
// getWorkList() // getWorkList()
} }
const handleCurrentChange = (newCurrentPage: number) => { const handleCurrentChange = (newCurrentPage: number) => {
pagination.currentPage = newCurrentPage pagination.currentPage = newCurrentPage
// getWorkList() // getWorkList()
} }
function statusClass(status: any) { function statusClass(status: any) {
if (status === 0) return 'status-following' if (status === 0) return 'status-following'
@@ -205,32 +187,28 @@ function statusClass(status: any) {
if (status === 2) return 'status-lost' if (status === 2) return 'status-lost'
return '' return ''
} }
const router = useRouter() const router = useRouter()
function goAdd() { function goAdd() {
router.push('/crmpc/customer/add') router.push('/crmpc/customer/add')
} }
function goEdit(row: any) { function goEdit(row: any) {
router.push({ router.push({
path: '/crmpc/customer/show', path: '/crmpc/customer/show',
query: { id: row.id } query: { id: row.id }
}) })
} }
function goAddFollow(row: any) { function goAddFollow(row: any) {
router.push({ router.push({
path: '/crmpc/customer/followadd', path: '/crmpc/customer/followadd',
query: { id: row.id } // 可带客户名等参数 query: { id: row.id } // 可带客户名等参数
}) })
} }
// 标签选中状态,key为组id,值为选中的标签id
// 标签选中状态,key为组名,值为选中的标签label function handleTagClick(groupId: string, tagId: string) {
const tagActiveMap = reactive<{ [group: string]: string }>({}) if (tagActiveMap[groupId] === tagId) {
tagActiveMap[groupId] = '' // 取消选中
function handleTagClick(group: string, label: string) {
if (tagActiveMap[group] === label) {
tagActiveMap[group] = '' // 取消选中
} else { } else {
tagActiveMap[group] = label // 选中 tagActiveMap[groupId] = tagId // 选中
} }
pagination.currentPage = 1 pagination.currentPage = 1
gettableList() gettableList()
@@ -338,6 +316,14 @@ function handleTagClick(group: string, label: string) {
.fade-enter-from, .fade-leave-to { .fade-enter-from, .fade-leave-to {
opacity: 0; opacity: 0;
} }
.tag-item { cursor: pointer; border-radius: 4px; padding: 2px 10px; margin-right: 8px; transition: background 0.2s; } .tag-item {
cursor: pointer;
border-radius: 4px;
padding: 2px 10px;
margin-right: 8px;
transition: background 0.2s;
min-width: 100px;
display: inline-block;
}
.tag-active { background: #3471FF; color: #fff; } .tag-active { background: #3471FF; color: #fff; }
</style> </style>