Merge branch 'dev' of https://gitee.com/yudaocode/yudao-ui-admin-vue3
# Conflicts: # src/api/infra/codegen/index.ts # src/views/system/sensitiveWord/index.vueliutao_branch
commit
7f3950018e
@ -0,0 +1,58 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface CrmStatisticFunnelRespVO {
|
||||||
|
customerCount: number // 客户数
|
||||||
|
businessCount: number // 商机数
|
||||||
|
businessWinCount: number // 赢单数
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CrmStatisticsBusinessSummaryByDateRespVO {
|
||||||
|
time: string // 时间
|
||||||
|
businessCreateCount: number // 商机数
|
||||||
|
totalPrice: number | string // 商机金额
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CrmStatisticsBusinessInversionRateSummaryByDateRespVO {
|
||||||
|
time: string // 时间
|
||||||
|
businessCount: number // 商机数量
|
||||||
|
businessWinCount: number // 赢单商机数
|
||||||
|
}
|
||||||
|
|
||||||
|
// 客户分析 API
|
||||||
|
export const StatisticFunnelApi = {
|
||||||
|
// 1. 获取销售漏斗统计数据
|
||||||
|
getFunnelSummary: (params: any) => {
|
||||||
|
return request.get({
|
||||||
|
url: '/crm/statistics-funnel/get-funnel-summary',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 2. 获取商机结束状态统计
|
||||||
|
getBusinessSummaryByEndStatus: (params: any) => {
|
||||||
|
return request.get({
|
||||||
|
url: '/crm/statistics-funnel/get-business-summary-by-end-status',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 3. 获取新增商机分析(按日期)
|
||||||
|
getBusinessSummaryByDate: (params: any) => {
|
||||||
|
return request.get({
|
||||||
|
url: '/crm/statistics-funnel/get-business-summary-by-date',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 4. 获取商机转化率分析(按日期)
|
||||||
|
getBusinessInversionRateSummaryByDate: (params: any) => {
|
||||||
|
return request.get({
|
||||||
|
url: '/crm/statistics-funnel/get-business-inversion-rate-summary-by-date',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 5. 获取商机列表(按日期)
|
||||||
|
getBusinessPageByDate: (params: any) => {
|
||||||
|
return request.get({
|
||||||
|
url: '/crm/statistics-funnel/get-business-page-by-date',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,122 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export type CodegenTableVO = {
|
||||||
|
id: number
|
||||||
|
tableId: number
|
||||||
|
isParentMenuIdValid: boolean
|
||||||
|
dataSourceConfigId: number
|
||||||
|
scene: number
|
||||||
|
tableName: string
|
||||||
|
tableComment: string
|
||||||
|
remark: string
|
||||||
|
moduleName: string
|
||||||
|
businessName: string
|
||||||
|
className: string
|
||||||
|
classComment: string
|
||||||
|
author: string
|
||||||
|
createTime: Date
|
||||||
|
updateTime: Date
|
||||||
|
templateType: number
|
||||||
|
parentMenuId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CodegenColumnVO = {
|
||||||
|
id: number
|
||||||
|
tableId: number
|
||||||
|
columnName: string
|
||||||
|
dataType: string
|
||||||
|
columnComment: string
|
||||||
|
nullable: number
|
||||||
|
primaryKey: number
|
||||||
|
ordinalPosition: number
|
||||||
|
javaType: string
|
||||||
|
javaField: string
|
||||||
|
dictType: string
|
||||||
|
example: string
|
||||||
|
createOperation: number
|
||||||
|
updateOperation: number
|
||||||
|
listOperation: number
|
||||||
|
listOperationCondition: string
|
||||||
|
listOperationResult: number
|
||||||
|
htmlType: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DatabaseTableVO = {
|
||||||
|
name: string
|
||||||
|
comment: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CodegenDetailVO = {
|
||||||
|
table: CodegenTableVO
|
||||||
|
columns: CodegenColumnVO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CodegenPreviewVO = {
|
||||||
|
filePath: string
|
||||||
|
code: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CodegenUpdateReqVO = {
|
||||||
|
table: CodegenTableVO | any
|
||||||
|
columns: CodegenColumnVO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CodegenCreateListReqVO = {
|
||||||
|
dataSourceConfigId: number
|
||||||
|
tableNames: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询列表代码生成表定义
|
||||||
|
export const getCodegenTableList = (dataSourceConfigId: number) => {
|
||||||
|
return request.get({ url: '/infra/codegen/table/list?dataSourceConfigId=' + dataSourceConfigId })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询列表代码生成表定义
|
||||||
|
export const getCodegenTablePage = (params: PageParam) => {
|
||||||
|
return request.get({ url: '/infra/codegen/table/page', params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询详情代码生成表定义
|
||||||
|
export const getCodegenTable = (id: number) => {
|
||||||
|
return request.get({ url: '/infra/codegen/detail?tableId=' + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增代码生成表定义
|
||||||
|
export const createCodegenTable = (data: CodegenCreateListReqVO) => {
|
||||||
|
return request.post({ url: '/infra/codegen/create', data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改代码生成表定义
|
||||||
|
export const updateCodegenTable = (data: CodegenUpdateReqVO) => {
|
||||||
|
return request.put({ url: '/infra/codegen/update', data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 基于数据库的表结构,同步数据库的表和字段定义
|
||||||
|
export const syncCodegenFromDB = (id: number) => {
|
||||||
|
return request.put({ url: '/infra/codegen/sync-from-db?tableId=' + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预览生成代码
|
||||||
|
export const previewCodegen = (id: number) => {
|
||||||
|
return request.get({ url: '/infra/codegen/preview?tableId=' + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载生成代码
|
||||||
|
export const downloadCodegen = (id: number) => {
|
||||||
|
return request.download({ url: '/infra/codegen/download?tableId=' + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获得表定义
|
||||||
|
export const getSchemaTableList = (params) => {
|
||||||
|
return request.get({ url: '/infra/codegen/db/table/list', params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 基于数据库的表结构,创建代码生成器的表定义
|
||||||
|
export const createCodegenList = (data) => {
|
||||||
|
return request.post({ url: '/infra/codegen/create-list', data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除代码生成表定义
|
||||||
|
export const deleteCodegenTable = (id: number) => {
|
||||||
|
return request.delete({ url: '/infra/codegen/delete?tableId=' + id })
|
||||||
|
}
|
||||||
@ -1,4 +1,3 @@
|
|||||||
import MyFormCreateDesigner from './src/MyFormCreateDesigner.vue'
|
|
||||||
import { useFormCreateDesigner } from './src/useFormCreateDesigner'
|
import { useFormCreateDesigner } from './src/useFormCreateDesigner'
|
||||||
|
|
||||||
export { MyFormCreateDesigner, useFormCreateDesigner }
|
export { useFormCreateDesigner }
|
||||||
|
|||||||
@ -1,33 +0,0 @@
|
|||||||
<!-- TODO puhui999: 没啥问题的话准备移除 -->
|
|
||||||
<template>
|
|
||||||
<FcDesigner ref="designer" height="780px" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { useUploadFileRule, useUploadImgRule, useUploadImgsRule } from './config'
|
|
||||||
|
|
||||||
defineOptions({ name: 'MyFormCreateDesigner' })
|
|
||||||
|
|
||||||
const designer = ref() // 表单设计器
|
|
||||||
const uploadFileRule = useUploadFileRule()
|
|
||||||
const uploadImgRule = useUploadImgRule()
|
|
||||||
const uploadImgsRule = useUploadImgsRule()
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
// 移除自带的上传组件规则
|
|
||||||
designer.value?.removeMenuItem('upload')
|
|
||||||
const components = [uploadFileRule, uploadImgRule, uploadImgsRule]
|
|
||||||
components.forEach((component) => {
|
|
||||||
//插入组件规则
|
|
||||||
designer.value?.addComponent(component)
|
|
||||||
//插入拖拽按钮到`main`分类下
|
|
||||||
designer.value?.appendMenuItem('main', {
|
|
||||||
icon: component.icon,
|
|
||||||
name: component.name,
|
|
||||||
label: component.label
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { generateUUID } from '@/utils'
|
||||||
|
import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
|
||||||
|
|
||||||
|
export const useEditorRule = () => {
|
||||||
|
const label = '富文本'
|
||||||
|
const name = 'Editor'
|
||||||
|
return {
|
||||||
|
icon: 'icon-editor',
|
||||||
|
label,
|
||||||
|
name,
|
||||||
|
rule() {
|
||||||
|
return {
|
||||||
|
type: name,
|
||||||
|
field: generateUUID(),
|
||||||
|
title: label,
|
||||||
|
info: '',
|
||||||
|
$required: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props(_, { t }) {
|
||||||
|
return localeProps(t, name + '.props', [
|
||||||
|
makeRequiredRule(),
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
field: 'height',
|
||||||
|
title: '高度'
|
||||||
|
},
|
||||||
|
{ type: 'switch', field: 'readonly', title: '是否只读' }
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,171 @@
|
|||||||
|
<!-- 数据统计 - 客户画像 -->
|
||||||
|
<template>
|
||||||
|
<ContentWrap>
|
||||||
|
<!-- 搜索工作栏 -->
|
||||||
|
<el-form
|
||||||
|
ref="queryFormRef"
|
||||||
|
:inline="true"
|
||||||
|
:model="queryParams"
|
||||||
|
class="-mb-15px"
|
||||||
|
label-width="68px"
|
||||||
|
>
|
||||||
|
<el-form-item label="时间范围" prop="orderDate">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="queryParams.times"
|
||||||
|
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||||
|
:shortcuts="defaultShortcuts"
|
||||||
|
class="!w-240px"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
type="daterange"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
@change="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="时间间隔" prop="interval">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.interval"
|
||||||
|
class="!w-240px"
|
||||||
|
placeholder="间隔类型"
|
||||||
|
@change="handleQuery"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="dict in getIntDictOptions(DICT_TYPE.DATE_INTERVAL)"
|
||||||
|
:key="dict.value"
|
||||||
|
:label="dict.label"
|
||||||
|
:value="dict.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="归属部门" prop="deptId">
|
||||||
|
<el-tree-select
|
||||||
|
v-model="queryParams.deptId"
|
||||||
|
:data="deptList"
|
||||||
|
:props="defaultProps"
|
||||||
|
check-strictly
|
||||||
|
class="!w-240px"
|
||||||
|
node-key="id"
|
||||||
|
placeholder="请选择归属部门"
|
||||||
|
@change="(queryParams.userId = undefined), handleQuery()"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="员工" prop="userId">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.userId"
|
||||||
|
class="!w-240px"
|
||||||
|
clearable
|
||||||
|
placeholder="员工"
|
||||||
|
@change="handleQuery"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="(user, index) in userListByDeptId"
|
||||||
|
:key="index"
|
||||||
|
:label="user.nickname"
|
||||||
|
:value="user.id"
|
||||||
|
/>
|
||||||
|
</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">
|
||||||
|
<Icon class="mr-5px" icon="ep:refresh" />
|
||||||
|
重置
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</ContentWrap>
|
||||||
|
|
||||||
|
<!-- 客户统计 -->
|
||||||
|
<el-col>
|
||||||
|
<el-tabs v-model="activeTab">
|
||||||
|
<el-tab-pane label="销售漏斗分析" lazy name="funnelRef">
|
||||||
|
<FunnelBusiness ref="funnelRef" :query-params="queryParams" />
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="新增商机分析" lazy name="businessSummaryRef">
|
||||||
|
<BusinessSummary ref="businessSummaryRef" :query-params="queryParams" />
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="商机转化率分析" lazy name="businessInversionRateSummaryRef">
|
||||||
|
<BusinessInversionRateSummary
|
||||||
|
ref="businessInversionRateSummaryRef"
|
||||||
|
:query-params="queryParams"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</el-col>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import * as DeptApi from '@/api/system/dept'
|
||||||
|
import * as UserApi from '@/api/system/user'
|
||||||
|
import { useUserStore } from '@/store/modules/user'
|
||||||
|
import { beginOfDay, defaultShortcuts, endOfDay, formatDate } from '@/utils/formatTime'
|
||||||
|
import { defaultProps, handleTree } from '@/utils/tree'
|
||||||
|
import FunnelBusiness from './components/FunnelBusiness.vue'
|
||||||
|
import BusinessSummary from './components/BusinessSummary.vue'
|
||||||
|
import BusinessInversionRateSummary from './components/BusinessInversionRateSummary.vue'
|
||||||
|
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||||
|
|
||||||
|
defineOptions({ name: 'CrmStatisticsFunnel' })
|
||||||
|
|
||||||
|
const queryParams = reactive({
|
||||||
|
interval: 2, // WEEK, 周
|
||||||
|
deptId: useUserStore().getUser.deptId,
|
||||||
|
userId: undefined,
|
||||||
|
times: [
|
||||||
|
// 默认显示最近一周的数据
|
||||||
|
formatDate(beginOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7))),
|
||||||
|
formatDate(endOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24)))
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const queryFormRef = ref() // 搜索的表单
|
||||||
|
const deptList = ref<Tree[]>([]) // 部门树形结构
|
||||||
|
const userList = ref<UserApi.UserVO[]>([]) // 全量用户清单
|
||||||
|
|
||||||
|
/** 根据选择的部门筛选员工清单 */
|
||||||
|
const userListByDeptId = computed(() =>
|
||||||
|
queryParams.deptId
|
||||||
|
? userList.value.filter((u: UserApi.UserVO) => u.deptId === queryParams.deptId)
|
||||||
|
: []
|
||||||
|
)
|
||||||
|
|
||||||
|
const activeTab = ref('funnelRef') // 活跃标签
|
||||||
|
const funnelRef = ref() // 销售漏斗
|
||||||
|
const businessSummaryRef = ref() // 新增商机分析
|
||||||
|
const businessInversionRateSummaryRef = ref() // 商机转化率分析
|
||||||
|
|
||||||
|
/** 搜索按钮操作 */
|
||||||
|
const handleQuery = () => {
|
||||||
|
switch (activeTab.value) {
|
||||||
|
case 'funnelRef':
|
||||||
|
funnelRef.value?.loadData?.()
|
||||||
|
break
|
||||||
|
case 'businessSummaryRef':
|
||||||
|
businessSummaryRef.value?.loadData?.()
|
||||||
|
break
|
||||||
|
case 'businessInversionRateSummaryRef':
|
||||||
|
businessInversionRateSummaryRef.value?.loadData?.()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 当 activeTab 改变时,刷新当前活动的 tab */
|
||||||
|
watch(activeTab, () => {
|
||||||
|
handleQuery()
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 重置按钮操作 */
|
||||||
|
const resetQuery = () => {
|
||||||
|
queryFormRef.value.resetFields()
|
||||||
|
handleQuery()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 初始化 */
|
||||||
|
onMounted(async () => {
|
||||||
|
deptList.value = handleTree(await DeptApi.getSimpleDeptList())
|
||||||
|
userList.value = handleTree(await UserApi.getSimpleUserList())
|
||||||
|
})
|
||||||
|
</script>
|
||||||
Loading…
Reference in New Issue