commit
8ab311b46f
|
|
@ -15,6 +15,6 @@ export default {
|
||||||
],
|
],
|
||||||
'package.json': ['prettier --cache --write'],
|
'package.json': ['prettier --cache --write'],
|
||||||
'{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': [
|
'{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': [
|
||||||
'prettier --cache --write--parser json',
|
'prettier --cache --write --parser json',
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
20.14.0
|
22.1.0
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
"editor.tabSize": 2,
|
"editor.tabSize": 2,
|
||||||
"editor.detectIndentation": false,
|
"editor.detectIndentation": false,
|
||||||
"editor.cursorBlinking": "expand",
|
"editor.cursorBlinking": "expand",
|
||||||
"editor.largeFileOptimizations": false,
|
"editor.largeFileOptimizations": true,
|
||||||
"editor.accessibilitySupport": "off",
|
"editor.accessibilitySupport": "off",
|
||||||
"editor.cursorSmoothCaretAnimation": "on",
|
"editor.cursorSmoothCaretAnimation": "on",
|
||||||
"editor.guides.bracketPairs": "active",
|
"editor.guides.bracketPairs": "active",
|
||||||
|
|
@ -91,6 +91,7 @@
|
||||||
"**/bower_components": true,
|
"**/bower_components": true,
|
||||||
"**/.turbo": true,
|
"**/.turbo": true,
|
||||||
"**/.idea": true,
|
"**/.idea": true,
|
||||||
|
"**/.vitepress": true,
|
||||||
"**/tmp": true,
|
"**/tmp": true,
|
||||||
"**/.git": true,
|
"**/.git": true,
|
||||||
"**/.svn": true,
|
"**/.svn": true,
|
||||||
|
|
@ -112,6 +113,8 @@
|
||||||
"**/yarn.lock": true
|
"**/yarn.lock": true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"typescript.tsserver.exclude": ["**/node_modules", "**/dist", "**/.turbo"],
|
||||||
|
|
||||||
// search
|
// search
|
||||||
"search.searchEditor.singleClickBehaviour": "peekDefinition",
|
"search.searchEditor.singleClickBehaviour": "peekDefinition",
|
||||||
"search.followSymlinks": false,
|
"search.followSymlinks": false,
|
||||||
|
|
|
||||||
|
|
@ -21,3 +21,6 @@ VITE_APP_DOCALERT_ENABLE=true
|
||||||
|
|
||||||
# 百度统计
|
# 百度统计
|
||||||
VITE_APP_BAIDU_CODE = e98f2eab6ceb8688bc6d8fc5332ff093
|
VITE_APP_BAIDU_CODE = e98f2eab6ceb8688bc6d8fc5332ff093
|
||||||
|
|
||||||
|
# GoView域名
|
||||||
|
VITE_GOVIEW_URL='http://127.0.0.1:3000'
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@
|
||||||
"@vben/types": "workspace:*",
|
"@vben/types": "workspace:*",
|
||||||
"@vben/utils": "workspace:*",
|
"@vben/utils": "workspace:*",
|
||||||
"@vueuse/core": "catalog:",
|
"@vueuse/core": "catalog:",
|
||||||
|
"@vueuse/integrations": "catalog:",
|
||||||
"ant-design-vue": "catalog:",
|
"ant-design-vue": "catalog:",
|
||||||
"cropperjs": "catalog:",
|
"cropperjs": "catalog:",
|
||||||
"crypto-js": "catalog:",
|
"crypto-js": "catalog:",
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ setupVbenVxeTable({
|
||||||
},
|
},
|
||||||
toolbarConfig: {
|
toolbarConfig: {
|
||||||
import: false, // 是否导入
|
import: false, // 是否导入
|
||||||
export: false, // 四否导出
|
export: false, // 是否导出
|
||||||
refresh: true, // 是否刷新
|
refresh: true, // 是否刷新
|
||||||
print: false, // 是否打印
|
print: false, // 是否打印
|
||||||
zoom: true, // 是否缩放
|
zoom: true, // 是否缩放
|
||||||
|
|
@ -259,6 +259,21 @@ setupVbenVxeTable({
|
||||||
|
|
||||||
// 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
|
// 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
|
||||||
// vxeUI.formats.add
|
// vxeUI.formats.add
|
||||||
|
vxeUI.formats.add('formatAmount', {
|
||||||
|
cellFormatMethod({ cellValue }, digits = 2) {
|
||||||
|
if (cellValue === null || cellValue === undefined) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (isString(cellValue)) {
|
||||||
|
cellValue = Number.parseFloat(cellValue);
|
||||||
|
}
|
||||||
|
// 如果非 number,则直接返回空串
|
||||||
|
if (Number.isNaN(cellValue)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return cellValue.toFixed(digits);
|
||||||
|
},
|
||||||
|
});
|
||||||
},
|
},
|
||||||
useVbenForm,
|
useVbenForm,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import type { PageParam, PageResult } from '@vben/request';
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import type { BpmModelApi } from '#/api/bpm/model';
|
||||||
|
|
||||||
import { requestClient } from '#/api/request';
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
export namespace BpmCategoryApi {
|
export namespace BpmCategoryApi {
|
||||||
|
|
@ -11,6 +13,13 @@ export namespace BpmCategoryApi {
|
||||||
status: number;
|
status: number;
|
||||||
sort: number; // 分类排序
|
sort: number; // 分类排序
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 模型分类信息 */
|
||||||
|
export interface ModelCategoryInfo {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
modelList: BpmModelApi.ModelVO[];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 查询流程分类分页 */
|
/** 查询流程分类分页 */
|
||||||
|
|
@ -30,15 +39,30 @@ export async function getCategory(id: number) {
|
||||||
|
|
||||||
/** 新增流程分类 */
|
/** 新增流程分类 */
|
||||||
export async function createCategory(data: BpmCategoryApi.CategoryVO) {
|
export async function createCategory(data: BpmCategoryApi.CategoryVO) {
|
||||||
return requestClient.post('/bpm/category/create', data);
|
return requestClient.post<number>('/bpm/category/create', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 修改流程分类 */
|
/** 修改流程分类 */
|
||||||
export async function updateCategory(data: BpmCategoryApi.CategoryVO) {
|
export async function updateCategory(data: BpmCategoryApi.CategoryVO) {
|
||||||
return requestClient.put('/bpm/category/update', data);
|
return requestClient.put<boolean>('/bpm/category/update', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除流程分类 */
|
/** 删除流程分类 */
|
||||||
export async function deleteCategory(id: number) {
|
export async function deleteCategory(id: number) {
|
||||||
return requestClient.delete(`/bpm/category/delete?id=${id}`);
|
return requestClient.delete<boolean>(`/bpm/category/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询流程分类列表 */
|
||||||
|
export async function getCategorySimpleList() {
|
||||||
|
return requestClient.get<BpmCategoryApi.CategoryVO[]>(
|
||||||
|
`/bpm/category/simple-list`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 批量修改流程分类的排序 */
|
||||||
|
export async function updateCategorySortBatch(ids: number[]) {
|
||||||
|
const params = ids.join(',');
|
||||||
|
return requestClient.put<boolean>(
|
||||||
|
`/bpm/category/update-sort-batch?ids=${params}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace BpmModelApi {
|
||||||
|
/** 用户信息 TODO 这个是不是可以抽取出来定义在公共模块 */
|
||||||
|
export interface UserInfo {
|
||||||
|
id: number;
|
||||||
|
nickname: string;
|
||||||
|
avatar?: string;
|
||||||
|
deptId?: number;
|
||||||
|
deptName?: string;
|
||||||
|
}
|
||||||
|
/** 流程定义 VO */
|
||||||
|
export interface ProcessDefinitionVO {
|
||||||
|
id: string;
|
||||||
|
version: number;
|
||||||
|
deploymentTime: number;
|
||||||
|
suspensionState: number;
|
||||||
|
formType?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 流程模型 VO */
|
||||||
|
export interface ModelVO {
|
||||||
|
id: number;
|
||||||
|
key: string;
|
||||||
|
name: string;
|
||||||
|
icon?: string;
|
||||||
|
description: string;
|
||||||
|
category: string;
|
||||||
|
formName: string;
|
||||||
|
formType: number;
|
||||||
|
formId: number;
|
||||||
|
formCustomCreatePath: string;
|
||||||
|
formCustomViewPath: string;
|
||||||
|
processDefinition: ProcessDefinitionVO;
|
||||||
|
status: number;
|
||||||
|
remark: string;
|
||||||
|
createTime: string;
|
||||||
|
bpmnXml: string;
|
||||||
|
startUsers?: UserInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 模型分类信息 */
|
||||||
|
export interface ModelCategoryInfo {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
modelList: ModelVO[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取流程模型列表 */
|
||||||
|
export async function getModelList(name: string | undefined) {
|
||||||
|
return requestClient.get<BpmModelApi.ModelVO[]>('/bpm/model/list', {
|
||||||
|
params: { name },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取流程模型详情 */
|
||||||
|
export async function getModel(id: string) {
|
||||||
|
return requestClient.get<BpmModelApi.ModelVO>(`/bpm/model/get?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新流程模型 */
|
||||||
|
export async function updateModel(data: BpmModelApi.ModelVO) {
|
||||||
|
return requestClient.put('/bpm/model/update', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 批量修改流程模型排序 */
|
||||||
|
export async function updateModelSortBatch(ids: number[]) {
|
||||||
|
const params = ids.join(',');
|
||||||
|
return requestClient.put<boolean>(
|
||||||
|
`/bpm/model/update-sort-batch?ids=${params}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新流程模型的 BPMN XML */
|
||||||
|
export async function updateModelBpmn(data: BpmModelApi.ModelVO) {
|
||||||
|
return requestClient.put('/bpm/model/update-bpmn', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新流程模型状态 */
|
||||||
|
export async function updateModelState(id: number, state: number) {
|
||||||
|
const data = {
|
||||||
|
id,
|
||||||
|
state,
|
||||||
|
};
|
||||||
|
return requestClient.put('/bpm/model/update-state', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 创建流程模型 */
|
||||||
|
export async function createModel(data: BpmModelApi.ModelVO) {
|
||||||
|
return requestClient.post('/bpm/model/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除流程模型 */
|
||||||
|
export async function deleteModel(id: number) {
|
||||||
|
return requestClient.delete(`/bpm/model/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 部署流程模型 */
|
||||||
|
export async function deployModel(id: number) {
|
||||||
|
return requestClient.post(`/bpm/model/deploy?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 清理流程模型 */
|
||||||
|
export async function cleanModel(id: number) {
|
||||||
|
return requestClient.delete(`/bpm/model/clean?id=${id}`);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import type { CrmPermissionApi } from '#/api/crm/permission';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmBusinessApi {
|
||||||
|
/** 商机产品信息 */
|
||||||
|
export interface BusinessProduct {
|
||||||
|
id: number;
|
||||||
|
productId: number;
|
||||||
|
productName: string;
|
||||||
|
productNo: string;
|
||||||
|
productUnit: number;
|
||||||
|
productPrice: number;
|
||||||
|
businessPrice: number;
|
||||||
|
count: number;
|
||||||
|
totalPrice: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 商机信息 */
|
||||||
|
export interface Business {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
customerId: number;
|
||||||
|
customerName?: string;
|
||||||
|
followUpStatus: boolean;
|
||||||
|
contactLastTime: Date;
|
||||||
|
contactNextTime: Date;
|
||||||
|
ownerUserId: number;
|
||||||
|
ownerUserName?: string; // 负责人的用户名称
|
||||||
|
ownerUserDept?: string; // 负责人的部门名称
|
||||||
|
statusTypeId: number;
|
||||||
|
statusTypeName?: string;
|
||||||
|
statusId: number;
|
||||||
|
statusName?: string;
|
||||||
|
endStatus: number;
|
||||||
|
endRemark: string;
|
||||||
|
dealTime: Date;
|
||||||
|
totalProductPrice: number;
|
||||||
|
totalPrice: number;
|
||||||
|
discountPercent: number;
|
||||||
|
remark: string;
|
||||||
|
creator: string; // 创建人
|
||||||
|
creatorName?: string; // 创建人名称
|
||||||
|
createTime: Date; // 创建时间
|
||||||
|
updateTime: Date; // 更新时间
|
||||||
|
products?: BusinessProduct[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询商机列表 */
|
||||||
|
export function getBusinessPage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmBusinessApi.Business>>(
|
||||||
|
'/crm/business/page',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询商机列表,基于指定客户 */
|
||||||
|
export function getBusinessPageByCustomer(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmBusinessApi.Business>>(
|
||||||
|
'/crm/business/page-by-customer',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询商机详情 */
|
||||||
|
export function getBusiness(id: number) {
|
||||||
|
return requestClient.get<CrmBusinessApi.Business>(
|
||||||
|
`/crm/business/get?id=${id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得商机列表(精简) */
|
||||||
|
export function getSimpleBusinessList() {
|
||||||
|
return requestClient.get<CrmBusinessApi.Business[]>(
|
||||||
|
'/crm/business/simple-all-list',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增商机 */
|
||||||
|
export function createBusiness(data: CrmBusinessApi.Business) {
|
||||||
|
return requestClient.post('/crm/business/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改商机 */
|
||||||
|
export function updateBusiness(data: CrmBusinessApi.Business) {
|
||||||
|
return requestClient.put('/crm/business/update', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改商机状态 */
|
||||||
|
export function updateBusinessStatus(data: CrmBusinessApi.Business) {
|
||||||
|
return requestClient.put('/crm/business/update-status', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除商机 */
|
||||||
|
export function deleteBusiness(id: number) {
|
||||||
|
return requestClient.delete(`/crm/business/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出商机 */
|
||||||
|
export function exportBusiness(params: any) {
|
||||||
|
return requestClient.download('/crm/business/export-excel', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 联系人关联商机列表 */
|
||||||
|
export function getBusinessPageByContact(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmBusinessApi.Business>>(
|
||||||
|
'/crm/business/page-by-contact',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 商机转移 */
|
||||||
|
export function transferBusiness(data: CrmPermissionApi.TransferReq) {
|
||||||
|
return requestClient.put('/crm/business/transfer', data);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmBusinessStatusApi {
|
||||||
|
/** 商机状态信息 */
|
||||||
|
export interface BusinessStatus {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
percent: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 商机状态组信息 */
|
||||||
|
export interface BusinessStatusType {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
deptIds: number[];
|
||||||
|
statuses?: BusinessStatus[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 默认商机状态 */
|
||||||
|
export const DEFAULT_STATUSES = [
|
||||||
|
{
|
||||||
|
endStatus: 1,
|
||||||
|
key: '结束',
|
||||||
|
name: '赢单',
|
||||||
|
percent: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endStatus: 2,
|
||||||
|
key: '结束',
|
||||||
|
name: '输单',
|
||||||
|
percent: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
endStatus: 3,
|
||||||
|
key: '结束',
|
||||||
|
name: '无效',
|
||||||
|
percent: 0,
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询商机状态组列表 */
|
||||||
|
export function getBusinessStatusPage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmBusinessStatusApi.BusinessStatusType>>(
|
||||||
|
'/crm/business-status/page',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增商机状态组 */
|
||||||
|
export function createBusinessStatus(
|
||||||
|
data: CrmBusinessStatusApi.BusinessStatusType,
|
||||||
|
) {
|
||||||
|
return requestClient.post('/crm/business-status/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改商机状态组 */
|
||||||
|
export function updateBusinessStatus(
|
||||||
|
data: CrmBusinessStatusApi.BusinessStatusType,
|
||||||
|
) {
|
||||||
|
return requestClient.put('/crm/business-status/update', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询商机状态类型详情 */
|
||||||
|
export function getBusinessStatus(id: number) {
|
||||||
|
return requestClient.get<CrmBusinessStatusApi.BusinessStatusType>(
|
||||||
|
`/crm/business-status/get?id=${id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除商机状态 */
|
||||||
|
export function deleteBusinessStatus(id: number) {
|
||||||
|
return requestClient.delete(`/crm/business-status/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得商机状态组列表 */
|
||||||
|
export function getBusinessStatusTypeSimpleList() {
|
||||||
|
return requestClient.get<CrmBusinessStatusApi.BusinessStatusType[]>(
|
||||||
|
'/crm/business-status/type-simple-list',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得商机阶段列表 */
|
||||||
|
export function getBusinessStatusSimpleList(typeId: number) {
|
||||||
|
return requestClient.get<CrmBusinessStatusApi.BusinessStatus[]>(
|
||||||
|
'/crm/business-status/status-simple-list',
|
||||||
|
{ params: { typeId } },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import type { CrmPermissionApi } from '#/api/crm/permission';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmClueApi {
|
||||||
|
/** 线索信息 */
|
||||||
|
export interface Clue {
|
||||||
|
id: number; // 编号
|
||||||
|
name: string; // 线索名称
|
||||||
|
followUpStatus: boolean; // 跟进状态
|
||||||
|
contactLastTime: Date; // 最后跟进时间
|
||||||
|
contactLastContent: string; // 最后跟进内容
|
||||||
|
contactNextTime: Date; // 下次联系时间
|
||||||
|
ownerUserId: number; // 负责人的用户编号
|
||||||
|
ownerUserName?: string; // 负责人的用户名称
|
||||||
|
ownerUserDept?: string; // 负责人的部门名称
|
||||||
|
transformStatus: boolean; // 转化状态
|
||||||
|
customerId: number; // 客户编号
|
||||||
|
customerName?: string; // 客户名称
|
||||||
|
mobile: string; // 手机号
|
||||||
|
telephone: string; // 电话
|
||||||
|
qq: string; // QQ
|
||||||
|
wechat: string; // wechat
|
||||||
|
email: string; // email
|
||||||
|
areaId: number; // 所在地
|
||||||
|
areaName?: string; // 所在地名称
|
||||||
|
detailAddress: string; // 详细地址
|
||||||
|
industryId: number; // 所属行业
|
||||||
|
level: number; // 客户等级
|
||||||
|
source: number; // 客户来源
|
||||||
|
remark: string; // 备注
|
||||||
|
creator: string; // 创建人
|
||||||
|
creatorName?: string; // 创建人名称
|
||||||
|
createTime: Date; // 创建时间
|
||||||
|
updateTime: Date; // 更新时间
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询线索列表 */
|
||||||
|
export function getCluePage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmClueApi.Clue>>('/crm/clue/page', {
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询线索详情 */
|
||||||
|
export function getClue(id: number) {
|
||||||
|
return requestClient.get<CrmClueApi.Clue>(`/crm/clue/get?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增线索 */
|
||||||
|
export function createClue(data: CrmClueApi.Clue) {
|
||||||
|
return requestClient.post('/crm/clue/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改线索 */
|
||||||
|
export function updateClue(data: CrmClueApi.Clue) {
|
||||||
|
return requestClient.put('/crm/clue/update', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除线索 */
|
||||||
|
export function deleteClue(id: number) {
|
||||||
|
return requestClient.delete(`/crm/clue/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出线索 */
|
||||||
|
export function exportClue(params: any) {
|
||||||
|
return requestClient.download('/crm/clue/export-excel', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 线索转移 */
|
||||||
|
export function transferClue(data: CrmPermissionApi.TransferReq) {
|
||||||
|
return requestClient.put('/crm/clue/transfer', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 线索转化为客户 */
|
||||||
|
export function transformClue(id: number) {
|
||||||
|
return requestClient.put('/crm/clue/transform', { id });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得分配给我的、待跟进的线索数量 */
|
||||||
|
export function getFollowClueCount() {
|
||||||
|
return requestClient.get<number>('/crm/clue/follow-count');
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,140 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import type { CrmPermissionApi } from '#/api/crm/permission';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmContactApi {
|
||||||
|
/** 联系人信息 */
|
||||||
|
export interface Contact {
|
||||||
|
id: number; // 编号
|
||||||
|
name: string; // 联系人名称
|
||||||
|
customerId: number; // 客户编号
|
||||||
|
customerName?: string; // 客户名称
|
||||||
|
contactLastTime: Date; // 最后跟进时间
|
||||||
|
contactLastContent: string; // 最后跟进内容
|
||||||
|
contactNextTime: Date; // 下次联系时间
|
||||||
|
ownerUserId: number; // 负责人的用户编号
|
||||||
|
ownerUserName?: string; // 负责人的用户名称
|
||||||
|
ownerUserDept?: string; // 负责人的部门名称
|
||||||
|
mobile: string; // 手机号
|
||||||
|
telephone: string; // 电话
|
||||||
|
qq: string; // QQ
|
||||||
|
wechat: string; // wechat
|
||||||
|
email: string; // email
|
||||||
|
areaId: number; // 所在地
|
||||||
|
areaName?: string; // 所在地名称
|
||||||
|
detailAddress: string; // 详细地址
|
||||||
|
sex: number; // 性别
|
||||||
|
master: boolean; // 是否主联系人
|
||||||
|
post: string; // 职务
|
||||||
|
parentId: number; // 上级联系人编号
|
||||||
|
parentName?: string; // 上级联系人名称
|
||||||
|
remark: string; // 备注
|
||||||
|
creator: string; // 创建人
|
||||||
|
creatorName?: string; // 创建人名称
|
||||||
|
createTime: Date; // 创建时间
|
||||||
|
updateTime: Date; // 更新时间
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 联系人商机关联请求 */
|
||||||
|
export interface ContactBusinessReq {
|
||||||
|
contactId: number;
|
||||||
|
businessIds: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 商机联系人关联请求 */
|
||||||
|
export interface BusinessContactReq {
|
||||||
|
businessId: number;
|
||||||
|
contactIds: number[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询联系人列表 */
|
||||||
|
export function getContactPage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmContactApi.Contact>>(
|
||||||
|
'/crm/contact/page',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询联系人列表,基于指定客户 */
|
||||||
|
export function getContactPageByCustomer(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmContactApi.Contact>>(
|
||||||
|
'/crm/contact/page-by-customer',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询联系人列表,基于指定商机 */
|
||||||
|
export function getContactPageByBusiness(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmContactApi.Contact>>(
|
||||||
|
'/crm/contact/page-by-business',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询联系人详情 */
|
||||||
|
export function getContact(id: number) {
|
||||||
|
return requestClient.get<CrmContactApi.Contact>(`/crm/contact/get?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增联系人 */
|
||||||
|
export function createContact(data: CrmContactApi.Contact) {
|
||||||
|
return requestClient.post('/crm/contact/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改联系人 */
|
||||||
|
export function updateContact(data: CrmContactApi.Contact) {
|
||||||
|
return requestClient.put('/crm/contact/update', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除联系人 */
|
||||||
|
export function deleteContact(id: number) {
|
||||||
|
return requestClient.delete(`/crm/contact/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出联系人 */
|
||||||
|
export function exportContact(params: any) {
|
||||||
|
return requestClient.download('/crm/contact/export-excel', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得联系人列表(精简) */
|
||||||
|
export function getSimpleContactList() {
|
||||||
|
return requestClient.get<CrmContactApi.Contact[]>(
|
||||||
|
'/crm/contact/simple-all-list',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 批量新增联系人商机关联 */
|
||||||
|
export function createContactBusinessList(
|
||||||
|
data: CrmContactApi.ContactBusinessReq,
|
||||||
|
) {
|
||||||
|
return requestClient.post('/crm/contact/create-business-list', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 批量新增商机联系人关联 */
|
||||||
|
export function createBusinessContactList(
|
||||||
|
data: CrmContactApi.BusinessContactReq,
|
||||||
|
) {
|
||||||
|
return requestClient.post('/crm/contact/create-business-list2', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 解除联系人商机关联 */
|
||||||
|
export function deleteContactBusinessList(
|
||||||
|
data: CrmContactApi.ContactBusinessReq,
|
||||||
|
) {
|
||||||
|
return requestClient.delete('/crm/contact/delete-business-list', { data });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 解除商机联系人关联 */
|
||||||
|
export function deleteBusinessContactList(
|
||||||
|
data: CrmContactApi.BusinessContactReq,
|
||||||
|
) {
|
||||||
|
return requestClient.delete('/crm/contact/delete-business-list2', { data });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 联系人转移 */
|
||||||
|
export function transferContact(data: CrmPermissionApi.TransferReq) {
|
||||||
|
return requestClient.put('/crm/contact/transfer', data);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmContractConfigApi {
|
||||||
|
/** 合同配置信息 */
|
||||||
|
export interface Config {
|
||||||
|
notifyEnabled?: boolean;
|
||||||
|
notifyDays?: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取合同配置 */
|
||||||
|
export function getContractConfig() {
|
||||||
|
return requestClient.get<CrmContractConfigApi.Config>(
|
||||||
|
'/crm/contract-config/get',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新合同配置 */
|
||||||
|
export function saveContractConfig(data: CrmContractConfigApi.Config) {
|
||||||
|
return requestClient.put('/crm/contract-config/save', data);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import type { CrmPermissionApi } from '#/api/crm/permission';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmContractApi {
|
||||||
|
/** 合同产品信息 */
|
||||||
|
export interface ContractProduct {
|
||||||
|
id: number;
|
||||||
|
productId: number;
|
||||||
|
productName: string;
|
||||||
|
productNo: string;
|
||||||
|
productUnit: number;
|
||||||
|
productPrice: number;
|
||||||
|
contractPrice: number;
|
||||||
|
count: number;
|
||||||
|
totalPrice: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 合同信息 */
|
||||||
|
export interface Contract {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
no: string;
|
||||||
|
customerId: number;
|
||||||
|
customerName?: string;
|
||||||
|
businessId: number;
|
||||||
|
businessName: string;
|
||||||
|
contactLastTime: Date;
|
||||||
|
ownerUserId: number;
|
||||||
|
ownerUserName?: string;
|
||||||
|
ownerUserDeptName?: string;
|
||||||
|
processInstanceId: number;
|
||||||
|
auditStatus: number;
|
||||||
|
orderDate: Date;
|
||||||
|
startTime: Date;
|
||||||
|
endTime: Date;
|
||||||
|
totalProductPrice: number;
|
||||||
|
discountPercent: number;
|
||||||
|
totalPrice: number;
|
||||||
|
totalReceivablePrice: number;
|
||||||
|
signContactId: number;
|
||||||
|
signContactName?: string;
|
||||||
|
signUserId: number;
|
||||||
|
signUserName: string;
|
||||||
|
remark: string;
|
||||||
|
createTime?: Date;
|
||||||
|
creator: string;
|
||||||
|
creatorName: string;
|
||||||
|
updateTime?: Date;
|
||||||
|
products?: ContractProduct[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询合同列表 */
|
||||||
|
export function getContractPage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmContractApi.Contract>>(
|
||||||
|
'/crm/contract/page',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询合同列表,基于指定客户 */
|
||||||
|
export function getContractPageByCustomer(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmContractApi.Contract>>(
|
||||||
|
'/crm/contract/page-by-customer',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询合同列表,基于指定商机 */
|
||||||
|
export function getContractPageByBusiness(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmContractApi.Contract>>(
|
||||||
|
'/crm/contract/page-by-business',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询合同详情 */
|
||||||
|
export function getContract(id: number) {
|
||||||
|
return requestClient.get<CrmContractApi.Contract>(
|
||||||
|
`/crm/contract/get?id=${id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询合同下拉列表 */
|
||||||
|
export function getContractSimpleList(customerId: number) {
|
||||||
|
return requestClient.get<CrmContractApi.Contract[]>(
|
||||||
|
`/crm/contract/simple-list?customerId=${customerId}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增合同 */
|
||||||
|
export function createContract(data: CrmContractApi.Contract) {
|
||||||
|
return requestClient.post('/crm/contract/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改合同 */
|
||||||
|
export function updateContract(data: CrmContractApi.Contract) {
|
||||||
|
return requestClient.put('/crm/contract/update', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除合同 */
|
||||||
|
export function deleteContract(id: number) {
|
||||||
|
return requestClient.delete(`/crm/contract/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出合同 */
|
||||||
|
export function exportContract(params: any) {
|
||||||
|
return requestClient.download('/crm/contract/export-excel', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 提交审核 */
|
||||||
|
export function submitContract(id: number) {
|
||||||
|
return requestClient.put(`/crm/contract/submit?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 合同转移 */
|
||||||
|
export function transferContract(data: CrmPermissionApi.TransferReq) {
|
||||||
|
return requestClient.put('/crm/contract/transfer', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得待审核合同数量 */
|
||||||
|
export function getAuditContractCount() {
|
||||||
|
return requestClient.get<number>('/crm/contract/audit-count');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得即将到期(提醒)的合同数量 */
|
||||||
|
export function getRemindContractCount() {
|
||||||
|
return requestClient.get<number>('/crm/contract/remind-count');
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,146 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import type { CrmPermissionApi } from '#/api/crm/permission';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmCustomerApi {
|
||||||
|
/** 客户信息 */
|
||||||
|
export interface Customer {
|
||||||
|
id: number; // 编号
|
||||||
|
name: string; // 客户名称
|
||||||
|
followUpStatus: boolean; // 跟进状态
|
||||||
|
contactLastTime: Date; // 最后跟进时间
|
||||||
|
contactLastContent: string; // 最后跟进内容
|
||||||
|
contactNextTime: Date; // 下次联系时间
|
||||||
|
ownerUserId: number; // 负责人的用户编号
|
||||||
|
ownerUserName?: string; // 负责人的用户名称
|
||||||
|
ownerUserDept?: string; // 负责人的部门名称
|
||||||
|
lockStatus?: boolean;
|
||||||
|
dealStatus?: boolean;
|
||||||
|
mobile: string; // 手机号
|
||||||
|
telephone: string; // 电话
|
||||||
|
qq: string; // QQ
|
||||||
|
wechat: string; // wechat
|
||||||
|
email: string; // email
|
||||||
|
areaId: number; // 所在地
|
||||||
|
areaName?: string; // 所在地名称
|
||||||
|
detailAddress: string; // 详细地址
|
||||||
|
industryId: number; // 所属行业
|
||||||
|
level: number; // 客户等级
|
||||||
|
source: number; // 客户来源
|
||||||
|
remark: string; // 备注
|
||||||
|
creator: string; // 创建人
|
||||||
|
creatorName?: string; // 创建人名称
|
||||||
|
createTime: Date; // 创建时间
|
||||||
|
updateTime: Date; // 更新时间
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询客户列表 */
|
||||||
|
export function getCustomerPage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmCustomerApi.Customer>>(
|
||||||
|
'/crm/customer/page',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询客户详情 */
|
||||||
|
export function getCustomer(id: number) {
|
||||||
|
return requestClient.get<CrmCustomerApi.Customer>(
|
||||||
|
`/crm/customer/get?id=${id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增客户 */
|
||||||
|
export function createCustomer(data: CrmCustomerApi.Customer) {
|
||||||
|
return requestClient.post('/crm/customer/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改客户 */
|
||||||
|
export function updateCustomer(data: CrmCustomerApi.Customer) {
|
||||||
|
return requestClient.put('/crm/customer/update', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除客户 */
|
||||||
|
export function deleteCustomer(id: number) {
|
||||||
|
return requestClient.delete(`/crm/customer/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出客户 */
|
||||||
|
export function exportCustomer(params: any) {
|
||||||
|
return requestClient.download('/crm/customer/export-excel', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 下载客户导入模板 */
|
||||||
|
export function importCustomerTemplate() {
|
||||||
|
return requestClient.download('/crm/customer/get-import-template');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导入客户 */
|
||||||
|
export function importCustomer(file: File) {
|
||||||
|
return requestClient.upload('/crm/customer/import', { file });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取客户精简信息列表 */
|
||||||
|
export function getCustomerSimpleList() {
|
||||||
|
return requestClient.get<CrmCustomerApi.Customer[]>(
|
||||||
|
'/crm/customer/simple-list',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户转移 */
|
||||||
|
export function transferCustomer(data: CrmPermissionApi.TransferReq) {
|
||||||
|
return requestClient.put('/crm/customer/transfer', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 锁定/解锁客户 */
|
||||||
|
export function lockCustomer(id: number, lockStatus: boolean) {
|
||||||
|
return requestClient.put('/crm/customer/lock', { id, lockStatus });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 领取公海客户 */
|
||||||
|
export function receiveCustomer(ids: number[]) {
|
||||||
|
return requestClient.put('/crm/customer/receive', { ids: ids.join(',') });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 分配公海给对应负责人 */
|
||||||
|
export function distributeCustomer(ids: number[], ownerUserId: number) {
|
||||||
|
return requestClient.put('/crm/customer/distribute', { ids, ownerUserId });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户放入公海 */
|
||||||
|
export function putCustomerPool(id: number) {
|
||||||
|
return requestClient.put(`/crm/customer/put-pool?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新客户的成交状态 */
|
||||||
|
export function updateCustomerDealStatus(id: number, dealStatus: boolean) {
|
||||||
|
return requestClient.put('/crm/customer/update-deal-status', {
|
||||||
|
id,
|
||||||
|
dealStatus,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 进入公海客户提醒的客户列表 */
|
||||||
|
export function getPutPoolRemindCustomerPage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmCustomerApi.Customer>>(
|
||||||
|
'/crm/customer/put-pool-remind-page',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得待进入公海客户数量 */
|
||||||
|
export function getPutPoolRemindCustomerCount() {
|
||||||
|
return requestClient.get<number>('/crm/customer/put-pool-remind-count');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得今日需联系客户数量 */
|
||||||
|
export function getTodayContactCustomerCount() {
|
||||||
|
return requestClient.get<number>('/crm/customer/today-contact-count');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得分配给我、待跟进的线索数量的客户数量 */
|
||||||
|
export function getFollowCustomerCount() {
|
||||||
|
return requestClient.get<number>('/crm/customer/follow-count');
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmCustomerLimitConfigApi {
|
||||||
|
/** 客户限制配置 */
|
||||||
|
export interface CustomerLimitConfig {
|
||||||
|
id?: number;
|
||||||
|
type?: number;
|
||||||
|
userIds?: string;
|
||||||
|
deptIds?: string;
|
||||||
|
maxCount?: number;
|
||||||
|
dealCountEnabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户限制配置类型
|
||||||
|
*/
|
||||||
|
export enum LimitConfType {
|
||||||
|
/** 锁定客户数限制 */
|
||||||
|
CUSTOMER_LOCK_LIMIT = 2,
|
||||||
|
/** 拥有客户数限制 */
|
||||||
|
CUSTOMER_QUANTITY_LIMIT = 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询客户限制配置列表 */
|
||||||
|
export function getCustomerLimitConfigPage(params: PageParam) {
|
||||||
|
return requestClient.get<
|
||||||
|
PageResult<CrmCustomerLimitConfigApi.CustomerLimitConfig>
|
||||||
|
>('/crm/customer-limit-config/page', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询客户限制配置详情 */
|
||||||
|
export function getCustomerLimitConfig(id: number) {
|
||||||
|
return requestClient.get<CrmCustomerLimitConfigApi.CustomerLimitConfig>(
|
||||||
|
`/crm/customer-limit-config/get?id=${id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增客户限制配置 */
|
||||||
|
export function createCustomerLimitConfig(
|
||||||
|
data: CrmCustomerLimitConfigApi.CustomerLimitConfig,
|
||||||
|
) {
|
||||||
|
return requestClient.post('/crm/customer-limit-config/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改客户限制配置 */
|
||||||
|
export function updateCustomerLimitConfig(
|
||||||
|
data: CrmCustomerLimitConfigApi.CustomerLimitConfig,
|
||||||
|
) {
|
||||||
|
return requestClient.put('/crm/customer-limit-config/update', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除客户限制配置 */
|
||||||
|
export function deleteCustomerLimitConfig(id: number) {
|
||||||
|
return requestClient.delete(`/crm/customer-limit-config/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmCustomerPoolConfigApi {
|
||||||
|
/** 客户公海规则设置 */
|
||||||
|
export interface CustomerPoolConfig {
|
||||||
|
enabled?: boolean;
|
||||||
|
contactExpireDays?: number;
|
||||||
|
dealExpireDays?: number;
|
||||||
|
notifyEnabled?: boolean;
|
||||||
|
notifyDays?: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取客户公海规则设置 */
|
||||||
|
export function getCustomerPoolConfig() {
|
||||||
|
return requestClient.get<CrmCustomerPoolConfigApi.CustomerPoolConfig>(
|
||||||
|
'/crm/customer-pool-config/get',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新客户公海规则设置 */
|
||||||
|
export function saveCustomerPoolConfig(
|
||||||
|
data: CrmCustomerPoolConfigApi.CustomerPoolConfig,
|
||||||
|
) {
|
||||||
|
return requestClient.put('/crm/customer-pool-config/save', data);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmFollowUpApi {
|
||||||
|
/** 关联商机信息 */
|
||||||
|
export interface Business {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 关联联系人信息 */
|
||||||
|
export interface Contact {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 跟进记录信息 */
|
||||||
|
export interface FollowUpRecord {
|
||||||
|
id: number; // 编号
|
||||||
|
bizType: number; // 数据类型
|
||||||
|
bizId: number; // 数据编号
|
||||||
|
type: number; // 跟进类型
|
||||||
|
content: string; // 跟进内容
|
||||||
|
picUrls: string[]; // 图片
|
||||||
|
fileUrls: string[]; // 附件
|
||||||
|
nextTime: Date; // 下次联系时间
|
||||||
|
businessIds: number[]; // 关联的商机编号数组
|
||||||
|
businesses: Business[]; // 关联的商机数组
|
||||||
|
contactIds: number[]; // 关联的联系人编号数组
|
||||||
|
contacts: Contact[]; // 关联的联系人数组
|
||||||
|
creator: string;
|
||||||
|
creatorName?: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询跟进记录分页 */
|
||||||
|
export function getFollowUpRecordPage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmFollowUpApi.FollowUpRecord>>(
|
||||||
|
'/crm/follow-up-record/page',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增跟进记录 */
|
||||||
|
export function createFollowUpRecord(data: CrmFollowUpApi.FollowUpRecord) {
|
||||||
|
return requestClient.post('/crm/follow-up-record/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除跟进记录 */
|
||||||
|
export function deleteFollowUpRecord(id: number) {
|
||||||
|
return requestClient.delete(`/crm/follow-up-record/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmOperateLogApi {
|
||||||
|
/** 操作日志查询参数 */
|
||||||
|
export interface OperateLogQuery extends PageParam {
|
||||||
|
bizType: number;
|
||||||
|
bizId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 操作日志信息 */
|
||||||
|
export interface OperateLog {
|
||||||
|
id: number;
|
||||||
|
bizType: number;
|
||||||
|
bizId: number;
|
||||||
|
type: number;
|
||||||
|
content: string;
|
||||||
|
creator: string;
|
||||||
|
creatorName?: string;
|
||||||
|
createTime: Date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得操作日志 */
|
||||||
|
export function getOperateLogPage(params: CrmOperateLogApi.OperateLogQuery) {
|
||||||
|
return requestClient.get<PageResult<CrmOperateLogApi.OperateLog>>(
|
||||||
|
'/crm/operate-log/page',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmPermissionApi {
|
||||||
|
/** 数据权限信息 */
|
||||||
|
export interface Permission {
|
||||||
|
id?: number; // 数据权限编号
|
||||||
|
userId: number; // 用户编号
|
||||||
|
bizType: number; // Crm 类型
|
||||||
|
bizId: number; // Crm 类型数据编号
|
||||||
|
level: number; // 权限级别
|
||||||
|
toBizTypes?: number[]; // 同时添加至
|
||||||
|
deptName?: string; // 部门名称
|
||||||
|
nickname?: string; // 用户昵称
|
||||||
|
postNames?: string[]; // 岗位名称数组
|
||||||
|
createTime?: Date;
|
||||||
|
ids?: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 数据权限转移请求 */
|
||||||
|
export interface TransferReq {
|
||||||
|
id: number; // 模块编号
|
||||||
|
newOwnerUserId: number; // 新负责人的用户编号
|
||||||
|
oldOwnerPermissionLevel?: number; // 老负责人加入团队后的权限级别
|
||||||
|
toBizTypes?: number[]; // 转移客户时,需要额外有【联系人】【商机】【合同】的 checkbox 选择
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRM 业务类型枚举
|
||||||
|
*/
|
||||||
|
export enum BizType {
|
||||||
|
CRM_BUSINESS = 4, // 商机
|
||||||
|
CRM_CLUE = 1, // 线索
|
||||||
|
CRM_CONTACT = 3, // 联系人
|
||||||
|
CRM_CONTRACT = 5, // 合同
|
||||||
|
CRM_CUSTOMER = 2, // 客户
|
||||||
|
CRM_PRODUCT = 6, // 产品
|
||||||
|
CRM_RECEIVABLE = 7, // 回款
|
||||||
|
CRM_RECEIVABLE_PLAN = 8, // 回款计划
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRM 数据权限级别枚举
|
||||||
|
*/
|
||||||
|
export enum PermissionLevel {
|
||||||
|
OWNER = 1, // 负责人
|
||||||
|
READ = 2, // 只读
|
||||||
|
WRITE = 3, // 读写
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得数据权限列表(查询团队成员列表) */
|
||||||
|
export function getPermissionList(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmPermissionApi.Permission>>(
|
||||||
|
'/crm/permission/list',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 创建数据权限(新增团队成员) */
|
||||||
|
export function createPermission(data: CrmPermissionApi.Permission) {
|
||||||
|
return requestClient.post('/crm/permission/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 编辑数据权限(修改团队成员权限级别) */
|
||||||
|
export function updatePermission(data: CrmPermissionApi.Permission) {
|
||||||
|
return requestClient.put('/crm/permission/update', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除数据权限(删除团队成员) */
|
||||||
|
export function deletePermissionBatch(ids: number[]) {
|
||||||
|
return requestClient.delete(`/crm/permission/delete?ids=${ids.join(',')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除自己的数据权限(退出团队) */
|
||||||
|
export function deleteSelfPermission(id: number) {
|
||||||
|
return requestClient.delete(`/crm/permission/delete-self?id=${id}`);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
import type { PageParam } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmProductCategoryApi {
|
||||||
|
/** 产品分类信息 */
|
||||||
|
export interface ProductCategory {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
parentId: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询产品分类详情 */
|
||||||
|
export function getProductCategory(id: number) {
|
||||||
|
return requestClient.get<CrmProductCategoryApi.ProductCategory>(
|
||||||
|
`/crm/product-category/get?id=${id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增产品分类 */
|
||||||
|
export function createProductCategory(
|
||||||
|
data: CrmProductCategoryApi.ProductCategory,
|
||||||
|
) {
|
||||||
|
return requestClient.post('/crm/product-category/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改产品分类 */
|
||||||
|
export function updateProductCategory(
|
||||||
|
data: CrmProductCategoryApi.ProductCategory,
|
||||||
|
) {
|
||||||
|
return requestClient.put('/crm/product-category/update', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除产品分类 */
|
||||||
|
export function deleteProductCategory(id: number) {
|
||||||
|
return requestClient.delete(`/crm/product-category/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 产品分类列表 */
|
||||||
|
export function getProductCategoryList(params?: PageParam) {
|
||||||
|
return requestClient.get<CrmProductCategoryApi.ProductCategory[]>(
|
||||||
|
'/crm/product-category/list',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmProductApi {
|
||||||
|
/** 产品信息 */
|
||||||
|
export interface Product {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
no: string;
|
||||||
|
unit: number;
|
||||||
|
price: number;
|
||||||
|
status: number;
|
||||||
|
categoryId: number;
|
||||||
|
categoryName?: string;
|
||||||
|
description: string;
|
||||||
|
ownerUserId: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询产品列表 */
|
||||||
|
export function getProductPage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmProductApi.Product>>(
|
||||||
|
'/crm/product/page',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得产品精简列表 */
|
||||||
|
export function getProductSimpleList() {
|
||||||
|
return requestClient.get<CrmProductApi.Product[]>('/crm/product/simple-list');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询产品详情 */
|
||||||
|
export function getProduct(id: number) {
|
||||||
|
return requestClient.get<CrmProductApi.Product>(`/crm/product/get?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增产品 */
|
||||||
|
export function createProduct(data: CrmProductApi.Product) {
|
||||||
|
return requestClient.post('/crm/product/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改产品 */
|
||||||
|
export function updateProduct(data: CrmProductApi.Product) {
|
||||||
|
return requestClient.put('/crm/product/update', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除产品 */
|
||||||
|
export function deleteProduct(id: number) {
|
||||||
|
return requestClient.delete(`/crm/product/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出产品 */
|
||||||
|
export function exportProduct(params: any) {
|
||||||
|
return requestClient.download('/crm/product/export-excel', params);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmReceivableApi {
|
||||||
|
/** 合同信息 */
|
||||||
|
export interface Contract {
|
||||||
|
id?: number;
|
||||||
|
name?: string;
|
||||||
|
no: string;
|
||||||
|
totalPrice: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 回款信息 */
|
||||||
|
export interface Receivable {
|
||||||
|
id: number;
|
||||||
|
no: string;
|
||||||
|
planId?: number;
|
||||||
|
period?: number;
|
||||||
|
customerId?: number;
|
||||||
|
customerName?: string;
|
||||||
|
contractId?: number;
|
||||||
|
contract?: Contract;
|
||||||
|
auditStatus: number;
|
||||||
|
processInstanceId: number;
|
||||||
|
returnTime: Date;
|
||||||
|
returnType: number;
|
||||||
|
price: number;
|
||||||
|
ownerUserId: number;
|
||||||
|
ownerUserName?: string;
|
||||||
|
remark: string;
|
||||||
|
creator: string; // 创建人
|
||||||
|
creatorName?: string; // 创建人名称
|
||||||
|
createTime: Date; // 创建时间
|
||||||
|
updateTime: Date; // 更新时间
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询回款列表 */
|
||||||
|
export function getReceivablePage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmReceivableApi.Receivable>>(
|
||||||
|
'/crm/receivable/page',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询回款列表,基于指定客户 */
|
||||||
|
export function getReceivablePageByCustomer(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmReceivableApi.Receivable>>(
|
||||||
|
'/crm/receivable/page-by-customer',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询回款详情 */
|
||||||
|
export function getReceivable(id: number) {
|
||||||
|
return requestClient.get<CrmReceivableApi.Receivable>(
|
||||||
|
`/crm/receivable/get?id=${id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增回款 */
|
||||||
|
export function createReceivable(data: CrmReceivableApi.Receivable) {
|
||||||
|
return requestClient.post('/crm/receivable/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改回款 */
|
||||||
|
export function updateReceivable(data: CrmReceivableApi.Receivable) {
|
||||||
|
return requestClient.put('/crm/receivable/update', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除回款 */
|
||||||
|
export function deleteReceivable(id: number) {
|
||||||
|
return requestClient.delete(`/crm/receivable/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出回款 */
|
||||||
|
export function exportReceivable(params: any) {
|
||||||
|
return requestClient.download('/crm/receivable/export-excel', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 提交审核 */
|
||||||
|
export function submitReceivable(id: number) {
|
||||||
|
return requestClient.put(`/crm/receivable/submit?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得待审核回款数量 */
|
||||||
|
export function getAuditReceivableCount() {
|
||||||
|
return requestClient.get<number>('/crm/receivable/audit-count');
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmReceivablePlanApi {
|
||||||
|
/** 回款计划信息 */
|
||||||
|
export interface Plan {
|
||||||
|
id: number;
|
||||||
|
period: number;
|
||||||
|
receivableId: number;
|
||||||
|
price: number;
|
||||||
|
returnTime: Date;
|
||||||
|
remindDays: number;
|
||||||
|
returnType: number;
|
||||||
|
remindTime: Date;
|
||||||
|
customerId: number;
|
||||||
|
customerName?: string;
|
||||||
|
contractId?: number;
|
||||||
|
contractNo?: string;
|
||||||
|
ownerUserId: number;
|
||||||
|
ownerUserName?: string;
|
||||||
|
remark: string;
|
||||||
|
creator: string;
|
||||||
|
creatorName?: string;
|
||||||
|
createTime: Date;
|
||||||
|
updateTime: Date;
|
||||||
|
receivable?: {
|
||||||
|
price: number;
|
||||||
|
returnTime: Date;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询回款计划列表 */
|
||||||
|
export function getReceivablePlanPage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmReceivablePlanApi.Plan>>(
|
||||||
|
'/crm/receivable-plan/page',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询回款计划列表(按客户) */
|
||||||
|
export function getReceivablePlanPageByCustomer(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<CrmReceivablePlanApi.Plan>>(
|
||||||
|
'/crm/receivable-plan/page-by-customer',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询回款计划详情 */
|
||||||
|
export function getReceivablePlan(id: number) {
|
||||||
|
return requestClient.get<CrmReceivablePlanApi.Plan>(
|
||||||
|
'/crm/receivable-plan/get',
|
||||||
|
{ params: { id } },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询回款计划下拉数据 */
|
||||||
|
export function getReceivablePlanSimpleList(
|
||||||
|
customerId: number,
|
||||||
|
contractId: number,
|
||||||
|
) {
|
||||||
|
return requestClient.get<CrmReceivablePlanApi.Plan[]>(
|
||||||
|
'/crm/receivable-plan/simple-list',
|
||||||
|
{
|
||||||
|
params: { customerId, contractId },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增回款计划 */
|
||||||
|
export function createReceivablePlan(data: CrmReceivablePlanApi.Plan) {
|
||||||
|
return requestClient.post('/crm/receivable-plan/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改回款计划 */
|
||||||
|
export function updateReceivablePlan(data: CrmReceivablePlanApi.Plan) {
|
||||||
|
return requestClient.put('/crm/receivable-plan/update', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除回款计划 */
|
||||||
|
export function deleteReceivablePlan(id: number) {
|
||||||
|
return requestClient.delete('/crm/receivable-plan/delete', {
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出回款计划 Excel */
|
||||||
|
export function exportReceivablePlan(params: PageParam) {
|
||||||
|
return requestClient.download('/crm/receivable-plan/export-excel', {
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得待回款提醒数量 */
|
||||||
|
export function getReceivablePlanRemindCount() {
|
||||||
|
return requestClient.get<number>('/crm/receivable-plan/remind-count');
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,191 @@
|
||||||
|
import type { PageParam } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmStatisticsCustomerApi {
|
||||||
|
/** 客户总量分析(按日期) */
|
||||||
|
export interface CustomerSummaryByDate {
|
||||||
|
time: string;
|
||||||
|
customerCreateCount: number;
|
||||||
|
customerDealCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户总量分析(按用户) */
|
||||||
|
export interface CustomerSummaryByUser {
|
||||||
|
ownerUserName: string;
|
||||||
|
customerCreateCount: number;
|
||||||
|
customerDealCount: number;
|
||||||
|
contractPrice: number;
|
||||||
|
receivablePrice: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户跟进次数分析(按日期) */
|
||||||
|
export interface FollowUpSummaryByDate {
|
||||||
|
time: string;
|
||||||
|
followUpRecordCount: number;
|
||||||
|
followUpCustomerCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户跟进次数分析(按用户) */
|
||||||
|
export interface FollowUpSummaryByUser {
|
||||||
|
ownerUserName: string;
|
||||||
|
followupRecordCount: number;
|
||||||
|
followupCustomerCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户跟进方式统计 */
|
||||||
|
export interface FollowUpSummaryByType {
|
||||||
|
followUpType: string;
|
||||||
|
followUpRecordCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 合同摘要信息 */
|
||||||
|
export interface CustomerContractSummary {
|
||||||
|
customerName: string;
|
||||||
|
contractName: string;
|
||||||
|
totalPrice: number;
|
||||||
|
receivablePrice: number;
|
||||||
|
customerType: string;
|
||||||
|
customerSource: string;
|
||||||
|
ownerUserName: string;
|
||||||
|
creatorUserName: string;
|
||||||
|
createTime: Date;
|
||||||
|
orderDate: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户公海分析(按日期) */
|
||||||
|
export interface PoolSummaryByDate {
|
||||||
|
time: string;
|
||||||
|
customerPutCount: number;
|
||||||
|
customerTakeCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户公海分析(按用户) */
|
||||||
|
export interface PoolSummaryByUser {
|
||||||
|
ownerUserName: string;
|
||||||
|
customerPutCount: number;
|
||||||
|
customerTakeCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户成交周期(按日期) */
|
||||||
|
export interface CustomerDealCycleByDate {
|
||||||
|
time: string;
|
||||||
|
customerDealCycle: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户成交周期(按用户) */
|
||||||
|
export interface CustomerDealCycleByUser {
|
||||||
|
ownerUserName: string;
|
||||||
|
customerDealCycle: number;
|
||||||
|
customerDealCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户成交周期(按地区) */
|
||||||
|
export interface CustomerDealCycleByArea {
|
||||||
|
areaName: string;
|
||||||
|
customerDealCycle: number;
|
||||||
|
customerDealCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户成交周期(按产品) */
|
||||||
|
export interface CustomerDealCycleByProduct {
|
||||||
|
productName: string;
|
||||||
|
customerDealCycle: number;
|
||||||
|
customerDealCount: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户总量分析(按日期) */
|
||||||
|
export function getCustomerSummaryByDate(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsCustomerApi.CustomerSummaryByDate[]>(
|
||||||
|
'/crm/statistics-customer/get-customer-summary-by-date',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户总量分析(按用户) */
|
||||||
|
export function getCustomerSummaryByUser(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsCustomerApi.CustomerSummaryByUser[]>(
|
||||||
|
'/crm/statistics-customer/get-customer-summary-by-user',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户跟进次数分析(按日期) */
|
||||||
|
export function getFollowUpSummaryByDate(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsCustomerApi.FollowUpSummaryByDate[]>(
|
||||||
|
'/crm/statistics-customer/get-follow-up-summary-by-date',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户跟进次数分析(按用户) */
|
||||||
|
export function getFollowUpSummaryByUser(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsCustomerApi.FollowUpSummaryByUser[]>(
|
||||||
|
'/crm/statistics-customer/get-follow-up-summary-by-user',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取客户跟进方式统计数 */
|
||||||
|
export function getFollowUpSummaryByType(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsCustomerApi.FollowUpSummaryByType[]>(
|
||||||
|
'/crm/statistics-customer/get-follow-up-summary-by-type',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 合同摘要信息(客户转化率页面) */
|
||||||
|
export function getContractSummary(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsCustomerApi.CustomerContractSummary[]>(
|
||||||
|
'/crm/statistics-customer/get-contract-summary',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取客户公海分析(按日期) */
|
||||||
|
export function getPoolSummaryByDate(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsCustomerApi.PoolSummaryByDate[]>(
|
||||||
|
'/crm/statistics-customer/get-pool-summary-by-date',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取客户公海分析(按用户) */
|
||||||
|
export function getPoolSummaryByUser(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsCustomerApi.PoolSummaryByUser[]>(
|
||||||
|
'/crm/statistics-customer/get-pool-summary-by-user',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取客户成交周期(按日期) */
|
||||||
|
export function getCustomerDealCycleByDate(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsCustomerApi.CustomerDealCycleByDate[]>(
|
||||||
|
'/crm/statistics-customer/get-customer-deal-cycle-by-date',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取客户成交周期(按用户) */
|
||||||
|
export function getCustomerDealCycleByUser(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsCustomerApi.CustomerDealCycleByUser[]>(
|
||||||
|
'/crm/statistics-customer/get-customer-deal-cycle-by-user',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取客户成交周期(按地区) */
|
||||||
|
export function getCustomerDealCycleByArea(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsCustomerApi.CustomerDealCycleByArea[]>(
|
||||||
|
'/crm/statistics-customer/get-customer-deal-cycle-by-area',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取客户成交周期(按产品) */
|
||||||
|
export function getCustomerDealCycleByProduct(params: PageParam) {
|
||||||
|
return requestClient.get<
|
||||||
|
CrmStatisticsCustomerApi.CustomerDealCycleByProduct[]
|
||||||
|
>('/crm/statistics-customer/get-customer-deal-cycle-by-product', { params });
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmStatisticsFunnelApi {
|
||||||
|
/** 销售漏斗统计数据 */
|
||||||
|
export interface FunnelSummary {
|
||||||
|
customerCount: number; // 客户数
|
||||||
|
businessCount: number; // 商机数
|
||||||
|
businessWinCount: number; // 赢单数
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 商机分析(按日期) */
|
||||||
|
export interface BusinessSummaryByDate {
|
||||||
|
time: string; // 时间
|
||||||
|
businessCreateCount: number; // 商机数
|
||||||
|
totalPrice: number | string; // 商机金额
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 商机转化率分析(按日期) */
|
||||||
|
export interface BusinessInversionRateSummaryByDate {
|
||||||
|
time: string; // 时间
|
||||||
|
businessCount: number; // 商机数量
|
||||||
|
businessWinCount: number; // 赢单商机数
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取销售漏斗统计数据 */
|
||||||
|
export function getFunnelSummary(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsFunnelApi.FunnelSummary>(
|
||||||
|
'/crm/statistics-funnel/get-funnel-summary',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取商机结束状态统计 */
|
||||||
|
export function getBusinessSummaryByEndStatus(params: PageParam) {
|
||||||
|
return requestClient.get<Record<string, number>>(
|
||||||
|
'/crm/statistics-funnel/get-business-summary-by-end-status',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取新增商机分析(按日期) */
|
||||||
|
export function getBusinessSummaryByDate(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsFunnelApi.BusinessSummaryByDate[]>(
|
||||||
|
'/crm/statistics-funnel/get-business-summary-by-date',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取商机转化率分析(按日期) */
|
||||||
|
export function getBusinessInversionRateSummaryByDate(params: PageParam) {
|
||||||
|
return requestClient.get<
|
||||||
|
CrmStatisticsFunnelApi.BusinessInversionRateSummaryByDate[]
|
||||||
|
>('/crm/statistics-funnel/get-business-inversion-rate-summary-by-date', {
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取商机列表(按日期) */
|
||||||
|
export function getBusinessPageByDate(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<any>>(
|
||||||
|
'/crm/statistics-funnel/get-business-page-by-date',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
import type { PageParam } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmStatisticsPerformanceApi {
|
||||||
|
/** 员工业绩统计 */
|
||||||
|
export interface Performance {
|
||||||
|
time: string;
|
||||||
|
currentMonthCount: number;
|
||||||
|
lastMonthCount: number;
|
||||||
|
lastYearCount: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 员工获得合同金额统计 */
|
||||||
|
export function getContractPricePerformance(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsPerformanceApi.Performance[]>(
|
||||||
|
'/crm/statistics-performance/get-contract-price-performance',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 员工获得回款统计 */
|
||||||
|
export function getReceivablePricePerformance(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsPerformanceApi.Performance[]>(
|
||||||
|
'/crm/statistics-performance/get-receivable-price-performance',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 员工获得签约合同数量统计 */
|
||||||
|
export function getContractCountPerformance(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsPerformanceApi.Performance[]>(
|
||||||
|
'/crm/statistics-performance/get-contract-count-performance',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
import type { PageParam } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmStatisticsPortraitApi {
|
||||||
|
/** 客户基础统计信息 */
|
||||||
|
export interface CustomerBase {
|
||||||
|
customerCount: number;
|
||||||
|
dealCount: number;
|
||||||
|
dealPortion: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户行业统计信息 */
|
||||||
|
export interface CustomerIndustry extends CustomerBase {
|
||||||
|
industryId: number;
|
||||||
|
industryPortion: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户来源统计信息 */
|
||||||
|
export interface CustomerSource extends CustomerBase {
|
||||||
|
source: number;
|
||||||
|
sourcePortion: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户级别统计信息 */
|
||||||
|
export interface CustomerLevel extends CustomerBase {
|
||||||
|
level: number;
|
||||||
|
levelPortion: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户地区统计信息 */
|
||||||
|
export interface CustomerArea extends CustomerBase {
|
||||||
|
areaId: number;
|
||||||
|
areaName: string;
|
||||||
|
areaPortion: number | string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取客户行业统计数据 */
|
||||||
|
export function getCustomerIndustry(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsPortraitApi.CustomerIndustry[]>(
|
||||||
|
'/crm/statistics-portrait/get-customer-industry-summary',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取客户来源统计数据 */
|
||||||
|
export function getCustomerSource(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsPortraitApi.CustomerSource[]>(
|
||||||
|
'/crm/statistics-portrait/get-customer-source-summary',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取客户级别统计数据 */
|
||||||
|
export function getCustomerLevel(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsPortraitApi.CustomerLevel[]>(
|
||||||
|
'/crm/statistics-portrait/get-customer-level-summary',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取客户地区统计数据 */
|
||||||
|
export function getCustomerArea(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsPortraitApi.CustomerArea[]>(
|
||||||
|
'/crm/statistics-portrait/get-customer-area-summary',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
import type { PageParam } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CrmStatisticsRankApi {
|
||||||
|
/** 排行统计数据 */
|
||||||
|
export interface Rank {
|
||||||
|
count: number;
|
||||||
|
nickname: string;
|
||||||
|
deptName: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得合同排行榜 */
|
||||||
|
export function getContractPriceRank(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsRankApi.Rank[]>(
|
||||||
|
'/crm/statistics-rank/get-contract-price-rank',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得回款排行榜 */
|
||||||
|
export function getReceivablePriceRank(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsRankApi.Rank[]>(
|
||||||
|
'/crm/statistics-rank/get-receivable-price-rank',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 签约合同排行 */
|
||||||
|
export function getContractCountRank(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsRankApi.Rank[]>(
|
||||||
|
'/crm/statistics-rank/get-contract-count-rank',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 产品销量排行 */
|
||||||
|
export function getProductSalesRank(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsRankApi.Rank[]>(
|
||||||
|
'/crm/statistics-rank/get-product-sales-rank',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增客户数排行 */
|
||||||
|
export function getCustomerCountRank(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsRankApi.Rank[]>(
|
||||||
|
'/crm/statistics-rank/get-customer-count-rank',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增联系人数排行 */
|
||||||
|
export function getContactsCountRank(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsRankApi.Rank[]>(
|
||||||
|
'/crm/statistics-rank/get-contacts-count-rank',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 跟进次数排行 */
|
||||||
|
export function getFollowCountRank(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsRankApi.Rank[]>(
|
||||||
|
'/crm/statistics-rank/get-follow-count-rank',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 跟进客户数排行 */
|
||||||
|
export function getFollowCustomerCountRank(params: PageParam) {
|
||||||
|
return requestClient.get<CrmStatisticsRankApi.Rank[]>(
|
||||||
|
'/crm/statistics-rank/get-follow-customer-count-rank',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace PayAppApi {
|
||||||
|
/** 支付应用信息 */
|
||||||
|
export interface App {
|
||||||
|
id?: number;
|
||||||
|
appKey: string;
|
||||||
|
name: string;
|
||||||
|
status: number;
|
||||||
|
remark: string;
|
||||||
|
payNotifyUrl: string;
|
||||||
|
refundNotifyUrl: string;
|
||||||
|
transferNotifyUrl: string;
|
||||||
|
merchantId: number;
|
||||||
|
merchantName: string;
|
||||||
|
createTime?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新状态请求 */
|
||||||
|
export interface UpdateStatusReq {
|
||||||
|
id: number;
|
||||||
|
status: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询支付应用列表 */
|
||||||
|
export function getAppPage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<PayAppApi.App>>('/pay/app/page', {
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询支付应用详情 */
|
||||||
|
export function getApp(id: number) {
|
||||||
|
return requestClient.get<PayAppApi.App>(`/pay/app/get?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增支付应用 */
|
||||||
|
export function createApp(data: PayAppApi.App) {
|
||||||
|
return requestClient.post('/pay/app/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改支付应用 */
|
||||||
|
export function updateApp(data: PayAppApi.App) {
|
||||||
|
return requestClient.put('/pay/app/update', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改支付应用状态 */
|
||||||
|
export function changeAppStatus(data: PayAppApi.UpdateStatusReq) {
|
||||||
|
return requestClient.put('/pay/app/update-status', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除支付应用 */
|
||||||
|
export function deleteApp(id: number) {
|
||||||
|
return requestClient.delete(`/pay/app/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取支付应用列表 */
|
||||||
|
export function getAppList() {
|
||||||
|
return requestClient.get<PayAppApi.App[]>('/pay/app/list');
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace PayChannelApi {
|
||||||
|
/** 支付渠道信息 */
|
||||||
|
export interface Channel {
|
||||||
|
id: number;
|
||||||
|
code: string;
|
||||||
|
config: string;
|
||||||
|
status: number;
|
||||||
|
remark: string;
|
||||||
|
feeRate: number;
|
||||||
|
appId: number;
|
||||||
|
createTime: Date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询支付渠道列表 */
|
||||||
|
export function getChannelPage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<PayChannelApi.Channel>>(
|
||||||
|
'/pay/channel/page',
|
||||||
|
{
|
||||||
|
params,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询支付渠道详情 */
|
||||||
|
export function getChannel(appId: string, code: string) {
|
||||||
|
return requestClient.get<PayChannelApi.Channel>('/pay/channel/get', {
|
||||||
|
params: { appId, code },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增支付渠道 */
|
||||||
|
export function createChannel(data: PayChannelApi.Channel) {
|
||||||
|
return requestClient.post('/pay/channel/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改支付渠道 */
|
||||||
|
export function updateChannel(data: PayChannelApi.Channel) {
|
||||||
|
return requestClient.put('/pay/channel/update', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除支付渠道 */
|
||||||
|
export function deleteChannel(id: number) {
|
||||||
|
return requestClient.delete(`/pay/channel/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出支付渠道 */
|
||||||
|
export function exportChannel(params: PageParam) {
|
||||||
|
return requestClient.download('/pay/channel/export-excel', { params });
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace PayDemoApi {
|
||||||
|
/** 示例订单信息 */
|
||||||
|
export interface DemoOrder {
|
||||||
|
spuId: number;
|
||||||
|
createTime: Date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 创建示例订单 */
|
||||||
|
export function createDemoOrder(data: PayDemoApi.DemoOrder) {
|
||||||
|
return requestClient.post('/pay/demo-order/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得示例订单 */
|
||||||
|
export function getDemoOrder(id: number) {
|
||||||
|
return requestClient.get<PayDemoApi.DemoOrder>(
|
||||||
|
`/pay/demo-order/get?id=${id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得示例订单分页 */
|
||||||
|
export function getDemoOrderPage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<PayDemoApi.DemoOrder>>(
|
||||||
|
'/pay/demo-order/page',
|
||||||
|
{
|
||||||
|
params,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 退款示例订单 */
|
||||||
|
export function refundDemoOrder(id: number) {
|
||||||
|
return requestClient.put(`/pay/demo-order/refund?id=${id}`);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace PayDemoTransferApi {
|
||||||
|
/** 示例转账单信息 */
|
||||||
|
export interface DemoTransfer {
|
||||||
|
price: number;
|
||||||
|
type: number;
|
||||||
|
userName: string;
|
||||||
|
alipayLogonId: string;
|
||||||
|
openid: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 创建示例转账单 */
|
||||||
|
export function createDemoTransfer(data: PayDemoTransferApi.DemoTransfer) {
|
||||||
|
return requestClient.post('/pay/demo-transfer/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得示例转账单分页 */
|
||||||
|
export function getDemoTransferPage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<PayDemoTransferApi.DemoTransfer>>(
|
||||||
|
'/pay/demo-transfer/page',
|
||||||
|
{
|
||||||
|
params,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import type { PageParam } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
/** 获得支付通知明细 */
|
||||||
|
export function getNotifyTaskDetail(id: number) {
|
||||||
|
return requestClient.get(`/pay/notify/get-detail?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得支付通知分页 */
|
||||||
|
export function getNotifyTaskPage(params: PageParam) {
|
||||||
|
return requestClient.get('/pay/notify/page', {
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace PayOrderApi {
|
||||||
|
/** 支付订单信息 */
|
||||||
|
export interface Order {
|
||||||
|
id: number;
|
||||||
|
merchantId: number;
|
||||||
|
appId: number;
|
||||||
|
channelId: number;
|
||||||
|
channelCode: string;
|
||||||
|
merchantOrderId: string;
|
||||||
|
subject: string;
|
||||||
|
body: string;
|
||||||
|
notifyUrl: string;
|
||||||
|
notifyStatus: number;
|
||||||
|
amount: number;
|
||||||
|
channelFeeRate: number;
|
||||||
|
channelFeeAmount: number;
|
||||||
|
status: number;
|
||||||
|
userIp: string;
|
||||||
|
expireTime: Date;
|
||||||
|
successTime: Date;
|
||||||
|
notifyTime: Date;
|
||||||
|
successExtensionId: number;
|
||||||
|
refundStatus: number;
|
||||||
|
refundTimes: number;
|
||||||
|
refundAmount: number;
|
||||||
|
channelUserId: string;
|
||||||
|
channelOrderNo: string;
|
||||||
|
createTime: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 支付订单分页请求 */
|
||||||
|
export interface OrderPageReqVO extends PageParam {
|
||||||
|
merchantId?: number;
|
||||||
|
appId?: number;
|
||||||
|
channelId?: number;
|
||||||
|
channelCode?: string;
|
||||||
|
merchantOrderId?: string;
|
||||||
|
subject?: string;
|
||||||
|
body?: string;
|
||||||
|
notifyUrl?: string;
|
||||||
|
notifyStatus?: number;
|
||||||
|
amount?: number;
|
||||||
|
channelFeeRate?: number;
|
||||||
|
channelFeeAmount?: number;
|
||||||
|
status?: number;
|
||||||
|
expireTime?: Date[];
|
||||||
|
successTime?: Date[];
|
||||||
|
notifyTime?: Date[];
|
||||||
|
successExtensionId?: number;
|
||||||
|
refundStatus?: number;
|
||||||
|
refundTimes?: number;
|
||||||
|
channelUserId?: string;
|
||||||
|
channelOrderNo?: string;
|
||||||
|
createTime?: Date[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 支付订单导出请求 */
|
||||||
|
export interface OrderExportReqVO {
|
||||||
|
merchantId?: number;
|
||||||
|
appId?: number;
|
||||||
|
channelId?: number;
|
||||||
|
channelCode?: string;
|
||||||
|
merchantOrderId?: string;
|
||||||
|
subject?: string;
|
||||||
|
body?: string;
|
||||||
|
notifyUrl?: string;
|
||||||
|
notifyStatus?: number;
|
||||||
|
amount?: number;
|
||||||
|
channelFeeRate?: number;
|
||||||
|
channelFeeAmount?: number;
|
||||||
|
status?: number;
|
||||||
|
expireTime?: Date[];
|
||||||
|
successTime?: Date[];
|
||||||
|
notifyTime?: Date[];
|
||||||
|
successExtensionId?: number;
|
||||||
|
refundStatus?: number;
|
||||||
|
refundTimes?: number;
|
||||||
|
channelUserId?: string;
|
||||||
|
channelOrderNo?: string;
|
||||||
|
createTime?: Date[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询支付订单列表 */
|
||||||
|
export function getOrderPage(params: PayOrderApi.OrderPageReqVO) {
|
||||||
|
return requestClient.get<PageResult<PayOrderApi.Order>>('/pay/order/page', {
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询支付订单详情 */
|
||||||
|
export function getOrder(id: number, sync?: boolean) {
|
||||||
|
return requestClient.get<PayOrderApi.Order>('/pay/order/get', {
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
sync,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获得支付订单的明细 */
|
||||||
|
export function getOrderDetail(id: number) {
|
||||||
|
return requestClient.get<PayOrderApi.Order>(`/pay/order/get-detail?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 提交支付订单 */
|
||||||
|
export function submitOrder(data: any) {
|
||||||
|
return requestClient.post('/pay/order/submit', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出支付订单 */
|
||||||
|
export function exportOrder(params: PayOrderApi.OrderExportReqVO) {
|
||||||
|
return requestClient.download('/pay/order/export-excel', { params });
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace PayRefundApi {
|
||||||
|
/** 退款订单信息 */
|
||||||
|
export interface Refund {
|
||||||
|
id: number;
|
||||||
|
merchantId: number;
|
||||||
|
appId: number;
|
||||||
|
channelId: number;
|
||||||
|
channelCode: string;
|
||||||
|
orderId: string;
|
||||||
|
tradeNo: string;
|
||||||
|
merchantOrderId: string;
|
||||||
|
merchantRefundNo: string;
|
||||||
|
notifyUrl: string;
|
||||||
|
notifyStatus: number;
|
||||||
|
status: number;
|
||||||
|
type: number;
|
||||||
|
payAmount: number;
|
||||||
|
refundAmount: number;
|
||||||
|
reason: string;
|
||||||
|
userIp: string;
|
||||||
|
channelOrderNo: string;
|
||||||
|
channelRefundNo: string;
|
||||||
|
channelErrorCode: string;
|
||||||
|
channelErrorMsg: string;
|
||||||
|
channelExtras: string;
|
||||||
|
expireTime: Date;
|
||||||
|
successTime: Date;
|
||||||
|
notifyTime: Date;
|
||||||
|
createTime: Date;
|
||||||
|
updateTime: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 退款订单分页请求 */
|
||||||
|
export interface RefundPageReqVO extends PageParam {
|
||||||
|
merchantId?: number;
|
||||||
|
appId?: number;
|
||||||
|
channelId?: number;
|
||||||
|
channelCode?: string;
|
||||||
|
orderId?: string;
|
||||||
|
tradeNo?: string;
|
||||||
|
merchantOrderId?: string;
|
||||||
|
merchantRefundNo?: string;
|
||||||
|
notifyUrl?: string;
|
||||||
|
notifyStatus?: number;
|
||||||
|
status?: number;
|
||||||
|
type?: number;
|
||||||
|
payAmount?: number;
|
||||||
|
refundAmount?: number;
|
||||||
|
reason?: string;
|
||||||
|
userIp?: string;
|
||||||
|
channelOrderNo?: string;
|
||||||
|
channelRefundNo?: string;
|
||||||
|
channelErrorCode?: string;
|
||||||
|
channelErrorMsg?: string;
|
||||||
|
channelExtras?: string;
|
||||||
|
expireTime?: Date[];
|
||||||
|
successTime?: Date[];
|
||||||
|
notifyTime?: Date[];
|
||||||
|
createTime?: Date[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 退款订单导出请求 */
|
||||||
|
export interface RefundExportReqVO {
|
||||||
|
merchantId?: number;
|
||||||
|
appId?: number;
|
||||||
|
channelId?: number;
|
||||||
|
channelCode?: string;
|
||||||
|
orderId?: string;
|
||||||
|
tradeNo?: string;
|
||||||
|
merchantOrderId?: string;
|
||||||
|
merchantRefundNo?: string;
|
||||||
|
notifyUrl?: string;
|
||||||
|
notifyStatus?: number;
|
||||||
|
status?: number;
|
||||||
|
type?: number;
|
||||||
|
payAmount?: number;
|
||||||
|
refundAmount?: number;
|
||||||
|
reason?: string;
|
||||||
|
userIp?: string;
|
||||||
|
channelOrderNo?: string;
|
||||||
|
channelRefundNo?: string;
|
||||||
|
channelErrorCode?: string;
|
||||||
|
channelErrorMsg?: string;
|
||||||
|
channelExtras?: string;
|
||||||
|
expireTime?: Date[];
|
||||||
|
successTime?: Date[];
|
||||||
|
notifyTime?: Date[];
|
||||||
|
createTime?: Date[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询退款订单列表 */
|
||||||
|
export function getRefundPage(params: PayRefundApi.RefundPageReqVO) {
|
||||||
|
return requestClient.get<PageResult<PayRefundApi.Refund>>(
|
||||||
|
'/pay/refund/page',
|
||||||
|
{
|
||||||
|
params,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询退款订单详情 */
|
||||||
|
export function getRefund(id: number) {
|
||||||
|
return requestClient.get<PayRefundApi.Refund>(`/pay/refund/get?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 创建退款订单 */
|
||||||
|
export function createRefund(data: PayRefundApi.Refund) {
|
||||||
|
return requestClient.post('/pay/refund/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新退款订单 */
|
||||||
|
export function updateRefund(data: PayRefundApi.Refund) {
|
||||||
|
return requestClient.put('/pay/refund/update', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除退款订单 */
|
||||||
|
export function deleteRefund(id: number) {
|
||||||
|
return requestClient.delete(`/pay/refund/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出退款订单 */
|
||||||
|
export function exportRefund(params: PayRefundApi.RefundExportReqVO) {
|
||||||
|
return requestClient.download('/pay/refund/export-excel', { params });
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace PayTransferApi {
|
||||||
|
/** 转账单信息 */
|
||||||
|
export interface Transfer {
|
||||||
|
id: number;
|
||||||
|
appId: number;
|
||||||
|
channelId: number;
|
||||||
|
channelCode: string;
|
||||||
|
merchantTransferId: string;
|
||||||
|
type: number;
|
||||||
|
price: number;
|
||||||
|
subject: string;
|
||||||
|
userName: string;
|
||||||
|
alipayLogonId: string;
|
||||||
|
openid: string;
|
||||||
|
status: number;
|
||||||
|
createTime: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 转账单分页请求 */
|
||||||
|
export interface TransferPageReqVO extends PageParam {
|
||||||
|
appId?: number;
|
||||||
|
channelId?: number;
|
||||||
|
channelCode?: string;
|
||||||
|
merchantTransferId?: string;
|
||||||
|
type?: number;
|
||||||
|
price?: number;
|
||||||
|
subject?: string;
|
||||||
|
userName?: string;
|
||||||
|
status?: number;
|
||||||
|
createTime?: Date[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询转账单列表 */
|
||||||
|
export function getTransferPage(params: PayTransferApi.TransferPageReqVO) {
|
||||||
|
return requestClient.get<PageResult<PayTransferApi.Transfer>>(
|
||||||
|
'/pay/transfer/page',
|
||||||
|
{
|
||||||
|
params,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询转账单详情 */
|
||||||
|
export function getTransfer(id: number) {
|
||||||
|
return requestClient.get<PayTransferApi.Transfer>(
|
||||||
|
`/pay/transfer/get?id=${id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 创建转账单 */
|
||||||
|
export function createTransfer(data: PayTransferApi.Transfer) {
|
||||||
|
return requestClient.post('/pay/transfer/create', data);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace PayWalletApi {
|
||||||
|
/** 用户钱包查询参数 */
|
||||||
|
export interface PayWalletUserReqVO {
|
||||||
|
userId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 钱包信息 */
|
||||||
|
export interface WalletVO {
|
||||||
|
id: number;
|
||||||
|
userId: number;
|
||||||
|
userType: number;
|
||||||
|
balance: number;
|
||||||
|
totalExpense: number;
|
||||||
|
totalRecharge: number;
|
||||||
|
freezePrice: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 钱包分页请求 */
|
||||||
|
export interface WalletPageReqVO extends PageParam {
|
||||||
|
userId?: number;
|
||||||
|
userType?: number;
|
||||||
|
balance?: number;
|
||||||
|
totalExpense?: number;
|
||||||
|
totalRecharge?: number;
|
||||||
|
freezePrice?: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询用户钱包详情 */
|
||||||
|
export function getWallet(params: PayWalletApi.PayWalletUserReqVO) {
|
||||||
|
return requestClient.get<PayWalletApi.WalletVO>('/pay/wallet/get', {
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询会员钱包列表 */
|
||||||
|
export function getWalletPage(params: PayWalletApi.WalletPageReqVO) {
|
||||||
|
return requestClient.get<PageResult<PayWalletApi.WalletVO>>(
|
||||||
|
'/pay/wallet/page',
|
||||||
|
{
|
||||||
|
params,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改会员钱包余额 */
|
||||||
|
export function updateWalletBalance(data: PayWalletApi.WalletVO) {
|
||||||
|
return requestClient.put('/pay/wallet/update-balance', data);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace WalletRechargePackageApi {
|
||||||
|
/** 充值套餐信息 */
|
||||||
|
export interface Package {
|
||||||
|
id?: number;
|
||||||
|
name: string;
|
||||||
|
payPrice: number;
|
||||||
|
bonusPrice: number;
|
||||||
|
status: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询充值套餐列表 */
|
||||||
|
export function getPackagePage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<WalletRechargePackageApi.Package>>(
|
||||||
|
'/pay/wallet-recharge-package/page',
|
||||||
|
{
|
||||||
|
params,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询充值套餐详情 */
|
||||||
|
export function getPackage(id: number) {
|
||||||
|
return requestClient.get<WalletRechargePackageApi.Package>(
|
||||||
|
`/pay/wallet-recharge-package/get?id=${id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增充值套餐 */
|
||||||
|
export function createPackage(data: WalletRechargePackageApi.Package) {
|
||||||
|
return requestClient.post('/pay/wallet-recharge-package/create', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改充值套餐 */
|
||||||
|
export function updatePackage(data: WalletRechargePackageApi.Package) {
|
||||||
|
return requestClient.put('/pay/wallet-recharge-package/update', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除充值套餐 */
|
||||||
|
export function deletePackage(id: number) {
|
||||||
|
return requestClient.delete(`/pay/wallet-recharge-package/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace WalletTransactionApi {
|
||||||
|
/** 钱包交易流水信息 */
|
||||||
|
export interface Transaction {
|
||||||
|
id: number;
|
||||||
|
walletId: number;
|
||||||
|
title: string;
|
||||||
|
price: number;
|
||||||
|
balance: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询钱包交易流水列表 */
|
||||||
|
export function getTransactionPage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<WalletTransactionApi.Transaction>>(
|
||||||
|
'/pay/wallet-transaction/page',
|
||||||
|
{
|
||||||
|
params,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import ImageUpload from '#/components/upload/image-upload.vue';
|
||||||
|
|
||||||
export const useImagesUpload = () => {
|
export const useImagesUpload = () => {
|
||||||
return defineComponent({
|
return defineComponent({
|
||||||
|
name: 'ImagesUpload',
|
||||||
props: {
|
props: {
|
||||||
multiple: {
|
multiple: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|
@ -20,6 +21,5 @@ export const useImagesUpload = () => {
|
||||||
<ImageUpload maxNumber={props.maxNumber} multiple={props.multiple} />
|
<ImageUpload maxNumber={props.maxNumber} multiple={props.multiple} />
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
name: 'ImagesUpload',
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ function openWindow(url: string) {
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-2 flex justify-center">
|
<p class="mt-2 flex justify-center">
|
||||||
<span>
|
<span>
|
||||||
<Image src="../../../public/wx-xingyu.png" alt="数舵科技" />
|
<Image src="/wx-xingyu.png" alt="数舵科技" />
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-2 flex justify-center pt-4 text-sm italic">
|
<p class="mt-2 flex justify-center pt-4 text-sm italic">
|
||||||
|
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
import type { RouteRecordRaw } from 'vue-router';
|
|
||||||
|
|
||||||
// import { $t } from '#/locales';
|
|
||||||
|
|
||||||
const routes: RouteRecordRaw[] = [
|
|
||||||
// {
|
|
||||||
// meta: {
|
|
||||||
// icon: 'ic:baseline-view-in-ar',
|
|
||||||
// keepAlive: true,
|
|
||||||
// order: 1000,
|
|
||||||
// title: $t('demos.title'),
|
|
||||||
// },
|
|
||||||
// name: 'Demos',
|
|
||||||
// path: '/demos',
|
|
||||||
// children: [
|
|
||||||
// {
|
|
||||||
// meta: {
|
|
||||||
// title: $t('demos.antd'),
|
|
||||||
// },
|
|
||||||
// name: 'AntDesignDemos',
|
|
||||||
// path: '/demos/ant-design',
|
|
||||||
// component: () => import('#/views/demos/antd/index.vue'),
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
];
|
|
||||||
|
|
||||||
export default routes; // update by 芋艿:不展示
|
|
||||||
|
|
@ -15,14 +15,22 @@ export function getRangePickerDefaultProps() {
|
||||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||||
format: 'YYYY-MM-DD HH:mm:ss',
|
format: 'YYYY-MM-DD HH:mm:ss',
|
||||||
placeholder: ['开始时间', '结束时间'],
|
placeholder: ['开始时间', '结束时间'],
|
||||||
// prettier-ignore
|
|
||||||
ranges: {
|
ranges: {
|
||||||
'今天': [dayjs().startOf('day'), dayjs().endOf('day')],
|
今天: [dayjs().startOf('day'), dayjs().endOf('day')],
|
||||||
'昨天': [dayjs().subtract(1, 'day').startOf('day'), dayjs().subtract(1, 'day').endOf('day')],
|
昨天: [
|
||||||
'本周': [dayjs().startOf('week'), dayjs().endOf('day')],
|
dayjs().subtract(1, 'day').startOf('day'),
|
||||||
'本月': [dayjs().startOf('month'), dayjs().endOf('day')],
|
dayjs().subtract(1, 'day').endOf('day'),
|
||||||
'最近 7 天': [dayjs().subtract(7, 'day').startOf('day'), dayjs().endOf('day')],
|
],
|
||||||
'最近 30 天': [dayjs().subtract(30, 'day').startOf('day'), dayjs().endOf('day')],
|
本周: [dayjs().startOf('week'), dayjs().endOf('day')],
|
||||||
|
本月: [dayjs().startOf('month'), dayjs().endOf('day')],
|
||||||
|
'最近 7 天': [
|
||||||
|
dayjs().subtract(7, 'day').startOf('day'),
|
||||||
|
dayjs().endOf('day'),
|
||||||
|
],
|
||||||
|
'最近 30 天': [
|
||||||
|
dayjs().subtract(30, 'day').startOf('day'),
|
||||||
|
dayjs().endOf('day'),
|
||||||
|
],
|
||||||
},
|
},
|
||||||
transformDateFunc: (dates: any) => {
|
transformDateFunc: (dates: any) => {
|
||||||
if (dates && dates.length === 2) {
|
if (dates && dates.length === 2) {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Recordable } from '@vben/types';
|
import type { Recordable } from '@vben/types';
|
||||||
|
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
import { useVbenForm, z } from '#/adapter/form';
|
import { useVbenForm, z } from '#/adapter/form';
|
||||||
import { updateUserPassword } from '#/api/system/user/profile';
|
import { updateUserPassword } from '#/api/system/user/profile';
|
||||||
import { $t } from '@vben/locales';
|
|
||||||
|
|
||||||
const [Form, formApi] = useVbenForm({
|
const [Form, formApi] = useVbenForm({
|
||||||
commonConfig: {
|
commonConfig: {
|
||||||
|
|
|
||||||
|
|
@ -2,26 +2,27 @@
|
||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { SystemSocialUserApi } from '#/api/system/social/user';
|
import type { SystemSocialUserApi } from '#/api/system/social/user';
|
||||||
|
|
||||||
|
import { computed, onMounted, ref } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
import { Button, Card, Image, message, Modal } from 'ant-design-vue';
|
import { Button, Card, Image, message, Modal } from 'ant-design-vue';
|
||||||
|
|
||||||
import { computed, ref, onMounted } from 'vue';
|
|
||||||
import { useRoute } from 'vue-router';
|
|
||||||
import { $t } from '#/locales';
|
|
||||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { socialAuthRedirect } from '#/api/core/auth';
|
||||||
import {
|
import {
|
||||||
getBindSocialUserList,
|
getBindSocialUserList,
|
||||||
socialUnbind,
|
|
||||||
socialBind,
|
socialBind,
|
||||||
|
socialUnbind,
|
||||||
} from '#/api/system/social/user';
|
} from '#/api/system/social/user';
|
||||||
import { socialAuthRedirect } from '#/api/core/auth';
|
import { $t } from '#/locales';
|
||||||
import { DICT_TYPE, getDictLabel } from '#/utils/dict';
|
|
||||||
import { SystemUserSocialTypeEnum } from '#/utils/constants';
|
import { SystemUserSocialTypeEnum } from '#/utils/constants';
|
||||||
|
import { DICT_TYPE, getDictLabel } from '#/utils/dict';
|
||||||
|
|
||||||
const route = useRoute();
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'update:activeName', v: string): void;
|
(e: 'update:activeName', v: string): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
/** 已经绑定的平台 */
|
/** 已经绑定的平台 */
|
||||||
const bindList = ref<SystemSocialUserApi.SocialUser[]>([]);
|
const bindList = ref<SystemSocialUserApi.SocialUser[]>([]);
|
||||||
const allBindList = computed<any[]>(() => {
|
const allBindList = computed<any[]>(() => {
|
||||||
|
|
@ -126,8 +127,7 @@ async function onBind(bind: any) {
|
||||||
try {
|
try {
|
||||||
// 计算 redirectUri
|
// 计算 redirectUri
|
||||||
// tricky: type 需要先 encode 一次,否则钉钉回调会丢失。配合 getUrlValue() 使用
|
// tricky: type 需要先 encode 一次,否则钉钉回调会丢失。配合 getUrlValue() 使用
|
||||||
const redirectUri =
|
const redirectUri = `${location.origin}/profile?${encodeURIComponent(`type=${type}`)}`;
|
||||||
location.origin + '/profile?' + encodeURIComponent(`type=${type}`);
|
|
||||||
|
|
||||||
// 进行跳转
|
// 进行跳转
|
||||||
window.location.href = await socialAuthRedirect(type, redirectUri);
|
window.location.href = await socialAuthRedirect(type, redirectUri);
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -69,7 +69,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
// 设置到 values
|
// 设置到 values
|
||||||
await formApi.setValues(formData.value);
|
await formApi.setValues(formData.value);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,242 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Page } from '@vben/common-ui';
|
import type { BpmModelApi } from '#/api/bpm/model';
|
||||||
|
|
||||||
import { Button } from 'ant-design-vue';
|
import { onActivated, reactive, ref, useTemplateRef, watch } from 'vue';
|
||||||
|
|
||||||
|
import { Page } from '@vben/common-ui';
|
||||||
|
import { Plus, Search, Settings } from '@vben/icons';
|
||||||
|
import { cloneDeep } from '@vben/utils';
|
||||||
|
|
||||||
|
import { refAutoReset } from '@vueuse/core';
|
||||||
|
import { useSortable } from '@vueuse/integrations/useSortable';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Divider,
|
||||||
|
Dropdown,
|
||||||
|
Form,
|
||||||
|
Input,
|
||||||
|
Menu,
|
||||||
|
message,
|
||||||
|
} from 'ant-design-vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getCategorySimpleList,
|
||||||
|
updateCategorySortBatch,
|
||||||
|
} from '#/api/bpm/category';
|
||||||
|
import { getModelList } from '#/api/bpm/model';
|
||||||
|
|
||||||
|
import CategoryDraggableModel from './modules/category-draggable-model.vue';
|
||||||
|
// 模型列表加载状态
|
||||||
|
const modelListSpinning = refAutoReset(false, 3000);
|
||||||
|
// 保存排序状态
|
||||||
|
const saveSortLoading = ref(false);
|
||||||
|
// 按照 category 分组的数据
|
||||||
|
const categoryGroup = ref<BpmModelApi.ModelCategoryInfo[]>([]);
|
||||||
|
// 未排序前的原始数据
|
||||||
|
const originalData = ref<BpmModelApi.ModelCategoryInfo[]>([]);
|
||||||
|
// 可以排序元素的容器
|
||||||
|
const sortable = useTemplateRef<HTMLElement>('categoryGroupRef');
|
||||||
|
// 排序引用,以便后续启用或禁用排序
|
||||||
|
const sortableInstance = ref<any>(null);
|
||||||
|
// 分类排序状态
|
||||||
|
const isCategorySorting = ref(false);
|
||||||
|
// 查询参数
|
||||||
|
const queryParams = reactive({
|
||||||
|
name: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听分类排序模式切换
|
||||||
|
watch(
|
||||||
|
() => isCategorySorting.value,
|
||||||
|
(newValue) => {
|
||||||
|
if (sortableInstance.value) {
|
||||||
|
if (newValue) {
|
||||||
|
// 启用排序功能
|
||||||
|
sortableInstance.value.option('disabled', false);
|
||||||
|
} else {
|
||||||
|
// 禁用排序功能
|
||||||
|
sortableInstance.value.option('disabled', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 加载数据 */
|
||||||
|
const getList = async () => {
|
||||||
|
modelListSpinning.value = true;
|
||||||
|
try {
|
||||||
|
const modelList = await getModelList(queryParams.name);
|
||||||
|
const categoryList = await getCategorySimpleList();
|
||||||
|
// 按照 category 聚合
|
||||||
|
categoryGroup.value = categoryList.map((category: any) => ({
|
||||||
|
...category,
|
||||||
|
modelList: modelList.filter(
|
||||||
|
(model: any) => model.categoryName === category.name,
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
// 重置排序实例
|
||||||
|
sortableInstance.value = null;
|
||||||
|
} finally {
|
||||||
|
modelListSpinning.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 初始化 */
|
||||||
|
onActivated(() => {
|
||||||
|
getList();
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 查询方法 */
|
||||||
|
const handleQuery = () => {
|
||||||
|
getList();
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 新增模型 */
|
||||||
|
const createModel = () => {
|
||||||
|
// TODO 新增模型
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 处理下拉菜单命令 */
|
||||||
|
const handleCommand = (command: string) => {
|
||||||
|
if (command === 'handleCategoryAdd') {
|
||||||
|
// TODO 新建分类逻辑
|
||||||
|
} else if (command === 'handleCategorySort') {
|
||||||
|
originalData.value = cloneDeep(categoryGroup.value);
|
||||||
|
isCategorySorting.value = true;
|
||||||
|
// 如果排序实例不存在,则初始化
|
||||||
|
if (sortableInstance.value) {
|
||||||
|
// 已存在实例,则启用排序功能
|
||||||
|
sortableInstance.value.option('disabled', false);
|
||||||
|
} else {
|
||||||
|
sortableInstance.value = useSortable(sortable, categoryGroup, {
|
||||||
|
disabled: false, // 启用排序
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 取消分类排序 */
|
||||||
|
const handleCategorySortCancel = () => {
|
||||||
|
// 恢复初始数据
|
||||||
|
categoryGroup.value = cloneDeep(originalData.value);
|
||||||
|
isCategorySorting.value = false;
|
||||||
|
// 直接禁用排序功能
|
||||||
|
if (sortableInstance.value) {
|
||||||
|
sortableInstance.value.option('disabled', true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 提交分类排序 */
|
||||||
|
const handleCategorySortSubmit = async () => {
|
||||||
|
saveSortLoading.value = true;
|
||||||
|
try {
|
||||||
|
// 保存排序逻辑
|
||||||
|
const ids = categoryGroup.value.map((item: any) => item.id);
|
||||||
|
await updateCategorySortBatch(ids);
|
||||||
|
} finally {
|
||||||
|
saveSortLoading.value = false;
|
||||||
|
}
|
||||||
|
message.success('分类排序成功');
|
||||||
|
isCategorySorting.value = false;
|
||||||
|
// 刷新列表
|
||||||
|
await getList();
|
||||||
|
// 禁用排序功能
|
||||||
|
if (sortableInstance.value) {
|
||||||
|
sortableInstance.value.option('disabled', true);
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page>
|
<Page auto-content-height>
|
||||||
<Button
|
<Card
|
||||||
danger
|
:body-style="{ padding: '10px' }"
|
||||||
type="link"
|
class="mb-4"
|
||||||
target="_blank"
|
v-spinning="modelListSpinning"
|
||||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
|
||||||
>
|
>
|
||||||
该功能支持 Vue3 + element-plus 版本!
|
<div class="flex items-center justify-between pl-5">
|
||||||
</Button>
|
<span class="-mb-4 text-lg font-extrabold">流程模型</span>
|
||||||
<br />
|
<!-- 搜索工作栏 -->
|
||||||
<Button
|
<Form
|
||||||
type="link"
|
v-if="!isCategorySorting"
|
||||||
target="_blank"
|
class="-mb-4 mr-2.5 flex"
|
||||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/model/index"
|
:model="queryParams"
|
||||||
>
|
layout="inline"
|
||||||
可参考
|
>
|
||||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/model/index
|
<Form.Item name="name" class="ml-auto">
|
||||||
代码,pull request 贡献给我们!
|
<Input
|
||||||
</Button>
|
v-model:value="queryParams.name"
|
||||||
|
placeholder="搜索流程"
|
||||||
|
allow-clear
|
||||||
|
@press-enter="handleQuery"
|
||||||
|
class="!w-60"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<Search class="mx-2.5" />
|
||||||
|
</template>
|
||||||
|
</Input>
|
||||||
|
</Form.Item>
|
||||||
|
<!-- 右上角:新建模型、更多操作 -->
|
||||||
|
<Form.Item>
|
||||||
|
<Button type="primary" @click="createModel">
|
||||||
|
<Plus class="size-5" /> 新建模型
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item>
|
||||||
|
<Dropdown placement="bottomRight">
|
||||||
|
<Button>
|
||||||
|
<template #icon>
|
||||||
|
<Settings class="size-4" />
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
<template #overlay>
|
||||||
|
<Menu @click="(e) => handleCommand(e.key as string)">
|
||||||
|
<Menu.Item key="handleCategoryAdd">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span
|
||||||
|
class="icon-[ant-design--plus-outlined] mr-1.5 text-[18px]"
|
||||||
|
></span>
|
||||||
|
新建分类
|
||||||
|
</div>
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item key="handleCategorySort">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="icon-[fa--sort-amount-desc] mr-1.5"></span>
|
||||||
|
分类排序
|
||||||
|
</div>
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
</template>
|
||||||
|
</Dropdown>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
<div class="-mb-4 mr-6" v-else>
|
||||||
|
<Button @click="handleCategorySortCancel" class="mr-3">
|
||||||
|
取 消
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
:loading="saveSortLoading"
|
||||||
|
@click="handleCategorySortSubmit"
|
||||||
|
>
|
||||||
|
保存排序
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
<!-- 按照分类,展示其所属的模型列表 -->
|
||||||
|
<div class="px-5" ref="categoryGroupRef">
|
||||||
|
<CategoryDraggableModel
|
||||||
|
v-for="element in categoryGroup"
|
||||||
|
:class="isCategorySorting ? 'cursor-move' : ''"
|
||||||
|
:key="element.id"
|
||||||
|
:category-info="element"
|
||||||
|
:is-category-sorting="isCategorySorting"
|
||||||
|
@success="getList"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,398 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { BpmCategoryApi } from '#/api/bpm/category';
|
||||||
|
import type { BpmModelApi } from '#/api/bpm/model';
|
||||||
|
|
||||||
|
import { computed, ref, watchEffect } from 'vue';
|
||||||
|
|
||||||
|
import { cloneDeep, formatDateTime, isEqual } from '@vben/utils';
|
||||||
|
|
||||||
|
import { useDebounceFn } from '@vueuse/core';
|
||||||
|
import { useSortable } from '@vueuse/integrations/useSortable';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Collapse,
|
||||||
|
message,
|
||||||
|
Table,
|
||||||
|
Tag,
|
||||||
|
Tooltip,
|
||||||
|
} from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { updateModelSortBatch } from '#/api/bpm/model';
|
||||||
|
import { DictTag } from '#/components/dict-tag';
|
||||||
|
import { DICT_TYPE } from '#/utils/dict';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
categoryInfo: BpmCategoryApi.ModelCategoryInfo;
|
||||||
|
isCategorySorting: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits(['success']);
|
||||||
|
|
||||||
|
const isModelSorting = ref(false);
|
||||||
|
const originalData = ref<BpmModelApi.ModelVO[]>([]);
|
||||||
|
const modelList = ref<BpmModelApi.ModelVO[]>([]);
|
||||||
|
const isExpand = ref(false);
|
||||||
|
const tableRef = ref();
|
||||||
|
|
||||||
|
// 排序引用,以便后续启用或禁用排序
|
||||||
|
const sortableInstance = ref<any>(null);
|
||||||
|
/** 解决 v-model 问题,使用计算属性 */
|
||||||
|
const expandKeys = computed(() => (isExpand.value ? ['1'] : []));
|
||||||
|
|
||||||
|
// 表格列配置
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '流程名',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
align: 'left' as const,
|
||||||
|
minWidth: 250,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '可见范围',
|
||||||
|
dataIndex: 'startUserIds',
|
||||||
|
key: 'startUserIds',
|
||||||
|
align: 'center' as const,
|
||||||
|
minWidth: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '流程类型',
|
||||||
|
dataIndex: 'type',
|
||||||
|
key: 'type',
|
||||||
|
align: 'center' as const,
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '表单信息',
|
||||||
|
dataIndex: 'formType',
|
||||||
|
key: 'formType',
|
||||||
|
align: 'center' as const,
|
||||||
|
minWidth: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '最后发布',
|
||||||
|
dataIndex: 'deploymentTime',
|
||||||
|
key: 'deploymentTime',
|
||||||
|
align: 'center' as const,
|
||||||
|
minWidth: 250,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'operation',
|
||||||
|
align: 'center' as const,
|
||||||
|
fixed: 'right' as const,
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/** 处理模型的排序 */
|
||||||
|
const handleModelSort = () => {
|
||||||
|
// 保存初始数据
|
||||||
|
originalData.value = cloneDeep(props.categoryInfo.modelList);
|
||||||
|
// 展开数据
|
||||||
|
isExpand.value = true;
|
||||||
|
isModelSorting.value = true;
|
||||||
|
// 如果排序实例不存在,则初始化
|
||||||
|
if (sortableInstance.value) {
|
||||||
|
// 已存在实例,则启用排序功能
|
||||||
|
sortableInstance.value.option('disabled', false);
|
||||||
|
} else {
|
||||||
|
const sortableClass = `.category-${props.categoryInfo.id} .ant-table .ant-table-tbody`;
|
||||||
|
sortableInstance.value = useSortable(sortableClass, modelList, {
|
||||||
|
disabled: false, // 启用排序
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 处理模型的排序提交 */
|
||||||
|
const handleModelSortSubmit = async () => {
|
||||||
|
try {
|
||||||
|
// 保存排序
|
||||||
|
const ids = modelList.value.map((item) => item.id);
|
||||||
|
await updateModelSortBatch(ids);
|
||||||
|
// 刷新列表
|
||||||
|
isModelSorting.value = false;
|
||||||
|
message.success('排序模型成功');
|
||||||
|
emit('success');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('排序保存失败', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 处理模型的排序取消 */
|
||||||
|
const handleModelSortCancel = () => {
|
||||||
|
// 恢复初始数据
|
||||||
|
modelList.value = cloneDeep(originalData.value);
|
||||||
|
isModelSorting.value = false;
|
||||||
|
// 禁用排序功能
|
||||||
|
if (sortableInstance.value) {
|
||||||
|
sortableInstance.value.option('disabled', true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 处理表单详情点击 */
|
||||||
|
const handleFormDetail = (row: any) => {
|
||||||
|
// TODO 待实现
|
||||||
|
console.warn('待实现', row);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 更新 modelList 模型列表 */
|
||||||
|
const updateModelList = useDebounceFn(() => {
|
||||||
|
const newModelList = props.categoryInfo.modelList;
|
||||||
|
if (!isEqual(modelList.value, newModelList)) {
|
||||||
|
modelList.value = cloneDeep(newModelList);
|
||||||
|
if (newModelList?.length > 0) {
|
||||||
|
isExpand.value = true;
|
||||||
|
}
|
||||||
|
// 关闭排序
|
||||||
|
isModelSorting.value = false;
|
||||||
|
// 重置排序实例
|
||||||
|
sortableInstance.value = null;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
/** 监听分类信息和排序状态变化 */
|
||||||
|
watchEffect(() => {
|
||||||
|
if (props.categoryInfo?.modelList) {
|
||||||
|
updateModelList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.isCategorySorting) {
|
||||||
|
isExpand.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 自定义表格行渲染 */
|
||||||
|
const customRow = (_record: any) => {
|
||||||
|
return {
|
||||||
|
class: isModelSorting.value ? 'cursor-move' : '',
|
||||||
|
};
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Card
|
||||||
|
:body-style="{ padding: 0 }"
|
||||||
|
class="category-draggable-model mb-5 rounded-lg transition-all duration-300 ease-in-out hover:shadow-xl"
|
||||||
|
>
|
||||||
|
<div class="flex h-12 items-center">
|
||||||
|
<!-- 头部:分类名 -->
|
||||||
|
<div class="flex items-center">
|
||||||
|
<Tooltip v-if="isCategorySorting" title="拖动排序">
|
||||||
|
<span
|
||||||
|
class="icon-[ic--round-drag-indicator] ml-2.5 cursor-move text-2xl text-gray-500"
|
||||||
|
></span>
|
||||||
|
</Tooltip>
|
||||||
|
<div class="ml-4 mr-2 text-lg font-medium">{{ categoryInfo.name }}</div>
|
||||||
|
<div class="text-gray-500">
|
||||||
|
({{ categoryInfo.modelList?.length || 0 }})
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 头部:操作 -->
|
||||||
|
<div class="flex flex-1 items-center" v-show="!isCategorySorting">
|
||||||
|
<div
|
||||||
|
v-if="categoryInfo.modelList.length > 0"
|
||||||
|
class="ml-3 flex cursor-pointer items-center transition-transform duration-300"
|
||||||
|
:class="isExpand ? 'rotate-180' : 'rotate-0'"
|
||||||
|
@click="isExpand = !isExpand"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon-[ic--round-expand-more] text-3xl text-gray-400"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="ml-auto flex items-center"
|
||||||
|
:class="isModelSorting ? 'mr-4' : 'mr-12'"
|
||||||
|
>
|
||||||
|
<template v-if="!isModelSorting">
|
||||||
|
<Button
|
||||||
|
v-if="categoryInfo.modelList.length > 0"
|
||||||
|
type="link"
|
||||||
|
class="mr-5 flex items-center text-[14px]"
|
||||||
|
@click.stop="handleModelSort"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<span class="icon-[fa--sort-amount-desc] mr-1"></span>
|
||||||
|
</template>
|
||||||
|
排序
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<Button @click.stop="handleModelSortCancel" class="mr-2">
|
||||||
|
取 消
|
||||||
|
</Button>
|
||||||
|
<Button type="primary" @click.stop="handleModelSortSubmit">
|
||||||
|
保存排序
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 模型列表 -->
|
||||||
|
<Collapse :active-key="expandKeys" :bordered="false" class="bg-transparent">
|
||||||
|
<Collapse.Panel
|
||||||
|
key="1"
|
||||||
|
:show-arrow="false"
|
||||||
|
class="border-0 bg-transparent p-0"
|
||||||
|
v-show="isExpand"
|
||||||
|
>
|
||||||
|
<Table
|
||||||
|
v-if="modelList && modelList.length > 0"
|
||||||
|
:class="`category-${categoryInfo.id}`"
|
||||||
|
ref="tableRef"
|
||||||
|
:data-source="modelList"
|
||||||
|
:columns="columns"
|
||||||
|
:pagination="false"
|
||||||
|
:custom-row="customRow"
|
||||||
|
row-key="id"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<!-- 流程名 -->
|
||||||
|
<template v-if="column.key === 'name'">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<Tooltip v-if="isModelSorting" title="拖动排序">
|
||||||
|
<span
|
||||||
|
class="icon-[ic--round-drag-indicator] mr-2.5 cursor-move text-2xl text-gray-500"
|
||||||
|
></span>
|
||||||
|
</Tooltip>
|
||||||
|
<div
|
||||||
|
v-if="!record.icon"
|
||||||
|
class="mr-2.5 flex h-9 w-9 items-center justify-center rounded bg-blue-500 text-white"
|
||||||
|
>
|
||||||
|
<span style="font-size: 12px">{{
|
||||||
|
record.name.substring(0, 2)
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
v-else
|
||||||
|
:src="record.icon"
|
||||||
|
class="mr-2.5 h-9 w-9 rounded"
|
||||||
|
alt="图标"
|
||||||
|
/>
|
||||||
|
{{ record.name }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 可见范围列-->
|
||||||
|
<template v-else-if="column.key === 'startUserIds'">
|
||||||
|
<span
|
||||||
|
v-if="!record.startUsers?.length && !record.startDepts?.length"
|
||||||
|
>
|
||||||
|
全部可见
|
||||||
|
</span>
|
||||||
|
<span v-else-if="record.startUsers?.length === 1">
|
||||||
|
{{ record.startUsers[0].nickname }}
|
||||||
|
</span>
|
||||||
|
<span v-else-if="record.startDepts?.length === 1">
|
||||||
|
{{ record.startDepts[0].name }}
|
||||||
|
</span>
|
||||||
|
<span v-else-if="record.startDepts?.length > 1">
|
||||||
|
<Tooltip
|
||||||
|
placement="top"
|
||||||
|
:title="
|
||||||
|
record.startDepts.map((dept: any) => dept.name).join('、')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ record.startDepts[0].name }}等
|
||||||
|
{{ record.startDepts.length }} 个部门可见
|
||||||
|
</Tooltip>
|
||||||
|
</span>
|
||||||
|
<span v-else-if="record.startUsers?.length > 1">
|
||||||
|
<Tooltip
|
||||||
|
placement="top"
|
||||||
|
:title="
|
||||||
|
record.startUsers
|
||||||
|
.map((user: any) => user.nickname)
|
||||||
|
.join('、')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ record.startUsers[0].nickname }}等
|
||||||
|
{{ record.startUsers.length }} 人可见
|
||||||
|
</Tooltip>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<!-- 流程类型列 -->
|
||||||
|
<template v-else-if="column.key === 'type'">
|
||||||
|
<!-- <DictTag :value="record.type" :type="DICT_TYPE.BPM_MODEL_TYPE" /> -->
|
||||||
|
<!-- <Tag>{{ record.type }}</Tag> -->
|
||||||
|
<DictTag :type="DICT_TYPE.BPM_MODEL_TYPE" :value="record.type" />
|
||||||
|
</template>
|
||||||
|
<!-- 表单信息列 -->
|
||||||
|
<template v-else-if="column.key === 'formType'">
|
||||||
|
<!-- TODO BpmModelFormType.NORMAL -->
|
||||||
|
<Button
|
||||||
|
v-if="record.formType === 10"
|
||||||
|
type="link"
|
||||||
|
@click="handleFormDetail(record)"
|
||||||
|
>
|
||||||
|
{{ record.formName }}
|
||||||
|
</Button>
|
||||||
|
<!-- TODO BpmModelFormType.CUSTOM -->
|
||||||
|
<Button
|
||||||
|
v-else-if="record.formType === 20"
|
||||||
|
type="link"
|
||||||
|
@click="handleFormDetail(record)"
|
||||||
|
>
|
||||||
|
{{ record.formCustomCreatePath }}
|
||||||
|
</Button>
|
||||||
|
<span v-else>暂无表单</span>
|
||||||
|
</template>
|
||||||
|
<!-- 最后发布列 -->
|
||||||
|
<template v-else-if="column.key === 'deploymentTime'">
|
||||||
|
<div class="flex items-center justify-center">
|
||||||
|
<span v-if="record.processDefinition" class="w-[150px]">
|
||||||
|
{{ formatDateTime(record.processDefinition.deploymentTime) }}
|
||||||
|
</span>
|
||||||
|
<Tag v-if="record.processDefinition">
|
||||||
|
v{{ record.processDefinition.version }}
|
||||||
|
</Tag>
|
||||||
|
<Tag v-else color="warning">未部署</Tag>
|
||||||
|
<Tag
|
||||||
|
v-if="record.processDefinition?.suspensionState === 2"
|
||||||
|
color="warning"
|
||||||
|
class="ml-[10px]"
|
||||||
|
>
|
||||||
|
已停用
|
||||||
|
</Tag>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<!-- 操作列 -->
|
||||||
|
<template v-else-if="column.key === 'operation'">
|
||||||
|
<div class="flex items-center justify-center">待实现</div>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</Table>
|
||||||
|
</Collapse.Panel>
|
||||||
|
</Collapse>
|
||||||
|
</Card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.category-draggable-model {
|
||||||
|
// ant-table-tbody 自定义样式
|
||||||
|
:deep(.ant-table-tbody > tr > td) {
|
||||||
|
overflow: hidden;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
// ant-collapse-header 自定义样式
|
||||||
|
:deep(.ant-collapse-header) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 优化表格渲染性能
|
||||||
|
:deep(.ant-table-tbody) {
|
||||||
|
transform: translateZ(0);
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 折叠面板样式
|
||||||
|
:deep(.ant-collapse-content-box) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,895 @@
|
||||||
|
import type { Ref } from 'vue';
|
||||||
|
|
||||||
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
|
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
import type { CrmContractApi } from '#/api/crm/contract';
|
||||||
|
import type { CrmReceivableApi } from '#/api/crm/receivable';
|
||||||
|
|
||||||
|
import { useAccess } from '@vben/access';
|
||||||
|
|
||||||
|
import { DICT_TYPE } from '#/utils/dict';
|
||||||
|
|
||||||
|
const { hasAccessByCodes } = useAccess();
|
||||||
|
|
||||||
|
export interface LeftSideItem {
|
||||||
|
name: string;
|
||||||
|
menu: string;
|
||||||
|
count: Ref<number>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 跟进状态 */
|
||||||
|
export const FOLLOWUP_STATUS = [
|
||||||
|
{ label: '待跟进', value: false },
|
||||||
|
{ label: '已跟进', value: true },
|
||||||
|
];
|
||||||
|
|
||||||
|
/** 归属范围 */
|
||||||
|
export const SCENE_TYPES = [
|
||||||
|
{ label: '我负责的', value: 1 },
|
||||||
|
{ label: '我参与的', value: 2 },
|
||||||
|
{ label: '下属负责的', value: 3 },
|
||||||
|
];
|
||||||
|
|
||||||
|
/** 联系状态 */
|
||||||
|
export const CONTACT_STATUS = [
|
||||||
|
{ label: '今日需联系', value: 1 },
|
||||||
|
{ label: '已逾期', value: 2 },
|
||||||
|
{ label: '已联系', value: 3 },
|
||||||
|
];
|
||||||
|
|
||||||
|
/** 审批状态 */
|
||||||
|
export const AUDIT_STATUS = [
|
||||||
|
{ label: '待审批', value: 10 },
|
||||||
|
{ label: '审核通过', value: 20 },
|
||||||
|
{ label: '审核不通过', value: 30 },
|
||||||
|
];
|
||||||
|
|
||||||
|
/** 回款提醒类型 */
|
||||||
|
export const RECEIVABLE_REMIND_TYPE = [
|
||||||
|
{ label: '待回款', value: 1 },
|
||||||
|
{ label: '已逾期', value: 2 },
|
||||||
|
{ label: '已回款', value: 3 },
|
||||||
|
];
|
||||||
|
|
||||||
|
/** 合同过期状态 */
|
||||||
|
export const CONTRACT_EXPIRY_TYPE = [
|
||||||
|
{ label: '即将过期', value: 1 },
|
||||||
|
{ label: '已过期', value: 2 },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const useLeftSides = (
|
||||||
|
customerTodayContactCount: Ref<number>,
|
||||||
|
clueFollowCount: Ref<number>,
|
||||||
|
customerFollowCount: Ref<number>,
|
||||||
|
customerPutPoolRemindCount: Ref<number>,
|
||||||
|
contractAuditCount: Ref<number>,
|
||||||
|
contractRemindCount: Ref<number>,
|
||||||
|
receivableAuditCount: Ref<number>,
|
||||||
|
receivablePlanRemindCount: Ref<number>,
|
||||||
|
): LeftSideItem[] => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: '今日需联系客户',
|
||||||
|
menu: 'customerTodayContact',
|
||||||
|
count: customerTodayContactCount,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '分配给我的线索',
|
||||||
|
menu: 'clueFollow',
|
||||||
|
count: clueFollowCount,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '分配给我的客户',
|
||||||
|
menu: 'customerFollow',
|
||||||
|
count: customerFollowCount,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '待进入公海的客户',
|
||||||
|
menu: 'customerPutPoolRemind',
|
||||||
|
count: customerPutPoolRemindCount,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '待审核合同',
|
||||||
|
menu: 'contractAudit',
|
||||||
|
count: contractAuditCount,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '待审核回款',
|
||||||
|
menu: 'receivableAudit',
|
||||||
|
count: receivableAuditCount,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '待回款提醒',
|
||||||
|
menu: 'receivablePlanRemind',
|
||||||
|
count: receivablePlanRemindCount,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '即将到期的合同',
|
||||||
|
menu: 'contractRemind',
|
||||||
|
count: contractRemindCount,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 分配给我的线索 列表的搜索表单 */
|
||||||
|
export function useClueFollowFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'followUpStatus',
|
||||||
|
label: '状态',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
options: FOLLOWUP_STATUS,
|
||||||
|
},
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 分配给我的线索 列表的字段 */
|
||||||
|
export function useClueFollowColumns(): VxeTableGridOptions['columns'] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
title: '线索名称',
|
||||||
|
minWidth: 160,
|
||||||
|
fixed: 'left',
|
||||||
|
slots: { default: 'name' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'source',
|
||||||
|
title: '线索来源',
|
||||||
|
minWidth: 100,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.CRM_CUSTOMER_SOURCE },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'mobile',
|
||||||
|
title: '手机',
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'telephone',
|
||||||
|
title: '电话',
|
||||||
|
minWidth: 130,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'email',
|
||||||
|
title: '邮箱',
|
||||||
|
minWidth: 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'detailAddress',
|
||||||
|
title: '地址',
|
||||||
|
minWidth: 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'industryId',
|
||||||
|
title: '客户行业',
|
||||||
|
minWidth: 100,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.CRM_CUSTOMER_INDUSTRY },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'level',
|
||||||
|
title: '客户级别',
|
||||||
|
minWidth: 100,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.CRM_CUSTOMER_LEVEL },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'contactNextTime',
|
||||||
|
title: '下次联系时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'remark',
|
||||||
|
title: '备注',
|
||||||
|
minWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'contactLastTime',
|
||||||
|
title: '最后跟进时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'contactLastContent',
|
||||||
|
title: '最后跟进记录',
|
||||||
|
minWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'ownerUserName',
|
||||||
|
title: '负责人',
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'ownerUserDeptName',
|
||||||
|
title: '所属部门',
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'updateTime',
|
||||||
|
title: '更新时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'createTime',
|
||||||
|
title: '创建时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'creatorName',
|
||||||
|
title: '创建人',
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 合同审核列表的搜索表单 */
|
||||||
|
export function useContractAuditFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'auditStatus',
|
||||||
|
label: '合同状态',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
options: AUDIT_STATUS,
|
||||||
|
},
|
||||||
|
defaultValue: 10,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 合同提醒列表的搜索表单 */
|
||||||
|
export function useContractRemindFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'expiryType',
|
||||||
|
label: '到期状态',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
options: CONTRACT_EXPIRY_TYPE,
|
||||||
|
},
|
||||||
|
defaultValue: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 合同审核列表的字段 */
|
||||||
|
export function useContractColumns<T = CrmContractApi.Contract>(
|
||||||
|
onActionClick: OnActionClickFn<T>,
|
||||||
|
): VxeTableGridOptions['columns'] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field: 'no',
|
||||||
|
title: '合同编号',
|
||||||
|
minWidth: 160,
|
||||||
|
fixed: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
title: '合同名称',
|
||||||
|
minWidth: 160,
|
||||||
|
slots: {
|
||||||
|
default: 'name',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'customerName',
|
||||||
|
title: '客户名称',
|
||||||
|
minWidth: 160,
|
||||||
|
slots: {
|
||||||
|
default: 'customerName',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'businessName',
|
||||||
|
title: '商机名称',
|
||||||
|
minWidth: 160,
|
||||||
|
slots: {
|
||||||
|
default: 'businessName',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'price',
|
||||||
|
title: '合同金额(元)',
|
||||||
|
minWidth: 120,
|
||||||
|
formatter: 'formatAmount',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'orderDate',
|
||||||
|
title: '下单时间',
|
||||||
|
minWidth: 120,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'startTime',
|
||||||
|
title: '合同开始时间',
|
||||||
|
minWidth: 120,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'endTime',
|
||||||
|
title: '合同结束时间',
|
||||||
|
minWidth: 120,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'contactName',
|
||||||
|
title: '客户签约人',
|
||||||
|
minWidth: 130,
|
||||||
|
slots: {
|
||||||
|
default: 'contactName',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'signUserName',
|
||||||
|
title: '公司签约人',
|
||||||
|
minWidth: 130,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'remark',
|
||||||
|
title: '备注',
|
||||||
|
minWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'totalReceivablePrice',
|
||||||
|
title: '已回款金额(元)',
|
||||||
|
minWidth: 140,
|
||||||
|
formatter: 'formatAmount',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'noReceivablePrice',
|
||||||
|
title: '未回款金额(元)',
|
||||||
|
minWidth: 120,
|
||||||
|
formatter: 'formatAmount',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'contactLastTime',
|
||||||
|
title: '最后跟进时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'ownerUserName',
|
||||||
|
title: '负责人',
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'ownerUserDeptName',
|
||||||
|
title: '所属部门',
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'updateTime',
|
||||||
|
title: '更新时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'createTime',
|
||||||
|
title: '创建时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'creatorName',
|
||||||
|
title: '创建人',
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'auditStatus',
|
||||||
|
title: '合同状态',
|
||||||
|
minWidth: 120,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.CRM_AUDIT_STATUS },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'operation',
|
||||||
|
title: '操作',
|
||||||
|
minWidth: 130,
|
||||||
|
align: 'center',
|
||||||
|
fixed: 'right',
|
||||||
|
cellRender: {
|
||||||
|
attrs: {
|
||||||
|
nameField: 'no',
|
||||||
|
nameTitle: '合同编号',
|
||||||
|
onClick: onActionClick,
|
||||||
|
},
|
||||||
|
name: 'CellOperation',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
code: 'processDetail',
|
||||||
|
show: hasAccessByCodes(['crm:contract:update']),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户跟进列表的搜索表单 */
|
||||||
|
export function useCustomerFollowFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'followUpStatus',
|
||||||
|
label: '状态',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
options: FOLLOWUP_STATUS,
|
||||||
|
},
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 待进入公海客户列表的搜索表单 */
|
||||||
|
export function useCustomerPutPoolFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'sceneType',
|
||||||
|
label: '归属',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
options: SCENE_TYPES,
|
||||||
|
},
|
||||||
|
defaultValue: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 今日需联系客户列表的搜索表单 */
|
||||||
|
export function useCustomerTodayContactFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'contactStatus',
|
||||||
|
label: '状态',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
options: CONTACT_STATUS,
|
||||||
|
},
|
||||||
|
defaultValue: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'sceneType',
|
||||||
|
label: '归属',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
options: SCENE_TYPES,
|
||||||
|
},
|
||||||
|
defaultValue: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 客户列表的字段 */
|
||||||
|
export function useCustomerColumns(): VxeTableGridOptions['columns'] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
title: '客户名称',
|
||||||
|
minWidth: 160,
|
||||||
|
slots: {
|
||||||
|
default: 'name',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'source',
|
||||||
|
title: '客户来源',
|
||||||
|
minWidth: 100,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.CRM_CUSTOMER_SOURCE },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'mobile',
|
||||||
|
title: '手机',
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'telephone',
|
||||||
|
title: '电话',
|
||||||
|
minWidth: 130,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'email',
|
||||||
|
title: '邮箱',
|
||||||
|
minWidth: 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'level',
|
||||||
|
title: '客户级别',
|
||||||
|
minWidth: 100,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.CRM_CUSTOMER_LEVEL },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'industryId',
|
||||||
|
title: '客户行业',
|
||||||
|
minWidth: 100,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.CRM_CUSTOMER_INDUSTRY },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'contactNextTime',
|
||||||
|
title: '下次联系时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'remark',
|
||||||
|
title: '备注',
|
||||||
|
minWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'lockStatus',
|
||||||
|
title: '锁定状态',
|
||||||
|
minWidth: 100,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'dealStatus',
|
||||||
|
title: '成交状态',
|
||||||
|
minWidth: 100,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'contactLastTime',
|
||||||
|
title: '最后跟进时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'contactLastContent',
|
||||||
|
title: '最后跟进记录',
|
||||||
|
minWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'detailAddress',
|
||||||
|
title: '地址',
|
||||||
|
minWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'poolDay',
|
||||||
|
title: '距离进入公海天数',
|
||||||
|
minWidth: 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'ownerUserName',
|
||||||
|
title: '负责人',
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'ownerUserDeptName',
|
||||||
|
title: '所属部门',
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'updateTime',
|
||||||
|
title: '更新时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'createTime',
|
||||||
|
title: '创建时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'creatorName',
|
||||||
|
title: '创建人',
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 回款审核列表的搜索表单 */
|
||||||
|
export function useReceivableAuditFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'auditStatus',
|
||||||
|
label: '合同状态',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
options: AUDIT_STATUS,
|
||||||
|
},
|
||||||
|
defaultValue: 10,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 回款审核列表的字段 */
|
||||||
|
export function useReceivableAuditColumns<T = CrmReceivableApi.Receivable>(
|
||||||
|
onActionClick: OnActionClickFn<T>,
|
||||||
|
): VxeTableGridOptions['columns'] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field: 'no',
|
||||||
|
title: '回款编号',
|
||||||
|
minWidth: 180,
|
||||||
|
fixed: 'left',
|
||||||
|
slots: {
|
||||||
|
default: 'no',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'customerName',
|
||||||
|
title: '客户名称',
|
||||||
|
minWidth: 120,
|
||||||
|
slots: {
|
||||||
|
default: 'customerName',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'contractNo',
|
||||||
|
title: '合同编号',
|
||||||
|
minWidth: 180,
|
||||||
|
slots: {
|
||||||
|
default: 'contractNo',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'returnTime',
|
||||||
|
title: '回款日期',
|
||||||
|
minWidth: 150,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'price',
|
||||||
|
title: '回款金额(元)',
|
||||||
|
minWidth: 140,
|
||||||
|
formatter: 'formatAmount',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'returnType',
|
||||||
|
title: '回款方式',
|
||||||
|
minWidth: 130,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'remark',
|
||||||
|
title: '备注',
|
||||||
|
minWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'contract.totalPrice',
|
||||||
|
title: '合同金额(元)',
|
||||||
|
minWidth: 140,
|
||||||
|
formatter: 'formatAmount',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'ownerUserName',
|
||||||
|
title: '负责人',
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'ownerUserDeptName',
|
||||||
|
title: '所属部门',
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'updateTime',
|
||||||
|
title: '更新时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'createTime',
|
||||||
|
title: '创建时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'creatorName',
|
||||||
|
title: '创建人',
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'auditStatus',
|
||||||
|
title: '回款状态',
|
||||||
|
minWidth: 120,
|
||||||
|
fixed: 'right',
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.CRM_AUDIT_STATUS },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'operation',
|
||||||
|
title: '操作',
|
||||||
|
width: 140,
|
||||||
|
fixed: 'right',
|
||||||
|
align: 'center',
|
||||||
|
cellRender: {
|
||||||
|
attrs: {
|
||||||
|
nameField: 'name',
|
||||||
|
nameTitle: '角色',
|
||||||
|
onClick: onActionClick,
|
||||||
|
},
|
||||||
|
name: 'CellOperation',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
code: 'processDetail',
|
||||||
|
text: '查看审批',
|
||||||
|
show: hasAccessByCodes(['crm:receivable:update']),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 回款计划提醒列表的搜索表单 */
|
||||||
|
export function useReceivablePlanRemindFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'remindType',
|
||||||
|
label: '合同状态',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
options: RECEIVABLE_REMIND_TYPE,
|
||||||
|
},
|
||||||
|
defaultValue: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 回款计划提醒列表的字段 */
|
||||||
|
export function useReceivablePlanRemindColumns<T = CrmReceivableApi.Receivable>(
|
||||||
|
onActionClick: OnActionClickFn<T>,
|
||||||
|
): VxeTableGridOptions['columns'] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field: 'customerName',
|
||||||
|
title: '客户名称',
|
||||||
|
minWidth: 160,
|
||||||
|
fixed: 'left',
|
||||||
|
slots: {
|
||||||
|
default: 'customerName',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'contractNo',
|
||||||
|
title: '合同编号',
|
||||||
|
minWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'period',
|
||||||
|
title: '期数',
|
||||||
|
minWidth: 160,
|
||||||
|
slots: {
|
||||||
|
default: 'period',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'price',
|
||||||
|
title: '计划回款金额(元)',
|
||||||
|
minWidth: 120,
|
||||||
|
formatter: 'formatAmount',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'returnTime',
|
||||||
|
title: '计划回款日期',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'remindDays',
|
||||||
|
title: '提前几天提醒',
|
||||||
|
minWidth: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'remindTime',
|
||||||
|
title: '提醒日期',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'returnType',
|
||||||
|
title: '回款方式',
|
||||||
|
minWidth: 120,
|
||||||
|
fixed: 'right',
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'remark',
|
||||||
|
title: '备注',
|
||||||
|
minWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'ownerUserName',
|
||||||
|
title: '负责人',
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'receivable.price',
|
||||||
|
title: '实际回款金额(元)',
|
||||||
|
minWidth: 160,
|
||||||
|
formatter: 'formatAmount',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'receivable.returnTime',
|
||||||
|
title: '实际回款日期',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'updateTime',
|
||||||
|
title: '更新时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'createTime',
|
||||||
|
title: '创建时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'creatorName',
|
||||||
|
title: '创建人',
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'operation',
|
||||||
|
title: '操作',
|
||||||
|
width: 140,
|
||||||
|
fixed: 'right',
|
||||||
|
align: 'center',
|
||||||
|
cellRender: {
|
||||||
|
attrs: {
|
||||||
|
nameField: 'customerName',
|
||||||
|
nameTitle: '客户名称',
|
||||||
|
onClick: onActionClick,
|
||||||
|
},
|
||||||
|
name: 'CellOperation',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
code: 'receivableForm',
|
||||||
|
text: '创建回款',
|
||||||
|
show: hasAccessByCodes(['crm:receivable:create']),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
@ -1,34 +1,121 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { computed, onActivated, onMounted, ref } from 'vue';
|
||||||
|
|
||||||
import { Page } from '@vben/common-ui';
|
import { Page } from '@vben/common-ui';
|
||||||
|
|
||||||
import { Button } from 'ant-design-vue';
|
import { Badge, Card, List } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import * as ClueApi from '#/api/crm/clue';
|
||||||
|
import * as ContractApi from '#/api/crm/contract';
|
||||||
|
import * as CustomerApi from '#/api/crm/customer';
|
||||||
|
import * as ReceivableApi from '#/api/crm/receivable';
|
||||||
|
import * as ReceivablePlanApi from '#/api/crm/receivable/plan';
|
||||||
import { DocAlert } from '#/components/doc-alert';
|
import { DocAlert } from '#/components/doc-alert';
|
||||||
</script>
|
|
||||||
|
|
||||||
|
import { useLeftSides } from './data';
|
||||||
|
import ClueFollowList from './modules/ClueFollowList.vue';
|
||||||
|
import ContractAuditList from './modules/ContractAuditList.vue';
|
||||||
|
import ContractRemindList from './modules/ContractRemindList.vue';
|
||||||
|
import CustomerFollowList from './modules/CustomerFollowList.vue';
|
||||||
|
import CustomerPutPoolRemindList from './modules/CustomerPutPoolRemindList.vue';
|
||||||
|
import CustomerTodayContactList from './modules/CustomerTodayContactList.vue';
|
||||||
|
import ReceivableAuditList from './modules/ReceivableAuditList.vue';
|
||||||
|
import ReceivablePlanRemindList from './modules/ReceivablePlanRemindList.vue';
|
||||||
|
|
||||||
|
defineOptions({ name: 'CrmBacklog' });
|
||||||
|
|
||||||
|
const leftMenu = ref('customerTodayContact');
|
||||||
|
|
||||||
|
const clueFollowCount = ref(0);
|
||||||
|
const customerFollowCount = ref(0);
|
||||||
|
const customerPutPoolRemindCount = ref(0);
|
||||||
|
const customerTodayContactCount = ref(0);
|
||||||
|
const contractAuditCount = ref(0);
|
||||||
|
const contractRemindCount = ref(0);
|
||||||
|
const receivableAuditCount = ref(0);
|
||||||
|
const receivablePlanRemindCount = ref(0);
|
||||||
|
|
||||||
|
const leftSides = useLeftSides(
|
||||||
|
customerTodayContactCount,
|
||||||
|
clueFollowCount,
|
||||||
|
customerFollowCount,
|
||||||
|
customerPutPoolRemindCount,
|
||||||
|
contractAuditCount,
|
||||||
|
contractRemindCount,
|
||||||
|
receivableAuditCount,
|
||||||
|
receivablePlanRemindCount,
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentComponent = computed(() => {
|
||||||
|
const components = {
|
||||||
|
customerTodayContact: CustomerTodayContactList,
|
||||||
|
clueFollow: ClueFollowList,
|
||||||
|
contractAudit: ContractAuditList,
|
||||||
|
receivableAudit: ReceivableAuditList,
|
||||||
|
contractRemind: ContractRemindList,
|
||||||
|
customerFollow: CustomerFollowList,
|
||||||
|
customerPutPoolRemind: CustomerPutPoolRemindList,
|
||||||
|
receivablePlanRemind: ReceivablePlanRemindList,
|
||||||
|
} as const;
|
||||||
|
return components[leftMenu.value as keyof typeof components];
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 侧边点击 */
|
||||||
|
function sideClick(item: { menu: string }) {
|
||||||
|
leftMenu.value = item.menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取数量 */
|
||||||
|
async function getCount() {
|
||||||
|
customerTodayContactCount.value =
|
||||||
|
await CustomerApi.getTodayContactCustomerCount();
|
||||||
|
customerPutPoolRemindCount.value =
|
||||||
|
await CustomerApi.getPutPoolRemindCustomerCount();
|
||||||
|
customerFollowCount.value = await CustomerApi.getFollowCustomerCount();
|
||||||
|
clueFollowCount.value = await ClueApi.getFollowClueCount();
|
||||||
|
contractAuditCount.value = await ContractApi.getAuditContractCount();
|
||||||
|
contractRemindCount.value = await ContractApi.getRemindContractCount();
|
||||||
|
receivableAuditCount.value = await ReceivableApi.getAuditReceivableCount();
|
||||||
|
receivablePlanRemindCount.value =
|
||||||
|
await ReceivablePlanApi.getReceivablePlanRemindCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 激活时 */
|
||||||
|
onActivated(async () => {
|
||||||
|
getCount();
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 初始化 */
|
||||||
|
onMounted(async () => {
|
||||||
|
getCount();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Page>
|
<Page auto-content-height>
|
||||||
<DocAlert
|
<template #doc>
|
||||||
title="【通用】跟进记录、待办事项"
|
<DocAlert
|
||||||
url="https://doc.iocoder.cn/crm/follow-up/"
|
title="【通用】跟进记录、待办事项"
|
||||||
/>
|
url="https://doc.iocoder.cn/crm/follow-up/"
|
||||||
<Button
|
/>
|
||||||
danger
|
</template>
|
||||||
type="link"
|
<div class="flex h-full w-full">
|
||||||
target="_blank"
|
<Card class="w-1/5">
|
||||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
<List item-layout="horizontal" :data-source="leftSides">
|
||||||
>
|
<template #renderItem="{ item }">
|
||||||
该功能支持 Vue3 + element-plus 版本!
|
<List.Item>
|
||||||
</Button>
|
<List.Item.Meta>
|
||||||
<br />
|
<template #title>
|
||||||
<Button
|
<a @click="sideClick(item)"> {{ item.name }} </a>
|
||||||
type="link"
|
</template>
|
||||||
target="_blank"
|
</List.Item.Meta>
|
||||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/crm/backlog/index"
|
<template #extra v-if="item.count.value && item.count.value > 0">
|
||||||
>
|
<Badge :count="item.count.value" />
|
||||||
可参考
|
</template>
|
||||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/crm/backlog/index
|
</List.Item>
|
||||||
代码,pull request 贡献给我们!
|
</template>
|
||||||
</Button>
|
</List>
|
||||||
|
</Card>
|
||||||
|
<component class="ml-4 w-4/5" :is="currentComponent" />
|
||||||
|
</div>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
<!-- 分配给我的线索 -->
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { CrmClueApi } from '#/api/crm/clue';
|
||||||
|
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import { Button } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { getCluePage } from '#/api/crm/clue';
|
||||||
|
|
||||||
|
import { useClueFollowColumns, useClueFollowFormSchema } from '../data';
|
||||||
|
|
||||||
|
const { push } = useRouter();
|
||||||
|
|
||||||
|
/** 打开线索详情 */
|
||||||
|
function onDetail(row: CrmClueApi.Clue) {
|
||||||
|
push({ name: 'CrmClueDetail', params: { id: row.id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Grid] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
schema: useClueFollowFormSchema(),
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns: useClueFollowColumns(),
|
||||||
|
height: 'auto',
|
||||||
|
keepSource: true,
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }, formValues) => {
|
||||||
|
return await getCluePage({
|
||||||
|
pageNo: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
transformStatus: false,
|
||||||
|
...formValues,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
refresh: { code: 'query' },
|
||||||
|
search: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Grid table-title="分配给我的线索">
|
||||||
|
<template #name="{ row }">
|
||||||
|
<Button type="link" @click="onDetail(row)">{{ row.name }}</Button>
|
||||||
|
</template>
|
||||||
|
</Grid>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
<!-- 待审核合同 -->
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { OnActionClickParams } from '#/adapter/vxe-table';
|
||||||
|
import type { CrmContractApi } from '#/api/crm/contract';
|
||||||
|
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import { Button } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { getContractPage } from '#/api/crm/contract';
|
||||||
|
|
||||||
|
import { useContractAuditFormSchema, useContractColumns } from '../data';
|
||||||
|
|
||||||
|
const { push } = useRouter();
|
||||||
|
|
||||||
|
/** 查看审批 */
|
||||||
|
function openProcessDetail(row: CrmContractApi.Contract) {
|
||||||
|
push({
|
||||||
|
name: 'BpmProcessInstanceDetail',
|
||||||
|
query: { id: row.processInstanceId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开合同详情 */
|
||||||
|
function openContractDetail(row: CrmContractApi.Contract) {
|
||||||
|
push({ name: 'CrmContractDetail', params: { id: row.id } });
|
||||||
|
}
|
||||||
|
/** 打开客户详情 */
|
||||||
|
function openCustomerDetail(row: CrmContractApi.Contract) {
|
||||||
|
push({ name: 'CrmCustomerDetail', params: { id: row.id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开联系人详情 */
|
||||||
|
function openContactDetail(row: CrmContractApi.Contract) {
|
||||||
|
push({ name: 'CrmContactDetail', params: { id: row.id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开商机详情 */
|
||||||
|
function openBusinessDetail(row: CrmContractApi.Contract) {
|
||||||
|
push({ name: 'CrmBusinessDetail', params: { id: row.id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 表格操作按钮的回调函数 */
|
||||||
|
function onActionClick({
|
||||||
|
code,
|
||||||
|
row,
|
||||||
|
}: OnActionClickParams<CrmContractApi.Contract>) {
|
||||||
|
switch (code) {
|
||||||
|
case 'processDetail': {
|
||||||
|
openProcessDetail(row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Grid] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
schema: useContractAuditFormSchema(),
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns: useContractColumns(onActionClick),
|
||||||
|
height: 'auto',
|
||||||
|
keepSource: true,
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }, formValues) => {
|
||||||
|
return await getContractPage({
|
||||||
|
pageNo: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
sceneType: 1, // 我负责的
|
||||||
|
...formValues,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
refresh: { code: 'query' },
|
||||||
|
search: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Grid table-title="待审核合同">
|
||||||
|
<template #name="{ row }">
|
||||||
|
<Button type="link" @click="openContractDetail(row)">
|
||||||
|
{{ row.name }}
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
<template #customerName="{ row }">
|
||||||
|
<Button type="link" @click="openCustomerDetail(row)">
|
||||||
|
{{ row.customerName }}
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
<template #businessName="{ row }">
|
||||||
|
<Button type="link" @click="openBusinessDetail(row)">
|
||||||
|
{{ row.businessName }}
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
<template #contactName="{ row }">
|
||||||
|
<Button type="link" @click="openContactDetail(row)">
|
||||||
|
{{ row.contactName }}
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
</Grid>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
<!-- 即将到期的合同 -->
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { OnActionClickParams } from '#/adapter/vxe-table';
|
||||||
|
import type { CrmContractApi } from '#/api/crm/contract';
|
||||||
|
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import { Button } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { getContractPage } from '#/api/crm/contract';
|
||||||
|
|
||||||
|
import { useContractColumns, useContractRemindFormSchema } from '../data';
|
||||||
|
|
||||||
|
const { push } = useRouter();
|
||||||
|
|
||||||
|
/** 查看审批 */
|
||||||
|
function openProcessDetail(row: CrmContractApi.Contract) {
|
||||||
|
push({
|
||||||
|
name: 'BpmProcessInstanceDetail',
|
||||||
|
query: { id: row.processInstanceId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开合同详情 */
|
||||||
|
function openContractDetail(row: CrmContractApi.Contract) {
|
||||||
|
push({ name: 'CrmContractDetail', params: { id: row.id } });
|
||||||
|
}
|
||||||
|
/** 打开客户详情 */
|
||||||
|
function openCustomerDetail(row: CrmContractApi.Contract) {
|
||||||
|
push({ name: 'CrmCustomerDetail', params: { id: row.id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开联系人详情 */
|
||||||
|
function openContactDetail(row: CrmContractApi.Contract) {
|
||||||
|
push({ name: 'CrmContactDetail', params: { id: row.id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开商机详情 */
|
||||||
|
function openBusinessDetail(row: CrmContractApi.Contract) {
|
||||||
|
push({ name: 'CrmBusinessDetail', params: { id: row.id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 表格操作按钮的回调函数 */
|
||||||
|
function onActionClick({
|
||||||
|
code,
|
||||||
|
row,
|
||||||
|
}: OnActionClickParams<CrmContractApi.Contract>) {
|
||||||
|
switch (code) {
|
||||||
|
case 'processDetail': {
|
||||||
|
openProcessDetail(row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Grid] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
schema: useContractRemindFormSchema(),
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns: useContractColumns(onActionClick),
|
||||||
|
height: 'auto',
|
||||||
|
keepSource: true,
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }, formValues) => {
|
||||||
|
return await getContractPage({
|
||||||
|
pageNo: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
sceneType: 1, // 自己负责的
|
||||||
|
...formValues,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
refresh: { code: 'query' },
|
||||||
|
search: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Grid table-title="即将到期的合同">
|
||||||
|
<template #name="{ row }">
|
||||||
|
<Button type="link" @click="openContractDetail(row)">
|
||||||
|
{{ row.name }}
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
<template #customerName="{ row }">
|
||||||
|
<Button type="link" @click="openCustomerDetail(row)">
|
||||||
|
{{ row.customerName }}
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
<template #businessName="{ row }">
|
||||||
|
<Button type="link" @click="openBusinessDetail(row)">
|
||||||
|
{{ row.businessName }}
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
<template #contactName="{ row }">
|
||||||
|
<Button type="link" @click="openContactDetail(row)">
|
||||||
|
{{ row.contactName }}
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
</Grid>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
<!-- 分配给我的客户 -->
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { CrmCustomerApi } from '#/api/crm/customer';
|
||||||
|
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import { Button } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { getCustomerPage } from '#/api/crm/customer';
|
||||||
|
|
||||||
|
import { useCustomerColumns, useCustomerFollowFormSchema } from '../data';
|
||||||
|
|
||||||
|
const { push } = useRouter();
|
||||||
|
|
||||||
|
/** 打开客户详情 */
|
||||||
|
function onDetail(row: CrmCustomerApi.Customer) {
|
||||||
|
push({ name: 'CrmCustomerDetail', params: { id: row.id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Grid] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
schema: useCustomerFollowFormSchema(),
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns: useCustomerColumns(),
|
||||||
|
height: 'auto',
|
||||||
|
keepSource: true,
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }, formValues) => {
|
||||||
|
return await getCustomerPage({
|
||||||
|
pageNo: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
sceneType: 1,
|
||||||
|
...formValues,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
refresh: { code: 'query' },
|
||||||
|
search: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Grid table-title="分配给我的客户">
|
||||||
|
<template #name="{ row }">
|
||||||
|
<Button type="link" @click="onDetail(row)">{{ row.name }}</Button>
|
||||||
|
</template>
|
||||||
|
</Grid>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
<!-- 待进入公海的客户 -->
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { CrmCustomerApi } from '#/api/crm/customer';
|
||||||
|
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import { Button } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { getCustomerPage } from '#/api/crm/customer';
|
||||||
|
|
||||||
|
import { useCustomerColumns, useCustomerPutPoolFormSchema } from '../data';
|
||||||
|
|
||||||
|
const { push } = useRouter();
|
||||||
|
|
||||||
|
/** 打开客户详情 */
|
||||||
|
function onDetail(row: CrmCustomerApi.Customer) {
|
||||||
|
push({ name: 'CrmCustomerDetail', params: { id: row.id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Grid] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
schema: useCustomerPutPoolFormSchema(),
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns: useCustomerColumns(),
|
||||||
|
height: 'auto',
|
||||||
|
keepSource: true,
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }, formValues) => {
|
||||||
|
return await getCustomerPage({
|
||||||
|
pageNo: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
pool: true, // 固定 公海参数为 true
|
||||||
|
...formValues,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
refresh: { code: 'query' },
|
||||||
|
search: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Grid table-title="待进入公海的客户">
|
||||||
|
<template #name="{ row }">
|
||||||
|
<Button type="link" @click="onDetail(row)">{{ row.name }}</Button>
|
||||||
|
</template>
|
||||||
|
</Grid>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
<!-- 今日需联系客户 -->
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { CrmCustomerApi } from '#/api/crm/customer';
|
||||||
|
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import { Button } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { getCustomerPage } from '#/api/crm/customer';
|
||||||
|
|
||||||
|
import { useCustomerColumns, useCustomerTodayContactFormSchema } from '../data';
|
||||||
|
|
||||||
|
const { push } = useRouter();
|
||||||
|
|
||||||
|
/** 打开客户详情 */
|
||||||
|
function onDetail(row: CrmCustomerApi.Customer) {
|
||||||
|
push({ name: 'CrmCustomerDetail', params: { id: row.id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Grid] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
schema: useCustomerTodayContactFormSchema(),
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns: useCustomerColumns(),
|
||||||
|
height: 'auto',
|
||||||
|
keepSource: true,
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }, formValues) => {
|
||||||
|
return await getCustomerPage({
|
||||||
|
pageNo: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
pool: null, // 是否公海数据
|
||||||
|
...formValues,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
refresh: { code: 'query' },
|
||||||
|
search: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Grid table-title="今日需联系客户">
|
||||||
|
<template #name="{ row }">
|
||||||
|
<Button type="link" @click="onDetail(row)">{{ row.name }}</Button>
|
||||||
|
</template>
|
||||||
|
</Grid>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
<!-- 待审核回款 -->
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { OnActionClickParams } from '#/adapter/vxe-table';
|
||||||
|
import type { CrmReceivableApi } from '#/api/crm/receivable';
|
||||||
|
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import { Button } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { getReceivablePage } from '#/api/crm/receivable';
|
||||||
|
|
||||||
|
import {
|
||||||
|
useReceivableAuditColumns,
|
||||||
|
useReceivableAuditFormSchema,
|
||||||
|
} from '../data';
|
||||||
|
|
||||||
|
const { push } = useRouter();
|
||||||
|
|
||||||
|
/** 查看审批 */
|
||||||
|
function openProcessDetail(row: CrmReceivableApi.Receivable) {
|
||||||
|
push({
|
||||||
|
name: 'BpmProcessInstanceDetail',
|
||||||
|
query: { id: row.processInstanceId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开回款详情 */
|
||||||
|
function openDetail(row: CrmReceivableApi.Receivable) {
|
||||||
|
push({ name: 'CrmReceivableDetail', params: { id: row.id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开客户详情 */
|
||||||
|
function openCustomerDetail(row: CrmReceivableApi.Receivable) {
|
||||||
|
push({ name: 'CrmCustomerDetail', params: { id: row.customerId } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开合同详情 */
|
||||||
|
function openContractDetail(row: CrmReceivableApi.Receivable) {
|
||||||
|
push({ name: 'CrmContractDetail', params: { id: row.contractId } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 表格操作按钮的回调函数 */
|
||||||
|
function onActionClick({
|
||||||
|
code,
|
||||||
|
row,
|
||||||
|
}: OnActionClickParams<CrmReceivableApi.Receivable>) {
|
||||||
|
switch (code) {
|
||||||
|
case 'processDetail': {
|
||||||
|
openProcessDetail(row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Grid] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
schema: useReceivableAuditFormSchema(),
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns: useReceivableAuditColumns(onActionClick),
|
||||||
|
height: 'auto',
|
||||||
|
keepSource: true,
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }, formValues) => {
|
||||||
|
return await getReceivablePage({
|
||||||
|
pageNo: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
...formValues,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
refresh: { code: 'query' },
|
||||||
|
search: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Grid table-title="待审核回款">
|
||||||
|
<template #no="{ row }">
|
||||||
|
<Button type="link" @click="openDetail(row)">
|
||||||
|
{{ row.no }}
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
<template #customerName="{ row }">
|
||||||
|
<Button type="link" @click="openCustomerDetail(row)">
|
||||||
|
{{ row.customerName }}
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
<template #contractNo="{ row }">
|
||||||
|
<Button type="link" @click="openContractDetail(row)">
|
||||||
|
{{ row.contractNo }}
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
</Grid>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
<!-- 待回款提醒 -->
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { OnActionClickParams } from '#/adapter/vxe-table';
|
||||||
|
import type { CrmReceivableApi } from '#/api/crm/receivable';
|
||||||
|
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import { Button } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { getReceivablePage } from '#/api/crm/receivable';
|
||||||
|
|
||||||
|
import {
|
||||||
|
useReceivablePlanRemindColumns,
|
||||||
|
useReceivablePlanRemindFormSchema,
|
||||||
|
} from '../data';
|
||||||
|
|
||||||
|
const { push } = useRouter();
|
||||||
|
|
||||||
|
/** 打开回款详情 */
|
||||||
|
function openDetail(row: CrmReceivableApi.Receivable) {
|
||||||
|
push({ name: 'CrmReceivableDetail', params: { id: row.id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开客户详情 */
|
||||||
|
function openCustomerDetail(row: CrmReceivableApi.Receivable) {
|
||||||
|
push({ name: 'CrmCustomerDetail', params: { id: row.customerId } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 创建回款 */
|
||||||
|
function openReceivableForm(row: CrmReceivableApi.Receivable) {
|
||||||
|
// Todo: 打开创建回款
|
||||||
|
push({ name: 'CrmCustomerDetail', params: { id: row.customerId } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 表格操作按钮的回调函数 */
|
||||||
|
function onActionClick({
|
||||||
|
code,
|
||||||
|
row,
|
||||||
|
}: OnActionClickParams<CrmReceivableApi.Receivable>) {
|
||||||
|
switch (code) {
|
||||||
|
case 'receivableForm': {
|
||||||
|
openReceivableForm(row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Grid] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
schema: useReceivablePlanRemindFormSchema(),
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns: useReceivablePlanRemindColumns(onActionClick),
|
||||||
|
height: 'auto',
|
||||||
|
keepSource: true,
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }, formValues) => {
|
||||||
|
return await getReceivablePage({
|
||||||
|
pageNo: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
...formValues,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
refresh: { code: 'query' },
|
||||||
|
search: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Grid table-title="待回款提醒">
|
||||||
|
<template #customerName="{ row }">
|
||||||
|
<Button type="link" @click="openCustomerDetail(row)">
|
||||||
|
{{ row.customerName }}
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
<template #period="{ row }">
|
||||||
|
<Button type="link" @click="openDetail(row)">{{ row.period }}</Button>
|
||||||
|
</template>
|
||||||
|
</Grid>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
|
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
import type { CrmBusinessStatusApi } from '#/api/crm/business/status';
|
||||||
|
|
||||||
|
import { useAccess } from '@vben/access';
|
||||||
|
import { getRangePickerDefaultProps } from '@vben/utils';
|
||||||
|
|
||||||
|
import { z } from '#/adapter/form';
|
||||||
|
import { CommonStatusEnum } from '#/utils/constants';
|
||||||
|
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||||
|
|
||||||
|
const { hasAccessByCodes } = useAccess();
|
||||||
|
|
||||||
|
/** 新增/修改的表单 */
|
||||||
|
export function useFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'id',
|
||||||
|
component: 'Input',
|
||||||
|
dependencies: {
|
||||||
|
triggerFields: [''],
|
||||||
|
show: () => false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'name',
|
||||||
|
label: '状态组名',
|
||||||
|
component: 'Input',
|
||||||
|
rules: 'required',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'deptIds',
|
||||||
|
label: '应用部门',
|
||||||
|
component: 'TreeSelect',
|
||||||
|
componentProps: {
|
||||||
|
multiple: true,
|
||||||
|
treeCheckable: true,
|
||||||
|
showCheckedStrategy: 'SHOW_PARENT',
|
||||||
|
placeholder: '请选择应用部门',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'status',
|
||||||
|
label: '状态',
|
||||||
|
component: 'RadioGroup',
|
||||||
|
componentProps: {
|
||||||
|
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||||
|
buttonStyle: 'solid',
|
||||||
|
optionType: 'button',
|
||||||
|
},
|
||||||
|
rules: z.number().default(CommonStatusEnum.ENABLE),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 列表的搜索表单 */
|
||||||
|
export function useGridFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'name',
|
||||||
|
label: '状态组名',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'createTime',
|
||||||
|
label: '创建时间',
|
||||||
|
component: 'RangePicker',
|
||||||
|
componentProps: {
|
||||||
|
...getRangePickerDefaultProps(),
|
||||||
|
allowClear: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 列表的字段 */
|
||||||
|
export function useGridColumns<T = CrmBusinessStatusApi.BusinessStatus>(
|
||||||
|
onActionClick: OnActionClickFn<T>,
|
||||||
|
): VxeTableGridOptions['columns'] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
title: '状态组名',
|
||||||
|
minWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'deptNames',
|
||||||
|
title: '应用部门',
|
||||||
|
minWidth: 200,
|
||||||
|
formatter: ({ cellValue }) => {
|
||||||
|
return cellValue?.length > 0 ? cellValue.join(' ') : '全公司';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'creator',
|
||||||
|
title: '创建人',
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'createTime',
|
||||||
|
title: '创建时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'operation',
|
||||||
|
title: '操作',
|
||||||
|
width: 160,
|
||||||
|
fixed: 'right',
|
||||||
|
align: 'center',
|
||||||
|
cellRender: {
|
||||||
|
name: 'TableAction',
|
||||||
|
props: {
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: '编辑',
|
||||||
|
code: 'edit',
|
||||||
|
show: hasAccessByCodes(['crm:business-status:update']),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
code: 'delete',
|
||||||
|
show: hasAccessByCodes(['crm:business-status:delete']),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onActionClick,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
@ -1,38 +1,137 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Page } from '@vben/common-ui';
|
import type {
|
||||||
|
OnActionClickParams,
|
||||||
|
VxeTableGridOptions,
|
||||||
|
} from '#/adapter/vxe-table';
|
||||||
|
import type { CrmBusinessStatusApi } from '#/api/crm/business/status';
|
||||||
|
|
||||||
import { Button } from 'ant-design-vue';
|
import { Page, useVbenModal } from '@vben/common-ui';
|
||||||
|
import { Plus } from '@vben/icons';
|
||||||
|
|
||||||
|
import { Button, message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import {
|
||||||
|
deleteBusinessStatus,
|
||||||
|
getBusinessStatusPage,
|
||||||
|
} from '#/api/crm/business/status';
|
||||||
import { DocAlert } from '#/components/doc-alert';
|
import { DocAlert } from '#/components/doc-alert';
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
|
import { useGridColumns, useGridFormSchema } from './data';
|
||||||
|
import Form from './modules/form.vue';
|
||||||
|
|
||||||
|
const [FormModal, formModalApi] = useVbenModal({
|
||||||
|
connectedComponent: Form,
|
||||||
|
destroyOnClose: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 刷新表格 */
|
||||||
|
function onRefresh() {
|
||||||
|
gridApi.query();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 创建商机状态 */
|
||||||
|
function onCreate() {
|
||||||
|
formModalApi.setData(null).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除商机状态 */
|
||||||
|
async function onDelete(row: CrmBusinessStatusApi.BusinessStatus) {
|
||||||
|
const hideLoading = message.loading({
|
||||||
|
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||||
|
duration: 0,
|
||||||
|
key: 'action_process_msg',
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await deleteBusinessStatus(row.id as number);
|
||||||
|
message.success({
|
||||||
|
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||||
|
key: 'action_process_msg',
|
||||||
|
});
|
||||||
|
onRefresh();
|
||||||
|
} catch {
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 编辑商机状态 */
|
||||||
|
function onEdit(row: CrmBusinessStatusApi.BusinessStatus) {
|
||||||
|
formModalApi.setData(row).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 表格操作按钮的回调函数 */
|
||||||
|
function onActionClick({
|
||||||
|
code,
|
||||||
|
row,
|
||||||
|
}: OnActionClickParams<CrmBusinessStatusApi.BusinessStatus>) {
|
||||||
|
switch (code) {
|
||||||
|
case 'delete': {
|
||||||
|
onDelete(row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'edit': {
|
||||||
|
onEdit(row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
schema: useGridFormSchema(),
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns: useGridColumns(onActionClick),
|
||||||
|
height: 'auto',
|
||||||
|
keepSource: true,
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }, formValues) => {
|
||||||
|
return await getBusinessStatusPage({
|
||||||
|
page: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
...formValues,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
refresh: { code: 'query' },
|
||||||
|
search: true,
|
||||||
|
},
|
||||||
|
} as VxeTableGridOptions<CrmBusinessStatusApi.BusinessStatus>,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page>
|
<Page auto-content-height>
|
||||||
<DocAlert
|
<template #doc>
|
||||||
title="【商机】商机管理、商机状态"
|
<DocAlert
|
||||||
url="https://doc.iocoder.cn/crm/business/"
|
title="【商机】商机管理、商机状态"
|
||||||
/>
|
url="https://doc.iocoder.cn/crm/business/"
|
||||||
<DocAlert
|
/>
|
||||||
title="【通用】数据权限"
|
<DocAlert
|
||||||
url="https://doc.iocoder.cn/crm/permission/"
|
title="【通用】数据权限"
|
||||||
/>
|
url="https://doc.iocoder.cn/crm/permission/"
|
||||||
<Button
|
/>
|
||||||
danger
|
</template>
|
||||||
type="link"
|
|
||||||
target="_blank"
|
<FormModal @success="onRefresh" />
|
||||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
<Grid table-title="商机状态列表">
|
||||||
>
|
<template #toolbar-tools>
|
||||||
该功能支持 Vue3 + element-plus 版本!
|
<Button
|
||||||
</Button>
|
type="primary"
|
||||||
<br />
|
@click="onCreate"
|
||||||
<Button
|
v-access:code="['crm:business-status:create']"
|
||||||
type="link"
|
>
|
||||||
target="_blank"
|
<Plus class="size-5" />
|
||||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/crm/business/status/index"
|
{{ $t('ui.actionTitle.create', ['商机状态']) }}
|
||||||
>
|
</Button>
|
||||||
可参考
|
</template>
|
||||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/crm/business/status/index
|
</Grid>
|
||||||
代码,pull request 贡献给我们!
|
|
||||||
</Button>
|
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { CrmBusinessStatusApi } from '#/api/crm/business/status';
|
||||||
|
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
import {
|
||||||
|
createBusinessStatus,
|
||||||
|
getBusinessStatus,
|
||||||
|
updateBusinessStatus,
|
||||||
|
} from '#/api/crm/business/status';
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
|
import { useFormSchema } from '../data';
|
||||||
|
|
||||||
|
const emit = defineEmits(['success']);
|
||||||
|
const formData = ref<CrmBusinessStatusApi.BusinessStatusType>();
|
||||||
|
const getTitle = computed(() => {
|
||||||
|
return formData.value?.id
|
||||||
|
? $t('ui.actionTitle.edit', ['商机状态'])
|
||||||
|
: $t('ui.actionTitle.create', ['商机状态']);
|
||||||
|
});
|
||||||
|
|
||||||
|
const [Form, formApi] = useVbenForm({
|
||||||
|
commonConfig: {
|
||||||
|
componentProps: {
|
||||||
|
class: 'w-full',
|
||||||
|
},
|
||||||
|
formItemClass: 'col-span-2',
|
||||||
|
labelWidth: 80,
|
||||||
|
},
|
||||||
|
layout: 'horizontal',
|
||||||
|
schema: useFormSchema(),
|
||||||
|
showDefaultActions: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
async onConfirm() {
|
||||||
|
const { valid } = await formApi.validate();
|
||||||
|
if (!valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
modalApi.lock();
|
||||||
|
// 提交表单
|
||||||
|
const data =
|
||||||
|
(await formApi.getValues()) as CrmBusinessStatusApi.BusinessStatusType;
|
||||||
|
try {
|
||||||
|
await (formData.value?.id
|
||||||
|
? updateBusinessStatus(data)
|
||||||
|
: createBusinessStatus(data));
|
||||||
|
// 关闭并提示
|
||||||
|
await modalApi.close();
|
||||||
|
emit('success');
|
||||||
|
message.success({
|
||||||
|
content: $t('ui.actionMessage.operationSuccess'),
|
||||||
|
key: 'action_process_msg',
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
modalApi.unlock();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
if (!isOpen) {
|
||||||
|
formData.value = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 加载数据
|
||||||
|
const data = modalApi.getData<CrmBusinessStatusApi.BusinessStatusType>();
|
||||||
|
if (!data || !data.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
modalApi.lock();
|
||||||
|
try {
|
||||||
|
formData.value = await getBusinessStatus(data.id as number);
|
||||||
|
// 设置到 values
|
||||||
|
if (formData.value) {
|
||||||
|
await formApi.setValues(formData.value);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
modalApi.unlock();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal :title="getTitle" class="w-1/2">
|
||||||
|
<Form class="mx-4" />
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
<script lang="ts" setup>
|
|
||||||
import { Page } from '@vben/common-ui';
|
|
||||||
|
|
||||||
import { Button, Card, message, notification, Space } from 'ant-design-vue';
|
|
||||||
|
|
||||||
type NotificationType = 'error' | 'info' | 'success' | 'warning';
|
|
||||||
|
|
||||||
function info() {
|
|
||||||
message.info('How many roads must a man walk down');
|
|
||||||
}
|
|
||||||
|
|
||||||
function error() {
|
|
||||||
message.error({
|
|
||||||
content: 'Once upon a time you dressed so fine',
|
|
||||||
duration: 2500,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function warning() {
|
|
||||||
message.warning('How many roads must a man walk down');
|
|
||||||
}
|
|
||||||
function success() {
|
|
||||||
message.success('Cause you walked hand in hand With another man in my place');
|
|
||||||
}
|
|
||||||
|
|
||||||
function notify(type: NotificationType) {
|
|
||||||
notification[type]({
|
|
||||||
duration: 2500,
|
|
||||||
message: '说点啥呢',
|
|
||||||
type,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Page
|
|
||||||
description="支持多语言,主题功能集成切换等"
|
|
||||||
title="Ant Design Vue组件使用演示"
|
|
||||||
>
|
|
||||||
<Card class="mb-5" title="按钮">
|
|
||||||
<Space>
|
|
||||||
<Button>Default</Button>
|
|
||||||
<Button type="primary"> Primary </Button>
|
|
||||||
<Button> Info </Button>
|
|
||||||
<Button danger> Error </Button>
|
|
||||||
</Space>
|
|
||||||
</Card>
|
|
||||||
<Card class="mb-5" title="Message">
|
|
||||||
<Space>
|
|
||||||
<Button @click="info"> 信息 </Button>
|
|
||||||
<Button danger @click="error"> 错误 </Button>
|
|
||||||
<Button @click="warning"> 警告 </Button>
|
|
||||||
<Button @click="success"> 成功 </Button>
|
|
||||||
</Space>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card class="mb-5" title="Notification">
|
|
||||||
<Space>
|
|
||||||
<Button @click="notify('info')"> 信息 </Button>
|
|
||||||
<Button danger @click="notify('error')"> 错误 </Button>
|
|
||||||
<Button @click="notify('warning')"> 警告 </Button>
|
|
||||||
<Button @click="notify('success')"> 成功 </Button>
|
|
||||||
</Space>
|
|
||||||
</Card>
|
|
||||||
</Page>
|
|
||||||
</template>
|
|
||||||
|
|
@ -28,7 +28,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
try {
|
try {
|
||||||
formData.value = data;
|
formData.value = data;
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
try {
|
try {
|
||||||
formData.value = data;
|
formData.value = data;
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -72,7 +72,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
// 设置到 values
|
// 设置到 values
|
||||||
await formApi.setValues(formData.value);
|
await formApi.setValues(formData.value);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -79,7 +79,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
// 设置到 values
|
// 设置到 values
|
||||||
await formApi.setValues(formData.value);
|
await formApi.setValues(formData.value);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -78,7 +78,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
try {
|
try {
|
||||||
data = await getDemo01Contact(data.id);
|
data = await getDemo01Contact(data.id);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 设置到 values
|
// 设置到 values
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -86,7 +86,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
try {
|
try {
|
||||||
data = await getDemo02Category(data.id);
|
data = await getDemo02Category(data.id);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 设置到 values
|
// 设置到 values
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -80,7 +80,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
try {
|
try {
|
||||||
data = await getDemo03Course(data.id);
|
data = await getDemo03Course(data.id);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 设置到 values
|
// 设置到 values
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -80,7 +80,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
try {
|
try {
|
||||||
data = await getDemo03Grade(data.id);
|
data = await getDemo03Grade(data.id);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 设置到 values
|
// 设置到 values
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -78,7 +78,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
try {
|
try {
|
||||||
data = await getDemo03Student(data.id);
|
data = await getDemo03Student(data.id);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 设置到 values
|
// 设置到 values
|
||||||
|
|
|
||||||
|
|
@ -61,9 +61,8 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
// 提交表单
|
// 提交表单
|
||||||
const data = (await formApi.getValues()) as Demo03StudentApi.Demo03Student;
|
const data = (await formApi.getValues()) as Demo03StudentApi.Demo03Student;
|
||||||
// 拼接子表的数据
|
// 拼接子表的数据
|
||||||
// TODO @puhui999:字段对不上
|
data.demo03courses = demo03CourseFormRef.value?.getData();
|
||||||
data.demo03Courses = demo03CourseFormRef.value?.getData();
|
data.demo03grade = await demo03GradeFormRef.value?.getValues();
|
||||||
data.demo03Grade = await demo03GradeFormRef.value?.getValues();
|
|
||||||
try {
|
try {
|
||||||
await (formData.value?.id
|
await (formData.value?.id
|
||||||
? updateDemo03Student(data)
|
? updateDemo03Student(data)
|
||||||
|
|
@ -76,7 +75,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -95,7 +94,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
try {
|
try {
|
||||||
data = await getDemo03Student(data.id);
|
data = await getDemo03Student(data.id);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 设置到 values
|
// 设置到 values
|
||||||
|
|
|
||||||
|
|
@ -61,8 +61,8 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
// 提交表单
|
// 提交表单
|
||||||
const data = (await formApi.getValues()) as Demo03StudentApi.Demo03Student;
|
const data = (await formApi.getValues()) as Demo03StudentApi.Demo03Student;
|
||||||
// 拼接子表的数据
|
// 拼接子表的数据
|
||||||
data.demo03Courses = demo03CourseFormRef.value?.getData();
|
data.demo03courses = demo03CourseFormRef.value?.getData();
|
||||||
data.demo03Grade = await demo03GradeFormRef.value?.getValues();
|
data.demo03grade = await demo03GradeFormRef.value?.getValues();
|
||||||
try {
|
try {
|
||||||
await (formData.value?.id
|
await (formData.value?.id
|
||||||
? updateDemo03Student(data)
|
? updateDemo03Student(data)
|
||||||
|
|
@ -75,7 +75,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -94,7 +94,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
try {
|
try {
|
||||||
data = await getDemo03Student(data.id);
|
data = await getDemo03Student(data.id);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 设置到 values
|
// 设置到 values
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -101,7 +101,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
try {
|
try {
|
||||||
data = await getDemo01Contact(data.id);
|
data = await getDemo01Contact(data.id);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
formData.value = data;
|
formData.value = data;
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -78,7 +78,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
// 设置到 values
|
// 设置到 values
|
||||||
await formApi.setValues(formData.value);
|
await formApi.setValues(formData.value);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
try {
|
try {
|
||||||
formData.value = await getJobLog(data.id);
|
formData.value = await getJobLog(data.id);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
// 获取下一次执行时间
|
// 获取下一次执行时间
|
||||||
nextTimes.value = await getJobNextTimes(data.id);
|
nextTimes.value = await getJobNextTimes(data.id);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -72,7 +72,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
// 设置到 values
|
// 设置到 values
|
||||||
await formApi.setValues(formData.value);
|
await formApi.setValues(formData.value);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,187 @@
|
||||||
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
|
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
|
||||||
|
import { useAccess } from '@vben/access';
|
||||||
|
|
||||||
|
import { getAppList } from '#/api/pay/app';
|
||||||
|
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||||
|
|
||||||
|
const { hasAccessByCodes } = useAccess();
|
||||||
|
|
||||||
|
/** 列表的搜索表单 */
|
||||||
|
export function useGridFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'appId',
|
||||||
|
label: '应用编号',
|
||||||
|
component: 'ApiSelect',
|
||||||
|
componentProps: {
|
||||||
|
api: async () => {
|
||||||
|
const data = await getAppList();
|
||||||
|
return data.map((item) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
autoSelect: 'first',
|
||||||
|
placeholder: '请选择数据源',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'type',
|
||||||
|
label: '通知类型',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
options: getDictOptions(DICT_TYPE.PAY_NOTIFY_TYPE, 'number'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'dataId',
|
||||||
|
label: '关联编号',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'status',
|
||||||
|
label: '通知状态',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
options: getDictOptions(DICT_TYPE.PAY_NOTIFY_STATUS, 'number'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'merchantOrderId',
|
||||||
|
label: '商户订单编号',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'createTime',
|
||||||
|
label: '创建时间',
|
||||||
|
component: 'DatePicker',
|
||||||
|
componentProps: {
|
||||||
|
type: 'daterange',
|
||||||
|
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||||
|
defaultTime: [new Date('1 00:00:00'), new Date('1 23:59:59')],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 列表的字段 */
|
||||||
|
export function useGridColumns<T = any>(
|
||||||
|
onActionClick: OnActionClickFn<T>,
|
||||||
|
): VxeTableGridOptions['columns'] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field: 'id',
|
||||||
|
title: '任务编号',
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'appName',
|
||||||
|
title: '应用编号',
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'merchantOrderId',
|
||||||
|
title: '商户订单编号',
|
||||||
|
minWidth: 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'type',
|
||||||
|
title: '通知类型',
|
||||||
|
minWidth: 120,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.PAY_NOTIFY_TYPE },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'dataId',
|
||||||
|
title: '关联编号',
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'status',
|
||||||
|
title: '通知状态',
|
||||||
|
minWidth: 120,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.PAY_NOTIFY_STATUS },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'lastExecuteTime',
|
||||||
|
title: '最后通知时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'nextNotifyTime',
|
||||||
|
title: '下次通知时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'notifyTimes',
|
||||||
|
title: '通知次数',
|
||||||
|
minWidth: 120,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellTag',
|
||||||
|
props: {
|
||||||
|
type: 'success',
|
||||||
|
content: '{notifyTimes} / {maxNotifyTimes}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'operation',
|
||||||
|
title: '操作',
|
||||||
|
minWidth: 100,
|
||||||
|
align: 'center',
|
||||||
|
fixed: 'right',
|
||||||
|
cellRender: {
|
||||||
|
attrs: {
|
||||||
|
onClick: onActionClick,
|
||||||
|
},
|
||||||
|
name: 'CellOperation',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
code: 'detail',
|
||||||
|
show: hasAccessByCodes(['pay:notify:query']),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 详情列表的字段 */
|
||||||
|
export const detailColumns = [
|
||||||
|
{
|
||||||
|
label: '日志编号',
|
||||||
|
prop: 'id',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '通知状态',
|
||||||
|
prop: 'status',
|
||||||
|
key: 'status',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '通知次数',
|
||||||
|
prop: 'notifyTimes',
|
||||||
|
key: 'notifyTimes',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '通知时间',
|
||||||
|
prop: 'lastExecuteTime',
|
||||||
|
key: 'lastExecuteTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '响应结果',
|
||||||
|
prop: 'response',
|
||||||
|
key: 'response',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
@ -1,31 +1,78 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Page } from '@vben/common-ui';
|
import type {
|
||||||
|
OnActionClickParams,
|
||||||
|
VxeTableGridOptions,
|
||||||
|
} from '#/adapter/vxe-table';
|
||||||
|
|
||||||
import { Button } from 'ant-design-vue';
|
import { Page, useVbenModal } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import * as PayNotifyApi from '#/api/pay/notify';
|
||||||
import { DocAlert } from '#/components/doc-alert';
|
import { DocAlert } from '#/components/doc-alert';
|
||||||
</script>
|
|
||||||
|
|
||||||
|
import { useGridColumns, useGridFormSchema } from './data';
|
||||||
|
import Detail from './modules/detail.vue';
|
||||||
|
|
||||||
|
const [NotifyDetailModal, notifyDetailModalApi] = useVbenModal({
|
||||||
|
connectedComponent: Detail,
|
||||||
|
destroyOnClose: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 刷新表格 */
|
||||||
|
function onRefresh() {
|
||||||
|
gridApi.query();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查看详情 */
|
||||||
|
function onDetail(row: any) {
|
||||||
|
notifyDetailModalApi.setData(row).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 表格操作按钮的回调函数 */
|
||||||
|
function onActionClick({ code, row }: OnActionClickParams<any>) {
|
||||||
|
switch (code) {
|
||||||
|
case 'detail': {
|
||||||
|
onDetail(row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
schema: useGridFormSchema(),
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns: useGridColumns(onActionClick),
|
||||||
|
height: 'auto',
|
||||||
|
keepSource: true,
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }, formValues) => {
|
||||||
|
return await PayNotifyApi.getNotifyTaskPage({
|
||||||
|
pageNo: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
...formValues,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
refresh: { code: 'query' },
|
||||||
|
search: true,
|
||||||
|
},
|
||||||
|
} as VxeTableGridOptions<any>,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Page>
|
<Page auto-content-height>
|
||||||
<DocAlert title="支付功能开启" url="https://doc.iocoder.cn/pay/build/" />
|
<template #doc>
|
||||||
<Button
|
<DocAlert title="支付功能开启" url="https://doc.iocoder.cn/pay/build/" />
|
||||||
danger
|
</template>
|
||||||
type="link"
|
<NotifyDetailModal @success="onRefresh" />
|
||||||
target="_blank"
|
<Grid table-title="支付通知列表" />
|
||||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
|
||||||
>
|
|
||||||
该功能支持 Vue3 + element-plus 版本!
|
|
||||||
</Button>
|
|
||||||
<br />
|
|
||||||
<Button
|
|
||||||
type="link"
|
|
||||||
target="_blank"
|
|
||||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/pay/notify/index"
|
|
||||||
>
|
|
||||||
可参考
|
|
||||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/pay/notify/index
|
|
||||||
代码,pull request 贡献给我们!
|
|
||||||
</Button>
|
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
import { formatDateTime } from '@vben/utils';
|
||||||
|
|
||||||
|
import { Descriptions, Divider, Table, Tag } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { getNotifyTaskDetail } from '#/api/pay/notify';
|
||||||
|
import { DictTag } from '#/components/dict-tag';
|
||||||
|
import { DICT_TYPE } from '#/utils/dict';
|
||||||
|
|
||||||
|
import { detailColumns } from '../data';
|
||||||
|
|
||||||
|
const formData = ref();
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
if (!isOpen) {
|
||||||
|
formData.value = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 加载数据
|
||||||
|
const data = modalApi.getData();
|
||||||
|
if (!data || !data.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
modalApi.lock();
|
||||||
|
try {
|
||||||
|
formData.value = await getNotifyTaskDetail(data.id);
|
||||||
|
} finally {
|
||||||
|
modalApi.unlock();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 打开弹窗 */
|
||||||
|
const open = (id: number) => {
|
||||||
|
modalApi.setData({ id }).open();
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({ open });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
title="通知详情"
|
||||||
|
class="w-1/2"
|
||||||
|
:show-cancel-button="false"
|
||||||
|
:show-confirm-button="false"
|
||||||
|
>
|
||||||
|
<Descriptions bordered :column="2" size="middle" class="mx-4">
|
||||||
|
<Descriptions.Item label="商户订单编号">
|
||||||
|
<Tag>{{ formData?.merchantOrderId }}</Tag>
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="通知状态">
|
||||||
|
<DictTag
|
||||||
|
:type="DICT_TYPE.PAY_NOTIFY_STATUS"
|
||||||
|
:value="formData?.status"
|
||||||
|
/>
|
||||||
|
</Descriptions.Item>
|
||||||
|
|
||||||
|
<Descriptions.Item label="应用编号">
|
||||||
|
{{ formData?.appId }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="应用名称">
|
||||||
|
{{ formData?.appName }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
|
||||||
|
<Descriptions.Item label="关联编号">
|
||||||
|
{{ formData?.dataId }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="通知类型">
|
||||||
|
<DictTag :type="DICT_TYPE.PAY_NOTIFY_TYPE" :value="formData?.type" />
|
||||||
|
</Descriptions.Item>
|
||||||
|
|
||||||
|
<Descriptions.Item label="通知次数">
|
||||||
|
{{ formData?.notifyTimes }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="最大通知次数">
|
||||||
|
{{ formData?.maxNotifyTimes }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
|
||||||
|
<Descriptions.Item label="最后通知时间">
|
||||||
|
{{ formatDateTime(formData?.lastExecuteTime || '') }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="下次通知时间">
|
||||||
|
{{ formatDateTime(formData?.nextNotifyTime || '') }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
|
||||||
|
<Descriptions.Item label="创建时间">
|
||||||
|
{{ formatDateTime(formData?.createTime || '') }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="更新时间">
|
||||||
|
{{ formatDateTime(formData?.updateTime || '') }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<Descriptions bordered :column="1" size="middle" class="mx-4">
|
||||||
|
<Descriptions.Item label="回调日志">
|
||||||
|
<Table :data="formData.logs" :columns="detailColumns" />
|
||||||
|
</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,177 @@
|
||||||
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
|
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
import type { PayRefundApi } from '#/api/pay/refund';
|
||||||
|
|
||||||
|
import { useAccess } from '@vben/access';
|
||||||
|
|
||||||
|
import { getAppList } from '#/api/pay/app';
|
||||||
|
import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '#/utils/dict';
|
||||||
|
|
||||||
|
const { hasAccessByCodes } = useAccess();
|
||||||
|
|
||||||
|
/** 列表的搜索表单 */
|
||||||
|
export function useGridFormSchema(): VbenFormSchema[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'appId',
|
||||||
|
label: '应用编号',
|
||||||
|
component: 'ApiSelect',
|
||||||
|
componentProps: {
|
||||||
|
api: async () => {
|
||||||
|
const data = await getAppList();
|
||||||
|
return data.map((item) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
autoSelect: 'first',
|
||||||
|
placeholder: '请选择数据源',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'channelCode',
|
||||||
|
label: '退款渠道',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
options: getStrDictOptions(DICT_TYPE.PAY_CHANNEL_CODE),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'merchantOrderId',
|
||||||
|
label: '商户支付单号',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'merchantRefundId',
|
||||||
|
label: '商户退款单号',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'channelOrderNo',
|
||||||
|
label: '渠道支付单号',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'channelRefundNo',
|
||||||
|
label: '渠道退款单号',
|
||||||
|
component: 'Input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'status',
|
||||||
|
label: '退款状态',
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
allowClear: true,
|
||||||
|
options: getIntDictOptions(DICT_TYPE.PAY_REFUND_STATUS),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'createTime',
|
||||||
|
label: '创建时间',
|
||||||
|
component: 'DatePicker',
|
||||||
|
componentProps: {
|
||||||
|
type: 'daterange',
|
||||||
|
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||||
|
defaultTime: [new Date('1 00:00:00'), new Date('1 23:59:59')],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 列表的字段 */
|
||||||
|
export function useGridColumns<T = PayRefundApi.Refund>(
|
||||||
|
onActionClick: OnActionClickFn<T>,
|
||||||
|
): VxeTableGridOptions['columns'] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field: 'id',
|
||||||
|
title: '编号',
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'createTime',
|
||||||
|
title: '创建时间',
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: 'formatDateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'payPrice',
|
||||||
|
title: '支付金额',
|
||||||
|
minWidth: 120,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellTag',
|
||||||
|
props: {
|
||||||
|
type: 'success',
|
||||||
|
content: '¥{payPrice}',
|
||||||
|
formatter: (value: number) => (value / 100).toFixed(2),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'refundPrice',
|
||||||
|
title: '退款金额',
|
||||||
|
minWidth: 120,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellTag',
|
||||||
|
props: {
|
||||||
|
type: 'danger',
|
||||||
|
content: '¥{refundPrice}',
|
||||||
|
formatter: (value: number) => (value / 100).toFixed(2),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'merchantRefundId',
|
||||||
|
title: '退款订单号',
|
||||||
|
minWidth: 300,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellTag',
|
||||||
|
props: {
|
||||||
|
type: 'info',
|
||||||
|
content: '商户 {merchantRefundId}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'channelRefundNo',
|
||||||
|
title: '渠道退款单号',
|
||||||
|
minWidth: 200,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellTag',
|
||||||
|
props: {
|
||||||
|
type: 'success',
|
||||||
|
content: '{channelRefundNo}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'status',
|
||||||
|
title: '退款状态',
|
||||||
|
minWidth: 120,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.PAY_REFUND_STATUS },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'operation',
|
||||||
|
title: '操作',
|
||||||
|
minWidth: 100,
|
||||||
|
align: 'center',
|
||||||
|
fixed: 'right',
|
||||||
|
cellRender: {
|
||||||
|
attrs: {
|
||||||
|
onClick: onActionClick,
|
||||||
|
},
|
||||||
|
name: 'CellOperation',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
code: 'detail',
|
||||||
|
show: hasAccessByCodes(['pay:refund:query']),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
@ -1,34 +1,104 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Page } from '@vben/common-ui';
|
import type {
|
||||||
|
OnActionClickParams,
|
||||||
|
VxeTableGridOptions,
|
||||||
|
} from '#/adapter/vxe-table';
|
||||||
|
|
||||||
|
import { Page, useVbenModal } from '@vben/common-ui';
|
||||||
|
import { Download } from '@vben/icons';
|
||||||
|
import { downloadFileFromBlobPart } from '@vben/utils';
|
||||||
|
|
||||||
import { Button } from 'ant-design-vue';
|
import { Button } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import * as RefundApi from '#/api/pay/refund';
|
||||||
import { DocAlert } from '#/components/doc-alert';
|
import { DocAlert } from '#/components/doc-alert';
|
||||||
</script>
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
|
import { useGridColumns, useGridFormSchema } from './data';
|
||||||
|
import Detail from './modules/detail.vue';
|
||||||
|
|
||||||
|
const [RefundDetailModal, refundDetailModalApi] = useVbenModal({
|
||||||
|
connectedComponent: Detail,
|
||||||
|
destroyOnClose: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 刷新表格 */
|
||||||
|
function onRefresh() {
|
||||||
|
gridApi.query();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出表格 */
|
||||||
|
async function onExport() {
|
||||||
|
const data = await RefundApi.exportRefund(await gridApi.formApi.getValues());
|
||||||
|
downloadFileFromBlobPart({ fileName: '支付退款.xls', source: data });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查看详情 */
|
||||||
|
function onDetail(row: any) {
|
||||||
|
refundDetailModalApi.setData(row).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 表格操作按钮的回调函数 */
|
||||||
|
function onActionClick({ code, row }: OnActionClickParams<any>) {
|
||||||
|
switch (code) {
|
||||||
|
case 'detail': {
|
||||||
|
onDetail(row);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
|
formOptions: {
|
||||||
|
schema: useGridFormSchema(),
|
||||||
|
},
|
||||||
|
gridOptions: {
|
||||||
|
columns: useGridColumns(onActionClick),
|
||||||
|
height: 'auto',
|
||||||
|
keepSource: true,
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }, formValues) => {
|
||||||
|
return await RefundApi.getRefundPage({
|
||||||
|
pageNo: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
...formValues,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
refresh: { code: 'query' },
|
||||||
|
search: true,
|
||||||
|
},
|
||||||
|
} as VxeTableGridOptions<any>,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Page>
|
<Page auto-content-height>
|
||||||
<DocAlert
|
<template #doc>
|
||||||
title="支付宝、微信退款接入"
|
<DocAlert
|
||||||
url="https://doc.iocoder.cn/pay/refund-demo/"
|
title="支付宝、微信退款接入"
|
||||||
/>
|
url="https://doc.iocoder.cn/pay/refund-demo/"
|
||||||
<Button
|
/>
|
||||||
danger
|
</template>
|
||||||
type="link"
|
<RefundDetailModal @success="onRefresh" />
|
||||||
target="_blank"
|
<Grid table-title="支付退款列表">
|
||||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
<template #toolbar-tools>
|
||||||
>
|
<Button
|
||||||
该功能支持 Vue3 + element-plus 版本!
|
type="primary"
|
||||||
</Button>
|
class="ml-2"
|
||||||
<br />
|
@click="onExport"
|
||||||
<Button
|
v-access:code="['pay:refund:export']"
|
||||||
type="link"
|
>
|
||||||
target="_blank"
|
<Download class="size-5" />
|
||||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/pay/refund/index"
|
{{ $t('ui.actionTitle.export') }}
|
||||||
>
|
</Button>
|
||||||
可参考
|
</template>
|
||||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/pay/refund/index
|
</Grid>
|
||||||
代码,pull request 贡献给我们!
|
|
||||||
</Button>
|
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { PayRefundApi } from '#/api/pay/refund';
|
||||||
|
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
import { formatDateTime } from '@vben/utils';
|
||||||
|
|
||||||
|
import { Descriptions, Divider, Tag } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { getRefund } from '#/api/pay/refund';
|
||||||
|
import { DictTag } from '#/components/dict-tag';
|
||||||
|
import { DICT_TYPE } from '#/utils/dict';
|
||||||
|
|
||||||
|
const formData = ref<PayRefundApi.Refund>();
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
if (!isOpen) {
|
||||||
|
formData.value = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 加载数据
|
||||||
|
const data = modalApi.getData<PayRefundApi.Refund>();
|
||||||
|
if (!data || !data.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
modalApi.lock();
|
||||||
|
try {
|
||||||
|
formData.value = await getRefund(data.id);
|
||||||
|
} finally {
|
||||||
|
modalApi.unlock();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 打开弹窗 */
|
||||||
|
const open = (id: number) => {
|
||||||
|
modalApi.setData({ id }).open();
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({ open });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
title="退款详情"
|
||||||
|
class="w-1/2"
|
||||||
|
:show-cancel-button="false"
|
||||||
|
:show-confirm-button="false"
|
||||||
|
>
|
||||||
|
<Descriptions bordered :column="2" size="middle" class="mx-4">
|
||||||
|
<Descriptions.Item label="商户退款单号">
|
||||||
|
<Tag size="small">{{ formData?.merchantRefundId }}</Tag>
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="渠道退款单号">
|
||||||
|
<Tag type="success" size="small" v-if="formData?.channelRefundNo">
|
||||||
|
{{ formData?.channelRefundNo }}
|
||||||
|
</Tag>
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="商户支付单号">
|
||||||
|
<Tag size="small">{{ formData?.merchantOrderId }}</Tag>
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="渠道支付单号">
|
||||||
|
<Tag type="success" size="small">
|
||||||
|
{{ formData?.channelOrderNo }}
|
||||||
|
</Tag>
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="应用编号">
|
||||||
|
{{ formData?.appId }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="应用名称">
|
||||||
|
{{ formData?.appName }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="支付金额">
|
||||||
|
<Tag type="success" size="small">
|
||||||
|
¥{{ (formData?.payPrice || 0) / 100.0 }}
|
||||||
|
</Tag>
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="退款金额">
|
||||||
|
<Tag size="mini" type="danger">
|
||||||
|
¥{{ (formData?.refundPrice || 0) / 100.0 }}
|
||||||
|
</Tag>
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="退款状态">
|
||||||
|
<DictTag
|
||||||
|
:type="DICT_TYPE.PAY_REFUND_STATUS"
|
||||||
|
:value="formData?.status"
|
||||||
|
/>
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="退款时间">
|
||||||
|
{{ formatDateTime(formData?.successTime || '') }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="创建时间">
|
||||||
|
{{ formatDateTime(formData?.createTime || '') }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="更新时间">
|
||||||
|
{{ formatDateTime(formData?.updateTime || '') }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
<Divider />
|
||||||
|
<Descriptions bordered :column="2" size="middle" class="mx-4">
|
||||||
|
<Descriptions.Item label="退款渠道">
|
||||||
|
<DictTag
|
||||||
|
:type="DICT_TYPE.PAY_CHANNEL_CODE"
|
||||||
|
:value="formData?.channelCode"
|
||||||
|
/>
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="退款原因">
|
||||||
|
{{ formData?.reason }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="退款 IP">
|
||||||
|
{{ formData?.userIp }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="通知 URL">
|
||||||
|
{{ formData?.notifyUrl }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
<Divider />
|
||||||
|
<Descriptions bordered :column="2" size="middle" class="mx-4">
|
||||||
|
<Descriptions.Item label="渠道错误码">
|
||||||
|
{{ formData?.channelErrorCode }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="渠道错误码描述">
|
||||||
|
{{ formData?.channelErrorMsg }}
|
||||||
|
</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
|
||||||
|
<Descriptions bordered :column="1" size="middle" class="mx-4">
|
||||||
|
<Descriptions.Item label="支付通道异步回调内容">
|
||||||
|
<p class="whitespace-pre-wrap break-words">
|
||||||
|
{{ formData?.channelNotifyData }}
|
||||||
|
</p>
|
||||||
|
</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
@ -1,31 +1,25 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
import { Page } from '@vben/common-ui';
|
import { Page } from '@vben/common-ui';
|
||||||
|
|
||||||
import { Button } from 'ant-design-vue';
|
|
||||||
|
|
||||||
import { DocAlert } from '#/components/doc-alert';
|
import { DocAlert } from '#/components/doc-alert';
|
||||||
|
import { IFrame } from '#/components/iframe';
|
||||||
|
|
||||||
|
defineOptions({ name: 'GoView' });
|
||||||
|
|
||||||
|
const src = ref(import.meta.env.VITE_GOVIEW_URL);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page>
|
<Page auto-content-height>
|
||||||
<DocAlert title="大屏设计器" url="https://doc.iocoder.cn/report/screen/" />
|
<template #doc>
|
||||||
<Button
|
<DocAlert
|
||||||
danger
|
title="大屏设计器"
|
||||||
type="link"
|
url="https://doc.iocoder.cn/report/screen/"
|
||||||
target="_blank"
|
/>
|
||||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
</template>
|
||||||
>
|
|
||||||
该功能支持 Vue3 + element-plus 版本!
|
<IFrame :src="src" />
|
||||||
</Button>
|
|
||||||
<br />
|
|
||||||
<Button
|
|
||||||
type="link"
|
|
||||||
target="_blank"
|
|
||||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/report/goview/index"
|
|
||||||
>
|
|
||||||
可参考
|
|
||||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/report/goview/index
|
|
||||||
代码,pull request 贡献给我们!
|
|
||||||
</Button>
|
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,28 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Page } from '@vben/common-ui';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
import { Button } from 'ant-design-vue';
|
import { Page } from '@vben/common-ui';
|
||||||
|
import { useAccessStore } from '@vben/stores';
|
||||||
|
|
||||||
import { DocAlert } from '#/components/doc-alert';
|
import { DocAlert } from '#/components/doc-alert';
|
||||||
|
import { IFrame } from '#/components/iframe';
|
||||||
|
|
||||||
|
defineOptions({ name: 'JimuReport' });
|
||||||
|
const accessStore = useAccessStore();
|
||||||
|
|
||||||
|
const src = ref(
|
||||||
|
`${import.meta.env.VITE_BASE_URL}/jmreport/list?token=${
|
||||||
|
accessStore.refreshToken
|
||||||
|
}`,
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page>
|
<Page auto-content-height>
|
||||||
<DocAlert title="报表设计器" url="https://doc.iocoder.cn/report/" />
|
<template #doc>
|
||||||
<Button
|
<DocAlert title="报表设计器" url="https://doc.iocoder.cn/report/" />
|
||||||
danger
|
</template>
|
||||||
type="link"
|
|
||||||
target="_blank"
|
<IFrame :src="src" />
|
||||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
|
||||||
>
|
|
||||||
该功能支持 Vue3 + element-plus 版本!
|
|
||||||
</Button>
|
|
||||||
<br />
|
|
||||||
<Button
|
|
||||||
type="link"
|
|
||||||
target="_blank"
|
|
||||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/report/jmreport/index"
|
|
||||||
>
|
|
||||||
可参考
|
|
||||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/report/jmreport/index
|
|
||||||
代码,pull request 贡献给我们!
|
|
||||||
</Button>
|
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -71,7 +71,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
try {
|
try {
|
||||||
data = await getDept(data.id);
|
data = await getDept(data.id);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 设置到 values
|
// 设置到 values
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -82,7 +82,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
await formApi.setValues(formData.value);
|
await formApi.setValues(formData.value);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
} else if (data && 'dictType' in data && data.dictType) {
|
} else if (data && 'dictType' in data && data.dictType) {
|
||||||
// 新增时,如果传入了dictType,则需要设置
|
// 新增时,如果传入了dictType,则需要设置
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -78,7 +78,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
await formApi.setValues(formData.value);
|
await formApi.setValues(formData.value);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
try {
|
try {
|
||||||
formData.value = data;
|
formData.value = data;
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -79,7 +79,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
// 设置到 values
|
// 设置到 values
|
||||||
await formApi.setValues(formData.value);
|
await formApi.setValues(formData.value);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
try {
|
try {
|
||||||
formData.value = data;
|
formData.value = data;
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -79,7 +79,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
// 设置到 values
|
// 设置到 values
|
||||||
await formApi.setValues(formData.value);
|
await formApi.setValues(formData.value);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('发送邮件失败', error);
|
console.error('发送邮件失败', error);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -71,7 +71,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
try {
|
try {
|
||||||
data = await getMenu(data.id as number);
|
data = await getMenu(data.id as number);
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 设置到 values
|
// 设置到 values
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onOpenChange(isOpen: boolean) {
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
|
@ -74,7 +74,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
await formApi.setValues(formData.value);
|
await formApi.setValues(formData.value);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.lock(false);
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue