Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
1fb7701e6e
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -83,7 +83,7 @@
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
||||
|
@ -44,7 +44,7 @@ export const exportTask = async (params) => {
|
||||
|
||||
// 获取所有可回退的节点
|
||||
export const getReturnList = async (params) => {
|
||||
return await request.get({ url: '/bpm/task/get-return-list', params })
|
||||
return await request.get({ url: '/bpm/task/return-list', params })
|
||||
}
|
||||
|
||||
// 回退
|
||||
|
@ -1,3 +1,10 @@
|
||||
/*
|
||||
* @Author: zyna
|
||||
* @Date: 2023-12-02 13:08:56
|
||||
* @LastEditTime: 2023-12-17 16:28:20
|
||||
* @FilePath: \yudao-ui-admin-vue3\src\api\crm\business\index.ts
|
||||
* @Description:
|
||||
*/
|
||||
import request from '@/config/axios'
|
||||
|
||||
export interface BusinessVO {
|
||||
@ -55,3 +62,8 @@ export const deleteBusiness = async (id: number) => {
|
||||
export const exportBusiness = async (params) => {
|
||||
return await request.download({ url: `/crm/business/export-excel`, params })
|
||||
}
|
||||
|
||||
//联系人关联商机列表
|
||||
export const getBusinessPageByContact = async (params) => {
|
||||
return await request.get({ url: `/crm/business/page-by-contact`, params })
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ export interface ContactVO {
|
||||
email: string
|
||||
post: string
|
||||
customerId: number
|
||||
address: string
|
||||
detailAddress: string
|
||||
remark: string
|
||||
ownerUserId: string
|
||||
lastTime: Date
|
||||
@ -26,6 +26,11 @@ export interface ContactVO {
|
||||
ownerUserName: string
|
||||
}
|
||||
|
||||
export interface ContactBusinessReqVO {
|
||||
contactId: number
|
||||
businessIds: number[]
|
||||
}
|
||||
|
||||
// 查询 CRM 联系人列表
|
||||
export const getContactPage = async (params) => {
|
||||
return await request.get({ url: `/crm/contact/page`, params })
|
||||
@ -65,3 +70,13 @@ export const exportContact = async (params) => {
|
||||
export const getSimpleContactList = async () => {
|
||||
return await request.get({ url: `/crm/contact/simple-all-list` })
|
||||
}
|
||||
|
||||
// 批量新增联系人商机关联
|
||||
export const createContactBusinessList = async (data: ContactBusinessReqVO) => {
|
||||
return await request.post({ url: `/crm/contact/create-business-list`, data })
|
||||
}
|
||||
|
||||
// 解除联系人商机关联
|
||||
export const deleteContactBusinessList = async (data: ContactBusinessReqVO) => {
|
||||
return await request.delete({ url: `/crm/contact/delete-business-list`, data })
|
||||
}
|
||||
|
@ -1,47 +0,0 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
export interface ContactBusinessLinkVO {
|
||||
id: number
|
||||
contactId: number
|
||||
businessId: number
|
||||
}
|
||||
|
||||
// 查询联系人商机关联分页
|
||||
export const getContactBusinessLinkPage = async (params) => {
|
||||
return await request.get({ url: `/crm/contact-business-link/page`, params })
|
||||
}
|
||||
|
||||
// 查询联系人商机关联详情
|
||||
export const getContactBusinessLink = async (id: number) => {
|
||||
return await request.get({ url: `/crm/contact-business-link/get?id=` + id })
|
||||
}
|
||||
|
||||
// 新增联系人商机关联
|
||||
export const createContactBusinessLink = async (data: ContactBusinessLinkVO) => {
|
||||
return await request.post({ url: `/crm/contact-business-link/create`, data })
|
||||
}
|
||||
|
||||
// 修改联系人商机关联
|
||||
export const updateContactBusinessLink = async (data: ContactBusinessLinkVO) => {
|
||||
return await request.put({ url: `/crm/contact-business-link/update`, data })
|
||||
}
|
||||
|
||||
// 删除联系人商机关联
|
||||
export const deleteContactBusinessLink = async (data: ContactBusinessLinkVO) => {
|
||||
return await request.delete({ url: `/crm/contact-business-link/delete-batch`, data })
|
||||
}
|
||||
|
||||
// 导出联系人商机关联 Excel
|
||||
export const exportContactBusinessLink = async (params) => {
|
||||
return await request.download({ url: `/crm/contact-business-link/export-excel`, params })
|
||||
}
|
||||
|
||||
//批量新增联系人商机关联
|
||||
export const createContactBusinessLinkBatch = async (data: ContactBusinessLinkVO[]) => {
|
||||
return await request.post({ url: `/crm/contact-business-link/create-batch`, data })
|
||||
}
|
||||
// 查询联系人关联商机列表
|
||||
export const getBusinessByContactPage = async (params) => {
|
||||
return await request.get({ url: `/crm/contact-business-link/page-by-contact`, params })
|
||||
}
|
||||
// TODO @zyna:根据后端,调整下请求
|
@ -59,7 +59,7 @@ export const deleteCustomer = async (id: number) => {
|
||||
}
|
||||
|
||||
// 导出客户 Excel
|
||||
export const exportCustomer = async (params) => {
|
||||
export const exportCustomer = async (params: any) => {
|
||||
return await request.download({ url: `/crm/customer/export-excel`, params })
|
||||
}
|
||||
|
||||
@ -67,3 +67,13 @@ export const exportCustomer = async (params) => {
|
||||
export const queryAllList = async () => {
|
||||
return await request.get({ url: `/crm/customer/query-all-list` })
|
||||
}
|
||||
|
||||
// 查询客户操作日志
|
||||
export const getOperateLogPage = async (params: any) => {
|
||||
return await request.get({ url: '/crm/customer/operate-log-page', params })
|
||||
}
|
||||
|
||||
// 锁定/解锁客户
|
||||
export const lockCustomer = async (id: number, lockStatus: boolean) => {
|
||||
return await request.put({ url: `/crm/customer/lock`, data: { id, lockStatus } })
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
import request from '@/config/axios'
|
||||
import { ConfigVO } from '@/api/infra/config'
|
||||
|
||||
export interface CustomerPoolConfigVO {
|
||||
enabled?: boolean
|
||||
contactExpireDays?: number
|
||||
dealExpireDays?: number
|
||||
notifyEnabled?: boolean
|
||||
notifyDays: number
|
||||
notifyDays?: number
|
||||
}
|
||||
|
||||
// 获取客户公海规则设置
|
||||
@ -15,6 +14,6 @@ export const getCustomerPoolConfig = async () => {
|
||||
}
|
||||
|
||||
// 更新客户公海规则设置
|
||||
export const saveCustomerPoolConfig = async (data: ConfigVO) => {
|
||||
export const saveCustomerPoolConfig = async (data: CustomerPoolConfigVO) => {
|
||||
return await request.put({ url: `/crm/customer-pool-config/save`, data })
|
||||
}
|
39
src/api/report/ureport/index.ts
Normal file
39
src/api/report/ureport/index.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
export interface UReportDataVO {
|
||||
id: number
|
||||
name: string
|
||||
status: number
|
||||
content: string
|
||||
remark: string
|
||||
}
|
||||
|
||||
// 查询Ureport2报表分页
|
||||
export const getUReportDataPage = async (params) => {
|
||||
return await request.get({ url: `/report/ureport-data/page`, params })
|
||||
}
|
||||
|
||||
// 查询Ureport2报表详情
|
||||
export const getUReportData = async (id: number) => {
|
||||
return await request.get({ url: `/report/ureport-data/get?id=` + id })
|
||||
}
|
||||
|
||||
// 新增Ureport2报表
|
||||
export const createUReportData = async (data: UReportDataVO) => {
|
||||
return await request.post({ url: `/report/ureport-data/create`, data })
|
||||
}
|
||||
|
||||
// 修改Ureport2报表
|
||||
export const updateUReportData = async (data: UReportDataVO) => {
|
||||
return await request.put({ url: `/report/ureport-data/update`, data })
|
||||
}
|
||||
|
||||
// 删除Ureport2报表
|
||||
export const deleteUReportData = async (id: number) => {
|
||||
return await request.delete({ url: `/report/ureport-data/delete?id=` + id })
|
||||
}
|
||||
|
||||
// 导出Ureport2报表 Excel
|
||||
export const exportUReportData = async (params) => {
|
||||
return await request.download({ url: `/report/ureport-data/export-excel`, params })
|
||||
}
|
@ -23,6 +23,32 @@ export type OperateLogVO = {
|
||||
resultData: string
|
||||
}
|
||||
|
||||
export type OperateLogV2VO = {
|
||||
id: number
|
||||
userNickname: string
|
||||
traceId: string
|
||||
userType: number
|
||||
userId: number
|
||||
userName: string
|
||||
type: string
|
||||
subType: string
|
||||
bizId: number
|
||||
action: string
|
||||
extra: string
|
||||
requestMethod: string
|
||||
requestUrl: string
|
||||
userIp: string
|
||||
userAgent: string
|
||||
creator: string
|
||||
creatorName: string
|
||||
createTime: Date
|
||||
// 数据扩展,渲染时使用
|
||||
title: string // 操作标题(如果为空则取 name 值)
|
||||
colSize: number // 变更记录行数
|
||||
contentStrList: string[]
|
||||
tagsContentList: string[]
|
||||
}
|
||||
|
||||
// 查询操作日志列表
|
||||
export const getOperateLogPage = (params: PageParam) => {
|
||||
return request.get({ url: '/system/operate-log/page', params })
|
||||
|
@ -35,8 +35,6 @@ export const enum APP_LINK_TYPE_ENUM {
|
||||
PRODUCT_DETAIL_NORMAL,
|
||||
// 拼团商品详情
|
||||
PRODUCT_DETAIL_COMBINATION,
|
||||
// 积分商品详情
|
||||
PRODUCT_DETAIL_POINT,
|
||||
// 秒杀商品详情
|
||||
PRODUCT_DETAIL_SECKILL
|
||||
}
|
||||
@ -80,10 +78,6 @@ export const APP_LINK_GROUP_LIST = [
|
||||
name: '系统设置',
|
||||
path: '/pages/public/setting'
|
||||
},
|
||||
{
|
||||
name: '问题反馈',
|
||||
path: '/pages/public/feedback'
|
||||
},
|
||||
{
|
||||
name: '常见问题',
|
||||
path: '/pages/public/faq'
|
||||
@ -112,11 +106,6 @@ export const APP_LINK_GROUP_LIST = [
|
||||
name: '秒杀商品详情',
|
||||
path: '/pages/goods/seckill',
|
||||
type: APP_LINK_TYPE_ENUM.PRODUCT_DETAIL_SECKILL
|
||||
},
|
||||
{
|
||||
name: '积分商品详情',
|
||||
path: '/pages/goods/score',
|
||||
type: APP_LINK_TYPE_ENUM.PRODUCT_DETAIL_POINT
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -145,10 +134,6 @@ export const APP_LINK_GROUP_LIST = [
|
||||
name: '签到中心',
|
||||
path: '/pages/app/sign'
|
||||
},
|
||||
{
|
||||
name: '积分商城',
|
||||
path: '/pages/app/score-shop'
|
||||
},
|
||||
{
|
||||
name: '优惠券中心',
|
||||
path: '/pages/coupon/list'
|
||||
@ -172,10 +157,6 @@ export const APP_LINK_GROUP_LIST = [
|
||||
name: '分销中心',
|
||||
path: '/pages/commission/index'
|
||||
},
|
||||
{
|
||||
name: '申请分销商',
|
||||
path: '/pages/commission/apply'
|
||||
},
|
||||
{
|
||||
name: '推广商品',
|
||||
path: '/pages/commission/goods'
|
||||
@ -184,10 +165,6 @@ export const APP_LINK_GROUP_LIST = [
|
||||
name: '分销订单',
|
||||
path: '/pages/commission/order'
|
||||
},
|
||||
{
|
||||
name: '分享记录',
|
||||
path: '/pages/commission/share-log'
|
||||
},
|
||||
{
|
||||
name: '我的团队',
|
||||
path: '/pages/commission/team'
|
||||
@ -208,10 +185,6 @@ export const APP_LINK_GROUP_LIST = [
|
||||
{
|
||||
name: '申请提现',
|
||||
path: '/pages/pay/withdraw'
|
||||
},
|
||||
{
|
||||
name: '提现记录',
|
||||
path: '/pages/pay/withdraw-log'
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -242,10 +215,6 @@ export const APP_LINK_GROUP_LIST = [
|
||||
name: '地址管理',
|
||||
path: '/pages/user/address/list'
|
||||
},
|
||||
{
|
||||
name: '发票管理',
|
||||
path: '/pages/user/invoice/list'
|
||||
},
|
||||
{
|
||||
name: '用户佣金',
|
||||
path: '/pages/user/wallet/commission'
|
||||
|
@ -8,11 +8,11 @@
|
||||
<component :is="component.id" :property="component.property" />
|
||||
</div>
|
||||
<div class="component-wrap">
|
||||
<!-- 左侧组件名 -->
|
||||
<!-- 左侧:组件名(悬浮的小贴条) -->
|
||||
<div class="component-name" v-if="component.name">
|
||||
{{ component.name }}
|
||||
</div>
|
||||
<!-- 左侧:组件操作工具栏 -->
|
||||
<!-- 右侧:组件操作工具栏 -->
|
||||
<div class="component-toolbar" v-if="showToolbar && component.name && active">
|
||||
<VerticalButtonGroup type="primary">
|
||||
<el-tooltip content="上移" placement="right">
|
||||
@ -54,7 +54,7 @@ import { propTypes } from '@/utils/propTypes'
|
||||
import { object } from 'vue-types'
|
||||
|
||||
/**
|
||||
* 组件容器
|
||||
* 组件容器:目前在中间部分
|
||||
* 用于包裹组件,为组件提供 背景、外边距、内边距、边框等样式
|
||||
*/
|
||||
defineOptions({ name: 'ComponentContainer' })
|
||||
@ -100,6 +100,7 @@ const emits = defineEmits<{
|
||||
(e: 'copy'): void
|
||||
(e: 'delete'): void
|
||||
}>()
|
||||
|
||||
/**
|
||||
* 移动组件
|
||||
* @param direction 移动方向
|
||||
@ -107,12 +108,14 @@ const emits = defineEmits<{
|
||||
const handleMoveComponent = (direction: number) => {
|
||||
emits('move', direction)
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制组件
|
||||
*/
|
||||
const handleCopyComponent = () => {
|
||||
emits('copy')
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除组件
|
||||
*/
|
||||
|
@ -1,8 +1,11 @@
|
||||
<template>
|
||||
<el-tabs stretch>
|
||||
<!-- 每个组件的自定义内容 -->
|
||||
<el-tab-pane label="内容" v-if="$slots.default">
|
||||
<slot></slot>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 每个组件的通用内容 -->
|
||||
<el-tab-pane label="样式" lazy>
|
||||
<el-card header="组件样式" class="property-group">
|
||||
<el-form :model="formData" label-width="80px">
|
||||
@ -51,7 +54,7 @@
|
||||
import { ComponentStyle, usePropertyForm } from '@/components/DiyEditor/util'
|
||||
|
||||
/**
|
||||
* 组件容器属性
|
||||
* 组件容器属性:目前右边部分
|
||||
* 用于包裹组件,为组件提供 背景、外边距、内边距、边框等样式
|
||||
*/
|
||||
defineOptions({ name: 'ComponentContainer' })
|
||||
|
@ -41,16 +41,19 @@ import { componentConfigs } from '../components/mobile/index'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { DiyComponent, DiyComponentLibrary } from '@/components/DiyEditor/util'
|
||||
|
||||
/** 组件库 */
|
||||
/** 组件库:目前左侧的【基础组件】、【图文组件】部分 */
|
||||
defineOptions({ name: 'ComponentLibrary' })
|
||||
|
||||
// 组件列表
|
||||
const props = defineProps<{
|
||||
list: DiyComponentLibrary[]
|
||||
}>()
|
||||
// 组件分组
|
||||
const groups = reactive<any[]>([])
|
||||
// 展开的折叠面板
|
||||
const extendGroups = reactive<string[]>([])
|
||||
|
||||
// 监听 list 属性,按照 DiyComponentLibrary 的 name 分组
|
||||
watch(
|
||||
() => props.list,
|
||||
() => {
|
||||
|
@ -4,6 +4,7 @@ import { floatToFixed2 } from '@/utils'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
|
||||
// 优惠值
|
||||
// TODO @疯狂:idea 有告警
|
||||
export const CouponDiscount = defineComponent({
|
||||
name: 'CouponDiscount',
|
||||
props: {
|
||||
|
@ -24,6 +24,7 @@ export interface CouponCardProperty {
|
||||
}
|
||||
|
||||
// 定义组件
|
||||
// TODO @疯狂:idea 有告警
|
||||
export const component = {
|
||||
id: 'CouponCard',
|
||||
name: '优惠券',
|
||||
|
@ -9,6 +9,7 @@ export interface HotZoneProperty {
|
||||
// 组件样式
|
||||
style: ComponentStyle
|
||||
}
|
||||
|
||||
/** 热区项目属性 */
|
||||
export interface HotZoneItemProperty {
|
||||
// 链接的名称
|
||||
|
@ -13,6 +13,7 @@ export interface MagicCubeProperty {
|
||||
// 组件样式
|
||||
style: ComponentStyle
|
||||
}
|
||||
|
||||
/** 广告魔方项目属性 */
|
||||
export interface MagicCubeItemProperty {
|
||||
// 图标链接
|
||||
@ -30,6 +31,7 @@ export interface MagicCubeItemProperty {
|
||||
}
|
||||
|
||||
// 定义组件
|
||||
// TODO @疯狂:有 idea 爆红告警
|
||||
export const component = {
|
||||
id: 'MagicCube',
|
||||
name: '广告魔方',
|
||||
|
@ -10,6 +10,7 @@ export interface MenuGridProperty {
|
||||
// 组件样式
|
||||
style: ComponentStyle
|
||||
}
|
||||
|
||||
/** 宫格导航项目属性 */
|
||||
export interface MenuGridItemProperty {
|
||||
// 图标链接
|
||||
|
@ -8,6 +8,7 @@ export interface MenuListProperty {
|
||||
// 组件样式
|
||||
style: ComponentStyle
|
||||
}
|
||||
|
||||
/** 列表导航项目属性 */
|
||||
export interface MenuListItemProperty {
|
||||
// 图标链接
|
||||
|
@ -12,7 +12,6 @@
|
||||
<el-form-item label="文字颜色" prop="文字颜色">
|
||||
<ColorInput v-model="formData.textColor" />
|
||||
</el-form-item>
|
||||
|
||||
<el-card header="公告内容" class="property-group" shadow="never">
|
||||
<Draggable v-model="formData.contents">
|
||||
<template #default="{ element }">
|
||||
|
@ -59,6 +59,7 @@ export interface ProductCardFieldProperty {
|
||||
}
|
||||
|
||||
// 定义组件
|
||||
// TODO @疯狂:idea 有告警
|
||||
export const component = {
|
||||
id: 'ProductCard',
|
||||
name: '商品卡片',
|
||||
|
@ -134,6 +134,7 @@ watch(
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* 计算商品的间距
|
||||
* @param index 商品索引
|
||||
|
@ -38,6 +38,7 @@ export interface ProductListFieldProperty {
|
||||
}
|
||||
|
||||
// 定义组件
|
||||
// TODO @疯狂:idea 有告警
|
||||
export const component = {
|
||||
id: 'ProductList',
|
||||
name: '商品栏',
|
||||
|
@ -6,6 +6,7 @@ import { PromotionArticleProperty } from './config'
|
||||
import * as ArticleApi from '@/api/mall/promotion/article/index'
|
||||
|
||||
/** 营销文章 */
|
||||
// TODO @疯狂:idea 有告警
|
||||
defineOptions({ name: 'PromotionArticle' })
|
||||
// 定义属性
|
||||
const props = defineProps<{ property: PromotionArticleProperty }>()
|
||||
|
@ -29,6 +29,7 @@ export interface PromotionCombinationProperty {
|
||||
// 组件样式
|
||||
style: ComponentStyle
|
||||
}
|
||||
|
||||
// 商品字段
|
||||
export interface PromotionCombinationFieldProperty {
|
||||
// 是否显示
|
||||
@ -38,6 +39,7 @@ export interface PromotionCombinationFieldProperty {
|
||||
}
|
||||
|
||||
// 定义组件
|
||||
// TODO @疯狂:idea 有告警
|
||||
export const component = {
|
||||
id: 'PromotionCombination',
|
||||
name: '拼团',
|
||||
|
@ -17,6 +17,7 @@ export interface SearchProperty {
|
||||
export type PlaceholderPosition = 'left' | 'center'
|
||||
|
||||
// 定义组件
|
||||
// TODO @疯狂:idea 这里爆红,可以卡看咋优化下哇:is missing the following properties from type DiyComponent<SearchProperty>: uid, position
|
||||
export const component = {
|
||||
id: 'SearchBar',
|
||||
name: '搜索框',
|
||||
|
@ -19,6 +19,7 @@ export interface VideoPlayerStyle extends ComponentStyle {
|
||||
}
|
||||
|
||||
// 定义组件
|
||||
// TODO @疯狂:idea 有告警
|
||||
export const component = {
|
||||
id: 'VideoPlayer',
|
||||
name: '视频播放',
|
||||
|
@ -27,11 +27,12 @@
|
||||
</el-tooltip>
|
||||
</el-button-group>
|
||||
</el-header>
|
||||
|
||||
<!-- 中心区域 -->
|
||||
<el-container class="editor-container">
|
||||
<!-- 左侧:组件库 -->
|
||||
<!-- 左侧:组件库(ComponentLibrary) -->
|
||||
<ComponentLibrary ref="componentLibrary" :list="libs" v-if="libs && libs.length > 0" />
|
||||
<!-- 中心设计区域 -->
|
||||
<!-- 中心:设计区域(ComponentContainer) -->
|
||||
<div class="editor-center page-prop-area" @click="handlePageSelected">
|
||||
<!-- 手机顶部 -->
|
||||
<div class="editor-design-top">
|
||||
@ -132,7 +133,7 @@
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 右侧属性面板 -->
|
||||
<!-- 右侧:属性面板(ComponentContainerProperty) -->
|
||||
<el-aside class="editor-right" width="350px" v-if="selectedComponent?.property">
|
||||
<el-card
|
||||
shadow="never"
|
||||
@ -160,6 +161,7 @@
|
||||
</el-aside>
|
||||
</el-container>
|
||||
</el-container>
|
||||
|
||||
<!-- 预览弹框 -->
|
||||
<Dialog v-model="previewDialogVisible" title="预览" width="700">
|
||||
<div class="flex justify-around">
|
||||
@ -231,6 +233,7 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
// 监听传入的页面配置
|
||||
// 解析出 pageConfigComponent 页面整体的配置,navigationBarComponent、pageComponents、tabBarComponent 页面上、中、下的配置
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
@ -251,6 +254,7 @@ watch(
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
// 保存
|
||||
const handleSave = () => {
|
||||
const pageConfig = {
|
||||
@ -303,7 +307,7 @@ const handleTabBarSelected = () => {
|
||||
handleComponentSelected(unref(tabBarComponent))
|
||||
}
|
||||
|
||||
// 组件变动
|
||||
// 组件变动(拖拽)
|
||||
const handleComponentChange = (dragEvent: any) => {
|
||||
// 新增,即从组件库拖拽添加组件
|
||||
if (dragEvent.added) {
|
||||
@ -327,19 +331,21 @@ const swapComponent = (oldIndex: number, newIndex: number) => {
|
||||
selectedComponentIndex.value = newIndex
|
||||
}
|
||||
|
||||
/** 移动组件 */
|
||||
/** 移动组件(上移、下移) */
|
||||
const handleMoveComponent = (index: number, direction: number) => {
|
||||
const newIndex = index + direction
|
||||
if (newIndex < 0 || newIndex >= pageComponents.value.length) return
|
||||
|
||||
swapComponent(index, newIndex)
|
||||
}
|
||||
|
||||
/** 复制组件 */
|
||||
const handleCopyComponent = (index: number) => {
|
||||
const component = cloneDeep(pageComponents.value[index])
|
||||
component.uid = new Date().getTime()
|
||||
pageComponents.value.splice(index + 1, 0, component)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除组件
|
||||
* @param index 当前组件index
|
||||
@ -371,6 +377,7 @@ const handleReset = () => {
|
||||
if (reload) reload()
|
||||
emits('reset')
|
||||
}
|
||||
|
||||
// 预览
|
||||
const previewDialogVisible = ref(false)
|
||||
const handlePreview = () => {
|
||||
@ -388,10 +395,12 @@ const setDefaultSelectedComponent = () => {
|
||||
selectedComponent.value = unref(tabBarComponent)
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [props.showPageConfig, props.showNavigationBar, props.showTabBar],
|
||||
() => setDefaultSelectedComponent()
|
||||
)
|
||||
|
||||
onMounted(() => setDefaultSelectedComponent())
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
@ -103,6 +103,7 @@ export function usePropertyForm<T>(modelValue: T, emit: Function): { formData: R
|
||||
}
|
||||
)
|
||||
|
||||
// TODO @疯狂:这个 idea 爆红,看看怎么可以解决哈
|
||||
return { formData }
|
||||
}
|
||||
|
||||
|
3
src/components/OperateLogV2/index.ts
Normal file
3
src/components/OperateLogV2/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import OperateLogV2 from './src/OperateLogV2.vue'
|
||||
|
||||
export { OperateLogV2 }
|
175
src/components/OperateLogV2/src/OperateLogV2.vue
Normal file
175
src/components/OperateLogV2/src/OperateLogV2.vue
Normal file
@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<div class="p-20px">
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="(log, index) in logDataList"
|
||||
:key="index"
|
||||
:timestamp="formatDate(log.createTime)"
|
||||
placement="top"
|
||||
>
|
||||
<div class="el-timeline-right-content">
|
||||
<el-row>
|
||||
<el-col :span="24" class="mb-10px">
|
||||
=======================
|
||||
<el-tag class="mr-10px" type="success">{{ log.userName }}</el-tag>
|
||||
<span>{{ log.title }}</span>
|
||||
=======================
|
||||
</el-col>
|
||||
<!-- 先处理一下有几行-->
|
||||
<template v-for="colNum in log.colSize" :key="colNum + 'col'">
|
||||
<el-col :span="24" class="mb-10px">
|
||||
<!-- 处理每一行-->
|
||||
<template
|
||||
v-for="(tagVal, index2) in log.tagsContentList.slice(
|
||||
(colNum - 1) * 3,
|
||||
3 * colNum
|
||||
)"
|
||||
:key="index2"
|
||||
>
|
||||
<el-tag class="mx-10px"> {{ tagVal }}</el-tag>
|
||||
<span>{{ log.contentStrList[index2] }}</span>
|
||||
</template>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
<template #dot>
|
||||
<span :style="{ backgroundColor: getUserTypeColor(log.userType) }" class="dot-node-style">
|
||||
{{ getDictLabel(DICT_TYPE.USER_TYPE, log.userType)[0] }}
|
||||
</span>
|
||||
</template>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { OperateLogV2VO } from '@/api/system/operatelog'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { DICT_TYPE, getDictLabel, getDictObj } from '@/utils/dict'
|
||||
import { ElTag } from 'element-plus'
|
||||
|
||||
defineOptions({ name: 'OperateLogV2' })
|
||||
|
||||
const props = defineProps<{
|
||||
logList: OperateLogV2VO[] // 操作日志列表
|
||||
}>()
|
||||
|
||||
const logDataList = ref<OperateLogV2VO[]>([]) // 操作日志列表
|
||||
|
||||
/** 获得 userType 颜色 */
|
||||
const getUserTypeColor = (type: number) => {
|
||||
const dict = getDictObj(DICT_TYPE.USER_TYPE, type)
|
||||
switch (dict?.colorType) {
|
||||
case 'success':
|
||||
return '#67C23A'
|
||||
case 'info':
|
||||
return '#909399'
|
||||
case 'warning':
|
||||
return '#E6A23C'
|
||||
case 'danger':
|
||||
return '#F56C6C'
|
||||
}
|
||||
return '#409EFF'
|
||||
}
|
||||
|
||||
// 提取 tag 所需内容和位置
|
||||
const renderTags = (content: string) => {
|
||||
let newStr = unref(content).slice() // 去掉引用
|
||||
newStr = newStr.replaceAll('【】', '【空】').replaceAll(';', '') // 处理掉分号 特殊:处理一下空的情况
|
||||
const regex = /【([^【】]+)】/g
|
||||
const fg = '|' // 原始位置替换符号
|
||||
let match: any[] | null
|
||||
let matchStr: string[] = []
|
||||
let oldStr: string[] = []
|
||||
while ((match = regex.exec(newStr)) !== null) {
|
||||
matchStr.push(match[1]) // 提取值
|
||||
oldStr.push(match[0]) // 原值
|
||||
}
|
||||
// 为什么重新循环不放在 while 中一起是因为替换重新赋值过后 match 值就不准确了
|
||||
oldStr.forEach((item) => {
|
||||
newStr = newStr.replace(item, fg)
|
||||
})
|
||||
return [newStr.split(fg), matchStr]
|
||||
}
|
||||
|
||||
const initLog = () => {
|
||||
logDataList.value = props.logList.map((logItem) => {
|
||||
const keyValue = renderTags(logItem.action)
|
||||
// 挂载数据
|
||||
logItem.contentStrList = keyValue[0]
|
||||
if (keyValue[0][0] === '从') {
|
||||
logItem.title = logItem.subType
|
||||
} else {
|
||||
logItem.title = keyValue[0][0]
|
||||
logItem.contentStrList.splice(0, 1)
|
||||
}
|
||||
logItem.colSize = keyValue[0].length / 3 // 变更记录行数
|
||||
logItem.tagsContentList = keyValue[1]
|
||||
return logItem
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.logList.length,
|
||||
(newObj) => {
|
||||
if (newObj) {
|
||||
initLog()
|
||||
console.log(logDataList.value)
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 时间线样式调整
|
||||
:deep(.el-timeline) {
|
||||
margin: 10px 0 0 160px;
|
||||
|
||||
.el-timeline-item__wrapper {
|
||||
position: relative;
|
||||
top: -20px;
|
||||
|
||||
.el-timeline-item__timestamp {
|
||||
position: absolute !important;
|
||||
top: 10px;
|
||||
left: -150px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-timeline-right-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 30px;
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 13px; /* 将伪元素水平居中 */
|
||||
border-color: transparent #fff transparent transparent; /* 尖角颜色,左侧朝向 */
|
||||
border-style: solid;
|
||||
border-width: 8px; /* 调整尖角大小 */
|
||||
content: ''; /* 必须设置 content 属性 */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dot-node-style {
|
||||
position: absolute;
|
||||
left: -5px;
|
||||
display: flex;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
font-size: 10px;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
@ -2,25 +2,27 @@
|
||||
<div class="upload-file">
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
:multiple="props.limit > 1"
|
||||
name="file"
|
||||
v-model="valueRef"
|
||||
v-model:file-list="fileList"
|
||||
:show-file-list="true"
|
||||
:auto-upload="autoUpload"
|
||||
:action="updateUrl"
|
||||
:auto-upload="autoUpload"
|
||||
:before-upload="beforeUpload"
|
||||
:drag="drag"
|
||||
:headers="uploadHeaders"
|
||||
:limit="props.limit"
|
||||
:drag="drag"
|
||||
:before-upload="beforeUpload"
|
||||
:on-exceed="handleExceed"
|
||||
:on-success="handleFileSuccess"
|
||||
:multiple="props.limit > 1"
|
||||
:on-error="excelUploadError"
|
||||
:on-remove="handleRemove"
|
||||
:on-exceed="handleExceed"
|
||||
:on-preview="handlePreview"
|
||||
:on-remove="handleRemove"
|
||||
:on-success="handleFileSuccess"
|
||||
:show-file-list="true"
|
||||
class="upload-file-uploader"
|
||||
name="file"
|
||||
>
|
||||
<el-button type="primary"><Icon icon="ep:upload-filled" />选取文件</el-button>
|
||||
<el-button type="primary">
|
||||
<Icon icon="ep:upload-filled" />
|
||||
选取文件
|
||||
</el-button>
|
||||
<template v-if="isShowTip" #tip>
|
||||
<div style="font-size: 8px">
|
||||
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
|
||||
@ -35,8 +37,8 @@
|
||||
<script lang="ts" setup>
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { getAccessToken, getTenantId } from '@/utils/auth'
|
||||
import type { UploadInstance, UploadUserFile, UploadProps, UploadRawFile } from 'element-plus'
|
||||
import { isArray, isString } from '@/utils/is'
|
||||
import type { UploadInstance, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus'
|
||||
import { isString } from '@/utils/is'
|
||||
|
||||
defineOptions({ name: 'UploadFile' })
|
||||
|
||||
@ -54,8 +56,8 @@ const props = defineProps({
|
||||
drag: propTypes.bool.def(false), // 拖拽上传
|
||||
isShowTip: propTypes.bool.def(true) // 是否显示提示
|
||||
})
|
||||
|
||||
// ========== 上传相关 ==========
|
||||
const valueRef = ref(props.modelValue)
|
||||
const uploadRef = ref<UploadInstance>()
|
||||
const uploadList = ref<UploadUserFile[]>([])
|
||||
const fileList = ref<UploadUserFile[]>([])
|
||||
@ -64,6 +66,7 @@ const uploadHeaders = ref({
|
||||
Authorization: 'Bearer ' + getAccessToken(),
|
||||
'tenant-id': getTenantId()
|
||||
})
|
||||
|
||||
// 文件上传之前判断
|
||||
const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
|
||||
if (fileList.value.length >= props.limit) {
|
||||
@ -97,12 +100,10 @@ const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
|
||||
// 文件上传成功
|
||||
const handleFileSuccess: UploadProps['onSuccess'] = (res: any): void => {
|
||||
message.success('上传成功')
|
||||
const fileListNew = fileList.value
|
||||
fileListNew.pop()
|
||||
fileList.value = fileListNew
|
||||
fileList.value.shift()
|
||||
uploadList.value.push({ name: res.data, url: res.data })
|
||||
if (uploadList.value.length == uploadNumber.value) {
|
||||
fileList.value = fileList.value.concat(uploadList.value)
|
||||
fileList.value.push(...uploadList.value)
|
||||
uploadList.value = []
|
||||
uploadNumber.value = 0
|
||||
emitUpdateModelValue()
|
||||
@ -131,29 +132,25 @@ const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
|
||||
// 监听模型绑定值变动
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
const files: string[] = []
|
||||
// 情况1:字符串
|
||||
if (isString(props.modelValue)) {
|
||||
// 情况1.1:逗号分隔的多值
|
||||
if (props.modelValue.includes(',')) {
|
||||
files.concat(props.modelValue.split(','))
|
||||
} else if (props.modelValue.length > 0) {
|
||||
files.push(props.modelValue)
|
||||
}
|
||||
} else if (isArray(props.modelValue)) {
|
||||
// 情况2:字符串
|
||||
files.concat(props.modelValue)
|
||||
} else if (props.modelValue == null) {
|
||||
// 情况3:undefined 不处理
|
||||
} else {
|
||||
throw new Error('不支持的 modelValue 类型')
|
||||
(val: string | string[]) => {
|
||||
if (!val) {
|
||||
fileList.value = [] // fix:处理掉缓存,表单重置后上传组件的内容并没有重置
|
||||
return
|
||||
}
|
||||
fileList.value = files.map((url: string) => {
|
||||
return { url, name: url.substring(url.lastIndexOf('/') + 1) } as UploadUserFile
|
||||
})
|
||||
|
||||
fileList.value = [] // 保障数据为空
|
||||
// 情况1:字符串
|
||||
if (isString(val)) {
|
||||
fileList.value.push(
|
||||
...val.split(',').map((url) => ({ name: url.substring(url.lastIndexOf('/') + 1), url }))
|
||||
)
|
||||
}
|
||||
// 情况2:数组
|
||||
fileList.value.push(
|
||||
...(val as string[]).map((url) => ({ name: url.substring(url.lastIndexOf('/') + 1), url }))
|
||||
)
|
||||
},
|
||||
{ immediate: true }
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
// 发送文件链接列表更新
|
||||
const emitUpdateModelValue = () => {
|
||||
@ -166,7 +163,7 @@ const emitUpdateModelValue = () => {
|
||||
emit('update:modelValue', result)
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.upload-file-uploader {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ service.interceptors.request.use(
|
||||
// response 拦截器
|
||||
service.interceptors.response.use(
|
||||
async (response: AxiosResponse<any>) => {
|
||||
const { data } = response
|
||||
let { data } = response
|
||||
const config = response.config
|
||||
if (!data) {
|
||||
// 返回“[HTTP]请求没有返回值”;
|
||||
@ -96,14 +96,18 @@ service.interceptors.response.use(
|
||||
}
|
||||
const { t } = useI18n()
|
||||
// 未设置状态码则默认成功状态
|
||||
const code = data.code || result_code
|
||||
// 二进制数据则直接返回
|
||||
// 二进制数据则直接返回,例如说 Excel 导出
|
||||
if (
|
||||
response.request.responseType === 'blob' ||
|
||||
response.request.responseType === 'arraybuffer'
|
||||
) {
|
||||
return response.data
|
||||
// 注意:如果导出的响应为 json,说明可能失败了,不直接返回进行下载
|
||||
if (response.data.type !== 'application/json') {
|
||||
return response.data
|
||||
}
|
||||
data = await new Response(response.data).json()
|
||||
}
|
||||
const code = data.code || result_code
|
||||
// 获取错误信息
|
||||
const msg = data.msg || errorCode[code] || errorCode['default']
|
||||
if (ignoreMsgs.indexOf(msg) !== -1) {
|
||||
|
@ -472,7 +472,9 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
meta: {
|
||||
title: '模板装修',
|
||||
noCache: true,
|
||||
hidden: true
|
||||
hidden: true,
|
||||
// TODO @疯狂:建议 menu 那的 /mall/promotion/diy-template/diy-template 改成 /mall/promotion/diy/template
|
||||
activeMenu: '/mall/promotion/diy-template/diy-template'
|
||||
},
|
||||
component: () => import('@/views/mall/promotion/diy/template/decorate.vue')
|
||||
},
|
||||
@ -482,7 +484,9 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
meta: {
|
||||
title: '页面装修',
|
||||
noCache: true,
|
||||
hidden: true
|
||||
hidden: true,
|
||||
// TODO @疯狂:建议 menu 那的 /mall/promotion/diy-template/diy-page 改成 /mall/promotion/diy/page
|
||||
activeMenu: '/mall/promotion/diy-template/diy-page'
|
||||
},
|
||||
component: () => import('@/views/mall/promotion/diy/page/decorate.vue')
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="formLoading"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="80px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="请假类型" prop="type">
|
||||
<el-select v-model="formData.type" placeholder="请选择请假类型" clearable>
|
||||
<el-select v-model="formData.type" clearable placeholder="请选择请假类型">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_OA_LEAVE_TYPE)"
|
||||
:key="dict.value"
|
||||
@ -18,27 +18,27 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="开始时间" prop="startTime">
|
||||
<el-date-picker
|
||||
clearable
|
||||
v-model="formData.startTime"
|
||||
clearable
|
||||
placeholder="请选择开始时间"
|
||||
type="datetime"
|
||||
value-format="x"
|
||||
placeholder="请选择开始时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="结束时间" prop="endTime">
|
||||
<el-date-picker
|
||||
clearable
|
||||
v-model="formData.endTime"
|
||||
clearable
|
||||
placeholder="请选择结束时间"
|
||||
type="datetime"
|
||||
value-format="x"
|
||||
placeholder="请选择结束时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="原因" prop="reason">
|
||||
<el-input v-model="formData.reason" type="textarea" placeholder="请输请假原因" />
|
||||
<el-input v-model="formData.reason" placeholder="请输请假原因" type="textarea" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
@ -50,9 +50,6 @@ import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
defineOptions({ name: 'BpmOALeaveCreate' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
const { currentRoute } = useRouter() // 路由
|
||||
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formData = ref({
|
||||
type: undefined,
|
||||
@ -67,7 +64,8 @@ const formRules = reactive({
|
||||
endTime: [{ required: true, message: '请假结束时间不能为空', trigger: 'change' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
const { push, currentRoute } = useRouter() // 路由
|
||||
/** 提交表单 */
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
@ -82,6 +80,7 @@ const submitForm = async () => {
|
||||
message.success('发起成功')
|
||||
// 关闭当前 Tab
|
||||
delView(unref(currentRoute))
|
||||
await push({ name: 'BpmOALeave' })
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
|
@ -2,18 +2,18 @@
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
:model="queryParams"
|
||||
class="-mb-15px"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="请假类型" prop="type">
|
||||
<el-select
|
||||
v-model="queryParams.type"
|
||||
placeholder="请选择请假类型"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
clearable
|
||||
placeholder="请选择请假类型"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_OA_LEAVE_TYPE)"
|
||||
@ -26,16 +26,16 @@
|
||||
<el-form-item label="申请时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
end-placeholder="结束日期"
|
||||
start-placeholder="开始日期"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="结果" prop="result">
|
||||
<el-select v-model="queryParams.result" placeholder="请选择结果" clearable class="!w-240px">
|
||||
<el-select v-model="queryParams.result" class="!w-240px" clearable placeholder="请选择结果">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT)"
|
||||
:key="dict.value"
|
||||
@ -47,17 +47,24 @@
|
||||
<el-form-item label="原因" prop="reason">
|
||||
<el-input
|
||||
v-model="queryParams.reason"
|
||||
placeholder="请输入原因"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
clearable
|
||||
placeholder="请输入原因"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</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" plain @click="handleCreate()">
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 发起请假
|
||||
<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-button plain type="primary" @click="handleCreate()">
|
||||
<Icon class="mr-5px" icon="ep:plus" />
|
||||
发起请假
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@ -66,63 +73,63 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="申请编号" align="center" prop="id" />
|
||||
<el-table-column label="状态" align="center" prop="result">
|
||||
<el-table-column align="center" label="申请编号" prop="id" />
|
||||
<el-table-column align="center" label="状态" prop="result">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT" :value="scope.row.result" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="开始时间"
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="开始时间"
|
||||
prop="startTime"
|
||||
width="180"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column
|
||||
label="结束时间"
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="结束时间"
|
||||
prop="endTime"
|
||||
width="180"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column label="请假类型" align="center" prop="type">
|
||||
<el-table-column align="center" label="请假类型" prop="type">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_OA_LEAVE_TYPE" :value="scope.row.type" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="原因" align="center" prop="reason" />
|
||||
<el-table-column align="center" label="原因" prop="reason" />
|
||||
<el-table-column
|
||||
label="申请时间"
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="申请时间"
|
||||
prop="createTime"
|
||||
width="180"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" width="200">
|
||||
<el-table-column align="center" label="操作" width="200">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-hasPermi="['bpm:oa-leave:query']"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleDetail(scope.row)"
|
||||
v-hasPermi="['bpm:oa-leave:query']"
|
||||
>
|
||||
详情
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['bpm:oa-leave:query']"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleProcessDetail(scope.row)"
|
||||
v-hasPermi="['bpm:oa-leave:query']"
|
||||
>
|
||||
进度
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.result === 1"
|
||||
v-hasPermi="['bpm:oa-leave:create']"
|
||||
link
|
||||
type="danger"
|
||||
@click="cancelLeave(scope.row)"
|
||||
v-hasPermi="['bpm:oa-leave:create']"
|
||||
v-if="scope.row.result === 1"
|
||||
>
|
||||
取消
|
||||
</el-button>
|
||||
@ -131,9 +138,9 @@
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
v-model:page="queryParams.pageNo"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
@ -228,6 +235,14 @@ const handleProcessDetail = (row) => {
|
||||
})
|
||||
}
|
||||
|
||||
// fix: 列表不刷新的问题。
|
||||
watch(
|
||||
() => router.currentRoute.value,
|
||||
() => {
|
||||
getList()
|
||||
}
|
||||
)
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
|
@ -62,12 +62,12 @@
|
||||
</el-row>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
<!-- TODO @ljlleo:idea 红色的报错,可以解决下 -->
|
||||
<el-form-item label="商机状态类型" prop="statusTypeId">
|
||||
<el-select
|
||||
v-model="formData.statusTypeId"
|
||||
placeholder="请选择商机状态类型"
|
||||
clearable
|
||||
size="small"
|
||||
@change="changeBusinessStatusType"
|
||||
>
|
||||
<el-option
|
||||
@ -79,7 +79,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="商机状态" prop="statusId">
|
||||
<el-select v-model="formData.statusId" placeholder="请选择商机状态" clearable size="small">
|
||||
<el-select v-model="formData.statusId" placeholder="请选择商机状态" clearable>
|
||||
<el-option
|
||||
v-for="item in businessStatusList"
|
||||
:key="item.id"
|
||||
@ -242,7 +242,8 @@ const queryParams = reactive({
|
||||
mobile: null,
|
||||
industryId: null,
|
||||
level: null,
|
||||
source: null
|
||||
source: null,
|
||||
pool: false
|
||||
})
|
||||
// 选择客户
|
||||
const showCustomer = ref(false)
|
||||
@ -251,12 +252,12 @@ const openCustomerSelect = () => {
|
||||
queryParams.pageNo = 1
|
||||
getCustomerList()
|
||||
}
|
||||
|
||||
/** 查询客户列表 */
|
||||
const getCustomerList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CustomerApi.getCustomerPage(queryParams)
|
||||
console.log(JSON.stringify(data))
|
||||
customerList.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
|
@ -5,11 +5,32 @@
|
||||
<Icon class="mr-5px" icon="ep:opportunity" />
|
||||
创建商机
|
||||
</el-button>
|
||||
<el-button
|
||||
@click="openBusinessModal"
|
||||
v-hasPermi="['crm:contact:create-business']"
|
||||
v-if="queryParams.contactId"
|
||||
>
|
||||
<Icon class="mr-5px" icon="ep:circle-plus" />关联
|
||||
</el-button>
|
||||
<el-button
|
||||
@click="deleteContactBusinessList"
|
||||
v-hasPermi="['crm:contact:delete-business']"
|
||||
v-if="queryParams.contactId"
|
||||
>
|
||||
<Icon class="mr-5px" icon="ep:remove" />解除关联
|
||||
</el-button>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap class="mt-10px">
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table
|
||||
ref="businessRef"
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
>
|
||||
<el-table-column type="selection" width="55" v-if="queryParams.contactId" />
|
||||
<el-table-column label="商机名称" fixed="left" align="center" prop="name">
|
||||
<template #default="scope">
|
||||
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">
|
||||
@ -33,17 +54,28 @@
|
||||
|
||||
<!-- 表单弹窗:添加 -->
|
||||
<BusinessForm ref="formRef" @success="getList" />
|
||||
<!-- 关联商机选择弹框 -->
|
||||
<BusinessListModal
|
||||
ref="businessModalRef"
|
||||
:customer-id="props.customerId"
|
||||
@success="createContactBusinessList"
|
||||
/>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as BusinessApi from '@/api/crm/business'
|
||||
import * as ContactApi from '@/api/crm/contact'
|
||||
import BusinessForm from './../BusinessForm.vue'
|
||||
import { BizTypeEnum } from '@/api/crm/permission'
|
||||
import { fenToYuanFormat } from '@/utils/formatter'
|
||||
import BusinessListModal from './BusinessListModal.vue'
|
||||
|
||||
const message = useMessage() // 消息
|
||||
|
||||
defineOptions({ name: 'CrmBusinessList' })
|
||||
const props = defineProps<{
|
||||
bizType: number // 业务类型
|
||||
bizId: number // 业务编号
|
||||
customerId?: number // 关联联系人与商机时,需要传入 customerId 进行筛选
|
||||
}>()
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
@ -52,7 +84,8 @@ const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
customerId: undefined as unknown // 允许 undefined + number
|
||||
customerId: undefined as unknown, // 允许 undefined + number
|
||||
contactId: undefined as unknown // 允许 undefined + number
|
||||
})
|
||||
|
||||
/** 查询列表 */
|
||||
@ -61,6 +94,7 @@ const getList = async () => {
|
||||
try {
|
||||
// 置空参数
|
||||
queryParams.customerId = undefined
|
||||
queryParams.contactId = undefined
|
||||
// 执行查询
|
||||
let data = { list: [], total: 0 }
|
||||
switch (props.bizType) {
|
||||
@ -68,6 +102,10 @@ const getList = async () => {
|
||||
queryParams.customerId = props.bizId
|
||||
data = await BusinessApi.getBusinessPageByCustomer(queryParams)
|
||||
break
|
||||
case BizTypeEnum.CRM_CONTACT:
|
||||
queryParams.contactId = props.bizId
|
||||
data = await BusinessApi.getBusinessPageByContact(queryParams)
|
||||
break
|
||||
default:
|
||||
return
|
||||
}
|
||||
@ -96,6 +134,41 @@ const openDetail = (id: number) => {
|
||||
push({ name: 'CrmBusinessDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 打开联系人与商机的关联弹窗 */
|
||||
const businessModalRef = ref()
|
||||
const openBusinessModal = () => {
|
||||
businessModalRef.value.open()
|
||||
}
|
||||
const createContactBusinessList = async (businessIds: number[]) => {
|
||||
const data = {
|
||||
contactId: props.bizId,
|
||||
businessIds: businessIds
|
||||
} as ContactApi.ContactBusinessReqVO
|
||||
businessRef.value.getSelectionRows().forEach((row: BusinessApi.BusinessVO) => {
|
||||
data.businessIds.push(row.id)
|
||||
})
|
||||
await ContactApi.createContactBusinessList(data)
|
||||
// 刷新列表
|
||||
message.success('关联商机成功')
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 解除联系人与商机的关联 */
|
||||
const businessRef = ref()
|
||||
const deleteContactBusinessList = async () => {
|
||||
const data = {
|
||||
contactId: props.bizId,
|
||||
businessIds: businessRef.value.getSelectionRows().map((row: BusinessApi.BusinessVO) => row.id)
|
||||
} as ContactApi.ContactBusinessReqVO
|
||||
if (data.businessIds.length === 0) {
|
||||
return message.error('未选择商机')
|
||||
}
|
||||
await ContactApi.deleteContactBusinessList(data)
|
||||
// 刷新列表
|
||||
message.success('取关商机成功')
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 监听打开的 bizId + bizType,从而加载最新的列表 */
|
||||
watch(
|
||||
() => [props.bizId, props.bizType],
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<Dialog title="关联商机" v-model="dialogVisible">
|
||||
<!-- 搜索工作栏 -->
|
||||
<ContentWrap>
|
||||
<el-form
|
||||
@ -29,7 +29,6 @@
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<!-- TODO @zyna:字段按照他们对齐下 -->
|
||||
<ContentWrap class="mt-10px">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
@ -75,16 +74,16 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as BusinessApi from '@/api/crm/business'
|
||||
import BusinessForm from '../../business/BusinessForm.vue'
|
||||
import BusinessForm from '../BusinessForm.vue'
|
||||
import { fenToYuanFormat } from '@/utils/formatter'
|
||||
// TODO @zyna:下面这个拼接,要注意大小写哈
|
||||
import * as ContactbusinesslinkApi from '@/api/crm/contactbusinesslink'
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
defineOptions({ name: 'CrmBusinessLinkContactList' })
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps<{
|
||||
customerId: number
|
||||
}>()
|
||||
defineOptions({ name: 'BusinessListModal' })
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题 TODO @zyna:是不是搞个标题?
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
@ -93,17 +92,14 @@ const formLoading = ref(false) // 表单的加载中:1)修改时的数据加
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
// TODO @zyna:是不是要根据 customerId 筛选?
|
||||
name: undefined
|
||||
name: undefined,
|
||||
customerId: props.customerId
|
||||
})
|
||||
const contactIdProp = ref(0) // 联系人编号
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (contactId: number) => {
|
||||
const open = async () => {
|
||||
dialogVisible.value = true
|
||||
contactIdProp.value = contactId
|
||||
// TODO @zyna:下面要 await 下;一般 idea 如果有黄色警告,最好都看看哈
|
||||
getList()
|
||||
await getList()
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
@ -111,7 +107,7 @@ defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await BusinessApi.getBusinessPage(queryParams)
|
||||
const data = await BusinessApi.getBusinessPageByCustomer(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
@ -141,25 +137,14 @@ const openForm = () => {
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const businessRef = ref()
|
||||
const submitForm = async () => {
|
||||
// TODO @zyna:可以 if return,这样括号层级简单一点
|
||||
if (businessRef.value.getSelectionRows().length === 0) {
|
||||
message.success('未选择商机')
|
||||
} else {
|
||||
// TODO @zyna:这里 postData 应该不用 ref,搞个 数组就好了?
|
||||
const postData = ref<ContactbusinesslinkApi.ContactBusinessLinkVO[]>([])
|
||||
businessRef.value.getSelectionRows().forEach((element) => {
|
||||
// TODO @zyna:可以直接 push,不用声明 data
|
||||
let data = {
|
||||
id: undefined,
|
||||
businessId: element.id,
|
||||
contactId: contactIdProp.value
|
||||
}
|
||||
postData.value.push(data)
|
||||
})
|
||||
await ContactbusinesslinkApi.createContactBusinessLinkBatch(postData.value)
|
||||
dialogVisible.value = false
|
||||
emit('success')
|
||||
const businessIds = businessRef.value
|
||||
.getSelectionRows()
|
||||
.map((row: BusinessApi.BusinessVO) => row.id)
|
||||
if (businessIds.length === 0) {
|
||||
return message.error('未选择商机')
|
||||
}
|
||||
dialogVisible.value = false
|
||||
emit('success', businessIds)
|
||||
}
|
||||
|
||||
/** 打开联系人详情 */
|
@ -49,8 +49,8 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12"
|
||||
><el-form-item label="性别" prop="sex">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select v-model="formData.sex" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||
@ -58,8 +58,9 @@
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select> </el-form-item
|
||||
></el-col>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
@ -72,8 +73,8 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="座机" prop="telephone">
|
||||
<el-input v-model="formData.telephone" placeholder="请输入座机" style="width: 215px" />
|
||||
<el-form-item label="电话" prop="telephone">
|
||||
<el-input v-model="formData.telephone" placeholder="请输入电话" style="width: 215px" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@ -104,10 +105,10 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="下次联系时间" prop="nextTime">
|
||||
<el-form-item label="下次联系时间" prop="contactNextTime">
|
||||
<el-date-picker
|
||||
v-model="formData.nextTime"
|
||||
type="date"
|
||||
v-model="formData.contactNextTime"
|
||||
type="datetime"
|
||||
value-format="x"
|
||||
placeholder="选择下次联系时间"
|
||||
/>
|
||||
@ -126,15 +127,17 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="地址" prop="address">
|
||||
<el-form-item label="地址" prop="detailAddress">
|
||||
<el-input
|
||||
input-style="width:190px;"
|
||||
v-model="formData.address"
|
||||
v-model="formData.detailAddress"
|
||||
placeholder="请输入地址"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col> </el-row
|
||||
><el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- TODO @zyna:解决下 ide 报错 -->
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="直属上级" prop="parentId">
|
||||
<el-select v-model="formData.parentId" placeholder="请选择">
|
||||
@ -152,10 +155,11 @@
|
||||
<el-form-item label="职位" prop="post">
|
||||
<el-input input-style="width:190px;" v-model="formData.post" placeholder="请输入职位" />
|
||||
</el-form-item>
|
||||
</el-col> </el-row
|
||||
><el-row>
|
||||
<el-col :span="12"
|
||||
><el-form-item label="是否关键决策人" prop="master" style="width: 400px">
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="关键决策人" prop="master" style="width: 400px">
|
||||
<el-radio-group v-model="formData.master">
|
||||
<el-radio
|
||||
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
||||
@ -169,8 +173,8 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24"
|
||||
><el-form-item label="备注" prop="remark">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -192,19 +196,20 @@ import { defaultProps } from '@/utils/tree'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const areaList = ref([]) // 地区列表
|
||||
const formData = ref({
|
||||
nextTime: undefined,
|
||||
contactNextTime: undefined,
|
||||
mobile: undefined,
|
||||
telephone: undefined,
|
||||
email: undefined,
|
||||
customerId: undefined,
|
||||
customerName: undefined,
|
||||
address: undefined,
|
||||
detailAddress: undefined,
|
||||
remark: undefined,
|
||||
ownerUserId: undefined,
|
||||
lastTime: undefined,
|
||||
@ -279,13 +284,14 @@ const submitForm = async () => {
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
// TODO zyna:ide 告警,看看怎么去掉哈;
|
||||
formData.value = {
|
||||
nextTime: undefined,
|
||||
contactNextTime: undefined,
|
||||
mobile: undefined,
|
||||
telephone: undefined,
|
||||
email: undefined,
|
||||
customerId: undefined,
|
||||
address: undefined,
|
||||
detailAddress: undefined,
|
||||
remark: undefined,
|
||||
ownerUserId: undefined,
|
||||
lastTime: undefined,
|
||||
|
@ -1,14 +1,5 @@
|
||||
<!--
|
||||
* @Author: zyna
|
||||
* @Date: 2023-12-02 13:08:57
|
||||
* @LastEditTime: 2023-12-03 13:47:16
|
||||
* @FilePath: \yudao-ui-admin-vue3\src\views\crm\contact\detail\ContactDetailsHeader.vue
|
||||
* @Description:
|
||||
-->
|
||||
<!-- TODO @zyna:上面这个不加哈 -->
|
||||
<template>
|
||||
<!-- TODO @zyna:loading 缺了 -->
|
||||
<div v-loading="loading">
|
||||
<div>
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<el-col>
|
||||
@ -48,7 +39,6 @@
|
||||
import * as ContactApi from '@/api/crm/contact'
|
||||
import ContactForm from '@/views/crm/contact/ContactForm.vue'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
|
||||
//操作修改
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
|
@ -1,4 +1,5 @@
|
||||
<template>
|
||||
<!-- TODO @zyna:少了一个外边框 -->
|
||||
<el-collapse v-model="activeNames">
|
||||
<el-collapse-item name="basicInfo">
|
||||
<template #title>
|
||||
|
@ -10,18 +10,22 @@
|
||||
<PermissionList :biz-id="contact.id!" :biz-type="BizTypeEnum.CRM_CONTACT" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="商机" lazy>
|
||||
<BusinessList :biz-id="contact.id!" :biz-type="BizTypeEnum.CRM_CONTACT" />
|
||||
<BusinessList
|
||||
:biz-id="contact.id!"
|
||||
:biz-type="BizTypeEnum.CRM_CONTACT"
|
||||
:customer-id="contact.customerId"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-col>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ElMessage } from 'element-plus' // TODO @zyna:使用 hook 引入 message
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import * as ContactApi from '@/api/crm/contact'
|
||||
import ContactDetailsHeader from '@/views/crm/contact/detail/ContactDetailsHeader.vue'
|
||||
import ContactDetailsInfo from '@/views/crm/contact/detail/ContactDetailsInfo.vue'
|
||||
import BusinessList from '@/views/crm/contactBusinessLink/components/BusinessListByContact.vue' // 商机列表
|
||||
import BusinessList from '@/views/crm/business/components/BusinessList.vue' // 商机列表
|
||||
import PermissionList from '@/views/crm/permission/components/PermissionList.vue' // 团队成员列表(权限)
|
||||
import { BizTypeEnum } from '@/api/crm/permission'
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
<el-form-item label="客户" prop="customerId">
|
||||
<el-select
|
||||
v-model="queryParams.customerId"
|
||||
class="!w-240px"
|
||||
placeholder="请选择客户"
|
||||
value-key="id"
|
||||
lable-key="name"
|
||||
@ -28,56 +29,55 @@
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
class="!w-240px"
|
||||
placeholder="请输入姓名"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号" prop="mobile">
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
class="!w-240px"
|
||||
placeholder="请输入手机号"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="座机" prop="telephone">
|
||||
<el-form-item label="电话" prop="telephone">
|
||||
<el-input
|
||||
v-model="queryParams.telephone"
|
||||
class="!w-240px"
|
||||
placeholder="请输入电话"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="QQ" prop="qq">
|
||||
<el-input
|
||||
v-model="queryParams.qq"
|
||||
class="!w-240px"
|
||||
placeholder="请输入QQ"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="微信" prop="wechat">
|
||||
<el-input
|
||||
v-model="queryParams.wechat"
|
||||
class="!w-240px"
|
||||
placeholder="请输入微信"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="电子邮箱" prop="email">
|
||||
<el-input
|
||||
v-model="queryParams.email"
|
||||
class="!w-240px"
|
||||
placeholder="请输入电子邮箱"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
@ -102,53 +102,57 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="姓名" fixed="left" align="center" prop="name">
|
||||
<el-table-column label="姓名" fixed="left" align="center" prop="name" width="140">
|
||||
<template #default="scope">
|
||||
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">
|
||||
{{ scope.row.name }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="客户" fixed="left" align="center" prop="customerName" />
|
||||
<el-table-column label="客户名称" fixed="left" align="center" prop="customerName" width="120">
|
||||
<template #default="scope">
|
||||
<el-link
|
||||
type="primary"
|
||||
:underline="false"
|
||||
@click="openCustomerDetail(scope.row.customerId)"
|
||||
>
|
||||
{{ scope.row.customerName }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="手机" align="center" prop="mobile" width="120" />
|
||||
<el-table-column label="电话" align="center" prop="telephone" width="120" />
|
||||
<el-table-column label="邮箱" align="center" prop="email" width="120" />
|
||||
<el-table-column label="职位" align="center" prop="post" width="120" />
|
||||
<el-table-column label="地址" align="center" prop="detailAddress" width="120" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="下次联系时间"
|
||||
prop="contactNextTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="关键决策人" align="center" prop="master" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.master" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="直属上级" align="center" prop="parentName" width="140" />
|
||||
<el-table-column
|
||||
label="最后跟进时间"
|
||||
align="center"
|
||||
prop="contactLastTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="性别" align="center" prop="sex">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="职位" align="center" prop="post" />
|
||||
<el-table-column label="是否关键决策人" align="center" prop="master">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.master" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="直属上级" align="center" prop="parentName" />
|
||||
<el-table-column label="手机号" align="center" prop="mobile" />
|
||||
<el-table-column label="座机" align="center" prop="telephone" />
|
||||
<el-table-column label="QQ" align="center" prop="qq" />
|
||||
<el-table-column label="微信" align="center" prop="wechat" />
|
||||
<el-table-column label="邮箱" align="center" prop="email" />
|
||||
<el-table-column label="地址" align="center" prop="address" />
|
||||
<el-table-column
|
||||
label="下次联系时间"
|
||||
align="center"
|
||||
prop="nextTime"
|
||||
width="180px"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column
|
||||
label="最后跟进时间"
|
||||
align="center"
|
||||
prop="lastTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="负责人" align="center" prop="ownerUserId">
|
||||
<template #default="scope">
|
||||
{{ scope.row.ownerUserName }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="所属部门" align="center" prop="ownerUserId" /> -->
|
||||
<el-table-column label="负责人" align="center" prop="ownerUserName" width="120" />
|
||||
<el-table-column label="创建人" align="center" prop="creatorName" width="120" />
|
||||
<el-table-column
|
||||
label="更新时间"
|
||||
align="center"
|
||||
@ -163,21 +167,10 @@
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<!-- <el-table-column
|
||||
label="创建人"
|
||||
align="center"
|
||||
prop="creator"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ userList.find((user) => user.id === scope.row.creator)?.nickname }}
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="操作" align="center" fixed="right" width="200">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
plain
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['crm:contact:update']"
|
||||
@ -185,7 +178,7 @@
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
plain
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['crm:contact:delete']"
|
||||
@ -228,23 +221,13 @@ const customerList = ref<CustomerApi.CustomerVO[]>([]) // 客户列表
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
nextTime: [],
|
||||
mobile: null,
|
||||
telephone: null,
|
||||
email: null,
|
||||
customerId: null,
|
||||
address: null,
|
||||
remark: null,
|
||||
ownerUserId: null,
|
||||
createTime: [],
|
||||
lastTime: [],
|
||||
parentId: null,
|
||||
name: null,
|
||||
post: null,
|
||||
qq: null,
|
||||
wechat: null,
|
||||
sex: null,
|
||||
policyMakers: null
|
||||
mobile: undefined,
|
||||
telephone: undefined,
|
||||
email: undefined,
|
||||
customerId: undefined,
|
||||
name: undefined,
|
||||
qq: undefined,
|
||||
wechat: undefined
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
@ -307,11 +290,15 @@ const handleExport = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开客户详情 */
|
||||
/** 打开联系人详情 */
|
||||
const { push } = useRouter()
|
||||
const openDetail = (id: number) => {
|
||||
push({ name: 'CrmContactDetail', params: { id } })
|
||||
}
|
||||
/** 打开客户详情 */
|
||||
const openCustomerDetail = (id: number) => {
|
||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
|
@ -1,143 +0,0 @@
|
||||
<template>
|
||||
<!-- 操作栏 -->
|
||||
<el-row justify="end">
|
||||
<el-button @click="openForm">
|
||||
<Icon class="mr-5px" icon="ep:opportunity" />
|
||||
创建商机
|
||||
</el-button>
|
||||
<el-button @click="openBusinessLink"> 关联 </el-button>
|
||||
<el-button @click="deleteBusinessLink"> 解除关联 </el-button>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap class="mt-10px">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
ref="businessRef"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="商机名称" fixed="left" align="center" prop="name">
|
||||
<template #default="scope">
|
||||
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">
|
||||
{{ scope.row.name }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商机金额" align="center" prop="price" :formatter="fenToYuanFormat" />
|
||||
<el-table-column label="客户名称" align="center" prop="customerName" />
|
||||
<el-table-column label="商机组" align="center" prop="statusTypeName" />
|
||||
<el-table-column label="商机阶段" align="center" prop="statusName" />
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加 -->
|
||||
<BusinessForm ref="formRef" @success="getList" />
|
||||
<!---->
|
||||
<BusinessLink ref="businessLinkRef" @success="getList" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as ContactBusinessLinkApi from '@/api/crm/contactbusinesslink'
|
||||
import BusinessForm from '../../business/BusinessForm.vue'
|
||||
import { BizTypeEnum } from '@/api/crm/permission'
|
||||
import { fenToYuanFormat } from '@/utils/formatter'
|
||||
import BusinessLink from './BusinessLinkContactList.vue'
|
||||
const message = useMessage() // 消息弹窗
|
||||
// TODO @zyna:这个组件,可以服用 BusinessList,然后根据传入的编号类型,做一些判断?
|
||||
defineOptions({ name: 'CrmBusinessContactList' })
|
||||
const props = defineProps<{
|
||||
bizType: number // 业务类型
|
||||
bizId: number // 业务编号
|
||||
}>()
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
contactId: undefined as unknown // 允许 undefined + number
|
||||
})
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 置空参数
|
||||
queryParams.contactId = undefined
|
||||
// 执行查询
|
||||
let data = { list: [], total: 0 }
|
||||
switch (props.bizType) {
|
||||
case BizTypeEnum.CRM_CONTACT:
|
||||
queryParams.contactId = props.bizId
|
||||
data = await ContactBusinessLinkApi.getBusinessByContactPage(queryParams)
|
||||
break
|
||||
default:
|
||||
return
|
||||
}
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 添加操作 */
|
||||
const formRef = ref()
|
||||
const openForm = () => {
|
||||
formRef.value.open('create')
|
||||
}
|
||||
/** 关联操作 */
|
||||
const businessLinkRef = ref()
|
||||
const openBusinessLink = () => {
|
||||
businessLinkRef.value.open(props.bizId)
|
||||
}
|
||||
/**解除关联 */
|
||||
const businessRef = ref()
|
||||
const deleteBusinessLink = async () => {
|
||||
if (businessRef.value.getSelectionRows().length === 0) {
|
||||
message.success('未选择商机')
|
||||
} else {
|
||||
const postData = ref<ContactBusinessLinkApi.ContactBusinessLinkVO[]>([])
|
||||
businessRef.value.getSelectionRows().forEach((element) => {
|
||||
let data = {
|
||||
id: undefined,
|
||||
businessId: element.id,
|
||||
contactId: props.bizId
|
||||
}
|
||||
postData.value.push(data)
|
||||
})
|
||||
await ContactBusinessLinkApi.deleteContactBusinessLink(postData.value)
|
||||
handleQuery()
|
||||
}
|
||||
}
|
||||
/** 打开联系人详情 */
|
||||
const { push } = useRouter()
|
||||
const openDetail = (id: number) => {
|
||||
push({ name: 'CrmBusinessDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 监听打开的 bizId + bizType,从而加载最新的列表 */
|
||||
watch(
|
||||
() => [props.bizId, props.bizType],
|
||||
() => {
|
||||
handleQuery()
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
</script>
|
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="formLoading"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
@ -17,7 +17,7 @@
|
||||
<el-form-item label="所属行业" prop="industryId">
|
||||
<el-select v-model="formData.industryId" placeholder="请选择所属行业">
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)"
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
@ -31,7 +31,7 @@
|
||||
<el-form-item label="客户来源" prop="source">
|
||||
<el-select v-model="formData.source" placeholder="请选择客户来源">
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)"
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
@ -43,7 +43,7 @@
|
||||
<el-form-item label="客户等级" prop="level">
|
||||
<el-select v-model="formData.level" placeholder="请选择客户等级">
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)"
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
@ -120,9 +120,9 @@
|
||||
<el-form-item label="下次联系时间" prop="contactNextTime">
|
||||
<el-date-picker
|
||||
v-model="formData.contactNextTime"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择下次联系时间"
|
||||
type="datetime"
|
||||
value-format="x"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -139,13 +139,13 @@
|
||||
</el-col>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import * as AreaApi from '@/api/system/area'
|
||||
import { defaultProps } from '@/utils/tree'
|
||||
|
@ -11,10 +11,19 @@
|
||||
</div>
|
||||
<div>
|
||||
<!-- 右上:按钮 -->
|
||||
<el-button v-hasPermi="['crm:customer:update']" @click="openForm(customer.id)">
|
||||
<el-button
|
||||
type="primary"
|
||||
v-hasPermi="['crm:customer:update']"
|
||||
@click="openForm(customer.id)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<!-- TODO @puhui999:转移的操作接入 -->
|
||||
<el-button type="primary" @click="transfer">转移</el-button>
|
||||
<!-- TODO @puhui999:修改成交状态的接入 -->
|
||||
<el-button>更改成交状态</el-button>
|
||||
<el-button v-if="customer.lockStatus" @click="handleUnlock(customer.id!)">解锁</el-button>
|
||||
<el-button v-else @click="handleLock(customer.id!)">锁定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -26,26 +35,29 @@
|
||||
<el-descriptions-item label="成交状态">
|
||||
{{ customer.dealStatus ? '已成交' : '未成交' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="负责人">{{ customer.ownerUserName }} </el-descriptions-item>
|
||||
<el-descriptions-item label="负责人">{{ customer.ownerUserName }}</el-descriptions-item>
|
||||
<!-- TODO wanwan 首要联系人? -->
|
||||
<el-descriptions-item label="首要联系人" />
|
||||
<!-- TODO wanwan 首要联系人电话? -->
|
||||
<el-descriptions-item label="首要联系人电话">{{ customer.mobile }} </el-descriptions-item>
|
||||
<el-descriptions-item label="首要联系人电话">{{ customer.mobile }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<CustomerForm ref="formRef" @success="emit('refresh')" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import CustomerForm from '../CustomerForm.vue'
|
||||
|
||||
defineOptions({ name: 'CustomerDetailsHeader' })
|
||||
|
||||
const { customer, loading } = defineProps<{
|
||||
customer: CustomerApi.CustomerVO // 客户信息
|
||||
loading: boolean // 加载中
|
||||
}>()
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
/** 修改操作 */
|
||||
const formRef = ref()
|
||||
@ -53,5 +65,20 @@ const openForm = (id?: number) => {
|
||||
formRef.value.open('update', id)
|
||||
}
|
||||
|
||||
/** 锁定操作 */
|
||||
const handleLock = async (id: number) => {
|
||||
await CustomerApi.lockCustomer(id, true)
|
||||
message.success('锁定成功')
|
||||
emit('refresh')
|
||||
}
|
||||
|
||||
/** 解锁操作 */
|
||||
const handleUnlock = async (id: number) => {
|
||||
console.log(customer, '=======')
|
||||
await CustomerApi.lockCustomer(id, false)
|
||||
message.success('解锁成功')
|
||||
emit('refresh')
|
||||
}
|
||||
|
||||
const emit = defineEmits(['refresh']) // 定义 success 事件,用于操作成功后的回调
|
||||
</script>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<el-collapse class="" v-model="activeNames">
|
||||
<el-collapse v-model="activeNames" class="">
|
||||
<el-collapse-item name="basicInfo">
|
||||
<template #title>
|
||||
<span class="text-base font-bold">基本信息</span>
|
||||
@ -20,11 +20,11 @@
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="手机">{{ customer.mobile }}</el-descriptions-item>
|
||||
<el-descriptions-item label="电话">{{ customer.telephone }}</el-descriptions-item>
|
||||
<el-descriptions-item label="邮箱">{{ customer.email }} </el-descriptions-item>
|
||||
<el-descriptions-item label="QQ">{{ customer.qq }} </el-descriptions-item>
|
||||
<el-descriptions-item label="微信">{{ customer.wechat }} </el-descriptions-item>
|
||||
<el-descriptions-item label="网址">{{ customer.website }} </el-descriptions-item>
|
||||
<el-descriptions-item label="所在地">{{ customer.areaName }} </el-descriptions-item>
|
||||
<el-descriptions-item label="邮箱">{{ customer.email }}</el-descriptions-item>
|
||||
<el-descriptions-item label="QQ">{{ customer.qq }}</el-descriptions-item>
|
||||
<el-descriptions-item label="微信">{{ customer.wechat }}</el-descriptions-item>
|
||||
<el-descriptions-item label="网址">{{ customer.website }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在地">{{ customer.areaName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="详细地址"
|
||||
>{{ customer.detailAddress }}
|
||||
</el-descriptions-item>
|
||||
@ -38,8 +38,8 @@
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-descriptions :column="1">
|
||||
<el-descriptions-item label="客户描述">{{ customer.description }} </el-descriptions-item>
|
||||
<el-descriptions-item label="备注">{{ customer.remark }} </el-descriptions-item>
|
||||
<el-descriptions-item label="客户描述">{{ customer.description }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">{{ customer.remark }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="systemInfo">
|
||||
@ -47,8 +47,8 @@
|
||||
<span class="text-base font-bold">系统信息</span>
|
||||
</template>
|
||||
<el-descriptions :column="2">
|
||||
<el-descriptions-item label="负责人">{{ customer.ownerUserName }} </el-descriptions-item>
|
||||
<el-descriptions-item label="创建人">{{ customer.creatorName }} </el-descriptions-item>
|
||||
<el-descriptions-item label="负责人">{{ customer.ownerUserName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建人">{{ customer.creatorName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">
|
||||
{{ customer.createTime ? formatDate(customer.createTime) : '空' }}
|
||||
</el-descriptions-item>
|
||||
@ -60,15 +60,16 @@
|
||||
</el-collapse>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
|
||||
defineOptions({ name: 'CustomerDetailsInfo' })
|
||||
const { customer } = defineProps<{
|
||||
customer: CustomerApi.CustomerVO // 客户明细
|
||||
}>()
|
||||
|
||||
const activeNames = ref(['basicInfo', 'systemInfo']) // 展示的折叠面板
|
||||
</script>
|
||||
<style scoped lang="scss"></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
@ -5,7 +5,9 @@
|
||||
<el-tab-pane label="详细资料">
|
||||
<CustomerDetailsInfo :customer="customer" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="操作日志" lazy>TODO 待开发</el-tab-pane>
|
||||
<el-tab-pane label="操作日志">
|
||||
<OperateLogV2 :log-list="logList" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="联系人" lazy>
|
||||
<ContactList :biz-id="customer.id!" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
|
||||
</el-tab-pane>
|
||||
@ -38,24 +40,39 @@ import ReceivableList from '@/views/crm/receivable/components/ReceivableList.vue
|
||||
import ReceivablePlanList from '@/views/crm/receivable/plan/components/ReceivablePlanList.vue' // 回款计划列表
|
||||
import PermissionList from '@/views/crm/permission/components/PermissionList.vue' // 团队成员列表(权限)
|
||||
import { BizTypeEnum } from '@/api/crm/permission'
|
||||
import { OperateLogV2VO } from '@/api/system/operatelog'
|
||||
|
||||
defineOptions({ name: 'CrmCustomerDetail' })
|
||||
|
||||
const route = useRoute()
|
||||
const id = Number(route.params.id) // 客户编号
|
||||
const loading = ref(true) // 加载中
|
||||
|
||||
/** 获取详情 */
|
||||
const customer = ref<CustomerApi.CustomerVO>({} as CustomerApi.CustomerVO) // 客户详情
|
||||
const getCustomer = async (id: number) => {
|
||||
loading.value = true
|
||||
try {
|
||||
customer.value = await CustomerApi.getCustomer(id)
|
||||
await getOperateLog(id)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const logList = ref<OperateLogV2VO[]>([]) // 操作日志列表
|
||||
/**
|
||||
* 获取操作日志
|
||||
*/
|
||||
const getOperateLog = async (customerId: number) => {
|
||||
if (!customerId) {
|
||||
return
|
||||
}
|
||||
const data = await CustomerApi.getOperateLogPage({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
bizId: customerId
|
||||
})
|
||||
logList.value = data.list
|
||||
}
|
||||
/** 初始化 */
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
const { currentRoute } = useRouter() // 路由
|
||||
|
@ -101,38 +101,24 @@
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="所属行业" prop="industryId" width="120">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="scope.row.industryId" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="手机" prop="mobile" width="120" />
|
||||
<el-table-column align="center" label="电话" prop="telephone" width="120" />
|
||||
<el-table-column align="center" label="客户来源" prop="source" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="所属行业" prop="industryId" width="120">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="scope.row.industryId" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="客户等级" prop="level" width="120">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="手机" prop="mobile" width="120" />
|
||||
<el-table-column align="center" label="详细地址" prop="detailAddress" width="200" />
|
||||
<el-table-column align="center" label="负责人" prop="ownerUserName" />
|
||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" />
|
||||
<el-table-column align="center" label="创建人" prop="creatorName" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="创建时间"
|
||||
prop="createTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column align="center" label="成交状态" prop="dealStatus">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.dealStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="网址" prop="website" width="200" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
@ -140,6 +126,13 @@
|
||||
prop="contactNextTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column align="center" label="备注" prop="remark" width="200" />
|
||||
<el-table-column align="center" label="成交状态" prop="dealStatus">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.dealStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- TODO @puhui999:距进入公海天数 -->
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
@ -147,12 +140,23 @@
|
||||
prop="contactLastTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column align="center" label="锁定状态" prop="lockStatus">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.lockStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- TODO @wanwan 距进入公海天数 -->
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="创建时间"
|
||||
prop="updateTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="创建时间"
|
||||
prop="createTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column align="center" label="负责人" prop="ownerUserName" width="100px" />
|
||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
|
||||
<el-table-column align="center" label="创建人" prop="creatorName" width="100px" />
|
||||
<el-table-column align="center" fixed="right" label="操作" min-width="150">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
|
@ -56,12 +56,12 @@
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as CustomerLimitConfigApi from '@/api/crm/customerLimitConfig'
|
||||
import * as CustomerLimitConfigApi from '@/api/crm/customer/limitConfig'
|
||||
import * as DeptApi from '@/api/system/dept'
|
||||
import { defaultProps, handleTree } from '@/utils/tree'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { LimitConfType } from '@/api/crm/customerLimitConfig'
|
||||
import { LimitConfType } from '@/api/crm/customer/limitConfig'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
@ -72,7 +72,7 @@ const formLoading = ref(false) // 表单的加载中:1)修改时的数据加
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
type: undefined,
|
||||
type: LimitConfType.CUSTOMER_LOCK_LIMIT, // 给个默认值,避免 IDE 报错
|
||||
userIds: undefined,
|
||||
deptIds: undefined,
|
||||
maxCount: undefined,
|
||||
@ -88,15 +88,11 @@ const deptTree = ref() // 部门树形结构
|
||||
const userTree = ref() // 用户树形结构
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number, limitConfType?: LimitConfType) => {
|
||||
const open = async (type: string, limitConfType: LimitConfType, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 获得部门树
|
||||
await getDeptTree()
|
||||
// 获得用户
|
||||
await getUserTree()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
@ -108,6 +104,10 @@ const open = async (type: string, id?: number, limitConfType?: LimitConfType) =>
|
||||
} else {
|
||||
formData.value.type = limitConfType
|
||||
}
|
||||
// 获得部门树
|
||||
await getDeptTree()
|
||||
// 获得用户
|
||||
await getUserTree()
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
@ -141,7 +141,7 @@ const submitForm = async () => {
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
type: undefined,
|
||||
type: LimitConfType.CUSTOMER_LOCK_LIMIT,
|
||||
userIds: undefined,
|
||||
deptIds: undefined,
|
||||
maxCount: undefined,
|
@ -1,7 +1,5 @@
|
||||
<template>
|
||||
<el-button type="primary" plain @click="handleQuery">
|
||||
<Icon icon="ep:refresh" class="mr-5px" /> 刷新
|
||||
</el-button>
|
||||
<el-button plain @click="handleQuery"> <Icon icon="ep:refresh" class="mr-5px" /> 刷新 </el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@ -87,10 +85,10 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as CustomerLimitConfigApi from '@/api/crm/customerLimitConfig'
|
||||
import CustomerLimitConfigForm from '@/views/crm/config/customerLimitConfig/CustomerLimitConfigForm.vue'
|
||||
import * as CustomerLimitConfigApi from '@/api/crm/customer/limitConfig'
|
||||
import CustomerLimitConfigForm from './CustomerLimitConfigForm.vue'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { LimitConfType } from '@/api/crm/customerLimitConfig'
|
||||
import { LimitConfType } from '@/api/crm/customer/limitConfig'
|
||||
|
||||
defineOptions({ name: 'CustomerLimitConfigList' })
|
||||
|
||||
@ -107,8 +105,6 @@ const queryParams = reactive({
|
||||
pageSize: 10,
|
||||
type: confType
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
@ -125,7 +121,7 @@ const getList = async () => {
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id, confType)
|
||||
formRef.value.open(type, confType, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
@ -12,8 +12,8 @@
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import CustomerLimitConfigList from '@/views/crm/config/customerLimitConfig/CustomerLimitConfigList.vue'
|
||||
import { LimitConfType } from '@/api/crm/customerLimitConfig'
|
||||
import CustomerLimitConfigList from './CustomerLimitConfigList.vue'
|
||||
import { LimitConfType } from '@/api/crm/customer/limitConfig'
|
||||
|
||||
defineOptions({ name: 'CrmCustomerLimitConfig' })
|
||||
</script>
|
@ -56,7 +56,7 @@
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as CustomerPoolConfigApi from '@/api/crm/customerPoolConfig'
|
||||
import * as CustomerPoolConfigApi from '@/api/crm/customer/poolConfig'
|
||||
import { CardTitle } from '@/components/Card'
|
||||
|
||||
defineOptions({ name: 'CrmCustomerPoolConfig' })
|
||||
@ -67,10 +67,10 @@ const { t } = useI18n() // 国际化
|
||||
const formLoading = ref(false)
|
||||
const formData = ref({
|
||||
enabled: false,
|
||||
contactExpireDays: 0,
|
||||
dealExpireDays: 0,
|
||||
contactExpireDays: undefined,
|
||||
dealExpireDays: undefined,
|
||||
notifyEnabled: false,
|
||||
notifyDays: 0
|
||||
notifyDays: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
enabled: [{ required: true, message: '是否启用客户公海不能为空', trigger: 'blur' }]
|
||||
@ -100,7 +100,7 @@ const onSubmit = async () => {
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as CustomerPoolConfigApi.CustomerPoolConfigVO
|
||||
const data = formData.value as CustomerPoolConfigApi.CustomerPoolConfigVO
|
||||
await CustomerPoolConfigApi.saveCustomerPoolConfig(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
await getConfig()
|
@ -17,6 +17,7 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- TODO @puhui999:编辑时,level 没带过来 -->
|
||||
<el-form-item label="权限级别" prop="level">
|
||||
<el-radio-group v-model="formData.level">
|
||||
<template
|
||||
|
@ -24,6 +24,7 @@
|
||||
:stripe="true"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<!-- TODO @puhui999:负责人不允许选中 -->
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column align="center" label="姓名" prop="nickname" />
|
||||
<el-table-column align="center" label="部门" prop="deptName" />
|
||||
|
@ -197,7 +197,7 @@ defineOptions({ name: 'ProductSpuBasicInfoForm' })
|
||||
const ruleConfig: RuleConfig[] = [
|
||||
{
|
||||
name: 'stock',
|
||||
rule: (arg) => arg >= 1,
|
||||
rule: (arg) => arg >= 0,
|
||||
message: '商品库存必须大于等于 1 !!!'
|
||||
},
|
||||
{
|
||||
@ -213,7 +213,7 @@ const ruleConfig: RuleConfig[] = [
|
||||
{
|
||||
name: 'costPrice',
|
||||
rule: (arg) => arg >= 0.01,
|
||||
message: '商品成本价格必须大于等于 0.01 元!!!'
|
||||
message: '商品成本价格必须大于等于 0.00 元!!!'
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -30,6 +30,7 @@ const getPageDetail = async (id: any) => {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
|
@ -27,10 +27,11 @@
|
||||
</DiyEditor>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// TODO @疯狂:要不要建个 decorate 目录,然后挪进去,改成 index.vue,这样可以更明确看到是个独立界面哈,更好找
|
||||
import * as DiyTemplateApi from '@/api/mall/promotion/diy/template'
|
||||
import * as DiyPageApi from '@/api/mall/promotion/diy/page'
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import { DiyComponentLibrary, PAGE_LIBS } from '@/components/DiyEditor/util'
|
||||
import { DiyComponentLibrary, PAGE_LIBS } from '@/components/DiyEditor/util' // 商城的 DIY 组件,在 DiyEditor 目录下
|
||||
import { toNumber } from 'lodash-es'
|
||||
|
||||
/** 装修模板表单 */
|
||||
@ -51,7 +52,7 @@ const formData = ref<DiyTemplateApi.DiyTemplatePropertyVO>()
|
||||
const formRef = ref() // 表单 Ref
|
||||
// 当前编辑的属性
|
||||
const currentFormData = ref<DiyTemplateApi.DiyTemplatePropertyVO | DiyPageApi.DiyPageVO>()
|
||||
// 商城H5预览地址
|
||||
// 商城 H5 预览地址
|
||||
const previewUrl = ref('')
|
||||
|
||||
// 获取详情
|
||||
|
@ -205,7 +205,7 @@ const handleUse = async (row: DiyTemplateApi.DiyTemplateVO) => {
|
||||
// 使用模板的二次确认
|
||||
await message.confirm(`是否使用模板“${row.name}”?`)
|
||||
// 发起删除
|
||||
await DiyTemplateApi.useDiyTemplate(row.id)
|
||||
await DiyTemplateApi.useDiyTemplate(row.id!)
|
||||
message.success('使用成功')
|
||||
// 刷新列表
|
||||
await getList()
|
||||
|
113
src/views/report/ureport/UReportDataForm.vue
Normal file
113
src/views/report/ureport/UReportDataForm.vue
Normal file
@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="文件名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入文件名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="formData.status" placeholder="请选择状态">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="文件内容" prop="content">
|
||||
<Editor v-model="formData.content" height="150px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import * as UReportDataApi from '@/api/report/ureport'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
status: undefined,
|
||||
content: undefined,
|
||||
remark: undefined,
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '文件名称不能为空', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '状态不能为空', trigger: 'change' }],
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await UReportDataApi.getUReportData(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as UReportDataApi.UReportDataVO
|
||||
if (formType.value === 'create') {
|
||||
await UReportDataApi.createUReportData(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await UReportDataApi.updateUReportData(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
status: undefined,
|
||||
content: undefined,
|
||||
remark: undefined,
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
13
src/views/report/ureport/index.vue
Normal file
13
src/views/report/ureport/index.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<IFrame :src="src" />
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getAccessToken } from '@/utils/auth'
|
||||
|
||||
defineOptions({ name: 'UReportData' })
|
||||
|
||||
const BASE_URL = import.meta.env.VITE_BASE_URL
|
||||
const src = ref(BASE_URL + '/ureport/designer?token=' + getAccessToken())
|
||||
</script>
|
220
src/views/report/ureport/index2.vue
Normal file
220
src/views/report/ureport/index2.vue
Normal file
@ -0,0 +1,220 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="文件名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入文件名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="请选择状态"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
v-model="queryParams.remark"
|
||||
placeholder="请输入备注"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
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"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['report:ureport-data:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['report:ureport-data:export']"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="ID" align="center" prop="id" />
|
||||
<el-table-column label="文件名称" align="center" prop="name" />
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="文件内容" align="center" prop="content" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['report:ureport-data:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['report:ureport-data:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<UReportDataForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import * as UReportDataApi from '@/api/report/ureport'
|
||||
import UReportDataForm from './UReportDataForm.vue'
|
||||
|
||||
defineOptions({ name: 'UReportData' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
status: null,
|
||||
remark: null,
|
||||
createTime: [],
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await UReportDataApi.getUReportDataPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await UReportDataApi.deleteUReportData(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await UReportDataApi.exportUReportData(queryParams)
|
||||
download.excel(data, 'Ureport2报表.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user